LCOV - code coverage report
Current view: top level - src/systems - equation_systems.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4411 (aefcbc) with base 893689 Lines: 652 808 80.7 %
Date: 2026-07-27 16:32:15 Functions: 47 57 82.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14