LCOV - code coverage report
Current view: top level - linalg - slepc.cpp (source / functions) Coverage Total Hit
Test: Palace Coverage Report Lines: 0.0 % 1101 0
Test Date: 2025-10-23 22:45:05 Functions: 0.0 % 124 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 "slepc.hpp"
       5              : 
       6              : #if defined(PALACE_WITH_SLEPC)
       7              : 
       8              : #include <algorithm>
       9              : #include <petsc.h>
      10              : #include <slepc.h>
      11              : #include <mfem.hpp>
      12              : #include "linalg/divfree.hpp"
      13              : #include "linalg/nleps.hpp"
      14              : #include "linalg/rap.hpp"
      15              : #include "utils/communication.hpp"
      16              : 
      17              : static PetscErrorCode __mat_apply_EPS_A0(Mat, Vec, Vec);
      18              : static PetscErrorCode __mat_apply_EPS_A1(Mat, Vec, Vec);
      19              : static PetscErrorCode __mat_apply_EPS_B(Mat, Vec, Vec);
      20              : static PetscErrorCode __pc_apply_EPS(PC, Vec, Vec);
      21              : static PetscErrorCode __mat_apply_PEPLinear_L0(Mat, Vec, Vec);
      22              : static PetscErrorCode __mat_apply_PEPLinear_L1(Mat, Vec, Vec);
      23              : static PetscErrorCode __mat_apply_PEPLinear_B(Mat, Vec, Vec);
      24              : static PetscErrorCode __pc_apply_PEPLinear(PC, Vec, Vec);
      25              : static PetscErrorCode __mat_apply_PEP_A0(Mat, Vec, Vec);
      26              : static PetscErrorCode __mat_apply_PEP_A1(Mat, Vec, Vec);
      27              : static PetscErrorCode __mat_apply_PEP_A2(Mat, Vec, Vec);
      28              : static PetscErrorCode __mat_apply_PEP_B(Mat, Vec, Vec);
      29              : static PetscErrorCode __pc_apply_PEP(PC, Vec, Vec);
      30              : // for NEP
      31              : static PetscErrorCode __mat_apply_NEP_A(Mat, Vec, Vec);
      32              : static PetscErrorCode __mat_apply_NEP_J(Mat, Vec, Vec);
      33              : static PetscErrorCode __mat_apply_NEP_B(Mat, Vec, Vec);
      34              : static PetscErrorCode __pc_apply_NEP(PC, Vec, Vec);
      35              : static PetscErrorCode __form_NEP_function(NEP, PetscScalar, Mat, Mat, void *);
      36              : static PetscErrorCode __form_NEP_jacobian(NEP, PetscScalar, Mat, void *);
      37              : 
      38              : using namespace std::complex_literals;
      39              : 
      40              : namespace
      41              : {
      42              : 
      43            0 : inline PetscErrorCode FromPetscVec(Vec x, palace::ComplexVector &y, int block = 0,
      44              :                                    int nblocks = 1)
      45              : {
      46              :   PetscInt n;
      47              :   const PetscScalar *px;
      48              :   PetscMemType mtype;
      49            0 :   PetscCall(VecGetLocalSize(x, &n));
      50              :   MFEM_ASSERT(y.Size() * nblocks == n,
      51              :               "Invalid size mismatch for PETSc vector conversion!");
      52            0 :   PetscCall(VecGetArrayReadAndMemType(x, &px, &mtype));
      53            0 :   y.Set(px + block * n / nblocks, n / nblocks, PetscMemTypeDevice(mtype));
      54            0 :   PetscCall(VecRestoreArrayReadAndMemType(x, &px));
      55              :   return PETSC_SUCCESS;
      56              : }
      57              : 
      58            0 : inline PetscErrorCode FromPetscVec(Vec x, palace::ComplexVector &y1,
      59              :                                    palace::ComplexVector &y2)
      60              : {
      61              :   PetscInt n;
      62              :   const PetscScalar *px;
      63              :   PetscMemType mtype;
      64            0 :   PetscCall(VecGetLocalSize(x, &n));
      65              :   MFEM_ASSERT(y1.Size() == n / 2 && y2.Size() == n / 2,
      66              :               "Invalid size mismatch for PETSc vector conversion!");
      67            0 :   PetscCall(VecGetArrayReadAndMemType(x, &px, &mtype));
      68            0 :   y1.Set(px, n / 2, PetscMemTypeDevice(mtype));
      69            0 :   y2.Set(px + n / 2, n / 2, PetscMemTypeDevice(mtype));
      70            0 :   PetscCall(VecRestoreArrayReadAndMemType(x, &px));
      71              :   return PETSC_SUCCESS;
      72              : }
      73              : 
      74            0 : inline PetscErrorCode ToPetscVec(const palace::ComplexVector &x, Vec y, int block = 0,
      75              :                                  int nblocks = 1)
      76              : {
      77              :   PetscInt n;
      78              :   PetscScalar *py;
      79              :   PetscMemType mtype;
      80            0 :   PetscCall(VecGetLocalSize(y, &n));
      81              :   MFEM_ASSERT(x.Size() * nblocks == n,
      82              :               "Invalid size mismatch for PETSc vector conversion!");
      83            0 :   PetscCall(VecGetArrayWriteAndMemType(y, &py, &mtype));
      84            0 :   x.Get(py + block * n / nblocks, n / nblocks, PetscMemTypeDevice(mtype));
      85            0 :   PetscCall(VecRestoreArrayWriteAndMemType(y, &py));
      86              :   return PETSC_SUCCESS;
      87              : }
      88              : 
      89            0 : inline PetscErrorCode ToPetscVec(const palace::ComplexVector &x1,
      90              :                                  const palace::ComplexVector &x2, Vec y)
      91              : {
      92              :   PetscInt n;
      93              :   PetscScalar *py;
      94              :   PetscMemType mtype;
      95            0 :   PetscCall(VecGetLocalSize(y, &n));
      96              :   MFEM_ASSERT(x1.Size() == n / 2 && x2.Size() == n / 2,
      97              :               "Invalid size mismatch for PETSc vector conversion!");
      98            0 :   PetscCall(VecGetArrayWriteAndMemType(y, &py, &mtype));
      99            0 :   x1.Get(py, n / 2, PetscMemTypeDevice(mtype));
     100            0 :   x2.Get(py + n / 2, n / 2, PetscMemTypeDevice(mtype));
     101            0 :   PetscCall(VecRestoreArrayWriteAndMemType(y, &py));
     102              :   return PETSC_SUCCESS;
     103              : }
     104              : 
     105              : }  // namespace
     106              : 
     107              : namespace palace::slepc
     108              : {
     109              : 
     110              : namespace
     111              : {
     112              : 
     113            0 : inline PetscErrorCode ConfigurePetscDevice()
     114              : {
     115              :   // Tell PETSc to use the same CUDA or HIP device as MFEM.
     116            0 :   if (mfem::Device::Allows(mfem::Backend::CUDA_MASK))
     117              :   {
     118            0 :     PetscCall(PetscOptionsSetValue(NULL, "-use_gpu_aware_mpi",
     119              :                                    mfem::Device::GetGPUAwareMPI() ? "1" : "0"));
     120            0 :     PetscCall(PetscOptionsSetValue(NULL, "-device_select_cuda",
     121              :                                    std::to_string(mfem::Device::GetId()).c_str()));
     122              :     // PetscCall(PetscOptionsSetValue(NULL, "-bv_type", "svec"));
     123              :     // PetscCall(PetscOptionsSetValue(NULL, "-vec_type", "cuda"));
     124              :   }
     125            0 :   if (mfem::Device::Allows(mfem::Backend::HIP_MASK))
     126              :   {
     127            0 :     PetscCall(PetscOptionsSetValue(NULL, "-use_gpu_aware_mpi",
     128              :                                    mfem::Device::GetGPUAwareMPI() ? "1" : "0"));
     129            0 :     PetscCall(PetscOptionsSetValue(NULL, "-device_select_hip",
     130              :                                    std::to_string(mfem::Device::GetId()).c_str()));
     131              :     // PetscCall(PetscOptionsSetValue(NULL, "-bv_type", "svec"));
     132              :     // PetscCall(PetscOptionsSetValue(NULL, "-vec_type", "hip"));
     133              :   }
     134              :   return PETSC_SUCCESS;
     135              : }
     136              : 
     137              : inline VecType PetscVecType()
     138              : {
     139            0 :   if (mfem::Device::Allows(mfem::Backend::CUDA_MASK))
     140              :   {
     141              :     return VECCUDA;
     142              :   }
     143            0 :   if (mfem::Device::Allows(mfem::Backend::HIP_MASK))
     144              :   {
     145            0 :     return VECHIP;
     146              :   }
     147              :   return VECSTANDARD;
     148              : }
     149              : 
     150              : struct MatShellContext
     151              : {
     152              :   const ComplexOperator &A;
     153              :   ComplexVector &x, &y;
     154              : };
     155              : 
     156            0 : PetscErrorCode __mat_apply_shell(Mat A, Vec x, Vec y)
     157              : {
     158              :   PetscFunctionBeginUser;
     159              :   MatShellContext *ctx;
     160            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
     161            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
     162              : 
     163            0 :   PetscCall(FromPetscVec(x, ctx->x));
     164            0 :   ctx->A.Mult(ctx->x, ctx->y);
     165            0 :   PetscCall(ToPetscVec(ctx->y, y));
     166              : 
     167              :   PetscFunctionReturn(PETSC_SUCCESS);
     168              : }
     169              : 
     170            0 : PetscErrorCode __mat_apply_transpose_shell(Mat A, Vec x, Vec y)
     171              : {
     172              :   PetscFunctionBeginUser;
     173              :   MatShellContext *ctx;
     174            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
     175            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
     176              : 
     177            0 :   PetscCall(FromPetscVec(x, ctx->x));
     178            0 :   ctx->A.MultTranspose(ctx->x, ctx->y);
     179            0 :   PetscCall(ToPetscVec(ctx->y, y));
     180              : 
     181              :   PetscFunctionReturn(PETSC_SUCCESS);
     182              : }
     183              : 
     184            0 : PetscErrorCode __mat_apply_hermitian_transpose_shell(Mat A, Vec x, Vec y)
     185              : {
     186              :   PetscFunctionBeginUser;
     187              :   MatShellContext *ctx;
     188            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
     189            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
     190              : 
     191            0 :   PetscCall(FromPetscVec(x, ctx->x));
     192            0 :   ctx->A.MultHermitianTranspose(ctx->x, ctx->y);
     193            0 :   PetscCall(ToPetscVec(ctx->y, y));
     194              : 
     195              :   PetscFunctionReturn(PETSC_SUCCESS);
     196              : };
     197              : 
     198            0 : inline void ConfigurePCShell(ST st, void *ctx, PetscErrorCode (*__pc_apply)(PC, Vec, Vec))
     199              : {
     200              :   KSP ksp;
     201              :   PC pc;
     202            0 :   PalacePetscCall(STGetKSP(st, &ksp));
     203            0 :   PalacePetscCall(KSPGetPC(ksp, &pc));
     204            0 :   PalacePetscCall(PCSetType(pc, PCSHELL));
     205            0 :   PalacePetscCall(PCShellSetContext(pc, ctx));
     206            0 :   PalacePetscCall(PCShellSetApply(pc, __pc_apply));
     207            0 : }
     208              : 
     209            0 : inline void ConfigureRG(RG rg, PetscReal lr, PetscReal ur, PetscReal li, PetscReal ui,
     210              :                         bool complement = false)
     211              : {
     212            0 :   PalacePetscCall(RGSetType(rg, RGINTERVAL));
     213            0 :   PalacePetscCall(RGIntervalSetEndpoints(rg, lr, ur, li, ui));
     214            0 :   if (complement)
     215              :   {
     216            0 :     PalacePetscCall(RGSetComplement(rg, PETSC_TRUE));
     217              :   }
     218            0 : }
     219              : 
     220              : }  // namespace
     221              : 
     222            0 : void Initialize(int &argc, char **&argv, const char rc_file[], const char help[])
     223              : {
     224            0 :   ConfigurePetscDevice();
     225            0 :   PalacePetscCall(SlepcInitialize(&argc, &argv, rc_file, help));
     226              : 
     227              :   // Remove default PETSc signal handling, since it can be confusing when the errors occur
     228              :   // not from within SLEPc/PETSc.
     229            0 :   PalacePetscCall(PetscPopSignalHandler());
     230            0 : }
     231              : 
     232            0 : void Initialize()
     233              : {
     234            0 :   ConfigurePetscDevice();
     235            0 :   PalacePetscCall(SlepcInitializeNoArguments());
     236              : 
     237              :   // Remove default PETSc signal handling, since it can be confusing when the errors occur
     238              :   // not from within SLEPc/PETSc.
     239            0 :   PalacePetscCall(PetscPopSignalHandler());
     240            0 : }
     241              : 
     242            0 : void Finalize()
     243              : {
     244            0 :   PalacePetscCall(SlepcFinalize());
     245            0 : }
     246              : 
     247            0 : PetscReal GetMaxSingularValue(MPI_Comm comm, const ComplexOperator &A, bool herm,
     248              :                               PetscReal tol, PetscInt max_it)
     249              : {
     250              :   // This method assumes the provided operator has the required operations for SLEPc's EPS
     251              :   // or SVD solvers, namely MATOP_MULT and MATOP_MULT_HERMITIAN_TRANSPOSE (if the matrix
     252              :   // is not Hermitian).
     253            0 :   MFEM_VERIFY(A.Height() == A.Width(), "Spectral norm requires a square matrix!");
     254            0 :   const PetscInt n = A.Height();
     255            0 :   ComplexVector x(n), y(n);
     256            0 :   x.UseDevice(true);
     257            0 :   y.UseDevice(true);
     258            0 :   MatShellContext ctx = {A, x, y};
     259              :   Mat A0;
     260            0 :   PalacePetscCall(
     261              :       MatCreateShell(comm, n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)&ctx, &A0));
     262            0 :   PalacePetscCall(MatShellSetOperation(A0, MATOP_MULT, (void (*)(void))__mat_apply_shell));
     263            0 :   PalacePetscCall(MatShellSetVecType(A0, PetscVecType()));
     264            0 :   if (herm)
     265              :   {
     266              :     EPS eps;
     267              :     PetscInt num_conv;
     268            0 :     PetscScalar eig;
     269            0 :     PalacePetscCall(EPSCreate(comm, &eps));
     270            0 :     PalacePetscCall(EPSSetOperators(eps, A0, nullptr));
     271            0 :     PalacePetscCall(EPSSetProblemType(eps, EPS_HEP));
     272            0 :     PalacePetscCall(EPSSetWhichEigenpairs(eps, EPS_LARGEST_MAGNITUDE));
     273            0 :     PalacePetscCall(EPSSetDimensions(eps, 1, PETSC_DEFAULT, PETSC_DEFAULT));
     274            0 :     PalacePetscCall(EPSSetTolerances(eps, tol, max_it));
     275            0 :     PalacePetscCall(EPSSolve(eps));
     276            0 :     PalacePetscCall(EPSGetConverged(eps, &num_conv));
     277            0 :     if (num_conv < 1)
     278              :     {
     279            0 :       Mpi::Warning(comm, "SLEPc EPS solve did not converge for maximum singular value!\n");
     280              :       eig = 0.0;
     281              :     }
     282              :     else
     283              :     {
     284            0 :       PalacePetscCall(EPSGetEigenvalue(eps, 0, &eig, nullptr));
     285            0 :       MFEM_VERIFY(PetscImaginaryPart(eig) == 0.0,
     286              :                   "Unexpected complex eigenvalue for Hermitian matrix (λ = " << eig
     287              :                                                                              << ")!");
     288              :     }
     289            0 :     PalacePetscCall(EPSDestroy(&eps));
     290            0 :     PalacePetscCall(MatDestroy(&A0));
     291            0 :     return PetscAbsScalar(eig);
     292              :   }
     293              :   else
     294              :   {
     295            0 :     PalacePetscCall(MatShellSetOperation(A0, MATOP_MULT_TRANSPOSE,
     296              :                                          (void (*)(void))__mat_apply_transpose_shell));
     297            0 :     PalacePetscCall(
     298              :         MatShellSetOperation(A0, MATOP_MULT_HERMITIAN_TRANSPOSE,
     299              :                              (void (*)(void))__mat_apply_hermitian_transpose_shell));
     300              :     SVD svd;
     301              :     PetscInt num_conv;
     302              :     PetscReal sigma;
     303            0 :     PalacePetscCall(SVDCreate(comm, &svd));
     304            0 :     PalacePetscCall(SVDSetOperators(svd, A0, nullptr));
     305            0 :     PalacePetscCall(SVDSetProblemType(svd, SVD_STANDARD));
     306            0 :     PalacePetscCall(SVDSetWhichSingularTriplets(svd, SVD_LARGEST));
     307            0 :     PalacePetscCall(SVDSetDimensions(svd, 1, PETSC_DEFAULT, PETSC_DEFAULT));
     308            0 :     PalacePetscCall(SVDSetTolerances(svd, tol, max_it));
     309            0 :     PalacePetscCall(SVDSolve(svd));
     310            0 :     PalacePetscCall(SVDGetConverged(svd, &num_conv));
     311            0 :     if (num_conv < 1)
     312              :     {
     313            0 :       Mpi::Warning(comm, "SLEPc SVD solve did not converge for maximum singular value!\n");
     314            0 :       sigma = 0.0;
     315              :     }
     316              :     else
     317              :     {
     318            0 :       PalacePetscCall(SVDGetSingularTriplet(svd, 0, &sigma, nullptr, nullptr));
     319              :     }
     320            0 :     PalacePetscCall(SVDDestroy(&svd));
     321            0 :     PalacePetscCall(MatDestroy(&A0));
     322            0 :     return sigma;
     323              :   }
     324              : }
     325              : 
     326              : // Eigensolver base class methods.
     327              : 
     328            0 : SlepcEigenvalueSolver::SlepcEigenvalueSolver(int print) : print(print)
     329              : {
     330            0 :   sinvert = false;
     331            0 :   region = true;
     332              :   sigma = 0.0;
     333            0 :   gamma = delta = 1.0;
     334              : 
     335            0 :   opInv = nullptr;
     336            0 :   opProj = nullptr;
     337            0 :   opB = nullptr;
     338              : 
     339            0 :   B0 = nullptr;
     340            0 :   v0 = nullptr;
     341              : 
     342            0 :   cl_custom = false;
     343            0 : }
     344              : 
     345            0 : SlepcEigenvalueSolver::~SlepcEigenvalueSolver()
     346              : {
     347            0 :   PalacePetscCall(MatDestroy(&B0));
     348            0 :   PalacePetscCall(VecDestroy(&v0));
     349            0 : }
     350              : 
     351            0 : void SlepcEigenvalueSolver::SetOperators(const ComplexOperator &K, const ComplexOperator &M,
     352              :                                          EigenvalueSolver::ScaleType type)
     353              : {
     354            0 :   MFEM_ABORT("SetOperators not defined for base class SlepcEigenvalueSolver!");
     355              : }
     356              : 
     357            0 : void SlepcEigenvalueSolver::SetOperators(const ComplexOperator &K, const ComplexOperator &C,
     358              :                                          const ComplexOperator &M,
     359              :                                          EigenvalueSolver::ScaleType type)
     360              : {
     361            0 :   MFEM_ABORT("SetOperators not defined for base class SlepcEigenvalueSolver!");
     362              : }
     363              : 
     364            0 : void SlepcEigenvalueSolver::SetLinearSolver(ComplexKspSolver &ksp)
     365              : {
     366            0 :   opInv = &ksp;
     367            0 : }
     368              : 
     369            0 : void SlepcEigenvalueSolver::SetDivFreeProjector(const DivFreeSolver<ComplexVector> &divfree)
     370              : {
     371            0 :   opProj = &divfree;
     372            0 : }
     373              : 
     374            0 : void SlepcEigenvalueSolver::SetBMat(const Operator &B)
     375              : {
     376            0 :   opB = &B;
     377            0 : }
     378              : 
     379            0 : void SlepcEigenvalueSolver::SetShiftInvert(std::complex<double> s, bool precond)
     380              : {
     381            0 :   ST st = GetST();
     382            0 :   if (precond)
     383              :   {
     384            0 :     PalacePetscCall(STSetType(st, STPRECOND));
     385              :   }
     386              :   else
     387              :   {
     388            0 :     PalacePetscCall(STSetType(st, STSINVERT));
     389              :   }
     390            0 :   PalacePetscCall(STSetTransform(st, PETSC_TRUE));
     391            0 :   PalacePetscCall(STSetMatMode(st, ST_MATMODE_SHELL));
     392            0 :   sigma = s;  // Wait until solve time to call EPS/PEPSetTarget
     393            0 :   sinvert = true;
     394            0 : }
     395              : 
     396            0 : void SlepcEigenvalueSolver::SetOrthogonalization(bool mgs, bool cgs2)
     397              : {
     398              :   // The SLEPc default is CGS with refinement if needed.
     399            0 :   if (mgs || cgs2)
     400              :   {
     401            0 :     BV bv = GetBV();
     402              :     BVOrthogType type;
     403              :     BVOrthogRefineType refine;
     404            0 :     if (mgs)
     405              :     {
     406              :       type = BV_ORTHOG_MGS;
     407              :       refine = BV_ORTHOG_REFINE_NEVER;
     408              :     }
     409              :     else  // cgs2
     410              :     {
     411              :       type = BV_ORTHOG_CGS;
     412              :       refine = BV_ORTHOG_REFINE_ALWAYS;
     413              :     }
     414            0 :     PalacePetscCall(BVSetOrthogonalization(bv, type, refine, 1.0, BV_ORTHOG_BLOCK_GS));
     415              :   }
     416            0 : }
     417              : 
     418            0 : void SlepcEigenvalueSolver::Customize()
     419              : {
     420              :   // Configure the KSP object for non-preconditioned spectral transformations.
     421              :   PetscBool precond;
     422            0 :   ST st = GetST();
     423            0 :   PalacePetscCall(
     424              :       PetscObjectTypeCompare(reinterpret_cast<PetscObject>(st), STPRECOND, &precond));
     425            0 :   if (!precond)
     426              :   {
     427              :     KSP ksp;
     428            0 :     PalacePetscCall(STGetKSP(st, &ksp));
     429            0 :     PalacePetscCall(KSPSetType(ksp, KSPPREONLY));
     430              :   }
     431              : 
     432              :   // Configure the region based on the given target if necessary.
     433            0 :   if (sinvert && region)
     434              :   {
     435            0 :     if (PetscImaginaryPart(sigma) == 0.0)
     436              :     {
     437              :       PetscReal sr = PetscRealPart(sigma);
     438            0 :       if (sr > 0.0)
     439              :       {
     440            0 :         ConfigureRG(GetRG(), sr / gamma, mfem::infinity(), -mfem::infinity(),
     441              :                     mfem::infinity());
     442              :       }
     443            0 :       else if (sr < 0.0)
     444              :       {
     445            0 :         ConfigureRG(GetRG(), -mfem::infinity(), sr / gamma, -mfem::infinity(),
     446              :                     mfem::infinity());
     447              :       }
     448              :     }
     449            0 :     else if (PetscRealPart(sigma) == 0.0)
     450              :     {
     451              :       PetscReal si = PetscImaginaryPart(sigma);
     452            0 :       if (si > 0.0)
     453              :       {
     454            0 :         ConfigureRG(GetRG(), -mfem::infinity(), mfem::infinity(), si / gamma,
     455              :                     mfem::infinity());
     456              :       }
     457            0 :       else if (si < 0.0)
     458              :       {
     459            0 :         ConfigureRG(GetRG(), -mfem::infinity(), mfem::infinity(), -mfem::infinity(),
     460            0 :                     si / gamma);
     461              :       }
     462              :     }
     463              :     else
     464              :     {
     465            0 :       MFEM_ABORT("Shift-and-invert with general complex eigenvalue target is unsupported!");
     466              :     }
     467              :   }
     468            0 : }
     469              : 
     470            0 : PetscReal SlepcEigenvalueSolver::GetEigenvectorNorm(const ComplexVector &x,
     471              :                                                     ComplexVector &Bx) const
     472              : {
     473            0 :   if (opB)
     474              :   {
     475            0 :     return linalg::Norml2(GetComm(), x, *opB, Bx);
     476              :   }
     477              :   else
     478              :   {
     479            0 :     return linalg::Norml2(GetComm(), x);
     480              :   }
     481              : }
     482              : 
     483            0 : PetscReal SlepcEigenvalueSolver::GetError(int i, EigenvalueSolver::ErrorType type) const
     484              : {
     485            0 :   switch (type)
     486              :   {
     487            0 :     case ErrorType::ABSOLUTE:
     488            0 :       return res.get()[i];
     489            0 :     case ErrorType::RELATIVE:
     490            0 :       return res.get()[i] / PetscAbsScalar(GetEigenvalue(i));
     491            0 :     case ErrorType::BACKWARD:
     492            0 :       return res.get()[i] / GetBackwardScaling(GetEigenvalue(i));
     493              :   }
     494              :   return 0.0;
     495              : }
     496              : 
     497            0 : void SlepcEigenvalueSolver::RescaleEigenvectors(int num_eig)
     498              : {
     499            0 :   res = std::make_unique<PetscReal[]>(num_eig);
     500            0 :   xscale = std::make_unique<PetscReal[]>(num_eig);
     501            0 :   for (int i = 0; i < num_eig; i++)
     502              :   {
     503            0 :     xscale.get()[i] = 0.0;
     504            0 :     GetEigenvector(i, x1);
     505            0 :     xscale.get()[i] = 1.0 / GetEigenvectorNorm(x1, y1);
     506            0 :     res.get()[i] =
     507            0 :         GetResidualNorm(GetEigenvalue(i), x1, y1) / linalg::Norml2(GetComm(), x1);
     508              :   }
     509            0 : }
     510              : 
     511              : // EPS specific methods.
     512              : 
     513            0 : SlepcEPSSolverBase::SlepcEPSSolverBase(MPI_Comm comm, int print, const std::string &prefix)
     514            0 :   : SlepcEigenvalueSolver(print)
     515              : {
     516            0 :   PalacePetscCall(EPSCreate(comm, &eps));
     517            0 :   PalacePetscCall(EPSSetOptionsPrefix(eps, prefix.c_str()));
     518            0 :   if (print > 0)
     519              :   {
     520            0 :     std::string opts = "-eps_monitor";
     521            0 :     if (print > 2)
     522              :     {
     523            0 :       opts.append(" -eps_view");
     524              :     }
     525            0 :     if (prefix.length() > 0)
     526              :     {
     527            0 :       PetscOptionsPrefixPush(nullptr, prefix.c_str());
     528              :     }
     529            0 :     PetscOptionsInsertString(nullptr, opts.c_str());
     530            0 :     if (prefix.length() > 0)
     531              :     {
     532            0 :       PetscOptionsPrefixPop(nullptr);
     533              :     }
     534              :   }
     535            0 :   A0 = A1 = nullptr;
     536            0 : }
     537              : 
     538            0 : SlepcEPSSolverBase::~SlepcEPSSolverBase()
     539              : {
     540            0 :   PalacePetscCall(EPSDestroy(&eps));
     541            0 :   PalacePetscCall(MatDestroy(&A0));
     542            0 :   PalacePetscCall(MatDestroy(&A1));
     543            0 : }
     544              : 
     545            0 : void SlepcEPSSolverBase::SetNumModes(int num_eig, int num_vec)
     546              : {
     547            0 :   PalacePetscCall(EPSSetDimensions(eps, num_eig, (num_vec > 0) ? num_vec : PETSC_DEFAULT,
     548              :                                    PETSC_DEFAULT));
     549            0 : }
     550              : 
     551            0 : void SlepcEPSSolverBase::SetTol(PetscReal tol)
     552              : {
     553            0 :   PalacePetscCall(EPSSetTolerances(eps, tol, PETSC_DEFAULT));
     554            0 :   PalacePetscCall(EPSSetConvergenceTest(eps, EPS_CONV_REL));
     555              :   // PalacePetscCall(EPSSetTrackAll(eps, PETSC_TRUE));
     556              :   // PalacePetscCall(EPSSetTrueResidual(eps, PETSC_TRUE));
     557            0 : }
     558              : 
     559            0 : void SlepcEPSSolverBase::SetMaxIter(int max_it)
     560              : {
     561            0 :   PalacePetscCall(
     562              :       EPSSetTolerances(eps, PETSC_DEFAULT, (max_it > 0) ? max_it : PETSC_DEFAULT));
     563            0 : }
     564              : 
     565            0 : void SlepcEPSSolverBase::SetWhichEigenpairs(EigenvalueSolver::WhichType type)
     566              : {
     567            0 :   switch (type)
     568              :   {
     569            0 :     case WhichType::LARGEST_MAGNITUDE:
     570            0 :       PalacePetscCall(EPSSetWhichEigenpairs(eps, EPS_LARGEST_MAGNITUDE));
     571            0 :       region = false;
     572            0 :       break;
     573            0 :     case WhichType::SMALLEST_MAGNITUDE:
     574            0 :       PalacePetscCall(EPSSetWhichEigenpairs(eps, EPS_SMALLEST_MAGNITUDE));
     575            0 :       region = false;
     576            0 :       break;
     577            0 :     case WhichType::LARGEST_REAL:
     578            0 :       PalacePetscCall(EPSSetWhichEigenpairs(eps, EPS_LARGEST_REAL));
     579              :       break;
     580            0 :     case WhichType::SMALLEST_REAL:
     581            0 :       PalacePetscCall(EPSSetWhichEigenpairs(eps, EPS_SMALLEST_REAL));
     582              :       break;
     583            0 :     case WhichType::LARGEST_IMAGINARY:
     584            0 :       PalacePetscCall(EPSSetWhichEigenpairs(eps, EPS_LARGEST_IMAGINARY));
     585              :       break;
     586            0 :     case WhichType::SMALLEST_IMAGINARY:
     587            0 :       PalacePetscCall(EPSSetWhichEigenpairs(eps, EPS_SMALLEST_IMAGINARY));
     588              :       break;
     589            0 :     case WhichType::TARGET_MAGNITUDE:
     590            0 :       PalacePetscCall(EPSSetWhichEigenpairs(eps, EPS_TARGET_MAGNITUDE));
     591            0 :       region = false;
     592            0 :       break;
     593            0 :     case WhichType::TARGET_REAL:
     594            0 :       PalacePetscCall(EPSSetWhichEigenpairs(eps, EPS_TARGET_REAL));
     595              :       break;
     596            0 :     case WhichType::TARGET_IMAGINARY:
     597            0 :       PalacePetscCall(EPSSetWhichEigenpairs(eps, EPS_TARGET_IMAGINARY));
     598              :       break;
     599              :   }
     600            0 : }
     601              : 
     602            0 : void SlepcEPSSolverBase::SetProblemType(SlepcEigenvalueSolver::ProblemType type)
     603              : {
     604            0 :   switch (type)
     605              :   {
     606            0 :     case ProblemType::HERMITIAN:
     607            0 :       PalacePetscCall(EPSSetProblemType(eps, EPS_HEP));
     608              :       break;
     609            0 :     case ProblemType::NON_HERMITIAN:
     610            0 :       PalacePetscCall(EPSSetProblemType(eps, EPS_NHEP));
     611              :       break;
     612            0 :     case ProblemType::GEN_HERMITIAN:
     613            0 :       PalacePetscCall(EPSSetProblemType(eps, EPS_GHEP));
     614              :       break;
     615            0 :     case ProblemType::GEN_INDEFINITE:
     616            0 :       PalacePetscCall(EPSSetProblemType(eps, EPS_GHIEP));
     617              :       break;
     618            0 :     case ProblemType::GEN_NON_HERMITIAN:
     619            0 :       PalacePetscCall(EPSSetProblemType(eps, EPS_GNHEP));
     620              :       // PalacePetscCall(EPSSetProblemType(eps, EPS_PGNHEP));  // If B is SPD
     621              :       break;
     622            0 :     case ProblemType::HYPERBOLIC:
     623              :     case ProblemType::GYROSCOPIC:
     624              :     case ProblemType::GENERAL:
     625            0 :       MFEM_ABORT("Problem type not implemented!");
     626              :       break;
     627              :   }
     628            0 : }
     629              : 
     630            0 : void SlepcEPSSolverBase::SetType(SlepcEigenvalueSolver::Type type)
     631              : {
     632            0 :   switch (type)
     633              :   {
     634            0 :     case Type::KRYLOVSCHUR:
     635            0 :       PalacePetscCall(EPSSetType(eps, EPSKRYLOVSCHUR));
     636              :       break;
     637            0 :     case Type::POWER:
     638            0 :       PalacePetscCall(EPSSetType(eps, EPSPOWER));
     639              :       break;
     640            0 :     case Type::SUBSPACE:
     641            0 :       PalacePetscCall(EPSSetType(eps, EPSSUBSPACE));
     642              :       break;
     643            0 :     case Type::JD:
     644            0 :       PalacePetscCall(EPSSetType(eps, EPSJD));
     645            0 :       region = false;
     646            0 :       break;
     647            0 :     case Type::TOAR:
     648              :     case Type::STOAR:
     649              :     case Type::QARNOLDI:
     650              :     case Type::SLP:
     651              :     case Type::NLEIGS:
     652            0 :       MFEM_ABORT("Eigenvalue solver type not implemented!");
     653              :       break;
     654              :   }
     655            0 : }
     656              : 
     657            0 : void SlepcEPSSolverBase::SetInitialSpace(const ComplexVector &v)
     658              : {
     659            0 :   MFEM_VERIFY(
     660              :       A0 && A1,
     661              :       "Must call SetOperators before using SetInitialSpace for SLEPc eigenvalue solver!");
     662            0 :   if (!v0)
     663              :   {
     664            0 :     PalacePetscCall(MatCreateVecs(A0, nullptr, &v0));
     665              :   }
     666            0 :   PalacePetscCall(ToPetscVec(v, v0));
     667            0 :   Vec is[1] = {v0};
     668            0 :   PalacePetscCall(EPSSetInitialSpace(eps, 1, is));
     669            0 : }
     670              : 
     671            0 : void SlepcEPSSolverBase::Customize()
     672              : {
     673            0 :   SlepcEigenvalueSolver::Customize();
     674            0 :   PalacePetscCall(EPSSetTarget(eps, sigma / gamma));
     675            0 :   if (!cl_custom)
     676              :   {
     677            0 :     PalacePetscCall(EPSSetFromOptions(eps));
     678            0 :     if (print > 0)
     679              :     {
     680            0 :       PetscOptionsView(nullptr, PETSC_VIEWER_STDOUT_(GetComm()));
     681            0 :       Mpi::Print(GetComm(), "\n");
     682              :     }
     683            0 :     cl_custom = true;
     684              :   }
     685            0 : }
     686              : 
     687            0 : int SlepcEPSSolverBase::Solve()
     688              : {
     689            0 :   MFEM_VERIFY(A0 && A1 && opInv, "Operators are not set for SlepcEPSSolverBase!");
     690              : 
     691              :   // Solve the eigenvalue problem.
     692              :   PetscInt num_conv;
     693            0 :   Customize();
     694            0 :   PalacePetscCall(EPSSolve(eps));
     695            0 :   PalacePetscCall(EPSGetConverged(eps, &num_conv));
     696            0 :   if (print > 0)
     697              :   {
     698            0 :     Mpi::Print(GetComm(), "\n");
     699            0 :     PalacePetscCall(EPSConvergedReasonView(eps, PETSC_VIEWER_STDOUT_(GetComm())));
     700            0 :     Mpi::Print(GetComm(),
     701              :                " Total number of linear systems solved: {:d}\n"
     702              :                " Total number of linear solver iterations: {:d}\n",
     703            0 :                opInv->NumTotalMult(), opInv->NumTotalMultIterations());
     704              :   }
     705              : 
     706              :   // Compute and store the eigenpair residuals.
     707            0 :   RescaleEigenvectors(num_conv);
     708            0 :   return (int)num_conv;
     709              : }
     710              : 
     711            0 : std::complex<double> SlepcEPSSolverBase::GetEigenvalue(int i) const
     712              : {
     713            0 :   PetscScalar l;
     714            0 :   PalacePetscCall(EPSGetEigenvalue(eps, i, &l, nullptr));
     715            0 :   return l * gamma;
     716              : }
     717              : 
     718            0 : void SlepcEPSSolverBase::GetEigenvector(int i, ComplexVector &x) const
     719              : {
     720            0 :   MFEM_VERIFY(
     721              :       v0,
     722              :       "Must call SetOperators before using GetEigenvector for SLEPc eigenvalue solver!");
     723            0 :   PalacePetscCall(EPSGetEigenvector(eps, i, v0, nullptr));
     724            0 :   PalacePetscCall(FromPetscVec(v0, x));
     725            0 :   if (xscale.get()[i] > 0.0)
     726              :   {
     727            0 :     x *= xscale.get()[i];
     728              :   }
     729            0 : }
     730              : 
     731            0 : BV SlepcEPSSolverBase::GetBV() const
     732              : {
     733              :   BV bv;
     734            0 :   PalacePetscCall(EPSGetBV(eps, &bv));
     735            0 :   return bv;
     736              : }
     737              : 
     738            0 : ST SlepcEPSSolverBase::GetST() const
     739              : {
     740              :   ST st;
     741            0 :   PalacePetscCall(EPSGetST(eps, &st));
     742            0 :   return st;
     743              : }
     744              : 
     745            0 : RG SlepcEPSSolverBase::GetRG() const
     746              : {
     747              :   RG rg;
     748            0 :   PalacePetscCall(EPSGetRG(eps, &rg));
     749            0 :   return rg;
     750              : }
     751              : 
     752            0 : SlepcEPSSolver::SlepcEPSSolver(MPI_Comm comm, int print, const std::string &prefix)
     753            0 :   : SlepcEPSSolverBase(comm, print, prefix)
     754              : {
     755            0 :   opK = opM = nullptr;
     756            0 :   normK = normM = 0.0;
     757            0 : }
     758              : 
     759            0 : void SlepcEPSSolver::SetOperators(const ComplexOperator &K, const ComplexOperator &M,
     760              :                                   EigenvalueSolver::ScaleType type)
     761              : {
     762              :   // Construct shell matrices for the scaled operators which define the generalized
     763              :   // eigenvalue problem.
     764            0 :   const bool first = (opK == nullptr);
     765            0 :   opK = &K;
     766            0 :   opM = &M;
     767              : 
     768            0 :   if (first)
     769              :   {
     770            0 :     const PetscInt n = opK->Height();
     771            0 :     PalacePetscCall(
     772              :         MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &A0));
     773            0 :     PalacePetscCall(
     774              :         MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &A1));
     775            0 :     PalacePetscCall(
     776              :         MatShellSetOperation(A0, MATOP_MULT, (void (*)(void))__mat_apply_EPS_A0));
     777            0 :     PalacePetscCall(
     778              :         MatShellSetOperation(A1, MATOP_MULT, (void (*)(void))__mat_apply_EPS_A1));
     779            0 :     PalacePetscCall(MatShellSetVecType(A0, PetscVecType()));
     780            0 :     PalacePetscCall(MatShellSetVecType(A1, PetscVecType()));
     781            0 :     PalacePetscCall(EPSSetOperators(eps, A0, A1));
     782              :   }
     783              : 
     784            0 :   if (first && type != ScaleType::NONE)
     785              :   {
     786            0 :     normK = linalg::SpectralNorm(GetComm(), *opK, opK->IsReal());
     787            0 :     normM = linalg::SpectralNorm(GetComm(), *opM, opM->IsReal());
     788            0 :     MFEM_VERIFY(normK >= 0.0 && normM >= 0.0, "Invalid matrix norms for EPS scaling!");
     789            0 :     if (normK > 0 && normM > 0.0)
     790              :     {
     791            0 :       gamma = normK / normM;  // Store γ² for linear problem
     792            0 :       delta = 2.0 / normK;
     793              :     }
     794              :   }
     795              : 
     796              :   // Set up workspace.
     797            0 :   if (!v0)
     798              :   {
     799            0 :     PalacePetscCall(MatCreateVecs(A0, nullptr, &v0));
     800              :   }
     801            0 :   x1.SetSize(opK->Height());
     802            0 :   y1.SetSize(opK->Height());
     803            0 :   x1.UseDevice(true);
     804            0 :   y1.UseDevice(true);
     805              : 
     806              :   // Configure linear solver for generalized problem or spectral transformation. This also
     807              :   // allows use of the divergence-free projector as a linear solve side-effect.
     808            0 :   if (first)
     809              :   {
     810            0 :     ConfigurePCShell(GetST(), (void *)this, __pc_apply_EPS);
     811              :   }
     812            0 : }
     813              : 
     814            0 : void SlepcEPSSolver::SetBMat(const Operator &B)
     815              : {
     816            0 :   SlepcEigenvalueSolver::SetBMat(B);
     817              : 
     818            0 :   const PetscInt n = B.Height();
     819            0 :   PalacePetscCall(
     820              :       MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &B0));
     821            0 :   PalacePetscCall(MatShellSetOperation(B0, MATOP_MULT, (void (*)(void))__mat_apply_EPS_B));
     822            0 :   PalacePetscCall(MatShellSetVecType(B0, PetscVecType()));
     823              : 
     824            0 :   BV bv = GetBV();
     825            0 :   PalacePetscCall(BVSetMatrix(bv, B0, PETSC_FALSE));
     826            0 : }
     827              : 
     828            0 : PetscReal SlepcEPSSolver::GetResidualNorm(PetscScalar l, const ComplexVector &x,
     829              :                                           ComplexVector &r) const
     830              : {
     831              :   // Compute the i-th eigenpair residual: || (K - λ M) x ||₂ for eigenvalue λ.
     832            0 :   opK->Mult(x, r);
     833            0 :   opM->AddMult(x, r, -l);
     834            0 :   return linalg::Norml2(GetComm(), r);
     835              : }
     836              : 
     837            0 : PetscReal SlepcEPSSolver::GetBackwardScaling(PetscScalar l) const
     838              : {
     839              :   // Make sure not to use norms from scaling as this can be confusing if they are different.
     840              :   // Note that SLEPc typically uses ||.||∞, not the 2-norm.
     841            0 :   if (normK <= 0.0)
     842              :   {
     843            0 :     normK = linalg::SpectralNorm(GetComm(), *opK, opK->IsReal());
     844              :   }
     845            0 :   if (normM <= 0.0)
     846              :   {
     847            0 :     normM = linalg::SpectralNorm(GetComm(), *opM, opM->IsReal());
     848              :   }
     849            0 :   return normK + PetscAbsScalar(l) * normM;
     850              : }
     851              : 
     852            0 : SlepcPEPLinearSolver::SlepcPEPLinearSolver(MPI_Comm comm, int print,
     853            0 :                                            const std::string &prefix)
     854            0 :   : SlepcEPSSolverBase(comm, print, prefix)
     855              : {
     856            0 :   opK = opC = opM = nullptr;
     857            0 :   normK = normC = normM = 0.0;
     858            0 : }
     859              : 
     860            0 : void SlepcPEPLinearSolver::SetOperators(const ComplexOperator &K, const ComplexOperator &C,
     861              :                                         const ComplexOperator &M,
     862              :                                         EigenvalueSolver::ScaleType type)
     863              : {
     864              :   // Construct shell matrices for the scaled linearized operators which define the block 2x2
     865              :   // eigenvalue problem.
     866            0 :   const bool first = (opK == nullptr);
     867            0 :   opK = &K;
     868            0 :   opC = &C;
     869            0 :   opM = &M;
     870            0 :   if (first)
     871              :   {
     872            0 :     const PetscInt n = opK->Height();
     873            0 :     PalacePetscCall(MatCreateShell(GetComm(), 2 * n, 2 * n, PETSC_DECIDE, PETSC_DECIDE,
     874              :                                    (void *)this, &A0));
     875            0 :     PalacePetscCall(MatCreateShell(GetComm(), 2 * n, 2 * n, PETSC_DECIDE, PETSC_DECIDE,
     876              :                                    (void *)this, &A1));
     877            0 :     PalacePetscCall(
     878              :         MatShellSetOperation(A0, MATOP_MULT, (void (*)(void))__mat_apply_PEPLinear_L0));
     879            0 :     PalacePetscCall(
     880              :         MatShellSetOperation(A1, MATOP_MULT, (void (*)(void))__mat_apply_PEPLinear_L1));
     881            0 :     PalacePetscCall(MatShellSetVecType(A0, PetscVecType()));
     882            0 :     PalacePetscCall(MatShellSetVecType(A1, PetscVecType()));
     883            0 :     PalacePetscCall(EPSSetOperators(eps, A0, A1));
     884              :   }
     885              : 
     886            0 :   if (first && type != ScaleType::NONE)
     887              :   {
     888            0 :     normK = linalg::SpectralNorm(GetComm(), *opK, opK->IsReal());
     889            0 :     normC = linalg::SpectralNorm(GetComm(), *opC, opC->IsReal());
     890            0 :     normM = linalg::SpectralNorm(GetComm(), *opM, opM->IsReal());
     891            0 :     MFEM_VERIFY(normK >= 0.0 && normC >= 0.0 && normM >= 0.0,
     892              :                 "Invalid matrix norms for PEP scaling!");
     893            0 :     if (normK > 0 && normC >= 0.0 && normM > 0.0)
     894              :     {
     895            0 :       gamma = std::sqrt(normK / normM);
     896            0 :       delta = 2.0 / (normK + gamma * normC);
     897              :     }
     898              :   }
     899              : 
     900              :   // Set up workspace.
     901            0 :   if (!v0)
     902              :   {
     903            0 :     PalacePetscCall(MatCreateVecs(A0, nullptr, &v0));
     904              :   }
     905            0 :   x1.SetSize(opK->Height());
     906            0 :   x2.SetSize(opK->Height());
     907            0 :   y1.SetSize(opK->Height());
     908            0 :   y2.SetSize(opK->Height());
     909            0 :   x1.UseDevice(true);
     910            0 :   x2.UseDevice(true);
     911            0 :   y1.UseDevice(true);
     912            0 :   y2.UseDevice(true);
     913              : 
     914              :   // Configure linear solver.
     915            0 :   if (first)
     916              :   {
     917            0 :     ConfigurePCShell(GetST(), (void *)this, __pc_apply_PEPLinear);
     918              :   }
     919            0 : }
     920              : 
     921            0 : void SlepcPEPLinearSolver::SetBMat(const Operator &B)
     922              : {
     923            0 :   SlepcEigenvalueSolver::SetBMat(B);
     924              : 
     925            0 :   const PetscInt n = B.Height();
     926            0 :   PalacePetscCall(MatCreateShell(GetComm(), 2 * n, 2 * n, PETSC_DECIDE, PETSC_DECIDE,
     927              :                                  (void *)this, &B0));
     928            0 :   PalacePetscCall(
     929              :       MatShellSetOperation(B0, MATOP_MULT, (void (*)(void))__mat_apply_PEPLinear_B));
     930            0 :   PalacePetscCall(MatShellSetVecType(B0, PetscVecType()));
     931              : 
     932            0 :   BV bv = GetBV();
     933            0 :   PalacePetscCall(BVSetMatrix(bv, B0, PETSC_FALSE));
     934            0 : }
     935              : 
     936            0 : void SlepcPEPLinearSolver::SetInitialSpace(const ComplexVector &v)
     937              : {
     938            0 :   MFEM_VERIFY(
     939              :       A0 && A1,
     940              :       "Must call SetOperators before using SetInitialSpace for SLEPc eigenvalue solver!");
     941            0 :   if (!v0)
     942              :   {
     943            0 :     PalacePetscCall(MatCreateVecs(A0, nullptr, &v0));
     944              :   }
     945            0 :   PalacePetscCall(ToPetscVec(v, v0, 0, 2));
     946            0 :   Vec is[1] = {v0};
     947            0 :   PalacePetscCall(EPSSetInitialSpace(eps, 1, is));
     948            0 : }
     949              : 
     950            0 : void SlepcPEPLinearSolver::GetEigenvector(int i, ComplexVector &x) const
     951              : {
     952              :   // Select the most accurate x for y = [x₁; x₂] from the linearized eigenvalue problem. Or,
     953              :   // just take x = x₁.
     954            0 :   MFEM_VERIFY(
     955              :       v0,
     956              :       "Must call SetOperators before using GetEigenvector for SLEPc eigenvalue solver!");
     957            0 :   PalacePetscCall(EPSGetEigenvector(eps, i, v0, nullptr));
     958            0 :   PalacePetscCall(FromPetscVec(v0, x, 0, 2));
     959            0 :   if (xscale.get()[i] > 0.0)
     960              :   {
     961            0 :     x *= xscale.get()[i];
     962              :   }
     963            0 : }
     964              : 
     965            0 : PetscReal SlepcPEPLinearSolver::GetResidualNorm(PetscScalar l, const ComplexVector &x,
     966              :                                                 ComplexVector &r) const
     967              : {
     968              :   // Compute the i-th eigenpair residual: || P(λ) x ||₂ = || (K + λ C + λ² M) x ||₂ for
     969              :   // eigenvalue λ.
     970            0 :   opK->Mult(x, r);
     971            0 :   if (opC)
     972              :   {
     973            0 :     opC->AddMult(x, r, l);
     974              :   }
     975            0 :   opM->AddMult(x, r, l * l);
     976            0 :   return linalg::Norml2(GetComm(), r);
     977              : }
     978              : 
     979            0 : PetscReal SlepcPEPLinearSolver::GetBackwardScaling(PetscScalar l) const
     980              : {
     981              :   // Make sure not to use norms from scaling as this can be confusing if they are different.
     982              :   // Note that SLEPc typically uses ||.||∞, not the 2-norm.
     983            0 :   if (normK <= 0.0)
     984              :   {
     985            0 :     normK = linalg::SpectralNorm(GetComm(), *opK, opK->IsReal());
     986              :   }
     987            0 :   if (normC <= 0.0 && opC)
     988              :   {
     989            0 :     normC = linalg::SpectralNorm(GetComm(), *opC, opC->IsReal());
     990              :   }
     991            0 :   if (normM <= 0.0)
     992              :   {
     993            0 :     normM = linalg::SpectralNorm(GetComm(), *opM, opM->IsReal());
     994              :   }
     995            0 :   PetscReal t = PetscAbsScalar(l);
     996            0 :   return normK + t * normC + t * t * normM;
     997              : }
     998              : 
     999              : // PEP specific methods.
    1000              : 
    1001            0 : SlepcPEPSolverBase::SlepcPEPSolverBase(MPI_Comm comm, int print, const std::string &prefix)
    1002            0 :   : SlepcEigenvalueSolver(print)
    1003              : {
    1004            0 :   PalacePetscCall(PEPCreate(comm, &pep));
    1005            0 :   PalacePetscCall(PEPSetOptionsPrefix(pep, prefix.c_str()));
    1006            0 :   if (print > 0)
    1007              :   {
    1008            0 :     std::string opts = "-pep_monitor";
    1009            0 :     if (print > 2)
    1010              :     {
    1011            0 :       opts.append(" -pep_view");
    1012              :     }
    1013            0 :     if (prefix.length() > 0)
    1014              :     {
    1015            0 :       PetscOptionsPrefixPush(nullptr, prefix.c_str());
    1016              :     }
    1017            0 :     PetscOptionsInsertString(nullptr, opts.c_str());
    1018            0 :     if (prefix.length() > 0)
    1019              :     {
    1020            0 :       PetscOptionsPrefixPop(nullptr);
    1021              :     }
    1022              :   }
    1023            0 :   A0 = A1 = A2 = nullptr;
    1024            0 : }
    1025              : 
    1026            0 : SlepcPEPSolverBase::~SlepcPEPSolverBase()
    1027              : {
    1028            0 :   PalacePetscCall(PEPDestroy(&pep));
    1029            0 :   PalacePetscCall(MatDestroy(&A0));
    1030            0 :   PalacePetscCall(MatDestroy(&A1));
    1031            0 :   PalacePetscCall(MatDestroy(&A2));
    1032            0 : }
    1033              : 
    1034            0 : void SlepcPEPSolverBase::SetNumModes(int num_eig, int num_vec)
    1035              : {
    1036            0 :   PalacePetscCall(PEPSetDimensions(pep, num_eig, (num_vec > 0) ? num_vec : PETSC_DEFAULT,
    1037              :                                    PETSC_DEFAULT));
    1038            0 : }
    1039              : 
    1040            0 : void SlepcPEPSolverBase::SetTol(PetscReal tol)
    1041              : {
    1042            0 :   PalacePetscCall(PEPSetTolerances(pep, tol, PETSC_DEFAULT));
    1043            0 :   PalacePetscCall(PEPSetConvergenceTest(pep, PEP_CONV_REL));
    1044              :   // PalacePetscCall(PEPSetTrackAll(pep, PETSC_TRUE));
    1045            0 : }
    1046              : 
    1047            0 : void SlepcPEPSolverBase::SetMaxIter(int max_it)
    1048              : {
    1049            0 :   PalacePetscCall(
    1050              :       PEPSetTolerances(pep, PETSC_DEFAULT, (max_it > 0) ? max_it : PETSC_DEFAULT));
    1051            0 : }
    1052              : 
    1053            0 : void SlepcPEPSolverBase::SetWhichEigenpairs(EigenvalueSolver::WhichType type)
    1054              : {
    1055            0 :   switch (type)
    1056              :   {
    1057            0 :     case WhichType::LARGEST_MAGNITUDE:
    1058            0 :       PalacePetscCall(PEPSetWhichEigenpairs(pep, PEP_LARGEST_MAGNITUDE));
    1059            0 :       region = false;
    1060            0 :       break;
    1061            0 :     case WhichType::SMALLEST_MAGNITUDE:
    1062            0 :       PalacePetscCall(PEPSetWhichEigenpairs(pep, PEP_SMALLEST_MAGNITUDE));
    1063            0 :       region = false;
    1064            0 :       break;
    1065            0 :     case WhichType::LARGEST_REAL:
    1066            0 :       PalacePetscCall(PEPSetWhichEigenpairs(pep, PEP_LARGEST_REAL));
    1067              :       break;
    1068            0 :     case WhichType::SMALLEST_REAL:
    1069            0 :       PalacePetscCall(PEPSetWhichEigenpairs(pep, PEP_SMALLEST_REAL));
    1070              :       break;
    1071            0 :     case WhichType::LARGEST_IMAGINARY:
    1072            0 :       PalacePetscCall(PEPSetWhichEigenpairs(pep, PEP_LARGEST_IMAGINARY));
    1073              :       break;
    1074            0 :     case WhichType::SMALLEST_IMAGINARY:
    1075            0 :       PalacePetscCall(PEPSetWhichEigenpairs(pep, PEP_SMALLEST_IMAGINARY));
    1076              :       break;
    1077            0 :     case WhichType::TARGET_MAGNITUDE:
    1078            0 :       PalacePetscCall(PEPSetWhichEigenpairs(pep, PEP_TARGET_MAGNITUDE));
    1079            0 :       region = false;
    1080            0 :       break;
    1081            0 :     case WhichType::TARGET_REAL:
    1082            0 :       PalacePetscCall(PEPSetWhichEigenpairs(pep, PEP_TARGET_REAL));
    1083              :       break;
    1084            0 :     case WhichType::TARGET_IMAGINARY:
    1085            0 :       PalacePetscCall(PEPSetWhichEigenpairs(pep, PEP_TARGET_IMAGINARY));
    1086              :       break;
    1087              :   }
    1088            0 : }
    1089              : 
    1090            0 : void SlepcPEPSolverBase::SetProblemType(SlepcEigenvalueSolver::ProblemType type)
    1091              : {
    1092            0 :   switch (type)
    1093              :   {
    1094            0 :     case ProblemType::HERMITIAN:
    1095              :     case ProblemType::GEN_HERMITIAN:
    1096            0 :       PalacePetscCall(PEPSetProblemType(pep, PEP_HERMITIAN));
    1097              :       break;
    1098            0 :     case ProblemType::NON_HERMITIAN:
    1099              :     case ProblemType::GEN_INDEFINITE:
    1100              :     case ProblemType::GEN_NON_HERMITIAN:
    1101              :     case ProblemType::GENERAL:
    1102            0 :       PalacePetscCall(PEPSetProblemType(pep, PEP_GENERAL));
    1103              :       break;
    1104            0 :     case ProblemType::HYPERBOLIC:
    1105            0 :       PalacePetscCall(PEPSetProblemType(pep, PEP_HYPERBOLIC));
    1106              :       break;
    1107            0 :     case ProblemType::GYROSCOPIC:
    1108            0 :       PalacePetscCall(PEPSetProblemType(pep, PEP_GYROSCOPIC));
    1109              :       break;
    1110              :   }
    1111            0 : }
    1112              : 
    1113            0 : void SlepcPEPSolverBase::SetType(SlepcEigenvalueSolver::Type type)
    1114              : {
    1115            0 :   switch (type)
    1116              :   {
    1117            0 :     case Type::TOAR:
    1118            0 :       PalacePetscCall(PEPSetType(pep, PEPTOAR));
    1119              :       break;
    1120            0 :     case Type::STOAR:
    1121            0 :       PalacePetscCall(PEPSetType(pep, PEPSTOAR));
    1122              :       break;
    1123            0 :     case Type::QARNOLDI:
    1124            0 :       PalacePetscCall(PEPSetType(pep, PEPQARNOLDI));
    1125              :       break;
    1126            0 :     case Type::JD:
    1127            0 :       PalacePetscCall(PEPSetType(pep, PEPJD));
    1128            0 :       region = false;
    1129            0 :       break;
    1130            0 :     case Type::KRYLOVSCHUR:
    1131              :     case Type::POWER:
    1132              :     case Type::SUBSPACE:
    1133              :     case Type::SLP:
    1134              :     case Type::NLEIGS:
    1135            0 :       MFEM_ABORT("Eigenvalue solver type not implemented!");
    1136              :       break;
    1137              :   }
    1138            0 : }
    1139              : 
    1140            0 : void SlepcPEPSolverBase::SetInitialSpace(const ComplexVector &v)
    1141              : {
    1142            0 :   MFEM_VERIFY(
    1143              :       A0 && A1 && A2,
    1144              :       "Must call SetOperators before using SetInitialSpace for SLEPc eigenvalue solver!");
    1145            0 :   if (!v0)
    1146              :   {
    1147            0 :     PalacePetscCall(MatCreateVecs(A0, nullptr, &v0));
    1148              :   }
    1149            0 :   PalacePetscCall(ToPetscVec(v, v0));
    1150            0 :   Vec is[1] = {v0};
    1151            0 :   PalacePetscCall(PEPSetInitialSpace(pep, 1, is));
    1152            0 : }
    1153              : 
    1154            0 : void SlepcPEPSolverBase::Customize()
    1155              : {
    1156            0 :   SlepcEigenvalueSolver::Customize();
    1157            0 :   PalacePetscCall(PEPSetTarget(pep, sigma / gamma));
    1158            0 :   if (!cl_custom)
    1159              :   {
    1160            0 :     PalacePetscCall(PEPSetFromOptions(pep));
    1161            0 :     if (print > 0)
    1162              :     {
    1163            0 :       PetscOptionsView(nullptr, PETSC_VIEWER_STDOUT_(GetComm()));
    1164            0 :       Mpi::Print(GetComm(), "\n");
    1165              :     }
    1166            0 :     cl_custom = true;
    1167              :   }
    1168            0 : }
    1169              : 
    1170            0 : int SlepcPEPSolverBase::Solve()
    1171              : {
    1172            0 :   MFEM_VERIFY(A0 && A1 && A2 && opInv, "Operators are not set for SlepcPEPSolverBase!");
    1173              : 
    1174              :   // Solve the eigenvalue problem.
    1175              :   PetscInt num_conv;
    1176            0 :   Customize();
    1177            0 :   PalacePetscCall(PEPSolve(pep));
    1178            0 :   PalacePetscCall(PEPGetConverged(pep, &num_conv));
    1179            0 :   if (print > 0)
    1180              :   {
    1181            0 :     Mpi::Print(GetComm(), "\n");
    1182            0 :     PalacePetscCall(PEPConvergedReasonView(pep, PETSC_VIEWER_STDOUT_(GetComm())));
    1183            0 :     Mpi::Print(GetComm(),
    1184              :                " Total number of linear systems solved: {:d}\n"
    1185              :                " Total number of linear solver iterations: {:d}\n",
    1186            0 :                opInv->NumTotalMult(), opInv->NumTotalMultIterations());
    1187              :   }
    1188              : 
    1189              :   // Compute and store the eigenpair residuals.
    1190            0 :   RescaleEigenvectors(num_conv);
    1191            0 :   return (int)num_conv;
    1192              : }
    1193              : 
    1194            0 : std::complex<double> SlepcPEPSolverBase::GetEigenvalue(int i) const
    1195              : {
    1196            0 :   PetscScalar l;
    1197            0 :   PalacePetscCall(PEPGetEigenpair(pep, i, &l, nullptr, nullptr, nullptr));
    1198            0 :   return l * gamma;
    1199              : }
    1200              : 
    1201            0 : void SlepcPEPSolverBase::GetEigenvector(int i, ComplexVector &x) const
    1202              : {
    1203            0 :   MFEM_VERIFY(
    1204              :       v0,
    1205              :       "Must call SetOperators before using GetEigenvector for SLEPc eigenvalue solver!");
    1206            0 :   PalacePetscCall(PEPGetEigenpair(pep, i, nullptr, nullptr, v0, nullptr));
    1207            0 :   PalacePetscCall(FromPetscVec(v0, x));
    1208            0 :   if (xscale.get()[i] > 0.0)
    1209              :   {
    1210            0 :     x *= xscale.get()[i];
    1211              :   }
    1212            0 : }
    1213              : 
    1214            0 : BV SlepcPEPSolverBase::GetBV() const
    1215              : {
    1216              :   BV bv;
    1217            0 :   PalacePetscCall(PEPGetBV(pep, &bv));
    1218            0 :   return bv;
    1219              : }
    1220              : 
    1221            0 : ST SlepcPEPSolverBase::GetST() const
    1222              : {
    1223              :   ST st;
    1224            0 :   PalacePetscCall(PEPGetST(pep, &st));
    1225            0 :   return st;
    1226              : }
    1227              : 
    1228            0 : RG SlepcPEPSolverBase::GetRG() const
    1229              : {
    1230              :   RG rg;
    1231            0 :   PalacePetscCall(PEPGetRG(pep, &rg));
    1232            0 :   return rg;
    1233              : }
    1234              : 
    1235            0 : SlepcPEPSolver::SlepcPEPSolver(MPI_Comm comm, int print, const std::string &prefix)
    1236            0 :   : SlepcPEPSolverBase(comm, print, prefix)
    1237              : {
    1238            0 :   opK = opC = opM = nullptr;
    1239            0 :   normK = normC = normM = 0.0;
    1240            0 : }
    1241              : 
    1242            0 : void SlepcPEPSolver::SetOperators(const ComplexOperator &K, const ComplexOperator &C,
    1243              :                                   const ComplexOperator &M,
    1244              :                                   EigenvalueSolver::ScaleType type)
    1245              : {
    1246              :   // Construct shell matrices for the scaled operators which define the quadratic polynomial
    1247              :   // eigenvalue problem.
    1248            0 :   const bool first = (opK == nullptr);
    1249            0 :   opK = &K;
    1250            0 :   opC = &C;
    1251            0 :   opM = &M;
    1252              : 
    1253            0 :   if (first)
    1254              :   {
    1255            0 :     const PetscInt n = opK->Height();
    1256            0 :     PalacePetscCall(
    1257              :         MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &A0));
    1258            0 :     PalacePetscCall(
    1259              :         MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &A1));
    1260            0 :     PalacePetscCall(
    1261              :         MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &A2));
    1262            0 :     PalacePetscCall(
    1263              :         MatShellSetOperation(A0, MATOP_MULT, (void (*)(void))__mat_apply_PEP_A0));
    1264            0 :     PalacePetscCall(
    1265              :         MatShellSetOperation(A1, MATOP_MULT, (void (*)(void))__mat_apply_PEP_A1));
    1266            0 :     PalacePetscCall(
    1267              :         MatShellSetOperation(A2, MATOP_MULT, (void (*)(void))__mat_apply_PEP_A2));
    1268            0 :     PalacePetscCall(MatShellSetVecType(A0, PetscVecType()));
    1269            0 :     PalacePetscCall(MatShellSetVecType(A1, PetscVecType()));
    1270            0 :     PalacePetscCall(MatShellSetVecType(A2, PetscVecType()));
    1271            0 :     Mat A[3] = {A0, A1, A2};
    1272            0 :     PalacePetscCall(PEPSetOperators(pep, 3, A));
    1273              :   }
    1274              : 
    1275            0 :   if (first && type != ScaleType::NONE)
    1276              :   {
    1277            0 :     normK = linalg::SpectralNorm(GetComm(), *opK, opK->IsReal());
    1278            0 :     normC = linalg::SpectralNorm(GetComm(), *opC, opC->IsReal());
    1279            0 :     normM = linalg::SpectralNorm(GetComm(), *opM, opM->IsReal());
    1280            0 :     MFEM_VERIFY(normK >= 0.0 && normC >= 0.0 && normM >= 0.0,
    1281              :                 "Invalid matrix norms for PEP scaling!");
    1282            0 :     if (normK > 0 && normC >= 0.0 && normM > 0.0)
    1283              :     {
    1284            0 :       gamma = std::sqrt(normK / normM);
    1285            0 :       delta = 2.0 / (normK + gamma * normC);
    1286              :     }
    1287              :   }
    1288              : 
    1289              :   // Set up workspace.
    1290            0 :   if (!v0)
    1291              :   {
    1292            0 :     PalacePetscCall(MatCreateVecs(A0, nullptr, &v0));
    1293              :   }
    1294            0 :   x1.SetSize(opK->Height());
    1295            0 :   y1.SetSize(opK->Height());
    1296              : 
    1297              :   // Configure linear solver.
    1298            0 :   if (first)
    1299              :   {
    1300            0 :     ConfigurePCShell(GetST(), (void *)this, __pc_apply_PEP);
    1301              :   }
    1302            0 : }
    1303              : 
    1304            0 : void SlepcPEPSolver::SetBMat(const Operator &B)
    1305              : {
    1306            0 :   SlepcEigenvalueSolver::SetBMat(B);
    1307              : 
    1308            0 :   const PetscInt n = B.Height();
    1309            0 :   PalacePetscCall(
    1310              :       MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &B0));
    1311            0 :   PalacePetscCall(MatShellSetOperation(B0, MATOP_MULT, (void (*)(void))__mat_apply_PEP_B));
    1312            0 :   PalacePetscCall(MatShellSetVecType(B0, PetscVecType()));
    1313              : 
    1314            0 :   BV bv = GetBV();
    1315            0 :   PalacePetscCall(BVSetMatrix(bv, B0, PETSC_FALSE));
    1316            0 : }
    1317              : 
    1318            0 : PetscReal SlepcPEPSolver::GetResidualNorm(PetscScalar l, const ComplexVector &x,
    1319              :                                           ComplexVector &r) const
    1320              : {
    1321              :   // Compute the i-th eigenpair residual: || P(λ) x ||₂ = || (K + λ C + λ² M) x ||₂ for
    1322              :   // eigenvalue λ.
    1323            0 :   opK->Mult(x, r);
    1324            0 :   if (opC)
    1325              :   {
    1326            0 :     opC->AddMult(x, r, l);
    1327              :   }
    1328            0 :   opM->AddMult(x, r, l * l);
    1329            0 :   return linalg::Norml2(GetComm(), r);
    1330              : }
    1331              : 
    1332            0 : PetscReal SlepcPEPSolver::GetBackwardScaling(PetscScalar l) const
    1333              : {
    1334              :   // Make sure not to use norms from scaling as this can be confusing if they are different.
    1335              :   // Note that SLEPc typically uses ||.||∞, not Frobenius.
    1336            0 :   if (normK <= 0.0)
    1337              :   {
    1338            0 :     normK = linalg::SpectralNorm(GetComm(), *opK, opK->IsReal());
    1339              :   }
    1340            0 :   if (normC <= 0.0 && opC)
    1341              :   {
    1342            0 :     normC = linalg::SpectralNorm(GetComm(), *opC, opC->IsReal());
    1343              :   }
    1344            0 :   if (normM <= 0.0)
    1345              :   {
    1346            0 :     normM = linalg::SpectralNorm(GetComm(), *opM, opM->IsReal());
    1347              :   }
    1348            0 :   PetscReal t = PetscAbsScalar(l);
    1349            0 :   return normK + t * normC + t * t * normM;
    1350              : }
    1351              : 
    1352              : // NEP specific methods.
    1353              : 
    1354            0 : SlepcNEPSolverBase::SlepcNEPSolverBase(MPI_Comm comm, int print, const std::string &prefix)
    1355            0 :   : SlepcEigenvalueSolver(print)
    1356              : {
    1357            0 :   PalacePetscCall(NEPCreate(comm, &nep));
    1358            0 :   PalacePetscCall(NEPSetOptionsPrefix(nep, prefix.c_str()));
    1359            0 :   if (print > 0)
    1360              :   {
    1361            0 :     std::string opts = "-nep_monitor";
    1362            0 :     if (print > 2)
    1363              :     {
    1364            0 :       opts.append(" -nep_view");
    1365              :     }
    1366            0 :     if (prefix.length() > 0)
    1367              :     {
    1368            0 :       PetscOptionsPrefixPush(nullptr, prefix.c_str());
    1369              :     }
    1370            0 :     PetscOptionsInsertString(nullptr, opts.c_str());
    1371            0 :     if (prefix.length() > 0)
    1372              :     {
    1373            0 :       PetscOptionsPrefixPop(nullptr);
    1374              :     }
    1375              :   }
    1376            0 :   A = J = nullptr;
    1377            0 : }
    1378              : 
    1379            0 : SlepcNEPSolverBase::~SlepcNEPSolverBase()
    1380              : {
    1381            0 :   PalacePetscCall(NEPDestroy(&nep));
    1382            0 :   PalacePetscCall(MatDestroy(&A));
    1383            0 :   PalacePetscCall(MatDestroy(&J));
    1384            0 : }
    1385              : 
    1386            0 : void SlepcNEPSolverBase::SetNumModes(int num_eig, int num_vec)
    1387              : {
    1388            0 :   PalacePetscCall(NEPSetDimensions(nep, num_eig, (num_vec > 0) ? num_vec : PETSC_DEFAULT,
    1389              :                                    PETSC_DEFAULT));
    1390            0 : }
    1391              : 
    1392            0 : void SlepcNEPSolverBase::SetTol(PetscReal tol)
    1393              : {
    1394            0 :   PalacePetscCall(NEPSetTolerances(nep, tol, PETSC_DEFAULT));
    1395            0 :   PalacePetscCall(NEPSetConvergenceTest(nep, NEP_CONV_REL));
    1396            0 : }
    1397              : 
    1398            0 : void SlepcNEPSolverBase::SetMaxIter(int max_it)
    1399              : {
    1400            0 :   PalacePetscCall(
    1401              :       NEPSetTolerances(nep, PETSC_DEFAULT, (max_it > 0) ? max_it : PETSC_DEFAULT));
    1402            0 : }
    1403              : 
    1404            0 : void SlepcNEPSolverBase::SetShiftInvert(std::complex<double> s, bool precond)
    1405              : {
    1406            0 :   sigma = s;  // Wait until solve time to call NEPSetTarget
    1407            0 :   sinvert = false;
    1408            0 : }
    1409              : 
    1410            0 : void SlepcNEPSolverBase::SetWhichEigenpairs(EigenvalueSolver::WhichType type)
    1411              : {
    1412            0 :   switch (type)
    1413              :   {
    1414            0 :     case WhichType::LARGEST_MAGNITUDE:
    1415            0 :       PalacePetscCall(NEPSetWhichEigenpairs(nep, NEP_LARGEST_MAGNITUDE));
    1416            0 :       region = false;
    1417            0 :       break;
    1418            0 :     case WhichType::SMALLEST_MAGNITUDE:
    1419            0 :       PalacePetscCall(NEPSetWhichEigenpairs(nep, NEP_SMALLEST_MAGNITUDE));
    1420            0 :       region = false;
    1421            0 :       break;
    1422            0 :     case WhichType::LARGEST_REAL:
    1423            0 :       PalacePetscCall(NEPSetWhichEigenpairs(nep, NEP_LARGEST_REAL));
    1424              :       break;
    1425            0 :     case WhichType::SMALLEST_REAL:
    1426            0 :       PalacePetscCall(NEPSetWhichEigenpairs(nep, NEP_SMALLEST_REAL));
    1427              :       break;
    1428            0 :     case WhichType::LARGEST_IMAGINARY:
    1429            0 :       PalacePetscCall(NEPSetWhichEigenpairs(nep, NEP_LARGEST_IMAGINARY));
    1430              :       break;
    1431            0 :     case WhichType::SMALLEST_IMAGINARY:
    1432            0 :       PalacePetscCall(NEPSetWhichEigenpairs(nep, NEP_SMALLEST_IMAGINARY));
    1433              :       break;
    1434            0 :     case WhichType::TARGET_MAGNITUDE:
    1435            0 :       PalacePetscCall(NEPSetWhichEigenpairs(nep, NEP_TARGET_MAGNITUDE));
    1436            0 :       region = false;
    1437            0 :       break;
    1438            0 :     case WhichType::TARGET_REAL:
    1439            0 :       PalacePetscCall(NEPSetWhichEigenpairs(nep, NEP_TARGET_REAL));
    1440              :       break;
    1441            0 :     case WhichType::TARGET_IMAGINARY:
    1442            0 :       PalacePetscCall(NEPSetWhichEigenpairs(nep, NEP_TARGET_IMAGINARY));
    1443              :       break;
    1444              :   }
    1445            0 : }
    1446              : 
    1447            0 : void SlepcNEPSolverBase::SetProblemType(SlepcEigenvalueSolver::ProblemType type)
    1448              : {
    1449            0 :   switch (type)
    1450              :   {
    1451            0 :     case ProblemType::GENERAL:
    1452            0 :       PalacePetscCall(NEPSetProblemType(nep, NEP_GENERAL));
    1453              :       break;
    1454            0 :     case ProblemType::HERMITIAN:
    1455              :     case ProblemType::GEN_HERMITIAN:
    1456              :     case ProblemType::NON_HERMITIAN:
    1457              :     case ProblemType::GEN_INDEFINITE:
    1458              :     case ProblemType::GEN_NON_HERMITIAN:
    1459              :     case ProblemType::HYPERBOLIC:
    1460              :     case ProblemType::GYROSCOPIC:
    1461            0 :       MFEM_ABORT("Problem type not implemented!");
    1462              :       break;
    1463              :   }
    1464            0 : }
    1465              : 
    1466            0 : void SlepcNEPSolverBase::SetType(SlepcEigenvalueSolver::Type type)
    1467              : {
    1468            0 :   switch (type)
    1469              :   {
    1470            0 :     case Type::SLP:
    1471            0 :       PalacePetscCall(NEPSetType(nep, NEPSLP));
    1472              :       break;
    1473            0 :     case Type::NLEIGS:
    1474              :     case Type::KRYLOVSCHUR:
    1475              :     case Type::POWER:
    1476              :     case Type::SUBSPACE:
    1477              :     case Type::JD:
    1478              :     case Type::TOAR:
    1479              :     case Type::STOAR:
    1480              :     case Type::QARNOLDI:
    1481            0 :       MFEM_ABORT("Eigenvalue solver type not implemented!");
    1482              :       break;
    1483              :   }
    1484            0 : }
    1485              : 
    1486            0 : void SlepcNEPSolverBase::SetInitialSpace(const ComplexVector &v)
    1487              : {
    1488            0 :   MFEM_VERIFY(
    1489              :       A && J,
    1490              :       "Must call SetOperators before using SetInitialSpace for SLEPc eigenvalue solver!");
    1491            0 :   if (!v0)
    1492              :   {
    1493            0 :     PalacePetscCall(MatCreateVecs(A, nullptr, &v0));
    1494              :   }
    1495            0 :   PalacePetscCall(ToPetscVec(v, v0));
    1496            0 :   Vec is[1] = {v0};
    1497            0 :   PalacePetscCall(NEPSetInitialSpace(nep, 1, is));
    1498            0 : }
    1499              : 
    1500            0 : void SlepcNEPSolverBase::Customize()
    1501              : {
    1502              :   // Configure the region based on the given target if necessary.
    1503            0 :   PalacePetscCall(NEPSetTarget(nep, sigma));
    1504            0 :   if (!cl_custom)
    1505              :   {
    1506            0 :     PalacePetscCall(NEPSetFromOptions(nep));
    1507            0 :     if (print > 0)
    1508              :     {
    1509            0 :       PetscOptionsView(nullptr, PETSC_VIEWER_STDOUT_(GetComm()));
    1510            0 :       Mpi::Print(GetComm(), "\n");
    1511              :     }
    1512            0 :     cl_custom = true;
    1513              :   }
    1514            0 : }
    1515              : 
    1516            0 : int SlepcNEPSolverBase::Solve()
    1517              : {
    1518            0 :   MFEM_VERIFY(A && J && opInv, "Operators are not set for SlepcNEPSolverBase!");
    1519              : 
    1520              :   // Solve the eigenvalue problem.
    1521              :   perm.reset();
    1522              :   PetscInt num_conv;
    1523            0 :   Customize();
    1524            0 :   PalacePetscCall(NEPSolve(nep));
    1525            0 :   PalacePetscCall(NEPGetConverged(nep, &num_conv));
    1526            0 :   if (print > 0)
    1527              :   {
    1528            0 :     Mpi::Print(GetComm(), "\n");
    1529            0 :     PalacePetscCall(NEPConvergedReasonView(nep, PETSC_VIEWER_STDOUT_(GetComm())));
    1530            0 :     Mpi::Print(GetComm(),
    1531              :                " Total number of linear systems solved: {:d}\n"
    1532              :                " Total number of linear solver iterations: {:d}\n",
    1533            0 :                opInv->NumTotalMult(), opInv->NumTotalMultIterations());
    1534              :   }
    1535              : 
    1536              :   // Compute and store the ordered eigenpair residuals.
    1537            0 :   const int nev = (int)num_conv;
    1538            0 :   perm = std::make_unique<int[]>(nev);
    1539            0 :   std::vector<std::complex<double>> eig(nev);
    1540            0 :   for (int i = 0; i < nev; i++)
    1541              :   {
    1542            0 :     PetscScalar l;
    1543            0 :     PalacePetscCall(NEPGetEigenpair(nep, i, &l, nullptr, nullptr, nullptr));
    1544            0 :     eig[i] = l;
    1545            0 :     perm[i] = i;
    1546              :   }
    1547              :   // Sort by ascending imaginary component.
    1548            0 :   std::sort(perm.get(), perm.get() + nev,
    1549            0 :             [&eig](auto l, auto r) { return eig[l].imag() < eig[r].imag(); });
    1550            0 :   RescaleEigenvectors(nev);
    1551            0 :   return nev;
    1552              : }
    1553              : 
    1554            0 : std::complex<double> SlepcNEPSolverBase::GetEigenvalue(int i) const
    1555              : {
    1556            0 :   PetscScalar l;
    1557            0 :   const int &j = perm.get()[i];
    1558            0 :   PalacePetscCall(NEPGetEigenpair(nep, j, &l, nullptr, nullptr, nullptr));
    1559            0 :   return l;
    1560              : }
    1561              : 
    1562            0 : void SlepcNEPSolverBase::GetEigenvector(int i, ComplexVector &x) const
    1563              : {
    1564            0 :   MFEM_VERIFY(
    1565              :       v0,
    1566              :       "Must call SetOperators before using GetEigenvector for SLEPc eigenvalue solver!");
    1567            0 :   const int &j = perm.get()[i];
    1568            0 :   PalacePetscCall(NEPGetEigenpair(nep, j, nullptr, nullptr, v0, nullptr));
    1569            0 :   PalacePetscCall(FromPetscVec(v0, x));
    1570            0 :   if (xscale.get()[i] > 0.0)
    1571              :   {
    1572            0 :     x *= xscale.get()[i];
    1573              :   }
    1574            0 : }
    1575              : 
    1576            0 : BV SlepcNEPSolverBase::GetBV() const
    1577              : {
    1578              :   BV bv;
    1579            0 :   PalacePetscCall(NEPGetBV(nep, &bv));
    1580            0 :   return bv;
    1581              : }
    1582              : 
    1583            0 : ST SlepcNEPSolverBase::GetST() const
    1584              : {
    1585              :   ST st = nullptr;
    1586              :   // NEPGetST does not exist.
    1587            0 :   return st;
    1588              : }
    1589              : 
    1590            0 : RG SlepcNEPSolverBase::GetRG() const
    1591              : {
    1592              :   RG rg;
    1593            0 :   PalacePetscCall(NEPGetRG(nep, &rg));
    1594            0 :   return rg;
    1595              : }
    1596              : 
    1597            0 : SlepcNEPSolver::SlepcNEPSolver(MPI_Comm comm, int print, const std::string &prefix)
    1598            0 :   : SlepcNEPSolverBase(comm, print, prefix)
    1599              : {
    1600            0 :   opK = opC = opM = nullptr;
    1601            0 :   normK = normC = normM = 0.0;
    1602            0 : }
    1603              : 
    1604            0 : void SlepcNEPSolver::SetExtraSystemMatrix(
    1605              :     std::function<std::unique_ptr<ComplexOperator>(double)> A2)
    1606              : {
    1607            0 :   funcA2 = A2;
    1608            0 : }
    1609              : 
    1610            0 : void SlepcNEPSolver::SetPreconditionerUpdate(
    1611              :     std::function<std::unique_ptr<ComplexOperator>(
    1612              :         std::complex<double>, std::complex<double>, std::complex<double>, double)>
    1613              :         P)
    1614              : {
    1615            0 :   funcP = P;
    1616            0 : }
    1617              : 
    1618            0 : void SlepcNEPSolver::SetOperators(const ComplexOperator &K, const ComplexOperator &M,
    1619              :                                   EigenvalueSolver::ScaleType type)
    1620              : {
    1621              :   // Construct shell matrices for the scaled operators which define the quadratic polynomial
    1622              :   // eigenvalue problem.
    1623            0 :   const bool first = (opK == nullptr);
    1624            0 :   opK = &K;
    1625            0 :   opM = &M;
    1626              : 
    1627            0 :   if (first)
    1628              :   {
    1629            0 :     const PetscInt n = opK->Height();
    1630            0 :     PalacePetscCall(
    1631              :         MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &A));
    1632            0 :     PalacePetscCall(
    1633              :         MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &J));
    1634            0 :     PalacePetscCall(MatShellSetOperation(A, MATOP_MULT, (void (*)(void))__mat_apply_NEP_A));
    1635            0 :     PalacePetscCall(MatShellSetOperation(J, MATOP_MULT, (void (*)(void))__mat_apply_NEP_J));
    1636            0 :     PalacePetscCall(MatShellSetVecType(A, PetscVecType()));
    1637            0 :     PalacePetscCall(MatShellSetVecType(J, PetscVecType()));
    1638            0 :     PalacePetscCall(NEPSetFunction(nep, A, A, __form_NEP_function, NULL));
    1639            0 :     PalacePetscCall(NEPSetJacobian(nep, J, __form_NEP_jacobian, NULL));
    1640              :   }
    1641              : 
    1642            0 :   if (first && type != ScaleType::NONE)
    1643              :   {
    1644            0 :     normK = linalg::SpectralNorm(GetComm(), *opK, opK->IsReal());
    1645            0 :     normM = linalg::SpectralNorm(GetComm(), *opM, opM->IsReal());
    1646            0 :     MFEM_VERIFY(normK >= 0.0 && normM >= 0.0, "Invalid matrix norms for NEP scaling!");
    1647            0 :     if (normK > 0 && normM > 0.0)
    1648              :     {
    1649            0 :       gamma = std::sqrt(normK / normM);
    1650            0 :       delta = 2.0 / normK;
    1651              :     }
    1652              :   }
    1653              : 
    1654              :   // Set up workspace.
    1655            0 :   if (!v0)
    1656              :   {
    1657            0 :     PalacePetscCall(MatCreateVecs(A, nullptr, &v0));
    1658              :   }
    1659            0 :   x1.SetSize(opK->Height());
    1660            0 :   y1.SetSize(opK->Height());
    1661              : 
    1662              :   // Configure linear solver.
    1663            0 :   if (first)
    1664              :   {
    1665              :     // SLP.
    1666              :     PC pc;
    1667              :     KSP ksp;
    1668              :     EPS eps;
    1669            0 :     PalacePetscCall(NEPSLPGetKSP(nep, &ksp));
    1670            0 :     PalacePetscCall(KSPSetType(ksp, KSPPREONLY));
    1671            0 :     PalacePetscCall(NEPSLPGetEPS(nep, &eps));
    1672            0 :     PalacePetscCall(EPSSetType(eps, EPSKRYLOVSCHUR));
    1673            0 :     PalacePetscCall(KSPGetPC(ksp, &pc));
    1674            0 :     PalacePetscCall(PCSetType(pc, PCSHELL));
    1675            0 :     PalacePetscCall(PCShellSetContext(pc, (void *)this));
    1676            0 :     PalacePetscCall(PCShellSetApply(pc, __pc_apply_NEP));
    1677              :   }
    1678            0 : }
    1679              : 
    1680            0 : void SlepcNEPSolver::SetOperators(const ComplexOperator &K, const ComplexOperator &C,
    1681              :                                   const ComplexOperator &M,
    1682              :                                   EigenvalueSolver::ScaleType type)
    1683              : {
    1684              :   // Construct shell matrices for the scaled operators which define the quadratic polynomial
    1685              :   // eigenvalue problem.
    1686            0 :   const bool first = (opK == nullptr);
    1687            0 :   opK = &K;
    1688            0 :   opC = &C;
    1689            0 :   opM = &M;
    1690              : 
    1691            0 :   if (first)
    1692              :   {
    1693            0 :     const PetscInt n = opK->Height();
    1694            0 :     PalacePetscCall(
    1695              :         MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &A));
    1696            0 :     PalacePetscCall(
    1697              :         MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &J));
    1698            0 :     PalacePetscCall(MatShellSetOperation(A, MATOP_MULT, (void (*)(void))__mat_apply_NEP_A));
    1699            0 :     PalacePetscCall(MatShellSetOperation(J, MATOP_MULT, (void (*)(void))__mat_apply_NEP_J));
    1700            0 :     PalacePetscCall(MatShellSetVecType(A, PetscVecType()));
    1701            0 :     PalacePetscCall(MatShellSetVecType(J, PetscVecType()));
    1702            0 :     PalacePetscCall(NEPSetFunction(nep, A, A, __form_NEP_function, NULL));
    1703            0 :     PalacePetscCall(NEPSetJacobian(nep, J, __form_NEP_jacobian, NULL));
    1704              :   }
    1705              : 
    1706            0 :   if (first && type != ScaleType::NONE)
    1707              :   {
    1708            0 :     normK = linalg::SpectralNorm(GetComm(), *opK, opK->IsReal());
    1709            0 :     normC = linalg::SpectralNorm(GetComm(), *opC, opC->IsReal());
    1710            0 :     normM = linalg::SpectralNorm(GetComm(), *opM, opM->IsReal());
    1711            0 :     MFEM_VERIFY(normK >= 0.0 && normC >= 0.0 && normM >= 0.0,
    1712              :                 "Invalid matrix norms for NEP scaling!");
    1713            0 :     if (normK > 0 && normC >= 0.0 && normM > 0.0)
    1714              :     {
    1715            0 :       gamma = std::sqrt(normK / normM);
    1716            0 :       delta = 2.0 / (normK + gamma * normC);
    1717              :     }
    1718              :   }
    1719              : 
    1720              :   // Set up workspace.
    1721            0 :   if (!v0)
    1722              :   {
    1723            0 :     PalacePetscCall(MatCreateVecs(A, nullptr, &v0));
    1724              :   }
    1725            0 :   x1.SetSize(opK->Height());
    1726            0 :   y1.SetSize(opK->Height());
    1727              : 
    1728              :   // Configure linear solver.
    1729            0 :   if (first)
    1730              :   {
    1731              :     // SLP.
    1732              :     PC pc;
    1733              :     KSP ksp;
    1734              :     EPS eps;
    1735            0 :     PalacePetscCall(NEPSLPGetKSP(nep, &ksp));
    1736            0 :     PalacePetscCall(KSPSetType(ksp, KSPPREONLY));
    1737            0 :     PalacePetscCall(NEPSLPGetEPS(nep, &eps));
    1738            0 :     PalacePetscCall(EPSSetType(eps, EPSKRYLOVSCHUR));
    1739            0 :     PalacePetscCall(KSPGetPC(ksp, &pc));
    1740            0 :     PalacePetscCall(PCSetType(pc, PCSHELL));
    1741            0 :     PalacePetscCall(PCShellSetContext(pc, (void *)this));
    1742            0 :     PalacePetscCall(PCShellSetApply(pc, __pc_apply_NEP));
    1743              :   }
    1744            0 : }
    1745              : 
    1746            0 : void SlepcNEPSolver::SetBMat(const Operator &B)
    1747              : {
    1748            0 :   SlepcEigenvalueSolver::SetBMat(B);
    1749              : 
    1750            0 :   const PetscInt n = B.Height();
    1751            0 :   PalacePetscCall(
    1752              :       MatCreateShell(GetComm(), n, n, PETSC_DECIDE, PETSC_DECIDE, (void *)this, &B0));
    1753            0 :   PalacePetscCall(MatShellSetOperation(B0, MATOP_MULT, (void (*)(void))__mat_apply_NEP_B));
    1754            0 :   PalacePetscCall(MatShellSetVecType(B0, PetscVecType()));
    1755              : 
    1756            0 :   BV bv = GetBV();
    1757            0 :   PalacePetscCall(BVSetMatrix(bv, B0, PETSC_FALSE));
    1758            0 : }
    1759              : 
    1760            0 : PetscReal SlepcNEPSolver::GetResidualNorm(PetscScalar l, const ComplexVector &x,
    1761              :                                           ComplexVector &r) const
    1762              : {
    1763              :   // Compute the i-th eigenpair residual: || P(λ) x ||₂ = || (K + λ C + λ² M) x ||₂ for
    1764              :   // eigenvalue λ.
    1765            0 :   opK->Mult(x, r);
    1766            0 :   if (opC)
    1767              :   {
    1768            0 :     opC->AddMult(x, r, l);
    1769              :   }
    1770            0 :   opM->AddMult(x, r, l * l);
    1771            0 :   if (funcA2)
    1772              :   {
    1773            0 :     auto A2 = (*funcA2)(std::abs(l.imag()));
    1774            0 :     A2->AddMult(x, r, 1.0 + 0.0i);
    1775              :   }
    1776            0 :   return linalg::Norml2(GetComm(), r);
    1777              : }
    1778              : 
    1779            0 : PetscReal SlepcNEPSolver::GetBackwardScaling(PetscScalar l) const
    1780              : {
    1781              :   // Make sure not to use norms from scaling as this can be confusing if they are different.
    1782              :   // Note that SLEPc typically uses ||.||∞, not Frobenius.
    1783            0 :   if (normK <= 0.0)
    1784              :   {
    1785            0 :     normK = linalg::SpectralNorm(GetComm(), *opK, opK->IsReal());
    1786              :   }
    1787            0 :   if (normC <= 0.0 && opC)
    1788              :   {
    1789            0 :     normC = linalg::SpectralNorm(GetComm(), *opC, opC->IsReal());
    1790              :   }
    1791            0 :   if (normM <= 0.0)
    1792              :   {
    1793            0 :     normM = linalg::SpectralNorm(GetComm(), *opM, opM->IsReal());
    1794              :   }
    1795            0 :   PetscReal t = PetscAbsScalar(l);
    1796            0 :   return normK + t * normC + t * t * normM;
    1797              : }
    1798              : 
    1799              : }  // namespace palace::slepc
    1800              : 
    1801            0 : PetscErrorCode __mat_apply_EPS_A0(Mat A, Vec x, Vec y)
    1802              : {
    1803              :   PetscFunctionBeginUser;
    1804              :   palace::slepc::SlepcEPSSolver *ctx;
    1805            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    1806            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    1807              : 
    1808            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    1809            0 :   ctx->opK->Mult(ctx->x1, ctx->y1);
    1810            0 :   ctx->y1 *= ctx->delta;
    1811            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    1812              : 
    1813              :   PetscFunctionReturn(PETSC_SUCCESS);
    1814              : }
    1815              : 
    1816            0 : PetscErrorCode __mat_apply_EPS_A1(Mat A, Vec x, Vec y)
    1817              : {
    1818              :   PetscFunctionBeginUser;
    1819              :   palace::slepc::SlepcEPSSolver *ctx;
    1820            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    1821            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    1822              : 
    1823            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    1824            0 :   ctx->opM->Mult(ctx->x1, ctx->y1);
    1825            0 :   ctx->y1 *= ctx->delta * ctx->gamma;
    1826            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    1827              : 
    1828              :   PetscFunctionReturn(PETSC_SUCCESS);
    1829              : }
    1830              : 
    1831            0 : PetscErrorCode __mat_apply_EPS_B(Mat A, Vec x, Vec y)
    1832              : {
    1833              :   PetscFunctionBeginUser;
    1834              :   palace::slepc::SlepcEPSSolver *ctx;
    1835            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    1836            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    1837              : 
    1838            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    1839            0 :   ctx->opB->Mult(ctx->x1.Real(), ctx->y1.Real());
    1840            0 :   ctx->opB->Mult(ctx->x1.Imag(), ctx->y1.Imag());
    1841            0 :   ctx->y1 *= ctx->delta * ctx->gamma;
    1842            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    1843              : 
    1844              :   PetscFunctionReturn(PETSC_SUCCESS);
    1845              : }
    1846              : 
    1847            0 : PetscErrorCode __pc_apply_EPS(PC pc, Vec x, Vec y)
    1848              : {
    1849              :   // Solve the linear system associated with the generalized eigenvalue problem: y =
    1850              :   // M⁻¹ x, or shift-and-invert spectral transformation: y = (K - σ M)⁻¹ x . Enforces the
    1851              :   // divergence-free constraint using the supplied projector.
    1852              :   PetscFunctionBeginUser;
    1853              :   palace::slepc::SlepcEPSSolver *ctx;
    1854            0 :   PetscCall(PCShellGetContext(pc, (void **)&ctx));
    1855            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell PC context for SLEPc!");
    1856              : 
    1857            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    1858            0 :   ctx->opInv->Mult(ctx->x1, ctx->y1);
    1859            0 :   if (!ctx->sinvert)
    1860              :   {
    1861            0 :     ctx->y1 *= 1.0 / (ctx->delta * ctx->gamma);
    1862              :   }
    1863              :   else
    1864              :   {
    1865            0 :     ctx->y1 *= 1.0 / ctx->delta;
    1866              :   }
    1867            0 :   if (ctx->opProj)
    1868              :   {
    1869              :     // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y1));
    1870            0 :     ctx->opProj->Mult(ctx->y1);
    1871              :     // Mpi::Print(" After projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y1));
    1872              :   }
    1873            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    1874              : 
    1875              :   PetscFunctionReturn(PETSC_SUCCESS);
    1876              : }
    1877              : 
    1878            0 : PetscErrorCode __mat_apply_PEPLinear_L0(Mat A, Vec x, Vec y)
    1879              : {
    1880              :   // Apply the linearized operator L₀ = [  0  I ]
    1881              :   //                                    [ -K -C ] .
    1882              :   PetscFunctionBeginUser;
    1883              :   palace::slepc::SlepcPEPLinearSolver *ctx;
    1884            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    1885            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    1886            0 :   PetscCall(FromPetscVec(x, ctx->x1, ctx->x2));
    1887            0 :   ctx->y1 = ctx->x2;
    1888            0 :   if (ctx->opC)
    1889              :   {
    1890            0 :     ctx->opC->Mult(ctx->x2, ctx->y2);
    1891              :   }
    1892              :   else
    1893              :   {
    1894            0 :     ctx->y2 = 0.0;
    1895              :   }
    1896            0 :   ctx->y2 *= ctx->gamma;
    1897            0 :   ctx->opK->AddMult(ctx->x1, ctx->y2, std::complex<double>(1.0, 0.0));
    1898            0 :   ctx->y2 *= -ctx->delta;
    1899            0 :   PetscCall(ToPetscVec(ctx->y1, ctx->y2, y));
    1900              : 
    1901              :   PetscFunctionReturn(PETSC_SUCCESS);
    1902              : }
    1903              : 
    1904            0 : PetscErrorCode __mat_apply_PEPLinear_L1(Mat A, Vec x, Vec y)
    1905              : {
    1906              :   // Apply the linearized operator L₁ = [ I  0 ]
    1907              :   //                                    [ 0  M ] .
    1908              :   PetscFunctionBeginUser;
    1909              :   palace::slepc::SlepcPEPLinearSolver *ctx;
    1910            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    1911            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    1912              : 
    1913            0 :   PetscCall(FromPetscVec(x, ctx->x1, ctx->x2));
    1914            0 :   ctx->y1 = ctx->x1;
    1915            0 :   ctx->opM->Mult(ctx->x2, ctx->y2);
    1916            0 :   ctx->y2 *= ctx->delta * ctx->gamma * ctx->gamma;
    1917            0 :   PetscCall(ToPetscVec(ctx->y1, ctx->y2, y));
    1918              : 
    1919              :   PetscFunctionReturn(PETSC_SUCCESS);
    1920              : }
    1921              : 
    1922            0 : PetscErrorCode __mat_apply_PEPLinear_B(Mat A, Vec x, Vec y)
    1923              : {
    1924              :   PetscFunctionBeginUser;
    1925              :   palace::slepc::SlepcPEPLinearSolver *ctx;
    1926            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    1927            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    1928              : 
    1929            0 :   PetscCall(FromPetscVec(x, ctx->x1, ctx->x2));
    1930            0 :   ctx->opB->Mult(ctx->x1.Real(), ctx->y1.Real());
    1931            0 :   ctx->opB->Mult(ctx->x1.Imag(), ctx->y1.Imag());
    1932            0 :   ctx->opB->Mult(ctx->x2.Real(), ctx->y2.Real());
    1933            0 :   ctx->opB->Mult(ctx->x2.Imag(), ctx->y2.Imag());
    1934            0 :   ctx->y1 *= ctx->delta * ctx->gamma * ctx->gamma;
    1935            0 :   ctx->y2 *= ctx->delta * ctx->gamma * ctx->gamma;
    1936            0 :   PetscCall(ToPetscVec(ctx->y1, ctx->y2, y));
    1937              : 
    1938              :   PetscFunctionReturn(PETSC_SUCCESS);
    1939              : }
    1940              : 
    1941            0 : PetscErrorCode __pc_apply_PEPLinear(PC pc, Vec x, Vec y)
    1942              : {
    1943              :   // Solve the linear system associated with the generalized eigenvalue problem after
    1944              :   // linearization: y = L₁⁻¹ x, or with the shift-and-invert spectral transformation:
    1945              :   // y = (L₀ - σ L₁)⁻¹ x, with:
    1946              :   //               L₀ = [  0  I ]    L₁ = [ I  0 ]
    1947              :   //                    [ -K -C ] ,       [ 0  M ] .
    1948              :   // Enforces the divergence-free constraint using the supplied projector.
    1949              :   PetscFunctionBeginUser;
    1950              :   palace::slepc::SlepcPEPLinearSolver *ctx;
    1951            0 :   PetscCall(PCShellGetContext(pc, (void **)&ctx));
    1952            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell PC context for SLEPc!");
    1953              : 
    1954            0 :   PetscCall(FromPetscVec(x, ctx->x1, ctx->x2));
    1955            0 :   if (!ctx->sinvert)
    1956              :   {
    1957            0 :     ctx->y1 = ctx->x1;
    1958            0 :     if (ctx->opProj)
    1959              :     {
    1960              :       // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y1));
    1961            0 :       ctx->opProj->Mult(ctx->y1);
    1962              :       // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y1));
    1963              :     }
    1964              : 
    1965            0 :     ctx->opInv->Mult(ctx->x2, ctx->y2);
    1966            0 :     ctx->y2 *= 1.0 / (ctx->delta * ctx->gamma * ctx->gamma);
    1967            0 :     if (ctx->opProj)
    1968              :     {
    1969              :       // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y2));
    1970            0 :       ctx->opProj->Mult(ctx->y2);
    1971              :       // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y2));
    1972              :     }
    1973              :   }
    1974              :   else
    1975              :   {
    1976            0 :     ctx->y1.AXPBY(-ctx->sigma / (ctx->delta * ctx->gamma), ctx->x2, 0.0);  // Temporarily
    1977            0 :     ctx->opK->AddMult(ctx->x1, ctx->y1, std::complex<double>(1.0, 0.0));
    1978            0 :     ctx->opInv->Mult(ctx->y1, ctx->y2);
    1979            0 :     if (ctx->opProj)
    1980              :     {
    1981              :       // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y2));
    1982            0 :       ctx->opProj->Mult(ctx->y2);
    1983              :       // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y2));
    1984              :     }
    1985              : 
    1986            0 :     ctx->y1.AXPBYPCZ(ctx->gamma / ctx->sigma, ctx->y2, -ctx->gamma / ctx->sigma, ctx->x1,
    1987              :                      0.0);
    1988            0 :     if (ctx->opProj)
    1989              :     {
    1990              :       // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y1));
    1991            0 :       ctx->opProj->Mult(ctx->y1);
    1992              :       // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y1));
    1993              :     }
    1994              :   }
    1995            0 :   PetscCall(ToPetscVec(ctx->y1, ctx->y2, y));
    1996              : 
    1997              :   PetscFunctionReturn(PETSC_SUCCESS);
    1998              : }
    1999              : 
    2000            0 : PetscErrorCode __mat_apply_PEP_A0(Mat A, Vec x, Vec y)
    2001              : {
    2002              :   PetscFunctionBeginUser;
    2003              :   palace::slepc::SlepcPEPSolver *ctx;
    2004            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    2005            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    2006              : 
    2007            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    2008            0 :   ctx->opK->Mult(ctx->x1, ctx->y1);
    2009            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    2010              : 
    2011              :   PetscFunctionReturn(PETSC_SUCCESS);
    2012              : }
    2013              : 
    2014            0 : PetscErrorCode __mat_apply_PEP_A1(Mat A, Vec x, Vec y)
    2015              : {
    2016              :   PetscFunctionBeginUser;
    2017              :   palace::slepc::SlepcPEPSolver *ctx;
    2018            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    2019            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    2020              : 
    2021            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    2022            0 :   if (ctx->opC)
    2023              :   {
    2024            0 :     ctx->opC->Mult(ctx->x1, ctx->y1);
    2025              :   }
    2026              :   else
    2027              :   {
    2028            0 :     ctx->y1 = 0.0;
    2029              :   }
    2030            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    2031              : 
    2032              :   PetscFunctionReturn(PETSC_SUCCESS);
    2033              : }
    2034              : 
    2035            0 : PetscErrorCode __mat_apply_PEP_A2(Mat A, Vec x, Vec y)
    2036              : {
    2037              :   PetscFunctionBeginUser;
    2038              :   palace::slepc::SlepcPEPSolver *ctx;
    2039            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    2040            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    2041              : 
    2042            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    2043            0 :   ctx->opM->Mult(ctx->x1, ctx->y1);
    2044            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    2045              : 
    2046              :   PetscFunctionReturn(PETSC_SUCCESS);
    2047              : }
    2048              : 
    2049            0 : PetscErrorCode __mat_apply_PEP_B(Mat A, Vec x, Vec y)
    2050              : {
    2051              :   PetscFunctionBeginUser;
    2052              :   palace::slepc::SlepcPEPSolver *ctx;
    2053            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    2054            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    2055              : 
    2056            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    2057            0 :   ctx->opB->Mult(ctx->x1.Real(), ctx->y1.Real());
    2058            0 :   ctx->opB->Mult(ctx->x1.Imag(), ctx->y1.Imag());
    2059            0 :   ctx->y1 *= ctx->delta * ctx->gamma;
    2060            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    2061              : 
    2062              :   PetscFunctionReturn(PETSC_SUCCESS);
    2063              : }
    2064              : 
    2065            0 : PetscErrorCode __pc_apply_PEP(PC pc, Vec x, Vec y)
    2066              : {
    2067              :   // Solve the linear system associated with the generalized eigenvalue problem: y = M⁻¹ x,
    2068              :   // or shift-and-invert spectral transformation: y = P(σ)⁻¹ x . Enforces the divergence-
    2069              :   // free constraint using the supplied projector.
    2070              :   PetscFunctionBeginUser;
    2071              :   palace::slepc::SlepcPEPSolver *ctx;
    2072            0 :   PetscCall(PCShellGetContext(pc, (void **)&ctx));
    2073            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell PC context for SLEPc!");
    2074              : 
    2075            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    2076            0 :   ctx->opInv->Mult(ctx->x1, ctx->y1);
    2077            0 :   if (!ctx->sinvert)
    2078              :   {
    2079            0 :     ctx->y1 *= 1.0 / (ctx->delta * ctx->gamma * ctx->gamma);
    2080              :   }
    2081              :   else
    2082              :   {
    2083            0 :     ctx->y1 *= 1.0 / ctx->delta;
    2084              :   }
    2085            0 :   if (ctx->opProj)
    2086              :   {
    2087              :     // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y1));
    2088            0 :     ctx->opProj->Mult(ctx->y1);
    2089              :     // Mpi::Print(" After projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y1));
    2090              :   }
    2091            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    2092              : 
    2093              :   PetscFunctionReturn(PETSC_SUCCESS);
    2094              : }
    2095              : 
    2096            0 : PetscErrorCode __mat_apply_NEP_A(Mat A, Vec x, Vec y)
    2097              : {
    2098              :   PetscFunctionBeginUser;
    2099              :   palace::slepc::SlepcNEPSolver *ctx;
    2100            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    2101            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    2102            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    2103            0 :   ctx->opA->Mult(ctx->x1, ctx->y1);
    2104            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    2105              :   PetscFunctionReturn(PETSC_SUCCESS);
    2106              : }
    2107              : 
    2108            0 : PetscErrorCode __mat_apply_NEP_J(Mat J, Vec x, Vec y)
    2109              : {
    2110              :   PetscFunctionBeginUser;
    2111              :   palace::slepc::SlepcNEPSolver *ctx;
    2112            0 :   PetscCall(MatShellGetContext(J, (void **)&ctx));
    2113            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    2114            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    2115            0 :   ctx->opJ->Mult(ctx->x1, ctx->y1);
    2116            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    2117              :   PetscFunctionReturn(PETSC_SUCCESS);
    2118              : }
    2119              : 
    2120            0 : PetscErrorCode __mat_apply_NEP_B(Mat A, Vec x, Vec y)
    2121              : {
    2122              :   PetscFunctionBeginUser;
    2123              :   palace::slepc::SlepcNEPSolver *ctx;
    2124            0 :   PetscCall(MatShellGetContext(A, (void **)&ctx));
    2125            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell matrix context for SLEPc!");
    2126            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    2127            0 :   ctx->opB->Mult(ctx->x1.Real(), ctx->y1.Real());
    2128            0 :   ctx->opB->Mult(ctx->x1.Imag(), ctx->y1.Imag());
    2129            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    2130              :   PetscFunctionReturn(PETSC_SUCCESS);
    2131              : }
    2132              : 
    2133            0 : PetscErrorCode __pc_apply_NEP(PC pc, Vec x, Vec y)
    2134              : {
    2135              :   PetscFunctionBeginUser;
    2136              :   palace::slepc::SlepcNEPSolver *ctx;
    2137            0 :   PetscCall(PCShellGetContext(pc, (void **)&ctx));
    2138            0 :   MFEM_VERIFY(ctx, "Invalid PETSc shell PC context for SLEPc!");
    2139            0 :   PetscCall(FromPetscVec(x, ctx->x1));
    2140              :   // Updating PC for new λ is needed for SLP, but should not be done for NLEIGS.
    2141            0 :   if (ctx->new_lambda && !ctx->first_pc)
    2142              :   {
    2143            0 :     if (ctx->lambda.imag() == 0.0)
    2144            0 :       ctx->lambda = ctx->sigma;
    2145            0 :     ctx->opA2_pc = (*ctx->funcA2)(std::abs(ctx->lambda.imag()));
    2146            0 :     ctx->opA_pc = palace::BuildParSumOperator(
    2147            0 :         {1.0 + 0.0i, ctx->lambda, ctx->lambda * ctx->lambda, 1.0 + 0.0i},
    2148            0 :         {ctx->opK, ctx->opC, ctx->opM, ctx->opA2_pc.get()}, true);
    2149            0 :     ctx->opP_pc = (*ctx->funcP)(std::complex<double>(1.0, 0.0), ctx->lambda,
    2150              :                                 ctx->lambda * ctx->lambda, ctx->lambda.imag());
    2151            0 :     ctx->opInv->SetOperators(*ctx->opA_pc, *ctx->opP_pc);
    2152            0 :     ctx->new_lambda = false;
    2153              :   }
    2154            0 :   else if (ctx->first_pc)
    2155              :   {
    2156            0 :     ctx->first_pc = false;
    2157            0 :     ctx->new_lambda = false;
    2158              :   }
    2159            0 :   ctx->opInv->Mult(ctx->x1, ctx->y1);
    2160            0 :   if (ctx->opProj)
    2161              :   {
    2162              :     // Mpi::Print(" Before projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y1));
    2163            0 :     ctx->opProj->Mult(ctx->y1);
    2164              :     // Mpi::Print(" After projection: {:e}\n", linalg::Norml2(ctx->GetComm(), ctx->y1));
    2165              :   }
    2166            0 :   PetscCall(ToPetscVec(ctx->y1, y));
    2167              :   PetscFunctionReturn(PETSC_SUCCESS);
    2168              : }
    2169              : 
    2170            0 : PetscErrorCode __form_NEP_function(NEP nep, PetscScalar lambda, Mat fun, Mat B, void *ctx)
    2171              : {
    2172              :   PetscFunctionBeginUser;
    2173              :   palace::slepc::SlepcNEPSolver *ctxF;
    2174            0 :   PetscCall(MatShellGetContext(fun, (void **)&ctxF));
    2175              :   // A(λ) = K + λ C + λ² M + A2(Im{λ}).
    2176            0 :   ctxF->opA2 = (*ctxF->funcA2)(std::abs(lambda.imag()));
    2177            0 :   ctxF->opA = palace::BuildParSumOperator(
    2178            0 :       {1.0 + 0.0i, lambda, lambda * lambda, 1.0 + 0.0i},
    2179            0 :       {ctxF->opK, ctxF->opC, ctxF->opM, ctxF->opA2.get()}, true);
    2180            0 :   ctxF->lambda = lambda;
    2181            0 :   ctxF->new_lambda = true;  // flag to update the preconditioner in SLP
    2182            0 :   PetscFunctionReturn(PETSC_SUCCESS);
    2183              : }
    2184              : 
    2185            0 : PetscErrorCode __form_NEP_jacobian(NEP nep, PetscScalar lambda, Mat fun, void *ctx)
    2186              : {
    2187              :   PetscFunctionBeginUser;
    2188              :   palace::slepc::SlepcNEPSolver *ctxF;
    2189            0 :   PetscCall(MatShellGetContext(fun, (void **)&ctxF));
    2190              :   // A(λ) = K + λ C + λ² M + A2(Im{λ}).
    2191              :   // J(λ) = C + 2 λ M + A2'(Im{λ}).
    2192            0 :   ctxF->opA2 = (*ctxF->funcA2)(std::abs(lambda.imag()));
    2193              :   const auto eps = std::sqrt(std::numeric_limits<double>::epsilon());
    2194            0 :   ctxF->opA2p = (*ctxF->funcA2)(std::abs(lambda.imag()) * (1.0 + eps));
    2195            0 :   std::complex<double> denom = std::complex<double>(0.0, eps * std::abs(lambda.imag()));
    2196            0 :   ctxF->opAJ = palace::BuildParSumOperator({1.0 / denom, -1.0 / denom},
    2197            0 :                                            {ctxF->opA2p.get(), ctxF->opA2.get()}, true);
    2198            0 :   ctxF->opJ = palace::BuildParSumOperator(
    2199            0 :       {0.0 + 0.0i, 1.0 + 0.0i, 2.0 * lambda, 1.0 + 0.0i},
    2200            0 :       {ctxF->opK, ctxF->opC, ctxF->opM, ctxF->opAJ.get()}, true);
    2201            0 :   PetscFunctionReturn(PETSC_SUCCESS);
    2202              : }
    2203              : 
    2204              : #endif
        

Generated by: LCOV version 2.0-1