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

Generated by: LCOV version 1.14