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