LCOV - code coverage report
Current view: top level - src/mfem/equation_systems - ComplexEquationSystem.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #33416 (b10b36) with base 9fbd27 Lines: 139 163 85.3 %
Date: 2026-07-23 16:15:30 Functions: 11 13 84.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #ifdef MOOSE_MFEM_ENABLED
       2             : 
       3             : #include "ComplexEquationSystem.h"
       4             : #include "CoefficientManager.h"
       5             : #include "libmesh/int_range.h"
       6             : 
       7             : namespace Moose::MFEM
       8             : {
       9             : 
      10             : void
      11          63 : ComplexEquationSystem::Init(GridFunctions & gridfunctions,
      12             :                             ComplexGridFunctions & cmplx_gridfunctions,
      13             :                             mfem::AssemblyLevel assembly_level)
      14             : {
      15          63 :   _assembly_level = assembly_level;
      16             : 
      17          63 :   if (gridfunctions.size())
      18           0 :     mooseError("Mixing real and complex variables is currently not supported.");
      19             : 
      20         105 :   for (auto & test_var_name : _test_var_names)
      21             :   {
      22          42 :     if (!cmplx_gridfunctions.Has(test_var_name))
      23             :     {
      24           0 :       mooseError("MFEM complex variable ",
      25             :                  test_var_name,
      26             :                  " requested by equation system during initialization was "
      27             :                  "not found in gridfunctions");
      28             :     }
      29             :     // Store pointers to test FESpaces
      30          42 :     _test_pfespaces.push_back(cmplx_gridfunctions.Get(test_var_name)->ParFESpace());
      31             :     // Create auxiliary gridfunctions for storing essential constraints from Dirichlet conditions
      32          42 :     _cmplx_var_ess_constraints.emplace_back(std::make_unique<mfem::ParComplexGridFunction>(
      33          84 :         cmplx_gridfunctions.Get(test_var_name)->ParFESpace()));
      34             :   }
      35             : 
      36             :   // Store pointers to FESpaces of all coupled variables
      37         105 :   for (auto & coupled_var_name : _coupled_var_names)
      38          42 :     _coupled_pfespaces.push_back(cmplx_gridfunctions.Get(coupled_var_name)->ParFESpace());
      39             : 
      40             :   // Extract which coupled variables are to be trivially eliminated and which are trial variables
      41          63 :   SetTrialVariableNames();
      42             : 
      43             :   // Store pointers to coupled variable ComplexGridFunctions that are to be eliminated prior to
      44             :   // forming the jacobian
      45          63 :   for (auto & eliminated_var_name : _eliminated_var_names)
      46           0 :     _cmplx_eliminated_variables.Register(eliminated_var_name,
      47           0 :                                          cmplx_gridfunctions.GetShared(eliminated_var_name));
      48             : 
      49             :   // Get a reference to the complex GridFunctions
      50          63 :   _complex_gfuncs = &cmplx_gridfunctions;
      51          63 : }
      52             : 
      53             : void
      54          42 : ComplexEquationSystem::BuildEquationSystem()
      55             : {
      56          42 :   BuildBilinearForms();
      57          42 :   BuildLinearForms();
      58          42 : }
      59             : 
      60             : void
      61          42 : ComplexEquationSystem::BuildLinearForms()
      62             : {
      63             :   // Register linear forms
      64          84 :   for (const auto i : index_range(_test_var_names))
      65             :   {
      66          42 :     auto test_var_name = _test_var_names.at(i);
      67          42 :     _clfs.Register(test_var_name,
      68          84 :                    std::make_shared<mfem::ParComplexLinearForm>(_test_pfespaces.at(i)));
      69          42 :     _clfs.GetRef(test_var_name) = 0.0;
      70          42 :   }
      71             :   // Apply boundary conditions
      72          42 :   ApplyEssentialBCs();
      73             : 
      74          84 :   for (auto & test_var_name : _test_var_names)
      75             :   {
      76             :     // Apply kernels
      77          42 :     auto clf = _clfs.GetShared(test_var_name);
      78          42 :     ApplyDomainLFIntegrators(test_var_name, clf, _cmplx_kernels_map);
      79          42 :     ApplyBoundaryLFIntegrators(test_var_name, clf, _cmplx_integrated_bc_map);
      80          42 :     clf->Assemble();
      81          42 :   }
      82          42 : }
      83             : 
      84             : void
      85          42 : ComplexEquationSystem::BuildBilinearForms()
      86             : {
      87             :   // Register bilinear forms
      88          84 :   for (const auto i : index_range(_test_var_names))
      89             :   {
      90          42 :     auto test_var_name = _test_var_names.at(i);
      91          42 :     _slfs.Register(test_var_name,
      92          84 :                    std::make_shared<mfem::ParSesquilinearForm>(_test_pfespaces.at(i)));
      93             : 
      94             :     // Apply kernels
      95          42 :     auto slf = _slfs.GetShared(test_var_name);
      96          42 :     slf->SetAssemblyLevel(_assembly_level);
      97          42 :     ApplyBoundaryBLFIntegrators<mfem::ParSesquilinearForm>(
      98          42 :         test_var_name, test_var_name, slf, _cmplx_integrated_bc_map);
      99          42 :     ApplyDomainBLFIntegrators<mfem::ParSesquilinearForm>(
     100          42 :         test_var_name, test_var_name, slf, _cmplx_kernels_map);
     101             :     // Assemble
     102          42 :     slf->Assemble();
     103          42 :   }
     104          42 : }
     105             : 
     106             : void
     107          42 : ComplexEquationSystem::ApplyComplexEssentialBC(const std::string & var_name,
     108             :                                                mfem::ParComplexGridFunction & trial_gf,
     109             :                                                mfem::Array<int> & global_ess_markers)
     110             : {
     111          42 :   if (_cmplx_essential_bc_map.Has(var_name))
     112             :   {
     113          42 :     auto & bcs = _cmplx_essential_bc_map.GetRef(var_name);
     114         105 :     for (auto & bc : bcs)
     115             :     {
     116             :       // Set constrained DoFs values on essential boundaries
     117          63 :       bc->ApplyBC(trial_gf);
     118             :       // Fetch marker array labelling essential boundaries of current BC
     119          63 :       mfem::Array<int> ess_bdrs(bc->getBoundaryMarkers());
     120             :       // Add these boundary markers to the set of markers labelling all essential boundaries
     121         392 :       for (const auto i : make_range(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max()))
     122         329 :         global_ess_markers[i] = std::max(global_ess_markers[i], ess_bdrs[i]);
     123          63 :     }
     124             :   }
     125          42 : }
     126             : 
     127             : void
     128          42 : ComplexEquationSystem::ApplyEssentialBCs()
     129             : {
     130          42 :   _ess_tdof_lists.resize(_trial_var_names.size());
     131          84 :   for (const auto i : index_range(_trial_var_names))
     132             :   {
     133          42 :     const auto & trial_var_name = _trial_var_names.at(i);
     134          42 :     mfem::ParComplexGridFunction & trial_gf = *_cmplx_var_ess_constraints.at(i);
     135             : 
     136             :     // Make sure we update the size, if this mesh has changed recently for instance
     137          42 :     trial_gf.Update();
     138             : 
     139             :     // Initial guess for iterative solvers (initial condition or the previous time step solution)
     140          42 :     static_cast<mfem::Vector &>(trial_gf) = _complex_gfuncs->GetRef(trial_var_name);
     141             : 
     142          42 :     mfem::Array<int> global_ess_markers(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max());
     143          42 :     global_ess_markers = 0;
     144             :     // Set strongly constrained DoFs of trial_gf on essential boundaries and add markers for all
     145             :     // essential boundaries to the global_ess_markers array
     146          42 :     ApplyComplexEssentialBC(trial_var_name, trial_gf, global_ess_markers);
     147          42 :     trial_gf.FESpace()->GetEssentialTrueDofs(global_ess_markers, _ess_tdof_lists.at(i));
     148          42 :   }
     149          42 : }
     150             : 
     151             : void
     152          63 : ComplexEquationSystem::AddComplexKernel(std::shared_ptr<MFEMComplexKernel> kernel)
     153             : {
     154          63 :   const auto & trial_var_name = kernel->getTrialVariableName();
     155          63 :   const auto & test_var_name = kernel->getTestVariableName();
     156          63 :   AddCoupledVariableNameIfMissing(trial_var_name);
     157          63 :   AddTestVariableNameIfMissing(test_var_name);
     158             :   // Register new complex kernels map if not present for the test variable
     159          63 :   if (!_cmplx_kernels_map.Has(test_var_name))
     160             :   {
     161             :     auto kernel_field_map =
     162          42 :         std::make_shared<NamedFieldsMap<std::vector<std::shared_ptr<MFEMComplexKernel>>>>();
     163          42 :     _cmplx_kernels_map.Register(test_var_name, std::move(kernel_field_map));
     164          42 :   }
     165             :   // Register new complex kernels map if not present for the test/trial variable pair
     166          63 :   if (!_cmplx_kernels_map.Get(test_var_name)->Has(trial_var_name))
     167             :   {
     168          42 :     auto kernels = std::make_shared<std::vector<std::shared_ptr<MFEMComplexKernel>>>();
     169          42 :     _cmplx_kernels_map.Get(test_var_name)->Register(trial_var_name, std::move(kernels));
     170          42 :   }
     171          63 :   _cmplx_kernels_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(kernel));
     172          63 : }
     173             : 
     174             : void
     175          14 : ComplexEquationSystem::AddComplexIntegratedBC(std::shared_ptr<MFEMComplexIntegratedBC> bc)
     176             : {
     177          14 :   const auto & trial_var_name = bc->getTrialVariableName();
     178          14 :   const auto & test_var_name = bc->getTestVariableName();
     179          14 :   AddCoupledVariableNameIfMissing(trial_var_name);
     180          14 :   AddTestVariableNameIfMissing(test_var_name);
     181             :   // Register new complex integrated bc map if not present for the test variable
     182          14 :   if (!_cmplx_integrated_bc_map.Has(test_var_name))
     183             :   {
     184             :     auto integrated_bc_field_map =
     185           7 :         std::make_shared<NamedFieldsMap<std::vector<std::shared_ptr<MFEMComplexIntegratedBC>>>>();
     186           7 :     _cmplx_integrated_bc_map.Register(test_var_name, std::move(integrated_bc_field_map));
     187           7 :   }
     188             :   // Register new complex integrated bc map if not present for the test/trial variable pair
     189          14 :   if (!_cmplx_integrated_bc_map.Get(test_var_name)->Has(trial_var_name))
     190             :   {
     191           7 :     auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMComplexIntegratedBC>>>();
     192           7 :     _cmplx_integrated_bc_map.Get(test_var_name)->Register(trial_var_name, std::move(bcs));
     193           7 :   }
     194          14 :   _cmplx_integrated_bc_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(bc));
     195          14 : }
     196             : 
     197             : void
     198          63 : ComplexEquationSystem::AddComplexEssentialBCs(std::shared_ptr<MFEMComplexEssentialBC> bc)
     199             : {
     200          63 :   const auto & test_var_name = bc->getTestVariableName();
     201          63 :   AddTestVariableNameIfMissing(test_var_name);
     202             :   // Register new complex essential bc map if not present for the test variable
     203          63 :   if (!_cmplx_essential_bc_map.Has(test_var_name))
     204             :   {
     205          42 :     auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMComplexEssentialBC>>>();
     206          42 :     _cmplx_essential_bc_map.Register(test_var_name, std::move(bcs));
     207          42 :   }
     208          63 :   _cmplx_essential_bc_map.GetRef(test_var_name).push_back(std::move(bc));
     209          63 : }
     210             : 
     211             : void
     212           0 : ComplexEquationSystem::FormSystemOperator(mfem::OperatorHandle & op,
     213             :                                           mfem::BlockVector & trueX,
     214             :                                           mfem::BlockVector & trueRHS)
     215             : {
     216           0 :   auto & test_var_name = _test_var_names.at(0);
     217           0 :   mfem::Vector aux_x, aux_rhs;
     218           0 :   mfem::OperatorPtr aux_a;
     219             : 
     220           0 :   auto slf = _slfs.Get(test_var_name);
     221           0 :   slf->FormLinearSystem(_ess_tdof_lists.at(0),
     222           0 :                         *_cmplx_var_ess_constraints.at(0),
     223           0 :                         *_clfs.Get(test_var_name),
     224             :                         aux_a,
     225             :                         aux_x,
     226             :                         aux_rhs,
     227             :                         /*copy_interior=*/true);
     228             : 
     229           0 :   trueX.GetBlock(0) = aux_x;
     230           0 :   trueRHS.GetBlock(0) = aux_rhs;
     231           0 :   trueX.SyncFromBlocks();
     232           0 :   trueRHS.SyncFromBlocks();
     233             : 
     234           0 :   op.Reset(aux_a.Ptr());
     235           0 :   aux_a.SetOperatorOwner(false);
     236           0 : }
     237             : 
     238             : void
     239          42 : ComplexEquationSystem::FormSystemMatrix(mfem::OperatorHandle & op,
     240             :                                         mfem::BlockVector & trueX,
     241             :                                         mfem::BlockVector & trueRHS)
     242             : {
     243             : 
     244             :   // Allocate block operator
     245          42 :   DeleteHBlocks();
     246          42 :   _h_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
     247          42 :   _h_blocks = nullptr;
     248             :   // Zero out RHS and sync memory
     249          42 :   trueRHS = 0.0;
     250          42 :   trueRHS.SyncToBlocks();
     251             : 
     252             :   // Form diagonal blocks.
     253          84 :   for (const auto i : index_range(_test_var_names))
     254             :   {
     255          42 :     auto & test_var_name = _test_var_names.at(i);
     256             : 
     257          42 :     mfem::Vector aux_x, aux_rhs;
     258          42 :     mfem::OperatorHandle aux_a;
     259             : 
     260          42 :     auto slf = _slfs.Get(test_var_name);
     261          42 :     slf->FormLinearSystem(_ess_tdof_lists.at(i),
     262          42 :                           *_cmplx_var_ess_constraints.at(i),
     263          42 :                           *_clfs.Get(test_var_name),
     264             :                           aux_a,
     265             :                           aux_x,
     266             :                           aux_rhs,
     267             :                           /*copy_interior=*/true);
     268          42 :     trueX.GetBlock(i) = aux_x;
     269          42 :     trueRHS.GetBlock(i) = aux_rhs;
     270          42 :     _h_blocks(i, i) = aux_a.As<mfem::ComplexHypreParMatrix>()->GetSystemMatrix();
     271          42 :   }
     272             :   // Sync memory
     273          42 :   trueX.SyncFromBlocks();
     274          42 :   trueRHS.SyncFromBlocks();
     275             : 
     276             :   // Create monolithic matrix
     277          42 :   op.Reset(mfem::HypreParMatrixFromBlocks(_h_blocks));
     278          42 : }
     279             : 
     280             : // Equation system Mult
     281             : void
     282           0 : ComplexEquationSystem::Mult(const mfem::Vector & x, mfem::Vector & residual) const
     283             : {
     284           0 :   _linear_operator->Mult(x, residual);
     285           0 :   x.HostRead();
     286           0 :   residual.HostRead();
     287           0 : }
     288             : 
     289             : void
     290          42 : ComplexEquationSystem::SetTrialVariablesFromTrueVectors(const mfem::BlockVector & trueX) const
     291             : {
     292          84 :   for (const auto i : index_range(_trial_var_names))
     293             :   {
     294          42 :     auto & trial_var_name = _trial_var_names.at(i);
     295          42 :     trueX.GetBlock(i).SyncMemory(trueX);
     296          42 :     _complex_gfuncs->Get(trial_var_name)->Distribute(&(trueX.GetBlock(i)));
     297             :   }
     298             :   // Solution variables changed: stored projections of solution-dependent coefficients are stale.
     299          42 :   if (_coefficient_manager)
     300          42 :     _coefficient_manager->markSolutionChanged();
     301          42 : }
     302             : }
     303             : 
     304             : #endif

Generated by: LCOV version 1.14