LCOV - code coverage report
Current view: top level - src/systems - equation_systems.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 581 730 79.6 %
Date: 2025-08-19 19:27:09 Functions: 35 46 76.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 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_interface.h"
      26             : #include "libmesh/frequency_system.h"
      27             : #include "libmesh/int_range.h"
      28             : #include "libmesh/libmesh_logging.h"
      29             : #include "libmesh/linear_implicit_system.h"
      30             : #include "libmesh/mesh_base.h"
      31             : #include "libmesh/mesh_refinement.h"
      32             : #include "libmesh/newmark_system.h"
      33             : #include "libmesh/nonlinear_implicit_system.h"
      34             : #include "libmesh/parallel.h"
      35             : #include "libmesh/rb_construction.h"
      36             : #include "libmesh/remote_elem.h"
      37             : #include "libmesh/transient_rb_construction.h"
      38             : #include "libmesh/transient_system.h"
      39             : 
      40             : // System includes
      41             : #include <functional> // std::plus
      42             : #include <numeric> // std::iota
      43             : #include <sstream>
      44             : 
      45             : // Include the systems before this one to avoid
      46             : // overlapping forward declarations.
      47             : #include "libmesh/equation_systems.h"
      48             : 
      49             : namespace libMesh
      50             : {
      51             : 
      52      234176 : EquationSystems::EquationSystems (MeshBase & m) :
      53             :   ParallelObject (m),
      54      220804 :   _mesh          (m),
      55      220804 :   _refine_in_reinit(true),
      56      240862 :   _enable_default_ghosting(true)
      57             : {
      58             :   // Set default parameters
      59      234176 :   this->parameters.set<Real>        ("linear solver tolerance") = TOLERANCE * TOLERANCE;
      60      234176 :   this->parameters.set<unsigned int>("linear solver maximum iterations") = 5000;
      61      234176 : }
      62             : 
      63             : 
      64             : 
      65      403105 : EquationSystems::~EquationSystems () = default;
      66             : 
      67             : 
      68             : 
      69          71 : void EquationSystems::clear ()
      70             : {
      71             :   // Clear any additional parameters
      72           2 :   parameters.clear ();
      73             : 
      74             :   // Clear the systems.
      75           2 :   _systems.clear();
      76          71 : }
      77             : 
      78             : 
      79             : 
      80      233845 : void EquationSystems::init ()
      81             : {
      82             : #ifndef NDEBUG
      83       13428 :   for (auto i : make_range(this->n_systems()))
      84        6750 :     libmesh_assert(!this->get_system(i).is_initialized());
      85             : #endif
      86             : 
      87      233845 :   this->reinit_mesh();
      88      233751 : }
      89             : 
      90             : 
      91             : 
      92       22188 : void EquationSystems::reinit ()
      93             : {
      94       22188 :   const bool mesh_changed = this->reinit_solutions();
      95             : 
      96             :   // If the mesh has changed, systems will need to reinitialize their
      97             :   // own data on the new mesh.
      98       22188 :   if (mesh_changed)
      99       22188 :     this->reinit_systems();
     100       22188 : }
     101             : 
     102      234271 : void EquationSystems::reinit_mesh ()
     103             : {
     104        6690 :   const unsigned int n_sys = this->n_systems();
     105             : 
     106        6690 :   libmesh_assert_not_equal_to (n_sys, 0);
     107             : 
     108             :   // Tell all the \p DofObject entities how many systems
     109             :   // there are.
     110    27352622 :   for (auto & node : _mesh.node_ptr_range())
     111    14633752 :     node->set_n_systems(n_sys);
     112             : 
     113    12188014 :   for (auto & elem : _mesh.element_ptr_range())
     114     6484302 :     elem->set_n_systems(n_sys);
     115             : 
     116             :   //for (auto i : make_range(this->n_systems()))
     117             :     //this->get_system(i).init();
     118             : 
     119             : #ifdef LIBMESH_ENABLE_AMR
     120      247651 :   MeshRefinement mesh_refine(_mesh);
     121      234271 :   mesh_refine.clean_refinement_flags();
     122             : #endif
     123             : 
     124             :  // Now loop over all the systems belonging to this ES
     125             :  // and call reinit_mesh for each system
     126      471064 :  for (auto i : make_range(this->n_systems()))
     127      236887 :     this->get_system(i).reinit_mesh();
     128             : 
     129      234263 : }
     130             : 
     131       22188 : bool EquationSystems::reinit_solutions ()
     132             : {
     133         754 :   parallel_object_only();
     134             : 
     135         754 :   const unsigned int n_sys = this->n_systems();
     136         754 :   libmesh_assert_not_equal_to (n_sys, 0);
     137             : 
     138             :   // And any new systems will need initialization
     139       45123 :   for (unsigned int i=0; i != n_sys; ++i)
     140       22935 :     if (!this->get_system(i).is_initialized())
     141         143 :       this->get_system(i).init();
     142             : 
     143             :   // We used to assert that all nodes and elements *already* had
     144             :   // n_systems() properly set; however this is false in the case where
     145             :   // user code has manually added nodes and/or elements to an
     146             :   // already-initialized system.
     147             : 
     148             :   // Make sure all the \p DofObject entities know how many systems
     149             :   // there are.
     150             :   {
     151             :     // All the nodes
     152    15338536 :     for (auto & node : _mesh.node_ptr_range())
     153     8291687 :       node->set_n_systems(n_sys);
     154             : 
     155             :     // All the elements
     156    16522204 :     for (auto & elem : _mesh.element_ptr_range())
     157     8758045 :       elem->set_n_systems(n_sys);
     158             :   }
     159             : 
     160             :   // Localize each system's vectors
     161       45123 :   for (unsigned int i=0; i != n_sys; ++i)
     162       22935 :     this->get_system(i).re_update();
     163             : 
     164             : #ifdef LIBMESH_ENABLE_AMR
     165             : 
     166         754 :   bool mesh_changed = false;
     167             : 
     168             :   // FIXME: For backwards compatibility, assume
     169             :   // refine_and_coarsen_elements or refine_uniformly have already
     170             :   // been called
     171             :   {
     172       45123 :     for (unsigned int i=0; i != n_sys; ++i)
     173             :       {
     174         766 :         System & sys = this->get_system(i);
     175             : 
     176             :         // Even if the system doesn't have any variables in it we want
     177             :         // consistent behavior; e.g. distribute_dofs should have the
     178             :         // opportunity to count up zero dofs on each processor.
     179             :         //
     180             :         // Who's been adding zero-var systems anyway, outside of my
     181             :         // unit tests? - RHS
     182             :         // if (!sys.n_vars())
     183             :         // continue;
     184             : 
     185       22935 :         sys.get_dof_map().distribute_dofs(_mesh);
     186             : 
     187             :         // Recreate any user or internal constraints
     188       22935 :         sys.reinit_constraints();
     189             : 
     190             :         // Even if there weren't any constraint changes,
     191             :         // reinit_constraints() did prepare_send_list() for us.
     192             : 
     193       22935 :         sys.prolong_vectors();
     194             :       }
     195         754 :     mesh_changed = true;
     196             :   }
     197             : 
     198       22188 :   if (this->_refine_in_reinit)
     199             :     {
     200             :       // Don't override any user refinement settings
     201       23546 :       MeshRefinement mesh_refine(_mesh);
     202       22046 :       mesh_refine.face_level_mismatch_limit() = 0; // unlimited
     203       22046 :       mesh_refine.overrefined_boundary_limit() = -1; // unlimited
     204       22046 :       mesh_refine.underrefined_boundary_limit() = -1; // unlimited
     205             : 
     206             :       // Try to coarsen the mesh, then restrict each system's vectors
     207             :       // if necessary
     208       22046 :       if (mesh_refine.coarsen_elements())
     209             :         {
     210           0 :           for (auto i : make_range(this->n_systems()))
     211             :             {
     212           0 :               System & sys = this->get_system(i);
     213           0 :               sys.get_dof_map().distribute_dofs(_mesh);
     214           0 :               sys.reinit_constraints();
     215             : 
     216             :               // Even if there weren't any constraint changes,
     217             :               // reinit_constraints() did prepare_send_list() for us.
     218             : 
     219           0 :               sys.restrict_vectors();
     220             :             }
     221           0 :           mesh_changed = true;
     222             :         }
     223             : 
     224             :       // Once vectors are all restricted, we can delete
     225             :       // children of coarsened elements
     226         750 :       if (mesh_changed)
     227       22046 :         this->get_mesh().contract();
     228             : 
     229             :       // Try to refine the mesh, then prolong each system's vectors
     230             :       // if necessary
     231       22046 :       if (mesh_refine.refine_elements())
     232             :         {
     233        2414 :           for (auto i : make_range(this->n_systems()))
     234             :             {
     235          34 :               System & sys = this->get_system(i);
     236        1207 :               sys.get_dof_map().distribute_dofs(_mesh);
     237        1207 :               sys.reinit_constraints();
     238             : 
     239             :               // Even if there weren't any constraint changes,
     240             :               // reinit_constraints() did prepare_send_list() for us.
     241             : 
     242        1207 :               sys.prolong_vectors();
     243             :             }
     244          34 :           mesh_changed = true;
     245             :         }
     246       20546 :     }
     247             : 
     248         754 :   return mesh_changed;
     249             : 
     250             : #endif // #ifdef LIBMESH_ENABLE_AMR
     251             : 
     252             :   return false;
     253             : }
     254             : 
     255             : 
     256             : 
     257       22188 : void EquationSystems::reinit_systems()
     258             : {
     259       45123 :   for (auto i : make_range(this->n_systems()))
     260       22935 :     this->get_system(i).reinit();
     261       22188 : }
     262             : 
     263             : 
     264             : 
     265           0 : void EquationSystems::allgather ()
     266             : {
     267             :   // A serial mesh means nothing needs to be done
     268           0 :   if (_mesh.is_serial())
     269           0 :     return;
     270             : 
     271           0 :   const unsigned int n_sys = this->n_systems();
     272             : 
     273           0 :   libmesh_assert_not_equal_to (n_sys, 0);
     274             : 
     275             :   // Gather the mesh
     276           0 :   _mesh.allgather();
     277             : 
     278             :   // Tell all the \p DofObject entities how many systems
     279             :   // there are.
     280           0 :   for (auto & node : _mesh.node_ptr_range())
     281           0 :     node->set_n_systems(n_sys);
     282             : 
     283           0 :   for (auto & elem : _mesh.element_ptr_range())
     284           0 :     elem->set_n_systems(n_sys);
     285             : 
     286             :   // And distribute each system's dofs
     287           0 :   for (auto i : make_range(this->n_systems()))
     288             :     {
     289           0 :       System & sys = this->get_system(i);
     290           0 :       DofMap & dof_map = sys.get_dof_map();
     291           0 :       dof_map.distribute_dofs(_mesh);
     292             : 
     293             :       // The user probably won't need constraint equations or the
     294             :       // send_list after an allgather, but let's keep it in consistent
     295             :       // shape just in case.
     296           0 :       sys.reinit_constraints();
     297             : 
     298             :       // Even if there weren't any constraint changes,
     299             :       // reinit_constraints() did prepare_send_list() for us.
     300             :     }
     301             : }
     302             : 
     303             : 
     304             : 
     305         213 : void EquationSystems::enable_default_ghosting (bool enable)
     306             : {
     307         213 :   _enable_default_ghosting = enable;
     308          12 :   MeshBase &mesh = this->get_mesh();
     309             : 
     310         213 :   if (enable)
     311          71 :     mesh.add_ghosting_functor(mesh.default_ghosting());
     312             :   else
     313         142 :     mesh.remove_ghosting_functor(mesh.default_ghosting());
     314             : 
     315         426 :   for (auto i : make_range(this->n_systems()))
     316             :     {
     317           6 :       DofMap & dof_map = this->get_system(i).get_dof_map();
     318         213 :       if (enable)
     319          71 :         dof_map.add_default_ghosting();
     320             :       else
     321         142 :         dof_map.remove_default_ghosting();
     322             :     }
     323         213 : }
     324             : 
     325             : 
     326             : 
     327       10858 : void EquationSystems::update ()
     328             : {
     329         624 :   LOG_SCOPE("update()", "EquationSystems");
     330             : 
     331             :   // Localize each system's vectors
     332       22347 :   for (auto i : make_range(this->n_systems()))
     333       11489 :     this->get_system(i).update();
     334       10858 : }
     335             : 
     336             : 
     337             : 
     338        1339 : System & EquationSystems::add_system (std::string_view sys_type,
     339             :                                       std::string_view name)
     340             : {
     341             :   // If the user already built a system with this name, we'll
     342             :   // trust them and we'll use it.  That way they can pre-add
     343             :   // non-standard derived system classes, and if their restart file
     344             :   // has some non-standard sys_type we won't throw an error.
     345        1339 :   if (_systems.count(name))
     346             :     {
     347         145 :       return this->get_system(name);
     348             :     }
     349             :   // Build a basic System
     350        1174 :   else if (sys_type == "Basic")
     351         701 :     this->add_system<System> (name);
     352             : 
     353             :   // Build a Newmark system
     354         493 :   else if (sys_type == "Newmark")
     355           0 :     this->add_system<NewmarkSystem> (name);
     356             : 
     357             :   // Build an Explicit system
     358         491 :   else if ((sys_type == "Explicit"))
     359          71 :     this->add_system<ExplicitSystem> (name);
     360             : 
     361             :   // Build an Implicit system
     362         434 :   else if ((sys_type == "Implicit") ||
     363         434 :            (sys_type == "Steady"  ))
     364           0 :     this->add_system<ImplicitSystem> (name);
     365             : 
     366             :   // build a transient implicit linear system
     367        1230 :   else if ((sys_type == "Transient") ||
     368         434 :            (sys_type == "TransientImplicit") ||
     369         434 :            (sys_type == "TransientLinearImplicit"))
     370         142 :     this->add_system<TransientLinearImplicitSystem> (name);
     371             : 
     372             :   // build a transient implicit nonlinear system
     373         280 :   else if (sys_type == "TransientNonlinearImplicit")
     374           0 :     this->add_system<TransientNonlinearImplicitSystem> (name);
     375             : 
     376             :   // build a transient explicit system
     377         280 :   else if (sys_type == "TransientExplicit")
     378           0 :     this->add_system<TransientExplicitSystem> (name);
     379             : 
     380             :   // build a linear implicit system
     381         280 :   else if (sys_type == "LinearImplicit")
     382           0 :     this->add_system<LinearImplicitSystem> (name);
     383             : 
     384             :   // build a nonlinear implicit system
     385         272 :   else if (sys_type == "NonlinearImplicit")
     386         280 :     this->add_system<NonlinearImplicitSystem> (name);
     387             : 
     388             :   // build a Reduced Basis Construction system
     389           0 :   else if (sys_type == "RBConstruction")
     390           0 :     this->add_system<RBConstruction> (name);
     391             : 
     392             :   // build a transient Reduced Basis Construction system
     393           0 :   else if (sys_type == "TransientRBConstruction")
     394           0 :     this->add_system<TransientRBConstruction> (name);
     395             : 
     396             : #ifdef LIBMESH_HAVE_SLEPC
     397             :   // build an eigen system
     398           0 :   else if (sys_type == "Eigen")
     399           0 :     this->add_system<EigenSystem> (name);
     400           0 :   else if (sys_type == "TransientEigenSystem")
     401           0 :     this->add_system<TransientEigenSystem> (name);
     402             : #endif
     403             : 
     404             : #if defined(LIBMESH_USE_COMPLEX_NUMBERS)
     405             :   // build a frequency system
     406           0 :   else if (sys_type == "Frequency")
     407           0 :     this->add_system<FrequencySystem> (name);
     408             : #endif
     409             : 
     410             :   else
     411           0 :     libmesh_error_msg("ERROR: Unknown system type: " << sys_type);
     412             : 
     413             :   // Return a reference to the new system
     414             :   //return (*this)(name);
     415        1194 :   return this->get_system(name);
     416             : }
     417             : 
     418             : 
     419             : 
     420           0 : void EquationSystems::solve ()
     421             : {
     422           0 :   libmesh_assert (this->n_systems());
     423             : 
     424           0 :   for (auto i : make_range(this->n_systems()))
     425           0 :     this->get_system(i).solve();
     426           0 : }
     427             : 
     428             : 
     429             : 
     430           0 : void EquationSystems::sensitivity_solve (const ParameterVector & parameters_in)
     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).sensitivity_solve(parameters_in);
     436           0 : }
     437             : 
     438             : 
     439             : 
     440           0 : void EquationSystems::adjoint_solve (const QoISet & qoi_indices)
     441             : {
     442           0 :   libmesh_assert (this->n_systems());
     443             : 
     444           0 :   for (unsigned int i=this->n_systems(); i != 0; --i)
     445           0 :     this->get_system(i-1).adjoint_solve(qoi_indices);
     446           0 : }
     447             : 
     448             : 
     449             : 
     450       85751 : void EquationSystems::build_variable_names (std::vector<std::string> & var_names,
     451             :                                             const FEType * type,
     452             :                                             const std::set<std::string> * system_names) const
     453             : {
     454             :   // start indexing at end of possibly non-empty vector of variable names to avoid overwriting them
     455       88463 :   unsigned int var_num = var_names.size();
     456             : 
     457             :   // We'll want to double-check that we don't have any naming
     458             :   // conflicts; this API causes problems down the line if so.
     459        5424 :   std::unordered_multiset<std::string> seen_names;
     460             : 
     461             :   // Need to size var_names by scalar variables plus all the
     462             :   // vector components for all the vector variables
     463             :   //Could this be replaced by a/some convenience methods?[PB]
     464             :   {
     465        2712 :     unsigned int n_scalar_vars = 0;
     466        2712 :     unsigned int n_vector_vars = 0;
     467             : 
     468      174316 :     for (const auto & [sys_name, sys_ptr] : _systems)
     469             :       {
     470             :         // Check current system is listed in system_names, and skip pos if not
     471        5648 :         bool use_current_system = (system_names == nullptr);
     472       88565 :         if (!use_current_system)
     473         846 :           use_current_system = system_names->count(sys_name);
     474       88565 :         if (!use_current_system || sys_ptr->hide_output())
     475             :           {
     476        1552 :             for (auto vn : make_range(sys_ptr->n_vars()))
     477          22 :               seen_names.insert(sys_ptr->variable_name(vn));
     478         754 :             continue;
     479         732 :           }
     480             : 
     481      208646 :         for (auto vn : make_range(sys_ptr->n_vars()))
     482             :           {
     483        3832 :             seen_names.insert(sys_ptr->variable_name(vn));
     484      120857 :             if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
     485        6938 :               n_vector_vars++;
     486             :             else
     487      113919 :               n_scalar_vars++;
     488             :           }
     489             :       }
     490             : 
     491             :     // Here, we're assuming the number of vector components is the same
     492             :     // as the mesh spatial dimension.
     493       85751 :     unsigned int dim = this->get_mesh().spatial_dimension();
     494       85751 :     unsigned int nv = n_scalar_vars + dim*n_vector_vars;
     495             : 
     496             :     // We'd better not have more than dim*this->n_vars() (all vector variables)
     497             :     // Treat the NodeElem-only mesh case as dim=1
     498        2712 :     libmesh_assert_less_equal ( nv, (dim > 0 ? dim : 1)*this->n_vars() );
     499             : 
     500             :     // 'nv' represents the max possible number of output variables, so allocate enough memory for
     501             :     // all variables in the system to be populated here. When this is called more than once on a
     502             :     // single 'var_names' vector, different filters should be used such that duplicates don't occur.
     503       85751 :     var_names.resize( nv );
     504             :   }
     505             : 
     506      174245 :   for (const auto & [sys_name, sys_ptr] : _systems)
     507             :     {
     508             :       // Check current system is listed in system_names, and skip pos if not
     509        5648 :       bool use_current_system = (system_names == nullptr);
     510       88565 :       if (!use_current_system)
     511         846 :         use_current_system = system_names->count(sys_name);
     512       88565 :       if (!use_current_system || sys_ptr->hide_output())
     513         754 :         continue;
     514             : 
     515      208575 :       for (auto vn : make_range(sys_ptr->n_vars()))
     516             :         {
     517        3832 :           const std::string & var_name = sys_ptr->variable_name(vn);
     518        3832 :           const FEType & fe_type = sys_ptr->variable_type(vn);
     519             : 
     520      120857 :           unsigned int n_vec_dim = FEInterface::n_vec_dim( sys_ptr->get_mesh(), fe_type);
     521             : 
     522             :           // Filter on the type if requested
     523      120857 :           if (type == nullptr || (type && *type == fe_type))
     524             :             {
     525      120504 :               if (FEInterface::field_type(fe_type) == TYPE_VECTOR)
     526             :                 {
     527        6868 :                   switch(n_vec_dim)
     528             :                     {
     529           0 :                     case 0:
     530             :                     case 1:
     531           0 :                       var_names[var_num++] = var_name;
     532           0 :                       libmesh_error_msg_if(seen_names.count(var_name) > 1,
     533             :                                            "Duplicate variable name "+var_name);
     534           0 :                       break;
     535        5732 :                     case 2:
     536        5732 :                       var_names[var_num++] = var_name+"_x";
     537        5732 :                       var_names[var_num++] = var_name+"_y";
     538       11509 :                       libmesh_error_msg_if(seen_names.count(var_name+"_x"),
     539             :                                            "Duplicate variable name "+var_name+"_x");
     540       11162 :                       libmesh_error_msg_if(seen_names.count(var_name+"_y"),
     541             :                                            "Duplicate variable name "+var_name+"_y");
     542         160 :                       break;
     543        1136 :                     case 3:
     544        1136 :                       var_names[var_num++] = var_name+"_x";
     545        1136 :                       var_names[var_num++] = var_name+"_y";
     546        1136 :                       var_names[var_num++] = var_name+"_z";
     547        2240 :                       libmesh_error_msg_if(seen_names.count(var_name+"_x"),
     548             :                                            "Duplicate variable name "+var_name+"_x");
     549        2240 :                       libmesh_error_msg_if(seen_names.count(var_name+"_y"),
     550             :                                            "Duplicate variable name "+var_name+"_y");
     551        2307 :                       libmesh_error_msg_if(seen_names.count(var_name+"_z"),
     552             :                                            "Duplicate variable name "+var_name+"_z");
     553          32 :                       break;
     554           0 :                     default:
     555           0 :                       libmesh_error_msg("Invalid dim in build_variable_names");
     556             :                     }
     557             :                 }
     558             :               else
     559      113636 :                 var_names[var_num++] = var_name;
     560             :             }
     561             :         }
     562             :     }
     563             :   // Now resize again in case we filtered any names
     564       85680 :   var_names.resize(var_num);
     565       85680 : }
     566             : 
     567             : 
     568             : 
     569           0 : void EquationSystems::build_solution_vector (std::vector<Number> &,
     570             :                                              std::string_view,
     571             :                                              std::string_view) const
     572             : {
     573             :   // TODO:[BSK] re-implement this from the method below
     574           0 :   libmesh_not_implemented();
     575             : }
     576             : 
     577             : 
     578             : 
     579             : 
     580             : std::unique_ptr<NumericVector<Number>>
     581       79114 : EquationSystems::build_parallel_solution_vector(const std::set<std::string> * system_names,
     582             :                                                 bool add_sides) const
     583             : {
     584        4900 :   LOG_SCOPE("build_parallel_solution_vector()", "EquationSystems");
     585             : 
     586             :   // This function must be run on all processors at once
     587        2450 :   parallel_object_only();
     588             : 
     589       79114 :   const unsigned int dim = _mesh.spatial_dimension();
     590       79114 :   const dof_id_type max_nn   = _mesh.max_node_id();
     591             : 
     592             :   // allocate vector storage to hold
     593             :   // (max_node_id)*(number_of_variables) entries.
     594             :   //
     595             :   // If node renumbering is disabled and adaptive coarsening has
     596             :   // created gaps between node numbers, then this vector will be
     597             :   // sparse.
     598             :   //
     599             :   // We have to differentiate between between scalar and vector
     600             :   // variables. We intercept vector variables and treat each
     601             :   // component as a scalar variable (consistently with build_solution_names).
     602             : 
     603        2450 :   unsigned int nv = 0;
     604             : 
     605             :   //Could this be replaced by a/some convenience methods?[PB]
     606             :   {
     607        2450 :     unsigned int n_scalar_vars = 0;
     608        2450 :     unsigned int n_vector_vars = 0;
     609      160900 :     for (const auto & [sys_name, sys_ptr] : _systems)
     610             :       {
     611             :         // Check current system is listed in system_names, and skip pos if not
     612        5116 :         bool use_current_system = (system_names == nullptr);
     613       81786 :         if (!use_current_system)
     614         352 :           use_current_system = system_names->count(sys_name);
     615       81786 :         if (!use_current_system || sys_ptr->hide_output())
     616         342 :           continue;
     617             : 
     618      194516 :         for (auto vn : make_range(sys_ptr->n_vars()))
     619             :           {
     620      113082 :             if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
     621        6444 :               n_vector_vars++;
     622             :             else
     623      106638 :               n_scalar_vars++;
     624             :           }
     625             :       }
     626             :     // Here, we're assuming the number of vector components is the same
     627             :     // as the mesh spatial dimension.
     628       79114 :     nv = n_scalar_vars + dim*n_vector_vars;
     629             :   }
     630             : 
     631             :   // Get the number of nodes to store locally.
     632             :   dof_id_type n_local_nodes = cast_int<dof_id_type>
     633      155778 :     (std::distance(_mesh.local_nodes_begin(),
     634      158228 :                    _mesh.local_nodes_end()));
     635             : 
     636             :   // If node renumbering has been disabled, nodes may not be numbered
     637             :   // contiguously, and the number of nodes might not match the
     638             :   // max_node_id.  In this case we just do our best.
     639       79114 :   dof_id_type n_total_nodes = n_local_nodes;
     640       79114 :   _mesh.comm().sum(n_total_nodes);
     641             : 
     642       79114 :   const processor_id_type n_proc = _mesh.comm().size();
     643        4900 :   const processor_id_type my_pid = _mesh.comm().rank();
     644       79114 :   const dof_id_type n_gaps = max_nn - n_total_nodes;
     645       79114 :   const dof_id_type gaps_per_processor = n_gaps / n_proc;
     646       79114 :   const dof_id_type remainder_gaps = n_gaps % n_proc;
     647             : 
     648       84014 :   n_local_nodes = n_local_nodes +      // Actual nodes
     649       79114 :                   gaps_per_processor + // Our even share of gaps
     650       79114 :                   (my_pid < remainder_gaps); // Leftovers
     651             : 
     652             :   // If we've been asked to build added sides' data, we need space to
     653             :   // add it.  Keep track of how much space.
     654       79114 :   dof_id_type local_added_side_nodes = 0,
     655        2450 :               added_side_nodes = 0;
     656             : 
     657             :   // others_added_side_nodes[p]: local_added_side_nodes on rank p
     658        4900 :   std::vector<dof_id_type> others_added_side_nodes;
     659             : 
     660             :   // A map of (element_id, side, side_node) pairs to the corresponding
     661             :   // added side node index.
     662             :   std::map<std::tuple<dof_id_type, unsigned short, unsigned short>,
     663        4900 :            dof_id_type> discontinuous_node_indices;
     664             : 
     665             :   // If we don't have any added side nodes, we'll have no offsets from
     666             :   // them, and we won't care about which offsets apply to which node
     667             :   // ids either.
     668             : 
     669             :   // Number of true nodes on processors [0,p]
     670        4900 :   std::vector<dof_id_type> true_node_offsets;
     671             :   // Number of added (fake) nodes on processors [0,p)
     672        4900 :   std::vector<dof_id_type> added_node_offsets;
     673             : 
     674             :   auto node_id_to_vec_id =
     675    69460004 :     [&true_node_offsets, &added_node_offsets]
     676    84448385 :     (const dof_id_type node_id)
     677             :     {
     678    84438125 :       if (true_node_offsets.empty())
     679    76926491 :         return node_id; // O(1) in the common !add_sides case
     680             : 
     681             :       // Find the processor id that has node_id in the parallel vec
     682       20520 :       const auto lb = std::upper_bound(true_node_offsets.begin(),
     683        2052 :                                        true_node_offsets.end(), node_id);
     684        2052 :       libmesh_assert(lb != true_node_offsets.end());
     685        2052 :       const processor_id_type p = lb - true_node_offsets.begin();
     686             : 
     687       26676 :       return node_id + added_node_offsets[p];
     688       79114 :     };
     689             : 
     690       79114 :   if (add_sides)
     691             :     {
     692        1207 :       true_node_offsets.resize(n_proc);
     693        1207 :       added_node_offsets.resize(n_proc);
     694             : 
     695             :       // One loop to count everyone's new side nodes
     696       10940 :       for (const auto & elem : _mesh.active_element_ptr_range())
     697             :         {
     698       25064 :           for (auto s : elem->side_index_range())
     699             :             {
     700       20400 :               if (redundant_added_side(*elem,s))
     701        5460 :                 continue;
     702             : 
     703             :               const std::vector<unsigned int> side_nodes =
     704       15548 :                 elem->nodes_on_side(s);
     705             : 
     706       15548 :               if (elem->processor_id() == this->processor_id())
     707        3952 :                 local_added_side_nodes += side_nodes.size();
     708             :             }
     709        1139 :         }
     710             : 
     711        1207 :       others_added_side_nodes.resize(n_proc);
     712        1207 :       _mesh.comm().allgather(local_added_side_nodes,
     713             :                              others_added_side_nodes);
     714             : 
     715        1207 :       added_side_nodes = std::accumulate(others_added_side_nodes.begin(),
     716             :                                          others_added_side_nodes.end(), 0,
     717             :                                          std::plus<>());
     718             : 
     719        1207 :       _mesh.comm().allgather(n_local_nodes, true_node_offsets);
     720       11781 :       for (auto p : make_range(n_proc-1))
     721       10642 :         true_node_offsets[p+1] += true_node_offsets[p];
     722          34 :       libmesh_assert_equal_to(true_node_offsets[n_proc-1], _mesh.max_node_id());
     723             : 
     724             :       // For nodes that exist in the mesh, we just need an offset to
     725             :       // tell where to put their solutions.
     726        1207 :       added_node_offsets[0] = 0;
     727       11781 :       for (auto p : make_range(n_proc-1))
     728       10574 :         added_node_offsets[p+1] =
     729       10642 :           added_node_offsets[p] + others_added_side_nodes[p];
     730             : 
     731             :       // For added side nodes, we need to fill a map.  Start after all
     732             :       // the true node for our pid plus all the side nodes for
     733             :       // previous pids
     734        1241 :       dof_id_type node_counter = true_node_offsets[my_pid];
     735        6494 :       for (auto p : make_range(my_pid))
     736        5304 :         node_counter += others_added_side_nodes[p];
     737             : 
     738             :       // One loop to figure out whose added side nodes get which index
     739        4492 :       for (const auto & elem : _mesh.active_local_element_ptr_range())
     740             :         {
     741        6240 :           for (auto s : elem->side_index_range())
     742             :             {
     743        4992 :               if (redundant_added_side(*elem,s))
     744        1344 :                 continue;
     745             : 
     746             :               const std::vector<unsigned int> side_nodes =
     747        3952 :                 elem->nodes_on_side(s);
     748             : 
     749       27264 :               for (auto n : index_range(side_nodes))
     750             :                 discontinuous_node_indices
     751       25584 :                   [std::make_tuple(elem->id(),s,n)] = node_counter++;
     752             :             }
     753        1139 :         }
     754             :     }
     755             : 
     756             :   const dof_id_type
     757       79114 :     n_global_vals = (max_nn + added_side_nodes) * nv,
     758       79114 :     n_local_vals = (n_local_nodes + local_added_side_nodes) * nv;
     759             : 
     760             :   // Create a NumericVector to hold the parallel solution
     761       79114 :   std::unique_ptr<NumericVector<Number>> parallel_soln_ptr = NumericVector<Number>::build(_communicator);
     762        2450 :   NumericVector<Number> & parallel_soln = *parallel_soln_ptr;
     763       79114 :   parallel_soln.init(n_global_vals, n_local_vals, false, PARALLEL);
     764             : 
     765             :   // Create a NumericVector to hold the "repeat_count" for each node - this is essentially
     766             :   // the number of elements contributing to that node's value
     767       81564 :   std::unique_ptr<NumericVector<Number>> repeat_count_ptr = NumericVector<Number>::build(_communicator);
     768        2450 :   NumericVector<Number> & repeat_count = *repeat_count_ptr;
     769       79114 :   repeat_count.init(n_global_vals, n_local_vals, false, PARALLEL);
     770             : 
     771       79114 :   repeat_count.close();
     772             : 
     773        2450 :   unsigned int var_num=0;
     774             : 
     775             :   // For each system in this EquationSystems object,
     776             :   // update the global solution and if we are on processor 0,
     777             :   // loop over the elements and build the nodal solution
     778             :   // from the element solution.  Then insert this nodal solution
     779             :   // into the vector passed to build_solution_vector.
     780      160900 :   for (const auto & [sys_name, sys_ptr] : _systems)
     781             :     {
     782             :       // Check current system is listed in system_names, and skip pos if not
     783        5116 :       bool use_current_system = (system_names == nullptr);
     784       81786 :       if (!use_current_system)
     785         352 :         use_current_system = system_names->count(sys_name);
     786       81786 :       if (!use_current_system || sys_ptr->hide_output())
     787         352 :         continue;
     788             : 
     789        2548 :       const System & system  = *sys_ptr;
     790        2548 :       const unsigned int nv_sys = system.n_vars();
     791        5096 :       const unsigned int sys_num = system.number();
     792             : 
     793             :       //Could this be replaced by a/some convenience methods?[PB]
     794        2548 :       unsigned int n_scalar_vars = 0;
     795        2548 :       unsigned int n_vector_vars = 0;
     796      194516 :       for (auto vn : make_range(sys_ptr->n_vars()))
     797             :         {
     798      113082 :           if (FEInterface::field_type(sys_ptr->variable_type(vn)) == TYPE_VECTOR)
     799        6444 :             n_vector_vars++;
     800             :           else
     801      106638 :             n_scalar_vars++;
     802             :         }
     803             : 
     804             :       // Here, we're assuming the number of vector components is the same
     805             :       // as the mesh spatial dimension.
     806       81434 :       unsigned int nv_sys_split = n_scalar_vars + dim*n_vector_vars;
     807             : 
     808             :       // Update the current_local_solution
     809             :       {
     810        2548 :         System & non_const_sys = const_cast<System &>(system);
     811             :         // We used to simply call non_const_sys.solution->close()
     812             :         // here, but that is not allowed when the solution vector is
     813             :         // locked read-only, for example when printing the solution
     814             :         // during the middle of a solve...  So try to be a bit
     815             :         // more careful about calling close() unnecessarily.
     816        2548 :         libmesh_assert(this->comm().verify(non_const_sys.solution->closed()));
     817       81434 :         if (!non_const_sys.solution->closed())
     818           0 :           non_const_sys.solution->close();
     819       81434 :         non_const_sys.update();
     820             :       }
     821             : 
     822        2548 :       NumericVector<Number> & sys_soln(*system.current_local_solution);
     823             : 
     824        2548 :       const DofMap & dof_map = system.get_dof_map();
     825             : 
     826        5096 :       std::vector<Number>      elem_soln;   // The finite element solution
     827        5096 :       std::vector<Number>      nodal_soln;  // The FE solution interpolated to the nodes
     828        2548 :       std::vector<dof_id_type> dof_indices; // The DOF indices for the finite element
     829             : 
     830        2548 :       unsigned var_inc = 0;
     831      194516 :       for (unsigned int var=0; var<nv_sys; var++)
     832             :         {
     833        3538 :           const FEType & fe_type           = system.variable_type(var);
     834        3538 :           const Variable & var_description = system.variable(var);
     835      113082 :           unsigned int n_vec_dim = FEInterface::n_vec_dim( sys_ptr->get_mesh(), fe_type );
     836      113082 :           const auto vg = dof_map.var_group_from_var_number(var);
     837      113082 :           const bool add_p_level = dof_map.should_p_refine(vg);
     838             : 
     839    28816765 :           for (const auto & elem : _mesh.active_local_element_ptr_range())
     840             :             {
     841    15726435 :               if (var_description.active_on_subdomain(elem->subdomain_id()))
     842             :                 {
     843    15720903 :                   dof_map.dof_indices (elem, dof_indices, var);
     844    15720903 :                   sys_soln.get(dof_indices, elem_soln);
     845             : 
     846    15720903 :                   FEInterface::nodal_soln (elem->dim(),
     847             :                                            fe_type,
     848             :                                            elem,
     849             :                                            elem_soln,
     850             :                                            nodal_soln,
     851             :                                            add_p_level,
     852             :                                            n_vec_dim);
     853             : 
     854             :                   // infinite elements should be skipped...
     855     3435953 :                   if (!elem->infinite())
     856             :                     {
     857     1428904 :                       libmesh_assert_equal_to (nodal_soln.size(), n_vec_dim*elem->n_nodes());
     858             : 
     859   101509753 :                       for (auto n : elem->node_index_range())
     860             :                         {
     861    84359945 :                           const Node & node = elem->node_ref(n);
     862             : 
     863             :                           const dof_id_type node_idx =
     864    84359945 :                             nv * node_id_to_vec_id(node.id());
     865             : 
     866   184873114 :                           for (unsigned int d=0; d < n_vec_dim; d++)
     867             :                             {
     868             :                               // For vector-valued elements, all components are in nodal_soln. For each
     869             :                               // node, the components are stored in order, i.e. node_0 -> s0_x, s0_y, s0_z
     870   109342021 :                               parallel_soln.add(node_idx + (var_inc+d + var_num), nodal_soln[n_vec_dim*n+d]);
     871             : 
     872             :                               // Increment the repeat count for this position
     873   100513169 :                               repeat_count.add(node_idx + (var_inc+d + var_num), 1);
     874             :                             }
     875             :                         }
     876             : 
     877    15720903 :                       if (add_sides)
     878             :                         {
     879        9020 :                           for (auto s : elem->side_index_range())
     880             :                             {
     881        7200 :                               if (redundant_added_side(*elem,s))
     882        1782 :                                 continue;
     883             : 
     884             :                               // Compute the FE solution at all the
     885             :                               // side nodes
     886             :                               FEInterface::side_nodal_soln
     887        5256 :                                 (fe_type, elem, s, elem_soln,
     888             :                                  nodal_soln, add_p_level, n_vec_dim);
     889             : 
     890             : #ifdef DEBUG
     891             :                               const std::vector<unsigned int> side_nodes =
     892         876 :                                 elem->nodes_on_side(s);
     893             : 
     894         438 :                               libmesh_assert_equal_to
     895             :                                   (nodal_soln.size(),
     896             :                                    side_nodes.size());
     897             : #endif
     898             : 
     899       38736 :                               for (auto n : index_range(nodal_soln))
     900             :                                 {
     901             :                                   // Retrieve index into global solution vector.
     902             :                                   std::size_t node_index =
     903       36270 :                                     nv * libmesh_map_find(discontinuous_node_indices,
     904             :                                                           std::make_tuple(elem->id(), s, n));
     905             : 
     906       66960 :                                   for (unsigned int d=0; d < n_vec_dim; d++)
     907             :                                     {
     908       36270 :                                       parallel_soln.add(node_index + (var_inc+d + var_num), nodal_soln[n_vec_dim*n+d]);
     909       33480 :                                       repeat_count.add(node_index + (var_inc+d + var_num), 1);
     910             :                                     }
     911             :                                 }
     912             :                             }
     913             :                         }
     914             :                     }
     915             :                 }
     916             :               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:
     917      100536 :                 for (auto n : elem->node_index_range())
     918             :                   {
     919       95004 :                     const Node & node = elem->node_ref(n);
     920             :                     // Only do this if this variable has NO DoFs at
     921             :                     // this node... it might have some from an
     922             :                     // adjoining element...
     923       95004 :                     if (!node.n_dofs(sys_num, var))
     924             :                       {
     925             :                         const dof_id_type node_idx =
     926       78180 :                           nv * node_id_to_vec_id(node.id());
     927             : 
     928      156360 :                         for (unsigned int d=0; d < n_vec_dim; d++)
     929       78180 :                           repeat_count.add(node_idx + (var_inc+d + var_num), 1);
     930             :                       }
     931             :                   }
     932             : 
     933      106006 :             } // end loop over elements
     934      113082 :           var_inc += n_vec_dim;
     935             :         } // end loop on variables in this system
     936             : 
     937       81434 :       var_num += nv_sys_split;
     938             :     } // end loop over systems
     939             : 
     940             :   // Sum the nodal solution values and repeat counts.
     941       79114 :   parallel_soln.close();
     942       79114 :   repeat_count.close();
     943             : 
     944             :   // If there were gaps in the node numbering, there will be
     945             :   // corresponding zeros in the parallel_soln and repeat_count
     946             :   // vectors.  We need to set those repeat_count entries to 1
     947             :   // in order to avoid dividing by zero.
     948       79114 :   if (n_gaps)
     949             :     {
     950           0 :       for (numeric_index_type i=repeat_count.first_local_index();
     951           0 :            i<repeat_count.last_local_index(); ++i)
     952             :         {
     953             :           // repeat_count entries are integral values but let's avoid a
     954             :           // direct floating point comparison with 0 just in case some
     955             :           // roundoff noise crept in during vector assembly?
     956           0 :           if (std::abs(repeat_count(i)) < TOLERANCE)
     957           0 :             repeat_count.set(i, 1.);
     958             :         }
     959             : 
     960             :       // Make sure the repeat_count vector is up-to-date on all
     961             :       // processors.
     962           0 :       repeat_count.close();
     963             :     }
     964             : 
     965             :   // Divide to get the average value at the nodes
     966       79114 :   parallel_soln /= repeat_count;
     967             : 
     968       81564 :   return parallel_soln_ptr;
     969       74214 : }
     970             : 
     971             : 
     972             : 
     973       79114 : void EquationSystems::build_solution_vector (std::vector<Number> & soln,
     974             :                                              const std::set<std::string> * system_names,
     975             :                                              bool add_sides) const
     976             : {
     977        4900 :   LOG_SCOPE("build_solution_vector()", "EquationSystems");
     978             : 
     979             :   // Call the parallel implementation
     980             :   std::unique_ptr<NumericVector<Number>> parallel_soln =
     981       81564 :     this->build_parallel_solution_vector(system_names, add_sides);
     982             : 
     983             :   // Localize the NumericVector into the provided std::vector.
     984       79114 :   parallel_soln->localize_to_one(soln);
     985       79114 : }
     986             : 
     987             : 
     988             : 
     989        8017 : void EquationSystems::get_vars_active_subdomains(const std::vector<std::string> & names,
     990             :                                                  std::vector<std::set<subdomain_id_type>> & vars_active_subdomains) const
     991             : {
     992        8017 :   vars_active_subdomains.clear();
     993        8243 :   vars_active_subdomains.resize(names.size());
     994             : 
     995       16034 :   for (const auto & pr : _systems)
     996             :     {
     997         226 :       const auto & sys_ptr = pr.second;
     998       16034 :       for (auto vn : make_range(sys_ptr->n_vars()))
     999             :         {
    1000         226 :           const std::string & var_name = sys_ptr->variable_name(vn);
    1001             : 
    1002        8017 :           auto names_it = std::find(names.begin(), names.end(), var_name);
    1003        8243 :           if(names_it != names.end())
    1004             :             {
    1005         216 :               const Variable & variable = sys_ptr->variable(vn);
    1006         216 :               const std::set<subdomain_id_type> & active_subdomains = variable.active_subdomains();
    1007        7883 :               vars_active_subdomains[std::distance(names.begin(), names_it)] = active_subdomains;
    1008             :             }
    1009             :         }
    1010             :     }
    1011        8017 : }
    1012             : 
    1013             : 
    1014             : 
    1015             : #ifdef LIBMESH_ENABLE_DEPRECATED
    1016           0 : void EquationSystems::get_solution (std::vector<Number> & soln,
    1017             :                                     std::vector<std::string> & names) const
    1018             : {
    1019             :   libmesh_deprecated();
    1020           0 :   this->build_elemental_solution_vector(soln, names);
    1021           0 : }
    1022             : #endif // LIBMESH_ENABLE_DEPRECATED
    1023             : 
    1024             : 
    1025             : 
    1026             : void
    1027         352 : EquationSystems::build_elemental_solution_vector (std::vector<Number> & soln,
    1028             :                                                   std::vector<std::string> & names) const
    1029             : {
    1030             :   // Call the parallel version of this function
    1031             :   std::unique_ptr<NumericVector<Number>> parallel_soln =
    1032         362 :     this->build_parallel_elemental_solution_vector(names);
    1033             : 
    1034             :   // Localize into 'soln', provided that parallel_soln is not empty.
    1035             :   // Note: parallel_soln will be empty in the event that none of the
    1036             :   // input names were CONSTANT, MONOMIAL nor components of CONSTANT,
    1037             :   // MONOMIAL_VEC variables, or there were simply none of these in
    1038             :   // the EquationSystems object.
    1039          10 :   soln.clear();
    1040         352 :   if (parallel_soln)
    1041         352 :     parallel_soln->localize_to_one(soln);
    1042         352 : }
    1043             : 
    1044             : std::vector<std::pair<unsigned int, unsigned int>>
    1045        8231 : EquationSystems::find_variable_numbers
    1046             :   (std::vector<std::string> & names, const FEType * type, const std::vector<FEType> * types) const
    1047             : {
    1048             :   // This function must be run on all processors at once
    1049         232 :   parallel_object_only();
    1050             : 
    1051         232 :   libmesh_assert (this->n_systems());
    1052             : 
    1053             :   // Resolve class of type input and assert that at least one of them is null
    1054         232 :   libmesh_assert_msg(!type || !types,
    1055             :                      "Input 'type', 'types', or neither in find_variable_numbers, but not both.");
    1056             : 
    1057         464 :   std::vector<FEType> type_filter;
    1058        8231 :   if (type)
    1059           0 :     type_filter.push_back(*type);
    1060        8231 :   else if (types)
    1061        7947 :     type_filter = *types;
    1062             : 
    1063             :   // Store a copy of the valid variable names, if any. The names vector will be repopulated with any
    1064             :   // valid names (or all if 'is_names_empty') in the system that passes through the type filter. If
    1065             :   // the variable is a vector, its name will be decomposed into its separate components in
    1066             :   // accordance with build_variable_names().
    1067        8695 :   std::vector<std::string> name_filter = names;
    1068         232 :   bool is_names_empty = name_filter.empty();
    1069         232 :   names.clear();
    1070             : 
    1071             :   // initialize convenience variables
    1072         232 :   FEType var_type;
    1073         464 :   std::string name;
    1074             : 
    1075       33156 :   const std::vector<std::string> component_suffix = {"_x", "_y", "_z"};
    1076        8231 :   unsigned int dim = _mesh.spatial_dimension();
    1077        8231 :   libmesh_error_msg_if(dim > 3, "Invalid dim in find_variable_numbers");
    1078             : 
    1079             :   // Now filter through the variables in each system and store the system index and their index
    1080             :   // within that system. This way, we know where to find their data even after we sort them.
    1081         464 :   std::vector<std::pair<unsigned int, unsigned int>> var_nums;
    1082             : 
    1083       16462 :   for (const auto & pr : _systems)
    1084             :     {
    1085         232 :       const System & system = *(pr.second);
    1086             : 
    1087       16604 :       for (auto var : make_range(system.n_vars()))
    1088             :         {
    1089             :           // apply the type filter
    1090        8373 :           var_type = system.variable_type(var);
    1091        8821 :           if (type_filter.size() &&
    1092        8183 :               std::find(type_filter.begin(), type_filter.end(), var_type) == type_filter.end())
    1093           0 :             continue;
    1094             : 
    1095             :           // apply the name filter (note that all variables pass if it is empty)
    1096        8373 :           if (FEInterface::field_type(var_type) == TYPE_VECTOR)
    1097             :             {
    1098          28 :               std::vector<std::string> component_names;
    1099        1476 :               for (unsigned int comp = 0; comp < dim; ++comp)
    1100             :                 {
    1101        1040 :                   name = system.variable_name(var) + component_suffix[comp];
    1102        1008 :                   if (is_names_empty ||
    1103         452 :                       (std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end()))
    1104         984 :                     component_names.push_back(name);
    1105             :                 }
    1106             : 
    1107         492 :               if (! component_names.empty())
    1108         492 :                 names.insert(names.end(), component_names.begin(), component_names.end());
    1109             :               else
    1110           0 :                 continue;
    1111         464 :             }
    1112             :           else /*scalar-valued variable*/
    1113             :             {
    1114         444 :               name = system.variable_name(var);
    1115        7897 :               if (is_names_empty ||
    1116         506 :                   (std::find(name_filter.begin(), name_filter.end(), name) != name_filter.end()))
    1117        7881 :                 names.push_back(name);
    1118             :               else
    1119           0 :                 continue;
    1120             :             }
    1121             : 
    1122             :           // if the variable made it through both filters get its system indices
    1123        8373 :           var_nums.emplace_back(system.number(), var);
    1124             :         }
    1125             :     }
    1126             : 
    1127             :   // Sort the var_nums vector pairs alphabetically based on the variable name
    1128        8695 :   std::vector<unsigned int> sort_index(var_nums.size());
    1129         232 :   std::iota(sort_index.begin(), sort_index.end(), 0);
    1130        8231 :   std::sort(sort_index.begin(), sort_index.end(),
    1131         284 :             [&](const unsigned int & lhs, const unsigned int & rhs)
    1132         828 :             {return this->get_system(var_nums[lhs].first).variable_name(var_nums[lhs].second) <
    1133         300 :                     this->get_system(var_nums[rhs].first).variable_name(var_nums[rhs].second);});
    1134             : 
    1135        8463 :   std::vector<std::pair<unsigned int, unsigned int>> var_nums_sorted(var_nums.size());
    1136       16604 :   for (auto i : index_range(var_nums_sorted))
    1137             :     {
    1138        8845 :       var_nums_sorted[i].first = var_nums[sort_index[i]].first;
    1139        8609 :       var_nums_sorted[i].second = var_nums[sort_index[i]].second;
    1140             :     }
    1141             : 
    1142             :   // Also sort the names vector
    1143        8231 :   std::sort(names.begin(), names.end());
    1144             : 
    1145             :   // Return the sorted vector pairs
    1146        8463 :   return var_nums_sorted;
    1147       31068 : }
    1148             : 
    1149             : 
    1150             : std::unique_ptr<NumericVector<Number>>
    1151         352 : EquationSystems::build_parallel_elemental_solution_vector (std::vector<std::string> & names) const
    1152             : {
    1153             :   // Filter any names that aren't elemental variables and get the system indices for those that are.
    1154             :   // Note that it's probably fine if the names vector is empty since we'll still at least filter
    1155             :   // out all non-monomials. If there are no monomials, then nothing is output here.
    1156         362 :   std::vector<FEType> type = {FEType(CONSTANT, MONOMIAL), FEType(CONSTANT, MONOMIAL_VEC)};
    1157             :   std::vector<std::pair<unsigned int, unsigned int>> var_nums =
    1158         362 :     this->find_variable_numbers(names, /*type=*/nullptr, &type);
    1159             : 
    1160          20 :   const std::size_t nv = names.size(); /*total number of vars including vector components*/
    1161         352 :   const dof_id_type ne = _mesh.n_elem();
    1162          10 :   libmesh_assert_equal_to (ne, _mesh.max_elem_id());
    1163             : 
    1164             :   // If there are no variables to write out don't do anything...
    1165         352 :   if (!nv)
    1166           0 :     return std::unique_ptr<NumericVector<Number>>(nullptr);
    1167             : 
    1168             :   // We can handle the case where there are nullptrs in the Elem vector
    1169             :   // by just having extra zeros in the solution vector.
    1170         352 :   numeric_index_type parallel_soln_global_size = ne*nv;
    1171             : 
    1172         352 :   numeric_index_type div = parallel_soln_global_size / this->n_processors();
    1173         352 :   numeric_index_type mod = parallel_soln_global_size % this->n_processors();
    1174             : 
    1175             :   // Initialize all processors to the average size.
    1176          10 :   numeric_index_type parallel_soln_local_size = div;
    1177             : 
    1178             :   // The first "mod" processors get an extra entry.
    1179         352 :   if (this->processor_id() < mod)
    1180         136 :     parallel_soln_local_size = div+1;
    1181             : 
    1182             :   // Create a NumericVector to hold the parallel solution
    1183         362 :   std::unique_ptr<NumericVector<Number>> parallel_soln_ptr = NumericVector<Number>::build(_communicator);
    1184          10 :   NumericVector<Number> & parallel_soln = *parallel_soln_ptr;
    1185         352 :   parallel_soln.init(parallel_soln_global_size,
    1186             :                      parallel_soln_local_size,
    1187             :                      /*fast=*/false,
    1188          20 :                      /*ParallelType=*/PARALLEL);
    1189             : 
    1190          10 :   unsigned int sys_ctr = 0;
    1191          10 :   unsigned int var_ctr = 0;
    1192         704 :   for (auto i : index_range(var_nums))
    1193             :     {
    1194         362 :       std::pair<unsigned int, unsigned int> var_num = var_nums[i];
    1195          10 :       const System & system = this->get_system(var_num.first);
    1196             : 
    1197             :       // Update the current_local_solution if necessary
    1198         352 :       if (sys_ctr != var_num.first)
    1199             :         {
    1200           0 :           System & non_const_sys = const_cast<System &>(system);
    1201             :           // We used to simply call non_const_sys.solution->close()
    1202             :           // here, but that is not allowed when the solution vector is
    1203             :           // locked read-only, for example when printing the solution
    1204             :           // during during the middle of a solve...  So try to be a bit
    1205             :           // more careful about calling close() unnecessarily.
    1206           0 :           libmesh_assert(this->comm().verify(non_const_sys.solution->closed()));
    1207           0 :           if (!non_const_sys.solution->closed())
    1208           0 :             non_const_sys.solution->close();
    1209           0 :           non_const_sys.update();
    1210           0 :           sys_ctr = var_num.first;
    1211             :         }
    1212             : 
    1213          10 :       NumericVector<Number> & sys_soln(*system.current_local_solution);
    1214             : 
    1215             :       // The DOF indices for the finite element
    1216          10 :       std::vector<dof_id_type> dof_indices;
    1217             : 
    1218          10 :       const unsigned int var = var_num.second;
    1219             : 
    1220          10 :       const Variable & variable = system.variable(var);
    1221          10 :       const DofMap & dof_map = system.get_dof_map();
    1222             : 
    1223             :       // We need to check if the constant monomial is a scalar or a vector and set the number of
    1224             :       // components as the mesh spatial dimension for the latter as per es.find_variable_numbers().
    1225             :       // Even for the case where a variable is not active on any subdomain belonging to the
    1226             :       // processor, we still need to know this number to update 'var_ctr'.
    1227             :       const unsigned int n_comps =
    1228         226 :         (system.variable_type(var) == type[1]) ? _mesh.spatial_dimension() : 1;
    1229             : 
    1230             :       // Loop over all elements in the mesh and index all components of the variable if it's active
    1231        5950 :       for (const auto & elem : _mesh.active_local_element_ptr_range())
    1232        2889 :         if (variable.active_on_subdomain(elem->subdomain_id()))
    1233             :           {
    1234        2889 :             dof_map.dof_indices(elem, dof_indices, var);
    1235             : 
    1236             :             // The number of DOF components needs to be equal to the expected number so that we know
    1237             :             // where to store data to correctly correspond to variable names.
    1238         261 :             libmesh_assert_equal_to(dof_indices.size(), n_comps);
    1239             : 
    1240        8451 :             for (unsigned int comp = 0; comp < n_comps; comp++)
    1241        6066 :               parallel_soln.set(ne * (var_ctr + comp) + elem->id(), sys_soln(dof_indices[comp]));
    1242         332 :           }
    1243             : 
    1244         352 :       var_ctr += n_comps;
    1245             :     } // end loop over var_nums
    1246             : 
    1247             :   // NOTE: number of output names might not be equal to the number passed to this function. Any that
    1248             :   // aren't CONSTANT MONOMIALS or components of CONSTANT MONOMIAL_VECS have been filtered out (see
    1249             :   // EquationSystems::find_variable_numbers).
    1250             :   //
    1251             :   // But, if everything is accounted for properly, then names.size() == var_ctr
    1252          10 :   libmesh_assert_equal_to(names.size(), var_ctr);
    1253             : 
    1254         352 :   parallel_soln.close();
    1255          10 :   return parallel_soln_ptr;
    1256         332 : }
    1257             : 
    1258             : 
    1259             : 
    1260             : void
    1261        5507 : EquationSystems::build_discontinuous_solution_vector
    1262             : (std::vector<Number> & soln,
    1263             :  const std::set<std::string> * system_names,
    1264             :  const std::vector<std::string> * var_names,
    1265             :  bool vertices_only,
    1266             :  bool add_sides) const
    1267             : {
    1268         460 :   LOG_SCOPE("build_discontinuous_solution_vector()", "EquationSystems");
    1269             : 
    1270         230 :   libmesh_assert (this->n_systems());
    1271             : 
    1272             :   // Get the number of variables (nv) by counting the number of variables
    1273             :   // in each system listed in system_names
    1274         230 :   unsigned int nv = 0;
    1275             : 
    1276       11085 :   for (const auto & [sys_name, sys_ptr] : _systems)
    1277             :     {
    1278             :       // Check current system is listed in system_names, and skip pos if not
    1279         464 :       bool use_current_system = (system_names == nullptr);
    1280        5578 :       if (!use_current_system)
    1281          70 :         use_current_system = system_names->count(sys_name);
    1282        5578 :       if (!use_current_system || sys_ptr->hide_output())
    1283           0 :         continue;
    1284             : 
    1285             :       // Loop over all variables in this System and check whether we
    1286             :       // are supposed to use each one.
    1287       12292 :       for (auto var_id : make_range(sys_ptr->n_vars()))
    1288             :         {
    1289         528 :           bool use_current_var = (var_names == nullptr);
    1290        6714 :           if (!use_current_var)
    1291          72 :             use_current_var = std::count(var_names->begin(),
    1292             :                                          var_names->end(),
    1293           2 :                                          sys_ptr->variable_name(var_id));
    1294             : 
    1295             :           // Only increment the total number of vars if we are
    1296             :           // supposed to use this one.
    1297        6714 :           if (use_current_var)
    1298        6714 :             nv++;
    1299             :         }
    1300             :     }
    1301             : 
    1302             :   // get the total "weight" - the number of nodal values to write for
    1303             :   // each variable.
    1304         230 :   unsigned int tw=0;
    1305      408702 :   for (const auto & elem : _mesh.active_element_ptr_range())
    1306             :     {
    1307      223087 :       tw += vertices_only ? elem->n_vertices() : elem->n_nodes();
    1308             : 
    1309      223087 :       if (add_sides)
    1310             :         {
    1311       25064 :           for (auto s : elem->side_index_range())
    1312             :             {
    1313       20400 :               if (redundant_added_side(*elem,s))
    1314        5460 :                 continue;
    1315             : 
    1316             :               const std::vector<unsigned int> side_nodes =
    1317       15548 :                 elem->nodes_on_side(s);
    1318             : 
    1319       14940 :               if (!vertices_only)
    1320       15548 :                 tw += side_nodes.size();
    1321             :               else
    1322           0 :                 for (auto n : index_range(side_nodes))
    1323           0 :                   if (elem->is_vertex(side_nodes[n]))
    1324           0 :                     ++tw;
    1325             :             }
    1326             :         }
    1327        5047 :     }
    1328             : 
    1329             :   // Only if we are on processor zero, allocate the storage
    1330             :   // to hold (number_of_nodes)*(number_of_variables) entries.
    1331        5737 :   if (_mesh.processor_id() == 0)
    1332         986 :     soln.resize(tw*nv);
    1333             : 
    1334         460 :   std::vector<Number> sys_soln;
    1335             : 
    1336             :   // Keep track of the variable "offset". This is used for indexing
    1337             :   // into the global solution vector.
    1338         230 :   unsigned int var_offset = 0;
    1339             : 
    1340             :   // For each system in this EquationSystems object,
    1341             :   // update the global solution and if we are on processor 0,
    1342             :   // loop over the elements and build the nodal solution
    1343             :   // from the element solution.  Then insert this nodal solution
    1344             :   // into the vector passed to build_solution_vector.
    1345       11085 :   for (const auto & [sys_name, system] : _systems)
    1346             :     {
    1347             :       // Check current system is listed in system_names, and skip pos if not
    1348         464 :       bool use_current_system = (system_names == nullptr);
    1349        5578 :       if (!use_current_system)
    1350          70 :         use_current_system = system_names->count(sys_name);
    1351        5578 :       if (!use_current_system || system->hide_output())
    1352           0 :         continue;
    1353             : 
    1354         232 :       const unsigned int nv_sys = system->n_vars();
    1355         232 :       const auto & dof_map = system->get_dof_map();
    1356             : 
    1357        5578 :       system->update_global_solution (sys_soln, 0);
    1358             : 
    1359             :       // Keep track of the number of vars actually written.
    1360         232 :       unsigned int n_vars_written_current_system = 0;
    1361             : 
    1362        5810 :       if (_mesh.processor_id() == 0)
    1363             :         {
    1364         232 :           std::vector<Number>       soln_coeffs; // The finite element solution coeffs
    1365         232 :           std::vector<Number>       nodal_soln;  // The FE solution interpolated to the nodes
    1366         232 :           std::vector<dof_id_type>  dof_indices; // The DOF indices for the finite element
    1367             : 
    1368             :           // For each variable, determine if we are supposed to
    1369             :           // write it, then loop over the active elements, compute
    1370             :           // the nodal_soln and store it to the "soln" vector. We
    1371             :           // store zeros for subdomain-restricted variables on
    1372             :           // elements where they are not active.
    1373        2188 :           for (unsigned int var=0; var<nv_sys; var++)
    1374             :             {
    1375         264 :               bool use_current_var = (var_names == nullptr);
    1376        1190 :               if (!use_current_var)
    1377          12 :                 use_current_var = std::count(var_names->begin(),
    1378             :                                              var_names->end(),
    1379           1 :                                              system->variable_name(var));
    1380             : 
    1381             :               // If we aren't supposed to write this var, go to the
    1382             :               // next loop iteration.
    1383        1190 :               if (!use_current_var)
    1384           0 :                 continue;
    1385             : 
    1386         132 :               const FEType & fe_type = system->variable_type(var);
    1387         132 :               const Variable & var_description = system->variable(var);
    1388        1190 :               const auto vg = dof_map.var_group_from_var_number(var);
    1389        1190 :               const bool add_p_level = dof_map.should_p_refine(vg);
    1390             : 
    1391         132 :               unsigned int nn=0;
    1392             : 
    1393      231970 :               for (auto & elem : _mesh.active_element_ptr_range())
    1394             :                 {
    1395      136185 :                   if (var_description.active_on_subdomain(elem->subdomain_id()))
    1396             :                     {
    1397      136185 :                       system->get_dof_map().dof_indices (elem, dof_indices, var);
    1398             : 
    1399      157509 :                       soln_coeffs.resize(dof_indices.size());
    1400             : 
    1401      977562 :                       for (auto i : index_range(dof_indices))
    1402     1082113 :                         soln_coeffs[i] = sys_soln[dof_indices[i]];
    1403             : 
    1404             :                       // Compute the FE solution at all the nodes, but
    1405             :                       // only use the first n_vertices() entries if
    1406             :                       // vertices_only == true.
    1407      136185 :                       FEInterface::nodal_soln (elem->dim(),
    1408             :                                                fe_type,
    1409             :                                                elem,
    1410             :                                                soln_coeffs,
    1411             :                                                nodal_soln,
    1412             :                                                add_p_level,
    1413      136185 :                                                FEInterface::n_vec_dim(_mesh, fe_type));
    1414             : 
    1415             :                       // infinite elements should be skipped...
    1416       61626 :                       if (!elem->infinite())
    1417             :                         {
    1418       21324 :                           libmesh_assert_equal_to (nodal_soln.size(), elem->n_nodes());
    1419             : 
    1420             :                           const unsigned int n_vals =
    1421      136185 :                             vertices_only ? elem->n_vertices() : elem->n_nodes();
    1422             : 
    1423     1063989 :                           for (unsigned int n=0; n<n_vals; n++)
    1424             :                             {
    1425             :                               // Compute index into global solution vector.
    1426             :                               std::size_t index =
    1427      927804 :                                 nv * (nn++) + (n_vars_written_current_system + var_offset);
    1428             : 
    1429     1207460 :                               soln[index] += nodal_soln[n];
    1430             :                             }
    1431             :                         }
    1432             :                     }
    1433             :                   else
    1434           0 :                     nn += vertices_only ? elem->n_vertices() : elem->n_nodes();
    1435         926 :                 } // end loop over active elements writing interiors
    1436             : 
    1437             :               // Loop writing "fake" sides, if requested
    1438        1190 :               if (add_sides)
    1439             :                 {
    1440             :                   // We don't build discontinuous solution vectors in
    1441             :                   // parallel yet, but we'll do ordering of fake side
    1442             :                   // values as if we did, for consistency with the
    1443             :                   // parallel continuous ordering and for future
    1444             :                   // compatibility.
    1445             :                   std::vector<std::vector<const Elem *>>
    1446         375 :                     elems_by_pid(_mesh.n_processors());
    1447             : 
    1448        3655 :                   for (const auto & elem : _mesh.active_element_ptr_range())
    1449        2070 :                     elems_by_pid[elem->processor_id()].push_back(elem);
    1450             : 
    1451        2075 :                   for (auto p : index_range(elems_by_pid))
    1452        3455 :                     for (const Elem * elem : elems_by_pid[p])
    1453             :                       {
    1454        1680 :                         if (var_description.active_on_subdomain(elem->subdomain_id()))
    1455             :                           {
    1456        1680 :                             system->get_dof_map().dof_indices (elem, dof_indices, var);
    1457             : 
    1458        1820 :                             soln_coeffs.resize(dof_indices.size());
    1459             : 
    1460       32064 :                             for (auto i : index_range(dof_indices))
    1461       35448 :                               soln_coeffs[i] = sys_soln[dof_indices[i]];
    1462             : 
    1463        8880 :                             for (auto s : elem->side_index_range())
    1464             :                               {
    1465        7200 :                                 if (redundant_added_side(*elem,s))
    1466        1944 :                                   continue;
    1467             : 
    1468             :                                 const std::vector<unsigned int> side_nodes =
    1469        5694 :                                   elem->nodes_on_side(s);
    1470             : 
    1471             :                                 // Compute the FE solution at all the
    1472             :                                 // side nodes, but only use those for
    1473             :                                 // which is_vertex() == true if
    1474             :                                 // vertices_only == true.
    1475             :                                 FEInterface::side_nodal_soln
    1476        5256 :                                   (fe_type, elem, s, soln_coeffs,
    1477             :                                    nodal_soln, add_p_level,
    1478        5256 :                                    FEInterface::n_vec_dim(_mesh, fe_type));
    1479             : 
    1480         438 :                                 libmesh_assert_equal_to
    1481             :                                     (nodal_soln.size(),
    1482             :                                      side_nodes.size());
    1483             : 
    1484             :                                 // If we don't have a continuous FE
    1485             :                                 // then we want to average between
    1486             :                                 // sides, at least in the equal-level
    1487             :                                 // case where it's easy.  This is
    1488             :                                 // analogous to our repeat_count
    1489             :                                 // behavior elsewhere.
    1490             :                                 const FEContinuity cont =
    1491        5256 :                                   FEInterface::get_continuity(fe_type);
    1492         876 :                                 const Elem * const neigh = elem->neighbor_ptr(s);
    1493             : 
    1494        5256 :                                 if ((cont == DISCONTINUOUS || cont == H_CURL || cont == H_DIV) &&
    1495           0 :                                     neigh &&
    1496        5694 :                                     neigh->level() == elem->level() &&
    1497           0 :                                     var_description.active_on_subdomain(neigh->subdomain_id()))
    1498             :                                   {
    1499           0 :                                     std::vector<dof_id_type> neigh_indices;
    1500           0 :                                     system->get_dof_map().dof_indices (neigh, neigh_indices, var);
    1501           0 :                                     std::vector<Number> neigh_coeffs(neigh_indices.size());
    1502             : 
    1503           0 :                                     for (auto i : index_range(neigh_indices))
    1504           0 :                                       neigh_coeffs[i] = sys_soln[neigh_indices[i]];
    1505             : 
    1506             :                                     const unsigned int s_neigh =
    1507           0 :                                       neigh->which_neighbor_am_i(elem);
    1508           0 :                                     std::vector<Number> neigh_soln;
    1509             :                                     FEInterface::side_nodal_soln
    1510           0 :                                       (fe_type, neigh, s_neigh,
    1511             :                                        neigh_coeffs, neigh_soln, add_p_level,
    1512           0 :                                        FEInterface::n_vec_dim(_mesh, fe_type));
    1513             : 
    1514             :                                     const std::vector<unsigned int> neigh_nodes =
    1515           0 :                                       neigh->nodes_on_side(s_neigh);
    1516           0 :                                     for (auto n : index_range(side_nodes))
    1517           0 :                                       for (auto neigh_n : index_range(neigh_nodes))
    1518           0 :                                         if (neigh->node_ptr(neigh_nodes[neigh_n])
    1519           0 :                                             == elem->node_ptr(side_nodes[n]))
    1520             :                                           {
    1521           0 :                                             nodal_soln[n] += neigh_soln[neigh_n];
    1522           0 :                                             nodal_soln[n] /= 2;
    1523             :                                           }
    1524             :                                   }
    1525             : 
    1526       38736 :                                 for (auto n : index_range(side_nodes))
    1527             :                                   {
    1528       33480 :                                     if (vertices_only &&
    1529           0 :                                         !elem->is_vertex(n))
    1530           0 :                                       continue;
    1531             : 
    1532             :                                     // Compute index into global solution vector.
    1533             :                                     std::size_t index =
    1534       33480 :                                       nv * (nn++) + (n_vars_written_current_system + var_offset);
    1535             : 
    1536       36270 :                                     soln[index] += nodal_soln[n];
    1537             :                                   }
    1538             :                               }
    1539             :                           }
    1540             :                         else
    1541             :                           {
    1542           0 :                             nn += vertices_only ? elem->n_vertices() : elem->n_nodes();
    1543             : 
    1544           0 :                             for (auto s : elem->side_index_range())
    1545             :                               {
    1546           0 :                                 if (redundant_added_side(*elem,s))
    1547           0 :                                   continue;
    1548             : 
    1549             :                                 const std::vector<unsigned int> side_nodes =
    1550           0 :                                   elem->nodes_on_side(s);
    1551             : 
    1552           0 :                                 for (auto n : index_range(side_nodes))
    1553             :                                   {
    1554           0 :                                     if (vertices_only &&
    1555           0 :                                         !elem->is_vertex(n))
    1556           0 :                                       continue;
    1557           0 :                                     nn++;
    1558             :                                   }
    1559             :                               }
    1560             :                           }
    1561             :                       } // end loop over active elements, writing "fake" sides
    1562         250 :                 }
    1563             :               // If we made it here, we actually wrote a variable, so increment
    1564             :               // the number of variables actually written for the current system.
    1565        1190 :               n_vars_written_current_system++;
    1566             : 
    1567             :             } // end loop over vars
    1568             :         } // end if proc 0
    1569             : 
    1570             :       // Update offset for next loop iteration.
    1571        5578 :       var_offset += n_vars_written_current_system;
    1572             :     } // end loop over systems
    1573        5507 : }
    1574             : 
    1575             : 
    1576             : 
    1577      188928 : bool EquationSystems::redundant_added_side(const Elem & elem, unsigned int side)
    1578             : {
    1579       10536 :   libmesh_assert(elem.active());
    1580             : 
    1581       21072 :   const Elem * neigh = elem.neighbor_ptr(side);
    1582             : 
    1583             :   // Write boundary sides.
    1584      188928 :   if (!neigh)
    1585        4860 :     return false;
    1586             : 
    1587             :   // Write ghost sides in Nemesis
    1588      102432 :   if (neigh == remote_elem)
    1589           0 :     return false;
    1590             : 
    1591             :   // Don't write a coarser side if a finer side exists
    1592        5676 :   if (!neigh->active())
    1593           0 :     return true;
    1594             : 
    1595             :   // Don't write a side redundantly from both of the
    1596             :   // elements sharing it.  We'll disambiguate with id().
    1597      101376 :   return (neigh->id() < elem.id());
    1598             : }
    1599             : 
    1600             : 
    1601             : 
    1602           0 : bool EquationSystems::compare (const EquationSystems & other_es,
    1603             :                                const Real threshold,
    1604             :                                const bool verbose) const
    1605             : {
    1606             :   // safety check, whether we handle at least the same number
    1607             :   // of systems
    1608           0 :   std::vector<bool> os_result;
    1609             : 
    1610           0 :   if (this->n_systems() != other_es.n_systems())
    1611             :     {
    1612           0 :       if (verbose)
    1613             :         {
    1614           0 :           libMesh::out << "  Fatal difference. This system handles "
    1615           0 :                        << this->n_systems() << " systems," << std::endl
    1616           0 :                        << "  while the other system handles "
    1617           0 :                        << other_es.n_systems()
    1618           0 :                        << " systems." << std::endl
    1619           0 :                        << "  Aborting comparison." << std::endl;
    1620             :         }
    1621           0 :       return false;
    1622             :     }
    1623             :   else
    1624             :     {
    1625             :       // start comparing each system
    1626           0 :       for (const auto & [sys_name, sys_ptr] : _systems)
    1627             :         {
    1628             :           // get the other system
    1629           0 :           const System & other_system   = other_es.get_system (sys_name);
    1630             : 
    1631           0 :           os_result.push_back (sys_ptr->compare (other_system, threshold, verbose));
    1632             : 
    1633             :         }
    1634             : 
    1635             :     }
    1636             : 
    1637             : 
    1638             :   // sum up the results
    1639           0 :   if (os_result.size()==0)
    1640           0 :     return true;
    1641             :   else
    1642             :     {
    1643             :       bool os_identical;
    1644           0 :       unsigned int n = 0;
    1645           0 :       do
    1646             :         {
    1647           0 :           os_identical = os_result[n];
    1648           0 :           n++;
    1649             :         }
    1650           0 :       while (os_identical && n<os_result.size());
    1651           0 :       return os_identical;
    1652             :     }
    1653             : }
    1654             : 
    1655             : 
    1656             : 
    1657       15827 : std::string EquationSystems::get_info () const
    1658             : {
    1659       16747 :   std::ostringstream oss;
    1660             : 
    1661             :   oss << " EquationSystems\n"
    1662       30734 :       << "  n_systems()=" << this->n_systems() << '\n';
    1663             : 
    1664             :   // Print the info for the individual systems
    1665       33348 :   for (const auto & pr : _systems)
    1666       34534 :     oss << pr.second->get_info();
    1667             : 
    1668             : 
    1669             :   //   // Possibly print the parameters
    1670             :   //   if (!this->parameters.empty())
    1671             :   //     {
    1672             :   //       oss << "  n_parameters()=" << this->n_parameters() << '\n';
    1673             :   //       oss << "   Parameters:\n";
    1674             : 
    1675             :   //       for (const auto & [key, val] : _parameters)
    1676             :   //         oss << "    "
    1677             :   //             << "\""
    1678             :   //             << key
    1679             :   //             << "\""
    1680             :   //             << "="
    1681             :   //             << val
    1682             :   //             << '\n';
    1683             :   //     }
    1684             : 
    1685       16287 :   return oss.str();
    1686       14907 : }
    1687             : 
    1688             : 
    1689             : 
    1690       15827 : void EquationSystems::print_info (std::ostream & os) const
    1691             : {
    1692       16287 :   os << this->get_info()
    1693         460 :      << std::endl;
    1694       15827 : }
    1695             : 
    1696             : 
    1697             : 
    1698           0 : std::ostream & operator << (std::ostream & os,
    1699             :                             const EquationSystems & es)
    1700             : {
    1701           0 :   es.print_info(os);
    1702           0 :   return os;
    1703             : }
    1704             : 
    1705             : 
    1706             : 
    1707        2712 : unsigned int EquationSystems::n_vars () const
    1708             : {
    1709        2712 :   unsigned int tot=0;
    1710             : 
    1711        5536 :   for (const auto & pr : _systems)
    1712        2824 :     tot += pr.second->n_vars();
    1713             : 
    1714        2712 :   return tot;
    1715             : }
    1716             : 
    1717             : 
    1718             : 
    1719           0 : std::size_t EquationSystems::n_dofs () const
    1720             : {
    1721           0 :   std::size_t tot=0;
    1722             : 
    1723           0 :   for (const auto & pr : _systems)
    1724           0 :     tot += pr.second->n_dofs();
    1725             : 
    1726           0 :   return tot;
    1727             : }
    1728             : 
    1729             : 
    1730             : 
    1731             : 
    1732       22948 : std::size_t EquationSystems::n_active_dofs () const
    1733             : {
    1734         730 :   std::size_t tot=0;
    1735             : 
    1736       45896 :   for (const auto & pr : _systems)
    1737       22948 :     tot += pr.second->n_active_dofs();
    1738             : 
    1739       22948 :   return tot;
    1740             : }
    1741             : 
    1742             : 
    1743      237355 : void EquationSystems::_add_system_to_nodes_and_elems()
    1744             : {
    1745             :   // All the nodes
    1746    29515684 :   for (auto & node : _mesh.node_ptr_range())
    1747    15797789 :     node->add_system();
    1748             : 
    1749             :   // All the elements
    1750    13471802 :   for (auto & elem : _mesh.element_ptr_range())
    1751     7139297 :     elem->add_system();
    1752      237355 : }
    1753             : 
    1754          71 : void EquationSystems::_remove_default_ghosting(unsigned int sys_num)
    1755             : {
    1756          71 :   this->get_system(sys_num).get_dof_map().remove_default_ghosting();
    1757          71 : }
    1758             : 
    1759             : } // namespace libMesh

Generated by: LCOV version 1.14