LCOV - code coverage report
Current view: top level - src/base - dof_map.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4475 (55045b) with base a68cc6 Lines: 1086 1285 84.5 %
Date: 2026-06-03 14:29:06 Functions: 104 116 89.7 %
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             : 
      20             : // Local includes
      21             : #include "libmesh/dof_map.h"
      22             : 
      23             : // libMesh includes
      24             : #include "libmesh/coupling_matrix.h"
      25             : #include "libmesh/default_coupling.h"
      26             : #include "libmesh/dense_matrix.h"
      27             : #include "libmesh/dense_vector_base.h"
      28             : #include "libmesh/dirichlet_boundaries.h"
      29             : #include "libmesh/enum_to_string.h"
      30             : #include "libmesh/fe_type.h"
      31             : #include "libmesh/fe_base.h" // FEBase::build() for continuity test
      32             : #include "libmesh/ghosting_functor.h"
      33             : #include "libmesh/int_range.h"
      34             : #include "libmesh/mesh_base.h"
      35             : #include "libmesh/mesh_tools.h"
      36             : #include "libmesh/numeric_vector.h"
      37             : #include "libmesh/periodic_boundary_base.h"
      38             : #include "libmesh/periodic_boundaries.h"
      39             : #include "libmesh/sparse_matrix.h"
      40             : #include "libmesh/sparsity_pattern.h"
      41             : #include "libmesh/threads.h"
      42             : #include "libmesh/static_condensation_dof_map.h"
      43             : #include "libmesh/system.h"
      44             : #include "libmesh/parallel_fe_type.h"
      45             : 
      46             : // TIMPI includes
      47             : #include "timpi/parallel_implementation.h"
      48             : #include "timpi/parallel_sync.h"
      49             : 
      50             : // C++ Includes
      51             : #include <algorithm> // for std::fill, std::equal_range, std::max, std::lower_bound, etc.
      52             : #include <memory>
      53             : #include <set>
      54             : #include <sstream>
      55             : #include <unordered_map>
      56             : 
      57             : namespace libMesh
      58             : {
      59             : 
      60             : // ------------------------------------------------------------
      61             : // DofMap member functions
      62             : std::unique_ptr<SparsityPattern::Build>
      63       44290 : DofMap::build_sparsity (const MeshBase & mesh,
      64             :                         const bool calculate_constrained,
      65             :                         const bool use_condensed_system) const
      66             : {
      67        1370 :   libmesh_assert (mesh.is_prepared());
      68             : 
      69        2740 :   LOG_SCOPE("build_sparsity()", "DofMap");
      70             : 
      71             :   // Compute the sparsity structure of the global matrix.  This can be
      72             :   // fed into a PetscMatrixBase to allocate exactly the number of nonzeros
      73             :   // necessary to store the matrix.  This algorithm should be linear
      74             :   // in the (# of elements)*(# nodes per element)
      75             : 
      76             :   // We can be more efficient in the threaded sparsity pattern assembly
      77             :   // if we don't need the exact pattern.  For some sparse matrix formats
      78             :   // a good upper bound will suffice.
      79             : 
      80             :   // See if we need to include sparsity pattern entries for coupling
      81             :   // between neighbor dofs
      82       44290 :   bool implicit_neighbor_dofs = this->use_coupled_neighbor_dofs(mesh);
      83             : 
      84       42920 :   const StaticCondensationDofMap * sc = nullptr;
      85       44290 :   if (use_condensed_system)
      86             :     {
      87         118 :       libmesh_assert(this->has_static_condensation());
      88        4012 :       sc = _sc.get();
      89             :     }
      90             : 
      91             :   // We can compute the sparsity pattern in parallel on multiple
      92             :   // threads.  The goal is for each thread to compute the full sparsity
      93             :   // pattern for a subset of elements.  These sparsity patterns can
      94             :   // be efficiently merged in the SparsityPattern::Build::join()
      95             :   // method, especially if there is not too much overlap between them.
      96             :   // Even better, if the full sparsity pattern is not needed then
      97             :   // the number of nonzeros per row can be estimated from the
      98             :   // sparsity patterns created on each thread.
      99             :   auto sp = std::make_unique<SparsityPattern::Build>
     100             :     (*this,
     101       42920 :      this->_dof_coupling,
     102       44290 :      this->_coupling_functors,
     103             :      implicit_neighbor_dofs,
     104       42920 :      need_full_sparsity_pattern,
     105             :      calculate_constrained,
     106       44290 :      sc);
     107             : 
     108       88580 :   Threads::parallel_reduce (ConstElemRange (mesh.active_local_elements_begin(),
     109       88580 :                                             mesh.active_local_elements_end()), *sp);
     110             : 
     111       44290 :   sp->parallel_sync();
     112             : 
     113        1370 :   libmesh_assert_equal_to (sp->get_sparsity_pattern().size(), this->n_local_dofs());
     114             : 
     115             :   // Check to see if we have any extra stuff to add to the sparsity_pattern
     116       44290 :   if (_extra_sparsity_function)
     117             :     {
     118           0 :       if (_augment_sparsity_pattern)
     119             :         {
     120           0 :           libmesh_here();
     121           0 :           libMesh::out << "WARNING:  You have specified both an extra sparsity function and object.\n"
     122           0 :                        << "          Are you sure this is what you meant to do??"
     123           0 :                        << std::endl;
     124             :         }
     125             : 
     126           0 :       sp->apply_extra_sparsity_function(_extra_sparsity_function,
     127           0 :                                         _extra_sparsity_context);
     128             :     }
     129             : 
     130       44290 :   if (_augment_sparsity_pattern)
     131           0 :     sp->apply_extra_sparsity_object(*_augment_sparsity_pattern);
     132             : 
     133       45660 :   return sp;
     134           0 : }
     135             : 
     136             : 
     137             : 
     138      242602 : DofMap::DofMap(const unsigned int number,
     139      242602 :                MeshBase & mesh) :
     140             :   DofMapBase (mesh.comm()),
     141      228826 :   _dof_coupling(nullptr),
     142      228826 :   _error_on_constraint_loop(false),
     143      228826 :   _constrained_sparsity_construction(false),
     144      228826 :   _variables(),
     145      228826 :   _variable_groups(),
     146             :   _variable_group_numbers(),
     147      228826 :   _sys_number(number),
     148      228826 :   _mesh(mesh),
     149             :   _matrices(),
     150             :   _first_scalar_df(),
     151             :   _send_list(),
     152      228826 :   _augment_sparsity_pattern(nullptr),
     153      228826 :   _extra_sparsity_function(nullptr),
     154      228826 :   _extra_sparsity_context(nullptr),
     155      228826 :   _augment_send_list(nullptr),
     156      228826 :   _extra_send_list_function(nullptr),
     157      228826 :   _extra_send_list_context(nullptr),
     158      228826 :   _default_coupling(std::make_unique<DefaultCoupling>()),
     159      228826 :   _default_evaluating(std::make_unique<DefaultCoupling>()),
     160      228826 :   need_full_sparsity_pattern(false),
     161      228826 :   _n_SCALAR_dofs(0)
     162             : #ifdef LIBMESH_ENABLE_AMR
     163             :   , _first_old_scalar_df()
     164             : #endif
     165             : #ifdef LIBMESH_ENABLE_CONSTRAINTS
     166             :   , _dof_constraints()
     167             :   , _stashed_dof_constraints()
     168             :   , _primal_constraint_values()
     169             :   , _adjoint_constraint_values()
     170             : #endif
     171             : #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
     172             :   , _node_constraints()
     173             : #endif
     174             : #ifdef LIBMESH_ENABLE_PERIODIC
     175      228826 :   , _periodic_boundaries(std::make_unique<PeriodicBoundaries>())
     176             : #endif
     177             : #ifdef LIBMESH_ENABLE_DIRICHLET
     178      228826 :   , _dirichlet_boundaries(std::make_unique<DirichletBoundaries>())
     179      228826 :   , _adjoint_dirichlet_boundaries()
     180             : #endif
     181      228826 :   , _implicit_neighbor_dofs_initialized(false),
     182      228826 :   _implicit_neighbor_dofs(false),
     183      228826 :   _verify_dirichlet_bc_consistency(true),
     184      970408 :   _sc(nullptr)
     185             : {
     186        6888 :   _matrices.clear();
     187             : 
     188      242602 :   _default_coupling->set_mesh(&_mesh);
     189      242602 :   _default_evaluating->set_mesh(&_mesh);
     190        6888 :   _default_evaluating->set_n_levels(1);
     191             : 
     192             : #ifdef LIBMESH_ENABLE_PERIODIC
     193      249490 :   _default_coupling->set_periodic_boundaries(_periodic_boundaries.get());
     194      249490 :   _default_evaluating->set_periodic_boundaries(_periodic_boundaries.get());
     195             : #endif
     196             : 
     197      242602 :   this->add_coupling_functor(*_default_coupling);
     198      242602 :   this->add_algebraic_ghosting_functor(*_default_evaluating);
     199      242602 : }
     200             : 
     201             : 
     202             : 
     203             : // Destructor
     204      498980 : DofMap::~DofMap()
     205             : {
     206      242602 :   this->clear();
     207             : 
     208             :   // clear() resets all but the default DofMap-based functors.  We
     209             :   // need to remove those from the mesh too before we die.
     210      249490 :   _mesh.remove_ghosting_functor(*_default_coupling);
     211      249490 :   _mesh.remove_ghosting_functor(*_default_evaluating);
     212     1400508 : }
     213             : 
     214             : 
     215             : #ifdef LIBMESH_ENABLE_PERIODIC
     216             : 
     217           0 : bool DofMap::is_periodic_boundary (const boundary_id_type boundaryid) const
     218             : {
     219           0 :   if (_periodic_boundaries->count(boundaryid) != 0)
     220           0 :     return true;
     221             : 
     222           0 :   return false;
     223             : }
     224             : 
     225             : #endif
     226             : 
     227           0 : void DofMap::set_error_on_cyclic_constraint(bool error_on_cyclic_constraint)
     228             : {
     229             :   // This function will eventually be officially libmesh_deprecated();
     230             :   // Call DofMap::set_error_on_constraint_loop() instead.
     231           0 :   set_error_on_constraint_loop(error_on_cyclic_constraint);
     232           0 : }
     233             : 
     234          71 : void DofMap::set_error_on_constraint_loop(bool error_on_constraint_loop)
     235             : {
     236          71 :   _error_on_constraint_loop = error_on_constraint_loop;
     237          71 : }
     238             : 
     239             : 
     240       22708 : void DofMap::attach_matrix (SparseMatrix<Number> & matrix)
     241             : {
     242         632 :   parallel_object_only();
     243             : 
     244             :   // We shouldn't be trying to re-attach the same matrices repeatedly
     245         632 :   libmesh_assert (std::find(_matrices.begin(), _matrices.end(),
     246             :                             &matrix) == _matrices.end());
     247             : 
     248       22708 :   _matrices.push_back(&matrix);
     249             : 
     250       22708 :   this->update_sparsity_pattern(matrix);
     251             : 
     252       22708 :   if (matrix.need_full_sparsity_pattern())
     253           0 :     need_full_sparsity_pattern = true;
     254       22708 : }
     255             : 
     256             : 
     257             : 
     258       23008 : bool DofMap::computed_sparsity_already() const
     259             : {
     260       23060 :   bool computed_sparsity_already = _sp &&
     261          56 :     (!_sp->get_n_nz().empty() ||
     262       23008 :      !_sp->get_n_oz().empty());
     263       23008 :   this->comm().max(computed_sparsity_already);
     264       23008 :   return computed_sparsity_already;
     265             : }
     266             : 
     267             : 
     268             : 
     269       22708 : void DofMap::update_sparsity_pattern(SparseMatrix<Number> & matrix) const
     270             : {
     271       22708 :   matrix.attach_dof_map (*this);
     272             : 
     273             :   // If we've already computed sparsity, then it's too late
     274             :   // to wait for "compute_sparsity" to help with sparse matrix
     275             :   // initialization, and we need to handle this matrix individually
     276       22708 :   if (this->computed_sparsity_already())
     277             :     {
     278          52 :       libmesh_assert(_sp.get());
     279             : 
     280        1844 :       if (matrix.need_full_sparsity_pattern())
     281             :         {
     282             :           // We'd better have already computed the full sparsity
     283             :           // pattern if we need it here
     284           0 :           libmesh_assert(need_full_sparsity_pattern);
     285             : 
     286           0 :           matrix.update_sparsity_pattern (_sp->get_sparsity_pattern());
     287             :         }
     288             : 
     289        1844 :       matrix.attach_sparsity_pattern(*_sp);
     290             :     }
     291       22708 : }
     292             : 
     293             : 
     294             : 
     295       20870 : bool DofMap::is_attached (SparseMatrix<Number> & matrix)
     296             : {
     297       20870 :   return (std::find(_matrices.begin(), _matrices.end(),
     298       21450 :                     &matrix) != _matrices.end());
     299             : }
     300             : 
     301             : 
     302             : 
     303    67415554 : DofObject * DofMap::node_ptr(MeshBase & mesh, dof_id_type i) const
     304             : {
     305    67415554 :   return mesh.node_ptr(i);
     306             : }
     307             : 
     308             : 
     309             : 
     310    38707776 : DofObject * DofMap::elem_ptr(MeshBase & mesh, dof_id_type i) const
     311             : {
     312    38707776 :   return mesh.elem_ptr(i);
     313             : }
     314             : 
     315             : 
     316             : 
     317             : template <typename iterator_type>
     318      534730 : void DofMap::set_nonlocal_dof_objects(iterator_type objects_begin,
     319             :                                       iterator_type objects_end,
     320             :                                       MeshBase & mesh,
     321             :                                       dofobject_accessor objects)
     322             : {
     323             :   // This function must be run on all processors at once
     324       15820 :   parallel_object_only();
     325             : 
     326             :   // First, iterate over local objects to find out how many
     327             :   // are on each processor
     328       31640 :   std::unordered_map<processor_id_type, dof_id_type> ghost_objects_from_proc;
     329             : 
     330       31640 :   iterator_type it  = objects_begin;
     331             : 
     332   100845588 :   for (; it != objects_end; ++it)
     333             :     {
     334    53061665 :       DofObject * obj = *it;
     335             : 
     336     2906346 :       if (obj)
     337             :         {
     338    53061665 :           processor_id_type obj_procid = obj->processor_id();
     339             :           // We'd better be completely partitioned by now
     340     2906346 :           libmesh_assert_not_equal_to (obj_procid, DofObject::invalid_processor_id);
     341    53061665 :           ghost_objects_from_proc[obj_procid]++;
     342             :         }
     343             :     }
     344             : 
     345             :   // Request sets to send to each processor
     346             :   std::map<processor_id_type, std::vector<dof_id_type>>
     347       31640 :     requested_ids;
     348             : 
     349             :   // We know how many of our objects live on each processor, so
     350             :   // reserve() space for requests from each.
     351     1960699 :   for (auto [p, size] : ghost_objects_from_proc)
     352             :     {
     353     1452625 :       if (p != this->processor_id())
     354     1175255 :         requested_ids[p].reserve(size);
     355             :     }
     356             : 
     357   100845588 :   for (it = objects_begin; it != objects_end; ++it)
     358             :     {
     359    53061665 :       DofObject * obj = *it;
     360    53061665 :       if (obj->processor_id() != DofObject::invalid_processor_id)
     361    53061665 :         requested_ids[obj->processor_id()].push_back(obj->id());
     362             :     }
     363             : #ifdef DEBUG
     364       47460 :   for (auto p : make_range(this->n_processors()))
     365             :     {
     366       31640 :       if (ghost_objects_from_proc.count(p))
     367       26656 :         libmesh_assert_equal_to (requested_ids[p].size(), ghost_objects_from_proc[p]);
     368             :       else
     369        4984 :         libmesh_assert(!requested_ids.count(p));
     370             :     }
     371             : #endif
     372             : 
     373             :   typedef std::vector<dof_id_type> datum;
     374             : 
     375     9333076 :   auto gather_functor =
     376     1372657 :     [this, &mesh, &objects]
     377             :     (processor_id_type,
     378             :      const std::vector<dof_id_type> & ids,
     379       53312 :      std::vector<datum> & data)
     380             :     {
     381             :       // Fill those requests
     382             :       const unsigned int
     383       53312 :         sys_num      = this->sys_number(),
     384       26656 :         n_var_groups = this->n_variable_groups();
     385             : 
     386       53312 :       const std::size_t query_size = ids.size();
     387             : 
     388     1425969 :       data.resize(query_size);
     389    54487634 :       for (auto & d : data)
     390    53061665 :         d.resize(2 * n_var_groups);
     391             : 
     392    54487634 :       for (std::size_t i=0; i != query_size; ++i)
     393             :         {
     394    53061665 :           DofObject * requested = (this->*objects)(mesh, ids[i]);
     395     2906346 :           libmesh_assert(requested);
     396     2906346 :           libmesh_assert_equal_to (requested->processor_id(), this->processor_id());
     397     2906346 :           libmesh_assert_equal_to (requested->n_var_groups(sys_num), n_var_groups);
     398   108361627 :           for (unsigned int vg=0; vg != n_var_groups; ++vg)
     399             :             {
     400             :               unsigned int n_comp_g =
     401     3440868 :                 requested->n_comp_group(sys_num, vg);
     402    58740610 :               data[i][vg] = n_comp_g;
     403    55299962 :               dof_id_type my_first_dof = n_comp_g ?
     404     1367178 :                 requested->vg_dof_base(sys_num, vg) : 0;
     405     3440868 :               libmesh_assert_not_equal_to (my_first_dof, DofObject::invalid_id);
     406    58740610 :               data[i][n_var_groups+vg] = my_first_dof;
     407             :             }
     408             :         }
     409             :     };
     410             : 
     411     9333076 :   auto action_functor =
     412     1372657 :     [this, &mesh, &objects]
     413             :     (processor_id_type libmesh_dbg_var(pid),
     414             :      const std::vector<dof_id_type> & ids,
     415       53312 :      const std::vector<datum> & data)
     416             :     {
     417             :       const unsigned int
     418       53312 :         sys_num      = this->sys_number(),
     419       26656 :         n_var_groups = this->n_variable_groups();
     420             : 
     421             :       // Copy the id changes we've now been informed of
     422    54487634 :       for (auto i : index_range(ids))
     423             :         {
     424    53061665 :           DofObject * requested = (this->*objects)(mesh, ids[i]);
     425     2906346 :           libmesh_assert(requested);
     426     2906346 :           libmesh_assert_equal_to (requested->processor_id(), pid);
     427   108361627 :           for (unsigned int vg=0; vg != n_var_groups; ++vg)
     428             :             {
     429             :               unsigned int n_comp_g =
     430    58740610 :                 cast_int<unsigned int>(data[i][vg]);
     431    55299962 :               requested->set_n_comp_group(sys_num, vg, n_comp_g);
     432    55299962 :               if (n_comp_g)
     433             :                 {
     434    25334989 :                   dof_id_type my_first_dof = data[i][n_var_groups+vg];
     435     1367178 :                   libmesh_assert_not_equal_to (my_first_dof, DofObject::invalid_id);
     436             :                   requested->set_vg_dof_base
     437     1367178 :                     (sys_num, vg, my_first_dof);
     438             :                 }
     439             :             }
     440             :         }
     441             :     };
     442             : 
     443       15820 :   datum * ex = nullptr;
     444             :   Parallel::pull_parallel_vector_data
     445      534730 :     (this->comm(), requested_ids, gather_functor, action_functor, ex);
     446             : 
     447             : #ifdef DEBUG
     448             :   // Double check for invalid dofs
     449     2922166 :   for (it = objects_begin; it != objects_end; ++it)
     450             :     {
     451     2906346 :       DofObject * obj = *it;
     452     2906346 :       libmesh_assert (obj);
     453     2906346 :       unsigned int num_variables = obj->n_vars(this->sys_number());
     454     9768858 :       for (unsigned int v=0; v != num_variables; ++v)
     455             :         {
     456             :           unsigned int n_comp =
     457     6862512 :             obj->n_comp(this->sys_number(), v);
     458     6862512 :           dof_id_type my_first_dof = n_comp ?
     459     2710780 :             obj->dof_number(this->sys_number(), v, 0) : 0;
     460     6862512 :           libmesh_assert_not_equal_to (my_first_dof, DofObject::invalid_id);
     461             :         }
     462             :     }
     463             : #endif
     464      534730 : }
     465             : 
     466             : 
     467             : 
     468      274933 : void DofMap::reinit
     469             :   (MeshBase & mesh,
     470             :    const std::map<const Node *, std::set<subdomain_id_type>> &
     471             :      constraining_subdomains)
     472             : {
     473        7912 :   libmesh_assert (mesh.is_prepared());
     474             : 
     475       15824 :   LOG_SCOPE("reinit()", "DofMap");
     476             : 
     477             :   // This is the common case and we want to optimize for it
     478             :   const bool constraining_subdomains_empty =
     479        7912 :     constraining_subdomains.empty();
     480             : 
     481             :   // We ought to reconfigure our default coupling functor.
     482             :   //
     483             :   // The user might have removed it from our coupling functors set,
     484             :   // but if so, who cares, this reconfiguration is cheap.
     485             : 
     486             :   // Avoid calling set_dof_coupling() with an empty/non-nullptr
     487             :   // _dof_coupling matrix which may happen when there are actually no
     488             :   // variables on the system.
     489      274933 :   if (this->_dof_coupling && this->_dof_coupling->empty() && !this->n_variables())
     490           0 :     this->_dof_coupling = nullptr;
     491      274933 :   _default_coupling->set_dof_coupling(this->_dof_coupling);
     492             : 
     493             :   // By default we may want 0 or 1 levels of coupling
     494             :   unsigned int standard_n_levels =
     495      274933 :     this->use_coupled_neighbor_dofs(mesh);
     496             :   _default_coupling->set_n_levels
     497      391487 :     (std::max(_default_coupling->n_levels(), standard_n_levels));
     498             : 
     499             :   // But we *don't* want to restrict to a CouplingMatrix unless the
     500             :   // user does so manually; the original libMesh behavior was to put
     501             :   // ghost indices on the send_list regardless of variable.
     502             :   //_default_evaluating->set_dof_coupling(this->_dof_coupling);
     503             : 
     504             :   const unsigned int
     505       15824 :     sys_num      = this->sys_number(),
     506        7912 :     n_var_groups = this->n_variable_groups();
     507             : 
     508             :   // The DofObjects need to know how many variable groups we have, and
     509             :   // how many variables there are in each group.
     510      282845 :   std::vector<unsigned int> n_vars_per_group; /**/ n_vars_per_group.reserve (n_var_groups);
     511             : 
     512      559452 :   for (unsigned int vg=0; vg<n_var_groups; vg++)
     513      284519 :     n_vars_per_group.push_back (this->variable_group(vg).n_variables());
     514             : 
     515             : #ifdef LIBMESH_ENABLE_AMR
     516             : 
     517             :   //------------------------------------------------------------
     518             :   // Clear the old_dof_objects for all the nodes
     519             :   // and elements so that we can overwrite them
     520    67619996 :   for (auto & node : mesh.node_ptr_range())
     521             :     {
     522    35411135 :       node->clear_old_dof_object();
     523     1872176 :       libmesh_assert (!node->get_old_dof_object());
     524      259109 :     }
     525             : 
     526             :   Threads::parallel_for
     527      274933 :     (mesh.element_stored_range(),
     528      276917 :      [](const ElemRange & range)
     529             :      {
     530    19650077 :        for (Elem * elem : range)
     531             :          {
     532    19370270 :            elem->clear_old_dof_object();
     533     1034252 :            libmesh_assert (!elem->get_old_dof_object());
     534             :          }
     535      276917 :      });
     536             : 
     537             :   //------------------------------------------------------------
     538             :   // Set the old_dof_objects for the elements that
     539             :   // weren't just created, if these old dof objects
     540             :   // had variables
     541    38918926 :   for (auto & elem : mesh.element_ptr_range())
     542             :     {
     543             :       // Skip the elements that were just refined
     544    20222690 :       if (elem->refinement_flag() == Elem::JUST_REFINED)
     545     1665249 :         continue;
     546             : 
     547   131309406 :       for (Node & node : elem->node_ref_range())
     548   112897065 :         if (node.get_old_dof_object() == nullptr)
     549    79960538 :           if (node.has_dofs(sys_num))
     550    11479013 :             node.set_old_dof_object();
     551             : 
     552      889152 :       libmesh_assert (!elem->get_old_dof_object());
     553             : 
     554    18412341 :       if (elem->has_dofs(sys_num))
     555     9236297 :         elem->set_old_dof_object();
     556      259109 :     }
     557             : 
     558             : #endif // #ifdef LIBMESH_ENABLE_AMR
     559             : 
     560             : 
     561             :   //------------------------------------------------------------
     562             :   // Then set the number of variables for each \p DofObject
     563             :   // equal to n_variables() for this system.  This will
     564             :   // handle new \p DofObjects that may have just been created
     565             : 
     566             :   // All the nodes
     567    67619996 :   for (auto & node : mesh.node_ptr_range())
     568    35670244 :     node->set_n_vars_per_group(sys_num, n_vars_per_group);
     569             : 
     570             :   // All the elements
     571             :   Threads::parallel_for
     572      274935 :     (mesh.element_stored_range(),
     573     2345344 :      [sys_num, n_vars_per_group](const ElemRange & range)
     574             :      {
     575    20512541 :        for (Elem * elem : range)
     576    20222690 :          elem->set_n_vars_per_group(sys_num, n_vars_per_group);
     577      276917 :      });
     578             : 
     579             :   // Zero _n_SCALAR_dofs, it will be updated below.
     580      274933 :   this->_n_SCALAR_dofs = 0;
     581             : 
     582             :   //------------------------------------------------------------
     583             :   // Next allocate space for the DOF indices
     584      559429 :   for (unsigned int vg=0; vg<n_var_groups; vg++)
     585             :     {
     586        8182 :       const VariableGroup & vg_description = this->variable_group(vg);
     587             : 
     588        8182 :       const unsigned int n_var_in_group = vg_description.n_variables();
     589        8182 :       const FEType & base_fe_type        = vg_description.type();
     590             : 
     591      284519 :       const bool add_p_level = base_fe_type.p_refinement;
     592             : 
     593             :       // Don't need to loop over elements for a SCALAR variable
     594             :       // Just increment _n_SCALAR_dofs
     595      284519 :       if (base_fe_type.family == SCALAR)
     596             :         {
     597        1272 :           this->_n_SCALAR_dofs += base_fe_type.order.get_order()*n_var_in_group;
     598        1272 :           continue;
     599             :         }
     600             : 
     601             :       // This should be constant even on p-refined elements
     602             :       const bool extra_hanging_dofs =
     603      283247 :         FEInterface::extra_hanging_dofs(base_fe_type);
     604             : 
     605             :       // For all the active elements, count vertex degrees of freedom.
     606    29279460 :       for (auto & elem : mesh.active_element_ptr_range())
     607             :         {
     608      867668 :           libmesh_assert(elem);
     609             : 
     610             :           // Only number dofs connected to active elements on this
     611             :           // processor and only for variables which are active on on
     612             :           // this element's subdomain or which are active on the
     613             :           // subdomain of a node constrained by this node.
     614             :           const bool active_on_elem =
     615    15228210 :             vg_description.active_on_subdomain(elem->subdomain_id());
     616             : 
     617             :           // If there's no way we're active on this element then we're
     618             :           // done
     619    15228210 :           if (!active_on_elem && constraining_subdomains_empty)
     620       36505 :             continue;
     621             : 
     622    15191705 :           FEType fe_type = base_fe_type;
     623             : 
     624    15191705 :           const ElemType type = elem->type();
     625             : 
     626    15191829 :           libmesh_error_msg_if(base_fe_type.order.get_order() >
     627             :                                int(FEInterface::max_order(base_fe_type,type)),
     628             :                                "ERROR: Finite element "
     629             :                                << Utility::enum_to_string(base_fe_type.family)
     630             :                                << " on geometric element "
     631             :                                << Utility::enum_to_string(type)
     632             :                                << "\nonly supports FEInterface::max_order = "
     633             :                                << FEInterface::max_order(base_fe_type,type)
     634             :                                << ", not fe_type.order = "
     635             :                                << base_fe_type.order);
     636             : 
     637             : #ifdef LIBMESH_ENABLE_AMR
     638             :           // Make sure we haven't done more p refinement than we can
     639             :           // handle
     640    15191682 :           if (base_fe_type.order + add_p_level*elem->p_level() >
     641             :               FEInterface::max_order(base_fe_type, type))
     642             :             {
     643             : #  ifdef DEBUG
     644           0 :               libMesh::err << "WARNING: Finite element "
     645           0 :                            << Utility::enum_to_string(base_fe_type.family)
     646           0 :                            << " on geometric element "
     647           0 :                            << Utility::enum_to_string(type) << std::endl
     648           0 :                            << "could not be p refined past FEInterface::max_order = "
     649           0 :                            << FEInterface::max_order(base_fe_type,type)
     650           0 :                            << std::endl;
     651             : #  endif
     652           0 :               elem->set_p_level(int(FEInterface::max_order(base_fe_type,type))
     653           0 :                                 - int(base_fe_type.order));
     654             :             }
     655             : #endif
     656             : 
     657             :           // Allocate the vertex DOFs
     658   121365876 :           for (auto n : elem->node_index_range())
     659             :             {
     660   105308632 :               Node & node = elem->node_ref(n);
     661             : 
     662             :               // If we're active on the element then we're active on
     663             :               // its nodes.  If we're not then we might *still* be
     664             :               // active on particular constraining nodes.
     665     6464454 :               bool active_on_node = active_on_elem;
     666   105308632 :               if (!active_on_node)
     667           0 :                 if (auto it = constraining_subdomains.find(&node);
     668           0 :                     it != constraining_subdomains.end())
     669           0 :                   for (auto s : it->second)
     670           0 :                     if (vg_description.active_on_subdomain(s))
     671             :                       {
     672           0 :                         active_on_node = true;
     673           0 :                         break;
     674             :                       }
     675             : 
     676    12928616 :               if (!active_on_node)
     677           0 :                 continue;
     678             : 
     679   105308632 :               if (elem->is_vertex(n))
     680             :                 {
     681             :                   const unsigned int old_node_dofs =
     682    61492365 :                     node.n_comp_group(sys_num, vg);
     683             : 
     684             :                   const unsigned int vertex_dofs =
     685    61492384 :                     std::max(FEInterface::n_dofs_at_node(fe_type, elem, n, add_p_level),
     686    61492365 :                              old_node_dofs);
     687             : 
     688             :                   // Some discontinuous FEs have no vertex dofs
     689    61492365 :                   if (vertex_dofs > old_node_dofs)
     690             :                     {
     691     9847980 :                       node.set_n_comp_group(sys_num, vg,
     692             :                                             vertex_dofs);
     693             : 
     694             :                       // Abusing dof_number to set a "this is a
     695             :                       // vertex" flag
     696     9055950 :                       node.set_vg_dof_base(sys_num, vg,
     697             :                                            vertex_dofs);
     698             : 
     699             :                       // libMesh::out << "sys_num,vg,old_node_dofs,vertex_dofs="
     700             :                       //       << sys_num << ","
     701             :                       //       << vg << ","
     702             :                       //       << old_node_dofs << ","
     703             :                       //       << vertex_dofs << '\n',
     704             :                       // node.debug_buffer();
     705             : 
     706             :                       // libmesh_assert_equal_to (vertex_dofs, node.n_comp(sys_num, vg));
     707             :                       // libmesh_assert_equal_to (vertex_dofs, node.vg_dof_base(sys_num, vg));
     708             :                     }
     709             :                 }
     710             :             }
     711      266955 :         } // done counting vertex dofs
     712             : 
     713             :       // count edge & face dofs next
     714    29279410 :       for (auto & elem : mesh.active_element_ptr_range())
     715             :         {
     716      867666 :           libmesh_assert(elem);
     717             : 
     718             :           // Only number dofs connected to active elements on this
     719             :           // processor and only for variables which are active on on
     720             :           // this element's subdomain or which are active on the
     721             :           // subdomain of a node constrained by this node.
     722             :           const bool active_on_elem =
     723    15228187 :             vg_description.active_on_subdomain(elem->subdomain_id());
     724             : 
     725             :           // If there's no way we're active on this element then we're
     726             :           // done
     727    15228187 :           if (!active_on_elem && constraining_subdomains_empty)
     728       34465 :             continue;
     729             : 
     730             :           // Allocate the edge and face DOFs
     731   120500314 :           for (auto n : elem->node_index_range())
     732             :             {
     733   105308632 :               Node & node = elem->node_ref(n);
     734             : 
     735             :               // If we're active on the element then we're active on
     736             :               // its nodes.  If we're not then we might *still* be
     737             :               // active on particular constraining nodes.
     738     6464454 :               bool active_on_node = active_on_elem;
     739   105308632 :               if (!active_on_node)
     740           0 :                 if (auto it = constraining_subdomains.find(&node);
     741           0 :                     it != constraining_subdomains.end())
     742           0 :                   for (auto s : it->second)
     743           0 :                     if (vg_description.active_on_subdomain(s))
     744             :                       {
     745           0 :                         active_on_node = true;
     746           0 :                         break;
     747             :                       }
     748             : 
     749    12928616 :               if (!active_on_node)
     750           0 :                 continue;
     751             : 
     752             :               const unsigned int old_node_dofs =
     753     6464454 :                 node.n_comp_group(sys_num, vg);
     754             : 
     755   102348146 :               const unsigned int vertex_dofs = old_node_dofs?
     756     6464454 :                 cast_int<unsigned int>(node.vg_dof_base (sys_num,vg)):0;
     757             : 
     758             :               const unsigned int new_node_dofs =
     759   105308632 :                 FEInterface::n_dofs_at_node(base_fe_type, elem, n, add_p_level);
     760             : 
     761             :               // We've already allocated vertex DOFs
     762   105308632 :               if (elem->is_vertex(n))
     763             :                 {
     764     3705266 :                   libmesh_assert_greater_equal (old_node_dofs, vertex_dofs);
     765             :                   // //if (vertex_dofs < new_node_dofs)
     766             :                   //   libMesh::out << "sys_num,vg,old_node_dofs,vertex_dofs,new_node_dofs="
     767             :                   //                << sys_num << ","
     768             :                   //                << vg << ","
     769             :                   //                << old_node_dofs << ","
     770             :                   //                << vertex_dofs << ","
     771             :                   //                << new_node_dofs << '\n',
     772             :                   //     node.debug_buffer();
     773             : 
     774     3705266 :                   libmesh_assert_greater_equal (vertex_dofs,   new_node_dofs);
     775             :                 }
     776             :               // We need to allocate the rest
     777             :               else
     778             :                 {
     779             :                   // If this has no dofs yet, it needs no vertex
     780             :                   // dofs, so we just give it edge or face dofs
     781    43816267 :                   if (!old_node_dofs)
     782             :                     {
     783    36028627 :                       node.set_n_comp_group(sys_num, vg,
     784             :                                             new_node_dofs);
     785             :                       // Abusing dof_number to set a "this has no
     786             :                       // vertex dofs" flag
     787    36028627 :                       if (new_node_dofs)
     788      565360 :                         node.set_vg_dof_base(sys_num, vg, 0);
     789             :                     }
     790             : 
     791             :                   // If this has dofs, but has no vertex dofs,
     792             :                   // it may still need more edge or face dofs if
     793             :                   // we're p-refined.
     794     7787640 :                   else if (vertex_dofs == 0)
     795             :                     {
     796     7402702 :                       if (new_node_dofs > old_node_dofs)
     797             :                         {
     798         224 :                           node.set_n_comp_group(sys_num, vg,
     799             :                                                 new_node_dofs);
     800             : 
     801          18 :                           node.set_vg_dof_base(sys_num, vg,
     802             :                                                vertex_dofs);
     803             :                         }
     804             :                     }
     805             :                   // If this is another element's vertex,
     806             :                   // add more (non-overlapping) edge/face dofs if
     807             :                   // necessary
     808      384938 :                   else if (extra_hanging_dofs)
     809             :                     {
     810      340159 :                       if (new_node_dofs > old_node_dofs - vertex_dofs)
     811             :                         {
     812      331144 :                           node.set_n_comp_group(sys_num, vg,
     813             :                                                 vertex_dofs + new_node_dofs);
     814             : 
     815      321045 :                           node.set_vg_dof_base(sys_num, vg,
     816             :                                                vertex_dofs);
     817             :                         }
     818             :                     }
     819             :                   // If this is another element's vertex, add any
     820             :                   // (overlapping) edge/face dofs if necessary
     821             :                   else
     822             :                     {
     823        5138 :                       libmesh_assert_greater_equal (old_node_dofs, vertex_dofs);
     824       44779 :                       if (new_node_dofs > old_node_dofs)
     825             :                         {
     826           0 :                           node.set_n_comp_group(sys_num, vg,
     827             :                                                 new_node_dofs);
     828             : 
     829           0 :                           node.set_vg_dof_base (sys_num, vg,
     830             :                                                 vertex_dofs);
     831             :                         }
     832             :                     }
     833             :                 }
     834             :             }
     835             :           // Allocate the element DOFs
     836             :           const unsigned int dofs_per_elem =
     837    15191682 :             FEInterface::n_dofs_per_elem(base_fe_type, elem, add_p_level);
     838             : 
     839    15191682 :           elem->set_n_comp_group(sys_num, vg, dofs_per_elem);
     840             : 
     841      266936 :         }
     842             :     } // end loop over variable groups
     843             : 
     844             :   // Calling DofMap::reinit() by itself makes little sense,
     845             :   // so we won't bother with nonlocal DofObjects.
     846             :   // Those will be fixed by distribute_dofs
     847             : 
     848             :   //------------------------------------------------------------
     849             :   // Finally, clear all the current DOF indices
     850             :   // (distribute_dofs expects them cleared!)
     851      274910 :   this->invalidate_dofs(mesh);
     852      534019 : }
     853             : 
     854             : 
     855             : 
     856      549820 : void DofMap::invalidate_dofs(MeshBase & mesh) const
     857             : {
     858       31640 :   const unsigned int sys_num = this->sys_number();
     859             : 
     860             :   // All the nodes
     861   135238320 :   for (auto & node : mesh.node_ptr_range())
     862    71339558 :     node->invalidate_dofs(sys_num);
     863             : 
     864             :   // All the active elements.
     865    58661364 :   for (auto & elem : mesh.active_element_ptr_range())
     866    30904744 :     elem->invalidate_dofs(sys_num);
     867      549820 : }
     868             : 
     869             : 
     870             : 
     871      243521 : void DofMap::clear()
     872             : {
     873      243521 :   DofMapBase::clear();
     874             : 
     875             :   // we don't want to clear
     876             :   // the coupling matrix!
     877             :   // It should not change...
     878             :   //_dof_coupling->clear();
     879             :   //
     880             :   // But it would be inconsistent to leave our coupling settings
     881             :   // through a clear()...
     882      243521 :   _dof_coupling = nullptr;
     883             : 
     884             :   // Reset ghosting functor statuses
     885             :   {
     886      487181 :     for (const auto & gf : _coupling_functors)
     887             :       {
     888        6920 :         libmesh_assert(gf);
     889      243660 :         _mesh.remove_ghosting_functor(*gf);
     890             :       }
     891        6916 :     this->_coupling_functors.clear();
     892             : 
     893             :     // Go back to default coupling
     894             : 
     895      243521 :     _default_coupling->set_dof_coupling(this->_dof_coupling);
     896      243521 :     _default_coupling->set_n_levels(this->use_coupled_neighbor_dofs(this->_mesh));
     897             : 
     898      243521 :     this->add_coupling_functor(*_default_coupling);
     899             :   }
     900             : 
     901             : 
     902             :   {
     903      487465 :     for (const auto & gf : _algebraic_ghosting_functors)
     904             :       {
     905        6928 :         libmesh_assert(gf);
     906      243944 :         _mesh.remove_ghosting_functor(*gf);
     907             :       }
     908        6916 :     this->_algebraic_ghosting_functors.clear();
     909             : 
     910             :     // Go back to default send_list generation
     911             : 
     912             :     // _default_evaluating->set_dof_coupling(this->_dof_coupling);
     913        6916 :     _default_evaluating->set_n_levels(1);
     914      243521 :     this->add_algebraic_ghosting_functor(*_default_evaluating);
     915             :   }
     916             : 
     917        6916 :   this->_shared_functors.clear();
     918             : 
     919      236605 :   _variables.clear();
     920      236605 :   _variable_groups.clear();
     921        6916 :   _var_to_vg.clear();
     922        6916 :   _variable_group_numbers.clear();
     923        6916 :   _array_variables.clear();
     924        6916 :   _first_scalar_df.clear();
     925        6916 :   this->clear_send_list();
     926      243521 :   this->clear_sparsity();
     927      243521 :   need_full_sparsity_pattern = false;
     928             : 
     929             : #ifdef LIBMESH_ENABLE_AMR
     930             : 
     931        6916 :   _dof_constraints.clear();
     932        6916 :   _stashed_dof_constraints.clear();
     933        6916 :   _primal_constraint_values.clear();
     934        6916 :   _adjoint_constraint_values.clear();
     935      243521 :   _n_old_dfs = 0;
     936        6916 :   _first_old_df.clear();
     937        6916 :   _end_old_df.clear();
     938        6916 :   _first_old_scalar_df.clear();
     939             : 
     940             : #endif
     941             : 
     942        6916 :   _matrices.clear();
     943      243521 :   if (_sc)
     944         560 :     _sc->clear();
     945      243521 : }
     946             : 
     947             : 
     948             : 
     949      274933 : std::size_t DofMap::distribute_dofs (MeshBase & mesh)
     950             : {
     951             :   // This function must be run on all processors at once
     952        7912 :   parallel_object_only();
     953             : 
     954             :   // Log how long it takes to distribute the degrees of freedom
     955       15824 :   LOG_SCOPE("distribute_dofs()", "DofMap");
     956             : 
     957        7912 :   libmesh_assert (mesh.is_prepared());
     958             : 
     959       15824 :   const processor_id_type proc_id = this->processor_id();
     960             : #ifndef NDEBUG
     961        7912 :   const processor_id_type n_proc  = this->n_processors();
     962             : #endif
     963             : 
     964             :   //  libmesh_assert_greater (this->n_variables(), 0);
     965        7912 :   libmesh_assert_less (proc_id, n_proc);
     966             : 
     967             :   // Data structure to ensure we can correctly combine
     968             :   // subdomain-restricted variables with constraining nodes from
     969             :   // different subdomains
     970             :   const std::map<const Node *, std::set<subdomain_id_type>>
     971             :     constraining_subdomains =
     972      274935 :     this->calculate_constraining_subdomains();
     973             : 
     974             :   // re-init in case the mesh has changed
     975      274933 :   this->reinit(mesh,
     976             :                constraining_subdomains);
     977             : 
     978             :   // By default distribute variables in a
     979             :   // var-major fashion, but allow run-time
     980             :   // specification
     981      274910 :   bool node_major_dofs = libMesh::on_command_line ("--node-major-dofs");
     982             : 
     983             :   // The DOF counter, will be incremented as we encounter
     984             :   // new degrees of freedom
     985      274910 :   dof_id_type next_free_dof = 0;
     986             : 
     987             :   // Clear the send list before we rebuild it
     988        7910 :   this->clear_send_list();
     989             : 
     990             :   // Set temporary DOF indices on this processor
     991      274910 :   if (node_major_dofs)
     992             :     this->distribute_local_dofs_node_major
     993         840 :       (next_free_dof, mesh, constraining_subdomains);
     994             :   else
     995             :     this->distribute_local_dofs_var_major
     996      274070 :       (next_free_dof, mesh, constraining_subdomains);
     997             : 
     998             :   // Get DOF counts on all processors
     999      274910 :   const auto n_dofs = this->compute_dof_info(next_free_dof);
    1000             : 
    1001             :   // Clear all the current DOF indices
    1002             :   // (distribute_dofs expects them cleared!)
    1003      274910 :   this->invalidate_dofs(mesh);
    1004             : 
    1005      274910 :   next_free_dof = _first_df[proc_id];
    1006             : 
    1007             :   // Set permanent DOF indices on this processor
    1008      274910 :   if (node_major_dofs)
    1009             :     this->distribute_local_dofs_node_major
    1010         840 :       (next_free_dof, mesh, constraining_subdomains);
    1011             :   else
    1012             :     this->distribute_local_dofs_var_major
    1013      274070 :       (next_free_dof, mesh, constraining_subdomains);
    1014             : 
    1015        7910 :   libmesh_assert_equal_to (next_free_dof, _end_df[proc_id]);
    1016             : 
    1017             :   //------------------------------------------------------------
    1018             :   // At this point, all n_comp and dof_number values on local
    1019             :   // DofObjects should be correct, but a DistributedMesh might have
    1020             :   // incorrect values on non-local DofObjects.  Let's request the
    1021             :   // correct values from each other processor.
    1022             : 
    1023      282820 :   if (this->n_processors() > 1)
    1024             :     {
    1025      526820 :       this->set_nonlocal_dof_objects(mesh.nodes_begin(),
    1026      275275 :                                      mesh.nodes_end(),
    1027             :                                      mesh, &DofMap::node_ptr);
    1028             : 
    1029      526820 :       this->set_nonlocal_dof_objects(mesh.elements_begin(),
    1030      534751 :                                      mesh.elements_end(),
    1031             :                                      mesh, &DofMap::elem_ptr);
    1032             :     }
    1033             : 
    1034             : #ifdef DEBUG
    1035             :   {
    1036             :     const unsigned int
    1037        7910 :       sys_num = this->sys_number();
    1038             : 
    1039             :     // Processors should all agree on DoF ids for the newly numbered
    1040             :     // system.
    1041        7910 :     MeshTools::libmesh_assert_valid_dof_ids(mesh, sys_num);
    1042             : 
    1043             :     // DoF processor ids should match DofObject processor ids
    1044     1880036 :     for (auto & node : mesh.node_ptr_range())
    1045             :       {
    1046     1872126 :         DofObject const * const dofobj = node;
    1047     1872126 :         const processor_id_type obj_proc_id = dofobj->processor_id();
    1048             : 
    1049     6535182 :         for (auto v : make_range(dofobj->n_vars(sys_num)))
    1050     7469902 :           for (auto c : make_range(dofobj->n_comp(sys_num,v)))
    1051             :             {
    1052     2806846 :               const dof_id_type dofid = dofobj->dof_number(sys_num,v,c);
    1053     2806846 :               libmesh_assert_greater_equal (dofid, this->first_dof(obj_proc_id));
    1054     2806846 :               libmesh_assert_less (dofid, this->end_dof(obj_proc_id));
    1055             :             }
    1056             :       }
    1057             : 
    1058     1042130 :     for (auto & elem : mesh.element_ptr_range())
    1059             :       {
    1060     1034220 :         DofObject const * const dofobj = elem;
    1061     1034220 :         const processor_id_type obj_proc_id = dofobj->processor_id();
    1062             : 
    1063     3233676 :         for (auto v : make_range(dofobj->n_vars(sys_num)))
    1064     3432220 :           for (auto c : make_range(dofobj->n_comp(sys_num,v)))
    1065             :             {
    1066     1232764 :               const dof_id_type dofid = dofobj->dof_number(sys_num,v,c);
    1067     1232764 :               libmesh_assert_greater_equal (dofid, this->first_dof(obj_proc_id));
    1068     1232764 :               libmesh_assert_less (dofid, this->end_dof(obj_proc_id));
    1069             :             }
    1070             :       }
    1071             :   }
    1072             : #endif
    1073             : 
    1074             :   // start finding SCALAR degrees of freedom
    1075             : #ifdef LIBMESH_ENABLE_AMR
    1076      274910 :   _first_old_scalar_df = _first_scalar_df;
    1077             : #endif
    1078        7910 :   _first_scalar_df.clear();
    1079      274910 :   _first_scalar_df.resize(this->n_variables(), DofObject::invalid_id);
    1080      274910 :   dof_id_type current_SCALAR_dof_index = n_dofs - n_SCALAR_dofs();
    1081             : 
    1082             :   // Calculate and cache the initial DoF indices for SCALAR variables.
    1083             :   // This is an O(N_vars) calculation so we want to do it once per
    1084             :   // renumbering rather than once per SCALAR_dof_indices() call
    1085             : 
    1086     7950989 :   for (auto v : make_range(this->n_variables()))
    1087     7409079 :     if (this->variable(v).type().family == SCALAR)
    1088             :       {
    1089        1272 :         _first_scalar_df[v] = current_SCALAR_dof_index;
    1090        1272 :         current_SCALAR_dof_index += this->variable(v).type().order.get_order();
    1091             :       }
    1092             : 
    1093             :   // Allow our GhostingFunctor objects to reinit if necessary
    1094      551737 :   for (const auto & gf : _algebraic_ghosting_functors)
    1095             :     {
    1096        7964 :       libmesh_assert(gf);
    1097      276827 :       gf->dofmap_reinit();
    1098             :     }
    1099             : 
    1100      549820 :   for (const auto & gf : _coupling_functors)
    1101             :     {
    1102        7910 :       libmesh_assert(gf);
    1103      274910 :       gf->dofmap_reinit();
    1104             :     }
    1105             : 
    1106             :   // Note that in the add_neighbors_to_send_list nodes on processor
    1107             :   // boundaries that are shared by multiple elements are added for
    1108             :   // each element.
    1109      274910 :   this->add_neighbors_to_send_list(mesh);
    1110             : 
    1111             :   // Here we used to clean up that data structure; now System and
    1112             :   // EquationSystems call that for us, after we've added constraint
    1113             :   // dependencies to the send_list too.
    1114             :   // this->sort_send_list ();
    1115             : 
    1116      282820 :   return n_dofs;
    1117             : }
    1118             : 
    1119             : 
    1120             : template <typename T, std::enable_if_t<std::is_same_v<T, dof_id_type> ||
    1121             :                                        std::is_same_v<T, std::vector<dof_id_type>>, int>>
    1122        2348 : void DofMap::local_variable_indices(T & idx,
    1123             :                                     const MeshBase & mesh,
    1124             :                                     unsigned int var_num) const
    1125             : {
    1126             :   // Only used if T == dof_id_type to keep track of the greatest dof we've seen
    1127          44 :   dof_id_type greatest = 0;
    1128             : 
    1129             :   if constexpr (std::is_same_v<T, dof_id_type>)
    1130         946 :     idx = 0;
    1131             :   else if constexpr (std::is_same_v<T, std::vector<dof_id_type>>)
    1132          40 :     idx.clear();
    1133             : 
    1134             :   // Count dofs in the *exact* order that distribute_dofs numbered
    1135             :   // them, so that we can assume ascending indices and use push_back
    1136             :   // instead of find+insert.
    1137             : 
    1138          88 :   const unsigned int sys_num = this->sys_number();
    1139             : 
    1140             :   // If this isn't a SCALAR variable, we need to find all its field
    1141             :   // dofs on the mesh
    1142        2348 :   if (this->variable_type(var_num).family != SCALAR)
    1143             :     {
    1144        2348 :       const Variable & var(this->variable(var_num));
    1145             : 
    1146      262330 :       for (auto & elem : mesh.active_local_element_ptr_range())
    1147             :         {
    1148      132285 :           if (!var.active_on_subdomain(elem->subdomain_id()))
    1149         176 :             continue;
    1150             : 
    1151             :           // Only count dofs connected to active
    1152             :           // elements on this processor.
    1153      132093 :           const unsigned int n_nodes = elem->n_nodes();
    1154             : 
    1155             :           // First get any new nodal DOFS
    1156     1483365 :           for (unsigned int n=0; n<n_nodes; n++)
    1157             :             {
    1158     1351272 :               const Node & node = elem->node_ref(n);
    1159             : 
    1160     1392184 :               if (node.processor_id() != this->processor_id())
    1161      103469 :                 continue;
    1162             : 
    1163     1247007 :               const unsigned int n_comp = node.n_comp(sys_num, var_num);
    1164     1933831 :               for(unsigned int i=0; i<n_comp; i++)
    1165             :                 {
    1166      686824 :                   const dof_id_type index = node.dof_number(sys_num,var_num,i);
    1167       32636 :                   libmesh_assert (this->local_index(index));
    1168             : 
    1169             :                   if constexpr (std::is_same_v<T, dof_id_type>)
    1170             :                     {
    1171      344228 :                       if (idx == 0 || index > greatest)
    1172      143178 :                         { idx++; greatest = index; }
    1173             :                     }
    1174             :                   else if constexpr (std::is_same_v<T, std::vector<dof_id_type>>)
    1175             :                     {
    1176      342596 :                       if (idx.empty() || index > idx.back())
    1177      159448 :                         idx.push_back(index);
    1178             :                     }
    1179             :                 }
    1180             :             }
    1181             : 
    1182             :           // Next get any new element DOFS
    1183      132093 :           const unsigned int n_comp = elem->n_comp(sys_num, var_num);
    1184      132093 :           for (unsigned int i=0; i<n_comp; i++)
    1185             :             {
    1186           0 :               const dof_id_type index = elem->dof_number(sys_num,var_num,i);
    1187             : 
    1188             :               if constexpr (std::is_same_v<T, dof_id_type>)
    1189             :                 {
    1190           0 :                   if (idx == 0 || index > greatest)
    1191           0 :                     { idx++; greatest = index; }
    1192             :                 }
    1193             :               else if constexpr (std::is_same_v<T, std::vector<dof_id_type>>)
    1194             :                 {
    1195           0 :                   if (idx.empty() || index > idx.back())
    1196           0 :                     idx.push_back(index);
    1197             :                 }
    1198             :             }
    1199             :         } // done looping over elements
    1200             : 
    1201             : 
    1202             :       // we may have missed assigning DOFs to nodes that we own
    1203             :       // but to which we have no connected elements matching our
    1204             :       // variable restriction criterion.  this will happen, for example,
    1205             :       // if variable V is restricted to subdomain S.  We may not own
    1206             :       // any elements which live in S, but we may own nodes which are
    1207             :       // *connected* to elements which do.  in this scenario these nodes
    1208             :       // will presently have unnumbered DOFs. we need to take care of
    1209             :       // them here since we own them and no other processor will touch them.
    1210     1471850 :       for (const auto & node : mesh.local_node_ptr_range())
    1211             :         {
    1212       43286 :           libmesh_assert(node);
    1213             : 
    1214      775755 :           const unsigned int n_comp = node->n_comp(sys_num, var_num);
    1215     1078419 :           for (unsigned int i=0; i<n_comp; i++)
    1216             :             {
    1217      302664 :               const dof_id_type index = node->dof_number(sys_num,var_num,i);
    1218             : 
    1219             :               if constexpr (std::is_same_v<T, dof_id_type>)
    1220             :                 {
    1221      143178 :                   if (idx == 0 || index > greatest)
    1222           0 :                     { idx++; greatest = index; }
    1223             :                 }
    1224             :               else if constexpr (std::is_same_v<T, std::vector<dof_id_type>>)
    1225             :                 {
    1226      159486 :                   if (idx.empty() || index > idx.back())
    1227          38 :                     idx.push_back(index);
    1228             :                 }
    1229             :             }
    1230             :         }
    1231             :     }
    1232             :   // Otherwise, count up the SCALAR dofs, if we're on the processor
    1233             :   // that holds this SCALAR variable
    1234           0 :   else if (this->processor_id() == (this->n_processors()-1))
    1235             :     {
    1236           0 :       std::vector<dof_id_type> di_scalar;
    1237           0 :       this->SCALAR_dof_indices(di_scalar,var_num);
    1238             : 
    1239             :       if constexpr (std::is_same_v<T, dof_id_type>)
    1240           0 :         idx += std::distance(di_scalar.begin(), di_scalar.end());
    1241             :       else if constexpr (std::is_same_v<T, std::vector<dof_id_type>>)
    1242           0 :         idx.insert(idx.end(), di_scalar.begin(), di_scalar.end());
    1243             :     }
    1244        2348 : }
    1245             : 
    1246             : template void DofMap::local_variable_indices(dof_id_type &,
    1247             :                                              const MeshBase &,
    1248             :                                              unsigned int) const;
    1249             : 
    1250             : template void DofMap::local_variable_indices(std::vector<dof_id_type> &,
    1251             :                                              const MeshBase &,
    1252             :                                              unsigned int) const;
    1253             : 
    1254             : 
    1255             : std::map<const Node *, std::set<subdomain_id_type>>
    1256      274933 : DofMap::calculate_constraining_subdomains()
    1257             : {
    1258        7912 :   std::map<const Node *, std::set<subdomain_id_type>> constraining_subdomains;
    1259      274933 :   const auto & constraint_rows = _mesh.get_constraint_rows();
    1260             : 
    1261             :   // We can't just loop over constraint rows here because we need
    1262             :   // element subdomain ids for the constrained nodes, but we don't
    1263             :   // want an extra loop if there are no constraint rows.
    1264      274933 :   if (!constraint_rows.empty())
    1265      260646 :     for (auto & elem : _mesh.active_element_ptr_range())
    1266             :       {
    1267      139599 :         const subdomain_id_type sbdid = elem->subdomain_id();
    1268             : 
    1269      678586 :         for (const Node & node : elem->node_ref_range())
    1270             :           {
    1271      538987 :             if (auto it = constraint_rows.find(&node);
    1272       44334 :                 it != constraint_rows.end())
    1273             :               {
    1274     2758107 :                 for (const auto & [pr, val] : it->second)
    1275             :                   {
    1276             :                     const Node * spline_node =
    1277     2297551 :                       pr.first->node_ptr(pr.second);
    1278             : 
    1279     2297551 :                     constraining_subdomains[spline_node].insert(sbdid);
    1280             :                   }
    1281             :               }
    1282             :           }
    1283         947 :       }
    1284             : 
    1285      274933 :   return constraining_subdomains;
    1286             : }
    1287             : 
    1288             : 
    1289        1680 : void DofMap::distribute_local_dofs_node_major
    1290             :   (dof_id_type & next_free_dof,
    1291             :    MeshBase & mesh,
    1292             :    const std::map<const Node *, std::set<subdomain_id_type>> &
    1293             :      constraining_subdomains)
    1294             : {
    1295          96 :   const unsigned int sys_num       = this->sys_number();
    1296          48 :   const unsigned int n_var_groups  = this->n_variable_groups();
    1297             : 
    1298             :   // This is the common case and we want to optimize for it
    1299             :   const bool constraining_subdomains_empty =
    1300          48 :     constraining_subdomains.empty();
    1301             : 
    1302             :   // Our numbering here must be kept consistent with the numbering
    1303             :   // scheme assumed by DofMap::local_variable_indices!
    1304             : 
    1305             :   //-------------------------------------------------------------------------
    1306             :   // First count and assign temporary numbers to local dofs
    1307      128592 :   for (auto & elem : mesh.active_local_element_ptr_range())
    1308             :     {
    1309             :       // Only number dofs connected to active
    1310             :       // elements on this processor.
    1311       68904 :       const unsigned int n_nodes = elem->n_nodes();
    1312             : 
    1313       68904 :       const subdomain_id_type sbdid = elem->subdomain_id();
    1314             : 
    1315             :       // First number the nodal DOFS
    1316      689040 :       for (unsigned int n=0; n<n_nodes; n++)
    1317             :         {
    1318      112752 :           Node & node = elem->node_ref(n);
    1319             : 
    1320     1860408 :           for (unsigned vg=0; vg<n_var_groups; vg++)
    1321             :             {
    1322      112752 :               const VariableGroup & vg_description(this->variable_group(vg));
    1323             : 
    1324     1240272 :               if (vg_description.type().family == SCALAR)
    1325           0 :                 continue;
    1326             : 
    1327             :               bool active_on_node =
    1328     1127520 :                 vg_description.active_on_subdomain(sbdid);
    1329             : 
    1330             :               // Are we at least active indirectly here?
    1331     1240272 :               if (!active_on_node && !constraining_subdomains_empty)
    1332           0 :                 if (auto it = constraining_subdomains.find(&node);
    1333           0 :                     it != constraining_subdomains.end())
    1334           0 :                   for (auto s : it->second)
    1335           0 :                     if (vg_description.active_on_subdomain(s))
    1336             :                       {
    1337           0 :                         active_on_node = true;
    1338           0 :                         break;
    1339             :                       }
    1340             : 
    1341     1240272 :               if (active_on_node)
    1342             :                 {
    1343             :                   // assign dof numbers (all at once) if this is
    1344             :                   // our node and if they aren't already there
    1345      927072 :                   if ((node.n_comp_group(sys_num,vg) > 0) &&
    1346     1319890 :                       (node.processor_id() == this->processor_id()) &&
    1347       79618 :                       (node.vg_dof_base(sys_num,vg) ==
    1348             :                        DofObject::invalid_id))
    1349             :                     {
    1350      368016 :                       node.set_vg_dof_base(sys_num, vg,
    1351             :                                            next_free_dof);
    1352      368016 :                       next_free_dof += (vg_description.n_variables()*
    1353       33456 :                                         node.n_comp_group(sys_num,vg));
    1354             :                       //node.debug_buffer();
    1355             :                     }
    1356             :                 }
    1357             :             }
    1358             :         }
    1359             : 
    1360             :       // Now number the element DOFS
    1361      206712 :       for (unsigned vg=0; vg<n_var_groups; vg++)
    1362             :         {
    1363       12528 :           const VariableGroup & vg_description(this->variable_group(vg));
    1364             : 
    1365      150336 :           if ((vg_description.type().family != SCALAR) &&
    1366      137808 :               (vg_description.active_on_subdomain(elem->subdomain_id())))
    1367      137808 :             if (elem->n_comp_group(sys_num,vg) > 0)
    1368             :               {
    1369           0 :                 libmesh_assert_equal_to (elem->vg_dof_base(sys_num,vg),
    1370             :                                          DofObject::invalid_id);
    1371             : 
    1372           0 :                 elem->set_vg_dof_base(sys_num,
    1373             :                                       vg,
    1374             :                                       next_free_dof);
    1375             : 
    1376           0 :                 next_free_dof += (vg_description.n_variables()*
    1377           0 :                                   elem->n_comp_group(sys_num,vg));
    1378             :               }
    1379             :         }
    1380        1584 :     } // done looping over elements
    1381             : 
    1382             : 
    1383             :   // we may have missed assigning DOFs to nodes that we own
    1384             :   // but to which we have no connected elements matching our
    1385             :   // variable restriction criterion.  this will happen, for example,
    1386             :   // if variable V is restricted to subdomain S.  We may not own
    1387             :   // any elements which live in S, but we may own nodes which are
    1388             :   // *connected* to elements which do.  in this scenario these nodes
    1389             :   // will presently have unnumbered DOFs. we need to take care of
    1390             :   // them here since we own them and no other processor will touch them.
    1391      995472 :   for (auto & node : mesh.local_node_ptr_range())
    1392     1637064 :     for (unsigned vg=0; vg<n_var_groups; vg++)
    1393             :       {
    1394       99216 :         const VariableGroup & vg_description(this->variable_group(vg));
    1395             : 
    1396     1190592 :         if (node->n_comp_group(sys_num,vg))
    1397      368016 :           if (node->vg_dof_base(sys_num,vg) == DofObject::invalid_id)
    1398             :             {
    1399           0 :               node->set_vg_dof_base (sys_num,
    1400             :                                      vg,
    1401             :                                      next_free_dof);
    1402             : 
    1403           0 :               next_free_dof += (vg_description.n_variables()*
    1404           0 :                                 node->n_comp(sys_num,vg));
    1405             :             }
    1406        1584 :       }
    1407             : 
    1408        1680 :   this->distribute_scalar_dofs(next_free_dof);
    1409             : 
    1410             : #ifdef DEBUG
    1411          48 :   this->assert_no_nodes_missed(mesh);
    1412             : #endif // DEBUG
    1413        1680 : }
    1414             : 
    1415             : 
    1416             : 
    1417      548140 : void DofMap::distribute_local_dofs_var_major
    1418             :   (dof_id_type & next_free_dof,
    1419             :    MeshBase & mesh,
    1420             :    const std::map<const Node *, std::set<subdomain_id_type>> &
    1421             :      constraining_subdomains)
    1422             : {
    1423       31544 :   const unsigned int sys_num      = this->sys_number();
    1424       15772 :   const unsigned int n_var_groups = this->n_variable_groups();
    1425             : 
    1426             :   // This is the common case and we want to optimize for it
    1427             :   const bool constraining_subdomains_empty =
    1428       15772 :     constraining_subdomains.empty();
    1429             : 
    1430             :   // Our numbering here must be kept consistent with the numbering
    1431             :   // scheme assumed by DofMap::local_variable_indices!
    1432             : 
    1433             :   //-------------------------------------------------------------------------
    1434             :   // First count and assign temporary numbers to local dofs
    1435     1113772 :   for (unsigned vg=0; vg<n_var_groups; vg++)
    1436             :     {
    1437       16264 :       const VariableGroup & vg_description(this->variable_group(vg));
    1438             : 
    1439       16264 :       const unsigned int n_vars_in_group = vg_description.n_variables();
    1440             : 
    1441             :       // Skip the SCALAR dofs
    1442      565632 :       if (vg_description.type().family == SCALAR)
    1443        2472 :         continue;
    1444             : 
    1445    18977180 :       for (auto & elem : mesh.active_local_element_ptr_range())
    1446             :         {
    1447             :           // Only number dofs connected to active elements on this
    1448             :           // processor and only for variables which are active on on
    1449             :           // this element's subdomain or which are active on the
    1450             :           // subdomain of a node constrained by this node.
    1451             :           const bool active_on_elem =
    1452     9791410 :             vg_description.active_on_subdomain(elem->subdomain_id());
    1453             : 
    1454             :           // If there's no way we're active on this element then we're
    1455             :           // done
    1456     9791410 :           if (!active_on_elem && constraining_subdomains_empty)
    1457       22308 :             continue;
    1458             : 
    1459     9767062 :           const unsigned int n_nodes = elem->n_nodes();
    1460             : 
    1461             :           // First number the nodal DOFS
    1462    87943202 :           for (unsigned int n=0; n<n_nodes; n++)
    1463             :             {
    1464    78176140 :               Node & node = elem->node_ref(n);
    1465             : 
    1466     6375966 :               bool active_on_node = active_on_elem;
    1467    78176140 :               if (!active_on_node)
    1468           0 :                 if (auto it = constraining_subdomains.find(&node);
    1469           0 :                     it != constraining_subdomains.end())
    1470           0 :                   for (auto s : it->second)
    1471           0 :                     if (vg_description.active_on_subdomain(s))
    1472             :                       {
    1473           0 :                         active_on_node = true;
    1474           0 :                         break;
    1475             :                       }
    1476             : 
    1477    12751640 :               if (!active_on_node)
    1478           0 :                 continue;
    1479             : 
    1480             :               // assign dof numbers (all at once) if this is
    1481             :               // our node and if they aren't already there
    1482    42346638 :               if ((node.n_comp_group(sys_num,vg) > 0) &&
    1483    81539602 :                   (node.processor_id() == this->processor_id()) &&
    1484     3363462 :                   (node.vg_dof_base(sys_num,vg) ==
    1485             :                    DofObject::invalid_id))
    1486             :                 {
    1487    12881560 :                   node.set_vg_dof_base(sys_num, vg, next_free_dof);
    1488             : 
    1489    12881560 :                   next_free_dof += (n_vars_in_group*
    1490     1112744 :                                     node.n_comp_group(sys_num,vg));
    1491             :                 }
    1492             :             }
    1493             : 
    1494             :           // Now number the element DOFS
    1495    10622802 :           if (elem->n_comp_group(sys_num,vg) > 0)
    1496             :             {
    1497      226730 :               libmesh_assert_equal_to (elem->vg_dof_base(sys_num,vg),
    1498             :                                        DofObject::invalid_id);
    1499             : 
    1500     2648388 :               elem->set_vg_dof_base(sys_num,
    1501             :                                     vg,
    1502             :                                     next_free_dof);
    1503             : 
    1504     2648388 :               next_free_dof += (n_vars_in_group*
    1505      226730 :                                 elem->n_comp_group(sys_num,vg));
    1506             :             }
    1507      530704 :         } // end loop on elements
    1508             : 
    1509             :       // we may have missed assigning DOFs to nodes that we own
    1510             :       // but to which we have no connected elements matching our
    1511             :       // variable restriction criterion.  this will happen, for example,
    1512             :       // if variable V is restricted to subdomain S.  We may not own
    1513             :       // any elements which live in S, but we may own nodes which are
    1514             :       // *connected* to elements which do.  in this scenario these nodes
    1515             :       // will presently have unnumbered DOFs. we need to take care of
    1516             :       // them here since we own them and no other processor will touch them.
    1517    48859212 :       for (auto & node : mesh.local_node_ptr_range())
    1518    28113380 :         if (node->n_comp_group(sys_num,vg))
    1519    12890180 :           if (node->vg_dof_base(sys_num,vg) == DofObject::invalid_id)
    1520             :             {
    1521        8620 :               node->set_vg_dof_base (sys_num,
    1522             :                                      vg,
    1523             :                                      next_free_dof);
    1524             : 
    1525        8620 :               next_free_dof += (n_vars_in_group*
    1526         682 :                                 node->n_comp_group(sys_num,vg));
    1527      530704 :             }
    1528             :     } // end loop on variable groups
    1529             : 
    1530      548140 :   this->distribute_scalar_dofs(next_free_dof);
    1531             : 
    1532             : #ifdef DEBUG
    1533       15772 :   this->assert_no_nodes_missed(mesh);
    1534             : #endif
    1535      548140 : }
    1536             : 
    1537             : 
    1538             : 
    1539      549820 : void DofMap::distribute_scalar_dofs(dof_id_type & next_free_dof)
    1540             : {
    1541      549820 :   this->_n_SCALAR_dofs = 0;
    1542     1118812 :   for (auto vg : make_range(this->n_variable_groups()))
    1543             :     {
    1544       16360 :       const VariableGroup & vg_description(this->variable_group(vg));
    1545             : 
    1546      568992 :       if (vg_description.type().family == SCALAR)
    1547             :         {
    1548        2544 :           this->_n_SCALAR_dofs += (vg_description.n_variables()*
    1549        2544 :                                    vg_description.type().order.get_order());
    1550        2544 :           continue;
    1551             :         }
    1552             :     }
    1553             : 
    1554             :   // Only increment next_free_dof if we're on the processor
    1555             :   // that holds this SCALAR variable
    1556      565640 :   if (this->processor_id() == (this->n_processors()-1))
    1557       92874 :     next_free_dof += _n_SCALAR_dofs;
    1558      549820 : }
    1559             : 
    1560             : 
    1561             : 
    1562             : #ifdef DEBUG
    1563       15820 : void DofMap::assert_no_nodes_missed(MeshBase & mesh)
    1564             : {
    1565       15820 :   MeshTools::libmesh_assert_valid_procids<Node>(mesh);
    1566             : 
    1567     1898546 :   for (auto & node : mesh.local_node_ptr_range())
    1568             :     {
    1569     1882726 :       unsigned int n_var_g = node->n_var_groups(this->sys_number());
    1570     4224934 :       for (unsigned int vg=0; vg != n_var_g; ++vg)
    1571             :         {
    1572             :           unsigned int n_comp_g =
    1573     2342208 :             node->n_comp_group(this->sys_number(), vg);
    1574     2342208 :           dof_id_type my_first_dof = n_comp_g ?
    1575     1146882 :             node->vg_dof_base(this->sys_number(), vg) : 0;
    1576     2342208 :           libmesh_assert_not_equal_to (my_first_dof, DofObject::invalid_id);
    1577             :         }
    1578             :     }
    1579       15820 : }
    1580             : #endif // DEBUG
    1581             : 
    1582             : 
    1583             : void
    1584     4097098 : DofMap::
    1585             : merge_ghost_functor_outputs(GhostingFunctor::map_type & elements_to_ghost,
    1586             :                             CouplingMatricesSet & temporary_coupling_matrices,
    1587             :                             const GhostingFunctorIterator & gf_begin,
    1588             :                             const GhostingFunctorIterator & gf_end,
    1589             :                             const MeshBase::const_element_iterator & elems_begin,
    1590             :                             const MeshBase::const_element_iterator & elems_end,
    1591             :                             processor_id_type p)
    1592             : {
    1593     8298104 :   for (const auto & gf : as_range(gf_begin, gf_end))
    1594             :     {
    1595      691834 :       GhostingFunctor::map_type more_elements_to_ghost;
    1596             : 
    1597      345917 :       libmesh_assert(gf);
    1598     4201006 :       (*gf)(elems_begin, elems_end, p, more_elements_to_ghost);
    1599             : 
    1600             :       // A GhostingFunctor should only return active elements, but
    1601             :       // I forgot to *document* that, so let's go as easy as we
    1602             :       // can on functors that return inactive elements.
    1603             : #if defined(LIBMESH_ENABLE_DEPRECATED) && defined(LIBMESH_ENABLE_AMR)
    1604      691834 :       std::vector<std::pair<const Elem*, const CouplingMatrix*>> children_to_couple;
    1605     4559324 :       for (auto it = more_elements_to_ghost.begin();
    1606    12395294 :            it != more_elements_to_ghost.end();)
    1607             :         {
    1608     8194288 :           const Elem * elem = it->first;
    1609      704225 :           if (!elem->active())
    1610             :             {
    1611             :               libmesh_deprecated();
    1612           0 :               std::vector<const Elem*> children_to_ghost;
    1613           0 :               elem->active_family_tree(children_to_ghost,
    1614             :                                        /*reset=*/ false);
    1615           0 :               for (const Elem * child : children_to_ghost)
    1616           0 :                 if (child->processor_id() != p)
    1617           0 :                   children_to_couple.emplace_back(child, it->second);
    1618             : 
    1619           0 :               it = more_elements_to_ghost.erase(it);
    1620             :             }
    1621             :           else
    1622      704225 :             ++it;
    1623             :         }
    1624      345917 :       more_elements_to_ghost.insert(children_to_couple.begin(),
    1625             :                                     children_to_couple.end());
    1626             : #endif
    1627             : 
    1628    12395294 :       for (const auto & [elem, elem_cm] : more_elements_to_ghost)
    1629             :         {
    1630             :           // At this point we should only have active elements, even
    1631             :           // if we had to fix up gf output to get here.
    1632      704225 :           libmesh_assert(elem->active());
    1633             : 
    1634     8194288 :           if (const auto existing_it = elements_to_ghost.find(elem);
    1635      704225 :               existing_it == elements_to_ghost.end())
    1636     7032006 :             elements_to_ghost.emplace(elem, elem_cm);
    1637             :           else
    1638             :             {
    1639      482564 :               if (existing_it->second)
    1640             :                 {
    1641           0 :                   if (elem_cm)
    1642             :                     {
    1643             :                       // If this isn't already a temporary
    1644             :                       // then we need to make one so we'll
    1645             :                       // have a non-const matrix to merge
    1646           0 :                       if (temporary_coupling_matrices.empty() ||
    1647           0 :                           !temporary_coupling_matrices.count(existing_it->second))
    1648             :                         {
    1649             :                           // Make copy. This just calls the
    1650             :                           // compiler-generated copy constructor
    1651             :                           // because the CouplingMatrix class does not
    1652             :                           // define a custom copy constructor.
    1653           0 :                           auto result_pr = temporary_coupling_matrices.insert(std::make_unique<CouplingMatrix>(*existing_it->second));
    1654           0 :                           existing_it->second = result_pr.first->get();
    1655             :                         }
    1656             : 
    1657             :                       // Merge elem_cm into existing CouplingMatrix
    1658           0 :                       const_cast<CouplingMatrix &>(*existing_it->second) &= *elem_cm;
    1659             :                     }
    1660             :                   else // elem_cm == nullptr
    1661             :                     {
    1662             :                       // Any existing_it matrix merged with a full
    1663             :                       // matrix (symbolized as nullptr) gives another
    1664             :                       // full matrix (symbolizable as nullptr).
    1665             : 
    1666             :                       // So if existing_it->second is a temporary then
    1667             :                       // we don't need it anymore; we might as well
    1668             :                       // remove it to keep the set of temporaries
    1669             :                       // small.
    1670           0 :                       if (const auto temp_it = temporary_coupling_matrices.find(existing_it->second);
    1671           0 :                           temp_it != temporary_coupling_matrices.end())
    1672           0 :                         temporary_coupling_matrices.erase(temp_it);
    1673             : 
    1674           0 :                       existing_it->second = nullptr;
    1675             :                     }
    1676             :                 }
    1677             :               // else we have a nullptr already, then we have a full
    1678             :               // coupling matrix, already, and merging with anything
    1679             :               // else won't change that, so we're done.
    1680             :             }
    1681             :         }
    1682             :     }
    1683     4097098 : }
    1684             : 
    1685             : 
    1686             : 
    1687      275330 : void DofMap::add_neighbors_to_send_list(MeshBase & mesh)
    1688             : {
    1689        7922 :   LOG_SCOPE("add_neighbors_to_send_list()", "DofMap");
    1690             : 
    1691             :   // Return immediately if there's no ghost data
    1692      283252 :   if (this->n_processors() == 1)
    1693        7551 :     return;
    1694             : 
    1695      267779 :   const unsigned int n_var  = this->n_variables();
    1696             : 
    1697             :   MeshBase::const_element_iterator       local_elem_it
    1698      275701 :     = mesh.active_local_elements_begin();
    1699             :   const MeshBase::const_element_iterator local_elem_end
    1700      535558 :     = mesh.active_local_elements_end();
    1701             : 
    1702       15844 :   GhostingFunctor::map_type elements_to_send;
    1703       15844 :   DofMap::CouplingMatricesSet temporary_coupling_matrices;
    1704             : 
    1705             :   // We need to add dofs to the send list if they've been directly
    1706             :   // requested by an algebraic ghosting functor or they've been
    1707             :   // indirectly requested by a coupling functor.
    1708      275701 :   this->merge_ghost_functor_outputs(elements_to_send,
    1709             :                                     temporary_coupling_matrices,
    1710      519714 :                                     this->algebraic_ghosting_functors_begin(),
    1711      267779 :                                     this->algebraic_ghosting_functors_end(),
    1712             :                                     local_elem_it, local_elem_end, mesh.processor_id());
    1713             : 
    1714      275701 :   this->merge_ghost_functor_outputs(elements_to_send,
    1715             :                                     temporary_coupling_matrices,
    1716      519714 :                                     this->coupling_functors_begin(),
    1717      267779 :                                     this->coupling_functors_end(),
    1718             :                                     local_elem_it, local_elem_end, mesh.processor_id());
    1719             : 
    1720             :   // Making a list of non-zero coupling matrix columns is an
    1721             :   // O(N_var^2) operation.  We cache it so we only have to do it once
    1722             :   // per CouplingMatrix and not once per element.
    1723             :   std::map<const CouplingMatrix *, std::vector<unsigned int>>
    1724       15844 :     column_variable_lists;
    1725             : 
    1726     1502390 :   for (const auto & [partner, ghost_coupling] : elements_to_send)
    1727             :     {
    1728             :       // We asked ghosting functors not to give us local elements
    1729       41897 :       libmesh_assert_not_equal_to
    1730             :         (partner->processor_id(), this->processor_id());
    1731             : 
    1732             :       // Loop over any present coupling matrix column variables if we
    1733             :       // have a coupling matrix, or just add all variables to
    1734             :       // send_list if not.
    1735     1234611 :       if (ghost_coupling)
    1736             :         {
    1737           6 :           libmesh_assert_equal_to (ghost_coupling->size(), n_var);
    1738             : 
    1739             :           // Try to find a cached list of column variables.
    1740             :           std::map<const CouplingMatrix *, std::vector<unsigned int>>::const_iterator
    1741           6 :             column_variable_list = column_variable_lists.find(ghost_coupling);
    1742             : 
    1743             :           // If we didn't find it, then we need to create it.
    1744          74 :           if (column_variable_list == column_variable_lists.end())
    1745             :             {
    1746             :               auto inserted_variable_list_pair =
    1747          54 :                 column_variable_lists.emplace(ghost_coupling, std::vector<unsigned int>());
    1748           4 :               column_variable_list = inserted_variable_list_pair.first;
    1749             : 
    1750             :               std::vector<unsigned int> & new_variable_list =
    1751          54 :                 inserted_variable_list_pair.first->second;
    1752             : 
    1753          58 :               std::vector<unsigned char> has_variable(n_var, false);
    1754             : 
    1755         270 :               for (unsigned int vi = 0; vi != n_var; ++vi)
    1756             :                 {
    1757         216 :                   ConstCouplingRow ccr(vi, *ghost_coupling);
    1758             : 
    1759         486 :                   for (const auto & vj : ccr)
    1760         290 :                     has_variable[vj] = true;
    1761             :                 }
    1762         270 :               for (unsigned int vj = 0; vj != n_var; ++vj)
    1763             :                 {
    1764         232 :                   if (has_variable[vj])
    1765         216 :                     new_variable_list.push_back(vj);
    1766             :                 }
    1767             :             }
    1768             : 
    1769             :           const std::vector<unsigned int> & variable_list =
    1770           6 :             column_variable_list->second;
    1771             : 
    1772         370 :           for (const auto & vj : variable_list)
    1773             :             {
    1774          48 :               std::vector<dof_id_type> di;
    1775         296 :               this->dof_indices (partner, di, vj);
    1776             : 
    1777             :               // Insert the remote DOF indices into the send list
    1778         888 :               for (auto d : di)
    1779         640 :                 if (d != DofObject::invalid_id &&
    1780         544 :                     !this->local_index(d))
    1781             :                   {
    1782          48 :                     libmesh_assert_less(d, this->n_dofs());
    1783         592 :                     _send_list.push_back(d);
    1784             :                   }
    1785             :             }
    1786             :         }
    1787             :       else
    1788             :         {
    1789       83782 :           std::vector<dof_id_type> di;
    1790     1234537 :           this->dof_indices (partner, di);
    1791             : 
    1792             :           // Insert the remote DOF indices into the send list
    1793    25587648 :           for (const auto & dof : di)
    1794    25202087 :             if (dof != DofObject::invalid_id &&
    1795    23504276 :                 !this->local_index(dof))
    1796             :               {
    1797      683230 :                 libmesh_assert_less(dof, this->n_dofs());
    1798    19880924 :                 _send_list.push_back(dof);
    1799             :               }
    1800             :         }
    1801             : 
    1802             :     }
    1803             : 
    1804             :   // We're now done with any merged coupling matrices we had to create.
    1805        7922 :   temporary_coupling_matrices.clear();
    1806             : 
    1807             :   //-------------------------------------------------------------------------
    1808             :   // Our coupling functors added dofs from neighboring elements to the
    1809             :   // send list, but we may still need to add non-local dofs from local
    1810             :   // elements.
    1811             :   //-------------------------------------------------------------------------
    1812             : 
    1813             :   // Loop over the active local elements, adding all active elements
    1814             :   // that neighbor an active local element to the send list.
    1815     7194557 :   for ( ; local_elem_it != local_elem_end; ++local_elem_it)
    1816             :     {
    1817     3863602 :       const Elem * elem = *local_elem_it;
    1818             : 
    1819      800458 :       std::vector<dof_id_type> di;
    1820     3863602 :       this->dof_indices (elem, di);
    1821             : 
    1822             :       // Insert the remote DOF indices into the send list
    1823    43833215 :       for (const auto & dof : di)
    1824    44025662 :         if (dof != DofObject::invalid_id &&
    1825    35913674 :             !this->local_index(dof))
    1826             :           {
    1827      182058 :             libmesh_assert_less(dof, this->n_dofs());
    1828     5038594 :             _send_list.push_back(dof);
    1829             :           }
    1830             :     }
    1831             : }
    1832             : 
    1833             : 
    1834             : 
    1835      275189 : void DofMap::prepare_send_list ()
    1836             : {
    1837        7918 :   LOG_SCOPE("prepare_send_list()", "DofMap");
    1838             : 
    1839             :   // Return immediately if there's no ghost data
    1840      283107 :   if (this->n_processors() == 1)
    1841        7548 :     return;
    1842             : 
    1843             :   // Check to see if we have any extra stuff to add to the send_list
    1844      267641 :   if (_extra_send_list_function)
    1845             :     {
    1846           0 :       if (_augment_send_list)
    1847             :         {
    1848           0 :           libmesh_here();
    1849           0 :           libMesh::out << "WARNING:  You have specified both an extra send list function and object.\n"
    1850           0 :                        << "          Are you sure this is what you meant to do??"
    1851           0 :                        << std::endl;
    1852             :         }
    1853             : 
    1854           0 :       _extra_send_list_function(_send_list, _extra_send_list_context);
    1855             :     }
    1856             : 
    1857      267641 :   if (_augment_send_list)
    1858           0 :     _augment_send_list->augment_send_list (_send_list);
    1859             : 
    1860             :   // First sort the send list.  After this
    1861             :   // duplicated elements will be adjacent in the
    1862             :   // vector
    1863      267641 :   std::sort(_send_list.begin(), _send_list.end());
    1864             : 
    1865             :   // Now use std::unique to remove duplicate entries
    1866             :   std::vector<dof_id_type>::iterator new_end =
    1867      267641 :     std::unique (_send_list.begin(), _send_list.end());
    1868             : 
    1869             :   // Remove the end of the send_list.  Use the "swap trick"
    1870             :   // from Effective STL
    1871      527364 :   std::vector<dof_id_type> (_send_list.begin(), new_end).swap (_send_list);
    1872             : 
    1873             :   // Make sure the send list has nothing invalid in it.
    1874        7918 :   libmesh_assert(_send_list.empty() || _send_list.back() < this->n_dofs());
    1875             : }
    1876             : 
    1877         420 : void DofMap::reinit_send_list (MeshBase & mesh)
    1878             : {
    1879          12 :   this->clear_send_list();
    1880         420 :   this->add_neighbors_to_send_list(mesh);
    1881             : 
    1882             : #ifdef LIBMESH_ENABLE_CONSTRAINTS
    1883             :   // This is assuming that we only need to recommunicate
    1884             :   // the constraints and no new ones have been added since
    1885             :   // a previous call to reinit_constraints.
    1886         420 :   this->process_constraints(mesh);
    1887             : #endif
    1888         420 :   this->prepare_send_list();
    1889         420 : }
    1890             : 
    1891         142 : void DofMap::set_implicit_neighbor_dofs(bool implicit_neighbor_dofs)
    1892             : {
    1893         142 :   _implicit_neighbor_dofs_initialized = true;
    1894         142 :   _implicit_neighbor_dofs = implicit_neighbor_dofs;
    1895         142 : }
    1896             : 
    1897           0 : void DofMap::set_verify_dirichlet_bc_consistency(bool val)
    1898             : {
    1899           0 :   _verify_dirichlet_bc_consistency = val;
    1900           0 : }
    1901             : 
    1902             : 
    1903      562744 : bool DofMap::use_coupled_neighbor_dofs(const MeshBase & /*mesh*/) const
    1904             : {
    1905             :   // If we were asked on the command line, then we need to
    1906             :   // include sensitivities between neighbor degrees of freedom
    1907             :   bool implicit_neighbor_dofs =
    1908      562744 :     libMesh::on_command_line ("--implicit-neighbor-dofs");
    1909             : 
    1910             :   // If the user specifies --implicit-neighbor-dofs 0, then
    1911             :   // presumably he knows what he is doing and we won't try to
    1912             :   // automatically turn it on even when all the variables are
    1913             :   // discontinuous.
    1914      562744 :   if (implicit_neighbor_dofs)
    1915             :     {
    1916             :       // No flag provided defaults to 'true'
    1917           0 :       int flag = 1;
    1918           0 :       flag = libMesh::command_line_next ("--implicit-neighbor-dofs", flag);
    1919             : 
    1920           0 :       if (!flag)
    1921             :         {
    1922             :           // The user said --implicit-neighbor-dofs 0, so he knows
    1923             :           // what he is doing and really doesn't want it.
    1924           0 :           return false;
    1925             :         }
    1926             :     }
    1927             : 
    1928             :   // Possibly override the commandline option, if set_implicit_neighbor_dofs
    1929             :   // has been called.
    1930      562744 :   if (_implicit_neighbor_dofs_initialized)
    1931             :     {
    1932         426 :       implicit_neighbor_dofs = _implicit_neighbor_dofs;
    1933             : 
    1934             :       // Again, if the user explicitly says implicit_neighbor_dofs = false,
    1935             :       // then we return here.
    1936         426 :       if (!implicit_neighbor_dofs)
    1937           0 :         return false;
    1938             :     }
    1939             : 
    1940             :   // Look at all the variables in this system.  If every one is
    1941             :   // discontinuous then the user must be doing DG/FVM, so be nice
    1942             :   // and force implicit_neighbor_dofs=true.
    1943             :   {
    1944       16198 :     bool all_discontinuous_dofs = true;
    1945             : 
    1946             :     // We may call this method even without ever having initialized our data
    1947    15413480 :       for (auto var : index_range(this->_variables))
    1948    14850736 :         if (FEInterface::get_continuity(this->variable_type(var)) !=  DISCONTINUOUS)
    1949      410768 :           all_discontinuous_dofs = false;
    1950             : 
    1951      562744 :     if (all_discontinuous_dofs)
    1952        7102 :       implicit_neighbor_dofs = true;
    1953             :   }
    1954             : 
    1955       16198 :   return implicit_neighbor_dofs;
    1956             : }
    1957             : 
    1958             : 
    1959             : 
    1960       39576 : void DofMap::compute_sparsity(const MeshBase & mesh)
    1961             : {
    1962       77916 :   _sp = this->build_sparsity(mesh, this->_constrained_sparsity_construction);
    1963             : 
    1964             :   // It is possible that some \p SparseMatrix implementations want to
    1965             :   // see the sparsity pattern before we throw it away.  If so, we
    1966             :   // share a view of its arrays, and we pass it in to the matrices.
    1967       79931 :   for (const auto & mat : _matrices)
    1968             :     {
    1969       41613 :       mat->attach_sparsity_pattern (*_sp);
    1970       40355 :       if (need_full_sparsity_pattern)
    1971           0 :         mat->update_sparsity_pattern (_sp->get_sparsity_pattern());
    1972             :     }
    1973             :   // If we don't need the full sparsity pattern anymore, free the
    1974             :   // parts of it we don't need.
    1975       39576 :   if (!need_full_sparsity_pattern)
    1976       39576 :     _sp->clear_full_sparsity();
    1977       39576 : }
    1978             : 
    1979             : 
    1980             : 
    1981      263428 : void DofMap::clear_sparsity()
    1982             : {
    1983        7604 :   _sp.reset();
    1984      263428 : }
    1985             : 
    1986             : 
    1987             : 
    1988         355 : void DofMap::remove_default_ghosting()
    1989             : {
    1990         355 :   this->remove_coupling_functor(this->default_coupling());
    1991         355 :   this->remove_algebraic_ghosting_functor(this->default_algebraic_ghosting());
    1992         355 : }
    1993             : 
    1994             : 
    1995             : 
    1996          71 : void DofMap::add_default_ghosting()
    1997             : {
    1998          71 :   this->add_coupling_functor(this->default_coupling());
    1999          71 :   this->add_algebraic_ghosting_functor(this->default_algebraic_ghosting());
    2000          71 : }
    2001             : 
    2002             : 
    2003             : 
    2004             : void
    2005      486901 : DofMap::add_coupling_functor(GhostingFunctor & coupling_functor,
    2006             :                              bool to_mesh)
    2007             : {
    2008             :   // We used to implicitly support duplicate inserts to std::set
    2009             : #ifdef LIBMESH_ENABLE_DEPRECATED
    2010             :   _coupling_functors.erase
    2011      459249 :     (std::remove(_coupling_functors.begin(),
    2012             :                  _coupling_functors.end(),
    2013      486901 :                  &coupling_functor),
    2014       41478 :      _coupling_functors.end());
    2015             : #endif
    2016             : 
    2017             :   // We shouldn't have two copies of the same functor
    2018       13826 :   libmesh_assert(std::find(_coupling_functors.begin(),
    2019             :                            _coupling_functors.end(),
    2020             :                            &coupling_functor) ==
    2021             :                  _coupling_functors.end());
    2022             : 
    2023      486901 :   _coupling_functors.push_back(&coupling_functor);
    2024      486901 :   coupling_functor.set_mesh(&_mesh);
    2025      486901 :   if (to_mesh)
    2026      486830 :     _mesh.add_ghosting_functor(coupling_functor);
    2027      486901 : }
    2028             : 
    2029             : 
    2030             : 
    2031             : void
    2032         639 : DofMap::remove_coupling_functor(GhostingFunctor & coupling_functor)
    2033             : {
    2034         603 :   auto raw_it = std::find(_coupling_functors.begin(),
    2035         657 :                           _coupling_functors.end(), &coupling_functor);
    2036             : 
    2037             : #ifndef LIBMESH_ENABLE_DEPRECATED
    2038             :   // We shouldn't be trying to remove a functor that isn't there
    2039             :   libmesh_assert(raw_it != _coupling_functors.end());
    2040             : #else
    2041             :   // Our old API supported trying to remove a functor that isn't there
    2042         639 :   if (raw_it != _coupling_functors.end())
    2043             : #endif
    2044         639 :     _coupling_functors.erase(raw_it);
    2045             : 
    2046             :   // We shouldn't have had two copies of the same functor
    2047          18 :   libmesh_assert(std::find(_coupling_functors.begin(),
    2048             :                            _coupling_functors.end(),
    2049             :                            &coupling_functor) ==
    2050             :                  _coupling_functors.end());
    2051             : 
    2052         639 :   _mesh.remove_ghosting_functor(coupling_functor);
    2053             : 
    2054         639 :   if (const auto it = _shared_functors.find(&coupling_functor);
    2055          18 :       it != _shared_functors.end())
    2056           0 :     _shared_functors.erase(it);
    2057         639 : }
    2058             : 
    2059             : 
    2060             : 
    2061             : void
    2062      487185 : DofMap::add_algebraic_ghosting_functor(GhostingFunctor & evaluable_functor,
    2063             :                                        bool to_mesh)
    2064             : {
    2065             :   // We used to implicitly support duplicate inserts to std::set
    2066             : #ifdef LIBMESH_ENABLE_DEPRECATED
    2067             :   _algebraic_ghosting_functors.erase
    2068      459517 :     (std::remove(_algebraic_ghosting_functors.begin(),
    2069             :                  _algebraic_ghosting_functors.end(),
    2070      487185 :                  &evaluable_functor),
    2071       41502 :      _algebraic_ghosting_functors.end());
    2072             : #endif
    2073             : 
    2074             :   // We shouldn't have two copies of the same functor
    2075       13834 :   libmesh_assert(std::find(_algebraic_ghosting_functors.begin(),
    2076             :                            _algebraic_ghosting_functors.end(),
    2077             :                            &evaluable_functor) ==
    2078             :                  _algebraic_ghosting_functors.end());
    2079             : 
    2080      487185 :   _algebraic_ghosting_functors.push_back(&evaluable_functor);
    2081      487185 :   evaluable_functor.set_mesh(&_mesh);
    2082      487185 :   if (to_mesh)
    2083      487185 :     _mesh.add_ghosting_functor(evaluable_functor);
    2084      487185 : }
    2085             : 
    2086             : 
    2087             : 
    2088             : void
    2089         639 : DofMap::remove_algebraic_ghosting_functor(GhostingFunctor & evaluable_functor)
    2090             : {
    2091         603 :   auto raw_it = std::find(_algebraic_ghosting_functors.begin(),
    2092             :                           _algebraic_ghosting_functors.end(),
    2093         657 :                           &evaluable_functor);
    2094             : 
    2095             : #ifndef LIBMESH_ENABLE_DEPRECATED
    2096             :   // We shouldn't be trying to remove a functor that isn't there
    2097             :   libmesh_assert(raw_it != _algebraic_ghosting_functors.end());
    2098             : #else
    2099             :   // Our old API supported trying to remove a functor that isn't there
    2100         639 :   if (raw_it != _algebraic_ghosting_functors.end())
    2101             : #endif
    2102         639 :     _algebraic_ghosting_functors.erase(raw_it);
    2103             : 
    2104             :   // We shouldn't have had two copies of the same functor
    2105          18 :   libmesh_assert(std::find(_algebraic_ghosting_functors.begin(),
    2106             :                            _algebraic_ghosting_functors.end(),
    2107             :                            &evaluable_functor) ==
    2108             :                  _algebraic_ghosting_functors.end());
    2109             : 
    2110         639 :   _mesh.remove_ghosting_functor(evaluable_functor);
    2111             : 
    2112         639 :   if (const auto it = _shared_functors.find(&evaluable_functor);
    2113          18 :       it != _shared_functors.end())
    2114           0 :     _shared_functors.erase(it);
    2115         639 : }
    2116             : 
    2117             : 
    2118             : 
    2119           0 : void DofMap::extract_local_vector (const NumericVector<Number> & Ug,
    2120             :                                    const std::vector<dof_id_type> & dof_indices_in,
    2121             :                                    DenseVectorBase<Number> & Ue) const
    2122             : {
    2123           0 :   const unsigned int n_original_dofs = dof_indices_in.size();
    2124             : 
    2125             : #ifdef LIBMESH_ENABLE_AMR
    2126             : 
    2127             :   // Trivial mapping
    2128           0 :   libmesh_assert_equal_to (dof_indices_in.size(), Ue.size());
    2129           0 :   bool has_constrained_dofs = false;
    2130             : 
    2131           0 :   for (unsigned int il=0; il != n_original_dofs; ++il)
    2132             :     {
    2133           0 :       const dof_id_type ig = dof_indices_in[il];
    2134             : 
    2135           0 :       if (this->is_constrained_dof (ig)) has_constrained_dofs = true;
    2136             : 
    2137           0 :       libmesh_assert_less (ig, Ug.size());
    2138             : 
    2139           0 :       Ue.el(il) = Ug(ig);
    2140             :     }
    2141             : 
    2142             :   // If the element has any constrained DOFs then we need
    2143             :   // to account for them in the mapping.  This will handle
    2144             :   // the case that the input vector is not constrained.
    2145           0 :   if (has_constrained_dofs)
    2146             :     {
    2147             :       // Copy the input DOF indices.
    2148           0 :       std::vector<dof_id_type> constrained_dof_indices(dof_indices_in);
    2149             : 
    2150           0 :       DenseMatrix<Number> C;
    2151           0 :       DenseVector<Number> H;
    2152             : 
    2153           0 :       this->build_constraint_matrix_and_vector (C, H, constrained_dof_indices);
    2154             : 
    2155           0 :       libmesh_assert_equal_to (dof_indices_in.size(), C.m());
    2156           0 :       libmesh_assert_equal_to (constrained_dof_indices.size(), C.n());
    2157             : 
    2158             :       // zero-out Ue
    2159           0 :       Ue.zero();
    2160             : 
    2161             :       // compute Ue = C Ug, with proper mapping.
    2162           0 :       for (unsigned int i=0; i != n_original_dofs; i++)
    2163             :         {
    2164           0 :           Ue.el(i) = H(i);
    2165             : 
    2166             :           const unsigned int n_constrained =
    2167           0 :             cast_int<unsigned int>(constrained_dof_indices.size());
    2168           0 :           for (unsigned int j=0; j<n_constrained; j++)
    2169             :             {
    2170           0 :               const dof_id_type jg = constrained_dof_indices[j];
    2171             : 
    2172             :               //          If Ug is a serial or ghosted vector, then this assert is
    2173             :               //          overzealous.  If Ug is a parallel vector, then this assert
    2174             :               //          is redundant.
    2175             :               //    libmesh_assert ((jg >= Ug.first_local_index()) &&
    2176             :               //    (jg <  Ug.last_local_index()));
    2177             : 
    2178           0 :               Ue.el(i) += C(i,j)*Ug(jg);
    2179             :             }
    2180             :         }
    2181           0 :     }
    2182             : 
    2183             : #else
    2184             : 
    2185             :   // Trivial mapping
    2186             : 
    2187             :   libmesh_assert_equal_to (n_original_dofs, Ue.size());
    2188             : 
    2189             :   for (unsigned int il=0; il<n_original_dofs; il++)
    2190             :     {
    2191             :       const dof_id_type ig = dof_indices_in[il];
    2192             : 
    2193             :       libmesh_assert ((ig >= Ug.first_local_index()) && (ig <  Ug.last_local_index()));
    2194             : 
    2195             :       Ue.el(il) = Ug(ig);
    2196             :     }
    2197             : 
    2198             : #endif
    2199           0 : }
    2200             : 
    2201   143358725 : void DofMap::dof_indices (const Elem * const elem,
    2202             :                           std::vector<dof_id_type> & di) const
    2203             : {
    2204             :   // We now allow elem==nullptr to request just SCALAR dofs
    2205             :   // libmesh_assert(elem);
    2206             : 
    2207             :   // If we are asking for current indices on an element, it ought to
    2208             :   // be an active element (or a temporary side, which also thinks it's
    2209             :   // active)
    2210    10876659 :   libmesh_assert(!elem || elem->active());
    2211             : 
    2212             :   // dof_indices() is a relatively light-weight function that is
    2213             :   // called millions of times in normal codes. Therefore, it is not a
    2214             :   // good candidate for logging, since the cost of the logging code
    2215             :   // itself is roughly on par with the time required to call
    2216             :   // dof_indices().
    2217             :   // LOG_SCOPE("dof_indices()", "DofMap");
    2218             : 
    2219             :   // Clear the DOF indices vector
    2220    10876659 :   di.clear();
    2221             : 
    2222    10876659 :   const unsigned int n_var_groups  = this->n_variable_groups();
    2223             : 
    2224             : #ifdef DEBUG
    2225             :   // Check that sizes match in DEBUG mode
    2226    10876659 :   std::size_t tot_size = 0;
    2227             : #endif
    2228             : 
    2229   143358725 :   if (elem && elem->type() == TRI3SUBDIVISION)
    2230             :     {
    2231             :       // Subdivision surface FE require the 1-ring around elem
    2232         616 :       const Tri3Subdivision * sd_elem = static_cast<const Tri3Subdivision *>(elem);
    2233             : 
    2234             :       // Ghost subdivision elements have no real dofs
    2235        7238 :       if (!sd_elem->is_ghost())
    2236             :         {
    2237             :           // Determine the nodes contributing to element elem
    2238        1088 :           std::vector<const Node *> elem_nodes;
    2239        6457 :           MeshTools::Subdivision::find_one_ring(sd_elem, elem_nodes);
    2240             : 
    2241             :           // Get the dof numbers
    2242       12914 :           for (unsigned int vg=0; vg<n_var_groups; vg++)
    2243             :             {
    2244         544 :               const VariableGroup & var = this->variable_group(vg);
    2245         544 :               const unsigned int vars_in_group = var.n_variables();
    2246             : 
    2247        6457 :               if (var.type().family == SCALAR &&
    2248           0 :                   var.active_on_subdomain(elem->subdomain_id()))
    2249             :                 {
    2250           0 :                   for (unsigned int vig=0; vig != vars_in_group; ++vig)
    2251             :                     {
    2252             : #ifdef DEBUG
    2253           0 :                       tot_size += var.type().order;
    2254             : #endif
    2255           0 :                       std::vector<dof_id_type> di_new;
    2256           0 :                       this->SCALAR_dof_indices(di_new,var.number(vig));
    2257           0 :                       di.insert( di.end(), di_new.begin(), di_new.end());
    2258             :                     }
    2259             :                 }
    2260             :               else
    2261       25828 :                 for (unsigned int vig=0; vig != vars_in_group; ++vig)
    2262             :                   {
    2263       24267 :                     _dof_indices(*elem, elem->p_level(), di, vg, vig,
    2264        1632 :                                  elem_nodes.data(),
    2265             :                                  cast_int<unsigned int>(elem_nodes.size()),
    2266             :                                  var.number(vig)
    2267             : #ifdef DEBUG
    2268             :                                  , tot_size
    2269             : #endif
    2270             :                                  );
    2271             :                   }
    2272             :             }
    2273             :         }
    2274             : 
    2275        7238 :       return;
    2276             :     }
    2277             : 
    2278             :   // Get the dof numbers for each variable
    2279   143351487 :   const unsigned int n_nodes = elem ? elem->n_nodes() : 0;
    2280   293492207 :   for (unsigned int vg=0; vg<n_var_groups; vg++)
    2281             :     {
    2282    11432372 :       const VariableGroup & var = this->variable_group(vg);
    2283    11432372 :       const unsigned int vars_in_group = var.n_variables();
    2284             : 
    2285   150220046 :       if (var.type().family == SCALAR &&
    2286      818963 :           (!elem ||
    2287      898289 :            var.active_on_subdomain(elem->subdomain_id())))
    2288             :         {
    2289     1796578 :           for (unsigned int vig=0; vig != vars_in_group; ++vig)
    2290             :             {
    2291             : #ifdef DEBUG
    2292       79326 :               tot_size += var.type().order;
    2293             : #endif
    2294       79326 :               std::vector<dof_id_type> di_new;
    2295      977615 :               this->SCALAR_dof_indices(di_new,var.number(vig));
    2296      898289 :               di.insert( di.end(), di_new.begin(), di_new.end());
    2297             :             }
    2298             :         }
    2299   149242431 :       else if (elem)
    2300   320925829 :         for (unsigned int vig=0; vig != vars_in_group; ++vig)
    2301             :           {
    2302   184668679 :             _dof_indices(*elem, elem->p_level(), di, vg, vig,
    2303             :                          elem->get_nodes(), n_nodes, var.number(vig)
    2304             : #ifdef DEBUG
    2305             :                          , tot_size
    2306             : #endif
    2307             :                      );
    2308             :           }
    2309             :     }
    2310             : 
    2311             : #ifdef DEBUG
    2312    10876043 :   libmesh_assert_equal_to (tot_size, di.size());
    2313             : #endif
    2314             : }
    2315             : 
    2316             : 
    2317   125269607 : void DofMap::dof_indices (const Elem * const elem,
    2318             :                           std::vector<dof_id_type> & di,
    2319             :                           const unsigned int vn,
    2320             :                           int p_level) const
    2321             : {
    2322   125269607 :   dof_indices(
    2323             :       elem,
    2324             :       di,
    2325             :       vn,
    2326      928938 :       [](const Elem &,
    2327             :          std::vector<dof_id_type> & dof_indices,
    2328      195838 :          const std::vector<dof_id_type> & scalar_dof_indices) {
    2329     1222695 :         dof_indices.insert(dof_indices.end(), scalar_dof_indices.begin(), scalar_dof_indices.end());
    2330     1124776 :       },
    2331             :       [](const Elem &,
    2332             :          unsigned int,
    2333             :          unsigned int,
    2334             :          std::vector<dof_id_type> & dof_indices,
    2335   609928960 :          const dof_id_type dof) { dof_indices.push_back(dof); },
    2336             :       p_level);
    2337   125269607 : }
    2338             : 
    2339         120 : void DofMap::array_dof_indices(const Elem * const elem,
    2340             :                                std::vector<dof_id_type> & di,
    2341             :                                const unsigned int vn,
    2342             :                                int p_level) const
    2343             : {
    2344         110 :   auto dof_indices_functor = [elem, p_level, this](std::vector<dof_id_type> & functor_di,
    2345         190 :                                                    const unsigned int functor_vn) {
    2346         150 :     this->dof_indices(elem, functor_di, functor_vn, p_level);
    2347         250 :   };
    2348         120 :   this->array_dof_indices(dof_indices_functor, di, vn);
    2349         120 : }
    2350             : 
    2351           0 : void DofMap::array_dof_indices(const Node * const node,
    2352             :                                std::vector<dof_id_type> & di,
    2353             :                                const unsigned int vn) const
    2354             : {
    2355             :   auto dof_indices_functor = [node, this](std::vector<dof_id_type> & functor_di,
    2356           0 :                                           const unsigned int functor_vn) {
    2357           0 :     this->dof_indices(node, functor_di, functor_vn);
    2358           0 :   };
    2359           0 :   this->array_dof_indices(dof_indices_functor, di, vn);
    2360           0 : }
    2361             : 
    2362      167508 : void DofMap::dof_indices (const Node * const node,
    2363             :                           std::vector<dof_id_type> & di) const
    2364             : {
    2365             :   // We allow node==nullptr to request just SCALAR dofs
    2366             :   // libmesh_assert(elem);
    2367             : 
    2368             :   // dof_indices() is a relatively light-weight function that is
    2369             :   // called millions of times in normal codes. Therefore, it is not a
    2370             :   // good candidate for logging, since the cost of the logging code
    2371             :   // itself is roughly on par with the time required to call
    2372             :   // dof_indices().
    2373             :   // LOG_SCOPE("dof_indices(Node)", "DofMap");
    2374             : 
    2375             :   // Clear the DOF indices vector
    2376       15228 :   di.clear();
    2377             : 
    2378       15228 :   const unsigned int n_var_groups  = this->n_variable_groups();
    2379       30456 :   const unsigned int sys_num = this->sys_number();
    2380             : 
    2381             :   // Get the dof numbers
    2382      502524 :   for (unsigned int vg=0; vg<n_var_groups; vg++)
    2383             :     {
    2384       30456 :       const VariableGroup & var = this->variable_group(vg);
    2385       30456 :       const unsigned int vars_in_group = var.n_variables();
    2386             : 
    2387      335016 :       if (var.type().family == SCALAR)
    2388             :         {
    2389           0 :           for (unsigned int vig=0; vig != vars_in_group; ++vig)
    2390             :             {
    2391           0 :               std::vector<dof_id_type> di_new;
    2392           0 :               this->SCALAR_dof_indices(di_new,var.number(vig));
    2393           0 :               di.insert( di.end(), di_new.begin(), di_new.end());
    2394             :             }
    2395             :         }
    2396             :       else
    2397             :         {
    2398      335016 :           const int n_comp = node->n_comp_group(sys_num,vg);
    2399      837540 :           for (unsigned int vig=0; vig != vars_in_group; ++vig)
    2400             :             {
    2401      911988 :               for (int i=0; i != n_comp; ++i)
    2402             :                 {
    2403             :                   const dof_id_type d =
    2404      409464 :                     node->dof_number(sys_num, vg, vig, i, n_comp);
    2405       37224 :                   libmesh_assert_not_equal_to
    2406             :                     (d, DofObject::invalid_id);
    2407      409464 :                   di.push_back(d);
    2408             :                 }
    2409             :             }
    2410             :         }
    2411             :     }
    2412      167508 : }
    2413             : 
    2414             : 
    2415         600 : void DofMap::dof_indices (const Node * const node,
    2416             :                           std::vector<dof_id_type> & di,
    2417             :                           const unsigned int vn) const
    2418             : {
    2419         600 :   if (vn == libMesh::invalid_uint)
    2420             :     {
    2421           0 :       this->dof_indices(node, di);
    2422           0 :       return;
    2423             :     }
    2424             : 
    2425             :   // We allow node==nullptr to request just SCALAR dofs
    2426             :   // libmesh_assert(elem);
    2427             : 
    2428             :   // dof_indices() is a relatively light-weight function that is
    2429             :   // called millions of times in normal codes. Therefore, it is not a
    2430             :   // good candidate for logging, since the cost of the logging code
    2431             :   // itself is roughly on par with the time required to call
    2432             :   // dof_indices().
    2433             :   // LOG_SCOPE("dof_indices(Node)", "DofMap");
    2434             : 
    2435             :   // Clear the DOF indices vector
    2436          50 :   di.clear();
    2437             : 
    2438         100 :   const unsigned int sys_num = this->sys_number();
    2439             : 
    2440             :   // Get the dof numbers
    2441         650 :   const unsigned int vg = this->_variable_group_numbers[vn];
    2442          50 :   const VariableGroup & var = this->variable_group(vg);
    2443             : 
    2444         600 :   if (var.type().family == SCALAR)
    2445             :     {
    2446           0 :       std::vector<dof_id_type> di_new;
    2447           0 :       this->SCALAR_dof_indices(di_new,vn);
    2448           0 :       di.insert( di.end(), di_new.begin(), di_new.end());
    2449             :     }
    2450             :   else
    2451             :     {
    2452         600 :       const unsigned int vig = vn - var.number();
    2453         600 :       const int n_comp = node->n_comp_group(sys_num,vg);
    2454         960 :       for (int i=0; i != n_comp; ++i)
    2455             :         {
    2456             :           const dof_id_type d =
    2457         360 :             node->dof_number(sys_num, vg, vig, i, n_comp);
    2458          30 :           libmesh_assert_not_equal_to
    2459             :             (d, DofObject::invalid_id);
    2460         360 :           di.push_back(d);
    2461             :         }
    2462             :     }
    2463             : }
    2464             : 
    2465             : 
    2466     5598253 : void DofMap::dof_indices (const Elem & elem,
    2467             :                           unsigned int n,
    2468             :                           std::vector<dof_id_type> & di,
    2469             :                           const unsigned int vn) const
    2470             : {
    2471     5598253 :   this->_node_dof_indices(elem, n, elem.node_ref(n), di, vn);
    2472     5598253 : }
    2473             : 
    2474             : 
    2475             : 
    2476             : #ifdef LIBMESH_ENABLE_AMR
    2477             : 
    2478     4606680 : void DofMap::old_dof_indices (const Elem & elem,
    2479             :                               unsigned int n,
    2480             :                               std::vector<dof_id_type> & di,
    2481             :                               const unsigned int vn) const
    2482             : {
    2483      486381 :   const DofObject & old_obj = elem.node_ref(n).get_old_dof_object_ref();
    2484     4606680 :   this->_node_dof_indices(elem, n, old_obj, di, vn);
    2485     4606680 : }
    2486             : 
    2487             : #endif // LIBMESH_ENABLE_AMR
    2488             : 
    2489             : 
    2490             : 
    2491    10204933 : void DofMap::_node_dof_indices (const Elem & elem,
    2492             :                                 unsigned int n,
    2493             :                                 const DofObject & obj,
    2494             :                                 std::vector<dof_id_type> & di,
    2495             :                                 const unsigned int vn) const
    2496             : {
    2497             :   // Half of this is a cut and paste of _dof_indices code below, but
    2498             :   // duplication actually seems cleaner than creating a helper
    2499             :   // function with a million arguments and hoping the compiler inlines
    2500             :   // it properly into one of our most highly trafficked functions.
    2501             : 
    2502             :   // dof_indices() is a relatively light-weight function; the cost of
    2503             :   // the logging code itself is roughly on par with the time required
    2504             :   // to call dof_indices().
    2505             :   // LOG_SCOPE("_node_dof_indices()", "DofMap");
    2506             : 
    2507     2024040 :   const unsigned int sys_num = this->sys_number();
    2508     1012293 :   const auto [vg, vig] =
    2509     9193186 :     obj.var_to_vg_and_offset(sys_num,vn);
    2510     9193186 :   const unsigned int n_comp = obj.n_comp_group(sys_num,vg);
    2511             : 
    2512     1012293 :   const VariableGroup & var = this->variable_group(vg);
    2513    10204933 :   FEType fe_type = var.type();
    2514             :   const bool extra_hanging_dofs =
    2515    10204933 :     FEInterface::extra_hanging_dofs(fe_type);
    2516             : 
    2517    10204933 :   const bool add_p_level = fe_type.p_refinement;
    2518             : 
    2519             :   // There is a potential problem with h refinement.  Imagine a
    2520             :   // quad9 that has a linear FE on it.  Then, on the hanging side,
    2521             :   // it can falsely identify a DOF at the mid-edge node. This is why
    2522             :   // we go through FEInterface instead of obj->n_comp() directly.
    2523             :   const unsigned int nc =
    2524    10204933 :     FEInterface::n_dofs_at_node(fe_type, &elem, n, add_p_level);
    2525             : 
    2526             :   // If this is a non-vertex on a hanging node with extra
    2527             :   // degrees of freedom, we use the non-vertex dofs (which
    2528             :   // come in reverse order starting from the end, to
    2529             :   // simplify p refinement)
    2530    10204933 :   if (extra_hanging_dofs && nc && !elem.is_vertex(n))
    2531             :     {
    2532     2246629 :       const int dof_offset = n_comp - nc;
    2533             : 
    2534             :       // We should never have fewer dofs than necessary on a
    2535             :       // node unless we're getting indices on a parent element,
    2536             :       // and we should never need the indices on such a node
    2537     2246629 :       if (dof_offset < 0)
    2538             :         {
    2539           0 :           libmesh_assert(!elem.active());
    2540           0 :           di.resize(di.size() + nc, DofObject::invalid_id);
    2541             :         }
    2542             :       else
    2543     6672001 :         for (unsigned int i = dof_offset; i != n_comp; ++i)
    2544             :           {
    2545             :             const dof_id_type d =
    2546     4425372 :               obj.dof_number(sys_num, vg, vig, i, n_comp);
    2547      344899 :             libmesh_assert_not_equal_to (d, DofObject::invalid_id);
    2548     4425372 :             di.push_back(d);
    2549             :           }
    2550             :     }
    2551             :   // If this is a vertex or an element without extra hanging
    2552             :   // dofs, our dofs come in forward order coming from the
    2553             :   // beginning.  But we still might not have all those dofs, in cases
    2554             :   // where a subdomain-restricted variable just had its subdomain
    2555             :   // expanded.
    2556             :   else
    2557             :     {
    2558             :       const unsigned int good_nc =
    2559     7977481 :         std::min(static_cast<unsigned int>(n_comp), nc);
    2560    15820020 :       for (unsigned int i=0; i != good_nc; ++i)
    2561             :         {
    2562             :           const dof_id_type d =
    2563     7861716 :             obj.dof_number(sys_num, vg, vig, i, n_comp);
    2564      817365 :           libmesh_assert_not_equal_to (d, DofObject::invalid_id);
    2565     7861716 :           di.push_back(d);
    2566             :         }
    2567     7958304 :       for (unsigned int i=good_nc; i != nc; ++i)
    2568           0 :         di.push_back(DofObject::invalid_id);
    2569             :     }
    2570    10204933 : }
    2571             : 
    2572             : void
    2573   171826674 : DofMap::_dof_indices(const Elem & elem,
    2574             :                      int p_level,
    2575             :                      std::vector<dof_id_type> & di,
    2576             :                      const unsigned int vg,
    2577             :                      const unsigned int vig,
    2578             :                      const Node * const * nodes,
    2579             :                      unsigned int n_nodes,
    2580             :                      const unsigned int v
    2581             : #ifdef DEBUG
    2582             :                      ,
    2583             :                      std::size_t & tot_size
    2584             : #endif
    2585             : ) const
    2586             : {
    2587   171826674 :   _dof_indices(elem,
    2588             :                p_level,
    2589             :                di,
    2590             :                vg,
    2591             :                vig,
    2592             :                nodes,
    2593             :                n_nodes,
    2594             :                v,
    2595             : #ifdef DEBUG
    2596             :                tot_size,
    2597             : #endif
    2598             :                [](const Elem &,
    2599             :                   unsigned int,
    2600             :                   unsigned int,
    2601             :                   std::vector<dof_id_type> & functor_di,
    2602   766197433 :                   const dof_id_type dof) { functor_di.push_back(dof); });
    2603   171826674 : }
    2604             : 
    2605     2023689 : void DofMap::SCALAR_dof_indices (std::vector<dof_id_type> & di,
    2606             :                                  const unsigned int vn,
    2607             : #ifdef LIBMESH_ENABLE_AMR
    2608             :                                  const bool old_dofs
    2609             : #else
    2610             :                                  const bool
    2611             : #endif
    2612             :                                  ) const
    2613             : {
    2614             :   // dof_indices() is a relatively light-weight function; the cost of
    2615             :   // the logging code itself is roughly on par with the time required
    2616             :   // to call dof_indices().
    2617             :   // LOG_SCOPE("SCALAR_dof_indices()", "DofMap");
    2618             : 
    2619      177293 :   libmesh_assert(this->variable(vn).type().family == SCALAR);
    2620             : 
    2621             : #ifdef LIBMESH_ENABLE_AMR
    2622             :   // If we're asking for old dofs then we'd better have some
    2623      177293 :   if (old_dofs)
    2624           0 :     libmesh_assert_greater_equal(n_old_dofs(), n_SCALAR_dofs());
    2625             : 
    2626     2200982 :   dof_id_type my_idx = old_dofs ?
    2627     2023689 :     this->_first_old_scalar_df[vn] : this->_first_scalar_df[vn];
    2628             : #else
    2629             :   dof_id_type my_idx = this->_first_scalar_df[vn];
    2630             : #endif
    2631             : 
    2632      177293 :   libmesh_assert_not_equal_to(my_idx, DofObject::invalid_id);
    2633             : 
    2634             :   // The number of SCALAR dofs comes from the variable order
    2635     2023689 :   const int n_dofs_vn = this->variable(vn).type().order.get_order();
    2636             : 
    2637     2023689 :   di.resize(n_dofs_vn);
    2638     4047378 :   for (int i = 0; i != n_dofs_vn; ++i)
    2639     2200982 :     di[i] = my_idx++;
    2640     2023689 : }
    2641             : 
    2642             : 
    2643             : 
    2644     1588251 : bool DofMap::semilocal_index (dof_id_type dof_index) const
    2645             : {
    2646             :   // If it's not in the local indices
    2647     1588251 :   if (!this->local_index(dof_index))
    2648             :     {
    2649             :       // and if it's not in the ghost indices, then we're not
    2650             :       // semilocal
    2651     1443072 :       if (!std::binary_search(_send_list.begin(), _send_list.end(), dof_index))
    2652        4558 :         return false;
    2653             :     }
    2654             : 
    2655      118066 :   return true;
    2656             : }
    2657             : 
    2658             : 
    2659             : 
    2660      139004 : bool DofMap::all_semilocal_indices (const std::vector<dof_id_type> & dof_indices_in) const
    2661             : {
    2662             :   // We're all semilocal unless we find a counterexample
    2663     1668261 :   for (const auto & di : dof_indices_in)
    2664     1588251 :     if (!this->semilocal_index(di))
    2665        2279 :       return false;
    2666             : 
    2667        6326 :   return true;
    2668             : }
    2669             : 
    2670             : 
    2671             : 
    2672             : template <typename DofObjectSubclass>
    2673      208693 : bool DofMap::is_evaluable(const DofObjectSubclass & obj,
    2674             :                           unsigned int var_num) const
    2675             : {
    2676             :   // Everything is evaluable on a local object
    2677      219196 :   if (obj.processor_id() == this->processor_id())
    2678       10694 :     return true;
    2679             : 
    2680       17162 :   std::vector<dof_id_type> di;
    2681             : 
    2682      138152 :   if (var_num == libMesh::invalid_uint)
    2683       16710 :     this->dof_indices(&obj, di);
    2684             :   else
    2685      121442 :     this->dof_indices(&obj, di, var_num);
    2686             : 
    2687      138152 :   return this->all_semilocal_indices(di);
    2688             : }
    2689             : 
    2690             : 
    2691             : 
    2692             : #ifdef LIBMESH_ENABLE_AMR
    2693             : 
    2694    11740215 : void DofMap::old_dof_indices (const Elem * const elem,
    2695             :                               std::vector<dof_id_type> & di,
    2696             :                               const unsigned int vn) const
    2697             : {
    2698             :   // dof_indices() is a relatively light-weight function; the cost of
    2699             :   // the logging code itself is roughly on par with the time required
    2700             :   // to call dof_indices().
    2701             :   // LOG_SCOPE("old_dof_indices()", "DofMap");
    2702             : 
    2703     1136813 :   libmesh_assert(elem);
    2704             : 
    2705    11740215 :   const ElemType type              = elem->type();
    2706     2273240 :   const unsigned int sys_num       = this->sys_number();
    2707     1136813 :   const unsigned int n_var_groups  = this->n_variable_groups();
    2708             : #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
    2709     2674147 :   const bool is_inf                = elem->infinite();
    2710             : #endif
    2711             : 
    2712             :   // If we have dof indices stored on the elem, and there's no chance
    2713             :   // that we only have those indices because we were just p refined,
    2714             :   // then we should have old dof indices too.
    2715     1136813 :   libmesh_assert(!elem->has_dofs(sys_num) ||
    2716             :                  elem->p_refinement_flag() == Elem::JUST_REFINED ||
    2717             :                  elem->get_old_dof_object());
    2718             : 
    2719             :   // Clear the DOF indices vector.
    2720     1136813 :   di.clear();
    2721             : 
    2722             :   // Determine the nodes contributing to element elem
    2723     2273626 :   std::vector<const Node *> elem_nodes;
    2724             :   const Node * const * nodes_ptr;
    2725             :   unsigned int n_nodes;
    2726    11740215 :   if (elem->type() == TRI3SUBDIVISION)
    2727             :     {
    2728             :       // Subdivision surface FE require the 1-ring around elem
    2729           0 :       const Tri3Subdivision * sd_elem = static_cast<const Tri3Subdivision *>(elem);
    2730           0 :       MeshTools::Subdivision::find_one_ring(sd_elem, elem_nodes);
    2731           0 :       nodes_ptr = elem_nodes.data();
    2732           0 :       n_nodes = cast_int<unsigned int>(elem_nodes.size());
    2733             :     }
    2734             :   else
    2735             :     {
    2736             :       // All other FE use only the nodes of elem itself
    2737     2273240 :       nodes_ptr = elem->get_nodes();
    2738    11740215 :       n_nodes = elem->n_nodes();
    2739             :     }
    2740             : 
    2741             :   // Get the dof numbers
    2742    24063788 :   for (unsigned int vg=0; vg<n_var_groups; vg++)
    2743             :     {
    2744     1188231 :       const VariableGroup & var = this->variable_group(vg);
    2745     1188231 :       const unsigned int vars_in_group = var.n_variables();
    2746             : 
    2747    24970534 :       for (unsigned int vig=0; vig<vars_in_group; vig++)
    2748             :         {
    2749     2429980 :           const unsigned int v = var.number(vig);
    2750    12646961 :           if ((vn == v) || (vn == libMesh::invalid_uint))
    2751             :             {
    2752    11958227 :               if (var.type().family == SCALAR &&
    2753           0 :                   (!elem ||
    2754           0 :                    var.active_on_subdomain(elem->subdomain_id())))
    2755             :                 {
    2756             :                   // We asked for this variable, so add it to the vector.
    2757           0 :                   std::vector<dof_id_type> di_new;
    2758           0 :                   this->SCALAR_dof_indices(di_new,v,true);
    2759           0 :                   di.insert( di.end(), di_new.begin(), di_new.end());
    2760             :                 }
    2761             :               else
    2762    11958227 :                 if (var.active_on_subdomain(elem->subdomain_id()))
    2763             :                   { // Do this for all the variables if one was not specified
    2764             :                     // or just for the specified variable
    2765             : 
    2766    11912449 :                     FEType fe_type = var.type();
    2767    11912449 :                     const bool add_p_level = fe_type.p_refinement;
    2768             : 
    2769             :                     // Increase the polynomial order on p refined elements,
    2770             :                     // but make sure you get the right polynomial order for
    2771             :                     // the OLD degrees of freedom
    2772     1150659 :                     int p_adjustment = 0;
    2773    13062624 :                     if (elem->p_refinement_flag() == Elem::JUST_REFINED)
    2774             :                       {
    2775        2757 :                         libmesh_assert_greater (elem->p_level(), 0);
    2776        2757 :                         p_adjustment = -1;
    2777             :                       }
    2778    11881057 :                     else if (elem->p_refinement_flag() == Elem::JUST_COARSENED)
    2779             :                       {
    2780          55 :                         p_adjustment = 1;
    2781             :                       }
    2782    11912449 :                     p_adjustment *= add_p_level;
    2783             : 
    2784             :                     // Compute the net amount of "extra" order, including Elem::p_level()
    2785    11912449 :                     int extra_order = int(add_p_level*elem->p_level()) + p_adjustment;
    2786             : 
    2787             :                     const bool extra_hanging_dofs =
    2788    11912449 :                       FEInterface::extra_hanging_dofs(fe_type);
    2789             : 
    2790             :                     const FEInterface::n_dofs_at_node_ptr ndan =
    2791    11912449 :                       FEInterface::n_dofs_at_node_function(fe_type, elem);
    2792             : 
    2793             :                     // Get the node-based DOF numbers
    2794    97857625 :                     for (unsigned int n=0; n<n_nodes; n++)
    2795             :                       {
    2796    85945176 :                         const Node * node = nodes_ptr[n];
    2797     7944739 :                         const DofObject & old_dof_obj = node->get_old_dof_object_ref();
    2798             : 
    2799             :                         // There is a potential problem with h refinement.  Imagine a
    2800             :                         // quad9 that has a linear FE on it.  Then, on the hanging side,
    2801             :                         // it can falsely identify a DOF at the mid-edge node. This is why
    2802             :                         // we call FEInterface instead of node->n_comp() directly.
    2803             :                         const unsigned int nc =
    2804             : #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
    2805    19633434 :                           is_inf ?
    2806       29696 :                           FEInterface::n_dofs_at_node(var.type(), extra_order, elem, n) :
    2807             : #endif
    2808    93858471 :                           ndan (type, var.type().order + extra_order, n);
    2809             : 
    2810    85945176 :                         const int n_comp = old_dof_obj.n_comp_group(sys_num,vg);
    2811             : 
    2812             :                         // If this is a non-vertex on a hanging node with extra
    2813             :                         // degrees of freedom, we use the non-vertex dofs (which
    2814             :                         // come in reverse order starting from the end, to
    2815             :                         // simplify p refinement)
    2816    85945176 :                         if (extra_hanging_dofs && !elem->is_vertex(n))
    2817             :                           {
    2818     8487721 :                             const int dof_offset = n_comp - nc;
    2819             : 
    2820             :                             // We should never have fewer dofs than necessary on a
    2821             :                             // node unless we're getting indices on a parent element
    2822             :                             // or a just-coarsened element
    2823     8487721 :                             if (dof_offset < 0)
    2824             :                               {
    2825           0 :                                 libmesh_assert(!elem->active() || elem->refinement_flag() ==
    2826             :                                                Elem::JUST_COARSENED);
    2827           0 :                                 di.resize(di.size() + nc, DofObject::invalid_id);
    2828             :                               }
    2829             :                             else
    2830    22978599 :                               for (int i=n_comp-1; i>=dof_offset; i--)
    2831             :                                 {
    2832             :                                   const dof_id_type d =
    2833    14490878 :                                     old_dof_obj.dof_number(sys_num, vg, vig, i, n_comp);
    2834             : 
    2835             :                                   // On a newly-expanded subdomain, we
    2836             :                                   // may have some DoFs that didn't
    2837             :                                   // exist in the old system, in which
    2838             :                                   // case we can't assert this:
    2839             :                                   // libmesh_assert_not_equal_to (d, DofObject::invalid_id);
    2840             : 
    2841    14490878 :                                   di.push_back(d);
    2842             :                                 }
    2843             :                           }
    2844             :                         // If this is a vertex or an element without extra hanging
    2845             :                         // dofs, our dofs come in forward order coming from the
    2846             :                         // beginning.  But we still might not have all
    2847             :                         // those dofs on the old_dof_obj, in cases
    2848             :                         // where a subdomain-restricted variable just
    2849             :                         // had its subdomain expanded.
    2850             :                         else
    2851             :                           {
    2852             :                             const unsigned int old_nc =
    2853    77528886 :                               std::min(static_cast<unsigned int>(n_comp), nc);
    2854   155009341 :                             for (unsigned int i=0; i != old_nc; ++i)
    2855             :                               {
    2856             :                                 const dof_id_type d =
    2857    77551886 :                                   old_dof_obj.dof_number(sys_num, vg, vig, i, n_comp);
    2858             : 
    2859     7232065 :                                 libmesh_assert_not_equal_to (d, DofObject::invalid_id);
    2860             : 
    2861    77551886 :                                 di.push_back(d);
    2862             :                               }
    2863    77457455 :                             for (unsigned int i=old_nc; i != nc; ++i)
    2864           0 :                               di.push_back(DofObject::invalid_id);
    2865             :                           }
    2866             :                       }
    2867             : 
    2868             :                     // If there are any element-based DOF numbers, get them
    2869             :                     const unsigned int nc =
    2870    11912449 :                       FEInterface::n_dofs_per_elem(fe_type, extra_order, elem);
    2871             : 
    2872    11912449 :                     if (nc != 0)
    2873             :                       {
    2874      162538 :                         const DofObject & old_dof_obj = elem->get_old_dof_object_ref();
    2875             : 
    2876             :                         const unsigned int n_comp =
    2877      162538 :                           old_dof_obj.n_comp_group(sys_num,vg);
    2878             : 
    2879     1908223 :                         if (old_dof_obj.n_systems() > sys_num &&
    2880             :                             nc <= n_comp)
    2881             :                           {
    2882             : 
    2883     7222906 :                             for (unsigned int i=0; i<nc; i++)
    2884             :                               {
    2885             :                                 const dof_id_type d =
    2886     5314683 :                                   old_dof_obj.dof_number(sys_num, vg, vig, i, n_comp);
    2887             : 
    2888     5314683 :                                 di.push_back(d);
    2889             :                               }
    2890             :                           }
    2891             :                         else
    2892             :                           {
    2893             :                             // We should never have fewer dofs than
    2894             :                             // necessary on an element unless we're
    2895             :                             // getting indices on a parent element, a
    2896             :                             // just-coarsened element ... or a
    2897             :                             // subdomain-restricted variable with a
    2898             :                             // just-expanded subdomain
    2899             :                             // libmesh_assert(!elem->active() || fe_type.family == LAGRANGE ||
    2900             :                             //                 elem->refinement_flag() == Elem::JUST_COARSENED);
    2901           0 :                             di.resize(di.size() + nc, DofObject::invalid_id);
    2902             :                           }
    2903             :                       }
    2904             :                   }
    2905             :             }
    2906             :         } // end loop over variables within group
    2907             :     } // end loop over variable groups
    2908    11740215 : }
    2909             : 
    2910             : #endif // LIBMESH_ENABLE_AMR
    2911             : 
    2912             : 
    2913             : #ifdef LIBMESH_ENABLE_CONSTRAINTS
    2914             : 
    2915     8877793 : void DofMap::find_connected_dofs (std::vector<dof_id_type> & elem_dofs) const
    2916             : {
    2917             :   typedef std::set<dof_id_type> RCSet;
    2918             : 
    2919             :   // First insert the DOFS we already depend on into the set.
    2920     9743462 :   RCSet dof_set (elem_dofs.begin(), elem_dofs.end());
    2921             : 
    2922      865669 :   bool done = true;
    2923             : 
    2924             :   // Next insert any dofs those might be constrained in terms
    2925             :   // of.  Note that in this case we may not be done:  Those may
    2926             :   // in turn depend on others.  So, we need to repeat this process
    2927             :   // in that case until the system depends only on unconstrained
    2928             :   // degrees of freedom.
    2929    59949608 :   for (const auto & dof : elem_dofs)
    2930    51071815 :     if (this->is_constrained_dof(dof))
    2931             :       {
    2932             :         // If the DOF is constrained
    2933             :         DofConstraints::const_iterator
    2934      632735 :           pos = _dof_constraints.find(dof);
    2935             : 
    2936      632735 :         libmesh_assert (pos != _dof_constraints.end());
    2937             : 
    2938      632735 :         const DofConstraintRow & constraint_row = pos->second;
    2939             : 
    2940             :         // adaptive p refinement currently gives us lots of empty constraint
    2941             :         // rows - we should optimize those DoFs away in the future.  [RHS]
    2942             :         //libmesh_assert (!constraint_row.empty());
    2943             : 
    2944             :         // Add the DOFs this dof is constrained in terms of.
    2945             :         // note that these dofs might also be constrained, so
    2946             :         // we will need to call this function recursively.
    2947    20431824 :         for (const auto & pr : constraint_row)
    2948    12478286 :           if (!dof_set.count (pr.first))
    2949             :             {
    2950     3381894 :               dof_set.insert (pr.first);
    2951      322451 :               done = false;
    2952             :             }
    2953             :       }
    2954             : 
    2955             : 
    2956             :   // If not done then we need to do more work
    2957             :   // (obviously :-) )!
    2958     8877793 :   if (!done)
    2959             :     {
    2960             :       // Fill the vector with the contents of the set
    2961      125842 :       elem_dofs.clear();
    2962     1061044 :       elem_dofs.insert (elem_dofs.end(),
    2963      377519 :                         dof_set.begin(), dof_set.end());
    2964             : 
    2965             : 
    2966             :       // May need to do this recursively.  It is possible
    2967             :       // that we just replaced a constrained DOF with another
    2968             :       // constrained DOF.
    2969     1186879 :       this->find_connected_dofs (elem_dofs);
    2970             : 
    2971             :     } // end if (!done)
    2972     8877793 : }
    2973             : 
    2974             : #endif // LIBMESH_ENABLE_CONSTRAINTS
    2975             : 
    2976             : 
    2977             : 
    2978           0 : void DofMap::print_info(std::ostream & os) const
    2979             : {
    2980           0 :   os << this->get_info();
    2981           0 : }
    2982             : 
    2983             : 
    2984             : 
    2985       17958 : std::string DofMap::get_info() const
    2986             : {
    2987       18954 :   std::ostringstream os;
    2988             : 
    2989             :   // If we didn't calculate the exact sparsity pattern, the threaded
    2990             :   // sparsity pattern assembly may have just given us an upper bound
    2991             :   // on sparsity.
    2992         498 :   const char * may_equal = " <= ";
    2993             : 
    2994             :   // If we calculated the exact sparsity pattern, then we can report
    2995             :   // exact bandwidth figures:
    2996       36130 :   for (const auto & mat : _matrices)
    2997       18172 :     if (mat->need_full_sparsity_pattern())
    2998           0 :       may_equal = " = ";
    2999             : 
    3000       17958 :   dof_id_type max_n_nz = 0, max_n_oz = 0;
    3001       17958 :   long double avg_n_nz = 0, avg_n_oz = 0;
    3002             : 
    3003       17958 :   if (_sp)
    3004             :     {
    3005     4728624 :       for (const auto & val : _sp->get_n_nz())
    3006             :         {
    3007     4712775 :           max_n_nz = std::max(max_n_nz, val);
    3008     4712775 :           avg_n_nz += val;
    3009             :         }
    3010             : 
    3011       15849 :       std::size_t n_nz_size = _sp->get_n_nz().size();
    3012             : 
    3013       15849 :       this->comm().max(max_n_nz);
    3014       15849 :       this->comm().sum(avg_n_nz);
    3015       15849 :       this->comm().sum(n_nz_size);
    3016             : 
    3017       15849 :       avg_n_nz /= std::max(n_nz_size,std::size_t(1));
    3018             : 
    3019     4728624 :       for (const auto & val : _sp->get_n_oz())
    3020             :         {
    3021     4712775 :           max_n_oz = std::max(max_n_oz, val);
    3022     4712775 :           avg_n_oz += val;
    3023             :         }
    3024             : 
    3025       15849 :       std::size_t n_oz_size = _sp->get_n_oz().size();
    3026             : 
    3027       15849 :       this->comm().max(max_n_oz);
    3028       15849 :       this->comm().sum(avg_n_oz);
    3029       15849 :       this->comm().sum(n_oz_size);
    3030             : 
    3031       15849 :       avg_n_oz /= std::max(n_oz_size,std::size_t(1));
    3032             :     }
    3033             : 
    3034             :   os << "    DofMap Sparsity\n      Average  On-Processor Bandwidth"
    3035       18456 :      << may_equal << avg_n_nz << '\n';
    3036             : 
    3037             :   os << "      Average Off-Processor Bandwidth"
    3038       18456 :      << may_equal << avg_n_oz << '\n';
    3039             : 
    3040             :   os << "      Maximum  On-Processor Bandwidth"
    3041       18456 :      << may_equal << max_n_nz << '\n';
    3042             : 
    3043             :   os << "      Maximum Off-Processor Bandwidth"
    3044       17958 :      << may_equal << max_n_oz << std::endl;
    3045             : 
    3046             : #ifdef LIBMESH_ENABLE_CONSTRAINTS
    3047             : 
    3048       17958 :   std::size_t n_constraints = 0, max_constraint_length = 0,
    3049       17958 :     n_rhss = 0;
    3050       17958 :   long double avg_constraint_length = 0.;
    3051             : 
    3052      656939 :   for (const auto & [constrained_dof, row] : _dof_constraints)
    3053             :     {
    3054             :       // Only count local constraints, then sum later
    3055      638981 :       if (!this->local_index(constrained_dof))
    3056      137784 :         continue;
    3057             : 
    3058      501197 :       std::size_t rowsize = row.size();
    3059             : 
    3060      501197 :       max_constraint_length = std::max(max_constraint_length,
    3061       42636 :                                        rowsize);
    3062      501197 :       avg_constraint_length += rowsize;
    3063      501197 :       n_constraints++;
    3064             : 
    3065       42636 :       if (_primal_constraint_values.count(constrained_dof))
    3066       44535 :         n_rhss++;
    3067             :     }
    3068             : 
    3069       17958 :   this->comm().sum(n_constraints);
    3070       17958 :   this->comm().sum(n_rhss);
    3071       17958 :   this->comm().sum(avg_constraint_length);
    3072       17958 :   this->comm().max(max_constraint_length);
    3073             : 
    3074       17460 :   os << "    DofMap Constraints\n      Number of DoF Constraints = "
    3075       17958 :      << n_constraints;
    3076       17958 :   if (n_rhss)
    3077             :     os << '\n'
    3078        4147 :        << "      Number of Heterogenous Constraints= " << n_rhss;
    3079       17958 :   if (n_constraints)
    3080             :     {
    3081       10051 :       avg_constraint_length /= n_constraints;
    3082             : 
    3083             :       os << '\n'
    3084       10337 :          << "      Average DoF Constraint Length= " << avg_constraint_length;
    3085             :     }
    3086             : 
    3087             : #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
    3088         996 :   std::size_t n_node_constraints = 0, max_node_constraint_length = 0,
    3089         996 :     n_node_rhss = 0;
    3090         996 :   long double avg_node_constraint_length = 0.;
    3091             : 
    3092       36170 :   for (const auto & [node, pr] : _node_constraints)
    3093             :     {
    3094             :       // Only count local constraints, then sum later
    3095       52761 :       if (node->processor_id() != this->processor_id())
    3096        6572 :         continue;
    3097             : 
    3098       14301 :       const NodeConstraintRow & row = pr.first;
    3099       28602 :       std::size_t rowsize = row.size();
    3100             : 
    3101       28602 :       max_node_constraint_length = std::max(max_node_constraint_length,
    3102       14301 :                                             rowsize);
    3103       28602 :       avg_node_constraint_length += rowsize;
    3104       28602 :       n_node_constraints++;
    3105             : 
    3106       14301 :       if (pr.second != Point(0))
    3107           0 :         n_node_rhss++;
    3108             :     }
    3109             : 
    3110         996 :   this->comm().sum(n_node_constraints);
    3111         996 :   this->comm().sum(n_node_rhss);
    3112         996 :   this->comm().sum(avg_node_constraint_length);
    3113         996 :   this->comm().max(max_node_constraint_length);
    3114             : 
    3115         996 :   os << "\n      Number of Node Constraints = " << n_node_constraints;
    3116         996 :   if (n_node_rhss)
    3117             :     os << '\n'
    3118           0 :        << "      Number of Heterogenous Node Constraints= " << n_node_rhss;
    3119         996 :   if (n_node_constraints)
    3120             :     {
    3121         200 :       avg_node_constraint_length /= n_node_constraints;
    3122         100 :       os << "\n      Maximum Node Constraint Length= " << max_node_constraint_length
    3123             :          << '\n'
    3124         400 :          << "      Average Node Constraint Length= " << avg_node_constraint_length;
    3125             :     }
    3126             : #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS
    3127             : 
    3128         498 :   os << std::endl;
    3129             : 
    3130             : #endif // LIBMESH_ENABLE_CONSTRAINTS
    3131             : 
    3132       18456 :   return os.str();
    3133       16962 : }
    3134             : 
    3135         560 : void DofMap::create_static_condensation(MeshBase & mesh, System & sys)
    3136             : {
    3137        1088 :   _sc = std::make_unique<StaticCondensationDofMap>(mesh, sys, *this);
    3138         560 : }
    3139             : 
    3140      272787 : void DofMap::reinit_static_condensation()
    3141             : {
    3142      272787 :   if (_sc)
    3143        4130 :     _sc->reinit();
    3144      272787 : }
    3145             : 
    3146      273574 : unsigned int DofMap::add_variable(System & sys,
    3147             :                                   std::string_view var,
    3148             :                                   const FEType & type,
    3149             :                                   const std::set<subdomain_id_type> * const active_subdomains)
    3150             : {
    3151        7744 :   parallel_object_only(); // Not strictly needed, but the only safe way to keep in sync
    3152             : 
    3153        7744 :   libmesh_assert(this->comm().verify(std::string(var)));
    3154        7744 :   libmesh_assert(this->comm().verify(type));
    3155        7744 :   libmesh_assert(this->comm().verify((active_subdomains == nullptr)));
    3156             : 
    3157        7744 :   if (active_subdomains)
    3158         148 :     libmesh_assert(this->comm().verify(active_subdomains->size()));
    3159             : 
    3160             :   // Make sure the variable isn't there already
    3161             :   // or if it is, that it's the type we want
    3162      350255 :   for (auto v : make_range(this->n_vars()))
    3163       77077 :     if (this->variable_name(v) == var)
    3164             :       {
    3165           0 :         if (this->variable_type(v) == type)
    3166             :           {
    3167             :             // Check whether the existing variable's active subdomains also matches
    3168             :             // the incoming variable's active subdomains. If they don't match, then
    3169             :             // either it is an error by the user or the user is trying to change the
    3170             :             // subdomain restriction after the variable has already been added, which
    3171             :             // is not supported.
    3172         396 :             const Variable & existing_var = this->variable(v);
    3173             : 
    3174             :             // Check whether active_subdomains is not provided/empty and the existing_var is
    3175             :             // implicitly_active()
    3176         396 :             bool check1 = (!active_subdomains || active_subdomains->empty()) &&
    3177           0 :                           existing_var.implicitly_active();
    3178             : 
    3179             :             // Check if the provided active_subdomains is equal to the existing_var's
    3180             :             // active_subdomains
    3181             :             bool check2 =
    3182         396 :                 (active_subdomains && (*active_subdomains == existing_var.active_subdomains()));
    3183             : 
    3184             :             // If either of these checks passed, then we already have this variable
    3185         396 :             if (check1 || check2)
    3186           0 :               return _variables[v].number();
    3187             :           }
    3188             : 
    3189           0 :         libmesh_error_msg("ERROR: incompatible variable "
    3190             :                           << var << " has already been added for this system!");
    3191             :       }
    3192             : 
    3193        7744 :   libmesh_assert(!sys.is_initialized());
    3194             : 
    3195      273178 :   if (this->n_variable_groups())
    3196             :     {
    3197             :       // Optimize for VariableGroups here - if the user is adding multiple
    3198             :       // variables of the same FEType and subdomain restriction, catch
    3199             :       // that here and add them as members of the same VariableGroup.
    3200             :       //
    3201             :       // start by setting this flag to whatever the user has requested
    3202             :       // and then consider the conditions which should negate it.
    3203        1816 :       bool should_be_in_vg = this->identify_variable_groups();
    3204             : 
    3205         908 :       VariableGroup & vg = _variable_groups.back();
    3206             : 
    3207             :       // get a pointer to their subdomain restriction, if any.
    3208             :       const std::set<subdomain_id_type> * const their_active_subdomains(
    3209       32411 :           vg.implicitly_active() ? nullptr : &vg.active_subdomains());
    3210             : 
    3211             :       // Different types?
    3212        2191 :       if (vg.type() != type)
    3213         288 :         should_be_in_vg = false;
    3214             : 
    3215             :       // they are restricted, we aren't?
    3216       32515 :       if (their_active_subdomains &&
    3217        3674 :           (!active_subdomains || (active_subdomains && active_subdomains->empty())))
    3218           0 :         should_be_in_vg = false;
    3219             : 
    3220             :       // they aren't restricted, we are?
    3221       32411 :       if (!their_active_subdomains && (active_subdomains && !active_subdomains->empty()))
    3222           2 :         should_be_in_vg = false;
    3223             : 
    3224       32411 :       if (their_active_subdomains && active_subdomains)
    3225             :         // restricted to different sets?
    3226        3674 :         if (*their_active_subdomains != *active_subdomains)
    3227          52 :           should_be_in_vg = false;
    3228             : 
    3229             :       // OK, after all that, append the variable to the vg if none of the conditions
    3230             :       // were violated
    3231       30623 :       if (should_be_in_vg)
    3232             :         {
    3233       21024 :           const unsigned int vn = this->n_vars();
    3234             : 
    3235       20438 :           std::string varstr(var);
    3236             : 
    3237       21024 :           _variable_numbers[varstr] = vn;
    3238       21024 :           vg.append(std::move(varstr));
    3239       42048 :           _variables.push_back(vg(vg.n_variables() - 1));
    3240       21024 :           const unsigned int vgn = _variable_groups.size() - 1;
    3241       21024 :           _variable_group_numbers.push_back(vgn);
    3242         586 :           _var_to_vg.emplace(vn, vgn);
    3243             : 
    3244        1172 :           return vn;
    3245             :         }
    3246             :     }
    3247             : 
    3248             :   // otherwise, fall back to adding a single variable group
    3249      252154 :   return this->add_variables(
    3250      742146 :       sys, std::vector<std::string>(1, std::string(var)), type, active_subdomains);
    3251             : }
    3252             : 
    3253      252793 : unsigned int DofMap::add_variables(System & sys,
    3254             :                                    const std::vector<std::string> & vars,
    3255             :                                    const FEType & type,
    3256             :                                    const std::set<subdomain_id_type> * const active_subdomains)
    3257             : {
    3258        7176 :   parallel_object_only(); // Not strictly needed, but the only safe way to keep in sync
    3259             : 
    3260        7176 :   libmesh_assert(!sys.is_initialized());
    3261             : 
    3262        7176 :   libmesh_assert(this->comm().verify(vars.size()));
    3263        7176 :   libmesh_assert(this->comm().verify(type));
    3264        7176 :   libmesh_assert(this->comm().verify((active_subdomains == nullptr)));
    3265             : 
    3266        7176 :   if (active_subdomains)
    3267          96 :     libmesh_assert(this->comm().verify(active_subdomains->size()));
    3268             : 
    3269             :   // Make sure the variable isn't there already
    3270             :   // or if it is, that it's the type we want
    3271     7606083 :   for (auto ovar : vars)
    3272             :     {
    3273      207190 :       libmesh_assert(this->comm().verify(ovar));
    3274             : 
    3275     7375997 :       for (auto v : make_range(this->n_vars()))
    3276       22063 :         if (this->variable_name(v) == ovar)
    3277             :           {
    3278           0 :             if (this->variable_type(v) == type)
    3279           0 :               return _variables[v].number();
    3280             : 
    3281           0 :             libmesh_error_msg("ERROR: incompatible variable "
    3282             :                               << ovar << " has already been added for this system!");
    3283             :           }
    3284             :     }
    3285             : 
    3286      252793 :   if (this->n_variable_groups())
    3287             :     {
    3288             :       // Optimize for VariableGroups here - if the user is adding multiple
    3289             :       // variables of the same FEType and subdomain restriction, catch
    3290             :       // that here and add them as members of the same VariableGroup.
    3291             :       //
    3292             :       // start by setting this flag to whatever the user has requested
    3293             :       // and then consider the conditions which should negate it.
    3294         644 :       bool should_be_in_vg = this->identify_variable_groups();
    3295             : 
    3296         322 :       VariableGroup & vg = _variable_groups.back();
    3297             : 
    3298             :       // get a pointer to their subdomain restriction, if any.
    3299             :       const std::set<subdomain_id_type> * const their_active_subdomains(
    3300       11387 :           vg.implicitly_active() ? nullptr : &vg.active_subdomains());
    3301             : 
    3302             :       // Different types?
    3303         761 :       if (vg.type() != type)
    3304         288 :         should_be_in_vg = false;
    3305             : 
    3306             :       // they are restricted, we aren't?
    3307       11439 :       if (their_active_subdomains &&
    3308        1840 :           (!active_subdomains || (active_subdomains && active_subdomains->empty())))
    3309           0 :         should_be_in_vg = false;
    3310             : 
    3311             :       // they aren't restricted, we are?
    3312       11387 :       if (!their_active_subdomains && (active_subdomains && !active_subdomains->empty()))
    3313           2 :         should_be_in_vg = false;
    3314             : 
    3315       11387 :       if (their_active_subdomains && active_subdomains)
    3316             :         // restricted to different sets?
    3317        1840 :         if (*their_active_subdomains != *active_subdomains)
    3318          52 :           should_be_in_vg = false;
    3319             : 
    3320             :       // If after all that none of the conditions were violated,
    3321             :       // append the variables to the vg and we're done
    3322        9599 :       if (should_be_in_vg)
    3323             :         {
    3324           0 :           unsigned int vn = this->n_vars();
    3325           0 :           const unsigned int vgn = _variable_groups.size() - 1;
    3326             : 
    3327           0 :           for (auto ovar : vars)
    3328             :             {
    3329           0 :               vn = this->n_vars();
    3330             : 
    3331           0 :               vg.append(ovar);
    3332             : 
    3333           0 :               _variables.push_back(vg(vg.n_variables() - 1));
    3334           0 :               _variable_numbers[ovar] = vn;
    3335           0 :               _variable_group_numbers.push_back(vgn);
    3336           0 :               _var_to_vg.emplace(vn, vgn);
    3337             :             }
    3338           0 :           return vn;
    3339             :         }
    3340             :     }
    3341             : 
    3342        7176 :   const unsigned int curr_n_vars = this->n_vars();
    3343             : 
    3344      252793 :   const unsigned int next_first_component = this->n_components(sys.get_mesh());
    3345             : 
    3346             :   // We weren't able to add to an existing variable group, so
    3347             :   // add a new variable group to the list
    3348      252793 :   _variable_groups.push_back(
    3349             :       (active_subdomains == nullptr)
    3350      505586 :           ? VariableGroup(&sys, vars, curr_n_vars, next_first_component, type)
    3351             :           : VariableGroup(&sys, vars, curr_n_vars, next_first_component, type, *active_subdomains));
    3352             : 
    3353        7176 :   const VariableGroup & vg(_variable_groups.back());
    3354      252793 :   const unsigned int vgn = _variable_groups.size() - 1;
    3355             : 
    3356             :   // Add each component of the group individually
    3357     7606083 :   for (auto v : make_range(vars.size()))
    3358             :     {
    3359     7353290 :       const unsigned int vn = curr_n_vars + v;
    3360    14499390 :       _variables.push_back(vg(v));
    3361     7560480 :       _variable_numbers[vars[v]] = vn;
    3362     7353290 :       _variable_group_numbers.push_back(vgn);
    3363      207190 :       _var_to_vg.emplace(vn, vgn);
    3364             :     }
    3365             : 
    3366        7176 :   libmesh_assert_equal_to((curr_n_vars + vars.size()), this->n_vars());
    3367             : 
    3368             :   // BSK - Defer this now to System::init_data() so we can detect
    3369             :   // VariableGroups 12/28/2012
    3370             :   // // Add the variable group to the _dof_map
    3371             :   // _dof_map->add_variable_group (vg);
    3372             : 
    3373             :   // Return the number of the new variable
    3374      267145 :   return cast_int<unsigned int>(curr_n_vars + vars.size() - 1);
    3375             : }
    3376             : 
    3377         568 : unsigned int DofMap::add_variable_array (System & sys,
    3378             :                                          const std::vector<std::string> & vars,
    3379             :                                          const FEType & type,
    3380             :                                          const std::set<subdomain_id_type> * const active_subdomains)
    3381             : {
    3382          32 :   const unsigned int count = cast_int<unsigned int>(vars.size());
    3383         568 :   const unsigned int last_var = this->add_variables(sys, vars, type, active_subdomains);
    3384         568 :   const unsigned int first_var = last_var + 1 - count;
    3385         568 :   _array_variables.push_back({first_var, first_var + count});
    3386         568 :   return last_var;
    3387             : }
    3388             : 
    3389         493 : void DofMap::get_all_variable_numbers(std::vector<unsigned int> & all_variable_numbers) const
    3390             : {
    3391         493 :   all_variable_numbers.resize(n_vars());
    3392             : 
    3393          14 :   unsigned int count = 0;
    3394        1266 :   for (auto vn : _variable_numbers)
    3395         795 :     all_variable_numbers[count++] = vn.second;
    3396         493 : }
    3397             : 
    3398             : template LIBMESH_EXPORT bool DofMap::is_evaluable<Elem>(const Elem &, unsigned int) const;
    3399             : template LIBMESH_EXPORT bool DofMap::is_evaluable<Node>(const Node &, unsigned int) const;
    3400             : 
    3401             : } // namespace libMesh

Generated by: LCOV version 1.14