LCOV - code coverage report
Current view: top level - src/systems - equation_systems.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4640 (c5f9f0) with base a68dd6 Lines: 676 825 81.9 %
Date: 2026-11-02 23:02:27 Functions: 53 66 80.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : // Local Includes
      20             : #include "libmesh/default_coupling.h" // For downconversion
      21             : #include "libmesh/dof_map.h"
      22             : #include "libmesh/eigen_system.h"
      23             : #include "libmesh/elem.h"
      24             : #include "libmesh/explicit_system.h"
      25             : #include "libmesh/fe_base.h"
      26             : #include "libmesh/fe_interface.h"
      27             : #include "libmesh/fem_context.h"
      28             : #include "libmesh/frequency_system.h"
      29             : #include "libmesh/int_range.h"
      30             : #include "libmesh/libmesh_logging.h"
      31             : #include "libmesh/linear_implicit_system.h"
      32             : #include "libmesh/mesh_base.h"
      33             : #include "libmesh/mesh_refinement.h"
      34             : #include "libmesh/newmark_system.h"
      35             : #include "libmesh/node_range.h"
      36             : #include "libmesh/nonlinear_implicit_system.h"
      37             : #include "libmesh/parallel.h"
      38             : #include "libmesh/rb_construction.h"
      39             : #include "libmesh/remote_elem.h"
      40             : #include "libmesh/tensor_tools.h"
      41             : #include "libmesh/transient_rb_construction.h"
      42             : #include "libmesh/transient_system.h"
      43             : 
      44             : // System includes
      45             : #include <functional> // std::plus
      46             : #include <numeric> // std::iota
      47             : #include <sstream>
      48             : 
      49             : // Include the systems before this one to avoid
      50             : // overlapping forward declarations.
      51             : #include "libmesh/equation_systems.h"
      52             : 
      53             : namespace libMesh
      54             : {
      55             : 
      56      354595 : EquationSystems::EquationSystems (MeshBase & m) :
      57             :   ParallelObject (m),
      58      334483 :   _mesh          (m),
      59      334483 :   _refine_in_reinit(true),
      60      364651 :   _enable_default_ghosting(true)
      61             : {
      62             :   // Set default parameters
      63      354595 :   this->parameters.set<Real>        ("linear solver tolerance") = TOLERANCE * TOLERANCE;
      64      354595 :   this->parameters.set<unsigned int>("linear solver maximum iterations") = 5000;
      65      354595 : }
      66             : 
      67             : 
      68             : 
      69      628537 : EquationSystems::~EquationSystems () = default;
      70             : 
      71             : 
      72             : 
      73          71 : void EquationSystems::clear ()
      74             : {
      75             :   // Clear any additional parameters
      76           2 :   parameters.clear ();
      77             : 
      78             :   // Clear the systems.
      79           2 :   _systems.clear();
      80          71 : }
      81             : 
      82             : 
      83             : 
      84      354122 : void EquationSystems::init ()
      85             : {
      86             : #ifndef NDEBUG
      87       20162 :   for (auto i : make_range(this->n_systems()))
      88       10118 :     libmesh_assert(!this->get_system(i).is_initialized());
      89             : #endif
      90             : 
      91      354122 :   this->reinit_mesh();
      92      354028 : }
      93             : 
      94             : 
      95             : 
      96       25759 : void EquationSystems::reinit ()
      97             : {
      98       25759 :   const bool mesh_changed = this->reinit_solutions();
      99             : 
     100             :   // If the mesh has changed, systems will need to reinitialize their
     101             :   // own data on the new mesh.
     102       25759 :   if (mesh_changed)
     103       25759 :     this->reinit_systems();
     104       25759 : }
     105             : 
     106      354548 : void EquationSystems::reinit_mesh ()
     107             : {
     108       10056 :   const unsigned int n_sys = this->n_systems();
     109             : 
     110       10056 :   libmesh_assert_not_equal_to (n_sys, 0);
     111             : 
     112             :   // Tell all the \p DofObject entities how many systems
     113             :   // there are.
     114             :   Threads::parallel_for_each
     115      354548 :     (_mesh.node_stored_range(),
     116    15566417 :      [n_sys](Node * node) { node->set_n_systems(n_sys); });
     117             : 
     118             :   Threads::parallel_for_each
     119      354548 :     (_mesh.element_stored_range(),
     120     6691642 :      [n_sys](Elem * elem) { elem->set_n_systems(n_sys); });
     121             : 
     122             :   //for (auto i : make_range(this->n_systems()))
     123             :     //this->get_system(i).init();
     124             : 
     125             : #ifdef LIBMESH_ENABLE_AMR
     126      374660 :   MeshRefinement mesh_refine(_mesh);
     127      354548 :   mesh_refine.clean_refinement_flags();
     128             : #endif
     129             : 
     130             :  // Now loop over all the systems belonging to this ES
     131             :  // and call reinit_mesh for each system
     132      711687 :  for (auto i : make_range(this->n_systems()))
     133      357233 :     this->get_system(i).reinit_mesh();
     134             : 
     135      354540 : }
     136             : 
     137       25759 : bool EquationSystems::reinit_solutions ()
     138             : {
     139         856 :   parallel_object_only();
     140             : 
     141         856 :   const unsigned int n_sys = this->n_systems();
     142         856 :   libmesh_assert_not_equal_to (n_sys, 0);
     143             : 
     144             :   // And any new systems will need initialization
     145       55765 :   for (unsigned int i=0; i != n_sys; ++i)
     146       30006 :     if (!this->get_system(i).is_initialized())
     147         143 :       this->get_system(i).init();
     148             : 
     149             :   // We used to assert that all nodes and elements *already* had
     150             :   // n_systems() properly set; however this is false in the case where
     151             :   // user code has manually added nodes and/or elements to an
     152             :   // already-initialized system.
     153             : 
     154             :   // Make sure all the \p DofObject entities know how many systems
     155             :   // there are.
     156             :   {
     157             :     // All the nodes
     158             :     Threads::parallel_for_each
     159       25759 :       (_mesh.node_stored_range(),
     160    12279126 :        [n_sys](Node * node) { node->set_n_systems(n_sys); });
     161             : 
     162             :     // All the elements
     163             :     Threads::parallel_for_each
     164       25759 :       (_mesh.element_stored_range(),
     165    10268068 :        [n_sys](Elem * elem) { elem->set_n_systems(n_sys); });
     166             :   }
     167             : 
     168             :   // Localize each system's vectors
     169       55765 :   for (unsigned int i=0; i != n_sys; ++i)
     170       30006 :     this->get_system(i).re_update();
     171             : 
     172             : #ifdef LIBMESH_ENABLE_AMR
     173             : 
     174         856 :   bool mesh_changed = false;
     175             : 
     176             :   // FIXME: For backwards compatibility, assume
     177             :   // refine_and_coarsen_elements or refine_uniformly have already
     178             :   // been called
     179             :   {
     180       55765 :     for (unsigned int i=0; i != n_sys; ++i)
     181             :       {
     182         968 :         System & sys = this->get_system(i);
     183             : 
     184             :         // Even if the system doesn't have any variables in it we want
     185             :         // consistent behavior; e.g. distribute_dofs should have the
     186             :         // opportunity to count up zero dofs on each processor.
     187             :         //
     188             :         // Who's been adding zero-var systems anyway, outside of my
     189             :         // unit tests? - RHS
     190             :         // if (!sys.n_vars())
     191             :         // continue;
     192             : 
     193       30006 :         sys.get_dof_map().distribute_dofs(_mesh);
     194             : 
     195             :         // Recreate any user or internal constraints
     196       30006 :         sys.reinit_constraints();
     197             : 
     198             :         // Even if there weren't any constraint changes,
     199             :         // reinit_constraints() did prepare_send_list() for us.
     200             : 
     201       30006 :         sys.prolong_vectors();
     202             :       }
     203         856 :     mesh_changed = true;
     204             :   }
     205             : 
     206       25759 :   if (this->_refine_in_reinit)
     207             :     {
     208             :       // Don't override any user refinement settings
     209       27321 :       MeshRefinement mesh_refine(_mesh);
     210       25617 :       mesh_refine.face_level_mismatch_limit() = 0; // unlimited
     211       25617 :       mesh_refine.overrefined_boundary_limit() = -1; // unlimited
     212       25617 :       mesh_refine.underrefined_boundary_limit() = -1; // unlimited
     213             : 
     214             :       // Try to coarsen the mesh, then restrict each system's vectors
     215             :       // if necessary
     216       25617 :       if (mesh_refine.coarsen_elements())
     217             :         {
     218         142 :           for (auto i : make_range(this->n_systems()))
     219             :             {
     220           2 :               System & sys = this->get_system(i);
     221          71 :               sys.get_dof_map().distribute_dofs(_mesh);
     222          71 :               sys.reinit_constraints();
     223             : 
     224             :               // Even if there weren't any constraint changes,
     225             :               // reinit_constraints() did prepare_send_list() for us.
     226             : 
     227          71 :               sys.restrict_vectors();
     228             :             }
     229           2 :           mesh_changed = true;
     230             :         }
     231             : 
     232             :       // Once vectors are all restricted, we can delete
     233             :       // children of coarsened elements
     234         852 :       if (mesh_changed)
     235       25617 :         this->get_mesh().contract();
     236             : 
     237             :       // Try to refine the mesh, then prolong each system's vectors
     238             :       // if necessary
     239       25617 :       if (mesh_refine.refine_elements())
     240             :         {
     241        2414 :           for (auto i : make_range(this->n_systems()))
     242             :             {
     243          34 :               System & sys = this->get_system(i);
     244        1207 :               sys.get_dof_map().distribute_dofs(_mesh);
     245        1207 :               sys.reinit_constraints();
     246             : 
     247             :               // Even if there weren't any constraint changes,
     248             :               // reinit_constraints() did prepare_send_list() for us.
     249             : 
     250        1207 :               sys.prolong_vectors();
     251             :             }
     252          34 :           mesh_changed = true;
     253             :         }
     254       23913 :     }
     255             : 
     256         856 :   return mesh_changed;
     257             : 
     258             : #endif // #ifdef LIBMESH_ENABLE_AMR
     259             : 
     260             :   return false;
     261             : }
     262             : 
     263             : 
     264             : 
     265       25759 : void EquationSystems::reinit_systems()
     266             : {
     267       55765 :   for (auto i : make_range(this->n_systems()))
     268       30006 :     this->get_system(i).reinit();
     269       25759 : }
     270             : 
     271             : 
     272             : 
     273           0 : void EquationSystems::allgather ()
     274             : {
     275             :   // A serial mesh means nothing needs to be done
     276           0 :   if (_mesh.is_serial())
     277           0 :     return;
     278             : 
     279           0 :   const unsigned int n_sys = this->n_systems();
     280             : 
     281           0 :   libmesh_assert_not_equal_to (n_sys, 0);
     282             : 
     283             :   // Gather the mesh
     284           0 :   _mesh.allgather();
     285             : 
     286             :   // Tell all the \p DofObject entities how many systems
     287             :   // there are.
     288             :   Threads::parallel_for_each
     289           0 :     (_mesh.node_stored_range(),
     290           0 :      [n_sys](Node * node) { node->set_n_systems(n_sys); });
     291             : 
     292             :   Threads::parallel_for_each
     293           0 :     (_mesh.element_stored_range(),
     294           0 :      [n_sys](Elem * elem) { elem->set_n_systems(n_sys); });
     295             : 
     296             :   // And distribute each system's dofs
     297           0 :   for (auto i : make_range(this->n_systems()))
     298             :     {
     299           0 :       System & sys = this->get_system(i);
     300           0 :       DofMap & dof_map = sys.get_dof_map();
     301           0 :       dof_map.distribute_dofs(_mesh);
     302             : 
     303             :       // The user probably won't need constraint equations or the
     304             :       // send_list after an allgather, but let's keep it in consistent
     305             :       // shape just in case.
     306           0 :       sys.reinit_constraints();
     307             : 
     308             :       // Even if there weren't any constraint changes,
     309             :       // reinit_constraints() did prepare_send_list() for us.
     310             :     }
     311             : }
     312             : 
     313             : 
     314             : 
     315         213 : void EquationSystems::enable_default_ghosting (bool enable)
     316             : {
     317         213 :   _enable_default_ghosting = enable;
     318          12 :   MeshBase &mesh = this->get_mesh();
     319             : 
     320         213 :   if (enable)
     321          71 :     mesh.add_ghosting_functor(mesh.default_ghosting());
     322             :   else
     323         142 :     mesh.remove_ghosting_functor(mesh.default_ghosting());
     324             : 
     325         426 :   for (auto i : make_range(this->n_systems()))
     326             :     {
     327           6 :       DofMap & dof_map = this->get_system(i).get_dof_map();
     328         213 :       if (enable)
     329          71 :         dof_map.add_default_ghosting();
     330             :       else
     331         142 :         dof_map.remove_default_ghosting();
     332             :     }
     333         213 : }
     334             : 
     335             : 
     336             : 
     337       10928 : void EquationSystems::update ()
     338             : {
     339         942 :   LOG_SCOPE("update()", "EquationSystems");
     340             : 
     341             :   // Localize each system's vectors
     342       22557 :   for (auto i : make_range(this->n_systems()))
     343       11629 :     this->get_system(i).update();
     344       10928 : }
     345             : 
     346             : 
     347             : 
     348        5049 : System & EquationSystems::add_system (std::string_view sys_type,
     349             :                                       std::string_view name)
     350             : {
     351             :   // If the user already built a system with this name, we'll
     352             :   // trust them and we'll use it.  That way they can pre-add
     353             :   // non-standard derived system classes, and if their restart file
     354             :   // has some non-standard sys_type we won't throw an error.
     355        5049 :   if (_systems.count(name))
     356             :     {
     357        3645 :       return this->get_system(name);
     358             :     }
     359             :   // Build a basic System
     360        1380 :   else if (sys_type == "Basic")
     361         841 :     this->add_system<System> (name);
     362             : 
     363             :   // Build a Newmark system
     364         563 :   else if (sys_type == "Newmark")
     365           0 :     this->add_system<NewmarkSystem> (name);
     366             : 
     367             :   // Build an Explicit system
     368         561 :   else if ((sys_type == "Explicit"))
     369          71 :     this->add_system<ExplicitSystem> (name);
     370             : 
     371             :   // Build an Implicit system
     372         506 :   else if ((sys_type == "Implicit") ||
     373         506 :            (sys_type == "Steady"  ))
     374           0 :     this->add_system<ImplicitSystem> (name);
     375             : 
     376             :   // build a transient implicit linear system
     377        1434 :   else if ((sys_type == "Transient") ||
     378         506 :            (sys_type == "TransientImplicit") ||
     379         506 :            (sys_type == "TransientLinearImplicit"))
     380         212 :     this->add_system<TransientLinearImplicitSystem> (name);
     381             : 
     382             :   // build a transient implicit nonlinear system
     383         280 :   else if (sys_type == "TransientNonlinearImplicit")
     384           0 :     this->add_system<TransientNonlinearImplicitSystem> (name);
     385             : 
     386             :   // build a transient explicit system
     387         280 :   else if (sys_type == "TransientExplicit")
     388           0 :     this->add_system<TransientExplicitSystem> (name);
     389             : 
     390             :   // build a linear implicit system
     391         280 :   else if (sys_type == "LinearImplicit")
     392           0 :     this->add_system<LinearImplicitSystem> (name);
     393             : 
     394             :   // build a nonlinear implicit system
     395         272 :   else if (sys_type == "NonlinearImplicit")
     396         280 :     this->add_system<NonlinearImplicitSystem> (name);
     397             : 
     398             :   // build a Reduced Basis Construction system
     399           0 :   else if (sys_type == "RBConstruction")
     400           0 :     this->add_system<RBConstruction> (name);
     401             : 
     402             :   // build a transient Reduced Basis Construction system
     403           0 :   else if (sys_type == "TransientRBConstruction")
     404           0 :     this->add_system<TransientRBConstruction> (name);
     405             : 
     406             : #ifdef LIBMESH_HAVE_SLEPC
     407             :   // build an eigen system
     408           0 :   else if (sys_type == "Eigen")
     409           0 :     this->add_system<EigenSystem> (name);
     410           0 :   else if (sys_type == "TransientEigenSystem")
     411           0 :     this->add_system<TransientEigenSystem> (name);
     412             : #endif
     413             : 
     414             : #if defined(LIBMESH_USE_COMPLEX_NUMBERS)
     415             :   // build a frequency system
     416           0 :   else if (sys_type == "Frequency")
     417           0 :     this->add_system<FrequencySystem> (name);
     418             : #endif
     419             : 
     420             :   else
     421           0 :     libmesh_error_msg("ERROR: Unknown system type: " << sys_type);
     422             : 
     423             :   // Return a reference to the new system
     424             :   //return (*this)(name);
     425        1404 :   return this->get_system(name);
     426             : }
     427             : 
     428             : 
     429             : 
     430           0 : void EquationSystems::solve ()
     431             : {
     432           0 :   libmesh_assert (this->n_systems());
     433             : 
     434           0 :   for (auto i : make_range(this->n_systems()))
     435           0 :     this->get_system(i).solve();
     436           0 : }
     437             : 
     438             : 
     439             : 
     440           0 : void EquationSystems::sensitivity_solve (const ParameterVector & parameters_in)
     441             : {
     442           0 :   libmesh_assert (this->n_systems());
     443             : 
     444           0 :   for (auto i : make_range(this->n_systems()))
     445           0 :     this->get_system(i).sensitivity_solve(parameters_in);
     446           0 : }
     447             : 
     448             : 
     449             : 
     450           0 : void EquationSystems::adjoint_solve (const QoISet & qoi_indices)
     451             : {
     452           0 :   libmesh_assert (this->n_systems());
     453             : 
     454           0 :   for (unsigned int i=this->n_systems(); i != 0; --i)
     455           0 :     this->get_system(i-1).adjoint_solve(qoi_indices);
     456           0 : }
     457             : 
     458             : 
     459             : 
     460       86254 : void EquationSystems::build_variable_names (std::vector<std::string> & var_names,
     461             :                                             const FEType * type,
     462             :                                             const std::set<std::string> * system_names) const
     463             : {
     464             :   // start indexing at end of possibly non-empty vector of variable names to avoid overwriting them
     465       88958 :   unsigned int var_num = var_names.size();
     466             : 
     467             :   // We'll want to double-check that we don't have any naming
     468             :   // conflicts; this API causes problems down the line if so.
     469        5408 :   std::unordered_multiset<std::string> seen_names;
     470             : 
     471             :   // Need to size var_names by scalar variables plus all the
     472             :   // vector components for all the vector variables
     473             :   //Could this be replaced by a/some convenience methods?[PB]
     474             :   {
     475        2704 :     unsigned int n_scalar_vars = 0;
     476        2704 :     unsigned int n_vector_vars = 0;
     477             : 
     478      176294 :     for (const auto & [sys_name, sys_ptr] : _systems)
     479             :       {
     480             :         // Check current system is listed in system_names, and skip pos if not
     481        5664 :         bool use_current_system = (system_names == nullptr);
     482       90040 :         if (!use_current_system)
     483         917 :           use_current_system = system_names->count(sys_name);
     484       90040 :         if (!use_current_system || sys_ptr->hide_output())
     485             :           {
     486        4434 :             for (auto vn : make_range(sys_ptr->n_vars()))
     487        2317 :               seen_names.insert(sys_ptr->variable_name(vn));
     488        2063 :             continue;
     489        2009 :           }
     490             : 
     491      209422 :         for (auto vn : make_range(sys_ptr->n_vars()))
     492             :           {
     493      121499 :             seen_names.insert(sys_ptr->variable_name(vn));
     494      121499 :             if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
     495        6879 :               n_vector_vars++;
     496             :             else
     497      114620 :               n_scalar_vars++;
     498             :           }
     499             :       }
     500             : 
     501             :     // Here, we're assuming the number of vector components is the same
     502             :     // as the mesh spatial dimension.
     503        5408 :     unsigned int dim = this->get_mesh().spatial_dimension();
     504       86254 :     unsigned int nv = n_scalar_vars + dim*n_vector_vars;
     505             : 
     506             :     // We'd better not have more than dim*this->n_vars() (all vector variables)
     507             :     // Treat the NodeElem-only mesh case as dim=1
     508        2704 :     libmesh_assert_less_equal ( nv, (dim > 0 ? dim : 1)*this->n_vars() );
     509             : 
     510             :     // 'nv' represents the max possible number of output variables, so allocate enough memory for
     511             :     // all variables in the system to be populated here. When this is called more than once on a
     512             :     // single 'var_names' vector, different filters should be used such that duplicates don't occur.
     513       86254 :     var_names.resize( nv );
     514             :   }
     515             : 
     516      176223 :   for (const auto & [sys_name, sys_ptr] : _systems)
     517             :     {
     518             :       // Check current system is listed in system_names, and skip pos if not
     519        5664 :       bool use_current_system = (system_names == nullptr);
     520       90040 :       if (!use_current_system)
     521         917 :         use_current_system = system_names->count(sys_name);
     522       90040 :       if (!use_current_system || sys_ptr->hide_output())
     523        2063 :         continue;
     524             : 
     525      209351 :       for (auto vn : make_range(sys_ptr->n_vars()))
     526             :         {
     527      121499 :           const std::string & var_name = sys_ptr->variable_name(vn);
     528      121499 :           const FEType & fe_type = sys_ptr->variable_type(vn);
     529             : 
     530      121499 :           unsigned int n_vec_dim = FEInterface::n_vec_dim( sys_ptr->get_mesh(), fe_type);
     531             : 
     532             :           // Filter on the type if requested
     533      121499 :           if (type == nullptr || (type && *type == fe_type))
     534             :             {
     535      121286 :               if (FEInterface::field_type(fe_type) == TYPE_VECTOR)
     536             :                 {
     537        6879 :                   switch(n_vec_dim)
     538             :                     {
     539           0 :                     case 0:
     540             :                     case 1:
     541           0 :                       var_names[var_num++] = var_name;
     542           0 :                       libmesh_error_msg_if(seen_names.count(var_name) > 1,
     543             :                                            "Duplicate variable name "+var_name);
     544           0 :                       break;
     545        5622 :                     case 2:
     546        5622 :                       var_names[var_num++] = var_name+"_x";
     547        5622 :                       var_names[var_num++] = var_name+"_y";
     548       11307 :                       libmesh_error_msg_if(seen_names.count(var_name+"_x"),
     549             :                                            "Duplicate variable name "+var_name+"_x");
     550       10960 :                       libmesh_error_msg_if(seen_names.count(var_name+"_y"),
     551             :                                            "Duplicate variable name "+var_name+"_y");
     552         142 :                       break;
     553        1257 :                     case 3:
     554        1257 :                       var_names[var_num++] = var_name+"_x";
     555        1257 :                       var_names[var_num++] = var_name+"_y";
     556        1257 :                       var_names[var_num++] = var_name+"_z";
     557        2486 :                       libmesh_error_msg_if(seen_names.count(var_name+"_x"),
     558             :                                            "Duplicate variable name "+var_name+"_x");
     559        2486 :                       libmesh_error_msg_if(seen_names.count(var_name+"_y"),
     560             :                                            "Duplicate variable name "+var_name+"_y");
     561        2553 :                       libmesh_error_msg_if(seen_names.count(var_name+"_z"),
     562             :                                            "Duplicate variable name "+var_name+"_z");
     563          28 :                       break;
     564           0 :                     default:
     565           0 :                       libmesh_error_msg("Invalid dim in build_variable_names");
     566             :                     }
     567             :                 }
     568             :               else
     569      114407 :                 var_names[var_num++] = var_name;
     570             :             }
     571             :         }
     572             :     }
     573             :   // Now resize again in case we filtered any names
     574       86183 :   var_names.resize(var_num);
     575       86183 : }
     576             : 
     577       11961 : bool EquationSystems::is_elemental_data_fe_type (const FEType & type,
     578             :                                                  bool allow_high_order_discontinuous)
     579             : {
     580       12297 :   return (type.order == CONSTANT || allow_high_order_discontinuous) &&
     581       11891 :          (type.family == MONOMIAL ||
     582         658 :           type.family == MONOMIAL_VEC ||
     583       11979 :           type.family == XYZ);
     584             : }
     585             : 
     586             : 
     587             : 
     588          70 : void EquationSystems::build_elemental_data_variable_names
     589             :   (std::vector<std::string> & var_names,
     590             :    const std::set<std::string> * system_names,
     591             :    bool allow_high_order_discontinuous) const
     592             : {
     593          74 :   const std::vector<std::string> name_filter = var_names;
     594           2 :   const bool is_names_empty = name_filter.empty();
     595           2 :   var_names.clear();
     596             : 
     597         280 :   const std::vector<std::string> component_suffix = {"_x", "_y", "_z"};
     598          70 :   const unsigned int dim = _mesh.spatial_dimension();
     599          70 :   libmesh_error_msg_if(dim > 3, "Invalid dim in build_elemental_data_variable_names");
     600             : 
     601         140 :   for (const auto & [sys_name, sys_ptr] : _systems)
     602             :     {
     603          70 :       const bool use_current_system = (system_names == nullptr) || system_names->count(sys_name);
     604          70 :       if (!use_current_system || sys_ptr->hide_output())
     605           0 :         continue;
     606             : 
     607         350 :       for (auto var : make_range(sys_ptr->n_vars()))
     608             :         {
     609         280 :           const FEType & var_type = sys_ptr->variable_type(var);
     610         280 :           if (!EquationSystems::is_elemental_data_fe_type(var_type, allow_high_order_discontinuous))
     611          68 :             continue;
     612             : 
     613         210 :           if (FEInterface::field_type(var_type) == TYPE_VECTOR)
     614             :             {
     615         280 :               for (auto comp : make_range(dim))
     616             :                 {
     617             :                   const std::string name =
     618         222 :                     sys_ptr->variable_name(var) + component_suffix[comp];
     619             : 
     620         210 :                   if (is_names_empty ||
     621           6 :                       std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end())
     622         210 :                     var_names.push_back(name);
     623             :                 }
     624             :             }
     625             :           else
     626             :             {
     627         140 :               const std::string & name = sys_ptr->variable_name(var);
     628             : 
     629         140 :               if (is_names_empty ||
     630           4 :                   std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end())
     631         140 :                 var_names.push_back(name);
     632             :             }
     633             :         }
     634             :     }
     635             : 
     636          70 :   std::sort(var_names.begin(), var_names.end());
     637         202 : }
     638             : 
     639             : 
     640             : 
     641           0 : void EquationSystems::build_solution_vector (std::vector<Number> &,
     642             :                                              std::string_view,
     643             :                                              std::string_view) const
     644             : {
     645             :   // TODO:[BSK] re-implement this from the method below
     646           0 :   libmesh_not_implemented();
     647             : }
     648             : 
     649             : 
     650             : 
     651             : 
     652             : std::unique_ptr<NumericVector<Number>>
     653       79827 : EquationSystems::build_parallel_solution_vector(const std::set<std::string> * system_names,
     654             :                                                 bool add_sides) const
     655             : {
     656        7344 :   LOG_SCOPE("build_parallel_solution_vector()", "EquationSystems");
     657             : 
     658             :   // This function must be run on all processors at once
     659        2448 :   parallel_object_only();
     660             : 
     661       79827 :   const unsigned int dim = _mesh.spatial_dimension();
     662       79827 :   const dof_id_type max_nn   = _mesh.max_node_id();
     663             : 
     664             :   // allocate vector storage to hold
     665             :   // (max_node_id)*(number_of_variables) entries.
     666             :   //
     667             :   // If node renumbering is disabled and adaptive coarsening has
     668             :   // created gaps between node numbers, then this vector will be
     669             :   // sparse.
     670             :   //
     671             :   // We have to differentiate between between scalar and vector
     672             :   // variables. We intercept vector variables and treat each
     673             :   // component as a scalar variable (consistently with build_solution_names).
     674             : 
     675        2448 :   unsigned int nv = 0;
     676             : 
     677             :   //Could this be replaced by a/some convenience methods?[PB]
     678             :   {
     679        2448 :     unsigned int n_scalar_vars = 0;
     680        2448 :     unsigned int n_vector_vars = 0;
     681      163298 :     for (const auto & [sys_name, sys_ptr] : _systems)
     682             :       {
     683             :         // Check current system is listed in system_names, and skip pos if not
     684        5144 :         bool use_current_system = (system_names == nullptr);
     685       83471 :         if (!use_current_system)
     686         423 :           use_current_system = system_names->count(sys_name);
     687       83471 :         if (!use_current_system || sys_ptr->hide_output())
     688        1651 :           continue;
     689             : 
     690      195502 :         for (auto vn : make_range(sys_ptr->n_vars()))
     691             :           {
     692      113724 :             if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
     693        6455 :               n_vector_vars++;
     694             :             else
     695      107269 :               n_scalar_vars++;
     696             :           }
     697             :       }
     698             :     // Here, we're assuming the number of vector components is the same
     699             :     // as the mesh spatial dimension.
     700       79827 :     nv = n_scalar_vars + dim*n_vector_vars;
     701             :   }
     702             : 
     703             :   // Get the number of nodes to store locally.
     704             :   dof_id_type n_local_nodes = cast_int<dof_id_type>
     705      157206 :     (std::distance(_mesh.local_nodes_begin(),
     706      159654 :                    _mesh.local_nodes_end()));
     707             : 
     708             :   // If node renumbering has been disabled, nodes may not be numbered
     709             :   // contiguously, and the number of nodes might not match the
     710             :   // max_node_id.  In this case we just do our best.
     711       79827 :   dof_id_type n_total_nodes = n_local_nodes;
     712       79827 :   _mesh.comm().sum(n_total_nodes);
     713             : 
     714       79827 :   const processor_id_type n_proc = _mesh.comm().size();
     715        4896 :   const processor_id_type my_pid = _mesh.comm().rank();
     716       79827 :   const dof_id_type n_gaps = max_nn - n_total_nodes;
     717       79827 :   const dof_id_type gaps_per_processor = n_gaps / n_proc;
     718       79827 :   const dof_id_type remainder_gaps = n_gaps % n_proc;
     719             : 
     720       84723 :   n_local_nodes = n_local_nodes +      // Actual nodes
     721       79827 :                   gaps_per_processor + // Our even share of gaps
     722       79827 :                   (my_pid < remainder_gaps); // Leftovers
     723             : 
     724             :   // If we've been asked to build added sides' data, we need space to
     725             :   // add it.  Keep track of how much space.
     726       79827 :   dof_id_type local_added_side_nodes = 0,
     727        2448 :               added_side_nodes = 0;
     728             : 
     729             :   // others_added_side_nodes[p]: local_added_side_nodes on rank p
     730        4896 :   std::vector<dof_id_type> others_added_side_nodes;
     731             : 
     732             :   // A map of (element_id, side, side_node) pairs to the corresponding
     733             :   // added side node index.
     734             :   std::map<std::tuple<dof_id_type, unsigned short, unsigned short>,
     735        4896 :            dof_id_type> discontinuous_node_indices;
     736             : 
     737             :   // If we don't have any added side nodes, we'll have no offsets from
     738             :   // them, and we won't care about which offsets apply to which node
     739             :   // ids either.
     740             : 
     741             :   // Number of true nodes on processors [0,p]
     742        4896 :   std::vector<dof_id_type> true_node_offsets;
     743             :   // Number of added (fake) nodes on processors [0,p)
     744        4896 :   std::vector<dof_id_type> added_node_offsets;
     745             : 
     746             :   auto node_id_to_vec_id =
     747    69685712 :     [&true_node_offsets, &added_node_offsets]
     748    84336314 :     (const dof_id_type node_id)
     749             :     {
     750    84326054 :       if (true_node_offsets.empty())
     751    76983311 :         return node_id; // O(1) in the common !add_sides case
     752             : 
     753             :       // Find the processor id that has node_id in the parallel vec
     754       20520 :       const auto lb = std::upper_bound(true_node_offsets.begin(),
     755        2052 :                                        true_node_offsets.end(), node_id);
     756        2052 :       libmesh_assert(lb != true_node_offsets.end());
     757        2052 :       const processor_id_type p = lb - true_node_offsets.begin();
     758             : 
     759       26676 :       return node_id + added_node_offsets[p];
     760       79827 :     };
     761             : 
     762       79827 :   if (add_sides)
     763             :     {
     764        1207 :       true_node_offsets.resize(n_proc);
     765        1207 :       added_node_offsets.resize(n_proc);
     766             : 
     767             :       // One loop to count everyone's new side nodes
     768       10940 :       for (const auto & elem : _mesh.active_element_ptr_range())
     769             :         {
     770       25064 :           for (auto s : elem->side_index_range())
     771             :             {
     772       20400 :               if (redundant_added_side(*elem,s))
     773        5460 :                 continue;
     774             : 
     775             :               const std::vector<unsigned int> side_nodes =
     776       15548 :                 elem->nodes_on_side(s);
     777             : 
     778       15548 :               if (elem->processor_id() == this->processor_id())
     779        3952 :                 local_added_side_nodes += side_nodes.size();
     780             :             }
     781        1139 :         }
     782             : 
     783        1207 :       others_added_side_nodes.resize(n_proc);
     784        1207 :       _mesh.comm().allgather(local_added_side_nodes,
     785             :                              others_added_side_nodes);
     786             : 
     787        1207 :       added_side_nodes = std::accumulate(others_added_side_nodes.begin(),
     788             :                                          others_added_side_nodes.end(), 0,
     789             :                                          std::plus<>());
     790             : 
     791        1207 :       _mesh.comm().allgather(n_local_nodes, true_node_offsets);
     792       11781 :       for (auto p : make_range(n_proc-1))
     793       10642 :         true_node_offsets[p+1] += true_node_offsets[p];
     794          34 :       libmesh_assert_equal_to(true_node_offsets[n_proc-1], _mesh.max_node_id());
     795             : 
     796             :       // For nodes that exist in the mesh, we just need an offset to
     797             :       // tell where to put their solutions.
     798        1207 :       added_node_offsets[0] = 0;
     799       11781 :       for (auto p : make_range(n_proc-1))
     800       10574 :         added_node_offsets[p+1] =
     801       10642 :           added_node_offsets[p] + others_added_side_nodes[p];
     802             : 
     803             :       // For added side nodes, we need to fill a map.  Start after all
     804             :       // the true node for our pid plus all the side nodes for
     805             :       // previous pids
     806        1241 :       dof_id_type node_counter = true_node_offsets[my_pid];
     807        6494 :       for (auto p : make_range(my_pid))
     808        5304 :         node_counter += others_added_side_nodes[p];
     809             : 
     810             :       // One loop to figure out whose added side nodes get which index
     811        4492 :       for (const auto & elem : _mesh.active_local_element_ptr_range())
     812             :         {
     813        6240 :           for (auto s : elem->side_index_range())
     814             :             {
     815        4992 :               if (redundant_added_side(*elem,s))
     816        1344 :                 continue;
     817             : 
     818             :               const std::vector<unsigned int> side_nodes =
     819        3952 :                 elem->nodes_on_side(s);
     820             : 
     821       27264 :               for (auto n : index_range(side_nodes))
     822             :                 discontinuous_node_indices
     823       25584 :                   [std::make_tuple(elem->id(),s,n)] = node_counter++;
     824             :             }
     825        1139 :         }
     826             :     }
     827             : 
     828             :   const dof_id_type
     829       79827 :     n_global_vals = (max_nn + added_side_nodes) * nv,
     830       79827 :     n_local_vals = (n_local_nodes + local_added_side_nodes) * nv;
     831             : 
     832             :   // Create a NumericVector to hold the parallel solution
     833       79827 :   std::unique_ptr<NumericVector<Number>> parallel_soln_ptr = NumericVector<Number>::build(_communicator);
     834        2448 :   NumericVector<Number> & parallel_soln = *parallel_soln_ptr;
     835       79827 :   parallel_soln.init(n_global_vals, n_local_vals, false, PARALLEL);
     836             : 
     837             :   // Create a NumericVector to hold the "repeat_count" for each node - this is essentially
     838             :   // the number of elements contributing to that node's value
     839       82275 :   std::unique_ptr<NumericVector<Number>> repeat_count_ptr = NumericVector<Number>::build(_communicator);
     840        2448 :   NumericVector<Number> & repeat_count = *repeat_count_ptr;
     841       79827 :   repeat_count.init(n_global_vals, n_local_vals, false, PARALLEL);
     842             : 
     843       79827 :   repeat_count.close();
     844             : 
     845        2448 :   unsigned int var_num=0;
     846             : 
     847             :   // For each system in this EquationSystems object,
     848             :   // update the global solution and if we are on processor 0,
     849             :   // loop over the elements and build the nodal solution
     850             :   // from the element solution.  Then insert this nodal solution
     851             :   // into the vector passed to build_solution_vector.
     852      163298 :   for (const auto & [sys_name, sys_ptr] : _systems)
     853             :     {
     854             :       // Check current system is listed in system_names, and skip pos if not
     855        5144 :       bool use_current_system = (system_names == nullptr);
     856       83471 :       if (!use_current_system)
     857         423 :         use_current_system = system_names->count(sys_name);
     858       83471 :       if (!use_current_system || sys_ptr->hide_output())
     859        1693 :         continue;
     860             : 
     861        2530 :       const System & system  = *sys_ptr;
     862       81778 :       const unsigned int nv_sys = system.n_vars();
     863        5060 :       const unsigned int sys_num = system.number();
     864             : 
     865             :       //Could this be replaced by a/some convenience methods?[PB]
     866        2530 :       unsigned int n_scalar_vars = 0;
     867        2530 :       unsigned int n_vector_vars = 0;
     868      195502 :       for (auto vn : make_range(sys_ptr->n_vars()))
     869             :         {
     870      113724 :           if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
     871        6455 :             n_vector_vars++;
     872             :           else
     873      107269 :             n_scalar_vars++;
     874             :         }
     875             : 
     876             :       // Here, we're assuming the number of vector components is the same
     877             :       // as the mesh spatial dimension.
     878       81778 :       unsigned int nv_sys_split = n_scalar_vars + dim*n_vector_vars;
     879             : 
     880             :       // Update the current_local_solution
     881             :       {
     882        2530 :         System & non_const_sys = const_cast<System &>(system);
     883             :         // We used to simply call non_const_sys.solution->close()
     884             :         // here, but that is not allowed when the solution vector is
     885             :         // locked read-only, for example when printing the solution
     886             :         // during the middle of a solve...  So try to be a bit
     887             :         // more careful about calling close() unnecessarily.
     888        2530 :         libmesh_assert(this->comm().verify(non_const_sys.solution->closed()));
     889       81778 :         if (!non_const_sys.solution->closed())
     890           0 :           non_const_sys.solution->close();
     891       81778 :         non_const_sys.update();
     892             :       }
     893             : 
     894        2530 :       NumericVector<Number> & sys_soln(*system.current_local_solution);
     895             : 
     896        2530 :       const DofMap & dof_map = system.get_dof_map();
     897             : 
     898        5060 :       std::vector<Number>      elem_soln;   // The finite element solution
     899        5060 :       std::vector<Number>      nodal_soln;  // The FE solution interpolated to the nodes
     900        2530 :       std::vector<dof_id_type> dof_indices; // The DOF indices for the finite element
     901             : 
     902        2530 :       unsigned var_inc = 0;
     903      195502 :       for (unsigned int var=0; var<nv_sys; var++)
     904             :         {
     905      113724 :           const FEType & fe_type           = system.variable_type(var);
     906      113724 :           const Variable & var_description = system.variable(var);
     907      113724 :           unsigned int n_vec_dim = FEInterface::n_vec_dim( sys_ptr->get_mesh(), fe_type );
     908      113724 :           const bool add_p_level = fe_type.p_refinement;
     909             : 
     910    28862674 :           for (const auto & elem : _mesh.active_local_element_ptr_range())
     911             :             {
     912    15739520 :               if (var_description.active_on_subdomain(elem->subdomain_id()))
     913             :                 {
     914    15733988 :                   dof_map.dof_indices (elem, dof_indices, var);
     915    15733988 :                   sys_soln.get(dof_indices, elem_soln);
     916             : 
     917    15733988 :                   FEInterface::nodal_soln (elem->dim(),
     918             :                                            fe_type,
     919             :                                            elem,
     920             :                                            elem_soln,
     921             :                                            nodal_soln,
     922             :                                            add_p_level,
     923             :                                            n_vec_dim);
     924             : 
     925             :                   // infinite elements should be skipped...
     926     3405426 :                   if (!elem->infinite())
     927             :                     {
     928     1419679 :                       libmesh_assert_equal_to (nodal_soln.size(), n_vec_dim*elem->n_nodes());
     929             : 
     930   101401541 :                       for (auto n : elem->node_index_range())
     931             :                         {
     932    84247874 :                           const Node & node = elem->node_ref(n);
     933             : 
     934             :                           const dof_id_type node_idx =
     935    84247874 :                             nv * node_id_to_vec_id(node.id());
     936             : 
     937   183962845 :                           for (unsigned int d=0; d < n_vec_dim; d++)
     938             :                             {
     939             :                               // For vector-valued elements, all components are in nodal_soln. For each
     940             :                               // node, the components are stored in order, i.e. node_0 -> s0_x, s0_y, s0_z
     941   108017199 :                               parallel_soln.add(node_idx + (var_inc+d + var_num), nodal_soln[n_vec_dim*n+d]);
     942             : 
     943             :                               // Increment the repeat count for this position
     944    99714971 :                               repeat_count.add(node_idx + (var_inc+d + var_num), 1);
     945             :                             }
     946             :                         }
     947             : 
     948    15733988 :                       if (add_sides)
     949             :                         {
     950        9020 :                           for (auto s : elem->side_index_range())
     951             :                             {
     952        7200 :                               if (redundant_added_side(*elem,s))
     953        1782 :                                 continue;
     954             : 
     955             :                               // Compute the FE solution at all the
     956             :                               // side nodes
     957             :                               FEInterface::side_nodal_soln
     958        5256 :                                 (fe_type, elem, s, elem_soln,
     959             :                                  nodal_soln, add_p_level, n_vec_dim);
     960             : 
     961             : #ifdef DEBUG
     962             :                               const std::vector<unsigned int> side_nodes =
     963         876 :                                 elem->nodes_on_side(s);
     964             : 
     965         438 :                               libmesh_assert_equal_to
     966             :                                   (nodal_soln.size(),
     967             :                                    side_nodes.size());
     968             : #endif
     969             : 
     970       38736 :                               for (auto n : index_range(nodal_soln))
     971             :                                 {
     972             :                                   // Retrieve index into global solution vector.
     973             :                                   std::size_t node_index =
     974       36270 :                                     nv * libmesh_map_find(discontinuous_node_indices,
     975             :                                                           std::make_tuple(elem->id(), s, n));
     976             : 
     977       66960 :                                   for (unsigned int d=0; d < n_vec_dim; d++)
     978             :                                     {
     979       36270 :                                       parallel_soln.add(node_index + (var_inc+d + var_num), nodal_soln[n_vec_dim*n+d]);
     980       33480 :                                       repeat_count.add(node_index + (var_inc+d + var_num), 1);
     981             :                                     }
     982             :                                 }
     983             :                             }
     984             :                         }
     985             :                     }
     986             :                 }
     987             :               else // If this variable doesn't exist on this subdomain we have to still increment repeat_count so that we won't divide by 0 later:
     988      100536 :                 for (auto n : elem->node_index_range())
     989             :                   {
     990       95004 :                     const Node & node = elem->node_ref(n);
     991             :                     // Only do this if this variable has NO DoFs at
     992             :                     // this node... it might have some from an
     993             :                     // adjoining element...
     994       95004 :                     if (!node.n_dofs(sys_num, var))
     995             :                       {
     996             :                         const dof_id_type node_idx =
     997       78180 :                           nv * node_id_to_vec_id(node.id());
     998             : 
     999      156360 :                         for (unsigned int d=0; d < n_vec_dim; d++)
    1000       78180 :                           repeat_count.add(node_idx + (var_inc+d + var_num), 1);
    1001             :                       }
    1002             :                   }
    1003             : 
    1004      106656 :             } // end loop over elements
    1005      113724 :           var_inc += n_vec_dim;
    1006             :         } // end loop on variables in this system
    1007             : 
    1008       81778 :       var_num += nv_sys_split;
    1009             :     } // end loop over systems
    1010             : 
    1011             :   // Sum the nodal solution values and repeat counts.
    1012       79827 :   parallel_soln.close();
    1013       79827 :   repeat_count.close();
    1014             : 
    1015             :   // If there were gaps in the node numbering, there will be
    1016             :   // corresponding zeros in the parallel_soln and repeat_count
    1017             :   // vectors.  We need to set those repeat_count entries to 1
    1018             :   // in order to avoid dividing by zero.
    1019       79827 :   if (n_gaps)
    1020             :     {
    1021           0 :       for (numeric_index_type i=repeat_count.first_local_index();
    1022           0 :            i<repeat_count.last_local_index(); ++i)
    1023             :         {
    1024             :           // repeat_count entries are integral values but let's avoid a
    1025             :           // direct floating point comparison with 0 just in case some
    1026             :           // roundoff noise crept in during vector assembly?
    1027           0 :           if (std::abs(repeat_count(i)) < TOLERANCE)
    1028           0 :             repeat_count.set(i, 1.);
    1029             :         }
    1030             : 
    1031             :       // Make sure the repeat_count vector is up-to-date on all
    1032             :       // processors.
    1033           0 :       repeat_count.close();
    1034             :     }
    1035             : 
    1036             :   // Divide to get the average value at the nodes
    1037       79827 :   parallel_soln /= repeat_count;
    1038             : 
    1039       82275 :   return parallel_soln_ptr;
    1040       74931 : }
    1041             : 
    1042             : 
    1043             : 
    1044       79827 : void EquationSystems::build_solution_vector (std::vector<Number> & soln,
    1045             :                                              const std::set<std::string> * system_names,
    1046             :                                              bool add_sides) const
    1047             : {
    1048        7344 :   LOG_SCOPE("build_solution_vector()", "EquationSystems");
    1049             : 
    1050             :   // Call the parallel implementation
    1051             :   std::unique_ptr<NumericVector<Number>> parallel_soln =
    1052       82275 :     this->build_parallel_solution_vector(system_names, add_sides);
    1053             : 
    1054             :   // Localize the NumericVector into the provided std::vector.
    1055       79827 :   parallel_soln->localize_to_one(soln);
    1056       79827 : }
    1057             : 
    1058             : 
    1059             : 
    1060        8088 : void EquationSystems::get_vars_active_subdomains(const std::vector<std::string> & names,
    1061             :                                                  std::vector<std::set<subdomain_id_type>> & vars_active_subdomains) const
    1062             : {
    1063        8088 :   vars_active_subdomains.clear();
    1064        8316 :   vars_active_subdomains.resize(names.size());
    1065             : 
    1066       16176 :   for (const auto & pr : _systems)
    1067             :     {
    1068         228 :       const auto & sys_ptr = pr.second;
    1069       17234 :       for (auto vn : make_range(sys_ptr->n_vars()))
    1070             :         {
    1071        9146 :           const std::string & var_name = sys_ptr->variable_name(vn);
    1072             : 
    1073         516 :           auto names_it = std::find(names.begin(), names.end(), var_name);
    1074        9404 :           if(names_it != names.end())
    1075             :             {
    1076        8446 :               const Variable & variable = sys_ptr->variable(vn);
    1077         238 :               const std::set<subdomain_id_type> & active_subdomains = variable.active_subdomains();
    1078        8684 :               vars_active_subdomains[std::distance(names.begin(), names_it)] = active_subdomains;
    1079             :             }
    1080             :         }
    1081             :     }
    1082        8088 : }
    1083             : 
    1084             : 
    1085             : 
    1086             : void
    1087         423 : EquationSystems::build_elemental_solution_vector (std::vector<Number> & soln,
    1088             :                                                   std::vector<std::string> & names,
    1089             :                                                   bool allow_high_order_discontinuous) const
    1090             : {
    1091             :   // Call the parallel version of this function
    1092             :   std::unique_ptr<NumericVector<Number>> parallel_soln =
    1093         435 :     this->build_parallel_elemental_solution_vector(names, allow_high_order_discontinuous);
    1094             : 
    1095             :   // Localize into 'soln', provided that parallel_soln is not empty.
    1096             :   // Note: parallel_soln will be empty in the event that none of the
    1097             :   // input names were elemental data variables, or there were simply none of these in
    1098             :   // the EquationSystems object.
    1099          12 :   soln.clear();
    1100         423 :   if (parallel_soln)
    1101         423 :     parallel_soln->localize_to_one(soln);
    1102         423 : }
    1103             : 
    1104             : std::vector<std::pair<unsigned int, unsigned int>>
    1105         284 : EquationSystems::find_variable_numbers
    1106             :   (std::vector<std::string> & names, const FEType * type, const std::vector<FEType> * types) const
    1107             : {
    1108             :   // Resolve class of type input and assert that at least one of them is null
    1109           8 :   libmesh_assert_msg(!type || !types,
    1110             :                      "Input 'type', 'types', or neither in find_variable_numbers, but not both.");
    1111             : 
    1112           8 :   std::vector<FEType> type_filter;
    1113         284 :   if (type)
    1114           0 :     type_filter.push_back(*type);
    1115         284 :   else if (types)
    1116           0 :     type_filter = *types;
    1117             : 
    1118             :   return this->find_variable_numbers_by_predicate
    1119             :     (names,
    1120         426 :      [&type_filter](const FEType & var_type)
    1121             :      {
    1122         426 :        return type_filter.empty() ||
    1123          12 :               std::find(type_filter.begin(), type_filter.end(), var_type) != type_filter.end();
    1124         568 :      });
    1125             : }
    1126             : 
    1127             : 
    1128             : 
    1129             : std::vector<std::pair<unsigned int, unsigned int>>
    1130        8018 : EquationSystems::find_elemental_data_variable_numbers (std::vector<std::string> & names,
    1131             :                                                        bool allow_high_order_discontinuous) const
    1132             : {
    1133             :   return this->find_variable_numbers_by_predicate
    1134             :     (names,
    1135        7816 :      [allow_high_order_discontinuous](const FEType & type)
    1136       24902 :      { return EquationSystems::is_elemental_data_fe_type(type, allow_high_order_discontinuous); });
    1137             : }
    1138             : 
    1139             : 
    1140             : 
    1141             : std::vector<std::pair<unsigned int, unsigned int>>
    1142        8302 : EquationSystems::find_variable_numbers_by_predicate
    1143             :   (std::vector<std::string> & names,
    1144             :    const std::function<bool(const FEType &)> & type_filter) const
    1145             : {
    1146             :   // This function must be run on all processors at once
    1147         234 :   parallel_object_only();
    1148             : 
    1149         234 :   libmesh_assert (this->n_systems());
    1150             : 
    1151             :   // Store a copy of the valid variable names, if any. The names vector will be repopulated with any
    1152             :   // valid names (or all if 'is_names_empty') in the system that passes through the type filter. If
    1153             :   // the variable is a vector, its name will be decomposed into its separate components in
    1154             :   // accordance with build_variable_names().
    1155        8770 :   std::vector<std::string> name_filter = names;
    1156         234 :   bool is_names_empty = name_filter.empty();
    1157         234 :   names.clear();
    1158             : 
    1159             :   // initialize convenience variables
    1160         234 :   FEType var_type;
    1161         468 :   std::string name;
    1162             : 
    1163       33442 :   const std::vector<std::string> component_suffix = {"_x", "_y", "_z"};
    1164        8302 :   unsigned int dim = _mesh.spatial_dimension();
    1165        8302 :   libmesh_error_msg_if(dim > 3, "Invalid dim in find_variable_numbers");
    1166             : 
    1167             :   // Now filter through the variables in each system and store the system index and their index
    1168             :   // within that system. This way, we know where to find their data even after we sort them.
    1169         468 :   std::vector<std::pair<unsigned int, unsigned int>> var_nums;
    1170             : 
    1171       16604 :   for (const auto & pr : _systems)
    1172             :     {
    1173         234 :       const System & system = *(pr.second);
    1174             : 
    1175       17594 :       for (auto var : make_range(system.n_vars()))
    1176             :         {
    1177             :           // apply the type filter
    1178        9292 :           var_type = system.variable_type(var);
    1179        9292 :           if (!type_filter(var_type))
    1180           0 :             continue;
    1181             : 
    1182             :           // apply the name filter (note that all variables pass if it is empty)
    1183        9292 :           if (FEInterface::field_type(var_type) == TYPE_VECTOR)
    1184             :             {
    1185          44 :               std::vector<std::string> component_names;
    1186        2316 :               for (unsigned int comp = 0; comp < dim; ++comp)
    1187             :                 {
    1188        1588 :                   name = system.variable_name(var) + component_suffix[comp];
    1189        1568 :                   if (is_names_empty ||
    1190         468 :                       (std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end()))
    1191        1544 :                     component_names.push_back(name);
    1192             :                 }
    1193             : 
    1194         772 :               if (! component_names.empty())
    1195         772 :                 names.insert(names.end(), component_names.begin(), component_names.end());
    1196             :               else
    1197           0 :                 continue;
    1198         728 :             }
    1199             :           else /*scalar-valued variable*/
    1200             :             {
    1201        8520 :               name = system.variable_name(var);
    1202        8536 :               if (is_names_empty ||
    1203         524 :                   (std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end()))
    1204        8520 :                 names.push_back(name);
    1205             :               else
    1206           0 :                 continue;
    1207             :             }
    1208             : 
    1209             :           // if the variable made it through both filters get its system indices
    1210        9292 :           var_nums.emplace_back(system.number(), var);
    1211             :         }
    1212             :     }
    1213             : 
    1214             :   // Sort the var_nums vector pairs alphabetically based on the variable name
    1215        8770 :   std::vector<unsigned int> sort_index(var_nums.size());
    1216         234 :   std::iota(sort_index.begin(), sort_index.end(), 0);
    1217        8302 :   std::sort(sort_index.begin(), sort_index.end(),
    1218        1980 :             [&](const unsigned int & lhs, const unsigned int & rhs)
    1219        2036 :             {return this->get_system(var_nums[lhs].first).variable_name(var_nums[lhs].second) <
    1220        2092 :                     this->get_system(var_nums[rhs].first).variable_name(var_nums[rhs].second);});
    1221             : 
    1222        8536 :   std::vector<std::pair<unsigned int, unsigned int>> var_nums_sorted(var_nums.size());
    1223       17594 :   for (auto i : index_range(var_nums_sorted))
    1224             :     {
    1225        9816 :       var_nums_sorted[i].first = var_nums[sort_index[i]].first;
    1226        9554 :       var_nums_sorted[i].second = var_nums[sort_index[i]].second;
    1227             :     }
    1228             : 
    1229             :   // Also sort the names vector
    1230        8302 :   std::sort(names.begin(), names.end());
    1231             : 
    1232             :   // Return the sorted vector pairs
    1233        8536 :   return var_nums_sorted;
    1234       31336 : }
    1235             : 
    1236             : 
    1237             : std::unique_ptr<NumericVector<Number>>
    1238         423 : EquationSystems::build_parallel_elemental_solution_vector (std::vector<std::string> & names,
    1239             :                                                            bool allow_high_order_discontinuous) const
    1240             : {
    1241             :   // Filter any names that aren't elemental variables and get the system indices for those that are.
    1242             :   // Note that it's probably fine if the names vector is empty since we'll still filter out all
    1243             :   // non-elemental-data variables. If there are none, then nothing is output here.
    1244             :   std::vector<std::pair<unsigned int, unsigned int>> var_nums =
    1245         435 :     this->find_elemental_data_variable_numbers(names, allow_high_order_discontinuous);
    1246             : 
    1247          24 :   const std::size_t nv = names.size(); /*total number of vars including vector components*/
    1248         423 :   const dof_id_type ne = _mesh.n_elem();
    1249          12 :   libmesh_assert_equal_to (ne, _mesh.max_elem_id());
    1250             : 
    1251             :   // If there are no variables to write out don't do anything...
    1252         423 :   if (!nv)
    1253           0 :     return std::unique_ptr<NumericVector<Number>>(nullptr);
    1254             : 
    1255             :   // We can handle the case where there are nullptrs in the Elem vector
    1256             :   // by just having extra zeros in the solution vector.
    1257         423 :   numeric_index_type parallel_soln_global_size = ne*nv;
    1258             : 
    1259         423 :   numeric_index_type div = parallel_soln_global_size / this->n_processors();
    1260         423 :   numeric_index_type mod = parallel_soln_global_size % this->n_processors();
    1261             : 
    1262             :   // Initialize all processors to the average size.
    1263          12 :   numeric_index_type parallel_soln_local_size = div;
    1264             : 
    1265             :   // The first "mod" processors get an extra entry.
    1266         423 :   if (this->processor_id() < mod)
    1267         156 :     parallel_soln_local_size = div+1;
    1268             : 
    1269             :   // Create a NumericVector to hold the parallel solution
    1270         435 :   std::unique_ptr<NumericVector<Number>> parallel_soln_ptr = NumericVector<Number>::build(_communicator);
    1271          12 :   NumericVector<Number> & parallel_soln = *parallel_soln_ptr;
    1272         423 :   parallel_soln.init(parallel_soln_global_size,
    1273             :                      parallel_soln_local_size,
    1274             :                      /*fast=*/false,
    1275          24 :                      /*ParallelType=*/PARALLEL);
    1276             : 
    1277          12 :   unsigned int sys_ctr = 0;
    1278          12 :   unsigned int var_ctr = 0;
    1279        1270 :   for (auto i : index_range(var_nums))
    1280             :     {
    1281         871 :       std::pair<unsigned int, unsigned int> var_num = var_nums[i];
    1282          24 :       const System & system = this->get_system(var_num.first);
    1283             : 
    1284             :       // Update the current_local_solution if necessary
    1285         847 :       if (sys_ctr != var_num.first)
    1286             :         {
    1287           0 :           System & non_const_sys = const_cast<System &>(system);
    1288             :           // We used to simply call non_const_sys.solution->close()
    1289             :           // here, but that is not allowed when the solution vector is
    1290             :           // locked read-only, for example when printing the solution
    1291             :           // during during the middle of a solve...  So try to be a bit
    1292             :           // more careful about calling close() unnecessarily.
    1293           0 :           libmesh_assert(this->comm().verify(non_const_sys.solution->closed()));
    1294           0 :           if (!non_const_sys.solution->closed())
    1295           0 :             non_const_sys.solution->close();
    1296           0 :           non_const_sys.update();
    1297           0 :           sys_ctr = var_num.first;
    1298             :         }
    1299             : 
    1300          24 :       NumericVector<Number> & sys_soln(*system.current_local_solution);
    1301             : 
    1302          24 :       const unsigned int var = var_num.second;
    1303             : 
    1304         847 :       const Variable & variable = system.variable(var);
    1305          24 :       const DofMap & dof_map = system.get_dof_map();
    1306             : 
    1307             :       // We need to check if the elemental data variable is a scalar or a vector and set the number of
    1308             :       // components for the latter as per es.find_variable_numbers().
    1309             :       // Even for the case where a variable is not active on any subdomain belonging to the
    1310             :       // processor, we still need to know this number to update 'var_ctr'.
    1311         847 :       const auto & var_type = system.variable_type(var);
    1312         847 :       const FEFieldType field_type = FEInterface::field_type(var_type);
    1313             :       const unsigned int n_comps =
    1314         847 :         (field_type == TYPE_VECTOR) ?
    1315         350 :         FEInterface::n_vec_dim(_mesh, var_type) : 1;
    1316             : 
    1317             :       // Elemental data variables are normally CONSTANT order, in which case
    1318             :       // there is exactly one DOF per component and we can just copy it
    1319             :       // directly.  When allow_high_order_discontinuous permits a
    1320             :       // non-CONSTANT order variable through, a single DOF no longer
    1321             :       // represents the element's value, so we instead compute a
    1322             :       // quadrature-weighted average of the FE solution over the element.
    1323             :       const bool need_quadrature_average =
    1324         847 :         allow_high_order_discontinuous && var_type.order != CONSTANT;
    1325             : 
    1326             :       // Loop over all elements in the mesh and index all components of the variable if it's active
    1327             :       Threads::parallel_for
    1328         847 :         (_mesh.active_local_element_stored_range(),
    1329        1598 :          [&system, &dof_map, &variable, ne, var, var_ctr, n_comps, field_type,
    1330       34976 :          need_quadrature_average, &parallel_soln, &sys_soln](const ConstElemRange & range)
    1331             :          {
    1332             :            // The DOF indices for the finite element
    1333          48 :            std::vector<dof_id_type> dof_indices;
    1334             : 
    1335             :            // FE context for the quadrature-average path.  It is built here,
    1336             :            // inside the parallel_for lambda body, so that each range
    1337             :            // invocation (which runs single-threaded) owns its own; sharing
    1338             :            // FE objects across threads would race on reinit().
    1339         847 :            std::unique_ptr<FEMContext> con;
    1340         847 :            if (need_quadrature_average)
    1341             :              {
    1342          71 :                const std::vector<unsigned int> active_vars {var};
    1343          73 :                con = std::make_unique<FEMContext>(system, &active_vars,
    1344         138 :                                                   /*allocate_local_matrices=*/false);
    1345             :              }
    1346             : 
    1347             :            // Quadrature-weighted average of the variable over elem.  The
    1348             :            // second argument only selects the value type: Number for
    1349             :            // scalar variables, Gradient for vector-valued ones.
    1350         594 :            auto element_average = [&con, &system, var](const Elem * elem, auto zero_value)
    1351             :              {
    1352             :                typedef decltype(zero_value) OutputType;
    1353             :                typedef typename TensorTools::MakeReal<OutputType>::type OutputShape;
    1354             : 
    1355         108 :                con->pre_fe_reinit(system, elem);
    1356             : 
    1357             :                // Request what interior_value() needs before reinit
    1358          99 :                FEGenericBase<OutputShape> * fe = nullptr;
    1359          99 :                con->get_element_fe(var, fe);
    1360         108 :                const std::vector<Real> & JxW = fe->get_JxW();
    1361           9 :                fe->get_phi();
    1362             : 
    1363         108 :                con->elem_fe_reinit();
    1364             : 
    1365          18 :                OutputType avg = zero_value;
    1366           9 :                Real vol = 0;
    1367         540 :                for (auto qp : index_range(JxW))
    1368             :                  {
    1369          36 :                    OutputType u_h;
    1370         432 :                    con->interior_value(var, qp, u_h);
    1371         396 :                    avg += JxW[qp] * u_h;
    1372         432 :                    vol += JxW[qp];
    1373             :                  }
    1374          99 :                avg /= vol;
    1375         108 :                return avg;
    1376         895 :              };
    1377             : 
    1378        4474 :            for (const Elem * elem : range)
    1379             :              {
    1380        3627 :                if (variable.active_on_subdomain(elem->subdomain_id()))
    1381             :                  {
    1382        3627 :                    if (need_quadrature_average)
    1383             :                      {
    1384         108 :                        if (field_type == TYPE_SCALAR)
    1385         108 :                          parallel_soln.set(ne * var_ctr + elem->id(), element_average(elem, Number()));
    1386             :                        else
    1387             :                          {
    1388           0 :                            const Gradient avg = element_average(elem, Gradient());
    1389           0 :                            for (unsigned int comp = 0; comp < n_comps; comp++)
    1390           0 :                              parallel_soln.set(ne * (var_ctr + comp) + elem->id(), avg(comp));
    1391             :                          }
    1392             :                      }
    1393             :                    else
    1394             :                      {
    1395        3519 :                        dof_map.dof_indices(elem, dof_indices, var);
    1396             : 
    1397             :                        // The number of DOF components needs to be equal to the expected number so that we know
    1398             :                        // where to store data to correctly correspond to variable names.
    1399         315 :                        libmesh_assert_equal_to(dof_indices.size(), n_comps);
    1400             : 
    1401        9909 :                        for (unsigned int comp = 0; comp < n_comps; comp++)
    1402        6966 :                          parallel_soln.set(ne * (var_ctr + comp) + elem->id(), sys_soln(dof_indices[comp]));
    1403             :                      }
    1404             :                  }
    1405             :              }
    1406        1646 :          });
    1407             : 
    1408         847 :       var_ctr += n_comps;
    1409             :     } // end loop over var_nums
    1410             : 
    1411             :   // NOTE: number of output names might not be equal to the number passed to this function. Any that
    1412             :   // aren't elemental data variables have been filtered out (see
    1413             :   // EquationSystems::find_variable_numbers).
    1414             :   //
    1415             :   // But, if everything is accounted for properly, then names.size() == var_ctr
    1416          12 :   libmesh_assert_equal_to(names.size(), var_ctr);
    1417             : 
    1418         423 :   parallel_soln.close();
    1419          12 :   return parallel_soln_ptr;
    1420         399 : }
    1421             : 
    1422             : 
    1423             : 
    1424             : void
    1425        5507 : EquationSystems::build_discontinuous_solution_vector
    1426             : (std::vector<Number> & soln,
    1427             :  const std::set<std::string> * system_names,
    1428             :  const std::vector<std::string> * var_names,
    1429             :  bool vertices_only,
    1430             :  bool add_sides) const
    1431             : {
    1432         690 :   LOG_SCOPE("build_discontinuous_solution_vector()", "EquationSystems");
    1433             : 
    1434         230 :   libmesh_assert (this->n_systems());
    1435             : 
    1436       22258 :   const std::vector<std::string> component_suffix = {"_x", "_y", "_z"};
    1437             :   const auto requested_components =
    1438        7337 :     [this, var_names, &component_suffix](const System & system,
    1439        1899 :                                          const unsigned int var)
    1440             :     {
    1441         405 :       std::vector<unsigned int> components;
    1442             : 
    1443        8147 :       const std::string & var_name = system.variable_name(var);
    1444        8147 :       const FEType & fe_type = system.variable_type(var);
    1445        8147 :       const unsigned int n_vec_dim = FEInterface::n_vec_dim(_mesh, fe_type);
    1446             : 
    1447        8147 :       if (FEInterface::field_type(fe_type) == TYPE_VECTOR)
    1448             :         {
    1449          84 :           libmesh_error_msg_if(n_vec_dim > component_suffix.size(),
    1450             :                                "Invalid dim in build_discontinuous_solution_vector");
    1451             : 
    1452             :           const bool use_all_components =
    1453          87 :             (var_names == nullptr) ||
    1454          81 :             std::count(var_names->begin(), var_names->end(), var_name);
    1455             : 
    1456          81 :           if (n_vec_dim <= 1)
    1457             :             {
    1458           0 :               if (use_all_components)
    1459           0 :                 components.push_back(0);
    1460             :             }
    1461             :           else
    1462         324 :             for (auto comp : make_range(n_vec_dim))
    1463             :               {
    1464         261 :                 const std::string component_name = var_name + component_suffix[comp];
    1465         261 :                 if (use_all_components ||
    1466         243 :                     std::count(var_names->begin(), var_names->end(), component_name))
    1467         243 :                   components.push_back(comp);
    1468             :               }
    1469             :         }
    1470        8084 :       else if (var_names == nullptr ||
    1471         243 :                std::count(var_names->begin(), var_names->end(), var_name))
    1472        8066 :         components.push_back(0);
    1473             : 
    1474        8147 :       return components;
    1475        5507 :     };
    1476             : 
    1477             :   // Get the number of variables (nv) by counting the number of variables
    1478             :   // in each system listed in system_names
    1479         230 :   unsigned int nv = 0;
    1480             : 
    1481       11085 :   for (const auto & [sys_name, sys_ptr] : _systems)
    1482             :     {
    1483             :       // Check current system is listed in system_names, and skip pos if not
    1484         464 :       bool use_current_system = (system_names == nullptr);
    1485        5578 :       if (!use_current_system)
    1486          70 :         use_current_system = system_names->count(sys_name);
    1487        5578 :       if (!use_current_system || sys_ptr->hide_output())
    1488           0 :         continue;
    1489             : 
    1490             :       // Loop over all variables in this System and check whether we
    1491             :       // are supposed to use each one.
    1492       12502 :       for (auto var_id : make_range(sys_ptr->n_vars()))
    1493        7194 :         nv += cast_int<unsigned int>(requested_components(*sys_ptr, var_id).size());
    1494             :     }
    1495             : 
    1496             :   // get the total "weight" - the number of nodal values to write for
    1497             :   // each variable.
    1498         230 :   unsigned int tw=0;
    1499      450524 :   for (const auto & elem : _mesh.active_element_ptr_range())
    1500             :     {
    1501      243890 :       tw += vertices_only ? elem->n_vertices() : elem->n_nodes();
    1502             : 
    1503      243890 :       if (add_sides)
    1504             :         {
    1505       25064 :           for (auto s : elem->side_index_range())
    1506             :             {
    1507       20400 :               if (redundant_added_side(*elem,s))
    1508        5460 :                 continue;
    1509             : 
    1510             :               const std::vector<unsigned int> side_nodes =
    1511       15548 :                 elem->nodes_on_side(s);
    1512             : 
    1513       14940 :               if (!vertices_only)
    1514       15548 :                 tw += side_nodes.size();
    1515             :               else
    1516           0 :                 for (auto n : index_range(side_nodes))
    1517           0 :                   if (elem->is_vertex(side_nodes[n]))
    1518           0 :                     ++tw;
    1519             :             }
    1520             :         }
    1521        5047 :     }
    1522             : 
    1523             :   // Only if we are on processor zero, allocate the storage
    1524             :   // to hold (number_of_nodes)*(number_of_variables) entries.
    1525        5737 :   if (_mesh.processor_id() == 0)
    1526         986 :     soln.resize(tw*nv);
    1527             : 
    1528         460 :   std::vector<Number> sys_soln;
    1529             : 
    1530             :   // Keep track of the variable "offset". This is used for indexing
    1531             :   // into the global solution vector.
    1532         230 :   unsigned int var_offset = 0;
    1533             : 
    1534             :   // For each system in this EquationSystems object,
    1535             :   // update the global solution and if we are on processor 0,
    1536             :   // loop over the elements and build the nodal solution
    1537             :   // from the element solution.  Then insert this nodal solution
    1538             :   // into the vector passed to build_solution_vector.
    1539       11085 :   for (const auto & [sys_name, system] : _systems)
    1540             :     {
    1541             :       // Check current system is listed in system_names, and skip pos if not
    1542         464 :       bool use_current_system = (system_names == nullptr);
    1543        5578 :       if (!use_current_system)
    1544          70 :         use_current_system = system_names->count(sys_name);
    1545        5578 :       if (!use_current_system || system->hide_output())
    1546           0 :         continue;
    1547             : 
    1548        5578 :       const unsigned int nv_sys = system->n_vars();
    1549         232 :       const auto & dof_map = system->get_dof_map();
    1550             : 
    1551        5578 :       system->update_global_solution (sys_soln, 0);
    1552             : 
    1553             :       // Keep track of the number of vars actually written.
    1554         232 :       unsigned int n_vars_written_current_system = 0;
    1555             : 
    1556        5810 :       if (_mesh.processor_id() == 0)
    1557             :         {
    1558         232 :           std::vector<Number>       soln_coeffs; // The finite element solution coeffs
    1559         232 :           std::vector<Number>       nodal_soln;  // The FE solution interpolated to the nodes
    1560         232 :           std::vector<dof_id_type>  dof_indices; // The DOF indices for the finite element
    1561             : 
    1562             :           // For each variable, determine if we are supposed to
    1563             :           // write it, then loop over the active elements, compute
    1564             :           // the nodal_soln and store it to the "soln" vector. We
    1565             :           // store zeros for subdomain-restricted variables on
    1566             :           // elements where they are not active.
    1567        2221 :           for (auto var : make_range(nv_sys))
    1568             :             {
    1569             :               const std::vector<unsigned int> components_to_write =
    1570        1223 :                 requested_components(*system, var);
    1571             : 
    1572             :               // If we aren't supposed to write this var, go to the
    1573             :               // next loop iteration.
    1574        1223 :               if (components_to_write.empty())
    1575           0 :                 continue;
    1576             : 
    1577        1223 :               const FEType & fe_type = system->variable_type(var);
    1578        1223 :               const Variable & var_description = system->variable(var);
    1579        1223 :               const bool add_p_level = fe_type.p_refinement;
    1580        1223 :               const unsigned int n_vec_dim = FEInterface::n_vec_dim(_mesh, fe_type);
    1581             : 
    1582         135 :               unsigned int nn=0;
    1583             : 
    1584      367965 :               for (auto & elem : _mesh.active_element_ptr_range())
    1585             :                 {
    1586      204121 :                   if (var_description.active_on_subdomain(elem->subdomain_id()))
    1587             :                     {
    1588      204121 :                       dof_map.dof_indices (elem, dof_indices, var);
    1589             : 
    1590      225415 :                       soln_coeffs.resize(dof_indices.size());
    1591             : 
    1592     1596033 :                       for (auto i : index_range(dof_indices))
    1593     1632620 :                         soln_coeffs[i] = sys_soln[dof_indices[i]];
    1594             : 
    1595             :                       // Compute the FE solution at all the nodes, but
    1596             :                       // only use the first n_vertices() entries if
    1597             :                       // vertices_only == true.
    1598      204121 :                       FEInterface::nodal_soln (elem->dim(),
    1599             :                                                fe_type,
    1600             :                                                elem,
    1601             :                                                soln_coeffs,
    1602             :                                                nodal_soln,
    1603             :                                                add_p_level,
    1604             :                                                n_vec_dim);
    1605             : 
    1606             :                       // infinite elements should be skipped...
    1607       61566 :                       if (!elem->infinite())
    1608             :                         {
    1609       21294 :                           libmesh_assert_equal_to (nodal_soln.size(), elem->n_nodes()*n_vec_dim);
    1610             : 
    1611             :                           const unsigned int n_vals =
    1612      204121 :                             vertices_only ? elem->n_vertices() : elem->n_nodes();
    1613             : 
    1614     1676013 :                           for (auto n : make_range(n_vals))
    1615             :                             {
    1616             :                               // Compute index into global solution vector.
    1617             :                               std::size_t index =
    1618     1471892 :                                 nv * (nn++) + (n_vars_written_current_system + var_offset);
    1619             : 
    1620     2945192 :                               for (auto component_index : index_range(components_to_write))
    1621     1613232 :                                 soln[index + component_index] +=
    1622     1613232 :                                   nodal_soln[n_vec_dim*n + components_to_write[component_index]];
    1623             :                             }
    1624             :                         }
    1625             :                     }
    1626             :                   else
    1627           0 :                     nn += vertices_only ? elem->n_vertices() : elem->n_nodes();
    1628         953 :                 } // end loop over active elements writing interiors
    1629             : 
    1630             :               // Loop writing "fake" sides, if requested
    1631        1223 :               if (add_sides)
    1632             :                 {
    1633             :                   // We don't build discontinuous solution vectors in
    1634             :                   // parallel yet, but we'll do ordering of fake side
    1635             :                   // values as if we did, for consistency with the
    1636             :                   // parallel continuous ordering and for future
    1637             :                   // compatibility.
    1638             :                   std::vector<std::vector<const Elem *>>
    1639         375 :                     elems_by_pid(_mesh.n_processors());
    1640             : 
    1641        3655 :                   for (const auto & elem : _mesh.active_element_ptr_range())
    1642        2070 :                     elems_by_pid[elem->processor_id()].push_back(elem);
    1643             : 
    1644        2075 :                   for (auto p : index_range(elems_by_pid))
    1645        3455 :                     for (const Elem * elem : elems_by_pid[p])
    1646             :                       {
    1647        1680 :                         if (var_description.active_on_subdomain(elem->subdomain_id()))
    1648             :                           {
    1649        1680 :                             dof_map.dof_indices (elem, dof_indices, var);
    1650             : 
    1651        1820 :                             soln_coeffs.resize(dof_indices.size());
    1652             : 
    1653       32064 :                             for (auto i : index_range(dof_indices))
    1654       35448 :                               soln_coeffs[i] = sys_soln[dof_indices[i]];
    1655             : 
    1656        8880 :                             for (auto s : elem->side_index_range())
    1657             :                               {
    1658        7200 :                                 if (redundant_added_side(*elem,s))
    1659        1944 :                                   continue;
    1660             : 
    1661             :                                 const std::vector<unsigned int> side_nodes =
    1662        5694 :                                   elem->nodes_on_side(s);
    1663             : 
    1664             :                                 // Compute the FE solution at all the
    1665             :                                 // side nodes, but only use those for
    1666             :                                 // which is_vertex() == true if
    1667             :                                 // vertices_only == true.
    1668             :                                 FEInterface::side_nodal_soln
    1669        5256 :                                   (fe_type, elem, s, soln_coeffs,
    1670             :                                    nodal_soln, add_p_level,
    1671             :                                    n_vec_dim);
    1672             : 
    1673         438 :                                 libmesh_assert_equal_to
    1674             :                                     (nodal_soln.size(),
    1675             :                                      side_nodes.size()*n_vec_dim);
    1676             : 
    1677             :                                 // If we don't have a continuous FE
    1678             :                                 // then we want to average between
    1679             :                                 // sides, at least in the equal-level
    1680             :                                 // case where it's easy.  This is
    1681             :                                 // analogous to our repeat_count
    1682             :                                 // behavior elsewhere.
    1683             :                                 const FEContinuity cont =
    1684        5256 :                                   FEInterface::get_continuity(fe_type);
    1685         876 :                                 const Elem * const neigh = elem->neighbor_ptr(s);
    1686             : 
    1687        5256 :                                 if ((cont == DISCONTINUOUS || cont == H_CURL || cont == H_DIV) &&
    1688           0 :                                     neigh &&
    1689        5694 :                                     neigh->level() == elem->level() &&
    1690           0 :                                     var_description.active_on_subdomain(neigh->subdomain_id()))
    1691             :                                   {
    1692           0 :                                     std::vector<dof_id_type> neigh_indices;
    1693           0 :                                     dof_map.dof_indices (neigh, neigh_indices, var);
    1694           0 :                                     std::vector<Number> neigh_coeffs(neigh_indices.size());
    1695             : 
    1696           0 :                                     for (auto i : index_range(neigh_indices))
    1697           0 :                                       neigh_coeffs[i] = sys_soln[neigh_indices[i]];
    1698             : 
    1699             :                                     const unsigned int s_neigh =
    1700           0 :                                       neigh->which_neighbor_am_i(elem);
    1701           0 :                                     std::vector<Number> neigh_soln;
    1702             :                                     FEInterface::side_nodal_soln
    1703           0 :                                       (fe_type, neigh, s_neigh,
    1704             :                                        neigh_coeffs, neigh_soln, add_p_level,
    1705             :                                        n_vec_dim);
    1706             : 
    1707             :                                     const std::vector<unsigned int> neigh_nodes =
    1708           0 :                                       neigh->nodes_on_side(s_neigh);
    1709           0 :                                     for (auto n : index_range(side_nodes))
    1710           0 :                                       for (auto neigh_n : index_range(neigh_nodes))
    1711           0 :                                         if (neigh->node_ptr(neigh_nodes[neigh_n])
    1712           0 :                                             == elem->node_ptr(side_nodes[n]))
    1713           0 :                                           for (auto comp : make_range(n_vec_dim))
    1714             :                                             {
    1715           0 :                                               const auto nodal_index = n_vec_dim*n + comp;
    1716           0 :                                               nodal_soln[nodal_index] +=
    1717           0 :                                                 neigh_soln[n_vec_dim*neigh_n + comp];
    1718           0 :                                               nodal_soln[nodal_index] /= 2;
    1719             :                                             }
    1720             :                                   }
    1721             : 
    1722       38736 :                                 for (auto n : index_range(side_nodes))
    1723             :                                   {
    1724       33480 :                                     if (vertices_only &&
    1725           0 :                                         !elem->is_vertex(n))
    1726           0 :                                       continue;
    1727             : 
    1728             :                                     // Compute index into global solution vector.
    1729             :                                     std::size_t index =
    1730       33480 :                                       nv * (nn++) + (n_vars_written_current_system + var_offset);
    1731             : 
    1732       66960 :                                     for (auto component_index : index_range(components_to_write))
    1733       36270 :                                       soln[index + component_index] +=
    1734       36270 :                                         nodal_soln[n_vec_dim*n + components_to_write[component_index]];
    1735             :                                   }
    1736             :                               }
    1737             :                           }
    1738             :                         else
    1739             :                           {
    1740           0 :                             nn += vertices_only ? elem->n_vertices() : elem->n_nodes();
    1741             : 
    1742           0 :                             for (auto s : elem->side_index_range())
    1743             :                               {
    1744           0 :                                 if (redundant_added_side(*elem,s))
    1745           0 :                                   continue;
    1746             : 
    1747             :                                 const std::vector<unsigned int> side_nodes =
    1748           0 :                                   elem->nodes_on_side(s);
    1749             : 
    1750           0 :                                 for (auto n : index_range(side_nodes))
    1751             :                                   {
    1752           0 :                                     if (vertices_only &&
    1753           0 :                                         !elem->is_vertex(n))
    1754           0 :                                       continue;
    1755           0 :                                     nn++;
    1756             :                                   }
    1757             :                               }
    1758             :                           }
    1759             :                       } // end loop over active elements, writing "fake" sides
    1760         250 :                 }
    1761             :               // If we made it here, we actually wrote a variable, so increment
    1762             :               // the number of variables actually written for the current system.
    1763        1358 :               n_vars_written_current_system += cast_int<unsigned int>(components_to_write.size());
    1764             : 
    1765             :             } // end loop over vars
    1766             :         } // end if proc 0
    1767             : 
    1768             :       // Update offset for next loop iteration.
    1769        5578 :       var_offset += n_vars_written_current_system;
    1770             :     } // end loop over systems
    1771       15601 : }
    1772             : 
    1773             : 
    1774             : 
    1775      132176 : bool EquationSystems::redundant_added_side(const Elem & elem, unsigned int side)
    1776             : {
    1777        9122 :   libmesh_assert(elem.active());
    1778             : 
    1779       18244 :   const Elem * neigh = elem.neighbor_ptr(side);
    1780             : 
    1781             :   // Write boundary sides.
    1782      132176 :   if (!neigh)
    1783        4342 :     return false;
    1784             : 
    1785             :   // Write ghost sides in Nemesis
    1786       68800 :   if (neigh == remote_elem)
    1787           0 :     return false;
    1788             : 
    1789             :   // Don't write a coarser side if a finer side exists
    1790        4780 :   if (!neigh->active())
    1791           0 :     return true;
    1792             : 
    1793             :   // Don't write a side redundantly from both of the
    1794             :   // elements sharing it.  We'll disambiguate with id().
    1795       68448 :   return (neigh->id() < elem.id());
    1796             : }
    1797             : 
    1798             : 
    1799             : 
    1800           0 : bool EquationSystems::compare (const EquationSystems & other_es,
    1801             :                                const Real threshold,
    1802             :                                const bool verbose) const
    1803             : {
    1804             :   // safety check, whether we handle at least the same number
    1805             :   // of systems
    1806           0 :   std::vector<bool> os_result;
    1807             : 
    1808           0 :   if (this->n_systems() != other_es.n_systems())
    1809             :     {
    1810           0 :       if (verbose)
    1811             :         {
    1812           0 :           libMesh::out << "  Fatal difference. This system handles "
    1813           0 :                        << this->n_systems() << " systems," << std::endl
    1814           0 :                        << "  while the other system handles "
    1815           0 :                        << other_es.n_systems()
    1816           0 :                        << " systems." << std::endl
    1817           0 :                        << "  Aborting comparison." << std::endl;
    1818             :         }
    1819           0 :       return false;
    1820             :     }
    1821             :   else
    1822             :     {
    1823             :       // start comparing each system
    1824           0 :       for (const auto & [sys_name, sys_ptr] : _systems)
    1825             :         {
    1826             :           // get the other system
    1827           0 :           const System & other_system   = other_es.get_system (sys_name);
    1828             : 
    1829           0 :           os_result.push_back (sys_ptr->compare (other_system, threshold, verbose));
    1830             : 
    1831             :         }
    1832             : 
    1833             :     }
    1834             : 
    1835             : 
    1836             :   // sum up the results
    1837           0 :   if (os_result.size()==0)
    1838           0 :     return true;
    1839             :   else
    1840             :     {
    1841             :       bool os_identical;
    1842           0 :       unsigned int n = 0;
    1843           0 :       do
    1844             :         {
    1845           0 :           os_identical = os_result[n];
    1846           0 :           n++;
    1847             :         }
    1848           0 :       while (os_identical && n<os_result.size());
    1849           0 :       return os_identical;
    1850             :     }
    1851             : }
    1852             : 
    1853             : 
    1854             : 
    1855       16825 : std::string EquationSystems::get_info () const
    1856             : {
    1857       17757 :   std::ostringstream oss;
    1858             : 
    1859         466 :   unsigned int n_hidden_sys = 0;
    1860       36230 :   for (auto & pr : _systems)
    1861       19405 :     n_hidden_sys += pr.second->hide_output();
    1862             : 
    1863             :   oss << " EquationSystems\n"
    1864       16359 :       << "  n_systems()=" << this->n_systems()
    1865       36374 :       << (n_hidden_sys ? " (hidden: " + std::to_string(n_hidden_sys) + ")" : "")
    1866       16825 :       << "\n";
    1867             : 
    1868             :   // Print the info for the individual systems
    1869       36230 :   for (const auto & pr : _systems)
    1870       19405 :     if (!pr.second->hide_output())
    1871       35558 :       oss << pr.second->get_info();
    1872             : 
    1873             : 
    1874             :   //   // Possibly print the parameters
    1875             :   //   if (!this->parameters.empty())
    1876             :   //     {
    1877             :   //       oss << "  n_parameters()=" << this->n_parameters() << '\n';
    1878             :   //       oss << "   Parameters:\n";
    1879             : 
    1880             :   //       for (const auto & [key, val] : _parameters)
    1881             :   //         oss << "    "
    1882             :   //             << "\""
    1883             :   //             << key
    1884             :   //             << "\""
    1885             :   //             << "="
    1886             :   //             << val
    1887             :   //             << '\n';
    1888             :   //     }
    1889             : 
    1890       17291 :   return oss.str();
    1891       15893 : }
    1892             : 
    1893             : 
    1894             : 
    1895       16825 : void EquationSystems::print_info (std::ostream & os) const
    1896             : {
    1897       17291 :   os << this->get_info()
    1898         466 :      << std::endl;
    1899       16825 : }
    1900             : 
    1901             : 
    1902             : 
    1903           0 : std::ostream & operator << (std::ostream & os,
    1904             :                             const EquationSystems & es)
    1905             : {
    1906           0 :   es.print_info(os);
    1907           0 :   return os;
    1908             : }
    1909             : 
    1910             : 
    1911             : 
    1912        2704 : unsigned int EquationSystems::n_vars () const
    1913             : {
    1914        2704 :   unsigned int tot=0;
    1915             : 
    1916        5536 :   for (const auto & pr : _systems)
    1917        2832 :     tot += pr.second->n_vars();
    1918             : 
    1919        2704 :   return tot;
    1920             : }
    1921             : 
    1922             : 
    1923             : 
    1924           0 : std::size_t EquationSystems::n_dofs () const
    1925             : {
    1926           0 :   std::size_t tot=0;
    1927             : 
    1928           0 :   for (const auto & pr : _systems)
    1929           0 :     tot += pr.second->n_dofs();
    1930             : 
    1931           0 :   return tot;
    1932             : }
    1933             : 
    1934             : 
    1935             : 
    1936             : 
    1937       22948 : std::size_t EquationSystems::n_active_dofs () const
    1938             : {
    1939         730 :   std::size_t tot=0;
    1940             : 
    1941       45896 :   for (const auto & pr : _systems)
    1942       22948 :     tot += pr.second->n_active_dofs();
    1943             : 
    1944       22948 :   return tot;
    1945             : }
    1946             : 
    1947             : 
    1948      358395 : void EquationSystems::_add_system_to_nodes_and_elems()
    1949             : {
    1950             :   // All the nodes
    1951             :   Threads::parallel_for_each
    1952      358395 :     (_mesh.node_stored_range(),
    1953    17103481 :      [](Node * node) { node->add_system(); });
    1954             : 
    1955             :   // All the elements
    1956             :   Threads::parallel_for_each
    1957      358395 :     (_mesh.element_stored_range(),
    1958     7357880 :      [](Elem * elem) { elem->add_system(); });
    1959      358395 : }
    1960             : 
    1961          71 : void EquationSystems::_remove_default_ghosting(unsigned int sys_num)
    1962             : {
    1963          71 :   this->get_system(sys_num).get_dof_map().remove_default_ghosting();
    1964          71 : }
    1965             : 
    1966             : } // namespace libMesh

Generated by: LCOV version 1.14