deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
dof_renumbering.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 1999 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
15#include <deal.II/base/types.h>
17
19
24
25#include <deal.II/fe/fe.h>
28
29#include <deal.II/grid/tria.h>
31
34
39
41
44
45#define BOOST_BIND_GLOBAL_PLACEHOLDERS
46#include <boost/config.hpp>
47#include <boost/graph/adjacency_list.hpp>
48#include <boost/graph/bandwidth.hpp>
49#include <boost/graph/cuthill_mckee_ordering.hpp>
50#include <boost/graph/king_ordering.hpp>
51#include <boost/graph/minimum_degree_ordering.hpp>
52#include <boost/graph/properties.hpp>
53#include <boost/random.hpp>
54#include <boost/random/uniform_int_distribution.hpp>
55
56#undef BOOST_BIND_GLOBAL_PLACEHOLDERS
57
58#include <algorithm>
59#include <cmath>
60#include <functional>
61#include <map>
62#include <vector>
63
64
66
67namespace DoFRenumbering
68{
69 namespace boost
70 {
71 namespace boosttypes
72 {
73 using namespace ::boost;
74
75 using Graph = adjacency_list<vecS,
76 vecS,
77 undirectedS,
78 property<vertex_color_t,
79 default_color_type,
80 property<vertex_degree_t, int>>>;
81 using Vertex = graph_traits<Graph>::vertex_descriptor;
82 using size_type = graph_traits<Graph>::vertices_size_type;
83
84 using Pair = std::pair<size_type, size_type>;
85 } // namespace boosttypes
86
87
88 namespace internal
89 {
90 template <int dim, int spacedim>
91 void
93 const bool use_constraints,
94 boosttypes::Graph &graph,
95 boosttypes::property_map<boosttypes::Graph,
96 boosttypes::vertex_degree_t>::type
97 &graph_degree)
98 {
99 {
100 // create intermediate sparsity pattern (faster than directly
101 // submitting indices)
102 AffineConstraints<double> constraints;
103 if (use_constraints)
104 DoFTools::make_hanging_node_constraints(dof_handler, constraints);
105 constraints.close();
106 DynamicSparsityPattern dsp(dof_handler.n_dofs(),
107 dof_handler.n_dofs());
108 DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints);
109
110 // submit the entries to the boost graph
111 for (types::global_dof_index row = 0; row < dsp.n_rows(); ++row)
112 for (types::global_dof_index col = 0; col < dsp.row_length(row);
113 ++col)
114 add_edge(row, dsp.column_number(row, col), graph);
115 }
116
117 boosttypes::graph_traits<boosttypes::Graph>::vertex_iterator ui, ui_end;
118
119 graph_degree = get(::boost::vertex_degree, graph);
120 for (::boost::tie(ui, ui_end) = vertices(graph); ui != ui_end; ++ui)
121 graph_degree[*ui] = degree(*ui, graph);
122 }
123 } // namespace internal
124
125
126 template <int dim, int spacedim>
127 void
129 const bool reversed_numbering,
130 const bool use_constraints)
131 {
132 std::vector<types::global_dof_index> renumbering(
133 dof_handler.n_dofs(), numbers::invalid_dof_index);
134 compute_Cuthill_McKee(renumbering,
135 dof_handler,
136 reversed_numbering,
137 use_constraints);
138
139 // actually perform renumbering;
140 // this is dimension specific and
141 // thus needs an own function
142 dof_handler.renumber_dofs(renumbering);
143 }
144
145
146 template <int dim, int spacedim>
147 void
148 compute_Cuthill_McKee(std::vector<types::global_dof_index> &new_dof_indices,
149 const DoFHandler<dim, spacedim> &dof_handler,
150 const bool reversed_numbering,
151 const bool use_constraints)
152 {
153 boosttypes::Graph graph(dof_handler.n_dofs());
154 boosttypes::property_map<boosttypes::Graph,
155 boosttypes::vertex_degree_t>::type graph_degree;
156
157 internal::create_graph(dof_handler, use_constraints, graph, graph_degree);
158
159 boosttypes::property_map<boosttypes::Graph,
160 boosttypes::vertex_index_t>::type index_map =
161 get(::boost::vertex_index, graph);
162
163
164 std::vector<boosttypes::Vertex> inv_perm(num_vertices(graph));
165
166 if (reversed_numbering == false)
167 ::boost::cuthill_mckee_ordering(graph,
168 inv_perm.rbegin(),
169 get(::boost::vertex_color, graph),
170 make_degree_map(graph));
171 else
172 ::boost::cuthill_mckee_ordering(graph,
173 inv_perm.begin(),
174 get(::boost::vertex_color, graph),
175 make_degree_map(graph));
176
177 for (boosttypes::size_type c = 0; c != inv_perm.size(); ++c)
178 new_dof_indices[index_map[inv_perm[c]]] = c;
179
180 Assert(std::find(new_dof_indices.begin(),
181 new_dof_indices.end(),
182 numbers::invalid_dof_index) == new_dof_indices.end(),
184 }
185
186
187
188 template <int dim, int spacedim>
189 void
191 const bool reversed_numbering,
192 const bool use_constraints)
193 {
194 std::vector<types::global_dof_index> renumbering(
195 dof_handler.n_dofs(), numbers::invalid_dof_index);
196 compute_king_ordering(renumbering,
197 dof_handler,
198 reversed_numbering,
199 use_constraints);
200
201 // actually perform renumbering;
202 // this is dimension specific and
203 // thus needs an own function
204 dof_handler.renumber_dofs(renumbering);
205 }
206
207
208 template <int dim, int spacedim>
209 void
210 compute_king_ordering(std::vector<types::global_dof_index> &new_dof_indices,
211 const DoFHandler<dim, spacedim> &dof_handler,
212 const bool reversed_numbering,
213 const bool use_constraints)
214 {
215 boosttypes::Graph graph(dof_handler.n_dofs());
216 boosttypes::property_map<boosttypes::Graph,
217 boosttypes::vertex_degree_t>::type graph_degree;
218
219 internal::create_graph(dof_handler, use_constraints, graph, graph_degree);
220
221 boosttypes::property_map<boosttypes::Graph,
222 boosttypes::vertex_index_t>::type index_map =
223 get(::boost::vertex_index, graph);
224
225
226 std::vector<boosttypes::Vertex> inv_perm(num_vertices(graph));
227
228 if (reversed_numbering == false)
229 ::boost::king_ordering(graph, inv_perm.rbegin());
230 else
231 ::boost::king_ordering(graph, inv_perm.begin());
232
233 for (boosttypes::size_type c = 0; c != inv_perm.size(); ++c)
234 new_dof_indices[index_map[inv_perm[c]]] = c;
235
236 Assert(std::find(new_dof_indices.begin(),
237 new_dof_indices.end(),
238 numbers::invalid_dof_index) == new_dof_indices.end(),
240 }
241
242
243
244 template <int dim, int spacedim>
245 void
247 const bool reversed_numbering,
248 const bool use_constraints)
249 {
250 std::vector<types::global_dof_index> renumbering(
251 dof_handler.n_dofs(), numbers::invalid_dof_index);
252 compute_minimum_degree(renumbering,
253 dof_handler,
254 reversed_numbering,
255 use_constraints);
256
257 // actually perform renumbering;
258 // this is dimension specific and
259 // thus needs an own function
260 dof_handler.renumber_dofs(renumbering);
261 }
262
263
264 template <int dim, int spacedim>
265 void
267 std::vector<types::global_dof_index> &new_dof_indices,
268 const DoFHandler<dim, spacedim> &dof_handler,
269 const bool reversed_numbering,
270 const bool use_constraints)
271 {
272 (void)use_constraints;
273 Assert(use_constraints == false, ExcNotImplemented());
274
275 // the following code is pretty
276 // much a verbatim copy of the
277 // sample code for the
278 // minimum_degree_ordering manual
279 // page from the BOOST Graph
280 // Library
281 using namespace ::boost;
282
283 int delta = 0;
284
285 // must be BGL directed graph now
286 using Graph = adjacency_list<vecS, vecS, directedS>;
287
288 const types::global_dof_index n_dofs = dof_handler.n_dofs();
289
290 Graph G(n_dofs);
291
292 std::vector<::types::global_dof_index> dofs_on_this_cell;
293
294 for (const auto &cell : dof_handler.active_cell_iterators())
295 {
296 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
297
298 dofs_on_this_cell.resize(dofs_per_cell);
299
300 cell->get_active_or_mg_dof_indices(dofs_on_this_cell);
301 for (unsigned int i = 0; i < dofs_per_cell; ++i)
302 for (unsigned int j = 0; j < dofs_per_cell; ++j)
303 if (dofs_on_this_cell[i] > dofs_on_this_cell[j])
304 {
305 add_edge(dofs_on_this_cell[i], dofs_on_this_cell[j], G);
306 add_edge(dofs_on_this_cell[j], dofs_on_this_cell[i], G);
307 }
308 }
309
310
311 // We would like to use the (unsigned) global_dof_index type,
312 // but the boost library only works correctly with signed integer
313 // types:
314 using Vector = std::vector<types::signed_global_dof_index>;
315
316
317 Vector inverse_perm(n_dofs, 0);
318
319 Vector perm(n_dofs, 0);
320
321
322 Vector supernode_sizes(n_dofs, 1);
323 // init has to be 1
324
325 ::boost::property_map<Graph, vertex_index_t>::type id =
326 get(vertex_index, G);
327
328
329 Vector degree(n_dofs, 0);
330
331
332 minimum_degree_ordering(
333 G,
334 make_iterator_property_map(degree.begin(), id, degree[0]),
335 inverse_perm.data(),
336 perm.data(),
337 make_iterator_property_map(supernode_sizes.begin(),
338 id,
339 supernode_sizes[0]),
340 delta,
341 id);
342
343
344 for (types::global_dof_index i = 0; i < n_dofs; ++i)
345 {
346 Assert(std::find(perm.begin(), perm.end(), i) != perm.end(),
348 Assert(std::find(inverse_perm.begin(), inverse_perm.end(), i) !=
349 inverse_perm.end(),
351 Assert(inverse_perm[perm[i]] ==
352 static_cast<types::signed_global_dof_index>(i),
354 }
355
356 if (reversed_numbering == true)
357 std::copy(perm.begin(), perm.end(), new_dof_indices.begin());
358 else
359 std::copy(inverse_perm.begin(),
360 inverse_perm.end(),
361 new_dof_indices.begin());
362 }
363
364 } // namespace boost
365
366
367
368 template <int dim, int spacedim>
369 void
371 const bool reversed_numbering,
372 const bool use_constraints,
373 const std::vector<types::global_dof_index> &starting_indices)
374 {
375 std::vector<types::global_dof_index> renumbering(
376 dof_handler.locally_owned_dofs().n_elements(),
378 compute_Cuthill_McKee(renumbering,
379 dof_handler,
380 reversed_numbering,
381 use_constraints,
382 starting_indices);
383
384 // actually perform renumbering;
385 // this is dimension specific and
386 // thus needs an own function
387 dof_handler.renumber_dofs(renumbering);
388 }
389
390
391
392 template <int dim, int spacedim>
393 void
395 std::vector<types::global_dof_index> &new_indices,
396 const DoFHandler<dim, spacedim> &dof_handler,
397 const bool reversed_numbering,
398 const bool use_constraints,
399 const std::vector<types::global_dof_index> &starting_indices,
400 const unsigned int level)
401 {
402 const bool reorder_level_dofs =
403 (level == numbers::invalid_unsigned_int) ? false : true;
404
405 // see if there is anything to do at all or whether we can skip the work on
406 // this processor
407 if (dof_handler.locally_owned_dofs().n_elements() == 0)
408 {
409 Assert(new_indices.empty(), ExcInternalError());
410 return;
411 }
412
413 // make the connection graph
414 //
415 // note that if constraints are not requested, then the 'constraints'
416 // object will be empty and using it has no effect
417 if (reorder_level_dofs == true)
420
421 const IndexSet locally_relevant_dofs =
422 (reorder_level_dofs == false ?
425 const IndexSet &locally_owned_dofs =
426 (reorder_level_dofs == false ? dof_handler.locally_owned_dofs() :
427 dof_handler.locally_owned_mg_dofs(level));
428
429 AffineConstraints<double> constraints;
430 if (use_constraints)
431 {
432 // reordering with constraints is not yet implemented on a level basis
433 Assert(reorder_level_dofs == false, ExcNotImplemented());
434
435 constraints.reinit(locally_owned_dofs, locally_relevant_dofs);
436 DoFTools::make_hanging_node_constraints(dof_handler, constraints);
437 }
438 constraints.close();
439
440 // see if we can get away with the sequential algorithm
441 if (locally_owned_dofs.n_elements() == locally_owned_dofs.size())
442 {
443 AssertDimension(new_indices.size(), locally_owned_dofs.n_elements());
444
445 DynamicSparsityPattern dsp(locally_owned_dofs.size(),
446 locally_owned_dofs.size());
447 if (reorder_level_dofs == false)
448 {
449 DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints);
450 }
451 else
452 {
453 MGTools::make_sparsity_pattern(dof_handler, dsp, level);
454 }
455
457 new_indices,
458 starting_indices);
459 if (reversed_numbering)
460 new_indices = Utilities::reverse_permutation(new_indices);
461 }
462 else
463 {
464 // we are in the parallel case where we need to work in the
465 // local index space, i.e., the locally owned part of the
466 // sparsity pattern.
467 //
468 // first figure out whether the user only gave us starting
469 // indices that are locally owned, or that are only locally
470 // relevant. in the process, also check that all indices
471 // really belong to at least the locally relevant ones
472 const IndexSet locally_active_dofs =
473 (reorder_level_dofs == false ?
476
477 bool needs_locally_active = false;
478 for (const auto starting_index : starting_indices)
479 {
480 if ((needs_locally_active ==
481 /* previously already set to */ true) ||
482 (locally_owned_dofs.is_element(starting_index) == false))
483 {
484 Assert(
485 locally_active_dofs.is_element(starting_index),
487 "You specified global degree of freedom " +
488 std::to_string(starting_index) +
489 " as a starting index, but this index is not among the "
490 "locally active ones on this processor, as required "
491 "for this function."));
492 needs_locally_active = true;
493 }
494 }
495
496 const IndexSet index_set_to_use =
497 (needs_locally_active ? locally_active_dofs : locally_owned_dofs);
498
499 // if this process doesn't own any DoFs (on this level), there is
500 // nothing to do
501 if (index_set_to_use.n_elements() == 0)
502 return;
503
504 // then create first the global sparsity pattern, and then the local
505 // sparsity pattern from the global one by transferring its indices to
506 // processor-local (locally owned or locally active) index space
507 DynamicSparsityPattern dsp(index_set_to_use.size(),
508 index_set_to_use.size(),
509 index_set_to_use);
510 if (reorder_level_dofs == false)
511 {
512 DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints);
513 }
514 else
515 {
516 MGTools::make_sparsity_pattern(dof_handler, dsp, level);
517 }
518
519 DynamicSparsityPattern local_sparsity(index_set_to_use.n_elements(),
520 index_set_to_use.n_elements());
521 std::vector<types::global_dof_index> row_entries;
522 for (unsigned int i = 0; i < index_set_to_use.n_elements(); ++i)
523 {
524 const types::global_dof_index row =
525 index_set_to_use.nth_index_in_set(i);
526 const unsigned int row_length = dsp.row_length(row);
527 row_entries.clear();
528 for (unsigned int j = 0; j < row_length; ++j)
529 {
530 const unsigned int col = dsp.column_number(row, j);
531 if (col != row && index_set_to_use.is_element(col))
532 row_entries.push_back(index_set_to_use.index_within_set(col));
533 }
534 local_sparsity.add_entries(i,
535 row_entries.begin(),
536 row_entries.end(),
537 true);
538 }
539
540 // translate starting indices from global to local indices
541 std::vector<types::global_dof_index> local_starting_indices(
542 starting_indices.size());
543 for (unsigned int i = 0; i < starting_indices.size(); ++i)
544 local_starting_indices[i] =
545 index_set_to_use.index_within_set(starting_indices[i]);
546
547 // then do the renumbering on the locally owned portion
548 AssertDimension(new_indices.size(), locally_owned_dofs.n_elements());
549 std::vector<types::global_dof_index> my_new_indices(
550 index_set_to_use.n_elements());
552 my_new_indices,
553 local_starting_indices);
554 if (reversed_numbering)
555 my_new_indices = Utilities::reverse_permutation(my_new_indices);
556
557 // now that we have a re-enumeration of all DoFs, we need to throw
558 // out the ones that are not locally owned in case we have worked
559 // with the locally active ones. that's because the renumbering
560 // functions only want new indices for the locally owned DoFs (other
561 // processors are responsible for renumbering the ones that are
562 // on cell interfaces)
563 if (needs_locally_active == true)
564 {
565 // first step: figure out which DoF indices to eliminate
566 IndexSet active_but_not_owned_dofs = locally_active_dofs;
567 active_but_not_owned_dofs.subtract_set(locally_owned_dofs);
568
569 std::set<types::global_dof_index> erase_these_indices;
570 for (const auto p : active_but_not_owned_dofs)
571 {
572 const auto index = index_set_to_use.index_within_set(p);
573 Assert(index < index_set_to_use.n_elements(),
575 erase_these_indices.insert(my_new_indices[index]);
576 my_new_indices[index] = numbers::invalid_dof_index;
577 }
578 Assert(erase_these_indices.size() ==
579 active_but_not_owned_dofs.n_elements(),
581 Assert(static_cast<unsigned int>(
582 std::count(my_new_indices.begin(),
583 my_new_indices.end(),
585 active_but_not_owned_dofs.n_elements(),
587
588 // then compute a renumbering of the remaining ones
589 std::vector<types::global_dof_index> translate_indices(
590 my_new_indices.size());
591 {
592 std::set<types::global_dof_index>::const_iterator
593 next_erased_index = erase_these_indices.begin();
594 types::global_dof_index next_new_index = 0;
595 for (unsigned int i = 0; i < translate_indices.size(); ++i)
596 if ((next_erased_index != erase_these_indices.end()) &&
597 (*next_erased_index == i))
598 {
599 translate_indices[i] = numbers::invalid_dof_index;
600 ++next_erased_index;
601 }
602 else
603 {
604 translate_indices[i] = next_new_index;
605 ++next_new_index;
606 }
607 Assert(next_new_index == locally_owned_dofs.n_elements(),
609 }
610
611 // and then do the renumbering of the result of the
612 // Cuthill-McKee algorithm above, right into the output array
613 new_indices.clear();
614 new_indices.reserve(locally_owned_dofs.n_elements());
615 for (const auto &p : my_new_indices)
617 {
618 Assert(translate_indices[p] != numbers::invalid_dof_index,
620 new_indices.push_back(translate_indices[p]);
621 }
622 Assert(new_indices.size() == locally_owned_dofs.n_elements(),
624 }
625 else
626 new_indices = std::move(my_new_indices);
627
628 // convert indices back to global index space. in both of the branches
629 // above, we ended up with new_indices only containing the local
630 // indices of the locally-owned DoFs. so that's where we get the
631 // indices
632 for (types::global_dof_index &new_index : new_indices)
633 new_index = locally_owned_dofs.nth_index_in_set(new_index);
634 }
635 }
636
637
638
639 template <int dim, int spacedim>
640 void
642 const unsigned int level,
643 const bool reversed_numbering,
644 const std::vector<types::global_dof_index> &starting_indices)
645 {
648
649 std::vector<types::global_dof_index> new_indices(
652
653 compute_Cuthill_McKee(new_indices,
654 dof_handler,
655 reversed_numbering,
656 false,
657 starting_indices,
658 level);
659
660 // actually perform renumbering;
661 // this is dimension specific and
662 // thus needs an own function
663 dof_handler.renumber_dofs(level, new_indices);
664 }
665
666
667
668 template <int dim, int spacedim>
669 void
671 const std::vector<unsigned int> &component_order_arg)
672 {
673 std::vector<types::global_dof_index> renumbering(
675
676 const types::global_dof_index result =
677 compute_component_wise<dim, spacedim>(renumbering,
678 dof_handler.begin_active(),
679 dof_handler.end(),
680 component_order_arg,
681 false);
682 (void)result;
683
684 // If we don't have a renumbering (i.e., when there is 1 component) then
685 // return
686 if (Utilities::MPI::logical_and(renumbering.size() == 0,
687 dof_handler.get_mpi_communicator()))
688 return;
689
690 // verify that the last numbered
691 // degree of freedom is either
692 // equal to the number of degrees
693 // of freedom in total (the
694 // sequential case) or in the
695 // distributed case at least
696 // makes sense
697 Assert((result == dof_handler.n_locally_owned_dofs()) ||
698 ((dof_handler.n_locally_owned_dofs() < dof_handler.n_dofs()) &&
699 (result <= dof_handler.n_dofs())),
701
702 dof_handler.renumber_dofs(renumbering);
703 }
704
705
706
707 template <int dim, int spacedim>
708 void
710 const unsigned int level,
711 const std::vector<unsigned int> &component_order_arg)
712 {
715
716 std::vector<types::global_dof_index> renumbering(
719
721 dof_handler.begin(level);
723 dof_handler.end(level);
724
725 const types::global_dof_index result =
726 compute_component_wise<dim, spacedim>(
727 renumbering, start, end, component_order_arg, true);
728 (void)result;
729
730 // If we don't have a renumbering (i.e., when there is 1 component) then
731 // return
732 if (Utilities::MPI::logical_and(renumbering.size() == 0,
733 dof_handler.get_mpi_communicator()))
734 return;
735
736 // verify that the last numbered
737 // degree of freedom is either
738 // equal to the number of degrees
739 // of freedom in total (the
740 // sequential case) or in the
741 // distributed case at least
742 // makes sense
743 Assert((result == dof_handler.locally_owned_mg_dofs(level).n_elements()) ||
744 ((dof_handler.locally_owned_mg_dofs(level).n_elements() <
745 dof_handler.n_dofs(level)) &&
746 (result <= dof_handler.n_dofs(level))),
748
749 dof_handler.renumber_dofs(level, renumbering);
750 }
751
752
753 namespace
754 {
755 // Utility function used to fill a given `component_order` vector by
756 // processing an `order` of FEValuesExtractors. Supported extractors are
757 // listed in FEValueExtractors::ExtractorVariant. The function is used in
758 // the implementation of component_wise(DoFHandler<dim, spacedim>&, const
759 // std::vector<FEValueExtractors::ExtractorVariant> &).
760 template <int dim, int spacedim>
761 std::vector<unsigned int>
762 generate_component_order(
763 const std::vector<FEValuesExtractors::AnyExtractor> &order,
764 const unsigned int fe_n_components)
765 {
766 // Initialize `component_order` with invalid unsigned ints representing
767 // unassigned state.
768 std::vector<unsigned int> component_order(fe_n_components,
770
771 // Extract the start component index and determine the number of
772 // components for each extractor.
773 unsigned int block_index = 0;
774 for (const auto &extractor : order)
775 {
776 auto start_component_index = numbers::invalid_unsigned_int;
777 auto n_components = numbers::invalid_unsigned_int;
778
779 if (std::holds_alternative<FEValuesExtractors::Scalar>(extractor))
780 {
781 start_component_index =
782 std::get<FEValuesExtractors::Scalar>(extractor).component;
783 n_components = 1;
784 }
785 else if (std::holds_alternative<FEValuesExtractors::Vector>(
786 extractor))
787 {
788 start_component_index =
789 std::get<FEValuesExtractors::Vector>(extractor)
790 .first_vector_component;
792 n_independent_components;
793 }
794 else if (std::holds_alternative<FEValuesExtractors::Tensor<2>>(
795 extractor))
796 {
797 start_component_index =
798 std::get<FEValuesExtractors::Tensor<2>>(extractor)
799 .first_tensor_component;
801 value_type::n_independent_components;
802 }
803 else if (std::holds_alternative<
805 {
806 start_component_index =
807 std::get<FEValuesExtractors::SymmetricTensor<2>>(extractor)
808 .first_tensor_component;
810 value_type::n_independent_components;
811 }
812 else
813 {
815 false,
817 "An unsupported ExtractorVariant was passed in the component_wise extractor_order argument."));
818 }
819
820 // Fill `component_order` vector with `n_components` starting at
821 // `start_component_index`. Set the values to `block_index`.
822 for (unsigned int i = start_component_index;
823 i < start_component_index + n_components;
824 ++i)
825 {
826 AssertThrow(i < component_order.size(),
827 ExcIndexRange(i, 0, component_order.size()));
828
830 component_order[i] == numbers::invalid_unsigned_int,
832 "A component which has already been assigned a block "
833 "index is trying to be overwritten. This indicates that the "
834 "component_wise function is being called with an invalid set "
835 "of extractors in the extractor_order argument "
836 "that overlap in component indices."));
837
838 component_order[i] = block_index;
839 }
840 // Increment block index
841 block_index++;
842 }
843 return component_order;
844 }
845 } // namespace
846
847 template <int dim, int spacedim>
848 void
850 const std::vector<FEValuesExtractors::AnyExtractor> &order)
851 {
852 // The function acts as a wrapper around the above-implemented function.
853
854 // Generate component order argument from extractors.
855 std::vector<unsigned int> component_order =
856 generate_component_order<dim, spacedim>(
857 order, dof_handler.get_fe().n_components());
858
859 // Size of `component_order` must be equal
860 // to the number of finite element system components
861 Assert(component_order.size() == dof_handler.get_fe().n_components(),
862 ExcDimensionMismatch(component_order.size(),
863 dof_handler.get_fe().n_components()));
864
865 // All entries must be assigned
867 std::none_of(component_order.begin(),
868 component_order.end(),
869 [](unsigned int value) {
870 return value == numbers::invalid_unsigned_int;
871 }),
873 "All entries in component_order must contain valid indices, no "
874 "numbers::invalid_unsigned_int must be present after processing all given "
875 "extractors. This error typically occurs when the component_wise function "
876 "is passed fewer extractors than needed or when the set of extractors "
877 "provided in the extractor_order argument doesn't cover all required components."));
878
879 // Wrapped function
880 component_wise(dof_handler, component_order);
881 }
882
883
884 template <int dim, int spacedim>
885 void
887 const unsigned int level,
888 const std::vector<FEValuesExtractors::AnyExtractor> &order)
889 {
890 // The function acts as a wrapper around the above-implemented function.
891
892 // Generate component order argument from extractors.
893 std::vector<unsigned int> component_order =
894 generate_component_order<dim, spacedim>(
895 order, dof_handler.get_fe().n_components());
896
897 // Size of `component_order` must be equal
898 // to the number of finite element system components
899 Assert(component_order.size() == dof_handler.get_fe().n_components(),
900 ExcDimensionMismatch(component_order.size(),
901 dof_handler.get_fe().n_components()));
902
903 // All entries must be assigned.
905 std::none_of(component_order.begin(),
906 component_order.end(),
907 [](unsigned int value) {
908 return value == numbers::invalid_unsigned_int;
909 }),
911 "All entries in component_order must contain valid indices, no "
912 "numbers::invalid_unsigned_int must be present after processing all given "
913 "extractors. This error typically occurs when the component_wise function "
914 "is passed fewer extractors than needed or when the set of extractors "
915 "provided in the extractor_order argument doesn't cover all required components. "));
916
917 // Wrapped function
918 component_wise(dof_handler, level, component_order);
919 }
920
921
922
923 template <int dim, int spacedim, typename CellIterator>
925 compute_component_wise(std::vector<types::global_dof_index> &new_indices,
926 const CellIterator &start,
928 const std::vector<unsigned int> &component_order_arg,
929 const bool is_level_operation)
930 {
931 const hp::FECollection<dim, spacedim> &fe_collection =
932 start->get_dof_handler().get_fe_collection();
933
934 // do nothing if the FE has only
935 // one component
936 if (fe_collection.n_components() == 1)
937 {
938 new_indices.resize(0);
939 return 0;
940 }
941
942 // Get a reference to the set of dofs. Note that we assume that all cells
943 // are assumed to be on the same level, otherwise the operation doesn't make
944 // much sense (we will assert this below).
945 const IndexSet &locally_owned_dofs =
946 is_level_operation ?
947 start->get_dof_handler().locally_owned_mg_dofs(start->level()) :
948 start->get_dof_handler().locally_owned_dofs();
949
950 // Copy last argument into a
951 // writable vector.
952 std::vector<unsigned int> component_order(component_order_arg);
953 // If the last argument was an
954 // empty vector, set up things to
955 // store components in the order
956 // found in the system.
957 if (component_order.empty())
958 for (unsigned int i = 0; i < fe_collection.n_components(); ++i)
959 component_order.push_back(i);
960
961 Assert(component_order.size() == fe_collection.n_components(),
962 ExcDimensionMismatch(component_order.size(),
963 fe_collection.n_components()));
964
965 for (const unsigned int component : component_order)
966 {
967 (void)component;
968 AssertIndexRange(component, fe_collection.n_components());
969 }
970
971 // vector to hold the dof indices on
972 // the cell we visit at a time
973 std::vector<types::global_dof_index> local_dof_indices;
974
975 // prebuilt list to which component
976 // a given dof on a cell
977 // should go. note that we get into
978 // trouble here if the shape
979 // function is not primitive, since
980 // then there is no single vector
981 // component to which it
982 // belongs. in this case, assign it
983 // to the first vector component to
984 // which it belongs
985 std::vector<std::vector<unsigned int>> component_list(fe_collection.size());
986 for (unsigned int f = 0; f < fe_collection.size(); ++f)
987 {
988 const FiniteElement<dim, spacedim> &fe = fe_collection[f];
989 const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
990 component_list[f].resize(dofs_per_cell);
991 for (unsigned int i = 0; i < dofs_per_cell; ++i)
992 if (fe.is_primitive(i))
993 component_list[f][i] =
994 component_order[fe.system_to_component_index(i).first];
995 else
996 {
997 const unsigned int comp =
999
1000 // then associate this degree
1001 // of freedom with this
1002 // component
1003 component_list[f][i] = component_order[comp];
1004 }
1005 }
1006
1007 // set up a map where for each
1008 // component the respective degrees
1009 // of freedom are collected.
1010 //
1011 // note that this map is sorted by
1012 // component but that within each
1013 // component it is NOT sorted by
1014 // dof index. note also that some
1015 // dof indices are entered
1016 // multiply, so we will have to
1017 // take care of that
1018 std::vector<std::vector<types::global_dof_index>> component_to_dof_map(
1019 fe_collection.n_components());
1020 for (CellIterator cell = start; cell != end; ++cell)
1021 {
1022 if (is_level_operation)
1023 {
1024 // we are dealing with mg dofs, skip foreign level cells:
1025 if (!cell->is_locally_owned_on_level())
1026 continue;
1027 }
1028 else
1029 {
1030 // we are dealing with active dofs, skip the loop if not locally
1031 // owned:
1032 if (!cell->is_locally_owned())
1033 continue;
1034 }
1035
1036 if (is_level_operation)
1037 Assert(
1038 cell->level() == start->level(),
1039 ExcMessage(
1040 "Multigrid renumbering in compute_component_wise() needs to be applied to a single level!"));
1041
1042 // on each cell: get dof indices
1043 // and insert them into the global
1044 // list using their component
1045 const types::fe_index fe_index = cell->active_fe_index();
1046 const unsigned int dofs_per_cell =
1047 fe_collection[fe_index].n_dofs_per_cell();
1048 local_dof_indices.resize(dofs_per_cell);
1049 cell->get_active_or_mg_dof_indices(local_dof_indices);
1050
1051 for (unsigned int i = 0; i < dofs_per_cell; ++i)
1052 if (locally_owned_dofs.is_element(local_dof_indices[i]))
1053 component_to_dof_map[component_list[fe_index][i]].push_back(
1054 local_dof_indices[i]);
1055 }
1056
1057 // now we've got all indices sorted
1058 // into buckets labeled by their
1059 // target component number. we've
1060 // only got to traverse this list
1061 // and assign the new indices
1062 //
1063 // however, we first want to sort
1064 // the indices entered into the
1065 // buckets to preserve the order
1066 // within each component and during
1067 // this also remove duplicate
1068 // entries
1069 //
1070 // note that we no longer have to
1071 // care about non-primitive shape
1072 // functions since the buckets
1073 // corresponding to the second and
1074 // following vector components of a
1075 // non-primitive FE will simply be
1076 // empty, everything being shoved
1077 // into the first one. The same
1078 // holds if several components were
1079 // joined into a single target.
1080 for (unsigned int component = 0; component < fe_collection.n_components();
1081 ++component)
1082 {
1083 std::sort(component_to_dof_map[component].begin(),
1084 component_to_dof_map[component].end());
1085 component_to_dof_map[component].erase(
1086 std::unique(component_to_dof_map[component].begin(),
1087 component_to_dof_map[component].end()),
1088 component_to_dof_map[component].end());
1089 }
1090
1091 // calculate the number of locally owned
1092 // DoFs per bucket
1093 const unsigned int n_buckets = fe_collection.n_components();
1094 std::vector<types::global_dof_index> shifts(n_buckets);
1095
1097 (dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1098 &start->get_dof_handler().get_triangulation())))
1099 {
1100#ifdef DEAL_II_WITH_MPI
1101 std::vector<types::global_dof_index> local_dof_count(n_buckets);
1102
1103 for (unsigned int c = 0; c < n_buckets; ++c)
1104 local_dof_count[c] = component_to_dof_map[c].size();
1105
1106 std::vector<types::global_dof_index> prefix_dof_count(n_buckets);
1107 const int ierr = MPI_Exscan(
1108 local_dof_count.data(),
1109 prefix_dof_count.data(),
1110 n_buckets,
1111 Utilities::MPI::mpi_type_id_for_type<types::global_dof_index>,
1112 MPI_SUM,
1113 tria->get_mpi_communicator());
1114 AssertThrowMPI(ierr);
1115
1116 std::vector<types::global_dof_index> global_dof_count(n_buckets);
1117 Utilities::MPI::sum(local_dof_count,
1118 tria->get_mpi_communicator(),
1119 global_dof_count);
1120
1121 // calculate shifts
1122 types::global_dof_index cumulated = 0;
1123 for (unsigned int c = 0; c < n_buckets; ++c)
1124 {
1125 shifts[c] = prefix_dof_count[c] + cumulated;
1126 cumulated += global_dof_count[c];
1127 }
1128#else
1129 (void)tria;
1131#endif
1132 }
1133 else
1134 {
1135 shifts[0] = 0;
1136 for (unsigned int c = 1; c < fe_collection.n_components(); ++c)
1137 shifts[c] = shifts[c - 1] + component_to_dof_map[c - 1].size();
1138 }
1139
1140
1141
1142 // now concatenate all the
1143 // components in the order the user
1144 // desired to see
1145 types::global_dof_index next_free_index = 0;
1146 for (unsigned int component = 0; component < fe_collection.n_components();
1147 ++component)
1148 {
1149 next_free_index = shifts[component];
1150
1151 for (const types::global_dof_index dof_index :
1152 component_to_dof_map[component])
1153 {
1154 Assert(locally_owned_dofs.index_within_set(dof_index) <
1155 new_indices.size(),
1157 new_indices[locally_owned_dofs.index_within_set(dof_index)] =
1158 next_free_index;
1159
1160 ++next_free_index;
1161 }
1162 }
1163
1164 return next_free_index;
1165 }
1166
1167
1168
1169 template <int dim, int spacedim>
1170 void
1172 {
1173 std::vector<types::global_dof_index> renumbering(
1175
1177 dim,
1178 spacedim,
1181 renumbering, dof_handler.begin_active(), dof_handler.end(), false);
1182 if (result == 0)
1183 return;
1184
1185 // verify that the last numbered
1186 // degree of freedom is either
1187 // equal to the number of degrees
1188 // of freedom in total (the
1189 // sequential case) or in the
1190 // distributed case at least
1191 // makes sense
1192 Assert((result == dof_handler.n_locally_owned_dofs()) ||
1193 ((dof_handler.n_locally_owned_dofs() < dof_handler.n_dofs()) &&
1194 (result <= dof_handler.n_dofs())),
1196
1197 dof_handler.renumber_dofs(renumbering);
1198 }
1199
1200
1201
1202 template <int dim, int spacedim>
1203 void
1204 block_wise(DoFHandler<dim, spacedim> &dof_handler, const unsigned int level)
1205 {
1208
1209 std::vector<types::global_dof_index> renumbering(
1210 dof_handler.locally_owned_mg_dofs(level).n_elements(),
1212
1214 dof_handler.begin(level);
1216 dof_handler.end(level);
1217
1219 dim,
1220 spacedim,
1223 start,
1224 end,
1225 true);
1226 (void)result;
1227
1228 Assert(result == 0 || result == dof_handler.n_dofs(level),
1230
1231 // If we don't have a renumbering (i.e., when there is 1 component) then
1232 // return
1233 if (Utilities::MPI::logical_and(renumbering.size() == 0,
1234 dof_handler.get_mpi_communicator()))
1235 return;
1236
1237 dof_handler.renumber_dofs(level, renumbering);
1238 }
1239
1240
1241
1242 template <int dim, int spacedim, class IteratorType, class EndIteratorType>
1244 compute_block_wise(std::vector<types::global_dof_index> &new_indices,
1245 const IteratorType &start,
1246 const EndIteratorType &end,
1247 const bool is_level_operation)
1248 {
1249 const hp::FECollection<dim, spacedim> &fe_collection =
1250 start->get_dof_handler().get_fe_collection();
1251
1252 // do nothing if the FE has only
1253 // one component
1254 if (fe_collection.n_blocks() == 1)
1255 {
1256 new_indices.resize(0);
1257 return 0;
1258 }
1259
1260 // Get a reference to the set of dofs. Note that we assume that all cells
1261 // are assumed to be on the same level, otherwise the operation doesn't make
1262 // much sense (we will assert this below).
1263 const IndexSet &locally_owned_dofs =
1264 is_level_operation ?
1265 start->get_dof_handler().locally_owned_mg_dofs(start->level()) :
1266 start->get_dof_handler().locally_owned_dofs();
1267
1268 // vector to hold the dof indices on
1269 // the cell we visit at a time
1270 std::vector<types::global_dof_index> local_dof_indices;
1271
1272 // prebuilt list to which block
1273 // a given dof on a cell
1274 // should go.
1275 std::vector<std::vector<types::global_dof_index>> block_list(
1276 fe_collection.size());
1277 for (unsigned int f = 0; f < fe_collection.size(); ++f)
1278 {
1279 const FiniteElement<dim, spacedim> &fe = fe_collection[f];
1280 block_list[f].resize(fe.n_dofs_per_cell());
1281 for (unsigned int i = 0; i < fe.n_dofs_per_cell(); ++i)
1282 block_list[f][i] = fe.system_to_block_index(i).first;
1283 }
1284
1285 // set up a map where for each
1286 // block the respective degrees
1287 // of freedom are collected.
1288 //
1289 // note that this map is sorted by
1290 // block but that within each
1291 // block it is NOT sorted by
1292 // dof index. note also that some
1293 // dof indices are entered
1294 // multiply, so we will have to
1295 // take care of that
1296 std::vector<std::vector<types::global_dof_index>> block_to_dof_map(
1297 fe_collection.n_blocks());
1298 for (IteratorType cell = start; cell != end; ++cell)
1299 {
1300 if (is_level_operation)
1301 {
1302 // we are dealing with mg dofs, skip foreign level cells:
1303 if (!cell->is_locally_owned_on_level())
1304 continue;
1305 }
1306 else
1307 {
1308 // we are dealing with active dofs, skip the loop if not locally
1309 // owned:
1310 if (!cell->is_locally_owned())
1311 continue;
1312 }
1313
1314 if (is_level_operation)
1315 Assert(
1316 cell->level() == start->level(),
1317 ExcMessage(
1318 "Multigrid renumbering in compute_block_wise() needs to be applied to a single level!"));
1319
1320 // on each cell: get dof indices
1321 // and insert them into the global
1322 // list using their component
1323 const types::fe_index fe_index = cell->active_fe_index();
1324 const unsigned int dofs_per_cell =
1325 fe_collection[fe_index].n_dofs_per_cell();
1326 local_dof_indices.resize(dofs_per_cell);
1327 cell->get_active_or_mg_dof_indices(local_dof_indices);
1328
1329 for (unsigned int i = 0; i < dofs_per_cell; ++i)
1330 if (locally_owned_dofs.is_element(local_dof_indices[i]))
1331 block_to_dof_map[block_list[fe_index][i]].push_back(
1332 local_dof_indices[i]);
1333 }
1334
1335 // now we've got all indices sorted
1336 // into buckets labeled by their
1337 // target block number. we've
1338 // only got to traverse this list
1339 // and assign the new indices
1340 //
1341 // however, we first want to sort
1342 // the indices entered into the
1343 // buckets to preserve the order
1344 // within each component and during
1345 // this also remove duplicate
1346 // entries
1347 for (unsigned int block = 0; block < fe_collection.n_blocks(); ++block)
1348 {
1349 std::sort(block_to_dof_map[block].begin(),
1350 block_to_dof_map[block].end());
1351 block_to_dof_map[block].erase(
1352 std::unique(block_to_dof_map[block].begin(),
1353 block_to_dof_map[block].end()),
1354 block_to_dof_map[block].end());
1355 }
1356
1357 // calculate the number of locally owned
1358 // DoFs per bucket
1359 const unsigned int n_buckets = fe_collection.n_blocks();
1360 std::vector<types::global_dof_index> shifts(n_buckets);
1361
1363 (dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1364 &start->get_dof_handler().get_triangulation())))
1365 {
1366#ifdef DEAL_II_WITH_MPI
1367 std::vector<types::global_dof_index> local_dof_count(n_buckets);
1368
1369 for (unsigned int c = 0; c < n_buckets; ++c)
1370 local_dof_count[c] = block_to_dof_map[c].size();
1371
1372 std::vector<types::global_dof_index> prefix_dof_count(n_buckets);
1373 const int ierr = MPI_Exscan(
1374 local_dof_count.data(),
1375 prefix_dof_count.data(),
1376 n_buckets,
1377 Utilities::MPI::mpi_type_id_for_type<types::global_dof_index>,
1378 MPI_SUM,
1379 tria->get_mpi_communicator());
1380 AssertThrowMPI(ierr);
1381
1382 std::vector<types::global_dof_index> global_dof_count(n_buckets);
1383 Utilities::MPI::sum(local_dof_count,
1384 tria->get_mpi_communicator(),
1385 global_dof_count);
1386
1387 // calculate shifts
1388 types::global_dof_index cumulated = 0;
1389 for (unsigned int c = 0; c < n_buckets; ++c)
1390 {
1391 shifts[c] = prefix_dof_count[c] + cumulated;
1392 cumulated += global_dof_count[c];
1393 }
1394#else
1395 (void)tria;
1397#endif
1398 }
1399 else
1400 {
1401 shifts[0] = 0;
1402 for (unsigned int c = 1; c < fe_collection.n_blocks(); ++c)
1403 shifts[c] = shifts[c - 1] + block_to_dof_map[c - 1].size();
1404 }
1405
1406
1407
1408 // now concatenate all the
1409 // components in the order the user
1410 // desired to see
1411 types::global_dof_index next_free_index = 0;
1412 for (unsigned int block = 0; block < fe_collection.n_blocks(); ++block)
1413 {
1414 const typename std::vector<types::global_dof_index>::const_iterator
1415 begin_of_component = block_to_dof_map[block].begin(),
1416 end_of_component = block_to_dof_map[block].end();
1417
1418 next_free_index = shifts[block];
1419
1420 for (typename std::vector<types::global_dof_index>::const_iterator
1421 dof_index = begin_of_component;
1422 dof_index != end_of_component;
1423 ++dof_index)
1424 {
1425 Assert(locally_owned_dofs.index_within_set(*dof_index) <
1426 new_indices.size(),
1428 new_indices[locally_owned_dofs.index_within_set(*dof_index)] =
1429 next_free_index++;
1430 }
1431 }
1432
1433 return next_free_index;
1434 }
1435
1436
1437
1438 namespace
1439 {
1440 // Helper function for DoFRenumbering::hierarchical(). This function
1441 // recurses into the given cell or, if that should be an active (terminal)
1442 // cell, renumbers DoF indices on it. The function starts renumbering with
1443 // 'next_free_dof_index' and returns the first still unused DoF index at the
1444 // end of its operation.
1445 template <int dim, typename CellIteratorType>
1447 compute_hierarchical_recursive(
1448 const types::global_dof_index next_free_dof_offset,
1449 const types::global_dof_index my_starting_index,
1450 const CellIteratorType &cell,
1451 const IndexSet &locally_owned_dof_indices,
1452 std::vector<types::global_dof_index> &new_indices)
1453 {
1454 types::global_dof_index current_next_free_dof_offset =
1455 next_free_dof_offset;
1456
1457 if (cell->has_children())
1458 {
1459 // recursion
1460 for (unsigned int c = 0; c < GeometryInfo<dim>::max_children_per_cell;
1461 ++c)
1462 current_next_free_dof_offset =
1463 compute_hierarchical_recursive<dim>(current_next_free_dof_offset,
1464 my_starting_index,
1465 cell->child(c),
1466 locally_owned_dof_indices,
1467 new_indices);
1468 }
1469 else
1470 {
1471 // this is a terminal cell. we need to renumber its DoF indices. there
1472 // are now three cases to decide:
1473 // - this is a sequential triangulation: we can just go ahead and
1474 // number
1475 // the DoFs in the order in which we encounter cells. in this case,
1476 // all cells are actually locally owned
1477 // - if this is a parallel::distributed::Triangulation, then we only
1478 // need to work on the locally owned cells since they contain
1479 // all locally owned DoFs.
1480 // - if this is a parallel::shared::Triangulation, then the same
1481 // applies
1482 //
1483 // in all cases, each processor starts new indices so that we get
1484 // a consecutive numbering on each processor, and disjoint ownership
1485 // of the global range of DoF indices
1486 if (cell->is_locally_owned())
1487 {
1488 // first get the existing DoF indices
1489 const unsigned int dofs_per_cell =
1490 cell->get_fe().n_dofs_per_cell();
1491 std::vector<types::global_dof_index> local_dof_indices(
1492 dofs_per_cell);
1493 cell->get_dof_indices(local_dof_indices);
1494
1495 // then loop over the existing DoF indices on this cell
1496 // and see whether it has already been re-numbered (it
1497 // may have been on a face or vertex to a neighboring
1498 // cell that we have encountered before). if not,
1499 // give it a new number and store that number in the
1500 // output array (new_indices)
1501 //
1502 // if this is a parallel triangulation and a DoF index is
1503 // not locally owned, then don't touch it. since
1504 // we don't actually *change* DoF indices (just record new
1505 // numbers in an array), we don't need to worry about
1506 // the decision whether a DoF is locally owned or not changing
1507 // as we progress in renumbering DoFs -- all adjacent cells
1508 // will always agree that a DoF is locally owned or not.
1509 // that said, the first cell to encounter a locally owned DoF
1510 // gets to number it, so the order in which we traverse cells
1511 // matters
1512 for (unsigned int i = 0; i < dofs_per_cell; ++i)
1513 if (locally_owned_dof_indices.is_element(local_dof_indices[i]))
1514 {
1515 // this is a locally owned DoF, assign new number if not
1516 // assigned a number yet
1517 const unsigned int idx =
1518 locally_owned_dof_indices.index_within_set(
1519 local_dof_indices[i]);
1520 if (new_indices[idx] == numbers::invalid_dof_index)
1521 {
1522 new_indices[idx] =
1523 my_starting_index + current_next_free_dof_offset;
1524 ++current_next_free_dof_offset;
1525 }
1526 }
1527 }
1528 }
1529
1530 return current_next_free_dof_offset;
1531 }
1532 } // namespace
1533
1534
1535
1536 template <int dim, int spacedim>
1537 void
1539 {
1540 std::vector<types::global_dof_index> renumbering(
1542
1543 types::global_dof_index next_free_dof_offset = 0;
1544 const IndexSet locally_owned = dof_handler.locally_owned_dofs();
1545
1546 // in the function we call recursively, we want to number DoFs so
1547 // that global cell zero starts with DoF zero, regardless of how
1548 // DoFs were previously numbered. to this end, we need to figure
1549 // out which DoF index the current processor should start with.
1550 //
1551 // if this is a sequential triangulation, then obviously the starting
1552 // index is zero. otherwise, make sure we get contiguous, successive
1553 // ranges on each processor. note that since the number of locally owned
1554 // DoFs is an invariant under renumbering, we can easily compute this
1555 // starting index by just accumulating over the number of locally owned
1556 // DoFs for all previous processes
1557 types::global_dof_index my_starting_index = 0;
1558
1560 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1561 &dof_handler.get_triangulation()))
1562 {
1563#ifdef DEAL_II_WITH_MPI
1565 dof_handler.locally_owned_dofs().n_elements();
1566 const int ierr = MPI_Exscan(
1568 &my_starting_index,
1569 1,
1570 Utilities::MPI::mpi_type_id_for_type<types::global_dof_index>,
1571 MPI_SUM,
1572 tria->get_mpi_communicator());
1573 AssertThrowMPI(ierr);
1574#endif
1575 }
1576
1579 *>(&dof_handler.get_triangulation()))
1580 {
1581#ifdef DEAL_II_WITH_P4EST
1582 // this is a distributed Triangulation. we need to traverse the coarse
1583 // cells in the order p4est does to match the z-order actually used
1584 // by p4est. this requires using the renumbering of coarse cells
1585 // we do before we hand things off to p4est
1586 for (unsigned int c = 0; c < tria->n_cells(0); ++c)
1587 {
1588 const unsigned int coarse_cell_index =
1589 tria->get_p4est_tree_to_coarse_cell_permutation()[c];
1590
1592 this_cell(tria, 0, coarse_cell_index, &dof_handler);
1593
1594 next_free_dof_offset =
1595 compute_hierarchical_recursive<dim>(next_free_dof_offset,
1596 my_starting_index,
1597 this_cell,
1598 locally_owned,
1599 renumbering);
1600 }
1601#else
1603#endif
1604 }
1605 else
1606 {
1607 // this is not a distributed Triangulation, so we can traverse coarse
1608 // cells in the normal order
1609 for (typename DoFHandler<dim, spacedim>::cell_iterator cell =
1610 dof_handler.begin(0);
1611 cell != dof_handler.end(0);
1612 ++cell)
1613 next_free_dof_offset =
1614 compute_hierarchical_recursive<dim>(next_free_dof_offset,
1615 my_starting_index,
1616 cell,
1617 locally_owned,
1618 renumbering);
1619 }
1620
1621 // verify that the last numbered degree of freedom is either
1622 // equal to the number of degrees of freedom in total (the
1623 // sequential case) or in the distributed case at least
1624 // makes sense
1625 Assert((next_free_dof_offset == dof_handler.n_locally_owned_dofs()) ||
1626 ((dof_handler.n_locally_owned_dofs() < dof_handler.n_dofs()) &&
1627 (next_free_dof_offset <= dof_handler.n_dofs())),
1629
1630 // make sure that all local DoFs got new numbers assigned
1631 Assert(std::find(renumbering.begin(),
1632 renumbering.end(),
1633 numbers::invalid_dof_index) == renumbering.end(),
1635
1636 dof_handler.renumber_dofs(renumbering);
1637 }
1638
1639
1640
1641 template <int dim, int spacedim>
1642 void
1644 const std::vector<bool> &selected_dofs)
1645 {
1646 std::vector<types::global_dof_index> renumbering(
1647 dof_handler.n_dofs(), numbers::invalid_dof_index);
1648 compute_sort_selected_dofs_back(renumbering, dof_handler, selected_dofs);
1649
1650 dof_handler.renumber_dofs(renumbering);
1651 }
1652
1653
1654
1655 template <int dim, int spacedim>
1656 void
1658 const std::vector<bool> &selected_dofs,
1659 const unsigned int level)
1660 {
1663
1664 std::vector<types::global_dof_index> renumbering(
1667 dof_handler,
1668 selected_dofs,
1669 level);
1670
1671 dof_handler.renumber_dofs(level, renumbering);
1672 }
1673
1674
1675
1676 template <int dim, int spacedim>
1677 void
1679 std::vector<types::global_dof_index> &new_indices,
1680 const DoFHandler<dim, spacedim> &dof_handler,
1681 const std::vector<bool> &selected_dofs)
1682 {
1683 const types::global_dof_index n_dofs = dof_handler.n_dofs();
1684 Assert(selected_dofs.size() == n_dofs,
1685 ExcDimensionMismatch(selected_dofs.size(), n_dofs));
1686
1687 // re-sort the dofs according to
1688 // their selection state
1689 Assert(new_indices.size() == n_dofs,
1690 ExcDimensionMismatch(new_indices.size(), n_dofs));
1691
1692 const types::global_dof_index n_selected_dofs =
1693 std::count(selected_dofs.begin(), selected_dofs.end(), false);
1694
1695 types::global_dof_index next_unselected = 0;
1696 types::global_dof_index next_selected = n_selected_dofs;
1697 for (types::global_dof_index i = 0; i < n_dofs; ++i)
1698 if (selected_dofs[i] == false)
1699 {
1700 new_indices[i] = next_unselected;
1701 ++next_unselected;
1702 }
1703 else
1704 {
1705 new_indices[i] = next_selected;
1706 ++next_selected;
1707 }
1708 Assert(next_unselected == n_selected_dofs, ExcInternalError());
1709 Assert(next_selected == n_dofs, ExcInternalError());
1710 }
1711
1712
1713
1714 template <int dim, int spacedim>
1715 void
1717 std::vector<types::global_dof_index> &new_indices,
1718 const DoFHandler<dim, spacedim> &dof_handler,
1719 const std::vector<bool> &selected_dofs,
1720 const unsigned int level)
1721 {
1724
1725 const types::global_dof_index n_dofs = dof_handler.n_dofs(level);
1726 Assert(selected_dofs.size() == n_dofs,
1727 ExcDimensionMismatch(selected_dofs.size(), n_dofs));
1728
1729 // re-sort the dofs according to
1730 // their selection state
1731 Assert(new_indices.size() == n_dofs,
1732 ExcDimensionMismatch(new_indices.size(), n_dofs));
1733
1734 const types::global_dof_index n_selected_dofs =
1735 std::count(selected_dofs.begin(), selected_dofs.end(), false);
1736
1737 types::global_dof_index next_unselected = 0;
1738 types::global_dof_index next_selected = n_selected_dofs;
1739 for (types::global_dof_index i = 0; i < n_dofs; ++i)
1740 if (selected_dofs[i] == false)
1741 {
1742 new_indices[i] = next_unselected;
1743 ++next_unselected;
1744 }
1745 else
1746 {
1747 new_indices[i] = next_selected;
1748 ++next_selected;
1749 }
1750 Assert(next_unselected == n_selected_dofs, ExcInternalError());
1751 Assert(next_selected == n_dofs, ExcInternalError());
1752 }
1753
1754
1755
1756 template <int dim, int spacedim>
1757 void
1760 const std::vector<typename DoFHandler<dim, spacedim>::active_cell_iterator>
1761 &cells)
1762 {
1763 std::vector<types::global_dof_index> renumbering(
1764 dof.n_locally_owned_dofs());
1765 std::vector<types::global_dof_index> reverse(dof.n_locally_owned_dofs());
1766 compute_cell_wise(renumbering, reverse, dof, cells);
1767
1768 dof.renumber_dofs(renumbering);
1769 }
1770
1771
1772 template <int dim, int spacedim>
1773 void
1775 std::vector<types::global_dof_index> &new_indices,
1776 std::vector<types::global_dof_index> &reverse,
1777 const DoFHandler<dim, spacedim> &dof,
1778 const typename std::vector<
1780 {
1782 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1783 &dof.get_triangulation()))
1784 {
1785 AssertDimension(cells.size(), p->n_locally_owned_active_cells());
1786 }
1787 else
1788 {
1789 AssertDimension(cells.size(), dof.get_triangulation().n_active_cells());
1790 }
1791
1792 const auto n_owned_dofs = dof.n_locally_owned_dofs();
1793
1794 // Actually, we compute the inverse of the reordering vector, called reverse
1795 // here. Later, its inverse is computed into new_indices, which is the
1796 // return argument.
1797
1798 AssertDimension(new_indices.size(), n_owned_dofs);
1799 AssertDimension(reverse.size(), n_owned_dofs);
1800
1801 // For continuous elements, we must make sure, that each dof is reordered
1802 // only once.
1803 std::vector<bool> already_sorted(n_owned_dofs, false);
1804 std::vector<types::global_dof_index> cell_dofs;
1805
1806 const auto &owned_dofs = dof.locally_owned_dofs();
1807
1808 types::global_dof_index index = 0;
1809
1810 for (const auto &cell : cells)
1811 {
1812 // Determine the number of dofs on this cell and reinit the
1813 // vector storing these numbers.
1814 const unsigned int n_cell_dofs = cell->get_fe().n_dofs_per_cell();
1815 cell_dofs.resize(n_cell_dofs);
1816
1817 cell->get_active_or_mg_dof_indices(cell_dofs);
1818
1819 // Sort here to make sure that degrees of freedom inside a single cell
1820 // are in the same order after renumbering.
1821 std::sort(cell_dofs.begin(), cell_dofs.end());
1822
1823 for (const auto dof : cell_dofs)
1824 {
1825 const auto local_dof = owned_dofs.index_within_set(dof);
1826 if (local_dof != numbers::invalid_dof_index &&
1827 !already_sorted[local_dof])
1828 {
1829 already_sorted[local_dof] = true;
1830 reverse[index++] = local_dof;
1831 }
1832 }
1833 }
1834 Assert(index == n_owned_dofs,
1835 ExcMessage(
1836 "Traversing over the given set of cells did not cover all "
1837 "degrees of freedom in the DoFHandler. Does the set of cells "
1838 "not include all active cells?"));
1839
1840 for (types::global_dof_index i = 0; i < reverse.size(); ++i)
1841 new_indices[reverse[i]] = owned_dofs.nth_index_in_set(i);
1842 }
1843
1844
1845
1846 template <int dim, int spacedim>
1847 void
1849 const unsigned int level,
1850 const typename std::vector<
1852 {
1855
1856 std::vector<types::global_dof_index> renumbering(dof.n_dofs(level));
1857 std::vector<types::global_dof_index> reverse(dof.n_dofs(level));
1858
1859 compute_cell_wise(renumbering, reverse, dof, level, cells);
1860 dof.renumber_dofs(level, renumbering);
1861 }
1862
1863
1864
1865 template <int dim, int spacedim>
1866 void
1868 std::vector<types::global_dof_index> &new_order,
1869 std::vector<types::global_dof_index> &reverse,
1870 const DoFHandler<dim, spacedim> &dof,
1871 const unsigned int level,
1872 const typename std::vector<
1874 {
1875 Assert(cells.size() == dof.get_triangulation().n_cells(level),
1876 ExcDimensionMismatch(cells.size(),
1878 Assert(new_order.size() == dof.n_dofs(level),
1879 ExcDimensionMismatch(new_order.size(), dof.n_dofs(level)));
1880 Assert(reverse.size() == dof.n_dofs(level),
1881 ExcDimensionMismatch(reverse.size(), dof.n_dofs(level)));
1882
1883 const types::global_dof_index n_global_dofs = dof.n_dofs(level);
1884 const unsigned int n_cell_dofs = dof.get_fe().n_dofs_per_cell();
1885
1886 std::vector<bool> already_sorted(n_global_dofs, false);
1887 std::vector<types::global_dof_index> cell_dofs(n_cell_dofs);
1888
1889 types::global_dof_index global_index = 0;
1890
1891 for (const auto &cell : cells)
1892 {
1893 Assert(cell->level() == static_cast<int>(level), ExcInternalError());
1894
1895 cell->get_active_or_mg_dof_indices(cell_dofs);
1896 std::sort(cell_dofs.begin(), cell_dofs.end());
1897
1898 for (unsigned int i = 0; i < n_cell_dofs; ++i)
1899 {
1900 if (!already_sorted[cell_dofs[i]])
1901 {
1902 already_sorted[cell_dofs[i]] = true;
1903 reverse[global_index++] = cell_dofs[i];
1904 }
1905 }
1906 }
1907 Assert(global_index == n_global_dofs,
1908 ExcMessage(
1909 "Traversing over the given set of cells did not cover all "
1910 "degrees of freedom in the DoFHandler. Does the set of cells "
1911 "not include all cells of the specified level?"));
1912
1913 for (types::global_dof_index i = 0; i < new_order.size(); ++i)
1914 new_order[reverse[i]] = i;
1915 }
1916
1917
1918
1919 template <int dim, int spacedim>
1920 void
1922 const Tensor<1, spacedim> &direction,
1923 const bool dof_wise_renumbering)
1924 {
1925 std::vector<types::global_dof_index> renumbering(dof.n_dofs());
1926 std::vector<types::global_dof_index> reverse(dof.n_dofs());
1928 renumbering, reverse, dof, direction, dof_wise_renumbering);
1929
1930 dof.renumber_dofs(renumbering);
1931 }
1932
1933
1934
1935 template <int dim, int spacedim>
1936 void
1937 compute_downstream(std::vector<types::global_dof_index> &new_indices,
1938 std::vector<types::global_dof_index> &reverse,
1939 const DoFHandler<dim, spacedim> &dof,
1940 const Tensor<1, spacedim> &direction,
1941 const bool dof_wise_renumbering)
1942 {
1944 &dof.get_triangulation()) == nullptr),
1946
1947 if (dof_wise_renumbering == false)
1948 {
1949 std::vector<typename DoFHandler<dim, spacedim>::active_cell_iterator>
1950 ordered_cells;
1951 ordered_cells.reserve(dof.get_triangulation().n_active_cells());
1952 const CompareDownstream<
1954 spacedim>
1955 comparator(direction);
1956
1957 for (const auto &cell : dof.active_cell_iterators())
1958 ordered_cells.push_back(cell);
1959
1960 std::sort(ordered_cells.begin(), ordered_cells.end(), comparator);
1961
1962 compute_cell_wise(new_indices, reverse, dof, ordered_cells);
1963 }
1964 else
1965 {
1966 // similar code as for
1967 // DoFTools::map_dofs_to_support_points, but
1968 // need to do this for general DoFHandler<dim, spacedim> classes and
1969 // want to be able to sort the result
1970 // (otherwise, could use something like
1971 // DoFTools::map_support_points_to_dofs)
1972 const unsigned int n_dofs = dof.n_dofs();
1973 std::vector<std::pair<Point<spacedim>, unsigned int>>
1974 support_point_list(n_dofs);
1975
1976 const hp::FECollection<dim> &fe_collection = dof.get_fe_collection();
1977 Assert(fe_collection[0].has_support_points(),
1979 hp::QCollection<dim> quadrature_collection;
1980 for (unsigned int comp = 0; comp < fe_collection.size(); ++comp)
1981 {
1982 Assert(fe_collection[comp].has_support_points(),
1984 quadrature_collection.push_back(
1985 Quadrature<dim>(fe_collection[comp].get_unit_support_points()));
1986 }
1987 hp::FEValues<dim, spacedim> hp_fe_values(fe_collection,
1988 quadrature_collection,
1990
1991 std::vector<bool> already_touched(n_dofs, false);
1992
1993 std::vector<types::global_dof_index> local_dof_indices;
1994
1995 for (const auto &cell : dof.active_cell_iterators())
1996 {
1997 const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
1998 local_dof_indices.resize(dofs_per_cell);
1999 hp_fe_values.reinit(cell);
2000 const FEValues<dim> &fe_values =
2001 hp_fe_values.get_present_fe_values();
2002 cell->get_active_or_mg_dof_indices(local_dof_indices);
2003 const std::vector<Point<spacedim>> &points =
2004 fe_values.get_quadrature_points();
2005 for (unsigned int i = 0; i < dofs_per_cell; ++i)
2006 if (!already_touched[local_dof_indices[i]])
2007 {
2008 support_point_list[local_dof_indices[i]].first = points[i];
2009 support_point_list[local_dof_indices[i]].second =
2010 local_dof_indices[i];
2011 already_touched[local_dof_indices[i]] = true;
2012 }
2013 }
2014
2015 ComparePointwiseDownstream<spacedim> comparator(direction);
2016 std::sort(support_point_list.begin(),
2017 support_point_list.end(),
2018 comparator);
2019 for (types::global_dof_index i = 0; i < n_dofs; ++i)
2020 new_indices[support_point_list[i].second] = i;
2021 }
2022 }
2023
2024
2025
2026 template <int dim, int spacedim>
2027 void
2029 const unsigned int level,
2030 const Tensor<1, spacedim> &direction,
2031 const bool dof_wise_renumbering)
2032 {
2033 std::vector<types::global_dof_index> renumbering(dof.n_dofs(level));
2034 std::vector<types::global_dof_index> reverse(dof.n_dofs(level));
2036 renumbering, reverse, dof, level, direction, dof_wise_renumbering);
2037
2038 dof.renumber_dofs(level, renumbering);
2039 }
2040
2041
2042
2043 template <int dim, int spacedim>
2044 void
2045 compute_downstream(std::vector<types::global_dof_index> &new_indices,
2046 std::vector<types::global_dof_index> &reverse,
2047 const DoFHandler<dim, spacedim> &dof,
2048 const unsigned int level,
2049 const Tensor<1, spacedim> &direction,
2050 const bool dof_wise_renumbering)
2051 {
2052 if (dof_wise_renumbering == false)
2053 {
2054 std::vector<typename DoFHandler<dim, spacedim>::level_cell_iterator>
2055 ordered_cells;
2056 ordered_cells.reserve(dof.get_triangulation().n_cells(level));
2057 const CompareDownstream<
2059 spacedim>
2060 comparator(direction);
2061
2063 dof.begin(level);
2065 dof.end(level);
2066
2067 while (p != end)
2068 {
2069 ordered_cells.push_back(p);
2070 ++p;
2071 }
2072 std::sort(ordered_cells.begin(), ordered_cells.end(), comparator);
2073
2074 compute_cell_wise(new_indices, reverse, dof, level, ordered_cells);
2075 }
2076 else
2077 {
2080 const types::global_dof_index n_dofs = dof.n_dofs(level);
2081 std::vector<std::pair<Point<spacedim>, unsigned int>>
2082 support_point_list(n_dofs);
2083
2084 const Quadrature<dim> q_dummy(dof.get_fe().get_unit_support_points());
2085 FEValues<dim, spacedim> fe_values(dof.get_fe(),
2086 q_dummy,
2088
2089 std::vector<bool> already_touched(dof.n_dofs(), false);
2090
2091 const unsigned int dofs_per_cell = dof.get_fe().n_dofs_per_cell();
2092 std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
2094 dof.begin(level);
2096 dof.end(level);
2097 for (; begin != end; ++begin)
2098 {
2100 &begin_tria = begin;
2101 begin->get_active_or_mg_dof_indices(local_dof_indices);
2102 fe_values.reinit(begin_tria);
2103 const std::vector<Point<spacedim>> &points =
2104 fe_values.get_quadrature_points();
2105 for (unsigned int i = 0; i < dofs_per_cell; ++i)
2106 if (!already_touched[local_dof_indices[i]])
2107 {
2108 support_point_list[local_dof_indices[i]].first = points[i];
2109 support_point_list[local_dof_indices[i]].second =
2110 local_dof_indices[i];
2111 already_touched[local_dof_indices[i]] = true;
2112 }
2113 }
2114
2115 ComparePointwiseDownstream<spacedim> comparator(direction);
2116 std::sort(support_point_list.begin(),
2117 support_point_list.end(),
2118 comparator);
2119 for (types::global_dof_index i = 0; i < n_dofs; ++i)
2120 new_indices[support_point_list[i].second] = i;
2121 }
2122 }
2123
2124
2125
2129 namespace internal
2130 {
2131 template <int dim>
2133 {
2142
2147 : center(center)
2148 , counter(counter)
2149 {}
2150
2154 template <class DHCellIterator>
2155 bool
2156 operator()(const DHCellIterator &c1, const DHCellIterator &c2) const
2157 {
2158 // dispatch to
2159 // dimension-dependent functions
2160 return compare(c1, c2, std::integral_constant<int, dim>());
2161 }
2162
2163 private:
2167 template <class DHCellIterator, int xdim>
2168 bool
2169 compare(const DHCellIterator &c1,
2170 const DHCellIterator &c2,
2171 std::integral_constant<int, xdim>) const
2172 {
2173 const Tensor<1, dim> v1 = c1->center() - center;
2174 const Tensor<1, dim> v2 = c2->center() - center;
2175 const double s1 = std::atan2(v1[0], v1[1]);
2176 const double s2 = std::atan2(v2[0], v2[1]);
2177 return (counter ? (s1 > s2) : (s2 > s1));
2178 }
2179
2180
2185 template <class DHCellIterator>
2186 bool
2187 compare(const DHCellIterator &,
2188 const DHCellIterator &,
2189 std::integral_constant<int, 1>) const
2190 {
2191 Assert(dim >= 2,
2192 ExcMessage("This operation only makes sense for dim>=2."));
2193 return false;
2194 }
2195 };
2196 } // namespace internal
2197
2198
2199
2200 template <int dim, int spacedim>
2201 void
2203 const Point<spacedim> &center,
2204 const bool counter)
2205 {
2206 std::vector<types::global_dof_index> renumbering(dof.n_dofs());
2207 compute_clockwise_dg(renumbering, dof, center, counter);
2208
2209 dof.renumber_dofs(renumbering);
2210 }
2211
2212
2213
2214 template <int dim, int spacedim>
2215 void
2216 compute_clockwise_dg(std::vector<types::global_dof_index> &new_indices,
2217 const DoFHandler<dim, spacedim> &dof,
2218 const Point<spacedim> &center,
2219 const bool counter)
2220 {
2221 std::vector<typename DoFHandler<dim, spacedim>::active_cell_iterator>
2222 ordered_cells;
2223 ordered_cells.reserve(dof.get_triangulation().n_active_cells());
2224 internal::ClockCells<spacedim> comparator(center, counter);
2225
2226 for (const auto &cell : dof.active_cell_iterators())
2227 ordered_cells.push_back(cell);
2228
2229 std::sort(ordered_cells.begin(), ordered_cells.end(), comparator);
2230
2231 std::vector<types::global_dof_index> reverse(new_indices.size());
2232 compute_cell_wise(new_indices, reverse, dof, ordered_cells);
2233 }
2234
2235
2236
2237 template <int dim, int spacedim>
2238 void
2240 const unsigned int level,
2241 const Point<spacedim> &center,
2242 const bool counter)
2243 {
2244 std::vector<typename DoFHandler<dim, spacedim>::level_cell_iterator>
2245 ordered_cells;
2246 ordered_cells.reserve(dof.get_triangulation().n_active_cells());
2247 internal::ClockCells<spacedim> comparator(center, counter);
2248
2250 dof.begin(level);
2252 dof.end(level);
2253
2254 while (p != end)
2255 {
2256 ordered_cells.push_back(p);
2257 ++p;
2258 }
2259 std::sort(ordered_cells.begin(), ordered_cells.end(), comparator);
2260
2261 cell_wise(dof, level, ordered_cells);
2262 }
2263
2264
2265
2266 template <int dim, int spacedim>
2267 void
2269 {
2270 std::vector<types::global_dof_index> renumbering(
2271 dof_handler.n_dofs(), numbers::invalid_dof_index);
2272 compute_random(renumbering, dof_handler);
2273
2274 dof_handler.renumber_dofs(renumbering);
2275 }
2276
2277
2278
2279 template <int dim, int spacedim>
2280 void
2281 random(DoFHandler<dim, spacedim> &dof_handler, const unsigned int level)
2282 {
2285
2286 std::vector<types::global_dof_index> renumbering(
2287 dof_handler.locally_owned_mg_dofs(level).n_elements(),
2289
2290 compute_random(renumbering, dof_handler, level);
2291
2292 dof_handler.renumber_dofs(level, renumbering);
2293 }
2294
2295
2296
2297 template <int dim, int spacedim>
2298 void
2299 compute_random(std::vector<types::global_dof_index> &new_indices,
2300 const DoFHandler<dim, spacedim> &dof_handler)
2301 {
2302 const types::global_dof_index n_dofs = dof_handler.n_dofs();
2303 Assert(new_indices.size() == n_dofs,
2304 ExcDimensionMismatch(new_indices.size(), n_dofs));
2305
2306 std::iota(new_indices.begin(),
2307 new_indices.end(),
2309
2310 // shuffle the elements; the following is essentially std::shuffle (which
2311 // is new in C++11) but with a boost URNG
2312 // we could use std::mt19937 here but doing so results in compiler-dependent
2313 // output
2314 ::boost::mt19937 random_number_generator;
2315 for (unsigned int i = 1; i < n_dofs; ++i)
2316 {
2317 // get a random number between 0 and i (inclusive)
2318 const unsigned int j =
2319 ::boost::random::uniform_int_distribution<>(0, i)(
2320 random_number_generator);
2321
2322 // if possible, swap the elements
2323 if (i != j)
2324 std::swap(new_indices[i], new_indices[j]);
2325 }
2326 }
2327
2328
2329
2330 template <int dim, int spacedim>
2331 void
2332 compute_random(std::vector<types::global_dof_index> &new_indices,
2333 const DoFHandler<dim, spacedim> &dof_handler,
2334 const unsigned int level)
2335 {
2336 const types::global_dof_index n_dofs = dof_handler.n_dofs(level);
2337 Assert(new_indices.size() == n_dofs,
2338 ExcDimensionMismatch(new_indices.size(), n_dofs));
2339
2340 std::iota(new_indices.begin(),
2341 new_indices.end(),
2343
2344 // shuffle the elements; the following is essentially std::shuffle (which
2345 // is new in C++11) but with a boost URNG
2346 // we could use std::mt19937 here but doing so results in
2347 // compiler-dependent output
2348 ::boost::mt19937 random_number_generator;
2349 for (unsigned int i = 1; i < n_dofs; ++i)
2350 {
2351 // get a random number between 0 and i (inclusive)
2352 const unsigned int j =
2353 ::boost::random::uniform_int_distribution<>(0, i)(
2354 random_number_generator);
2355
2356 // if possible, swap the elements
2357 if (i != j)
2358 std::swap(new_indices[i], new_indices[j]);
2359 }
2360 }
2361
2362
2363
2364 template <int dim, int spacedim>
2365 void
2367 {
2368 Assert(
2369 (!dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
2370 &dof_handler.get_triangulation())),
2371 ExcMessage(
2372 "Parallel triangulations are already enumerated according to their MPI process id."));
2373
2374 std::vector<types::global_dof_index> renumbering(
2375 dof_handler.n_dofs(), numbers::invalid_dof_index);
2376 compute_subdomain_wise(renumbering, dof_handler);
2377
2378 dof_handler.renumber_dofs(renumbering);
2379 }
2380
2381
2382
2383 template <int dim, int spacedim>
2384 void
2385 compute_subdomain_wise(std::vector<types::global_dof_index> &new_dof_indices,
2386 const DoFHandler<dim, spacedim> &dof_handler)
2387 {
2388 const types::global_dof_index n_dofs = dof_handler.n_dofs();
2389 Assert(new_dof_indices.size() == n_dofs,
2390 ExcDimensionMismatch(new_dof_indices.size(), n_dofs));
2391
2392 // first get the association of each dof
2393 // with a subdomain and determine the total
2394 // number of subdomain ids used
2395 std::vector<types::subdomain_id> subdomain_association(n_dofs);
2396 DoFTools::get_subdomain_association(dof_handler, subdomain_association);
2397 const unsigned int n_subdomains =
2398 *std::max_element(subdomain_association.begin(),
2399 subdomain_association.end()) +
2400 1;
2401
2402 // then renumber the subdomains by first
2403 // looking at those belonging to subdomain
2404 // 0, then those of subdomain 1, etc. note
2405 // that the algorithm is stable, i.e. if
2406 // two dofs i,j have i<j and belong to the
2407 // same subdomain, then they will be in
2408 // this order also after reordering
2409 std::fill(new_dof_indices.begin(),
2410 new_dof_indices.end(),
2412 types::global_dof_index next_free_index = 0;
2413 for (types::subdomain_id subdomain = 0; subdomain < n_subdomains;
2414 ++subdomain)
2415 for (types::global_dof_index i = 0; i < n_dofs; ++i)
2416 if (subdomain_association[i] == subdomain)
2417 {
2418 Assert(new_dof_indices[i] == numbers::invalid_dof_index,
2420 new_dof_indices[i] = next_free_index;
2421 ++next_free_index;
2422 }
2423
2424 // we should have numbered all dofs
2425 Assert(next_free_index == n_dofs, ExcInternalError());
2426 Assert(std::find(new_dof_indices.begin(),
2427 new_dof_indices.end(),
2428 numbers::invalid_dof_index) == new_dof_indices.end(),
2430 }
2431
2432
2433
2434 template <int dim, int spacedim>
2435 void
2437 {
2438 std::vector<types::global_dof_index> renumbering(
2440 compute_support_point_wise(renumbering, dof_handler);
2441
2442 // If we don't have a renumbering (i.e., when there is 1 component) then
2443 // return
2444 if (Utilities::MPI::logical_and(renumbering.size() == 0,
2445 dof_handler.get_mpi_communicator()))
2446 return;
2447
2448 dof_handler.renumber_dofs(renumbering);
2449 }
2450
2451
2452
2453 template <int dim, int spacedim>
2454 void
2456 std::vector<types::global_dof_index> &new_dof_indices,
2457 const DoFHandler<dim, spacedim> &dof_handler)
2458 {
2459 const types::global_dof_index n_dofs = dof_handler.n_locally_owned_dofs();
2460 Assert(new_dof_indices.size() == n_dofs,
2461 ExcDimensionMismatch(new_dof_indices.size(), n_dofs));
2462
2463 // This renumbering occurs in three steps:
2464 // 1. Compute the component-wise renumbering so that all DoFs of component
2465 // i are less than the DoFs of component i + 1.
2466 // 2. Compute a second renumbering component_to_nodal in which the
2467 // renumbering is now, for two components, [u0, v0, u1, v1, ...]: i.e.,
2468 // DoFs are first sorted by component and then by support point.
2469 // 3. Compose the two renumberings to obtain the final result.
2470
2471 // Step 1:
2472 std::vector<types::global_dof_index> component_renumbering(
2474 compute_component_wise<dim, spacedim>(component_renumbering,
2475 dof_handler.begin_active(),
2476 dof_handler.end(),
2477 std::vector<unsigned int>(),
2478 false);
2479
2480 if constexpr (running_in_debug_mode())
2481 {
2482 {
2483 const std::vector<types::global_dof_index> dofs_per_component =
2484 DoFTools::count_dofs_per_fe_component(dof_handler, true);
2485 for (const auto &dpc : dofs_per_component)
2486 Assert(dofs_per_component[0] == dpc, ExcNotImplemented());
2487 }
2488 }
2489 const unsigned int n_components =
2490 dof_handler.get_fe_collection().n_components();
2491 Assert(dof_handler.n_dofs() % n_components == 0, ExcInternalError());
2492 const types::global_dof_index dofs_per_component =
2493 dof_handler.n_dofs() / n_components;
2494 const types::global_dof_index local_dofs_per_component =
2495 dof_handler.n_locally_owned_dofs() / n_components;
2496
2497 // At this point we have no more communication to do - simplify things by
2498 // returning early if possible
2499 if (component_renumbering.empty())
2500 {
2501 new_dof_indices.resize(0);
2502 return;
2503 }
2504 std::fill(new_dof_indices.begin(),
2505 new_dof_indices.end(),
2507 // This index set equals what dof_handler.locally_owned_dofs() would be if
2508 // we executed the componentwise renumbering.
2509 IndexSet component_renumbered_dofs(dof_handler.n_dofs());
2510 // DoFs in each component are now consecutive, which IndexSet::add_indices()
2511 // can exploit by avoiding calls to sort. Make use of that by adding DoFs
2512 // one component at a time:
2513 std::vector<types::global_dof_index> component_dofs(
2514 local_dofs_per_component);
2515 for (unsigned int component = 0; component < n_components; ++component)
2516 {
2517 for (std::size_t i = 0; i < local_dofs_per_component; ++i)
2518 component_dofs[i] =
2519 component_renumbering[n_components * i + component];
2520 component_renumbered_dofs.add_indices(component_dofs.begin(),
2521 component_dofs.end());
2522 }
2523 component_renumbered_dofs.compress();
2524 if constexpr (running_in_debug_mode())
2525 {
2526 {
2527 IndexSet component_renumbered_dofs2(dof_handler.n_dofs());
2528 component_renumbered_dofs2.add_indices(component_renumbering.begin(),
2529 component_renumbering.end());
2530 Assert(component_renumbered_dofs2 == component_renumbered_dofs,
2532 }
2533 }
2534 for (const FiniteElement<dim, spacedim> &fe :
2535 dof_handler.get_fe_collection())
2536 {
2537 AssertThrow(fe.dofs_per_cell == 0 || fe.has_support_points(),
2539 for (unsigned int i = 0; i < fe.n_base_elements(); ++i)
2541 fe.base_element(0).get_unit_support_points() ==
2542 fe.base_element(i).get_unit_support_points(),
2543 ExcMessage(
2544 "All base elements should have the same support points."));
2545 }
2546
2547 std::vector<types::global_dof_index> component_to_nodal(
2549
2550 // Step 2:
2551 std::vector<types::global_dof_index> cell_dofs;
2552 std::vector<types::global_dof_index> component_renumbered_cell_dofs;
2553 const IndexSet &locally_owned_dofs = dof_handler.locally_owned_dofs();
2554 // Reuse the original index space for the new renumbering: it is the right
2555 // size and is contiguous on the current processor
2556 auto next_dof_it = locally_owned_dofs.begin();
2557 for (const auto &cell : dof_handler.active_cell_iterators())
2558 if (cell->is_locally_owned())
2559 {
2560 const FiniteElement<dim, spacedim> &fe = cell->get_fe();
2561 cell_dofs.resize(fe.dofs_per_cell);
2562 component_renumbered_cell_dofs.resize(fe.dofs_per_cell);
2563 cell->get_dof_indices(cell_dofs);
2564 // Apply the component renumbering while skipping any ghost dofs. This
2565 // algorithm assumes that all locally owned DoFs before the component
2566 // renumbering are still locally owned afterwards (just with a new
2567 // index).
2568 for (unsigned int i = 0; i < fe.dofs_per_cell; ++i)
2569 {
2570 if (locally_owned_dofs.is_element(cell_dofs[i]))
2571 {
2572 const auto local_index =
2573 locally_owned_dofs.index_within_set(cell_dofs[i]);
2574 component_renumbered_cell_dofs[i] =
2575 component_renumbering[local_index];
2576 }
2577 else
2578 {
2579 component_renumbered_cell_dofs[i] =
2581 }
2582 }
2583
2584 for (unsigned int i = 0; i < fe.dofs_per_cell; ++i)
2585 {
2586 if (fe.system_to_component_index(i).first == 0 &&
2587 component_renumbered_dofs.is_element(
2588 component_renumbered_cell_dofs[i]))
2589 {
2590 for (unsigned int component = 0;
2591 component < fe.n_components();
2592 ++component)
2593 {
2594 // Since we are indexing in an odd way here it is much
2595 // simpler to compute the permutation separately and
2596 // combine it at the end instead of doing both at once
2597 const auto local_index =
2598 component_renumbered_dofs.index_within_set(
2599 component_renumbered_cell_dofs[i] +
2600 dofs_per_component * component);
2601
2602 if (component_to_nodal[local_index] ==
2604 {
2605 component_to_nodal[local_index] = *next_dof_it;
2606 ++next_dof_it;
2607 }
2608 }
2609 }
2610 }
2611 }
2612
2613 // Step 3:
2614 for (std::size_t i = 0; i < dof_handler.n_locally_owned_dofs(); ++i)
2615 {
2616 const auto local_index =
2617 component_renumbered_dofs.index_within_set(component_renumbering[i]);
2618 new_dof_indices[i] = component_to_nodal[local_index];
2619 }
2620 }
2621
2622
2623
2624 template <int dim>
2625 void
2626 lexicographic(DoFHandler<dim> &dof_handler, const double tolerance)
2627 {
2628 std::vector<types::global_dof_index> renumbering;
2629 compute_lexicographic(renumbering, dof_handler, tolerance);
2630 dof_handler.renumber_dofs(renumbering);
2631 }
2632
2633
2634 template <int dim>
2635 void
2636 compute_lexicographic(std::vector<types::global_dof_index> &new_dof_indices,
2637 const DoFHandler<dim> &dof_handler,
2638 const double tolerance)
2639 {
2641 &dof_handler.get_triangulation()) == nullptr,
2642 ExcMessage(
2643 "Lexicographic renumbering is not implemented for distributed "
2644 "triangulations."));
2645
2646 std::map<types::global_dof_index, Point<dim>> dof_location_map =
2648 std::vector<std::pair<types::global_dof_index, Point<dim>>>
2649 dof_location_vector;
2650
2651 dof_location_vector.reserve(dof_location_map.size());
2652 for (const auto &s : dof_location_map)
2653 dof_location_vector.push_back(s);
2654
2655 std::sort(
2656 dof_location_vector.begin(),
2657 dof_location_vector.end(),
2658 [&](const std::pair<types::global_dof_index, Point<dim>> &p1,
2659 const std::pair<types::global_dof_index, Point<dim>> &p2) -> bool {
2660 for (int i = dim - 1; i >= 0; --i)
2661 {
2662 const double diff = p1.second(i) - p2.second(i);
2663 // Check if p1 is significantly smaller than p2 in this dimension
2664 if (diff < -tolerance)
2665 return true;
2666 // Check if p1 is significantly larger than p2 in this dimension
2667 if (diff > tolerance)
2668 return false;
2669 // Otherwise, coordinates are considered equal within tolerance for
2670 // this dimension, continue to the next dimension.
2671 }
2672 // All coordinates are within tolerance, points are considered
2673 // equivalent. Use the original DoF index as a tie-breaker to preserve
2674 // relative order.
2675 return p1.first < p2.first;
2676 });
2677
2678 new_dof_indices.resize(dof_location_vector.size(),
2680
2681 for (types::global_dof_index dof = 0; dof < dof_location_vector.size();
2682 ++dof)
2683 new_dof_indices[dof_location_vector[dof].first] = dof;
2684 }
2685
2686
2687
2688 template <int dim,
2689 int spacedim,
2690 typename Number,
2691 typename VectorizedArrayType>
2692 void
2694 DoFHandler<dim, spacedim> &dof_handler,
2696 {
2697 const std::vector<types::global_dof_index> new_global_numbers =
2698 compute_matrix_free_data_locality(dof_handler, matrix_free);
2699 if (matrix_free.get_mg_level() == ::numbers::invalid_unsigned_int)
2700 dof_handler.renumber_dofs(new_global_numbers);
2701 else
2702 dof_handler.renumber_dofs(matrix_free.get_mg_level(), new_global_numbers);
2703 }
2704
2705
2706
2707 template <int dim, int spacedim, typename Number, typename AdditionalDataType>
2708 void
2710 const AffineConstraints<Number> &constraints,
2711 const AdditionalDataType &matrix_free_data)
2712 {
2713 const std::vector<types::global_dof_index> new_global_numbers =
2715 constraints,
2716 matrix_free_data);
2717 if (matrix_free_data.mg_level == ::numbers::invalid_unsigned_int)
2718 dof_handler.renumber_dofs(new_global_numbers);
2719 else
2720 dof_handler.renumber_dofs(matrix_free_data.mg_level, new_global_numbers);
2721 }
2722
2723
2724
2725 template <int dim, int spacedim, typename Number, typename AdditionalDataType>
2726 std::vector<types::global_dof_index>
2728 const DoFHandler<dim, spacedim> &dof_handler,
2729 const AffineConstraints<Number> &constraints,
2730 const AdditionalDataType &matrix_free_data)
2731 {
2732 AdditionalDataType my_mf_data = matrix_free_data;
2733 my_mf_data.initialize_mapping = false;
2734 my_mf_data.tasks_parallel_scheme = AdditionalDataType::none;
2735
2736 typename AdditionalDataType::MatrixFreeType separate_matrix_free;
2737 separate_matrix_free.reinit(::MappingQ1<dim>(),
2738 dof_handler,
2739 constraints,
2740 ::QGauss<1>(2),
2741 my_mf_data);
2742 return compute_matrix_free_data_locality(dof_handler, separate_matrix_free);
2743 }
2744
2745
2746
2747 // Implementation details for matrix-free renumbering
2748 namespace
2749 {
2750 // Compute a vector of lists with number of unknowns of the same category
2751 // in terms of influence from other MPI processes, starting with unknowns
2752 // touched only by the local process and finally a new set of indices
2753 // going to different MPI neighbors. Later passes of the algorithm will
2754 // re-order unknowns within each of these sets.
2755 std::vector<std::vector<unsigned int>>
2756 group_dofs_by_rank_access(
2757 const ::Utilities::MPI::Partitioner &partitioner)
2758 {
2759 // count the number of times a locally owned DoF is accessed by the
2760 // remote ghost data
2761 std::vector<unsigned int> touch_count(partitioner.locally_owned_size());
2762 for (const auto &p : partitioner.import_indices())
2763 for (unsigned int i = p.first; i < p.second; ++i)
2764 touch_count[i]++;
2765
2766 // category 0: DoFs never touched by ghosts
2767 std::vector<std::vector<unsigned int>> result(1);
2768 for (unsigned int i = 0; i < touch_count.size(); ++i)
2769 if (touch_count[i] == 0)
2770 result.back().push_back(i);
2771
2772 // DoFs with 1 appearance can be simply categorized by their (single)
2773 // MPI rank, whereas we need to go an extra round for the remaining DoFs
2774 // by collecting the owning processes by hand
2775 std::map<unsigned int, std::vector<unsigned int>>
2776 multiple_ranks_access_dof;
2777 const std::vector<std::pair<unsigned int, unsigned int>> &import_targets =
2778 partitioner.import_targets();
2779 auto it = partitioner.import_indices().begin();
2780 for (const std::pair<unsigned int, unsigned int> &proc : import_targets)
2781 {
2782 result.emplace_back();
2783 unsigned int count_dofs = 0;
2784 while (count_dofs < proc.second)
2785 {
2786 for (unsigned int i = it->first; i < it->second;
2787 ++i, ++count_dofs)
2788 {
2789 if (touch_count[i] == 1)
2790 result.back().push_back(i);
2791 else
2792 multiple_ranks_access_dof[i].push_back(proc.first);
2793 }
2794 ++it;
2795 }
2796 }
2797 Assert(it == partitioner.import_indices().end(), ExcInternalError());
2798
2799 // Now go from the computed map on DoFs to a map on the processes for
2800 // DoFs with multiple owners, and append the DoFs found this way to our
2801 // global list
2802 std::map<std::vector<unsigned int>,
2803 std::vector<unsigned int>,
2804 std::function<bool(const std::vector<unsigned int> &,
2805 const std::vector<unsigned int> &)>>
2806 dofs_by_rank{[](const std::vector<unsigned int> &a,
2807 const std::vector<unsigned int> &b) {
2808 if (a.size() < b.size())
2809 return true;
2810 if (a.size() == b.size())
2811 {
2812 for (unsigned int i = 0; i < a.size(); ++i)
2813 if (a[i] < b[i])
2814 return true;
2815 else if (a[i] > b[i])
2816 return false;
2817 }
2818 return false;
2819 }};
2820 for (const auto &entry : multiple_ranks_access_dof)
2821 dofs_by_rank[entry.second].push_back(entry.first);
2822
2823 for (const auto &procs : dofs_by_rank)
2824 result.push_back(procs.second);
2825
2826 return result;
2827 }
2828
2829
2830
2831 // Compute two vectors, the first indicating the best numbers for a
2832 // MatrixFree::cell_loop and the second the count of how often a DoF is
2833 // touched by different cell groups, in order to later figure out DoFs
2834 // with far reach and those with only local influence.
2835 template <int dim, typename Number, typename VectorizedArrayType>
2836 std::pair<std::vector<unsigned int>, std::vector<unsigned char>>
2837 compute_mf_numbering(
2839 const unsigned int component)
2840 {
2841 const IndexSet &owned_dofs = matrix_free.get_dof_info(component)
2842 .vector_partitioner->locally_owned_range();
2843 const unsigned int n_comp =
2844 matrix_free.get_dof_handler(component).get_fe().n_components();
2845 Assert(
2846 matrix_free.get_dof_handler(component).get_fe().n_base_elements() == 1,
2848 const bool is_fe_q = dynamic_cast<const FE_Q_Base<dim> *>(
2849 &matrix_free.get_dof_handler(component).get_fe().base_element(0));
2850
2851 const unsigned int fe_degree =
2852 matrix_free.get_dof_handler(component).get_fe().degree;
2853 const unsigned int nn = fe_degree - 1;
2854
2855 // Data structure used further down for the identification of various
2856 // entities in the hierarchical numbering of unknowns. The first number
2857 // indicates the offset from which a given object starts its range of
2858 // numbers in the hierarchical DoF numbering of FE_Q, and the second the
2859 // number of unknowns per component on that particular component. The
2860 // numbers are group by the 3^dim possible objects, listed in
2861 // lexicographic order.
2862 std::array<std::pair<unsigned int, unsigned int>,
2863 ::Utilities::pow(3, dim)>
2864 dofs_on_objects;
2865 if (dim == 1)
2866 {
2867 dofs_on_objects[0] = std::make_pair(0U, 1U);
2868 dofs_on_objects[1] = std::make_pair(2 * n_comp, nn);
2869 dofs_on_objects[2] = std::make_pair(n_comp, 1U);
2870 }
2871 else if (dim == 2)
2872 {
2873 dofs_on_objects[0] = std::make_pair(0U, 1U);
2874 dofs_on_objects[1] = std::make_pair(n_comp * (4 + 2 * nn), nn);
2875 dofs_on_objects[2] = std::make_pair(n_comp, 1U);
2876 dofs_on_objects[3] = std::make_pair(n_comp * 4, nn);
2877 dofs_on_objects[4] = std::make_pair(n_comp * (4 + 4 * nn), nn * nn);
2878 dofs_on_objects[5] = std::make_pair(n_comp * (4 + 1 * nn), nn);
2879 dofs_on_objects[6] = std::make_pair(2 * n_comp, 1U);
2880 dofs_on_objects[7] = std::make_pair(n_comp * (4 + 3 * nn), nn);
2881 dofs_on_objects[8] = std::make_pair(3 * n_comp, 1U);
2882 }
2883 else if (dim == 3)
2884 {
2885 dofs_on_objects[0] = std::make_pair(0U, 1U);
2886 dofs_on_objects[1] = std::make_pair(n_comp * (8 + 2 * nn), nn);
2887 dofs_on_objects[2] = std::make_pair(n_comp, 1U);
2888 dofs_on_objects[3] = std::make_pair(n_comp * 8, nn);
2889 dofs_on_objects[4] =
2890 std::make_pair(n_comp * (8 + 12 * nn + 4 * nn * nn), nn * nn);
2891 dofs_on_objects[5] = std::make_pair(n_comp * (8 + 1 * nn), nn);
2892 dofs_on_objects[6] = std::make_pair(n_comp * 2, 1U);
2893 dofs_on_objects[7] = std::make_pair(n_comp * (8 + 3 * nn), nn);
2894 dofs_on_objects[8] = std::make_pair(n_comp * 3, 1U);
2895 dofs_on_objects[9] = std::make_pair(n_comp * (8 + 8 * nn), nn);
2896 dofs_on_objects[10] =
2897 std::make_pair(n_comp * (8 + 12 * nn + 2 * nn * nn), nn * nn);
2898 dofs_on_objects[11] = std::make_pair(n_comp * (8 + 9 * nn), nn);
2899 dofs_on_objects[12] = std::make_pair(n_comp * (8 + 12 * nn), nn * nn);
2900 dofs_on_objects[13] =
2901 std::make_pair(n_comp * (8 + 12 * nn + 6 * nn * nn), nn * nn * nn);
2902 dofs_on_objects[14] =
2903 std::make_pair(n_comp * (8 + 12 * nn + 1 * nn * nn), nn * nn);
2904 dofs_on_objects[15] = std::make_pair(n_comp * (8 + 10 * nn), nn);
2905 dofs_on_objects[16] =
2906 std::make_pair(n_comp * (8 + 12 * nn + 3 * nn * nn), nn * nn);
2907 dofs_on_objects[17] = std::make_pair(n_comp * (8 + 11 * nn), nn);
2908 dofs_on_objects[18] = std::make_pair(n_comp * 4, 1U);
2909 dofs_on_objects[19] = std::make_pair(n_comp * (8 + 6 * nn), nn);
2910 dofs_on_objects[20] = std::make_pair(n_comp * 5, 1U);
2911 dofs_on_objects[21] = std::make_pair(n_comp * (8 + 4 * nn), nn);
2912 dofs_on_objects[22] =
2913 std::make_pair(n_comp * (8 + 12 * nn + 5 * nn * nn), nn * nn);
2914 dofs_on_objects[23] = std::make_pair(n_comp * (8 + 5 * nn), nn);
2915 dofs_on_objects[24] = std::make_pair(n_comp * 6, 1U);
2916 dofs_on_objects[25] = std::make_pair(n_comp * (8 + 7 * nn), nn);
2917 dofs_on_objects[26] = std::make_pair(n_comp * 7, 1U);
2918 }
2919
2920 const auto renumber_func = [](const types::global_dof_index dof_index,
2921 const IndexSet &owned_dofs,
2922 std::vector<unsigned int> &result,
2923 unsigned int &counter_dof_numbers) {
2924 const types::global_dof_index local_dof_index =
2925 owned_dofs.index_within_set(dof_index);
2926 if (local_dof_index != numbers::invalid_dof_index)
2927 {
2928 AssertIndexRange(local_dof_index, result.size());
2929 if (result[local_dof_index] == numbers::invalid_unsigned_int)
2930 result[local_dof_index] = counter_dof_numbers++;
2931 }
2932 };
2933
2934 unsigned int counter_dof_numbers = 0;
2935 std::vector<unsigned int> dofs_extracted;
2936 std::vector<types::global_dof_index> dof_indices(
2937 matrix_free.get_dof_handler(component).get_fe().dofs_per_cell);
2938
2939 // We now define a lambda function that does two things: (a) determine
2940 // DoF numbers in a way that fit with the order a MatrixFree loop
2941 // travels through the cells (variable 'dof_numbers_mf_order'), and (b)
2942 // determine which unknowns are only touched from within a single range
2943 // of cells and which ones span multiple ranges (variable
2944 // 'touch_count'). Note that this process is done by calling into
2945 // MatrixFree::cell_loop, which gives the right level of granularity
2946 // (when executed in serial) for the scheduled vector operations. Note
2947 // that we pick the unconstrained indices in the hierarchical order for
2948 // part (a) as this makes it easy to identify the DoFs on the individual
2949 // entities, whereas we pick the numbers with constraints eliminated for
2950 // part (b). For the latter, we keep track of each DoF's interaction
2951 // with different ranges of cell batches, i.e., call-backs into the
2952 // operation_on_cell_range() function.
2953 const unsigned int n_owned_dofs = owned_dofs.n_elements();
2954 std::vector<unsigned int> dof_numbers_mf_order(
2955 n_owned_dofs, ::numbers::invalid_unsigned_int);
2956 std::vector<unsigned int> last_touch_by_cell_batch_range(
2957 n_owned_dofs, ::numbers::invalid_unsigned_int);
2958 std::vector<unsigned char> touch_count(n_owned_dofs);
2959
2960 const auto operation_on_cell_range =
2962 unsigned int &,
2963 const unsigned int &,
2964 const std::pair<unsigned int, unsigned int> &cell_range) {
2965 for (unsigned int cell = cell_range.first; cell < cell_range.second;
2966 ++cell)
2967 {
2968 // part (a): assign beneficial numbers
2969 for (unsigned int v = 0;
2970 v < data.n_active_entries_per_cell_batch(cell);
2971 ++v)
2972 {
2973 // get the indices for the dofs in cell_batch
2974 if (data.get_mg_level() == numbers::invalid_unsigned_int)
2975 data.get_cell_iterator(cell, v, component)
2976 ->get_dof_indices(dof_indices);
2977 else
2978 data.get_cell_iterator(cell, v, component)
2979 ->get_mg_dof_indices(dof_indices);
2980
2981 if (is_fe_q)
2982 for (unsigned int a = 0; a < dofs_on_objects.size(); ++a)
2983 {
2984 const auto &r = dofs_on_objects[a];
2985 if (a == 10 || a == 16)
2986 // switch order x-z for y faces in 3d to lexicographic
2987 // layout
2988 for (unsigned int i1 = 0; i1 < nn; ++i1)
2989 for (unsigned int i0 = 0; i0 < nn; ++i0)
2990 for (unsigned int c = 0; c < n_comp; ++c)
2991 renumber_func(
2992 dof_indices[r.first + r.second * c + i1 +
2993 i0 * nn],
2994 owned_dofs,
2995 dof_numbers_mf_order,
2996 counter_dof_numbers);
2997 else
2998 for (unsigned int i = 0; i < r.second; ++i)
2999 for (unsigned int c = 0; c < n_comp; ++c)
3000 renumber_func(
3001 dof_indices[r.first + r.second * c + i],
3002 owned_dofs,
3003 dof_numbers_mf_order,
3004 counter_dof_numbers);
3005 }
3006 else
3007 for (const types::global_dof_index i : dof_indices)
3008 renumber_func(i,
3009 owned_dofs,
3010 dof_numbers_mf_order,
3011 counter_dof_numbers);
3012 }
3013
3014 // part (b): increment the touch count of a dof appearing in the
3015 // current cell batch if it was last touched by another than the
3016 // present cell batch range (we track them via storing the last
3017 // cell batch range that touched a particular dof)
3018 data.get_dof_info(component).get_dof_indices_on_cell_batch(
3019 dofs_extracted, cell);
3020 for (const unsigned int dof_index : dofs_extracted)
3021 if (dof_index < n_owned_dofs &&
3022 last_touch_by_cell_batch_range[dof_index] !=
3023 cell_range.first)
3024 {
3025 ++touch_count[dof_index];
3026 last_touch_by_cell_batch_range[dof_index] =
3027 cell_range.first;
3028 }
3029 }
3030 };
3031
3032 // Finally run the matrix-free loop.
3033 Assert(matrix_free.get_task_info().scheme ==
3035 ExcNotImplemented("Renumbering only available for non-threaded "
3036 "version of MatrixFree::cell_loop"));
3037
3038 matrix_free.template cell_loop<unsigned int, unsigned int>(
3039 operation_on_cell_range, counter_dof_numbers, counter_dof_numbers);
3040
3041 AssertDimension(counter_dof_numbers, n_owned_dofs);
3042
3043 return std::make_pair(dof_numbers_mf_order, touch_count);
3044 }
3045
3046 } // namespace
3047
3048
3049
3050 template <int dim,
3051 int spacedim,
3052 typename Number,
3053 typename VectorizedArrayType>
3054 std::vector<types::global_dof_index>
3056 const DoFHandler<dim, spacedim> &dof_handler,
3058 {
3059 Assert(matrix_free.indices_initialized(),
3060 ExcMessage("You need to set up indices in MatrixFree "
3061 "to be able to compute a renumbering!"));
3062
3063 // try to locate the `DoFHandler` within the given MatrixFree object.
3064 unsigned int component = 0;
3065 for (; component < matrix_free.n_components(); ++component)
3066 if (&matrix_free.get_dof_handler(component) == &dof_handler)
3067 break;
3068
3069 Assert(component < matrix_free.n_components(),
3070 ExcMessage("Could not locate the given DoFHandler in MatrixFree"));
3071
3072 // Summary of the algorithm below:
3073 // (a) compute renumbering of each DoF in the order the corresponding
3074 // object appears in the mf loop -> local_numbering
3075 // (b) determine by how many cell groups (call-back places in the loop) a
3076 // dof is touched -> touch_count
3077 // (c) determine by how many MPI processes a dof is touched
3078 // -> dofs_by_rank_access
3079 // (d) combine both category types of (b) and (c) and list the indices
3080 // according to four categories
3081
3082 const std::vector<std::vector<unsigned int>> dofs_by_rank_access =
3083 group_dofs_by_rank_access(
3084 *matrix_free.get_dof_info(component).vector_partitioner);
3085
3086 const auto &[local_numbering, touch_count] =
3087 compute_mf_numbering(matrix_free, component);
3088
3089 const IndexSet &owned_dofs = matrix_free.get_dof_info(component)
3090 .vector_partitioner->locally_owned_range();
3092 AssertDimension(locally_owned_size, local_numbering.size());
3093 AssertDimension(locally_owned_size, touch_count.size());
3094
3095 // Create a second permutation to group the unknowns into the following
3096 // four categories, to be eventually composed with 'local_renumbering'
3097 enum class Category : unsigned char
3098 {
3099 single, // DoFs only referring to a single batch and MPI process
3100 multiple, // DoFs referring to multiple batches (long-range)
3101 mpi_dof, // DoFs referring to DoFs in exchange with other MPI processes
3102 constrained // DoFs without any reference (constrained DoFs)
3103 };
3104 std::vector<Category> categories(locally_owned_size);
3105 for (unsigned int i = 0; i < locally_owned_size; ++i)
3106 switch (touch_count[i])
3107 {
3108 case 0:
3109 categories[local_numbering[i]] = Category::constrained;
3110 break;
3111 case 1:
3112 categories[local_numbering[i]] = Category::single;
3113 break;
3114 default:
3115 categories[local_numbering[i]] = Category::multiple;
3116 break;
3117 }
3118 for (unsigned int chunk = 1; chunk < dofs_by_rank_access.size(); ++chunk)
3119 for (const auto i : dofs_by_rank_access[chunk])
3120 categories[local_numbering[i]] = Category::mpi_dof;
3121
3122 // Assign numbers to the categories in the order listed in the enum, which
3123 // is done by starting 'counter' for each category at an offset
3124 std::array<unsigned int, 4> n_entries_per_category{};
3125 for (const Category i : categories)
3126 {
3127 AssertIndexRange(static_cast<int>(i), 4);
3128 ++n_entries_per_category[static_cast<int>(i)];
3129 }
3130 std::array<unsigned int, 4> counters{};
3131 for (unsigned int i = 1; i < 4; ++i)
3132 counters[i] = counters[i - 1] + n_entries_per_category[i - 1];
3133 std::vector<unsigned int> numbering_categories;
3134 numbering_categories.reserve(locally_owned_size);
3135 for (const Category category : categories)
3136 {
3137 numbering_categories.push_back(counters[static_cast<int>(category)]);
3138 ++counters[static_cast<int>(category)];
3139 }
3140
3141 // The final numbers are given by the composition of the two permutations
3142 std::vector<::types::global_dof_index> new_global_numbers(
3144 for (unsigned int i = 0; i < locally_owned_size; ++i)
3145 new_global_numbers[i] =
3146 owned_dofs.nth_index_in_set(numbering_categories[local_numbering[i]]);
3147
3148 return new_global_numbers;
3149 }
3150
3151} // namespace DoFRenumbering
3152
3153
3154
3155/*-------------- Explicit Instantiations -------------------------------*/
3156#include "dofs/dof_renumbering.inst"
3157
3158
*  iterator end()
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  *  iterator begin()
***mech_lbc_system increment_interpolation_handlers push_back(scale_z_handler)
unsigned int first_selected_component(const unsigned int overall_number_of_components=numbers::invalid_unsigned_int) const
cell_iterator end() const
const hp::FECollection< dim, spacedim > & get_fe_collection() const
void renumber_dofs(const std::vector< types::global_dof_index > &new_numbers)
const FiniteElement< dim, spacedim > & get_fe(const types::fe_index index=0) const
const IndexSet & locally_owned_mg_dofs(const unsigned int level) const
const Triangulation< dim, spacedim > & get_triangulation() const
const IndexSet & locally_owned_dofs() const
active_cell_iterator begin_active(const unsigned int level=0) const
types::global_dof_index n_dofs() const
typename LevelSelector::cell_iterator level_cell_iterator
cell_iterator begin(const unsigned int level=0) const
MPI_Comm get_mpi_communicator() const
types::global_dof_index n_locally_owned_dofs() const
void add_entries(const size_type row, ForwardIterator begin, ForwardIterator end, const bool indices_are_unique_and_sorted=false)
size_type row_length(const size_type row) const
size_type column_number(const size_type row, const size_type index) const
const std::vector< Point< spacedim > > & get_quadrature_points() const
void reinit(const TriaIterator< DoFCellAccessor< dim, spacedim, level_dof_access > > &cell)
unsigned int n_dofs_per_cell() const
unsigned int n_components() const
const unsigned int dofs_per_cell
Definition fe_data.h:434
std::pair< unsigned int, types::global_dof_index > system_to_block_index(const unsigned int component) const
const ComponentMask & get_nonzero_components(const unsigned int i) const
bool has_support_points() const
const std::vector< Point< dim > > & get_unit_support_points() const
bool is_primitive() const
std::pair< unsigned int, unsigned int > system_to_component_index(const unsigned int index) const
size_type size() const
Definition index_set.h:1759
size_type index_within_set(const size_type global_index) const
Definition index_set.h:1977
size_type n_elements() const
Definition index_set.h:1917
bool is_element(const size_type index) const
Definition index_set.h:1877
ElementIterator begin() const
Definition index_set.h:1693
void subtract_set(const IndexSet &other)
Definition index_set.cc:496
size_type nth_index_in_set(const size_type local_index) const
Definition index_set.h:1958
void compress() const
Definition index_set.h:1767
void add_indices(const ForwardIterator &begin, const ForwardIterator &end)
Definition index_set.h:1814
const internal::MatrixFreeFunctions::TaskInfo & get_task_info() const
unsigned int get_mg_level() const
const internal::MatrixFreeFunctions::DoFInfo & get_dof_info(const unsigned int dof_handler_index_component=0) const
const DoFHandler< dim > & get_dof_handler(const unsigned int dof_handler_index=0) const
bool indices_initialized() const
unsigned int n_components() const
Definition point.h:111
size_type n_rows() const
unsigned int n_active_cells() const
unsigned int n_cells() const
pointer data()
iterator end()
iterator begin()
unsigned int size() const
Definition collection.h:314
void push_back(const FiniteElement< dim, spacedim > &new_fe)
unsigned int n_blocks() const
unsigned int n_components() const
const FEValuesType & get_present_fe_values() const
Definition fe_values.h:693
void reinit(const TriaIterator< DoFCellAccessor< dim, spacedim, lda > > &cell, const unsigned int q_index=numbers::invalid_unsigned_int, const unsigned int mapping_index=numbers::invalid_unsigned_int, const unsigned int fe_index=numbers::invalid_unsigned_int)
Definition fe_values.cc:294
void push_back(const Quadrature< dim_in > &new_quadrature)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
unsigned int level
Definition grid_out.cc:4642
const unsigned int v1
IteratorRange< active_cell_iterator > active_cell_iterators() const
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcIndexRange(std::size_t arg1, std::size_t arg2, std::size_t arg3)
static ::ExceptionBase & ExcDoFHandlerNotInitialized()
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
typename ActiveSelector::cell_iterator cell_iterator
typename ActiveSelector::active_cell_iterator active_cell_iterator
void make_hanging_node_constraints(const DoFHandler< dim, spacedim > &dof_handler, AffineConstraints< number > &constraints)
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints={}, const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
@ update_quadrature_points
Transformed quadrature points.
std::vector< index_type > data
Definition mpi.cc:734
std::size_t size
Definition mpi.cc:733
types::global_dof_index locally_owned_size
Definition mpi.cc:821
graph_traits< Graph >::vertices_size_type size_type
graph_traits< Graph >::vertex_descriptor Vertex
adjacency_list< vecS, vecS, undirectedS, property< vertex_color_t, default_color_type, property< vertex_degree_t, int > > > Graph
std::pair< size_type, size_type > Pair
void create_graph(const DoFHandler< dim, spacedim > &dof_handler, const bool use_constraints, boosttypes::Graph &graph, boosttypes::property_map< boosttypes::Graph, boosttypes::vertex_degree_t >::type &graph_degree)
void compute_Cuthill_McKee(std::vector< types::global_dof_index > &new_dof_indices, const DoFHandler< dim, spacedim > &, const bool reversed_numbering=false, const bool use_constraints=false)
void Cuthill_McKee(DoFHandler< dim, spacedim > &dof_handler, const bool reversed_numbering=false, const bool use_constraints=false)
void king_ordering(DoFHandler< dim, spacedim > &dof_handler, const bool reversed_numbering=false, const bool use_constraints=false)
void compute_king_ordering(std::vector< types::global_dof_index > &new_dof_indices, const DoFHandler< dim, spacedim > &, const bool reversed_numbering=false, const bool use_constraints=false)
void compute_minimum_degree(std::vector< types::global_dof_index > &new_dof_indices, const DoFHandler< dim, spacedim > &, const bool reversed_numbering=false, const bool use_constraints=false)
void minimum_degree(DoFHandler< dim, spacedim > &dof_handler, const bool reversed_numbering=false, const bool use_constraints=false)
void compute_support_point_wise(std::vector< types::global_dof_index > &new_dof_indices, const DoFHandler< dim, spacedim > &dof_handler)
void compute_subdomain_wise(std::vector< types::global_dof_index > &new_dof_indices, const DoFHandler< dim, spacedim > &dof_handler)
void matrix_free_data_locality(DoFHandler< dim, spacedim > &dof_handler, const MatrixFree< dim, Number, VectorizedArrayType > &matrix_free)
void subdomain_wise(DoFHandler< dim, spacedim > &dof_handler)
types::global_dof_index compute_block_wise(std::vector< types::global_dof_index > &new_dof_indices, const IteratorType &start, const EndIteratorType &end, const bool is_level_operation)
void hierarchical(DoFHandler< dim, spacedim > &dof_handler)
void compute_random(std::vector< types::global_dof_index > &new_dof_indices, const DoFHandler< dim, spacedim > &dof_handler)
void component_wise(DoFHandler< dim, spacedim > &dof_handler, const std::vector< unsigned int > &target_component=std::vector< unsigned int >())
void downstream(DoFHandler< dim, spacedim > &dof_handler, const Tensor< 1, spacedim > &direction, const bool dof_wise_renumbering=false)
void compute_Cuthill_McKee(std::vector< types::global_dof_index > &new_dof_indices, const DoFHandler< dim, spacedim > &, const bool reversed_numbering=false, const bool use_constraints=false, const std::vector< types::global_dof_index > &starting_indices=std::vector< types::global_dof_index >(), const unsigned int level=numbers::invalid_unsigned_int)
void block_wise(DoFHandler< dim, spacedim > &dof_handler)
void Cuthill_McKee(DoFHandler< dim, spacedim > &dof_handler, const bool reversed_numbering=false, const bool use_constraints=false, const std::vector< types::global_dof_index > &starting_indices=std::vector< types::global_dof_index >())
void compute_sort_selected_dofs_back(std::vector< types::global_dof_index > &new_dof_indices, const DoFHandler< dim, spacedim > &dof_handler, const std::vector< bool > &selected_dofs)
void sort_selected_dofs_back(DoFHandler< dim, spacedim > &dof_handler, const std::vector< bool > &selected_dofs)
void support_point_wise(DoFHandler< dim, spacedim > &dof_handler)
void compute_cell_wise(std::vector< types::global_dof_index > &renumbering, std::vector< types::global_dof_index > &inverse_renumbering, const DoFHandler< dim, spacedim > &dof_handler, const std::vector< typename DoFHandler< dim, spacedim >::active_cell_iterator > &cell_order)
std::vector< types::global_dof_index > compute_matrix_free_data_locality(const DoFHandler< dim, spacedim > &dof_handler, const MatrixFree< dim, Number, VectorizedArrayType > &matrix_free)
void clockwise_dg(DoFHandler< dim, spacedim > &dof_handler, const Point< spacedim > &center, const bool counter=false)
void lexicographic(DoFHandler< dim > &dof_handler, const double tolerance=1e-12)
void random(DoFHandler< dim, spacedim > &dof_handler)
void compute_downstream(std::vector< types::global_dof_index > &new_dof_indices, std::vector< types::global_dof_index > &reverse, const DoFHandler< dim, spacedim > &dof_handler, const Tensor< 1, spacedim > &direction, const bool dof_wise_renumbering)
void cell_wise(DoFHandler< dim, spacedim > &dof_handler, const std::vector< typename DoFHandler< dim, spacedim >::active_cell_iterator > &cell_order)
void compute_lexicographic(std::vector< types::global_dof_index > &new_dof_indices, const DoFHandler< dim > &handler, const double tolerance=1e-12)
types::global_dof_index compute_component_wise(std::vector< types::global_dof_index > &new_dof_indices, const CellIterator &start, const std_cxx20::type_identity_t< CellIterator > &end, const std::vector< unsigned int > &target_component, const bool is_level_operation)
void compute_clockwise_dg(std::vector< types::global_dof_index > &new_dof_indices, const DoFHandler< dim, spacedim > &dof_handler, const Point< spacedim > &center, const bool counter)
void get_subdomain_association(const DoFHandler< dim, spacedim > &dof_handler, std::vector< types::subdomain_id > &subdomain)
IndexSet extract_locally_relevant_dofs(const DoFHandler< dim, spacedim > &dof_handler)
IndexSet extract_locally_active_dofs(const DoFHandler< dim, spacedim > &dof_handler)
IndexSet extract_locally_relevant_level_dofs(const DoFHandler< dim, spacedim > &dof_handler, const unsigned int level)
IndexSet extract_locally_active_level_dofs(const DoFHandler< dim, spacedim > &dof_handler, const unsigned int level)
std::vector< types::global_dof_index > count_dofs_per_fe_component(const DoFHandler< dim, spacedim > &dof_handler, const bool vector_valued_once=false, const std::vector< unsigned int > &target_component={})
void map_dofs_to_support_points(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof_handler, std::vector< Point< spacedim > > &support_points, const ComponentMask &mask={}, const bool map_locally_relevant_dofs=true)
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity, const unsigned int level, const AffineConstraints< number > &constraints={}, const bool keep_constrained_dofs=true)
Definition mg_tools.cc:575
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
*  *  *  RotationFunction< dim, Number >::RotationFunction Number(dim)
void reorder_Cuthill_McKee(const DynamicSparsityPattern &sparsity, std::vector< DynamicSparsityPattern::size_type > &new_indices, const std::vector< DynamicSparsityPattern::size_type > &starting_indices=std::vector< DynamicSparsityPattern::size_type >())
T sum(const T &t, const MPI_Comm mpi_communicator)
bool logical_and(const bool t, const MPI_Comm mpi_communicator)
constexpr T pow(const T base, const int iexp)
Definition utilities.h:966
std::vector< Integer > reverse_permutation(const std::vector< Integer > &permutation)
Definition utilities.h:1655
constexpr types::global_dof_index invalid_dof_index
Definition types.h:259
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
typename type_identity< T >::type type_identity_t
Definition type_traits.h:93
unsigned short int fe_index
Definition types.h:70
bool compare(const DHCellIterator &c1, const DHCellIterator &c2, std::integral_constant< int, xdim >) const
bool compare(const DHCellIterator &, const DHCellIterator &, std::integral_constant< int, 1 >) const
ClockCells(const Point< dim > &center, bool counter)
bool operator()(const DHCellIterator &c1, const DHCellIterator &c2) const
std::shared_ptr< const Utilities::MPI::Partitioner > vector_partitioner
Definition dof_info.h:591