LCOV - code coverage report
Current view: top level - src/mfem/equation_systems - EquationSystem.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #33380 (547b29) with base 8581c3 Lines: 370 383 96.6 %
Date: 2026-07-20 19:36:28 Functions: 34 35 97.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #ifdef MOOSE_MFEM_ENABLED
      11             : 
      12             : #include "EquationSystem.h"
      13             : #include "MFEMLinearSolverBase.h"
      14             : #include "CoefficientManager.h"
      15             : #include "libmesh/int_range.h"
      16             : 
      17             : namespace Moose::MFEM
      18             : {
      19             : 
      20        1523 : EquationSystem::~EquationSystem()
      21             : {
      22        1523 :   DeleteHBlocks();
      23        1523 :   DeleteJacobianBlocks();
      24        1523 : }
      25             : 
      26             : void
      27        3966 : EquationSystem::DeleteHBlocks()
      28             : {
      29        6440 :   for (const auto i : make_range(_h_blocks.NumRows()))
      30        5010 :     for (const auto j : make_range(_h_blocks.NumCols()))
      31             :     {
      32        2536 :       if (_jacobian_blocks.NumRows() && _jacobian_blocks(i, j) == _h_blocks(i, j))
      33           0 :         _jacobian_blocks(i, j) = nullptr;
      34        2536 :       delete _h_blocks(i, j);
      35             :     }
      36        3966 :   _h_blocks.DeleteAll();
      37        3966 : }
      38             : 
      39             : void
      40        2429 : EquationSystem::DeleteJacobianBlocks()
      41             : {
      42        3335 :   for (const auto i : make_range(_jacobian_blocks.NumRows()))
      43        1812 :     for (const auto j : make_range(_jacobian_blocks.NumCols()))
      44         906 :       if (!_h_blocks.NumRows() || _jacobian_blocks(i, j) != _h_blocks(i, j))
      45         906 :         delete _jacobian_blocks(i, j);
      46        2429 :   _jacobian_blocks.DeleteAll();
      47        2429 : }
      48             : 
      49             : bool
      50        7867 : EquationSystem::VectorContainsName(const std::vector<std::string> & the_vector,
      51             :                                    const std::string & name) const
      52             : {
      53        7867 :   return std::find(the_vector.begin(), the_vector.end(), name) != the_vector.end();
      54             : }
      55             : 
      56             : void
      57        1862 : EquationSystem::AddCoupledVariableNameIfMissing(const std::string & coupled_var_name)
      58             : {
      59        1862 :   if (!VectorContainsName(_coupled_var_names, coupled_var_name))
      60        1190 :     _coupled_var_names.push_back(coupled_var_name);
      61        1862 : }
      62             : 
      63             : void
      64         183 : EquationSystem::AddEliminatedVariableNameIfMissing(const std::string & eliminated_var_name)
      65             : {
      66         183 :   if (!VectorContainsName(_eliminated_var_names, eliminated_var_name))
      67         177 :     _eliminated_var_names.push_back(eliminated_var_name);
      68         183 : }
      69             : 
      70             : void
      71        3412 : EquationSystem::AddTestVariableNameIfMissing(const std::string & test_var_name)
      72             : {
      73        3412 :   if (!VectorContainsName(_test_var_names, test_var_name))
      74        1096 :     _test_var_names.push_back(test_var_name);
      75        3412 : }
      76             : 
      77             : void
      78        1525 : EquationSystem::SetTrialVariableNames()
      79             : {
      80             :   // If a coupled variable has an equation associated with it,
      81             :   // add it to the set of trial variables.
      82        2619 :   for (const auto & test_var_name : _test_var_names)
      83        1094 :     if (VectorContainsName(_coupled_var_names, test_var_name))
      84        1094 :       _trial_var_names.push_back(test_var_name);
      85             : 
      86             :   // Otherwise, add it to the set of eliminated variables.
      87        2713 :   for (const auto & coupled_var_name : _coupled_var_names)
      88        1188 :     if (!VectorContainsName(_test_var_names, coupled_var_name))
      89          94 :       _eliminated_var_names.push_back(coupled_var_name);
      90        1525 : }
      91             : 
      92             : void
      93        1723 : EquationSystem::AddKernel(std::shared_ptr<MFEMKernel> kernel)
      94             : {
      95        1723 :   const auto & trial_var_name = kernel->getTrialVariableName();
      96        1723 :   const auto & test_var_name = kernel->getTestVariableName();
      97        1723 :   AddCoupledVariableNameIfMissing(trial_var_name);
      98        1723 :   AddTestVariableNameIfMissing(test_var_name);
      99             :   // Register new kernels map if not present for the test variable
     100        1723 :   if (!_kernels_map.Has(test_var_name))
     101             :   {
     102             :     auto kernel_field_map =
     103        1048 :         std::make_shared<Moose::MFEM::NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>>();
     104        1048 :     _kernels_map.Register(test_var_name, std::move(kernel_field_map));
     105        1048 :   }
     106             :   // Register new kernels map if not present for the test/trial variable pair
     107        1723 :   if (!_kernels_map.Get(test_var_name)->Has(trial_var_name))
     108             :   {
     109        1155 :     auto kernels = std::make_shared<std::vector<std::shared_ptr<MFEMKernel>>>();
     110        1155 :     _kernels_map.Get(test_var_name)->Register(trial_var_name, std::move(kernels));
     111        1155 :   }
     112        1723 :   _kernels_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(kernel));
     113        1723 : }
     114             : 
     115             : void
     116          62 : EquationSystem::AddIntegratedBC(std::shared_ptr<MFEMIntegratedBC> bc)
     117             : {
     118          62 :   const auto & trial_var_name = bc->getTrialVariableName();
     119          62 :   const auto & test_var_name = bc->getTestVariableName();
     120          62 :   AddCoupledVariableNameIfMissing(trial_var_name);
     121          62 :   AddTestVariableNameIfMissing(test_var_name);
     122             :   // Register new integrated bc map if not present for the test variable
     123          62 :   if (!_integrated_bc_map.Has(test_var_name))
     124             :   {
     125             :     auto integrated_bc_field_map = std::make_shared<
     126          56 :         Moose::MFEM::NamedFieldsMap<std::vector<std::shared_ptr<MFEMIntegratedBC>>>>();
     127          56 :     _integrated_bc_map.Register(test_var_name, std::move(integrated_bc_field_map));
     128          56 :   }
     129             :   // Register new integrated bc map if not present for the test/trial variable pair
     130          62 :   if (!_integrated_bc_map.Get(test_var_name)->Has(trial_var_name))
     131             :   {
     132          56 :     auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMIntegratedBC>>>();
     133          56 :     _integrated_bc_map.Get(test_var_name)->Register(trial_var_name, std::move(bcs));
     134          56 :   }
     135          62 :   _integrated_bc_map.GetRef(test_var_name).Get(trial_var_name)->push_back(std::move(bc));
     136          62 : }
     137             : 
     138             : void
     139        1304 : EquationSystem::AddEssentialBC(std::shared_ptr<MFEMEssentialBC> bc)
     140             : {
     141        1304 :   const auto & test_var_name = bc->getTestVariableName();
     142        1304 :   AddTestVariableNameIfMissing(test_var_name);
     143             :   // Register new essential bc map if not present for the test variable
     144        1304 :   if (!_essential_bc_map.Has(test_var_name))
     145             :   {
     146         951 :     auto bcs = std::make_shared<std::vector<std::shared_ptr<MFEMEssentialBC>>>();
     147         951 :     _essential_bc_map.Register(test_var_name, std::move(bcs));
     148         951 :   }
     149        1304 :   _essential_bc_map.GetRef(test_var_name).push_back(std::move(bc));
     150        1304 : }
     151             : 
     152             : void
     153        1462 : EquationSystem::Init(Moose::MFEM::GridFunctions & gridfunctions,
     154             :                      Moose::MFEM::ComplexGridFunctions & cmplx_gridfunctions,
     155             :                      mfem::AssemblyLevel assembly_level)
     156             : {
     157        1462 :   _assembly_level = assembly_level;
     158             : 
     159        1462 :   if (cmplx_gridfunctions.size())
     160           0 :     mooseError("Complex variables have been created but the executioner numeric type has not been "
     161             :                "set to complex. Please set Executioner/numeric_type = complex.");
     162             : 
     163             :   // Extract which coupled variables are to be trivially eliminated and which are trial variables
     164        1462 :   SetTrialVariableNames();
     165             : 
     166        2514 :   for (auto & test_var_name : _test_var_names)
     167             :   {
     168        1052 :     if (!gridfunctions.Has(test_var_name))
     169             :     {
     170           0 :       mooseError("MFEM variable ",
     171             :                  test_var_name,
     172             :                  " requested by equation system during initialization was "
     173             :                  "not found in gridfunctions");
     174             :     }
     175             :     // Store pointers to test FESpaces
     176        1052 :     _test_pfespaces.push_back(gridfunctions.Get(test_var_name)->ParFESpace());
     177             :   }
     178             : 
     179        2514 :   for (auto & trial_var_name : _trial_var_names)
     180             :   {
     181        1052 :     if (!gridfunctions.Has(trial_var_name))
     182             :     {
     183           0 :       mooseError("MFEM variable ",
     184             :                  trial_var_name,
     185             :                  " requested by equation system during initialization was "
     186             :                  "not found in gridfunctions");
     187             :     }
     188             :     // Create auxiliary gridfunctions for storing essential constraints from Dirichlet conditions
     189        1052 :     _var_ess_constraints.emplace_back(
     190        2104 :         std::make_unique<mfem::ParGridFunction>(gridfunctions.Get(trial_var_name)->ParFESpace()));
     191             :   }
     192             : 
     193             :   // Store pointers to FESpaces of all coupled variables
     194        2608 :   for (auto & coupled_var_name : _coupled_var_names)
     195        1146 :     _coupled_pfespaces.push_back(gridfunctions.Get(coupled_var_name)->ParFESpace());
     196             : 
     197             :   // Store pointers to coupled variable GridFunctions that are to be eliminated prior to forming the
     198             :   // jacobian
     199        1731 :   for (auto & eliminated_var_name : _eliminated_var_names)
     200         269 :     _eliminated_variables.Register(eliminated_var_name,
     201         538 :                                    gridfunctions.GetShared(eliminated_var_name));
     202             : 
     203             :   // Get a reference to the GridFunctions
     204        1462 :   _gfuncs = &gridfunctions;
     205        1462 : }
     206             : 
     207             : void
     208        2557 : EquationSystem::ApplyEssentialBC(const std::string & var_name,
     209             :                                  mfem::ParGridFunction & trial_gf,
     210             :                                  mfem::Array<int> & global_ess_markers)
     211             : {
     212        2557 :   if (_essential_bc_map.Has(var_name))
     213             :   {
     214        1830 :     auto & bcs = _essential_bc_map.GetRef(var_name);
     215        4811 :     for (auto & bc : bcs)
     216             :     {
     217             :       // Set constrained DoFs values on essential boundaries
     218        2981 :       bc->ApplyBC(trial_gf);
     219             :       // Fetch marker array labelling essential boundaries of current BC
     220        2981 :       mfem::Array<int> ess_bdrs(bc->getBoundaryMarkers());
     221             :       // Add these boundary markers to the set of markers labelling all essential boundaries
     222       15063 :       for (const auto i : make_range(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max()))
     223       12082 :         global_ess_markers[i] = std::max(global_ess_markers[i], ess_bdrs[i]);
     224        2981 :     }
     225             :   }
     226        2557 : }
     227             : 
     228             : void
     229        2470 : EquationSystem::ApplyEssentialBCs()
     230             : {
     231        2470 :   _ess_tdof_lists.resize(_trial_var_names.size());
     232        4975 :   for (const auto i : index_range(_trial_var_names))
     233             :   {
     234        2505 :     const auto & trial_var_name = _trial_var_names.at(i);
     235        2505 :     mfem::ParGridFunction & trial_gf = *_var_ess_constraints.at(i);
     236             : 
     237             :     // Make sure we update the size, if this mesh has changed recently for instance
     238        2505 :     trial_gf.Update();
     239             : 
     240             :     // Initial guess for iterative solvers (initial condition or the previous time step solution)
     241        2505 :     trial_gf = _gfuncs->GetRef(trial_var_name);
     242             : 
     243        2505 :     mfem::Array<int> global_ess_markers(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max());
     244        2505 :     global_ess_markers = 0;
     245             :     // Set strongly constrained DoFs of trial_gf on essential boundaries and add markers for all
     246             :     // essential boundaries to the global_ess_markers array
     247        2505 :     ApplyEssentialBC(trial_var_name, trial_gf, global_ess_markers);
     248        2505 :     trial_gf.ParFESpace()->GetEssentialTrueDofs(global_ess_markers, _ess_tdof_lists.at(i));
     249        2505 :   }
     250        2470 : }
     251             : 
     252             : void
     253        2496 : EquationSystem::EliminateCoupledVariables()
     254             : {
     255        5027 :   for (const auto & test_var_name : _test_var_names)
     256        4279 :     for (const auto & eliminated_var_name : _eliminated_var_names)
     257        1876 :       if (_mblfs.Has(test_var_name) && _mblfs.Get(test_var_name)->Has(eliminated_var_name) &&
     258         128 :           !VectorContainsName(_test_var_names, eliminated_var_name))
     259             :       {
     260          92 :         auto & mblf = *_mblfs.Get(test_var_name)->Get(eliminated_var_name);
     261          92 :         mblf.AddMult(*_eliminated_variables.Get(eliminated_var_name), *_lfs.Get(test_var_name), -1);
     262             :       }
     263        2496 : }
     264             : 
     265             : void
     266        2502 : EquationSystem::FormLinearSystem(mfem::OperatorHandle & op,
     267             :                                  mfem::BlockVector & trueX,
     268             :                                  mfem::BlockVector & trueRHS)
     269             : {
     270             :   mooseAssert(_test_var_names.size() == _trial_var_names.size(),
     271             :               "Number of test and trial variables must be the same for block matrix assembly.");
     272             : 
     273        2502 :   if (_assembly_level == mfem::AssemblyLevel::LEGACY)
     274        2443 :     FormSystemMatrix(op, trueX, trueRHS);
     275             :   else
     276             :   {
     277             :     mooseAssert(_test_var_names.size() == 1 && _test_var_names.size() == _trial_var_names.size(),
     278             :                 "Non-legacy assembly is only supported for single test and trial variable systems");
     279          59 :     FormSystemOperator(op, trueX, trueRHS);
     280             :   }
     281        2502 : }
     282             : 
     283             : void
     284          59 : EquationSystem::FormSystemOperator(mfem::OperatorHandle & op,
     285             :                                    mfem::BlockVector & trueX,
     286             :                                    mfem::BlockVector & trueRHS)
     287             : {
     288          59 :   auto & test_var_name = _test_var_names.at(0);
     289          59 :   mfem::Vector aux_x, aux_rhs;
     290          59 :   mfem::OperatorPtr aux_a;
     291             : 
     292          59 :   auto blf = _blfs.Get(test_var_name);
     293          59 :   blf->FormLinearSystem(_ess_tdof_lists.at(0),
     294          59 :                         *_var_ess_constraints.at(0),
     295          59 :                         *_lfs.Get(test_var_name),
     296             :                         aux_a,
     297             :                         aux_x,
     298             :                         aux_rhs,
     299             :                         /*copy_interior=*/true);
     300             : 
     301          59 :   trueX.GetBlock(0) = aux_x;
     302          59 :   trueRHS.GetBlock(0) = aux_rhs;
     303          59 :   trueX.SyncFromBlocks();
     304          59 :   trueRHS.SyncFromBlocks();
     305             : 
     306          59 :   op.Reset(aux_a.Ptr());
     307          59 :   aux_a.SetOperatorOwner(false);
     308          59 : }
     309             : 
     310             : void
     311        2401 : EquationSystem::FormSystemMatrix(mfem::OperatorHandle & op,
     312             :                                  mfem::BlockVector & trueX,
     313             :                                  mfem::BlockVector & trueRHS)
     314             : {
     315             :   // Allocate block operator
     316        2401 :   DeleteHBlocks();
     317        2401 :   _h_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
     318        2401 :   _h_blocks = nullptr;
     319             :   // Zero out RHS and sync memory
     320        2401 :   trueRHS = 0.0;
     321        2401 :   trueRHS.SyncToBlocks();
     322             : 
     323        4833 :   for (const auto i : index_range(_test_var_names))
     324             :   {
     325        2432 :     auto test_var_name = _test_var_names.at(i);
     326             : 
     327        4926 :     for (const auto j : index_range(_trial_var_names))
     328             :     {
     329        2494 :       auto trial_var_name = _trial_var_names.at(j);
     330             : 
     331        2494 :       mfem::Vector aux_x, aux_rhs;
     332        2494 :       mfem::ParLinearForm aux_lf(_test_pfespaces.at(i));
     333        2494 :       mfem::HypreParMatrix * aux_a = new mfem::HypreParMatrix;
     334             : 
     335        2494 :       if (test_var_name == trial_var_name)
     336             :       {
     337             :         mooseAssert(i == j, "Trial and test variables must have the same ordering.");
     338        2432 :         auto blf = _blfs.Get(test_var_name);
     339        2432 :         blf->FormLinearSystem(_ess_tdof_lists.at(j),
     340        2432 :                               *_var_ess_constraints.at(j),
     341        2432 :                               *_lfs.Get(test_var_name),
     342             :                               *aux_a,
     343             :                               aux_x,
     344             :                               aux_rhs,
     345             :                               /*copy_interior=*/true);
     346        2432 :         trueX.GetBlock(j) = aux_x;
     347             :       }
     348          62 :       else if (_mblfs.Has(test_var_name) && _mblfs.Get(test_var_name)->Has(trial_var_name))
     349             :       {
     350          62 :         auto mblf = _mblfs.Get(test_var_name)->Get(trial_var_name);
     351          62 :         mblf->FormRectangularLinearSystem(_ess_tdof_lists.at(j),
     352          62 :                                           _ess_tdof_lists.at(i),
     353          62 :                                           *_var_ess_constraints.at(j),
     354          62 :                                           aux_lf = 0,
     355             :                                           *aux_a,
     356             :                                           aux_x,
     357             :                                           aux_rhs);
     358             :       }
     359             :       else
     360           0 :         continue;
     361             : 
     362        2494 :       trueRHS.GetBlock(i) += aux_rhs;
     363        2494 :       _h_blocks(i, j) = aux_a;
     364        2494 :     }
     365        2432 :   }
     366             :   // Sync memory
     367        2401 :   trueX.SyncFromBlocks();
     368        2401 :   trueRHS.SyncFromBlocks();
     369             : 
     370             :   // Create monolithic matrix
     371        2401 :   op.Reset(mfem::HypreParMatrixFromBlocks(_h_blocks));
     372        2401 : }
     373             : 
     374             : void
     375        2502 : EquationSystem::FormSystem(mfem::BlockVector & trueX, mfem::BlockVector & trueRHS)
     376             : {
     377        2502 :   height = trueX.Size();
     378        2502 :   width = trueRHS.Size();
     379             :   // Store block offsets
     380        2502 :   _block_true_offsets.SetSize(trueX.NumBlocks() + 1);
     381        2502 :   _block_true_offsets[0] = 0;
     382        5035 :   for (unsigned i = 0; i < _trial_var_names.size(); i++)
     383        2533 :     _block_true_offsets[i + 1] = trueX.BlockSize(i);
     384        2502 :   _block_true_offsets.PartialSum();
     385        2502 :   FormLinearSystem(_linear_operator, trueX, trueRHS);
     386        2502 : }
     387             : 
     388             : void
     389        1584 : EquationSystem::Mult(const mfem::Vector & sol, mfem::Vector & residual) const
     390             : {
     391        1584 :   if (_non_linear)
     392             :   {
     393        1584 :     ComputeNonlinearResidual(sol, residual);
     394        1584 :     _linear_operator->AddMult(sol, residual);
     395             :   }
     396             :   else
     397             :   {
     398           0 :     residual = 0.0;
     399           0 :     _linear_operator->Mult(sol, residual);
     400             :   }
     401             : 
     402        1584 :   sol.HostRead();
     403        1584 :   residual.HostRead();
     404        1584 : }
     405             : 
     406             : void
     407        1584 : EquationSystem::ComputeNonlinearResidual(const mfem::Vector & sol, mfem::Vector & residual) const
     408             : {
     409             :   mooseAssert(_non_linear, "Should not be calling this method if our forms are not nonlinear");
     410        1584 :   residual = 0.0;
     411             : 
     412        1584 :   const mfem::BlockVector block_solution(const_cast<mfem::Vector &>(sol), _block_true_offsets);
     413        1584 :   SetTrialVariablesFromTrueVectors(block_solution);
     414             : 
     415        1584 :   mfem::BlockVector block_residual(residual, _block_true_offsets);
     416        3168 :   for (unsigned int i = 0; i < _test_var_names.size(); i++)
     417             :   {
     418        1584 :     auto & test_var_name = _test_var_names.at(i);
     419        1584 :     auto nlf = _nlfs.GetShared(test_var_name);
     420        1584 :     nlf->Mult(block_solution.GetBlock(i), block_residual.GetBlock(i));
     421        1584 :     block_residual.GetBlock(i).SyncAliasMemory(block_residual);
     422        1584 :   }
     423        1584 : }
     424             : 
     425             : void
     426         906 : EquationSystem::FormJacobianMatrix(const mfem::Vector & u)
     427             : {
     428         906 :   DeleteJacobianBlocks();
     429         906 :   _jacobian_blocks.SetSize(_test_var_names.size(), _trial_var_names.size());
     430         906 :   _jacobian_blocks = nullptr;
     431             : 
     432         906 :   const mfem::BlockVector update_vector(const_cast<mfem::Vector &>(u), _block_true_offsets);
     433        1812 :   for (const auto i : index_range(_test_var_names))
     434             :   {
     435         906 :     auto test_var_name = _test_var_names.at(i);
     436         906 :     if (_nlfs.Has(test_var_name))
     437             :     {
     438         906 :       auto nlf = _nlfs.Get(test_var_name);
     439             :       mfem::HypreParMatrix * nlf_jac =
     440         906 :           dynamic_cast<mfem::HypreParMatrix *>(&nlf->GetGradient(update_vector.GetBlock(i)));
     441             :       mooseAssert(nlf_jac,
     442             :                   "Jacobian contribution of nonlinear form associated with " + test_var_name +
     443             :                       " is not castable into a HypreParMatrix");
     444         906 :       _jacobian_blocks(i, i) = mfem::ParAdd(_h_blocks(i, i), nlf_jac);
     445             :     }
     446             :     else
     447           0 :       _jacobian_blocks(i, i) = _h_blocks(i, i);
     448        1812 :     for (const auto j : index_range(_trial_var_names))
     449         906 :       if (i != j) // nlf->GetGradient only contributes to on-diagonal blocks
     450           0 :         _jacobian_blocks(i, j) = _h_blocks(i, j);
     451         906 :   }
     452             :   // Create monolithic matrix
     453         906 :   _jacobian.Reset(mfem::HypreParMatrixFromBlocks(_jacobian_blocks));
     454         906 : }
     455             : 
     456             : mfem::Operator &
     457         908 : EquationSystem::GetGradient(const mfem::Vector & u) const
     458             : {
     459         908 :   if (_non_linear)
     460             :   {
     461         908 :     if (_assembly_level != mfem::AssemblyLevel::LEGACY)
     462           2 :       mooseError("MFEM nonlinear solvers that require GetGradient() currently require legacy "
     463             :                  "assembly in EquationSystem.");
     464         906 :     const_cast<EquationSystem *>(this)->FormJacobianMatrix(u);
     465             :   }
     466             :   else
     467           0 :     _jacobian = _linear_operator;
     468             : 
     469         906 :   return *_jacobian;
     470             : }
     471             : 
     472             : void
     473        2420 : EquationSystem::SetTrialVariablesFromTrueVectors(const mfem::BlockVector & trueX) const
     474             : {
     475        4853 :   for (const auto i : index_range(_trial_var_names))
     476             :   {
     477        2433 :     auto & trial_var_name = _trial_var_names.at(i);
     478        2433 :     trueX.GetBlock(i).SyncMemory(trueX);
     479        2433 :     _gfuncs->Get(trial_var_name)->Distribute(&(trueX.GetBlock(i)));
     480             :   }
     481             :   // Solution variables changed: stored projections of solution-dependent coefficients are stale.
     482        2420 :   if (_coefficient_manager)
     483        2420 :     _coefficient_manager->markSolutionChanged();
     484        2420 : }
     485             : 
     486             : void
     487        2498 : EquationSystem::BuildLinearForms()
     488             : {
     489             :   // Register linear forms
     490        5031 :   for (const auto i : index_range(_test_var_names))
     491             :   {
     492        2533 :     auto test_var_name = _test_var_names.at(i);
     493        2533 :     _lfs.Register(test_var_name, std::make_shared<mfem::ParLinearForm>(_test_pfespaces.at(i)));
     494        2533 :     _lfs.GetRef(test_var_name) = 0.0;
     495        2533 :   }
     496             : 
     497        5029 :   for (auto & test_var_name : _test_var_names)
     498             :   {
     499             :     // Apply kernels
     500        2533 :     auto lf = _lfs.GetShared(test_var_name);
     501        2533 :     ApplyDomainLFIntegrators(test_var_name, lf, _kernels_map);
     502        2533 :     ApplyBoundaryLFIntegrators(test_var_name, lf, _integrated_bc_map);
     503        2533 :     lf->Assemble();
     504        2531 :   }
     505             : 
     506             :   // Apply essential boundary conditions
     507        2496 :   ApplyEssentialBCs();
     508             : 
     509             :   // Eliminate trivially eliminated variables by subtracting contributions from linear forms
     510        2496 :   EliminateCoupledVariables();
     511        2496 : }
     512             : 
     513             : void
     514         872 : EquationSystem::BuildNonlinearForms()
     515             : {
     516             :   // Register non-linear Action forms
     517        1757 :   for (const auto i : index_range(_test_var_names))
     518             :   {
     519         889 :     auto test_var_name = _test_var_names.at(i);
     520         889 :     _nlfs.Register(test_var_name, std::make_shared<mfem::ParNonlinearForm>(_test_pfespaces.at(i)));
     521             :     // Apply kernels
     522         889 :     auto nlf = _nlfs.GetShared(test_var_name);
     523         889 :     nlf->SetEssentialTrueDofs(_ess_tdof_lists.at(i));
     524         891 :     ApplyDomainNLFIntegrators(test_var_name, nlf, _kernels_map, std::nullopt);
     525         889 :     ApplyBoundaryNLFIntegrators(test_var_name, nlf, _integrated_bc_map, std::nullopt);
     526         893 :   }
     527         868 : }
     528             : 
     529             : void
     530         874 : EquationSystem::BuildBilinearForms()
     531             : {
     532             :   // Register bilinear forms
     533        1765 :   for (const auto i : index_range(_test_var_names))
     534             :   {
     535         891 :     auto test_var_name = _test_var_names.at(i);
     536         891 :     _blfs.Register(test_var_name, std::make_shared<mfem::ParBilinearForm>(_test_pfespaces.at(i)));
     537             : 
     538             :     // Apply kernels
     539         891 :     auto blf = _blfs.GetShared(test_var_name);
     540         891 :     blf->SetAssemblyLevel(_assembly_level);
     541        1782 :     ApplyBoundaryBLFIntegrators<mfem::ParBilinearForm>(
     542         891 :         test_var_name, test_var_name, blf, _integrated_bc_map);
     543        1782 :     ApplyDomainBLFIntegrators<mfem::ParBilinearForm>(
     544         891 :         test_var_name, test_var_name, blf, _kernels_map);
     545             :     // Assemble
     546         891 :     blf->Assemble();
     547         891 :   }
     548         874 : }
     549             : 
     550             : void
     551         874 : EquationSystem::BuildMixedBilinearForms()
     552             : {
     553             :   // Register mixed bilinear forms. Note that not all combinations may
     554             :   // have a kernel.
     555             : 
     556             :   // Create mblf for each test/coupled variable pair with an added kernel.
     557             :   // Mixed bilinear forms with coupled variables that are not trial variables are
     558             :   // associated with contributions from eliminated variables.
     559        1765 :   for (const auto i : index_range(_test_var_names))
     560             :   {
     561         891 :     auto test_var_name = _test_var_names.at(i);
     562         891 :     auto test_mblfs = std::make_shared<Moose::MFEM::NamedFieldsMap<mfem::ParMixedBilinearForm>>();
     563        1910 :     for (const auto j : index_range(_coupled_var_names))
     564             :     {
     565        1019 :       const auto & coupled_var_name = _coupled_var_names.at(j);
     566        2038 :       auto mblf = std::make_shared<mfem::ParMixedBilinearForm>(_coupled_pfespaces.at(j),
     567        1019 :                                                                _test_pfespaces.at(i));
     568             :       // Register MixedBilinearForm if kernels exist for it, and assemble kernels
     569        1019 :       if (_kernels_map.Has(test_var_name) &&
     570        2017 :           _kernels_map.Get(test_var_name)->Has(coupled_var_name) &&
     571         998 :           test_var_name != coupled_var_name)
     572             :       {
     573         120 :         mblf->SetAssemblyLevel(_assembly_level);
     574             :         // Apply all mixed kernels with this test/trial pair
     575         240 :         ApplyDomainBLFIntegrators<mfem::ParMixedBilinearForm>(
     576         120 :             coupled_var_name, test_var_name, mblf, _kernels_map);
     577             :         // Assemble mixed bilinear forms
     578         120 :         mblf->Assemble();
     579             :         // Register mixed bilinear forms associated with a single trial variable
     580             :         // for the current test variable
     581         120 :         test_mblfs->Register(coupled_var_name, mblf);
     582             :       }
     583        1019 :     }
     584             :     // Register all mixed bilinear form sets associated with a single test variable
     585         891 :     _mblfs.Register(test_var_name, test_mblfs);
     586         891 :   }
     587         874 : }
     588             : 
     589             : void
     590        2498 : EquationSystem::BuildEquationSystem()
     591             : {
     592        2498 :   BuildBilinearForms();
     593        2498 :   BuildMixedBilinearForms();
     594        2498 :   BuildLinearForms();
     595        2496 :   BuildNonlinearForms();
     596        2492 : }
     597             : 
     598             : void
     599        2533 : EquationSystem::ApplyDomainLFIntegrators(
     600             :     const std::string & test_var_name,
     601             :     std::shared_ptr<mfem::ParLinearForm> form,
     602             :     NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>> & kernels_map)
     603             : {
     604        2533 :   if (kernels_map.Has(test_var_name) && kernels_map.Get(test_var_name)->Has(test_var_name))
     605             :   {
     606        2484 :     auto kernels = kernels_map.GetRef(test_var_name).GetRef(test_var_name);
     607        6124 :     for (auto & kernel : kernels)
     608             :     {
     609        3640 :       mfem::LinearFormIntegrator * integ = kernel->createLFIntegrator();
     610             : 
     611        3640 :       if (integ)
     612             :       {
     613         461 :         kernel->isSubdomainRestricted()
     614         461 :             ? form->AddDomainIntegrator(std::move(integ), kernel->getSubdomainMarkers())
     615         426 :             : form->AddDomainIntegrator(std::move(integ));
     616             :       }
     617             :     }
     618        2484 :   }
     619        2533 : }
     620             : 
     621             : void
     622        2531 : EquationSystem::ApplyDomainNLFIntegrators(
     623             :     const std::string & test_var_name,
     624             :     std::shared_ptr<mfem::ParNonlinearForm> form,
     625             :     NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMKernel>>>> & kernels_map,
     626             :     std::optional<mfem::real_t> scale_factor)
     627             : {
     628        2531 :   if (kernels_map.Has(test_var_name))
     629        5131 :     for (const auto & [trial_var_name, kernels] : kernels_map.GetRef(test_var_name))
     630        6392 :       for (auto & kernel : *kernels)
     631        3774 :         if (auto * integ = kernel->createNLIntegrator())
     632             :         {
     633         646 :           if (_solver_requires_gradient && (trial_var_name != test_var_name))
     634           2 :             mooseError("Support for off-diagonal MFEM nonlinear domain integrators in conjunction "
     635             :                        "with a nonlinear solver that requires a gradient is not currently "
     636             :                        "implemented. Kernel '",
     637           2 :                        kernel->name(),
     638             :                        "' contributes to test variable '",
     639             :                        test_var_name,
     640             :                        "' from trial variable '",
     641             :                        trial_var_name,
     642             :                        "'.");
     643             : 
     644         644 :           _non_linear = true;
     645         644 :           if (scale_factor.has_value())
     646         624 :             integ = new NLScaleIntegrator(integ, scale_factor.value(), true);
     647         644 :           kernel->isSubdomainRestricted()
     648         644 :               ? form->AddDomainIntegrator(std::move(integ), kernel->getSubdomainMarkers())
     649         620 :               : form->AddDomainIntegrator(std::move(integ));
     650             :         }
     651        2529 : }
     652             : 
     653             : void
     654        2533 : EquationSystem::ApplyBoundaryLFIntegrators(
     655             :     const std::string & test_var_name,
     656             :     std::shared_ptr<mfem::ParLinearForm> form,
     657             :     NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMIntegratedBC>>>> &
     658             :         integrated_bc_map)
     659             : {
     660        2647 :   if (integrated_bc_map.Has(test_var_name) &&
     661         114 :       integrated_bc_map.Get(test_var_name)->Has(test_var_name))
     662             :   {
     663         110 :     auto bcs = integrated_bc_map.GetRef(test_var_name).GetRef(test_var_name);
     664         238 :     for (auto & bc : bcs)
     665             :     {
     666         128 :       mfem::LinearFormIntegrator * integ = bc->createLFIntegrator();
     667             : 
     668         128 :       if (integ)
     669             :       {
     670          92 :         bc->isBoundaryRestricted()
     671          92 :             ? form->AddBoundaryIntegrator(std::move(integ), bc->getBoundaryMarkers())
     672          13 :             : form->AddBoundaryIntegrator(std::move(integ));
     673             :       }
     674             :     }
     675         110 :   }
     676        2533 : }
     677             : 
     678             : void
     679        2529 : EquationSystem::ApplyBoundaryNLFIntegrators(
     680             :     const std::string & test_var_name,
     681             :     std::shared_ptr<mfem::ParNonlinearForm> form,
     682             :     NamedFieldsMap<NamedFieldsMap<std::vector<std::shared_ptr<MFEMIntegratedBC>>>> &
     683             :         integrated_bc_map,
     684             :     std::optional<mfem::real_t> scale_factor)
     685             : {
     686        2529 :   if (integrated_bc_map.Has(test_var_name))
     687         226 :     for (const auto & [trial_var_name, bcs] : integrated_bc_map.GetRef(test_var_name))
     688         244 :       for (auto & bc : *bcs)
     689         132 :         if (auto * integ = bc->createNLIntegrator())
     690             :         {
     691          38 :           if (_solver_requires_gradient && (test_var_name != trial_var_name))
     692           2 :             mooseError(
     693             :                 "Support for Off-diagonal MFEM nonlinear boundary integrators in conjunction with "
     694             :                 "a nonlinear solver that requires a gradient is not currently "
     695             :                 "implemented. Boundary condition '",
     696           2 :                 bc->name(),
     697             :                 "' contributes to test variable '",
     698             :                 test_var_name,
     699             :                 "' from trial variable '",
     700             :                 trial_var_name,
     701             :                 "'.");
     702             : 
     703          36 :           _non_linear = true;
     704          36 :           if (scale_factor.has_value())
     705          36 :             integ = new NLScaleIntegrator(integ, scale_factor.value(), true);
     706          36 :           bc->isBoundaryRestricted()
     707          36 :               ? form->AddBoundaryIntegrator(std::move(integ), bc->getBoundaryMarkers())
     708           0 :               : form->AddBoundaryIntegrator(std::move(integ));
     709             :         }
     710        2527 : }
     711             : 
     712             : void
     713        2495 : EquationSystem::PrepareLinearSolver(LinearSolverBase & solver)
     714             : {
     715        2495 :   if (solver.IsLOR())
     716             :   {
     717          52 :     if (Complex())
     718           0 :       mooseError("LOR solve is not supported for complex equation systems.");
     719          52 :     if (_test_var_names.size() > 1)
     720           0 :       mooseError("LOR solve is only supported for single-variable systems");
     721             : 
     722          52 :     const auto & test_var_name = _test_var_names.at(0);
     723          52 :     const auto & trial_var_name = _trial_var_names.at(0);
     724          52 :     mfem::ParGridFunction & trial_gf = _gfuncs->GetRef(trial_var_name);
     725          52 :     mfem::Array<int> global_ess_markers(trial_gf.ParFESpace()->GetParMesh()->bdr_attributes.Max());
     726          52 :     global_ess_markers = 0;
     727          52 :     ApplyEssentialBC(trial_var_name, trial_gf, global_ess_markers);
     728          52 :     solver.SetupLOR(*_blfs.Get(test_var_name), global_ess_markers);
     729          52 :   }
     730             : 
     731             :   mooseAssert(_linear_operator.Ptr(),
     732             :               "If we are preparing a linear solver, we better have a linear operator");
     733        2495 :   solver.SetOperator(_linear_operator);
     734        2495 : }
     735             : 
     736             : } // namespace Moose::MFEM
     737             : 
     738             : #endif

Generated by: LCOV version 1.14