LCOV - code coverage report
Current view: top level - models - spaceoperator.cpp (source / functions) Coverage Total Hit
Test: Palace Coverage Report Lines: 17.4 % 443 77
Test Date: 2025-10-23 22:45:05 Functions: 6.7 % 45 3
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 "spaceoperator.hpp"
       5              : 
       6              : #include <set>
       7              : #include <type_traits>
       8              : #include "fem/bilinearform.hpp"
       9              : #include "fem/coefficient.hpp"
      10              : #include "fem/integrator.hpp"
      11              : #include "fem/mesh.hpp"
      12              : #include "fem/multigrid.hpp"
      13              : #include "linalg/hypre.hpp"
      14              : #include "linalg/rap.hpp"
      15              : #include "utils/communication.hpp"
      16              : #include "utils/geodata.hpp"
      17              : #include "utils/iodata.hpp"
      18              : #include "utils/prettyprint.hpp"
      19              : 
      20              : namespace palace
      21              : {
      22              : 
      23              : using namespace std::complex_literals;
      24              : 
      25           17 : SpaceOperator::SpaceOperator(const IoData &iodata,
      26           17 :                              const std::vector<std::unique_ptr<Mesh>> &mesh)
      27           17 :   : pc_mat_real(iodata.solver.linear.pc_mat_real),
      28           17 :     pc_mat_shifted(iodata.solver.linear.pc_mat_shifted), print_hdr(true),
      29           17 :     print_prec_hdr(true), dbc_attr(SetUpBoundaryProperties(iodata, *mesh.back())),
      30           17 :     nd_fecs(fem::ConstructFECollections<mfem::ND_FECollection>(
      31           17 :         iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels,
      32           17 :         iodata.solver.linear.mg_coarsening, false)),
      33           17 :     h1_fecs(fem::ConstructFECollections<mfem::H1_FECollection>(
      34           17 :         iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels,
      35           17 :         iodata.solver.linear.mg_coarsening, false)),
      36            0 :     rt_fecs(fem::ConstructFECollections<mfem::RT_FECollection>(
      37           17 :         iodata.solver.order - 1, mesh.back()->Dimension(),
      38           17 :         iodata.solver.linear.estimator_mg ? iodata.solver.linear.mg_max_levels : 1,
      39           17 :         iodata.solver.linear.mg_coarsening, false)),
      40           17 :     nd_fespaces(fem::ConstructFiniteElementSpaceHierarchy<mfem::ND_FECollection>(
      41           17 :         iodata.solver.linear.mg_max_levels, mesh, nd_fecs, &dbc_attr, &nd_dbc_tdof_lists)),
      42           17 :     h1_fespaces(fem::ConstructFiniteElementSpaceHierarchy<mfem::H1_FECollection>(
      43           17 :         iodata.solver.linear.mg_max_levels, mesh, h1_fecs, &dbc_attr, &h1_dbc_tdof_lists)),
      44           17 :     rt_fespaces(fem::ConstructFiniteElementSpaceHierarchy<mfem::RT_FECollection>(
      45           17 :         iodata.solver.linear.estimator_mg ? iodata.solver.linear.mg_max_levels : 1, mesh,
      46           17 :         rt_fecs)),
      47           17 :     mat_op(iodata, *mesh.back()), farfield_op(iodata, mat_op, *mesh.back()),
      48           17 :     surf_sigma_op(iodata, mat_op, *mesh.back()), surf_z_op(iodata, mat_op, *mesh.back()),
      49           17 :     lumped_port_op(iodata, mat_op, *mesh.back()),
      50           17 :     wave_port_op(iodata, mat_op, GetNDSpace(), GetH1Space()),
      51           17 :     surf_j_op(iodata, *mesh.back()),
      52           17 :     port_excitation_helper(lumped_port_op, wave_port_op, surf_j_op)
      53              : {
      54              :   // Check Excitations.
      55           17 :   if (iodata.problem.type == ProblemType::DRIVEN)
      56              :   {
      57           11 :     MFEM_VERIFY(!port_excitation_helper.Empty(),
      58              :                 "Driven problems must specify at least one excitation!");
      59              :   }
      60            6 :   else if (iodata.problem.type == ProblemType::EIGENMODE)
      61              :   {
      62            3 :     MFEM_VERIFY(port_excitation_helper.Empty(),
      63              :                 "Eigenmode problems must not specify any excitation!");
      64              :   }
      65            3 :   else if (iodata.problem.type == ProblemType::TRANSIENT)
      66              :   {
      67            3 :     MFEM_VERIFY(
      68              :         port_excitation_helper.Size() == 1,
      69              :         "Transient problems currently only support a single excitation per simulation!");
      70              :   }
      71              :   else
      72              :   {
      73            0 :     MFEM_ABORT("Internal Error: Solver type incompatible with SpaceOperator.");
      74              :   }
      75              : 
      76              :   // Finalize setup.
      77           17 :   CheckBoundaryProperties();
      78              : 
      79              :   // Print essential BC information.
      80           17 :   if (dbc_attr.Size())
      81              :   {
      82            9 :     Mpi::Print("\nConfiguring Dirichlet PEC BC at attributes:\n");
      83           18 :     utils::PrettyPrint(dbc_attr);
      84              :   }
      85           17 : }
      86              : 
      87           17 : mfem::Array<int> SpaceOperator::SetUpBoundaryProperties(const IoData &iodata,
      88              :                                                         const mfem::ParMesh &mesh)
      89              : {
      90              :   // Check that boundary attributes have been specified correctly.
      91           17 :   int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0;
      92              :   mfem::Array<int> bdr_attr_marker;
      93           17 :   if (!iodata.boundaries.pec.empty())
      94              :   {
      95              :     bdr_attr_marker.SetSize(bdr_attr_max);
      96              :     bdr_attr_marker = 0;
      97           63 :     for (auto attr : mesh.bdr_attributes)
      98              :     {
      99           54 :       bdr_attr_marker[attr - 1] = 1;
     100              :     }
     101              :     std::set<int> bdr_warn_list;
     102           18 :     for (auto attr : iodata.boundaries.pec.attributes)
     103              :     {
     104              :       // MFEM_VERIFY(attr > 0 && attr <= bdr_attr_max,
     105              :       //             "PEC boundary attribute tags must be non-negative and correspond to "
     106              :       //             "attributes in the mesh!");
     107              :       // MFEM_VERIFY(bdr_attr_marker[attr - 1],
     108              :       //             "Unknown PEC boundary attribute " << attr << "!");
     109            9 :       if (attr <= 0 || attr > bdr_attr_max || !bdr_attr_marker[attr - 1])
     110              :       {
     111              :         bdr_warn_list.insert(attr);
     112              :       }
     113              :     }
     114            9 :     if (!bdr_warn_list.empty())
     115              :     {
     116            0 :       Mpi::Print("\n");
     117            0 :       Mpi::Warning("Unknown PEC boundary attributes!\nSolver will just ignore them!");
     118            0 :       utils::PrettyPrint(bdr_warn_list, "Boundary attribute list:");
     119            0 :       Mpi::Print("\n");
     120              :     }
     121              :   }
     122              : 
     123              :   // Mark selected boundary attributes from the mesh as essential (Dirichlet).
     124              :   mfem::Array<int> dbc_bcs;
     125           17 :   dbc_bcs.Reserve(static_cast<int>(iodata.boundaries.pec.attributes.size()));
     126           26 :   for (auto attr : iodata.boundaries.pec.attributes)
     127              :   {
     128            9 :     if (attr <= 0 || attr > bdr_attr_max || !bdr_attr_marker[attr - 1])
     129              :     {
     130            0 :       continue;  // Can just ignore if wrong
     131              :     }
     132            9 :     dbc_bcs.Append(attr);
     133              :   }
     134           17 :   return dbc_bcs;
     135              : }
     136              : 
     137           17 : void SpaceOperator::CheckBoundaryProperties()
     138              : {
     139              :   // Mark selected boundary attributes from the mesh as having some Dirichlet, Neumann, or
     140              :   // mixed BC applied.
     141              :   const mfem::ParMesh &mesh = GetMesh();
     142           17 :   int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0;
     143           17 :   const auto dbc_marker = mesh::AttrToMarker(bdr_attr_max, dbc_attr);
     144           17 :   const auto farfield_marker = mesh::AttrToMarker(bdr_attr_max, farfield_op.GetAttrList());
     145              :   const auto surf_sigma_marker =
     146           17 :       mesh::AttrToMarker(bdr_attr_max, surf_sigma_op.GetAttrList());
     147           17 :   const auto surf_z_Rs_marker = mesh::AttrToMarker(bdr_attr_max, surf_z_op.GetRsAttrList());
     148           17 :   const auto surf_z_Ls_marker = mesh::AttrToMarker(bdr_attr_max, surf_z_op.GetLsAttrList());
     149              :   const auto lumped_port_Rs_marker =
     150           17 :       mesh::AttrToMarker(bdr_attr_max, lumped_port_op.GetRsAttrList());
     151              :   const auto lumped_port_Ls_marker =
     152           17 :       mesh::AttrToMarker(bdr_attr_max, lumped_port_op.GetLsAttrList());
     153              :   const auto wave_port_marker =
     154           34 :       mesh::AttrToMarker(bdr_attr_max, wave_port_op.GetAttrList());
     155              :   mfem::Array<int> aux_bdr_marker(dbc_marker.Size());
     156          263 :   for (int i = 0; i < dbc_marker.Size(); i++)
     157              :   {
     158          246 :     aux_bdr_marker[i] =
     159          237 :         (dbc_marker[i] || farfield_marker[i] || surf_sigma_marker[i] ||
     160          237 :          surf_z_Rs_marker[i] || surf_z_Ls_marker[i] || lumped_port_Rs_marker[i] ||
     161          516 :          lumped_port_Ls_marker[i] || wave_port_marker[i]);
     162          246 :     if (aux_bdr_marker[i])
     163              :     {
     164           87 :       aux_bdr_attr.Append(i + 1);
     165              :     }
     166              :   }
     167              :   // aux_bdr_marker = 1;  // Mark all boundaries (including material interfaces
     168              :   //                      // added during mesh preprocessing)
     169              :   //                      // As tested, this does not eliminate all DC modes!
     170           34 :   for (std::size_t l = 0; l < GetH1Spaces().GetNumLevels(); l++)
     171              :   {
     172           17 :     GetH1Spaces().GetFESpaceAtLevel(l).Get().GetEssentialTrueDofs(
     173           17 :         aux_bdr_marker, aux_bdr_tdof_lists.emplace_back());
     174              :   }
     175              : 
     176              :   // A final check that no boundary attribute is assigned multiple boundary conditions.
     177           17 :   const auto surf_z_marker = mesh::AttrToMarker(bdr_attr_max, surf_z_op.GetAttrList());
     178              :   const auto lumped_port_marker =
     179           17 :       mesh::AttrToMarker(bdr_attr_max, lumped_port_op.GetAttrList());
     180           17 :   const auto surf_j_marker = mesh::AttrToMarker(bdr_attr_max, surf_j_op.GetAttrList());
     181          263 :   for (int i = 0; i < dbc_marker.Size(); i++)
     182              :   {
     183          246 :     MFEM_VERIFY(dbc_marker[i] + farfield_marker[i] + surf_sigma_marker[i] +
     184              :                         surf_z_marker[i] + lumped_port_marker[i] + wave_port_marker[i] +
     185              :                         surf_j_marker[i] <=
     186              :                     1,
     187              :                 "Boundary attributes should not be specified with multiple BC!");
     188              :   }
     189           17 : }
     190              : 
     191              : namespace
     192              : {
     193              : 
     194            0 : void PrintHeader(const mfem::ParFiniteElementSpace &h1_fespace,
     195              :                  const mfem::ParFiniteElementSpace &nd_fespace,
     196              :                  const mfem::ParFiniteElementSpace &rt_fespace, bool &print_hdr)
     197              : {
     198            0 :   if (print_hdr)
     199              :   {
     200            0 :     Mpi::Print("\nAssembling system matrices, number of global unknowns:\n"
     201              :                " H1 (p = {:d}): {:d}, ND (p = {:d}): {:d}, RT (p = {:d}): {:d}\n Operator "
     202              :                "assembly level: {}\n",
     203            0 :                h1_fespace.GetMaxElementOrder(), h1_fespace.GlobalTrueVSize(),
     204            0 :                nd_fespace.GetMaxElementOrder(), nd_fespace.GlobalTrueVSize(),
     205            0 :                rt_fespace.GetMaxElementOrder(), rt_fespace.GlobalTrueVSize(),
     206            0 :                (nd_fespace.GetMaxElementOrder() >= BilinearForm::pa_order_threshold)
     207            0 :                    ? "Partial"
     208              :                    : "Full");
     209              : 
     210              :     const auto &mesh = *nd_fespace.GetParMesh();
     211            0 :     const auto geom_types = mesh::CheckElements(mesh).GetGeomTypes();
     212            0 :     Mpi::Print(" Mesh geometries:\n");
     213            0 :     for (auto geom : geom_types)
     214              :     {
     215            0 :       const auto *fe = nd_fespace.FEColl()->FiniteElementForGeometry(geom);
     216            0 :       MFEM_VERIFY(fe, "MFEM does not support ND spaces on geometry = "
     217              :                           << mfem::Geometry::Name[geom] << "!");
     218            0 :       const int q_order = fem::DefaultIntegrationOrder::Get(mesh, geom);
     219            0 :       Mpi::Print("  {}: P = {:d}, Q = {:d} (quadrature order = {:d}){}\n",
     220            0 :                  mfem::Geometry::Name[geom], fe->GetDof(),
     221            0 :                  mfem::IntRules.Get(geom, q_order).GetNPoints(), q_order,
     222            0 :                  (geom == geom_types.back()) ? "" : ",");
     223              :     }
     224              :   }
     225            0 :   print_hdr = false;
     226            0 : }
     227              : 
     228            0 : void AddIntegrators(BilinearForm &a, const MaterialPropertyCoefficient *df,
     229              :                     const MaterialPropertyCoefficient *f,
     230              :                     const MaterialPropertyCoefficient *dfb,
     231              :                     const MaterialPropertyCoefficient *fb,
     232              :                     const MaterialPropertyCoefficient *fp, bool assemble_q_data = false)
     233              : {
     234            0 :   if (df && !df->empty() && f && !f->empty())
     235              :   {
     236            0 :     a.AddDomainIntegrator<CurlCurlMassIntegrator>(*df, *f);
     237              :   }
     238              :   else
     239              :   {
     240            0 :     if (df && !df->empty())
     241              :     {
     242            0 :       a.AddDomainIntegrator<CurlCurlIntegrator>(*df);
     243              :     }
     244            0 :     if (f && !f->empty())
     245              :     {
     246            0 :       a.AddDomainIntegrator<VectorFEMassIntegrator>(*f);
     247              :     }
     248              :   }
     249            0 :   if (dfb && !dfb->empty() && fb && !fb->empty())
     250              :   {
     251            0 :     a.AddBoundaryIntegrator<CurlCurlMassIntegrator>(*dfb, *fb);
     252              :   }
     253              :   else
     254              :   {
     255            0 :     if (dfb && !dfb->empty())
     256              :     {
     257            0 :       a.AddBoundaryIntegrator<CurlCurlIntegrator>(*dfb);
     258              :     }
     259            0 :     if (fb && !fb->empty())
     260              :     {
     261            0 :       a.AddBoundaryIntegrator<VectorFEMassIntegrator>(*fb);
     262              :     }
     263              :   }
     264            0 :   if (fp && !fp->empty())
     265              :   {
     266            0 :     a.AddDomainIntegrator<MixedVectorWeakCurlIntegrator>(*fp);
     267            0 :     a.AddDomainIntegrator<MixedVectorCurlIntegrator>(*fp, true);
     268              :   }
     269            0 :   if (assemble_q_data)
     270              :   {
     271            0 :     a.AssembleQuadratureData();
     272              :   }
     273            0 : }
     274              : 
     275            0 : void AddAuxIntegrators(BilinearForm &a, const MaterialPropertyCoefficient *f,
     276              :                        const MaterialPropertyCoefficient *fb, bool assemble_q_data = false)
     277              : {
     278            0 :   if (f && !f->empty())
     279              :   {
     280            0 :     a.AddDomainIntegrator<DiffusionIntegrator>(*f);
     281              :   }
     282            0 :   if (fb && !fb->empty())
     283              :   {
     284            0 :     a.AddBoundaryIntegrator<DiffusionIntegrator>(*fb);
     285              :   }
     286            0 :   if (assemble_q_data)
     287              :   {
     288            0 :     a.AssembleQuadratureData();
     289              :   }
     290            0 : }
     291              : 
     292            0 : auto AssembleOperator(const FiniteElementSpace &fespace,
     293              :                       const MaterialPropertyCoefficient *df,
     294              :                       const MaterialPropertyCoefficient *f,
     295              :                       const MaterialPropertyCoefficient *dfb,
     296              :                       const MaterialPropertyCoefficient *fb,
     297              :                       const MaterialPropertyCoefficient *fp, bool skip_zeros = false,
     298              :                       bool assemble_q_data = false)
     299              : {
     300              :   BilinearForm a(fespace);
     301            0 :   AddIntegrators(a, df, f, dfb, fb, fp, assemble_q_data);
     302            0 :   return a.Assemble(skip_zeros);
     303              : }
     304              : 
     305            0 : auto AssembleOperators(const FiniteElementSpaceHierarchy &fespaces,
     306              :                        const MaterialPropertyCoefficient *df,
     307              :                        const MaterialPropertyCoefficient *f,
     308              :                        const MaterialPropertyCoefficient *dfb,
     309              :                        const MaterialPropertyCoefficient *fb,
     310              :                        const MaterialPropertyCoefficient *fp, bool skip_zeros = false,
     311              :                        bool assemble_q_data = false, std::size_t l0 = 0)
     312              : {
     313              :   BilinearForm a(fespaces.GetFinestFESpace());
     314            0 :   AddIntegrators(a, df, f, dfb, fb, fp, assemble_q_data);
     315            0 :   return a.Assemble(fespaces, skip_zeros, l0);
     316              : }
     317              : 
     318            0 : auto AssembleAuxOperators(const FiniteElementSpaceHierarchy &fespaces,
     319              :                           const MaterialPropertyCoefficient *f,
     320              :                           const MaterialPropertyCoefficient *fb, bool skip_zeros = false,
     321              :                           bool assemble_q_data = false, std::size_t l0 = 0)
     322              : {
     323              :   BilinearForm a(fespaces.GetFinestFESpace());
     324            0 :   AddAuxIntegrators(a, f, fb, assemble_q_data);
     325            0 :   return a.Assemble(fespaces, skip_zeros, l0);
     326              : }
     327              : 
     328              : }  // namespace
     329              : 
     330              : template <typename OperType>
     331              : std::unique_ptr<OperType>
     332            0 : SpaceOperator::GetStiffnessMatrix(Operator::DiagonalPolicy diag_policy)
     333              : {
     334            0 :   PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr);
     335            0 :   MaterialPropertyCoefficient df(mat_op.MaxCeedAttribute()), f(mat_op.MaxCeedAttribute()),
     336            0 :       fb(mat_op.MaxCeedBdrAttribute()), fc(mat_op.MaxCeedAttribute());
     337            0 :   AddStiffnessCoefficients(1.0, df, f);
     338            0 :   AddStiffnessBdrCoefficients(1.0, fb);
     339            0 :   AddRealPeriodicCoefficients(1.0, f);
     340            0 :   AddImagPeriodicCoefficients(1.0, fc);
     341            0 :   int empty[2] = {(df.empty() && f.empty() && fb.empty()), (fc.empty())};
     342              :   Mpi::GlobalMin(2, empty, GetComm());
     343            0 :   if (empty[0] && empty[1])
     344              :   {
     345              :     return {};
     346              :   }
     347              :   constexpr bool skip_zeros = false;
     348            0 :   std::unique_ptr<Operator> kr, ki;
     349            0 :   if (!empty[0])
     350              :   {
     351            0 :     kr = AssembleOperator(GetNDSpace(), &df, &f, nullptr, &fb, nullptr, skip_zeros);
     352              :   }
     353            0 :   if (!empty[1])
     354              :   {
     355              :     ki =
     356            0 :         AssembleOperator(GetNDSpace(), nullptr, nullptr, nullptr, nullptr, &fc, skip_zeros);
     357              :   }
     358              :   if constexpr (std::is_same<OperType, ComplexOperator>::value)
     359              :   {
     360            0 :     auto K =
     361              :         std::make_unique<ComplexParOperator>(std::move(kr), std::move(ki), GetNDSpace());
     362            0 :     K->SetEssentialTrueDofs(nd_dbc_tdof_lists.back(), diag_policy);
     363              :     return K;
     364              :   }
     365              :   else
     366              :   {
     367            0 :     MFEM_VERIFY(!ki, "Unexpected imaginary part in GetStiffnessMatrix<Operator>!");
     368            0 :     auto K = std::make_unique<ParOperator>(std::move(kr), GetNDSpace());
     369            0 :     K->SetEssentialTrueDofs(nd_dbc_tdof_lists.back(), diag_policy);
     370              :     return K;
     371              :   }
     372            0 : }
     373              : 
     374              : template <typename OperType>
     375              : std::unique_ptr<OperType>
     376            0 : SpaceOperator::GetDampingMatrix(Operator::DiagonalPolicy diag_policy)
     377              : {
     378            0 :   PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr);
     379            0 :   MaterialPropertyCoefficient f(mat_op.MaxCeedAttribute()),
     380            0 :       fb(mat_op.MaxCeedBdrAttribute());
     381            0 :   AddDampingCoefficients(1.0, f);
     382            0 :   AddDampingBdrCoefficients(1.0, fb);
     383            0 :   int empty = (f.empty() && fb.empty());
     384              :   Mpi::GlobalMin(1, &empty, GetComm());
     385            0 :   if (empty)
     386              :   {
     387              :     return {};
     388              :   }
     389              :   constexpr bool skip_zeros = false;
     390            0 :   auto c = AssembleOperator(GetNDSpace(), nullptr, &f, nullptr, &fb, nullptr, skip_zeros);
     391              :   if constexpr (std::is_same<OperType, ComplexOperator>::value)
     392              :   {
     393            0 :     auto C = std::make_unique<ComplexParOperator>(std::move(c), nullptr, GetNDSpace());
     394            0 :     C->SetEssentialTrueDofs(nd_dbc_tdof_lists.back(), diag_policy);
     395              :     return C;
     396              :   }
     397              :   else
     398              :   {
     399            0 :     auto C = std::make_unique<ParOperator>(std::move(c), GetNDSpace());
     400            0 :     C->SetEssentialTrueDofs(nd_dbc_tdof_lists.back(), diag_policy);
     401              :     return C;
     402              :   }
     403            0 : }
     404              : 
     405              : template <typename OperType>
     406            0 : std::unique_ptr<OperType> SpaceOperator::GetMassMatrix(Operator::DiagonalPolicy diag_policy)
     407              : {
     408            0 :   PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr);
     409            0 :   MaterialPropertyCoefficient fr(mat_op.MaxCeedAttribute()), fi(mat_op.MaxCeedAttribute()),
     410            0 :       fbr(mat_op.MaxCeedBdrAttribute()), fbi(mat_op.MaxCeedBdrAttribute());
     411            0 :   AddRealMassCoefficients(1.0, fr);
     412            0 :   AddRealMassBdrCoefficients(1.0, fbr);
     413              :   if constexpr (std::is_same<OperType, ComplexOperator>::value)
     414              :   {
     415            0 :     AddImagMassCoefficients(1.0, fi);
     416              :   }
     417            0 :   int empty[2] = {(fr.empty() && fbr.empty()), (fi.empty() && fbi.empty())};
     418              :   Mpi::GlobalMin(2, empty, GetComm());
     419            0 :   if (empty[0] && empty[1])
     420              :   {
     421              :     return {};
     422              :   }
     423              :   constexpr bool skip_zeros = false;
     424            0 :   std::unique_ptr<Operator> mr, mi;
     425            0 :   if (!empty[0])
     426              :   {
     427            0 :     mr = AssembleOperator(GetNDSpace(), nullptr, &fr, nullptr, &fbr, nullptr, skip_zeros);
     428              :   }
     429            0 :   if (!empty[1])
     430              :   {
     431            0 :     mi = AssembleOperator(GetNDSpace(), nullptr, &fi, nullptr, &fbi, nullptr, skip_zeros);
     432              :   }
     433              :   if constexpr (std::is_same<OperType, ComplexOperator>::value)
     434              :   {
     435            0 :     auto M =
     436              :         std::make_unique<ComplexParOperator>(std::move(mr), std::move(mi), GetNDSpace());
     437            0 :     M->SetEssentialTrueDofs(nd_dbc_tdof_lists.back(), diag_policy);
     438              :     return M;
     439              :   }
     440              :   else
     441              :   {
     442            0 :     auto M = std::make_unique<ParOperator>(std::move(mr), GetNDSpace());
     443            0 :     M->SetEssentialTrueDofs(nd_dbc_tdof_lists.back(), diag_policy);
     444              :     return M;
     445              :   }
     446            0 : }
     447              : 
     448              : template <typename OperType>
     449              : std::unique_ptr<OperType>
     450            0 : SpaceOperator::GetExtraSystemMatrix(double omega, Operator::DiagonalPolicy diag_policy)
     451              : {
     452            0 :   PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr);
     453            0 :   MaterialPropertyCoefficient dfbr(mat_op.MaxCeedBdrAttribute()),
     454            0 :       dfbi(mat_op.MaxCeedBdrAttribute()), fbr(mat_op.MaxCeedBdrAttribute()),
     455            0 :       fbi(mat_op.MaxCeedBdrAttribute());
     456            0 :   AddExtraSystemBdrCoefficients(omega, dfbr, dfbi, fbr, fbi);
     457            0 :   int empty[2] = {(dfbr.empty() && fbr.empty()), (dfbi.empty() && fbi.empty())};
     458              :   Mpi::GlobalMin(2, empty, GetComm());
     459            0 :   if (empty[0] && empty[1])
     460              :   {
     461              :     return {};
     462              :   }
     463              :   constexpr bool skip_zeros = false;
     464            0 :   std::unique_ptr<Operator> ar, ai;
     465            0 :   if (!empty[0])
     466              :   {
     467            0 :     ar = AssembleOperator(GetNDSpace(), nullptr, nullptr, &dfbr, &fbr, nullptr, skip_zeros);
     468              :   }
     469            0 :   if (!empty[1])
     470              :   {
     471            0 :     ai = AssembleOperator(GetNDSpace(), nullptr, nullptr, &dfbi, &fbi, nullptr, skip_zeros);
     472              :   }
     473              :   if constexpr (std::is_same<OperType, ComplexOperator>::value)
     474              :   {
     475            0 :     auto A =
     476              :         std::make_unique<ComplexParOperator>(std::move(ar), std::move(ai), GetNDSpace());
     477            0 :     A->SetEssentialTrueDofs(nd_dbc_tdof_lists.back(), diag_policy);
     478              :     return A;
     479              :   }
     480              :   else
     481              :   {
     482            0 :     MFEM_VERIFY(!ai, "Unexpected imaginary part in GetExtraSystemMatrix<Operator>!");
     483            0 :     auto A = std::make_unique<ParOperator>(std::move(ar), GetNDSpace());
     484            0 :     A->SetEssentialTrueDofs(nd_dbc_tdof_lists.back(), diag_policy);
     485              :     return A;
     486              :   }
     487            0 : }
     488              : 
     489              : template <typename OperType, typename ScalarType>
     490              : std::unique_ptr<OperType>
     491            0 : SpaceOperator::GetSystemMatrix(ScalarType a0, ScalarType a1, ScalarType a2,
     492              :                                const OperType *K, const OperType *C, const OperType *M,
     493              :                                const OperType *A2)
     494              : {
     495            0 :   return BuildParSumOperator({a0, a1, a2, ScalarType{1}}, {K, C, M, A2});
     496              : }
     497              : 
     498            0 : std::unique_ptr<Operator> SpaceOperator::GetInnerProductMatrix(double a0, double a2,
     499              :                                                                const ComplexOperator *K,
     500              :                                                                const ComplexOperator *M)
     501              : {
     502            0 :   const auto *PtAP_K = (K) ? dynamic_cast<const ComplexParOperator *>(K) : nullptr;
     503            0 :   const auto *PtAP_M = (M) ? dynamic_cast<const ComplexParOperator *>(M) : nullptr;
     504            0 :   return BuildParSumOperator(
     505            0 :       {a0, a2}, {PtAP_K ? PtAP_K->Real() : nullptr, PtAP_M ? PtAP_M->Real() : nullptr});
     506              : }
     507              : 
     508              : namespace
     509              : {
     510              : 
     511              : template <typename OperType>
     512              : auto BuildLevelParOperator(std::unique_ptr<Operator> &&br, std::unique_ptr<Operator> &&bi,
     513              :                            const FiniteElementSpace &fespace);
     514              : 
     515              : template <>
     516            0 : auto BuildLevelParOperator<Operator>(std::unique_ptr<Operator> &&br,
     517              :                                      std::unique_ptr<Operator> &&bi,
     518              :                                      const FiniteElementSpace &fespace)
     519              : {
     520            0 :   MFEM_VERIFY(
     521              :       !bi,
     522              :       "Should not be constructing a real-valued ParOperator with non-zero imaginary part!");
     523            0 :   return std::make_unique<ParOperator>(std::move(br), fespace);
     524              : }
     525              : 
     526              : template <>
     527              : auto BuildLevelParOperator<ComplexOperator>(std::unique_ptr<Operator> &&br,
     528              :                                             std::unique_ptr<Operator> &&bi,
     529              :                                             const FiniteElementSpace &fespace)
     530              : {
     531            0 :   return std::make_unique<ComplexParOperator>(std::move(br), std::move(bi), fespace);
     532              : }
     533              : 
     534              : }  // namespace
     535              : 
     536            0 : void SpaceOperator::AssemblePreconditioner(
     537              :     std::complex<double> a0, std::complex<double> a1, std::complex<double> a2, double a3,
     538              :     std::vector<std::unique_ptr<Operator>> &br_vec,
     539              :     std::vector<std::unique_ptr<Operator>> &br_aux_vec,
     540              :     std::vector<std::unique_ptr<Operator>> &bi_vec,
     541              :     std::vector<std::unique_ptr<Operator>> &bi_aux_vec)
     542              : {
     543              :   constexpr bool skip_zeros = false, assemble_q_data = false;
     544            0 :   MaterialPropertyCoefficient dfr(mat_op.MaxCeedAttribute()),
     545            0 :       dfi(mat_op.MaxCeedAttribute()), fr(mat_op.MaxCeedAttribute()),
     546            0 :       fi(mat_op.MaxCeedAttribute()), dfbr(mat_op.MaxCeedBdrAttribute()),
     547            0 :       dfbi(mat_op.MaxCeedBdrAttribute()), fbr(mat_op.MaxCeedBdrAttribute()),
     548            0 :       fbi(mat_op.MaxCeedBdrAttribute()), fpi(mat_op.MaxCeedAttribute()),
     549            0 :       fpr(mat_op.MaxCeedAttribute());
     550            0 :   AddStiffnessCoefficients(a0.real(), dfr, fr);
     551            0 :   AddStiffnessCoefficients(a0.imag(), dfi, fi);
     552            0 :   AddStiffnessBdrCoefficients(a0.real(), fbr);
     553            0 :   AddStiffnessBdrCoefficients(a0.imag(), fbi);
     554            0 :   AddDampingCoefficients(a1.real(), fr);
     555            0 :   AddDampingCoefficients(a1.imag(), fi);
     556            0 :   AddDampingBdrCoefficients(a1.real(), fbr);
     557            0 :   AddDampingBdrCoefficients(a1.imag(), fbi);
     558            0 :   AddRealMassCoefficients(pc_mat_shifted ? std::abs(a2.real()) : a2.real(), fr);
     559            0 :   AddRealMassCoefficients(a2.imag(), fi);
     560            0 :   AddRealMassBdrCoefficients(pc_mat_shifted ? std::abs(a2.real()) : a2.real(), fbr);
     561            0 :   AddRealMassBdrCoefficients(a2.imag(), fbi);
     562            0 :   AddImagMassCoefficients(a2.real(), fi);
     563            0 :   AddImagMassCoefficients(-a2.imag(), fr);
     564            0 :   AddExtraSystemBdrCoefficients(a3, dfbr, dfbi, fbr, fbi);
     565            0 :   AddRealPeriodicCoefficients(a0.real(), fr);
     566            0 :   AddRealPeriodicCoefficients(a0.imag(), fi);
     567            0 :   AddImagPeriodicCoefficients(a0.real(), fpi);
     568            0 :   AddImagPeriodicCoefficients(-a0.imag(), fpr);
     569              :   int empty[2] = {
     570            0 :       (dfr.empty() && fr.empty() && dfbr.empty() && fbr.empty() && fpr.empty()),
     571            0 :       (dfi.empty() && fi.empty() && dfbi.empty() && fbi.empty() && fpi.empty())};
     572              :   Mpi::GlobalMin(2, empty, GetComm());
     573            0 :   if (!empty[0])
     574              :   {
     575            0 :     br_vec = AssembleOperators(GetNDSpaces(), &dfr, &fr, &dfbr, &fbr, &fpr, skip_zeros,
     576            0 :                                assemble_q_data);
     577              :     br_aux_vec =
     578            0 :         AssembleAuxOperators(GetH1Spaces(), &fr, &fbr, skip_zeros, assemble_q_data);
     579              :   }
     580            0 :   if (!empty[1])
     581              :   {
     582            0 :     bi_vec = AssembleOperators(GetNDSpaces(), &dfi, &fi, &dfbi, &fbi, &fpi, skip_zeros,
     583            0 :                                assemble_q_data);
     584              :     bi_aux_vec =
     585            0 :         AssembleAuxOperators(GetH1Spaces(), &fi, &fbi, skip_zeros, assemble_q_data);
     586              :   }
     587            0 : }
     588              : 
     589            0 : void SpaceOperator::AssemblePreconditioner(
     590              :     std::complex<double> a0, std::complex<double> a1, std::complex<double> a2, double a3,
     591              :     std::vector<std::unique_ptr<Operator>> &br_vec,
     592              :     std::vector<std::unique_ptr<Operator>> &br_aux_vec)
     593              : {
     594              :   constexpr bool skip_zeros = false, assemble_q_data = false;
     595            0 :   MaterialPropertyCoefficient dfr(mat_op.MaxCeedAttribute()), fr(mat_op.MaxCeedAttribute()),
     596            0 :       dfbr(mat_op.MaxCeedBdrAttribute()), fbr(mat_op.MaxCeedBdrAttribute());
     597            0 :   AddStiffnessCoefficients(a0.real(), dfr, fr);
     598            0 :   AddStiffnessBdrCoefficients(a0.real(), fbr);
     599            0 :   AddDampingCoefficients(a1.imag(), fr);
     600            0 :   AddDampingBdrCoefficients(a1.imag(), fbr);
     601            0 :   AddAbsMassCoefficients(pc_mat_shifted ? std::abs(a2.real()) : a2.real(), fr);
     602            0 :   AddRealMassBdrCoefficients(pc_mat_shifted ? std::abs(a2.real()) : a2.real(), fbr);
     603            0 :   AddExtraSystemBdrCoefficients(a3, dfbr, dfbr, fbr, fbr);
     604            0 :   AddRealPeriodicCoefficients(a0.real(), fr);
     605            0 :   int empty = (dfr.empty() && fr.empty() && dfbr.empty() && fbr.empty());
     606              :   Mpi::GlobalMin(1, &empty, GetComm());
     607            0 :   if (!empty)
     608              :   {
     609            0 :     br_vec = AssembleOperators(GetNDSpaces(), &dfr, &fr, &dfbr, &fbr, nullptr, skip_zeros,
     610            0 :                                assemble_q_data);
     611              :     br_aux_vec =
     612            0 :         AssembleAuxOperators(GetH1Spaces(), &fr, &fbr, skip_zeros, assemble_q_data);
     613              :   }
     614            0 : }
     615              : 
     616            0 : void SpaceOperator::AssemblePreconditioner(
     617              :     double a0, double a1, double a2, double a3,
     618              :     std::vector<std::unique_ptr<Operator>> &br_vec,
     619              :     std::vector<std::unique_ptr<Operator>> &br_aux_vec)
     620              : {
     621              :   constexpr bool skip_zeros = false, assemble_q_data = false;
     622            0 :   MaterialPropertyCoefficient dfr(mat_op.MaxCeedAttribute()), fr(mat_op.MaxCeedAttribute()),
     623            0 :       dfbr(mat_op.MaxCeedBdrAttribute()), fbr(mat_op.MaxCeedBdrAttribute());
     624            0 :   AddStiffnessCoefficients(a0, dfr, fr);
     625            0 :   AddStiffnessBdrCoefficients(a0, fbr);
     626            0 :   AddDampingCoefficients(a1, fr);
     627            0 :   AddDampingBdrCoefficients(a1, fbr);
     628            0 :   AddAbsMassCoefficients(pc_mat_shifted ? std::abs(a2) : a2, fr);
     629            0 :   AddRealMassBdrCoefficients(pc_mat_shifted ? std::abs(a2) : a2, fbr);
     630            0 :   AddExtraSystemBdrCoefficients(a3, dfbr, dfbr, fbr, fbr);
     631            0 :   AddRealPeriodicCoefficients(a0, fr);
     632            0 :   int empty = (dfr.empty() && fr.empty() && dfbr.empty() && fbr.empty());
     633              :   Mpi::GlobalMin(1, &empty, GetComm());
     634            0 :   if (!empty)
     635              :   {
     636            0 :     br_vec = AssembleOperators(GetNDSpaces(), &dfr, &fr, &dfbr, &fbr, nullptr, skip_zeros,
     637            0 :                                assemble_q_data);
     638              :     br_aux_vec =
     639            0 :         AssembleAuxOperators(GetH1Spaces(), &fr, &fbr, skip_zeros, assemble_q_data);
     640              :   }
     641            0 : }
     642              : 
     643              : template <typename OperType, typename ScalarType>
     644            0 : std::unique_ptr<OperType> SpaceOperator::GetPreconditionerMatrix(ScalarType a0,
     645              :                                                                  ScalarType a1,
     646              :                                                                  ScalarType a2, double a3)
     647              : {
     648              :   // When partially assembled, the coarse operators can reuse the fine operator quadrature
     649              :   // data if the spaces correspond to the same mesh. When appropriate, we build the
     650              :   // preconditioner on all levels based on the actual complex-valued system matrix. The
     651              :   // coarse operator is always fully assembled.
     652            0 :   if (print_prec_hdr)
     653              :   {
     654            0 :     Mpi::Print("\nAssembling multigrid hierarchy:\n");
     655              :   }
     656            0 :   MFEM_VERIFY(GetH1Spaces().GetNumLevels() == GetNDSpaces().GetNumLevels(),
     657              :               "Multigrid hierarchy mismatch for auxiliary space preconditioning!");
     658              : 
     659            0 :   const auto n_levels = GetNDSpaces().GetNumLevels();
     660            0 :   std::vector<std::unique_ptr<Operator>> br_vec(n_levels), bi_vec(n_levels),
     661            0 :       br_aux_vec(n_levels), bi_aux_vec(n_levels);
     662            0 :   if (std::is_same<OperType, ComplexOperator>::value && !pc_mat_real)
     663              :   {
     664            0 :     AssemblePreconditioner(a0, a1, a2, a3, br_vec, br_aux_vec, bi_vec, bi_aux_vec);
     665              :   }
     666              :   else
     667              :   {
     668            0 :     AssemblePreconditioner(a0, a1, a2, a3, br_vec, br_aux_vec);
     669              :   }
     670              : 
     671            0 :   auto B = std::make_unique<BaseMultigridOperator<OperType>>(n_levels);
     672            0 :   for (bool aux : {false, true})
     673              :   {
     674            0 :     for (std::size_t l = 0; l < n_levels; l++)
     675              :     {
     676            0 :       const auto &fespace_l =
     677              :           aux ? GetH1Spaces().GetFESpaceAtLevel(l) : GetNDSpaces().GetFESpaceAtLevel(l);
     678            0 :       const auto &dbc_tdof_lists_l = aux ? h1_dbc_tdof_lists[l] : nd_dbc_tdof_lists[l];
     679            0 :       auto &br_l = aux ? br_aux_vec[l] : br_vec[l];
     680            0 :       auto &bi_l = aux ? bi_aux_vec[l] : bi_vec[l];
     681            0 :       if (print_prec_hdr)
     682              :       {
     683            0 :         Mpi::Print(" Level {:d}{} (p = {:d}): {:d} unknowns", l, aux ? " (auxiliary)" : "",
     684            0 :                    fespace_l.GetMaxElementOrder(), fespace_l.GlobalTrueVSize());
     685            0 :         const auto *b_spm = dynamic_cast<const hypre::HypreCSRMatrix *>(br_l.get());
     686            0 :         if (!b_spm)
     687              :         {
     688            0 :           b_spm = dynamic_cast<const hypre::HypreCSRMatrix *>(bi_l.get());
     689              :         }
     690            0 :         if (b_spm)
     691              :         {
     692            0 :           HYPRE_BigInt nnz = b_spm->NNZ();
     693              :           Mpi::GlobalSum(1, &nnz, fespace_l.GetComm());
     694            0 :           Mpi::Print(", {:d} NNZ\n", nnz);
     695              :         }
     696              :         else
     697              :         {
     698            0 :           Mpi::Print("\n");
     699              :         }
     700              :       }
     701            0 :       auto B_l =
     702              :           BuildLevelParOperator<OperType>(std::move(br_l), std::move(bi_l), fespace_l);
     703            0 :       B_l->SetEssentialTrueDofs(dbc_tdof_lists_l, Operator::DiagonalPolicy::DIAG_ONE);
     704            0 :       if (aux)
     705              :       {
     706            0 :         B->AddAuxiliaryOperator(std::move(B_l));
     707              :       }
     708              :       else
     709              :       {
     710            0 :         B->AddOperator(std::move(B_l));
     711              :       }
     712              :     }
     713              :   }
     714              : 
     715            0 :   print_prec_hdr = false;
     716            0 :   return B;
     717            0 : }
     718              : 
     719            0 : void SpaceOperator::AddStiffnessCoefficients(double coeff, MaterialPropertyCoefficient &df,
     720              :                                              MaterialPropertyCoefficient &f)
     721              : {
     722              :   // Contribution from material permeability.
     723            0 :   df.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetInvPermeability(), coeff);
     724              : 
     725              :   // Contribution for London superconductors.
     726            0 :   if (mat_op.HasLondonDepth())
     727              :   {
     728            0 :     f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetInvLondonDepth(), coeff);
     729              :   }
     730            0 : }
     731              : 
     732            0 : void SpaceOperator::AddStiffnessBdrCoefficients(double coeff,
     733              :                                                 MaterialPropertyCoefficient &fb)
     734              : {
     735              :   // Robin BC contributions due to surface impedance and lumped ports (inductance).
     736            0 :   surf_z_op.AddStiffnessBdrCoefficients(coeff, fb);
     737            0 :   lumped_port_op.AddStiffnessBdrCoefficients(coeff, fb);
     738            0 : }
     739              : 
     740            0 : void SpaceOperator::AddDampingCoefficients(double coeff, MaterialPropertyCoefficient &f)
     741              : {
     742              :   // Contribution for domain conductivity.
     743            0 :   if (mat_op.HasConductivity())
     744              :   {
     745            0 :     f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetConductivity(), coeff);
     746              :   }
     747            0 : }
     748              : 
     749            0 : void SpaceOperator::AddDampingBdrCoefficients(double coeff, MaterialPropertyCoefficient &fb)
     750              : {
     751              :   // Robin BC contributions due to surface impedance, lumped ports, and absorbing
     752              :   // boundaries (resistance).
     753            0 :   farfield_op.AddDampingBdrCoefficients(coeff, fb);
     754            0 :   surf_z_op.AddDampingBdrCoefficients(coeff, fb);
     755            0 :   lumped_port_op.AddDampingBdrCoefficients(coeff, fb);
     756            0 : }
     757              : 
     758            0 : void SpaceOperator::AddRealMassCoefficients(double coeff, MaterialPropertyCoefficient &f)
     759              : {
     760            0 :   f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal(), coeff);
     761            0 : }
     762              : 
     763            0 : void SpaceOperator::AddRealMassBdrCoefficients(double coeff,
     764              :                                                MaterialPropertyCoefficient &fb)
     765              : {
     766              :   // Robin BC contributions due to surface impedance and lumped ports (capacitance).
     767            0 :   surf_z_op.AddMassBdrCoefficients(coeff, fb);
     768            0 :   lumped_port_op.AddMassBdrCoefficients(coeff, fb);
     769            0 : }
     770              : 
     771            0 : void SpaceOperator::AddImagMassCoefficients(double coeff, MaterialPropertyCoefficient &f)
     772              : {
     773              :   // Contribution for loss tangent: ε -> ε * (1 - i tan(δ)).
     774            0 :   if (mat_op.HasLossTangent())
     775              :   {
     776            0 :     f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityImag(), coeff);
     777              :   }
     778            0 : }
     779              : 
     780            0 : void SpaceOperator::AddAbsMassCoefficients(double coeff, MaterialPropertyCoefficient &f)
     781              : {
     782            0 :   f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityAbs(), coeff);
     783            0 : }
     784              : 
     785            0 : void SpaceOperator::AddExtraSystemBdrCoefficients(double omega,
     786              :                                                   MaterialPropertyCoefficient &dfbr,
     787              :                                                   MaterialPropertyCoefficient &dfbi,
     788              :                                                   MaterialPropertyCoefficient &fbr,
     789              :                                                   MaterialPropertyCoefficient &fbi)
     790              : {
     791              :   // Contribution for second-order farfield boundaries and finite conductivity boundaries.
     792            0 :   farfield_op.AddExtraSystemBdrCoefficients(omega, dfbr, dfbi);
     793            0 :   surf_sigma_op.AddExtraSystemBdrCoefficients(omega, fbr, fbi);
     794              : 
     795              :   // Contribution for numeric wave ports.
     796            0 :   wave_port_op.AddExtraSystemBdrCoefficients(omega, fbr, fbi);
     797            0 : }
     798              : 
     799            0 : void SpaceOperator::AddRealPeriodicCoefficients(double coeff,
     800              :                                                 MaterialPropertyCoefficient &f)
     801              : {
     802              :   // Floquet periodicity contributions.
     803            0 :   if (mat_op.HasWaveVector())
     804              :   {
     805            0 :     f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetFloquetMass(), coeff);
     806              :   }
     807            0 : }
     808              : 
     809            0 : void SpaceOperator::AddImagPeriodicCoefficients(double coeff,
     810              :                                                 MaterialPropertyCoefficient &f)
     811              : {
     812              :   // Floquet periodicity contributions.
     813            0 :   if (mat_op.HasWaveVector())
     814              :   {
     815            0 :     f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetFloquetCurl(), coeff);
     816              :   }
     817            0 : }
     818              : 
     819            0 : bool SpaceOperator::GetExcitationVector(int excitation_idx, Vector &RHS)
     820              : {
     821              :   // Time domain excitation vector.
     822            0 :   RHS.SetSize(GetNDSpace().GetTrueVSize());
     823            0 :   RHS.UseDevice(true);
     824            0 :   RHS = 0.0;
     825            0 :   bool nnz = AddExcitationVector1Internal(excitation_idx, RHS);
     826            0 :   linalg::SetSubVector(RHS, nd_dbc_tdof_lists.back(), 0.0);
     827            0 :   return nnz;
     828              : }
     829              : 
     830            0 : bool SpaceOperator::GetExcitationVector(int excitation_idx, double omega,
     831              :                                         ComplexVector &RHS)
     832              : {
     833              :   // Frequency domain excitation vector: RHS = iω RHS1 + RHS2(ω).
     834            0 :   RHS.SetSize(GetNDSpace().GetTrueVSize());
     835            0 :   RHS.UseDevice(true);
     836              :   RHS = 0.0;
     837            0 :   bool nnz1 = AddExcitationVector1Internal(excitation_idx, RHS.Real());
     838            0 :   RHS *= 1i * omega;
     839            0 :   bool nnz2 = AddExcitationVector2Internal(excitation_idx, omega, RHS);
     840            0 :   linalg::SetSubVector(RHS, nd_dbc_tdof_lists.back(), 0.0);
     841            0 :   return nnz1 || nnz2;
     842              : }
     843              : 
     844            0 : bool SpaceOperator::GetExcitationVector1(int excitation_idx, ComplexVector &RHS1)
     845              : {
     846              :   // Assemble the frequency domain excitation term with linear frequency dependence
     847              :   // (coefficient iω, see GetExcitationVector above, is accounted for later).
     848            0 :   RHS1.SetSize(GetNDSpace().GetTrueVSize());
     849            0 :   RHS1.UseDevice(true);
     850              :   RHS1 = 0.0;
     851            0 :   bool nnz1 = AddExcitationVector1Internal(excitation_idx, RHS1.Real());
     852            0 :   linalg::SetSubVector(RHS1.Real(), nd_dbc_tdof_lists.back(), 0.0);
     853            0 :   return nnz1;
     854              : }
     855              : 
     856            0 : bool SpaceOperator::GetExcitationVector2(int excitation_idx, double omega,
     857              :                                          ComplexVector &RHS2)
     858              : {
     859            0 :   RHS2.SetSize(GetNDSpace().GetTrueVSize());
     860            0 :   RHS2.UseDevice(true);
     861              :   RHS2 = 0.0;
     862            0 :   bool nnz2 = AddExcitationVector2Internal(excitation_idx, omega, RHS2);
     863            0 :   linalg::SetSubVector(RHS2, nd_dbc_tdof_lists.back(), 0.0);
     864            0 :   return nnz2;
     865              : }
     866              : 
     867            0 : bool SpaceOperator::AddExcitationVector1Internal(int excitation_idx, Vector &RHS1)
     868              : {
     869              :   // Assemble the time domain excitation -g'(t) J or frequency domain excitation -iω J.
     870              :   // The g'(t) or iω factors are not accounted for here, they is accounted for in the time
     871              :   // integration or frequency sweep later.
     872            0 :   MFEM_VERIFY(RHS1.Size() == GetNDSpace().GetTrueVSize(),
     873              :               "Invalid T-vector size for AddExcitationVector1Internal!");
     874              :   SumVectorCoefficient fb(GetMesh().SpaceDimension());
     875            0 :   lumped_port_op.AddExcitationBdrCoefficients(excitation_idx, fb);
     876            0 :   surf_j_op.AddExcitationBdrCoefficients(fb);  // No excitation_idx — currently in all
     877            0 :   int empty = (fb.empty());
     878              :   Mpi::GlobalMin(1, &empty, GetComm());
     879            0 :   if (empty)
     880              :   {
     881              :     return false;
     882              :   }
     883            0 :   mfem::LinearForm rhs1(&GetNDSpace().Get());
     884            0 :   rhs1.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fb));
     885            0 :   rhs1.UseFastAssembly(false);
     886              :   rhs1.UseDevice(false);
     887            0 :   rhs1.Assemble();
     888              :   rhs1.UseDevice(true);
     889            0 :   GetNDSpace().GetProlongationMatrix()->AddMultTranspose(rhs1, RHS1);
     890              :   return true;
     891            0 : }
     892              : 
     893            0 : bool SpaceOperator::AddExcitationVector2Internal(int excitation_idx, double omega,
     894              :                                                  ComplexVector &RHS2)
     895              : {
     896              :   // Assemble the contribution of wave ports to the frequency domain excitation term at the
     897              :   // specified frequency.
     898            0 :   MFEM_VERIFY(RHS2.Size() == GetNDSpace().GetTrueVSize(),
     899              :               "Invalid T-vector size for AddExcitationVector2Internal!");
     900              :   SumVectorCoefficient fbr(GetMesh().SpaceDimension()), fbi(GetMesh().SpaceDimension());
     901            0 :   wave_port_op.AddExcitationBdrCoefficients(excitation_idx, omega, fbr, fbi);
     902            0 :   int empty = (fbr.empty() && fbi.empty());
     903              :   Mpi::GlobalMin(1, &empty, GetComm());
     904            0 :   if (empty)
     905              :   {
     906              :     return false;
     907              :   }
     908              :   {
     909            0 :     mfem::LinearForm rhs2(&GetNDSpace().Get());
     910            0 :     rhs2.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fbr));
     911            0 :     rhs2.UseFastAssembly(false);
     912              :     rhs2.UseDevice(false);
     913            0 :     rhs2.Assemble();
     914              :     rhs2.UseDevice(true);
     915            0 :     GetNDSpace().GetProlongationMatrix()->AddMultTranspose(rhs2, RHS2.Real());
     916            0 :   }
     917              :   {
     918            0 :     mfem::LinearForm rhs2(&GetNDSpace().Get());
     919            0 :     rhs2.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fbi));
     920            0 :     rhs2.UseFastAssembly(false);
     921              :     rhs2.UseDevice(false);
     922            0 :     rhs2.Assemble();
     923              :     rhs2.UseDevice(true);
     924            0 :     GetNDSpace().GetProlongationMatrix()->AddMultTranspose(rhs2, RHS2.Imag());
     925            0 :   }
     926            0 :   return true;
     927              : }
     928              : 
     929            0 : void SpaceOperator::GetConstantInitialVector(ComplexVector &v)
     930              : {
     931            0 :   v.SetSize(GetNDSpace().GetTrueVSize());
     932            0 :   v.UseDevice(true);
     933              :   v = 1.0;
     934            0 :   linalg::SetSubVector(v.Real(), nd_dbc_tdof_lists.back(), 0.0);
     935            0 : }
     936              : 
     937            0 : void SpaceOperator::GetRandomInitialVector(ComplexVector &v)
     938              : {
     939            0 :   v.SetSize(GetNDSpace().GetTrueVSize());
     940            0 :   v.UseDevice(true);
     941            0 :   linalg::SetRandom(GetNDSpace().GetComm(), v);
     942            0 :   linalg::SetSubVector(v, nd_dbc_tdof_lists.back(), 0.0);
     943            0 : }
     944              : 
     945              : template std::unique_ptr<Operator>
     946              :     SpaceOperator::GetStiffnessMatrix(Operator::DiagonalPolicy);
     947              : template std::unique_ptr<ComplexOperator>
     948              :     SpaceOperator::GetStiffnessMatrix(Operator::DiagonalPolicy);
     949              : 
     950              : template std::unique_ptr<Operator>
     951              :     SpaceOperator::GetDampingMatrix(Operator::DiagonalPolicy);
     952              : template std::unique_ptr<ComplexOperator>
     953              :     SpaceOperator::GetDampingMatrix(Operator::DiagonalPolicy);
     954              : 
     955              : template std::unique_ptr<Operator> SpaceOperator::GetMassMatrix(Operator::DiagonalPolicy);
     956              : template std::unique_ptr<ComplexOperator>
     957              :     SpaceOperator::GetMassMatrix(Operator::DiagonalPolicy);
     958              : 
     959              : template std::unique_ptr<Operator>
     960              : SpaceOperator::GetExtraSystemMatrix(double, Operator::DiagonalPolicy);
     961              : template std::unique_ptr<ComplexOperator>
     962              : SpaceOperator::GetExtraSystemMatrix(double, Operator::DiagonalPolicy);
     963              : 
     964              : template std::unique_ptr<Operator>
     965              : SpaceOperator::GetSystemMatrix<Operator, double>(double, double, double, const Operator *,
     966              :                                                  const Operator *, const Operator *,
     967              :                                                  const Operator *);
     968              : template std::unique_ptr<ComplexOperator>
     969              : SpaceOperator::GetSystemMatrix<ComplexOperator, std::complex<double>>(
     970              :     std::complex<double>, std::complex<double>, std::complex<double>,
     971              :     const ComplexOperator *, const ComplexOperator *, const ComplexOperator *,
     972              :     const ComplexOperator *);
     973              : 
     974              : template std::unique_ptr<Operator>
     975              : SpaceOperator::GetPreconditionerMatrix<Operator, double>(double, double, double, double);
     976              : template std::unique_ptr<ComplexOperator>
     977              : SpaceOperator::GetPreconditionerMatrix<ComplexOperator, std::complex<double>>(
     978              :     std::complex<double>, std::complex<double>, std::complex<double>, double);
     979              : 
     980              : }  // namespace palace
        

Generated by: LCOV version 2.0-1