deal.II version GIT relicensing-6848-g68f52df3ff 2026-10-09 12:50: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
mg_transfer_internal.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) 2016 - 2025 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
13
15
17
18#include <deal.II/fe/fe_tools.h>
19
21
23
24#include <memory>
25
27
28namespace internal
29{
30 namespace MGTransfer
31 {
32 // Internal data structure that is used in the MPI communication in
33 // fill_copy_indices(). It represents an entry in the copy_indices* map,
34 // that associates a level dof index with a global dof index.
55
56 template <int dim, int spacedim>
57 void
59 const DoFHandler<dim, spacedim> &dof_handler,
60 const MGConstrainedDoFs *mg_constrained_dofs,
61 std::vector<std::vector<
62 std::pair<types::global_dof_index, types::global_dof_index>>>
63 &copy_indices,
64 std::vector<std::vector<
65 std::pair<types::global_dof_index, types::global_dof_index>>>
66 &copy_indices_global_mine,
67 std::vector<std::vector<
68 std::pair<types::global_dof_index, types::global_dof_index>>>
69 &copy_indices_level_mine,
70 const bool skip_interface_dofs)
71 {
72 // Now we are filling the variables copy_indices*, which are essentially
73 // maps from global to mgdof for each level stored as a std::vector of
74 // pairs. We need to split this map on each level depending on the
75 // ownership of the global and mgdof, so that we later do not access
76 // non-local elements in copy_to/from_mg.
77 // We keep track in the bitfield dof_touched which global dof has been
78 // processed already (on the current level). This is the same as the
79 // multigrid running in serial.
80
81 // map cpu_index -> vector of data
82 // that will be copied into copy_indices_level_mine
83 std::vector<DoFPair> send_data_temp;
84
85 const unsigned int n_levels =
86 dof_handler.get_triangulation().n_global_levels();
87 copy_indices.resize(n_levels);
88 copy_indices_global_mine.resize(n_levels);
89 copy_indices_level_mine.resize(n_levels);
90 const IndexSet &owned_dofs = dof_handler.locally_owned_dofs();
91
92 const unsigned int dofs_per_cell = dof_handler.get_fe().n_dofs_per_cell();
93 std::vector<types::global_dof_index> global_dof_indices(dofs_per_cell);
94 std::vector<types::global_dof_index> level_dof_indices(dofs_per_cell);
95
96 for (unsigned int level = 0; level < n_levels; ++level)
97 {
98 std::vector<bool> dof_touched(owned_dofs.n_elements(), false);
99 const IndexSet &owned_level_dofs =
100 dof_handler.locally_owned_mg_dofs(level);
101
102 // for the most common case where copy_indices are locally owned
103 // both globally and on the level, we want to skip collecting pairs
104 // and later sorting them. instead, we insert these indices into a
105 // plain vector
106 std::vector<types::global_dof_index> unrolled_copy_indices;
107
108 copy_indices_level_mine[level].clear();
109 copy_indices_global_mine[level].clear();
110
111 for (const auto &level_cell :
113 {
114 if (dof_handler.get_triangulation().locally_owned_subdomain() !=
116 (level_cell->level_subdomain_id() ==
118 level_cell->subdomain_id() ==
120 continue;
121
122 unrolled_copy_indices.resize(owned_dofs.n_elements(),
124
125 // get the dof numbers of this cell for the global and the
126 // level-wise numbering
127 level_cell->get_dof_indices(global_dof_indices);
128 level_cell->get_mg_dof_indices(level_dof_indices);
129
130 for (unsigned int i = 0; i < dofs_per_cell; ++i)
131 {
132 // we need to ignore if the DoF is on a refinement edge
133 // (hanging node)
134 if (skip_interface_dofs && mg_constrained_dofs != nullptr &&
135 mg_constrained_dofs->at_refinement_edge(
136 level, level_dof_indices[i]))
137 continue;
138
139 // First check whether we own any of the active dof index
140 // and the level one. This check involves locally owned
141 // indices which often consist only of a single range, so
142 // they are cheap to look up.
143 const types::global_dof_index global_index_in_set =
144 owned_dofs.index_within_set(global_dof_indices[i]);
145 bool global_mine =
146 global_index_in_set != numbers::invalid_dof_index;
147 bool level_mine =
148 owned_level_dofs.is_element(level_dof_indices[i]);
149
150 if (global_mine && level_mine)
151 {
152 // we own both the active dof index and the level one ->
153 // set them into the vector, indexed by the local index
154 // range of the active dof
155 unrolled_copy_indices[global_index_in_set] =
156 level_dof_indices[i];
157 }
158 else if (global_mine &&
159 dof_touched[global_index_in_set] == false)
160 {
161 copy_indices_global_mine[level].emplace_back(
162 global_dof_indices[i], level_dof_indices[i]);
163
164 // send this to the owner of the level_dof:
165 send_data_temp.emplace_back(level,
166 global_dof_indices[i],
167 level_dof_indices[i]);
168 dof_touched[global_index_in_set] = true;
169 }
170 else
171 {
172 // somebody will send those to me
173 }
174 }
175 }
176
177 // we now translate the plain vector for the copy_indices field into
178 // the expected format of a pair of indices
179 if (!unrolled_copy_indices.empty())
180 {
181 copy_indices[level].clear();
182
183 // reserve the full length in case we did not hit global-mine
184 // indices, so we expect all indices to come into copy_indices
185 if (copy_indices_global_mine[level].empty())
186 copy_indices[level].reserve(unrolled_copy_indices.size());
187
188 // owned_dofs.nth_index_in_set(i) in this query is
189 // usually cheap to look up as there are few ranges in
190 // the locally owned part
191 for (unsigned int i = 0; i < unrolled_copy_indices.size(); ++i)
192 if (unrolled_copy_indices[i] != numbers::invalid_dof_index)
193 copy_indices[level].emplace_back(
194 owned_dofs.nth_index_in_set(i), unrolled_copy_indices[i]);
195 }
196 }
197
198 const ::parallel::TriangulationBase<dim, spacedim> *tria =
199 (dynamic_cast<const ::parallel::TriangulationBase<dim, spacedim>
200 *>(&dof_handler.get_triangulation()));
202 send_data_temp.empty() || tria != nullptr,
204 "We should only be sending information with a parallel Triangulation!"));
205
206#ifdef DEAL_II_WITH_MPI
207 if (tria && Utilities::MPI::sum(send_data_temp.size(),
208 tria->get_mpi_communicator()) > 0)
209 {
210 const std::set<types::subdomain_id> &neighbors =
211 tria->level_ghost_owners();
212 std::map<int, std::vector<DoFPair>> send_data;
213
214 std::sort(send_data_temp.begin(),
215 send_data_temp.end(),
216 [](const DoFPair &lhs, const DoFPair &rhs) {
217 if (lhs.level < rhs.level)
218 return true;
219 if (lhs.level > rhs.level)
220 return false;
221
222 if (lhs.level_dof_index < rhs.level_dof_index)
223 return true;
224 if (lhs.level_dof_index > rhs.level_dof_index)
225 return false;
226
227 if (lhs.global_dof_index < rhs.global_dof_index)
228 return true;
229 else
230 return false;
231 });
232 send_data_temp.erase(
233 std::unique(send_data_temp.begin(),
234 send_data_temp.end(),
235 [](const DoFPair &lhs, const DoFPair &rhs) {
236 return (lhs.level == rhs.level) &&
237 (lhs.level_dof_index == rhs.level_dof_index) &&
238 (lhs.global_dof_index == rhs.global_dof_index);
239 }),
240 send_data_temp.end());
241
242 for (unsigned int level = 0; level < n_levels; ++level)
243 {
244 const IndexSet &owned_level_dofs =
245 dof_handler.locally_owned_mg_dofs(level);
246
247 std::vector<types::global_dof_index> level_dof_indices;
248 std::vector<types::global_dof_index> global_dof_indices;
249 for (const auto &dofpair : send_data_temp)
250 if (dofpair.level == level)
251 {
252 level_dof_indices.push_back(dofpair.level_dof_index);
253 global_dof_indices.push_back(dofpair.global_dof_index);
254 }
255
256 IndexSet is_ghost(owned_level_dofs.size());
257 is_ghost.add_indices(level_dof_indices.begin(),
258 level_dof_indices.end());
259
260 AssertThrow(level_dof_indices.size() == is_ghost.n_elements(),
261 ExcMessage("Size does not match!"));
262
263 const auto index_owner = Utilities::MPI::compute_index_owner(
264 owned_level_dofs, is_ghost, tria->get_mpi_communicator());
265
266 AssertThrow(level_dof_indices.size() == index_owner.size(),
267 ExcMessage("Size does not match!"));
268
269 for (unsigned int i = 0; i < index_owner.size(); ++i)
270 send_data[index_owner[i]].emplace_back(level,
271 global_dof_indices[i],
272 level_dof_indices[i]);
273 }
274
275
276 // Protect the send/recv logic with a mutex:
279 mutex, tria->get_mpi_communicator());
280
281 const int mpi_tag =
283
284 // * send
285 std::vector<MPI_Request> requests;
286 {
287 for (const auto dest : neighbors)
288 {
289 requests.push_back(MPI_Request());
290 std::vector<DoFPair> &data = send_data[dest];
291
292 const int ierr =
293 MPI_Isend(data.data(),
294 data.size() * sizeof(decltype(*data.data())),
295 MPI_BYTE,
296 dest,
297 mpi_tag,
298 tria->get_mpi_communicator(),
299 &*requests.rbegin());
300 AssertThrowMPI(ierr);
301 }
302 }
303
304 // * receive
305 {
306 // We should get one message from each of our neighbors
307 std::vector<DoFPair> receive_buffer;
308 for (unsigned int counter = 0; counter < neighbors.size();
309 ++counter)
310 {
311 MPI_Status status;
312 int ierr = MPI_Probe(MPI_ANY_SOURCE,
313 mpi_tag,
314 tria->get_mpi_communicator(),
315 &status);
316 AssertThrowMPI(ierr);
317 int len;
318 ierr = MPI_Get_count(&status, MPI_BYTE, &len);
319 AssertThrowMPI(ierr);
320
321 if (len == 0)
322 {
323 ierr = MPI_Recv(nullptr,
324 0,
325 MPI_BYTE,
326 status.MPI_SOURCE,
327 status.MPI_TAG,
328 tria->get_mpi_communicator(),
329 &status);
330 AssertThrowMPI(ierr);
331 continue;
332 }
333
334 int count = len / sizeof(DoFPair);
335 Assert(static_cast<int>(count * sizeof(DoFPair)) == len,
337 receive_buffer.resize(count);
338
339 void *ptr = receive_buffer.data();
340 ierr = MPI_Recv(ptr,
341 len,
342 MPI_BYTE,
343 status.MPI_SOURCE,
344 status.MPI_TAG,
345 tria->get_mpi_communicator(),
346 &status);
347 AssertThrowMPI(ierr);
348
349 for (const auto &dof_pair : receive_buffer)
350 {
351 copy_indices_level_mine[dof_pair.level].emplace_back(
352 dof_pair.global_dof_index, dof_pair.level_dof_index);
353 }
354 }
355 }
356
357 // * wait for all MPI_Isend to complete
358 if (requests.size() > 0)
359 {
360 const int ierr = MPI_Waitall(requests.size(),
361 requests.data(),
362 MPI_STATUSES_IGNORE);
363 AssertThrowMPI(ierr);
364 requests.clear();
365 }
366 if constexpr (running_in_debug_mode())
367 {
368 // Make sure in debug mode, that everybody sent/received all
369 // packages on this level. If a deadlock occurs here, the list of
370 // expected senders is not computed correctly.
371 const int ierr = MPI_Barrier(tria->get_mpi_communicator());
372 AssertThrowMPI(ierr);
373 }
374 }
375#endif
376
377 // Sort the indices, except the copy_indices which already are
378 // sorted. This will produce more reliable debug output for regression
379 // tests and won't hurt performance even in release mode because the
380 // non-owned indices are a small subset of all unknowns.
381 std::less<std::pair<types::global_dof_index, types::global_dof_index>>
382 compare;
383 for (auto &level_indices : copy_indices_level_mine)
384 std::sort(level_indices.begin(), level_indices.end(), compare);
385 for (auto &level_indices : copy_indices_global_mine)
386 std::sort(level_indices.begin(), level_indices.end(), compare);
387 }
388
389
390
391 // initialize the vectors needed for the transfer (and merge with the
392 // content in copy_indices_global_mine)
393 void
395 const IndexSet &locally_owned,
396 std::vector<types::global_dof_index> &ghosted_level_dofs,
397 const std::shared_ptr<const Utilities::MPI::Partitioner>
398 &external_partitioner,
399 const MPI_Comm communicator,
400 std::shared_ptr<const Utilities::MPI::Partitioner> &target_partitioner,
401 Table<2, unsigned int> &copy_indices_global_mine)
402 {
403 std::sort(ghosted_level_dofs.begin(), ghosted_level_dofs.end());
404 IndexSet ghosted_dofs(locally_owned.size());
405 ghosted_dofs.add_indices(ghosted_level_dofs.begin(),
406 ghosted_level_dofs.end());
407 ghosted_dofs.compress();
408
409 // Add possible ghosts from the previous content in the vector
410 if (target_partitioner.get() != nullptr &&
411 target_partitioner->size() == locally_owned.size())
412 {
413 ghosted_dofs.add_indices(target_partitioner->ghost_indices());
414 }
415
416 // check if the given partitioner's ghosts represent a superset of the
417 // ghosts we require in this function
418 const bool ghosts_locally_contained =
419 external_partitioner.get() != nullptr &&
420 (external_partitioner->ghost_indices() & ghosted_dofs) == ghosted_dofs;
421 if (external_partitioner.get() != nullptr &&
422 Utilities::MPI::logical_and(ghosts_locally_contained, communicator))
423 {
424 // shift the local number of the copy indices according to the new
425 // partitioner that we are going to use during the access to the
426 // entries
427 if (target_partitioner.get() != nullptr &&
428 target_partitioner->size() == locally_owned.size())
429 for (unsigned int i = 0; i < copy_indices_global_mine.n_cols(); ++i)
430 copy_indices_global_mine(1, i) =
431 external_partitioner->global_to_local(
432 target_partitioner->local_to_global(
433 copy_indices_global_mine(1, i)));
434 target_partitioner = external_partitioner;
435 }
436 else
437 {
438 if (target_partitioner.get() != nullptr &&
439 target_partitioner->size() == locally_owned.size())
440 for (unsigned int i = 0; i < copy_indices_global_mine.n_cols(); ++i)
441 copy_indices_global_mine(1, i) =
442 locally_owned.n_elements() +
443 ghosted_dofs.index_within_set(
444 target_partitioner->local_to_global(
445 copy_indices_global_mine(1, i)));
446 target_partitioner =
447 std::make_shared<Utilities::MPI::Partitioner>(locally_owned,
448 ghosted_dofs,
449 communicator);
450 }
451 }
452
453
454
455 // Transform the ghost indices to local index space for the vector
456 void
458 const Utilities::MPI::Partitioner &part,
459 const std::vector<types::global_dof_index> &mine,
460 const std::vector<types::global_dof_index> &remote,
461 std::vector<unsigned int> &localized_indices)
462 {
463 localized_indices.resize(mine.size() + remote.size(),
465 for (unsigned int i = 0; i < mine.size(); ++i)
466 if (mine[i] != numbers::invalid_dof_index)
467 localized_indices[i] = part.global_to_local(mine[i]);
468
469 for (unsigned int i = 0; i < remote.size(); ++i)
470 if (remote[i] != numbers::invalid_dof_index)
471 localized_indices[i + mine.size()] = part.global_to_local(remote[i]);
472 }
473
474
475
476 // given the collection of child cells in lexicographic ordering as seen
477 // from the parent, compute the first index of the given child
478 template <int dim>
479 unsigned int
480 compute_shift_within_children(const unsigned int child,
481 const unsigned int fe_shift_1d,
482 const unsigned int fe_degree)
483 {
484 // we put the degrees of freedom of all child cells in lexicographic
485 // ordering
486 unsigned int c_tensor_index[dim];
487 unsigned int tmp = child;
488 for (unsigned int d = 0; d < dim; ++d)
489 {
490 c_tensor_index[d] = tmp % 2;
491 tmp /= 2;
492 }
493 const unsigned int n_child_dofs_1d = fe_degree + 1 + fe_shift_1d;
494 unsigned int factor = 1;
495 unsigned int shift = fe_shift_1d * c_tensor_index[0];
496 for (unsigned int d = 1; d < dim; ++d)
497 {
498 factor *= n_child_dofs_1d;
499 shift = shift + factor * fe_shift_1d * c_tensor_index[d];
500 }
501 return shift;
502 }
503
504
505
506 // puts the indices on the given child cell in lexicographic ordering with
507 // respect to the collection of all child cells as seen from the parent
508 template <int dim>
509 void
511 const unsigned int child,
512 const unsigned int fe_shift_1d,
513 const unsigned int fe_degree,
514 const std::vector<unsigned int> &lexicographic_numbering,
515 const std::vector<types::global_dof_index> &local_dof_indices,
516 types::global_dof_index *target_indices)
517 {
518 const unsigned int n_child_dofs_1d = fe_degree + 1 + fe_shift_1d;
519 const unsigned int shift =
520 compute_shift_within_children<dim>(child, fe_shift_1d, fe_degree);
521 const unsigned int n_components =
522 local_dof_indices.size() / Utilities::fixed_power<dim>(fe_degree + 1);
523 types::global_dof_index *indices = target_indices + shift;
524 const unsigned int n_scalar_cell_dofs =
525 Utilities::fixed_power<dim>(n_child_dofs_1d);
526 for (unsigned int c = 0, m = 0; c < n_components; ++c)
527 for (unsigned int k = 0; k < (dim > 2 ? (fe_degree + 1) : 1); ++k)
528 for (unsigned int j = 0; j < (dim > 1 ? (fe_degree + 1) : 1); ++j)
529 for (unsigned int i = 0; i < (fe_degree + 1); ++i, ++m)
530 {
531 const unsigned int index =
532 c * n_scalar_cell_dofs +
533 k * n_child_dofs_1d * n_child_dofs_1d + j * n_child_dofs_1d +
534 i;
536 indices[index] ==
537 local_dof_indices[lexicographic_numbering[m]],
539 indices[index] = local_dof_indices[lexicographic_numbering[m]];
540 }
541 }
542
543
544
545 template <int dim, typename Number>
546 void
548 const FiniteElement<1> &fe,
549 const DoFHandler<dim> &dof_handler)
550 {
551 // currently, we have only FE_Q and FE_DGQ type elements implemented
552 elem_info.n_components = dof_handler.get_fe().element_multiplicity(0);
553 AssertDimension(Utilities::fixed_power<dim>(fe.n_dofs_per_cell()) *
554 elem_info.n_components,
555 dof_handler.get_fe().n_dofs_per_cell());
556 AssertDimension(fe.degree, dof_handler.get_fe().degree);
557 elem_info.fe_degree = fe.degree;
558 elem_info.element_is_continuous = fe.n_dofs_per_vertex() > 0;
560
561 // step 1.2: get renumbering of 1d basis functions to lexicographic
562 // numbers. The distinction according to fe.n_dofs_per_vertex() is to
563 // support both continuous and discontinuous bases.
564 std::vector<unsigned int> renumbering(fe.n_dofs_per_cell());
565 {
567 renumbering[0] = 0;
568 for (unsigned int i = 0; i < fe.n_dofs_per_line(); ++i)
569 renumbering[i + fe.n_dofs_per_vertex()] =
571 if (fe.n_dofs_per_vertex() > 0)
572 renumbering[fe.n_dofs_per_cell() - fe.n_dofs_per_vertex()] =
574 }
575
576 // step 1.3: create a dummy 1d quadrature formula to extract the
577 // lexicographic numbering for the elements
578 Assert(fe.n_dofs_per_vertex() == 0 || fe.n_dofs_per_vertex() == 1,
580 const unsigned int shift = fe.n_dofs_per_cell() - fe.n_dofs_per_vertex();
581 const unsigned int n_child_dofs_1d =
582 (fe.n_dofs_per_vertex() > 0 ? (2 * fe.n_dofs_per_cell() - 1) :
583 (2 * fe.n_dofs_per_cell()));
584
585 elem_info.n_child_cell_dofs =
586 elem_info.n_components * Utilities::fixed_power<dim>(n_child_dofs_1d);
587 const Quadrature<1> dummy_quadrature(
588 std::vector<Point<1>>(1, Point<1>()));
590 shape_info.reinit(dummy_quadrature, dof_handler.get_fe(), 0);
592
593 // step 1.4: get the 1d prolongation matrix and combine from both children
594 elem_info.prolongation_matrix_1d.resize(fe.n_dofs_per_cell() *
595 n_child_dofs_1d);
596
597 for (unsigned int c = 0; c < GeometryInfo<1>::max_children_per_cell; ++c)
598 for (unsigned int i = 0; i < fe.n_dofs_per_cell(); ++i)
599 for (unsigned int j = 0; j < fe.n_dofs_per_cell(); ++j)
600 elem_info
601 .prolongation_matrix_1d[i * n_child_dofs_1d + j + c * shift] =
602 fe.get_prolongation_matrix(c)(renumbering[j], renumbering[i]);
603 }
604
605
606
607 // Sets up most of the internal data structures of the MGTransferMatrixFree
608 // class
609 template <int dim, typename Number>
610 void
612 const DoFHandler<dim> &dof_handler,
613 const MGConstrainedDoFs *mg_constrained_dofs,
614 const std::vector<std::shared_ptr<const Utilities::MPI::Partitioner>>
615 &external_partitioners,
616 ElementInfo<Number> &elem_info,
617 std::vector<std::vector<unsigned int>> &level_dof_indices,
618 std::vector<std::vector<std::pair<unsigned int, unsigned int>>>
619 &parent_child_connect,
620 std::vector<unsigned int> &n_owned_level_cells,
621 std::vector<std::vector<std::vector<unsigned short>>> &dirichlet_indices,
622 std::vector<std::vector<Number>> &weights_on_refined,
623 std::vector<Table<2, unsigned int>> &copy_indices_global_mine,
624 MGLevelObject<std::shared_ptr<const Utilities::MPI::Partitioner>>
625 &target_partitioners)
626 {
627 level_dof_indices.clear();
628 parent_child_connect.clear();
629 n_owned_level_cells.clear();
630 dirichlet_indices.clear();
631 weights_on_refined.clear();
632
633 if constexpr (running_in_debug_mode())
634 {
635 if (mg_constrained_dofs)
636 {
637 const unsigned int n_levels =
638 dof_handler.get_triangulation().n_global_levels();
639
640 for (unsigned int l = 0; l < n_levels; ++l)
641 {
642 const auto &constraints =
643 mg_constrained_dofs->get_user_constraint_matrix(l);
644
645 // no inhomogeneities are supported
646 AssertDimension(constraints.n_inhomogeneities(), 0);
647
648 for (const auto dof : constraints.get_local_lines())
649 {
650 const auto *entries_ptr =
651 constraints.get_constraint_entries(dof);
652
653 if (entries_ptr == nullptr)
654 continue;
655
656 // only homogeneous or identity constraints are supported
657 Assert((entries_ptr->size() == 0) ||
658 ((entries_ptr->size() == 1) &&
659 (std::abs((*entries_ptr)[0].second - 1.) <
660 100 * std::numeric_limits<double>::epsilon())),
662 }
663 }
664 }
665 }
666
667 // we collect all child DoFs of a mother cell together. For faster
668 // tensorized operations, we align the degrees of freedom
669 // lexicographically. We distinguish FE_Q elements and FE_DGQ elements
670
671 const ::Triangulation<dim> &tria = dof_handler.get_triangulation();
672
673 // ---------------------------- 1. Extract 1d info about the finite
674 // element step 1.1: create a 1d copy of the finite element from FETools
675 // where we substitute the template argument
676 AssertDimension(dof_handler.get_fe().n_base_elements(), 1);
677 std::string fe_name = dof_handler.get_fe().base_element(0).get_name();
678 {
679 const std::size_t template_starts = fe_name.find_first_of('<');
680 Assert(fe_name[template_starts + 1] ==
681 (dim == 1 ? '1' : (dim == 2 ? '2' : '3')),
683 fe_name[template_starts + 1] = '1';
684 }
685 const std::unique_ptr<FiniteElement<1>> fe(
686 FETools::get_fe_by_name<1, 1>(fe_name));
687
688 setup_element_info(elem_info, *fe, dof_handler);
689
690
691 // ---------- 2. Extract and match dof indices between child and parent
692 const unsigned int n_levels = tria.n_global_levels();
693 level_dof_indices.resize(n_levels);
694 parent_child_connect.resize(n_levels - 1);
695 n_owned_level_cells.resize(n_levels - 1);
696 std::vector<std::vector<unsigned int>> coarse_level_indices(n_levels - 1);
697 for (unsigned int level = 0;
698 level < std::min(tria.n_levels(), n_levels - 1);
699 ++level)
700 coarse_level_indices[level].resize(tria.n_raw_cells(level),
702 std::vector<types::global_dof_index> local_dof_indices(
703 dof_handler.get_fe().n_dofs_per_cell());
704 dirichlet_indices.resize(n_levels - 1);
705
706 AssertDimension(target_partitioners.max_level(), n_levels - 1);
707 Assert(external_partitioners.empty() ||
708 external_partitioners.size() == n_levels,
709 ExcDimensionMismatch(external_partitioners.size(), n_levels));
710
711 for (unsigned int level = n_levels - 1; level > 0; --level)
712 {
713 unsigned int counter = 0;
714 std::vector<types::global_dof_index> global_level_dof_indices;
715 std::vector<types::global_dof_index> global_level_dof_indices_remote;
716 std::vector<types::global_dof_index> ghosted_level_dofs;
717 std::vector<types::global_dof_index> global_level_dof_indices_l0;
718 std::vector<types::global_dof_index> ghosted_level_dofs_l0;
719
720 // step 2.1: loop over the cells on the coarse side
721 typename DoFHandler<dim>::cell_iterator cell,
722 endc = dof_handler.end(level - 1);
723 for (cell = dof_handler.begin(level - 1); cell != endc; ++cell)
724 {
725 // need to look into a cell if it has children and it is locally
726 // owned
727 if (!cell->has_children())
728 continue;
729
730 bool consider_cell =
731 (tria.locally_owned_subdomain() ==
733 cell->level_subdomain_id() == tria.locally_owned_subdomain());
734
735 // due to the particular way we store DoF indices (via children),
736 // we also need to add the DoF indices for coarse cells where we
737 // own at least one child
738 const bool cell_is_remote = !consider_cell;
739 for (unsigned int c = 0;
740 c < GeometryInfo<dim>::max_children_per_cell;
741 ++c)
742 if (cell->child(c)->level_subdomain_id() ==
743 tria.locally_owned_subdomain())
744 {
745 consider_cell = true;
746 break;
747 }
748
749 if (!consider_cell)
750 continue;
751
752 // step 2.2: loop through children and append the dof indices to
753 // the appropriate list. We need separate lists for the owned
754 // coarse cell case (which will be part of
755 // restriction/prolongation between level-1 and level) and the
756 // remote case (which needs to store DoF indices for the
757 // operations between level and level+1).
758 AssertDimension(cell->n_children(),
760 std::vector<types::global_dof_index> &next_indices =
761 cell_is_remote ? global_level_dof_indices_remote :
762 global_level_dof_indices;
763 const std::size_t start_index = next_indices.size();
764 next_indices.resize(start_index + elem_info.n_child_cell_dofs,
766 for (unsigned int c = 0;
767 c < GeometryInfo<dim>::max_children_per_cell;
768 ++c)
769 {
770 if (cell_is_remote && cell->child(c)->level_subdomain_id() !=
771 tria.locally_owned_subdomain())
772 continue;
773 cell->child(c)->get_mg_dof_indices(local_dof_indices);
774
775 resolve_identity_constraints(mg_constrained_dofs,
776 level,
777 local_dof_indices);
778
779 const IndexSet &owned_level_dofs =
780 dof_handler.locally_owned_mg_dofs(level);
781 for (const auto local_dof_index : local_dof_indices)
782 if (!owned_level_dofs.is_element(local_dof_index))
783 ghosted_level_dofs.push_back(local_dof_index);
784
785 add_child_indices<dim>(c,
786 fe->n_dofs_per_cell() -
787 fe->n_dofs_per_vertex(),
788 fe->degree,
789 elem_info.lexicographic_numbering,
790 local_dof_indices,
791 &next_indices[start_index]);
792
793 // step 2.3 store the connectivity to the parent
794 if (cell->child(c)->has_children() &&
795 (tria.locally_owned_subdomain() ==
797 cell->child(c)->level_subdomain_id() ==
798 tria.locally_owned_subdomain()))
799 {
800 const unsigned int child_index =
801 coarse_level_indices[level][cell->child(c)->index()];
802 AssertIndexRange(child_index,
803 parent_child_connect[level].size());
804 unsigned int parent_index = counter;
805 // remote cells, i.e., cells where we work on a further
806 // level but are not treated on the current level, need to
807 // be placed at the end of the list; however, we do not
808 // yet know the exact position in the array, so shift
809 // their parent index by the number of cells so we can set
810 // the correct number after the end of this loop
811 if (cell_is_remote)
812 parent_index =
813 start_index / elem_info.n_child_cell_dofs +
814 tria.n_cells(level);
815 parent_child_connect[level][child_index] =
816 std::make_pair(parent_index, c);
818 static_cast<unsigned short>(-1));
819
820 // set Dirichlet boundary conditions (as a list of
821 // constrained DoFs) for the child
822 if (mg_constrained_dofs != nullptr)
823 for (unsigned int i = 0;
824 i < dof_handler.get_fe().n_dofs_per_cell();
825 ++i)
826 if (mg_constrained_dofs->is_boundary_index(
827 level,
828 local_dof_indices
829 [elem_info.lexicographic_numbering[i]]))
830 dirichlet_indices[level][child_index].push_back(i);
831 }
832 }
833 if (!cell_is_remote)
834 {
835 AssertIndexRange(static_cast<unsigned int>(cell->index()),
836 coarse_level_indices[level - 1].size());
837 coarse_level_indices[level - 1][cell->index()] = counter++;
838 }
839
840 // step 2.4: include indices for the coarsest cells. we still
841 // insert the indices as if they were from a child in order to use
842 // the same code (the coarsest level does not matter much in terms
843 // of memory, so we gain in code simplicity)
844 if (level == 1 && !cell_is_remote)
845 {
846 cell->get_mg_dof_indices(local_dof_indices);
847
848 resolve_identity_constraints(mg_constrained_dofs,
849 level - 1,
850 local_dof_indices);
851
852 const IndexSet &owned_level_dofs_l0 =
853 dof_handler.locally_owned_mg_dofs(0);
854 for (const auto local_dof_index : local_dof_indices)
855 if (!owned_level_dofs_l0.is_element(local_dof_index))
856 ghosted_level_dofs_l0.push_back(local_dof_index);
857
858 const std::size_t start_index =
859 global_level_dof_indices_l0.size();
860 global_level_dof_indices_l0.resize(
861 start_index + elem_info.n_child_cell_dofs,
863 add_child_indices<dim>(
864 0,
865 fe->n_dofs_per_cell() - fe->n_dofs_per_vertex(),
866 fe->degree,
867 elem_info.lexicographic_numbering,
868 local_dof_indices,
869 &global_level_dof_indices_l0[start_index]);
870
871 dirichlet_indices[0].emplace_back();
872 if (mg_constrained_dofs != nullptr)
873 for (unsigned int i = 0;
874 i < dof_handler.get_fe().n_dofs_per_cell();
875 ++i)
876 if (mg_constrained_dofs->is_boundary_index(
877 0,
878 local_dof_indices[elem_info
880 dirichlet_indices[0].back().push_back(i);
881 }
882 }
883
884 // step 2.5: store information about the current level and prepare the
885 // Dirichlet indices and parent-child relationship for the next
886 // coarser level
887 AssertDimension(counter * elem_info.n_child_cell_dofs,
888 global_level_dof_indices.size());
889 n_owned_level_cells[level - 1] = counter;
890 dirichlet_indices[level - 1].resize(counter);
891 parent_child_connect[level - 1].resize(
892 counter,
893 std::make_pair(numbers::invalid_unsigned_int,
895
896 // step 2.6: put the cells with remotely owned parent to the end of
897 // the list (these are needed for the transfer from level to level+1
898 // but not for the transfer from level-1 to level).
899 if (level < n_levels - 1)
900 for (std::vector<std::pair<unsigned int, unsigned int>>::iterator
901 i = parent_child_connect[level].begin();
902 i != parent_child_connect[level].end();
903 ++i)
904 if (i->first >= tria.n_cells(level))
905 {
906 i->first -= tria.n_cells(level);
907 i->first += counter;
908 }
909
910 // step 2.7: Initialize the partitioner for the ghosted vector
911 //
912 // We use a vector based on the target partitioner handed in also in
913 // the base class for keeping ghosted transfer indices. To avoid
914 // keeping two very similar vectors, we keep one single ghosted
915 // vector that is augmented/filled here.
917 ghosted_level_dofs,
918 external_partitioners.empty() ?
919 nullptr :
920 external_partitioners[level],
921 tria.get_mpi_communicator(),
922 target_partitioners[level],
923 copy_indices_global_mine[level]);
924
925 copy_indices_to_mpi_local_numbers(*target_partitioners[level],
926 global_level_dof_indices,
927 global_level_dof_indices_remote,
928 level_dof_indices[level]);
929 // step 2.8: Initialize the ghosted vector for level 0
930 if (level == 1)
931 {
932 for (unsigned int i = 0; i < parent_child_connect[0].size(); ++i)
933 parent_child_connect[0][i] = std::make_pair(i, 0U);
934
936 ghosted_level_dofs_l0,
937 external_partitioners.empty() ?
938 nullptr :
939 external_partitioners[0],
940 tria.get_mpi_communicator(),
941 target_partitioners[0],
942 copy_indices_global_mine[0]);
943
945 *target_partitioners[0],
946 global_level_dof_indices_l0,
947 std::vector<types::global_dof_index>(),
948 level_dof_indices[0]);
949 }
950 }
951
952 // ---------------------- 3. compute weights to make restriction additive
953
954 const unsigned int n_child_dofs_1d =
955 fe->degree + 1 + fe->n_dofs_per_cell() - fe->n_dofs_per_vertex();
956
957 // get the valence of the individual components and compute the weights as
958 // the inverse of the valence
959 weights_on_refined.resize(n_levels - 1);
960 for (unsigned int level = 1; level < n_levels; ++level)
961 {
963 target_partitioners[level]);
964 for (unsigned int c = 0; c < n_owned_level_cells[level - 1]; ++c)
965 for (unsigned int j = 0; j < elem_info.n_child_cell_dofs; ++j)
966 touch_count.local_element(
967 level_dof_indices[level][elem_info.n_child_cell_dofs * c +
968 j]) += Number(1.);
969 touch_count.compress(VectorOperation::add);
970 touch_count.update_ghost_values();
971
972 std::vector<unsigned int> degree_to_3(n_child_dofs_1d);
973 degree_to_3[0] = 0;
974 for (unsigned int i = 1; i < n_child_dofs_1d - 1; ++i)
975 degree_to_3[i] = 1;
976 degree_to_3.back() = 2;
977
978 // we only store 3^dim weights because all dofs on a line have the
979 // same valence, and all dofs on a quad have the same valence.
980 weights_on_refined[level - 1].resize(n_owned_level_cells[level - 1] *
981 Utilities::fixed_power<dim>(3));
982 for (unsigned int c = 0; c < n_owned_level_cells[level - 1]; ++c)
983 for (unsigned int k = 0, m = 0; k < (dim > 2 ? n_child_dofs_1d : 1);
984 ++k)
985 for (unsigned int j = 0; j < (dim > 1 ? n_child_dofs_1d : 1); ++j)
986 {
987 unsigned int shift = 9 * degree_to_3[k] + 3 * degree_to_3[j];
988 for (unsigned int i = 0; i < n_child_dofs_1d; ++i, ++m)
989 weights_on_refined[level -
990 1][c * Utilities::fixed_power<dim>(3) +
991 shift + degree_to_3[i]] =
992 Number(1.) /
993 touch_count.local_element(
994 level_dof_indices[level]
995 [elem_info.n_child_cell_dofs * c + m]);
996 }
997 }
998 }
999
1000
1001
1002 void
1004 const MGConstrainedDoFs *mg_constrained_dofs,
1005 const unsigned int level,
1006 std::vector<types::global_dof_index> &dof_indices)
1007 {
1008 if (mg_constrained_dofs != nullptr &&
1009 mg_constrained_dofs->get_level_constraints(level).n_constraints() > 0)
1010 for (auto &ind : dof_indices)
1011 if (mg_constrained_dofs->get_level_constraints(level)
1013 {
1014 Assert(mg_constrained_dofs->get_level_constraints(level)
1016 ->size() == 1,
1018 ind = mg_constrained_dofs->get_level_constraints(level)
1020 ->front()
1021 .first;
1022 }
1023 }
1024
1025 } // namespace MGTransfer
1026} // namespace internal
1027
1028// Explicit instantiations
1029
1030#include "multigrid/mg_transfer_internal.inst"
1031
*  *  iterator begin()
const std::vector< std::pair< size_type, number > > * get_constraint_entries(const size_type line_n) const
bool is_identity_constrained(const size_type line_n) const
size_type n_constraints() const
cell_iterator end() const
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
cell_iterator begin(const unsigned int level=0) const
unsigned int n_dofs_per_vertex() const
const unsigned int degree
Definition fe_data.h:450
unsigned int n_dofs_per_cell() const
unsigned int n_dofs_per_line() const
virtual std::string get_name() const =0
virtual const FiniteElement< dim, spacedim > & base_element(const unsigned int index) const
unsigned int element_multiplicity(const unsigned int index) const
unsigned int n_base_elements() const
virtual const FullMatrix< double > & get_prolongation_matrix(const unsigned int child, const RefinementCase< dim > &refinement_case=RefinementCase< dim >::isotropic_refinement) 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
void clear()
Definition index_set.h:1735
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
Number local_element(const size_type local_index) const
void compress(VectorOperation::values operation)
bool at_refinement_edge(const unsigned int level, const types::global_dof_index index) const
bool is_boundary_index(const unsigned int level, const types::global_dof_index index) const
const AffineConstraints< double > & get_user_constraint_matrix(const unsigned int level) const
const AffineConstraints< double > & get_level_constraints(const unsigned int level) const
Definition point.h:111
virtual types::subdomain_id locally_owned_subdomain() const
virtual unsigned int n_global_levels() const
unsigned int global_to_local(const types::global_dof_index global_index) const
#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
unsigned int level
Definition grid_out.cc:4642
IteratorRange< active_cell_iterator > active_cell_iterators_on_level(const unsigned int level) 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 & 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
std::vector< index_type > data
Definition mpi.cc:734
std::size_t size
Definition mpi.cc:733
@ mg_transfer_fill_copy_indices
mg_transfer_internal.cc: fill_copy_indices()
Definition mpi_tags.h:70
T sum(const T &t, const MPI_Comm mpi_communicator)
bool logical_and(const bool t, const MPI_Comm mpi_communicator)
std::vector< unsigned int > compute_index_owner(const IndexSet &owned_indices, const IndexSet &indices_to_look_up, const MPI_Comm comm)
Definition mpi.cc:1820
void setup_element_info(ElementInfo< Number > &elem_info, const FiniteElement< 1 > &fe, const DoFHandler< dim > &dof_handler)
void resolve_identity_constraints(const MGConstrainedDoFs *mg_constrained_dofs, const unsigned int level, std::vector< types::global_dof_index > &dof_indices)
unsigned int compute_shift_within_children(const unsigned int child, const unsigned int fe_shift_1d, const unsigned int fe_degree)
void add_child_indices(const unsigned int child, const unsigned int fe_shift_1d, const unsigned int fe_degree, const std::vector< unsigned int > &lexicographic_numbering, const std::vector< types::global_dof_index > &local_dof_indices, types::global_dof_index *target_indices)
void setup_transfer(const DoFHandler< dim > &dof_handler, const MGConstrainedDoFs *mg_constrained_dofs, const std::vector< std::shared_ptr< const Utilities::MPI::Partitioner > > &external_partitioners, ElementInfo< Number > &elem_info, std::vector< std::vector< unsigned int > > &level_dof_indices, std::vector< std::vector< std::pair< unsigned int, unsigned int > > > &parent_child_connect, std::vector< unsigned int > &n_owned_level_cells, std::vector< std::vector< std::vector< unsigned short > > > &dirichlet_indices, std::vector< std::vector< Number > > &weights_on_refined, std::vector< Table< 2, unsigned int > > &copy_indices_global_mine, MGLevelObject< std::shared_ptr< const Utilities::MPI::Partitioner > > &vector_partitioners)
void fill_copy_indices(const DoFHandler< dim, spacedim > &dof_handler, const MGConstrainedDoFs *mg_constrained_dofs, std::vector< std::vector< std::pair< types::global_dof_index, types::global_dof_index > > > &copy_indices, std::vector< std::vector< std::pair< types::global_dof_index, types::global_dof_index > > > &copy_indices_global_mine, std::vector< std::vector< std::pair< types::global_dof_index, types::global_dof_index > > > &copy_indices_level_mine, const bool skip_interface_dofs=true)
void reinit_level_partitioner(const IndexSet &locally_owned, std::vector< types::global_dof_index > &ghosted_level_dofs, const std::shared_ptr< const Utilities::MPI::Partitioner > &external_partitioner, const MPI_Comm communicator, std::shared_ptr< const Utilities::MPI::Partitioner > &target_partitioner, Table< 2, unsigned int > &copy_indices_global_mine)
void copy_indices_to_mpi_local_numbers(const Utilities::MPI::Partitioner &part, const std::vector< types::global_dof_index > &mine, const std::vector< types::global_dof_index > &remote, std::vector< unsigned int > &localized_indices)
constexpr types::global_dof_index invalid_dof_index
Definition types.h:259
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
constexpr types::subdomain_id artificial_subdomain_id
Definition types.h:406
constexpr types::subdomain_id invalid_subdomain_id
Definition types.h:385
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
types::global_dof_index global_dof_index
types::global_dof_index level_dof_index
DoFPair(const unsigned int level, const types::global_dof_index global_dof_index, const types::global_dof_index level_dof_index)
std::vector< unsigned int > lexicographic_numbering
void reinit(const Quadrature< dim_q > &quad, const FiniteElement< dim, spacedim > &fe_dim, const unsigned int base_element=0)
std::vector< unsigned int > lexicographic_numbering
Definition shape_info.h:484