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

Generated by: LCOV version 1.14