libMesh
Loading...
Searching...
No Matches
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
57namespace libMesh
58{
59
60// ------------------------------------------------------------
61// DofMap member functions
62std::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
138DofMap::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{
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
218{
219 if (_periodic_boundaries->count(boundaryid) != 0)
220 return true;
221
222 return false;
223}
224
225#endif
226
227void 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
234void 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
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
317template <typename iterator_type>
318void 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;
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
469 (MeshBase & mesh,
470 const std::map<const Node *, std::set<subdomain_id_type>> &
471 constraining_subdomains)
472{
473 libmesh_assert (mesh.is_prepared());
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
527 (mesh.element_stored_range(),
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 =
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 "
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
856void DofMap::invalidate_dofs(MeshBase & mesh) const
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
871void DofMap::clear()
872{
873 DofMapBase::clear();
874
875 // we don't want to clear
876 // the coupling matrix!
877 // It should not change...
878 //_dof_coupling->clear();
879 //
880 // But it would be inconsistent to leave our coupling settings
881 // through a clear()...
882 _dof_coupling = nullptr;
883
884 // Reset ghosting functor statuses
885 {
886 for (const auto & gf : _coupling_functors)
887 {
888 libmesh_assert(gf);
889 _mesh.remove_ghosting_functor(*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
898 this->add_coupling_functor(*_default_coupling);
899 }
900
901
902 {
903 for (const auto & gf : _algebraic_ghosting_functors)
904 {
905 libmesh_assert(gf);
906 _mesh.remove_ghosting_functor(*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);
914 this->add_algebraic_ghosting_functor(*_default_evaluating);
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();
927 need_full_sparsity_pattern = false;
928
929#ifdef LIBMESH_ENABLE_AMR
930
931 _dof_constraints.clear();
932 _stashed_dof_constraints.clear();
933 _primal_constraint_values.clear();
934 _adjoint_constraint_values.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
949std::size_t DofMap::distribute_dofs (MeshBase & mesh)
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 =
972 this->calculate_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)
992 this->distribute_local_dofs_node_major
993 (next_free_dof, mesh, constraining_subdomains);
994 else
995 this->distribute_local_dofs_var_major
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)
1009 this->distribute_local_dofs_node_major
1010 (next_free_dof, mesh, constraining_subdomains);
1011 else
1012 this->distribute_local_dofs_var_major
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(),
1027 mesh, &DofMap::node_ptr);
1028
1029 this->set_nonlocal_dof_objects(mesh.elements_begin(),
1030 mesh.elements_end(),
1031 mesh, &DofMap::elem_ptr);
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.
1041 MeshTools::libmesh_assert_valid_dof_ids(mesh, sys_num);
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
1076 _first_old_scalar_df = _first_scalar_df;
1077#endif
1078 _first_scalar_df.clear();
1079 _first_scalar_df.resize(this->n_variables(), DofObject::invalid_id);
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
1120template <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>>
1122void DofMap::local_variable_indices(T & idx,
1123 const MeshBase & mesh,
1124 unsigned int var_num) const
1125{
1126 // Only used if T == dof_id_type to keep track of the greatest dof we've seen
1127 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
1246template void DofMap::local_variable_indices(dof_id_type &,
1247 const MeshBase &,
1248 unsigned int) const;
1249
1250template void DofMap::local_variable_indices(std::vector<dof_id_type> &,
1251 const MeshBase &,
1252 unsigned int) const;
1253
1254
1255std::map<const Node *, std::set<subdomain_id_type>>
1256DofMap::calculate_constraining_subdomains()
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
1289void DofMap::distribute_local_dofs_node_major
1290 (dof_id_type & next_free_dof,
1291 MeshBase & mesh,
1292 const std::map<const Node *, std::set<subdomain_id_type>> &
1293 constraining_subdomains)
1294{
1295 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) ==
1348 DofObject::invalid_id))
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),
1370 DofObject::invalid_id);
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
1417void DofMap::distribute_local_dofs_var_major
1418 (dof_id_type & next_free_dof,
1419 MeshBase & mesh,
1420 const std::map<const Node *, std::set<subdomain_id_type>> &
1421 constraining_subdomains)
1422{
1423 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) ==
1485 DofObject::invalid_id))
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),
1498 DofObject::invalid_id);
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
1539void DofMap::distribute_scalar_dofs(dof_id_type & next_free_dof)
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
1563void DofMap::assert_no_nodes_missed(MeshBase & mesh)
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
1583void
1584DofMap::
1585merge_ghost_functor_outputs(GhostingFunctor::map_type & elements_to_ghost,
1586 CouplingMatricesSet & temporary_coupling_matrices,
1587 const GhostingFunctorIterator & gf_begin,
1588 const GhostingFunctorIterator & gf_end,
1589 const MeshBase::const_element_iterator & elems_begin,
1590 const MeshBase::const_element_iterator & elems_end,
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
1687void DofMap::add_neighbors_to_send_list(MeshBase & mesh)
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
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,
1710 this->algebraic_ghosting_functors_begin(),
1711 this->algebraic_ghosting_functors_end(),
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
1835void DofMap::prepare_send_list ()
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
1844 if (_extra_send_list_function)
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
1854 _extra_send_list_function(_send_list, _extra_send_list_context);
1855 }
1856
1857 if (_augment_send_list)
1858 _augment_send_list->augment_send_list (_send_list);
1859
1860 // First sort the send list. After this
1861 // duplicated elements will be adjacent in the
1862 // vector
1863 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
1877void DofMap::reinit_send_list (MeshBase & mesh)
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
1891void DofMap::set_implicit_neighbor_dofs(bool implicit_neighbor_dofs)
1892{
1893 _implicit_neighbor_dofs_initialized = true;
1894 _implicit_neighbor_dofs = implicit_neighbor_dofs;
1895}
1896
1897void DofMap::set_verify_dirichlet_bc_consistency(bool val)
1898{
1899 _verify_dirichlet_bc_consistency = val;
1900}
1901
1902
1903bool 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.
1930 if (_implicit_neighbor_dofs_initialized)
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))
1948 if (FEInterface::get_continuity(this->variable_type(var)) != DISCONTINUOUS)
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
1960void DofMap::compute_sparsity(const MeshBase & mesh)
1961{
1962 _sp = this->build_sparsity(mesh, this->_constrained_sparsity_construction);
1963
1964 // It is possible that some \p SparseMatrix implementations want to
1965 // see the sparsity pattern before we throw it away. If so, we
1966 // share a view of its arrays, and we pass it in to the matrices.
1967 for (const auto & mat : _matrices)
1968 {
1969 mat->attach_sparsity_pattern (*_sp);
1970 if (need_full_sparsity_pattern)
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.
1975 if (!need_full_sparsity_pattern)
1976 _sp->clear_full_sparsity();
1977}
1978
1979
1980
1981void DofMap::clear_sparsity()
1982{
1983 _sp.reset();
1984}
1985
1986
1987
1988void DofMap::remove_default_ghosting()
1989{
1990 this->remove_coupling_functor(this->default_coupling());
1991 this->remove_algebraic_ghosting_functor(this->default_algebraic_ghosting());
1992}
1993
1994
1995
1996void DofMap::add_default_ghosting()
1997{
1998 this->add_coupling_functor(this->default_coupling());
1999 this->add_algebraic_ghosting_functor(this->default_algebraic_ghosting());
2000}
2001
2002
2003
2004void
2005DofMap::add_coupling_functor(GhostingFunctor & coupling_functor,
2006 bool to_mesh)
2007{
2008 // We used to implicitly support duplicate inserts to std::set
2009#ifdef LIBMESH_ENABLE_DEPRECATED
2010 _coupling_functors.erase
2011 (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
2031void
2032DofMap::remove_coupling_functor(GhostingFunctor & coupling_functor)
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
2061void
2062DofMap::add_algebraic_ghosting_functor(GhostingFunctor & evaluable_functor,
2063 bool to_mesh)
2064{
2065 // We used to implicitly support duplicate inserts to std::set
2066#ifdef LIBMESH_ENABLE_DEPRECATED
2067 _algebraic_ghosting_functors.erase
2068 (std::remove(_algebraic_ghosting_functors.begin(),
2069 _algebraic_ghosting_functors.end(),
2070 &evaluable_functor),
2071 _algebraic_ghosting_functors.end());
2072#endif
2073
2074 // We shouldn't have two copies of the same functor
2075 libmesh_assert(std::find(_algebraic_ghosting_functors.begin(),
2076 _algebraic_ghosting_functors.end(),
2077 &evaluable_functor) ==
2078 _algebraic_ghosting_functors.end());
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
2088void
2089DofMap::remove_algebraic_ghosting_functor(GhostingFunctor & evaluable_functor)
2090{
2091 auto raw_it = std::find(_algebraic_ghosting_functors.begin(),
2092 _algebraic_ghosting_functors.end(),
2093 &evaluable_functor);
2094
2095#ifndef LIBMESH_ENABLE_DEPRECATED
2096 // We shouldn't be trying to remove a functor that isn't there
2097 libmesh_assert(raw_it != _algebraic_ghosting_functors.end());
2098#else
2099 // Our old API supported trying to remove a functor that isn't there
2100 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(),
2106 _algebraic_ghosting_functors.end(),
2107 &evaluable_functor) ==
2108 _algebraic_ghosting_functors.end());
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
2119void DofMap::extract_local_vector (const NumericVector<Number> & Ug,
2120 const std::vector<dof_id_type> & dof_indices_in,
2121 DenseVectorBase<Number> & Ue) const
2122{
2123 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
2201void 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
2317void 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
2339void 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
2351void 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
2362void 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
2415void 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
2466void 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
2478void 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
2491void 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 =
2515 FEInterface::extra_hanging_dofs(fe_type);
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
2572void
2573DofMap::_dof_indices(const Elem & elem,
2574 int p_level,
2575 std::vector<dof_id_type> & di,
2576 const unsigned int vg,
2577 const unsigned int vig,
2578 const Node * const * nodes,
2579 unsigned int n_nodes,
2580 const unsigned int v
2581#ifdef DEBUG
2582 ,
2583 std::size_t & tot_size
2584#endif
2585) const
2586{
2587 _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
2605void 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
2644bool 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
2660bool 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
2672template <typename DofObjectSubclass>
2673bool 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
2694void 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) ||
2716 elem->p_refinement_flag() == Elem::JUST_REFINED ||
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 =
2788 FEInterface::extra_hanging_dofs(fe_type);
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() ==
2826 Elem::JUST_COARSENED);
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
2915void 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
2978void DofMap::print_info(std::ostream & os) const
2979{
2980 os << this->get_info();
2981}
2982
2983
2984
2985std::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
3135void DofMap::create_static_condensation(MeshBase & mesh, System & sys)
3136{
3137 _sc = std::make_unique<StaticCondensationDofMap>(mesh, sys, *this);
3138}
3139
3140void DofMap::reinit_static_condensation()
3141{
3142 if (_sc)
3143 _sc->reinit();
3144}
3145
3146unsigned 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
3253unsigned 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
3377unsigned int DofMap::add_variable_array (System & sys,
3378 const std::vector<std::string> & vars,
3379 const FEType & type,
3380 const std::set<subdomain_id_type> * const active_subdomains)
3381{
3382 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
3389void 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
3398template LIBMESH_EXPORT bool DofMap::is_evaluable<Elem>(const Elem &, unsigned int) const;
3399template LIBMESH_EXPORT bool DofMap::is_evaluable<Node>(const Node &, unsigned int) const;
3400
3401} // namespace libMesh
unsigned int n_vars
void ErrorVector unsigned int
void max(const T &r, T &o, Request &req) const
This proxy class acts like a container of indices from a single coupling row.
This class defines a coupling matrix.
This class implements the default algebraic coupling in libMesh: elements couple to themselves,...
Defines a dense matrix for use in Finite Element-type computations.
Defines an abstract dense vector base class for use in Finite Element-type computations.
virtual void zero()=0
Set every element in the vector to 0.
virtual T el(const unsigned int i) const =0
virtual unsigned int size() const =0
Defines a dense vector for use in Finite Element-type computations.
We're using a class instead of a typedef to allow forward declarations and future flexibility.
This base class provides a minimal set of interfaces for satisfying user requests for.
virtual void clear()
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
unsigned int n_variable_groups() const
Definition dof_map.h:733
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
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 * _extra_sparsity_context
A pointer associated with the extra sparsity that can optionally be passed in.
Definition dof_map.h:2176
std::vector< GhostingFunctor * >::const_iterator GhostingFunctorIterator
Iterator type for coupling and algebraic ghosting functor ranges.
Definition dof_map.h:315
bool is_attached(SparseMatrix< Number > &matrix)
Matrices should not be attached more than once.
Definition dof_map.C:295
std::unique_ptr< DefaultCoupling > _default_evaluating
The default algebraic GhostingFunctor, used to implement standard libMesh send_list construction.
Definition dof_map.h:2207
void update_sparsity_pattern(SparseMatrix< Number > &matrix) const
Additional matrices may be be temporarily initialized by this DofMap.
Definition dof_map.C:269
bool computed_sparsity_already() const
Returns true iff a sparsity pattern has already been computed.
Definition dof_map.C:258
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
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
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 sys_number() const
Definition dof_map.h:2340
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
SparsityPattern::AugmentSparsityPattern * _augment_sparsity_pattern
Function object to call to add extra entries to the sparsity pattern.
Definition dof_map.h:2164
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
void attach_matrix(SparseMatrix< Number > &matrix)
Additional matrices may be attached to this DofMap.
Definition dof_map.C:240
bool need_full_sparsity_pattern
Default false; set to true if any attached matrix requires a full sparsity pattern.
Definition dof_map.h:2245
std::vector< SparseMatrix< Number > * > _matrices
Additional matrices handled by this object.
Definition dof_map.h:2147
std::unique_ptr< SparsityPattern::Build > _sp
The sparsity pattern of the global matrix.
Definition dof_map.h:2252
CouplingMatrix * _dof_coupling
Degree of freedom coupling.
Definition dof_map.h:1741
bool is_periodic_boundary(const boundary_id_type boundaryid) const
Definition dof_map.C:217
std::unique_ptr< PeriodicBoundaries > _periodic_boundaries
Data structure containing periodic boundaries.
Definition dof_map.h:2294
std::unique_ptr< DefaultCoupling > _default_coupling
The default coupling GhostingFunctor, used to implement standard libMesh sparsity pattern constructio...
Definition dof_map.h:2199
std::set< std::unique_ptr< CouplingMatrix >, Utility::CompareUnderlying > CouplingMatricesSet
Definition dof_map.h:1999
bool has_static_condensation() const
Checks whether we have static condensation.
Definition dof_map.h:1797
MeshBase & _mesh
The mesh that system uses.
Definition dof_map.h:2140
~DofMap()
Destructor.
Definition dof_map.C:204
DofObject * node_ptr(MeshBase &mesh, dof_id_type i) const
Definition dof_map.C:303
DofMap(const unsigned int sys_number, MeshBase &mesh)
Constructor.
Definition dof_map.C:138
DofObject * elem_ptr(MeshBase &mesh, dof_id_type i) const
Definition dof_map.C:310
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::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
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
void set_error_on_constraint_loop(bool error_on_constraint_loop)
Definition dof_map.C:234
std::unique_ptr< StaticCondensationDofMap > _sc
Static condensation class.
Definition dof_map.h:2333
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition dof_object.h:55
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...
unsigned int n_systems() const
Definition dof_object.h:913
unsigned int n_comp(const unsigned int s, const unsigned int var) const
Definition dof_object.h:978
unsigned int n_comp_group(const unsigned int s, const unsigned int vg) const
Definition dof_object.h:991
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
dof_id_type dof_number(const unsigned int s, const unsigned int var, const unsigned int comp) const
std::pair< unsigned int, unsigned int > var_to_vg_and_offset(const unsigned int s, const unsigned int var) const
processor_id_type processor_id() const
Definition dof_object.h:881
unsigned int n_var_groups(const unsigned int s) const
Definition dof_object.h:933
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
DofObject * get_old_dof_object()
Pointer accessor for previously public old_dof_object.
Definition dof_object.h:96
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
dof_id_type id() const
Definition dof_object.h:819
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...
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
unsigned int n_vars(const unsigned int s, const unsigned int vg) const
Definition dof_object.h:943
bool has_dofs(const unsigned int s=libMesh::invalid_uint) const
This is the base class from which all geometric element types are derived.
Definition elem.h:96
RefinementState refinement_flag() const
Definition elem.h:3227
bool active() const
Definition elem.h:2958
const Node & node_ref(const unsigned int i) const
Definition elem.h:2538
virtual unsigned int n_nodes() const =0
subdomain_id_type subdomain_id() const
Definition elem.h:2591
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
const Node *const * get_nodes() const
Definition elem.h:2508
virtual bool is_vertex(const unsigned int i) const =0
virtual ElemType type() const =0
unsigned int p_level() const
Definition elem.h:3125
RefinementState p_refinement_flag() const
Definition elem.h:3243
virtual bool infinite() const =0
static unsigned int n_dofs_at_node(const unsigned int dim, const FEType &fe_t, const ElemType t, const unsigned int n)
static unsigned int max_order(const FEType &fe_t, const ElemType &el_t)
static unsigned int n_dofs_per_elem(const unsigned int dim, const FEType &fe_t, const ElemType t)
static bool extra_hanging_dofs(const FEType &fe_t)
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
OrderWrapper order
The approximation order of the element (at 0 p-refinement level).
Definition fe_type.h:203
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
FEFamily family
The type of finite element.
Definition fe_type.h:228
This abstract base class defines the interface by which library code and user code can report associa...
virtual void set_mesh(const MeshBase *mesh)
It should be called after cloning a ghosting functor.
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?
This is the MeshBase class.
Definition mesh_base.h:81
const ElemRange & element_stored_range()
Definition mesh_base.C:1939
void remove_ghosting_functor(GhostingFunctor &ghosting_functor)
Removes a functor which was previously added to the set of ghosting functors.
Definition mesh_base.C:1100
A Node is like a Point, but with more information.
Definition node.h:55
Provides a uniform interface to vector storage schemes for different linear algebra libraries.
virtual numeric_index_type last_local_index() const =0
virtual numeric_index_type size() const =0
virtual numeric_index_type first_local_index() const =0
int get_order() const
Explicitly request the order as an int.
Definition fe_type.h:80
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
We're using a class instead of a typedef to allow forward declarations and future flexibility.
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
Generic sparse matrix.
virtual void update_sparsity_pattern(const SparsityPattern::Graph &)
Updates the matrix sparsity pattern.
void attach_dof_map(const DofMap &dof_map)
Set a pointer to the DofMap to use.
virtual bool need_full_sparsity_pattern() const
void attach_sparsity_pattern(const SparsityPattern::Build &sp)
Set a pointer to a sparsity pattern to use.
A class holding degree of freedom information pertinent to static condensation.
The StoredRange class defines a contiguous, divisible set of objects.
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition system.h:100
bool is_initialized() const
Definition system.h:2457
const MeshBase & get_mesh() const
Definition system.h:2401
The Tri3Subdivision element is a three-noded subdivision surface shell element used in mechanics calc...
This class defines a logically grouped set of variables in the system.
Definition variable.h:216
unsigned int n_variables() const
Definition variable.h:278
unsigned int number(unsigned int v) const
Definition variable.h:316
void append(std::string var_name)
Appends a variable to the group.
Definition variable.h:340
This class defines the notion of a variable in the system.
Definition variable.h:51
const std::set< subdomain_id_type > & active_subdomains() const
Definition variable.h:181
bool active_on_subdomain(subdomain_id_type sid) const
Definition variable.h:167
bool implicitly_active() const
Definition variable.h:175
const FEType & type() const
Definition variable.h:144
MeshBase & mesh
void pull_parallel_vector_data(const Communicator &comm, const MapToVectors &queries, GatherFunctor &gather_data, const ActionFunctor &act_on_data, const datum *example)
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.
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.
std::string enum_to_string(const T e)
The libMesh namespace provides an interface to certain functionality in the library.
OStreamProxy err
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
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
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
ElemType
Defines an enum for geometric element types.
int8_t boundary_id_type
Definition id_types.h:51
libmesh_assert(ctx)
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
OStreamProxy out
StoredRange< MeshBase::element_iterator, Elem * > ElemRange
Definition elem_range.h:33
T command_line_next(std::string name, T default_value)
Use GetPot's search()/next() functions to get following arguments from the command line.
Definition libmesh.C:1025
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
uint8_t dof_id_type
Definition id_types.h:67
bool on_command_line(std::string arg)
Definition libmesh.C:934
uint8_t processor_id_type
Definition id_types.h:104
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 definition of the const_element_iterator struct.
Definition mesh_base.h:2556
const dof_id_type n_nodes
Definition tecplot_io.C:67