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
tria_description.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) 2020 - 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
15#include <deal.II/base/mpi.h>
17
20
23
25#include <deal.II/grid/tria.h>
27
29
30
31template <int structdim>
32CellData<structdim>::CellData(const unsigned int n_vertices)
33 : vertices(n_vertices, numbers::invalid_unsigned_int)
34 , material_id(0)
35 , manifold_id(numbers::flat_manifold_id)
36{}
37
38
39
40template <int structdim>
41bool
43{
44 if (vertices.size() != other.vertices.size())
45 return false;
46
47 for (unsigned int i = 0; i < vertices.size(); ++i)
48 if (vertices[i] != other.vertices[i])
49 return false;
50
51 if (material_id != other.material_id)
52 return false;
53
54 if (boundary_id != other.boundary_id)
55 return false;
56
57 if (manifold_id != other.manifold_id)
58 return false;
59
60 return true;
61}
62
63
64
65bool
66SubCellData::check_consistency(const unsigned int dim) const
67{
68 switch (dim)
69 {
70 case 1:
71 return ((boundary_lines.empty()) && (boundary_quads.empty()));
72 case 2:
73 return (boundary_quads.empty());
74 }
75 return true;
76}
77
79{
80 namespace Utilities
81 {
82 namespace
83 {
87 template <int dim, int spacedim>
88 struct DescriptionTemp
89 {
94 template <class Archive>
95 void
96 serialize(Archive &ar, const unsigned int /*version*/)
97 {
98 ar &coarse_cells;
101 ar &cell_infos;
102 }
103
107 void
108 collect(
109 const std::vector<unsigned int> &future_owners_of_locally_owned_cells,
110 const std::vector<DescriptionTemp<dim, spacedim>> &description_temp,
111 const MPI_Comm comm,
112 const bool vertices_have_unique_ids)
113 {
114 // Use the some-to-some version of the consensus algorithm framework
115 // whereby we send requests to other processes that then deal with
116 // them but do not send anything back.
117 //
118 // Note that the input (description_temp) *may* contain an entry for
119 // the current process. As documented, the consensus algorithm will
120 // simply copy that into the output queue, i.e., it will call
121 // process_request() on it as well, and the data will simply come
122 // back out on the local process.
123 const auto create_request = [&](const unsigned int other_rank) {
124 const auto ptr =
125 std::find(future_owners_of_locally_owned_cells.begin(),
126 future_owners_of_locally_owned_cells.end(),
127 other_rank);
128
129 Assert(ptr != future_owners_of_locally_owned_cells.end(),
131
132 const auto other_rank_index =
133 std::distance(future_owners_of_locally_owned_cells.begin(), ptr);
134
135 return description_temp[other_rank_index];
136 };
137
138 const auto process_request =
139 [&](const unsigned int,
140 const DescriptionTemp<dim, spacedim> &request) -> void {
141 this->merge(request, vertices_have_unique_ids);
142 };
143
145 DescriptionTemp<dim, spacedim>>(
146 future_owners_of_locally_owned_cells,
147 create_request,
148 process_request,
149 comm);
150 }
157 void
158 merge(const DescriptionTemp<dim, spacedim> &other,
159 const bool vertices_have_unique_ids)
160 {
161 this->cell_infos.resize(
162 std::max(other.cell_infos.size(), this->cell_infos.size()));
163
164 if (vertices_have_unique_ids == false) // need to compare points
165 {
166 // map point to local vertex index
167 std::map<Point<spacedim>,
168 unsigned int,
170 map_point_to_local_vertex_index(
172
173 // ... initialize map with existing points
174 for (unsigned int i = 0; i < this->coarse_cell_vertices.size();
175 ++i)
176 map_point_to_local_vertex_index[coarse_cell_vertices[i]
177 .second] = i;
178
179 // map local vertex indices within other to the new local indices
180 std::map<unsigned int, unsigned int>
181 map_old_to_new_local_vertex_index;
182
183 // 1) re-enumerate vertices in other and insert into maps
184 unsigned int counter = coarse_cell_vertices.size();
185 for (const auto &p : other.coarse_cell_vertices)
186 if (map_point_to_local_vertex_index.find(p.second) ==
187 map_point_to_local_vertex_index.end())
188 {
189 this->coarse_cell_vertices.emplace_back(counter, p.second);
190 map_point_to_local_vertex_index[p.second] =
191 map_old_to_new_local_vertex_index[p.first] = counter++;
192 }
193 else
194 map_old_to_new_local_vertex_index[p.first] =
195 map_point_to_local_vertex_index[p.second];
196
197 // 2) re-enumerate vertices of cells
198 auto other_coarse_cells_copy = other.coarse_cells;
199
200 for (auto &cell : other_coarse_cells_copy)
201 for (auto &v : cell.vertices)
202 v = map_old_to_new_local_vertex_index[v];
203
204 this->coarse_cells.insert(this->coarse_cells.end(),
205 other_coarse_cells_copy.begin(),
206 other_coarse_cells_copy.end());
207 }
208 else
209 {
210 this->coarse_cells.insert(this->coarse_cells.end(),
211 other.coarse_cells.begin(),
212 other.coarse_cells.end());
213 this->coarse_cell_vertices.insert(
214 this->coarse_cell_vertices.end(),
215 other.coarse_cell_vertices.begin(),
216 other.coarse_cell_vertices.end());
217 }
218
221 other.coarse_cell_index_to_coarse_cell_id.begin(),
222 other.coarse_cell_index_to_coarse_cell_id.end());
223
224 for (unsigned int i = 0; i < this->cell_infos.size(); ++i)
225 this->cell_infos[i].insert(this->cell_infos[i].end(),
226 other.cell_infos[i].begin(),
227 other.cell_infos[i].end());
228 }
229
233 void
234 reduce()
235 {
236 // make coarse cells unique
237 {
238 std::vector<std::tuple<types::coarse_cell_id,
240 unsigned int>>
241 temp;
242
243 temp.reserve(this->coarse_cells.size());
244 for (unsigned int i = 0; i < this->coarse_cells.size(); ++i)
245 temp.emplace_back(this->coarse_cell_index_to_coarse_cell_id[i],
246 this->coarse_cells[i],
247 i);
248
249 std::sort(temp.begin(),
250 temp.end(),
251 [](const auto &a, const auto &b) {
252 return std::get<0>(a) < std::get<0>(b);
253 });
254 temp.erase(std::unique(temp.begin(),
255 temp.end(),
256 [](const auto &a, const auto &b) {
257 return std::get<0>(a) == std::get<0>(b);
258 }),
259 temp.end());
260 std::sort(temp.begin(),
261 temp.end(),
262 [](const auto &a, const auto &b) {
263 return std::get<2>(a) < std::get<2>(b);
264 });
265
266 this->coarse_cell_index_to_coarse_cell_id.resize(temp.size());
267 this->coarse_cells.resize(temp.size());
268
269 for (unsigned int i = 0; i < temp.size(); ++i)
270 {
272 std::get<0>(temp[i]);
273 this->coarse_cells[i] = std::get<1>(temp[i]);
274 }
275 }
276
277 // make coarse cell vertices unique
278 {
279 std::sort(this->coarse_cell_vertices.begin(),
280 this->coarse_cell_vertices.end(),
281 [](const std::pair<unsigned int, Point<spacedim>> &a,
282 const std::pair<unsigned int, Point<spacedim>> &b) {
283 return a.first < b.first;
284 });
285 this->coarse_cell_vertices.erase(
286 std::unique(
287 this->coarse_cell_vertices.begin(),
288 this->coarse_cell_vertices.end(),
289 [](const std::pair<unsigned int, Point<spacedim>> &a,
290 const std::pair<unsigned int, Point<spacedim>> &b) {
291 if (a.first == b.first)
292 {
293 Assert(a.second.distance(b.second) <=
294 1e-7 *
295 std::max(a.second.norm(), b.second.norm()),
296 ExcMessage(
297 "In the process of merging the vertices of "
298 "the coarse meshes used on different processes, "
299 "there were two processes that used the same "
300 "vertex index for points that are not the same. "
301 "This suggests that you are using different "
302 "coarse meshes on different processes. This "
303 "should not happen."));
304 return true;
305 }
306 return false;
307 }),
308 this->coarse_cell_vertices.end());
309 }
310
311 // make cells unique
312 for (unsigned int i = 0; i < this->cell_infos.size(); ++i)
313 {
314 if (this->cell_infos[i].empty())
315 continue;
316
317 std::sort(this->cell_infos[i].begin(),
318 this->cell_infos[i].end(),
319 [](const auto &a, const auto &b) {
320 return a.id < b.id;
321 });
322
323 std::vector<CellData<dim>> temp;
324 temp.push_back(this->cell_infos[i][0]);
325
326 for (unsigned int j = 1; j < this->cell_infos[i].size(); ++j)
327 if (temp.back().id == cell_infos[i][j].id)
328 {
329 temp.back().subdomain_id =
330 std::min(temp.back().subdomain_id,
331 this->cell_infos[i][j].subdomain_id);
332 temp.back().level_subdomain_id =
333 std::min(temp.back().level_subdomain_id,
334 this->cell_infos[i][j].level_subdomain_id);
335 }
336 else
337 {
338 temp.push_back(this->cell_infos[i][j]);
339 }
340
341 this->cell_infos[i] = temp;
342 }
343 }
344
350 Description<dim, spacedim>
351 convert(const MPI_Comm comm,
353 mesh_smoothing,
355 {
356 Description<dim, spacedim> description;
357
358 // copy communicator
359 description.comm = comm;
360
361 description.settings = settings;
362
363 // use mesh smoothing from base triangulation
364 description.smoothing = mesh_smoothing;
365
366 std::map<unsigned int, unsigned int> map;
367
368 for (unsigned int i = 0; i < this->coarse_cell_vertices.size(); ++i)
369 {
370 description.coarse_cell_vertices.push_back(
372 map[this->coarse_cell_vertices[i].first] = i;
373 }
374
375 description.coarse_cells = this->coarse_cells;
376
377 for (auto &cell : description.coarse_cells)
378 for (unsigned int v = 0; v < cell.vertices.size(); ++v)
379 cell.vertices[v] = map[cell.vertices[v]];
380
381 description.coarse_cell_index_to_coarse_cell_id =
383 description.cell_infos = this->cell_infos;
384
385 return description;
386 }
387
388 std::vector<::CellData<dim>> coarse_cells;
389
390 std::vector<std::pair<unsigned int, Point<spacedim>>>
392
393 std::vector<types::coarse_cell_id> coarse_cell_index_to_coarse_cell_id;
394
395 std::vector<std::vector<CellData<dim>>> cell_infos;
396 };
397
401 template <int dim, int spacedim>
402 void
403 mark_cell_and_its_parents(
405 std::vector<std::vector<bool>> &cell_marked)
406 {
407 cell_marked[cell->level()][cell->index()] = true;
408 if (cell->level() != 0)
409 mark_cell_and_its_parents(cell->parent(), cell_marked);
410 }
411
417 template <typename DescriptionType, int dim, int spacedim>
418 DescriptionType
419 create_description_for_rank(
420 const ::Triangulation<dim, spacedim> &tria,
421 const std::function<types::subdomain_id(
422 const typename ::Triangulation<dim, spacedim>::cell_iterator &)>
423 &subdomain_id_function,
424 const std::function<types::subdomain_id(
425 const typename ::Triangulation<dim, spacedim>::cell_iterator &)>
426 &level_subdomain_id_function,
427 const std::map<unsigned int, std::vector<unsigned int>>
428 &coinciding_vertex_groups,
429 const std::map<unsigned int, unsigned int>
430 &vertex_to_coinciding_vertex_group,
431 const MPI_Comm comm,
432 const unsigned int my_rank,
434 {
435 static_assert(
436 std::is_same_v<DescriptionType, Description<dim, spacedim>> ||
437 std::is_same_v<DescriptionType, DescriptionTemp<dim, spacedim>>,
438 "Wrong template type.");
439 Assert(
442 (tria.get_mesh_smoothing() &
443 Triangulation<dim, spacedim>::limit_level_difference_at_vertices),
445 "Source triangulation has to be set up with "
446 "limit_level_difference_at_vertices if the construction of the "
447 "multigrid hierarchy is requested!"));
448
449 const bool construct_multigrid =
452
453 DescriptionType construction_data;
454 if constexpr (std::is_same_v<DescriptionType,
455 Description<dim, spacedim>>)
456 {
457 construction_data.comm = comm;
458 construction_data.smoothing = tria.get_mesh_smoothing();
459 construction_data.settings = settings;
460 }
461 else
462 (void)comm;
463
464 // A helper function that marks the indices of all vertices belonging
465 // to a cell (also taking into account their periodic breathren) in
466 // the bit vector passed as second argument.
467 const auto
468 add_vertices_of_cell_to_vertices_owned_by_locally_owned_cells =
469 [&coinciding_vertex_groups, &vertex_to_coinciding_vertex_group](
470 const typename ::Triangulation<dim, spacedim>::cell_iterator
471 &cell,
472 std::vector<bool> &vertices_on_locally_owned_cells) {
473 for (const unsigned int v : cell->vertex_indices())
474 {
475 const auto global_vertex_index = cell->vertex_index(v);
476 vertices_on_locally_owned_cells[global_vertex_index] = true;
477
478 const auto coinciding_vertex_group =
479 vertex_to_coinciding_vertex_group.find(global_vertex_index);
480 if (coinciding_vertex_group !=
481 vertex_to_coinciding_vertex_group.end())
482 for (const auto &co_vertex : coinciding_vertex_groups.at(
483 coinciding_vertex_group->second))
484 vertices_on_locally_owned_cells[co_vertex] = true;
485 }
486 };
487
488 const auto add_vertices =
489 [&tria](const std::vector<bool> &vertices_locally_relevant,
490 DescriptionType &construction_data) {
491 if constexpr (std::is_same_v<DescriptionType,
492 Description<dim, spacedim>>)
493 {
494 std::vector<unsigned int> vertices_locally_relevant_indices(
495 vertices_locally_relevant.size());
496
497 // enumerate locally relevant vertices
498 unsigned int vertex_counter = 0;
499 for (unsigned int i = 0; i < vertices_locally_relevant.size();
500 ++i)
501 if (vertices_locally_relevant[i])
502 {
503 construction_data.coarse_cell_vertices.push_back(
504 tria.get_vertices()[i]);
505 vertices_locally_relevant_indices[i] = vertex_counter++;
506 }
507
508 // correct vertices of cells (make them local)
509 for (auto &cell : construction_data.coarse_cells)
510 for (unsigned int v = 0; v < cell.vertices.size(); ++v)
511 cell.vertices[v] =
512 vertices_locally_relevant_indices[cell.vertices[v]];
513 }
514 else
515 {
516 for (unsigned int i = 0; i < vertices_locally_relevant.size();
517 ++i)
518 if (vertices_locally_relevant[i])
519 construction_data.coarse_cell_vertices.emplace_back(
520 i, tria.get_vertices()[i]);
521 }
522 };
523
524
525 // 1) loop over levels (from fine to coarse) and mark on each level
526 // the locally relevant cells
527 std::vector<std::vector<bool>> cell_marked(tria.n_levels());
528 for (unsigned int l = 0; l < tria.n_levels(); ++l)
529 cell_marked[l].resize(tria.n_raw_cells(l));
530
531 for (int level = tria.get_triangulation().n_global_levels() - 1;
532 level >= 0;
533 --level)
534 {
535 // collect vertices connected to a (on any level) locally owned
536 // cell
537 std::vector<bool> vertices_owned_by_locally_owned_cells_on_level(
538 tria.n_vertices());
539 for (const auto &cell : tria.cell_iterators_on_level(level))
540 if (construct_multigrid &&
541 (level_subdomain_id_function(cell) == my_rank))
542 add_vertices_of_cell_to_vertices_owned_by_locally_owned_cells(
543 cell, vertices_owned_by_locally_owned_cells_on_level);
544
545 for (const auto &cell : tria.active_cell_iterators())
546 if (subdomain_id_function(cell) == my_rank)
547 add_vertices_of_cell_to_vertices_owned_by_locally_owned_cells(
548 cell, vertices_owned_by_locally_owned_cells_on_level);
549
550 // helper function to determine if cell is locally relevant
551 // (i.e. a cell which is connected to a vertex via a locally
552 // owned cell)
553 const auto is_locally_relevant_on_level = [&](const auto &cell) {
554 for (const auto v : cell->vertex_indices())
555 if (vertices_owned_by_locally_owned_cells_on_level
556 [cell->vertex_index(v)])
557 return true;
558 return false;
559 };
560
561 // mark all locally relevant cells
562 for (const auto &cell : tria.cell_iterators_on_level(level))
563 if (is_locally_relevant_on_level(cell))
564 mark_cell_and_its_parents(cell, cell_marked);
565 }
566
567 // 2) set_up coarse-grid triangulation
568 {
569 std::vector<bool> vertices_locally_relevant(tria.n_vertices(), false);
570
571 // a) loop over all cells
572 for (const auto &cell : tria.cell_iterators_on_level(0))
573 {
574 if (!cell_marked[cell->level()][cell->index()])
575 continue;
576
577 // extract cell definition (with old numbering of vertices)
578 ::CellData<dim> cell_data(cell->n_vertices());
579 cell_data.material_id = cell->material_id();
580 cell_data.manifold_id = cell->manifold_id();
581 for (const auto v : cell->vertex_indices())
582 cell_data.vertices[v] = cell->vertex_index(v);
583 construction_data.coarse_cells.push_back(cell_data);
584
585 // save indices of each vertex of this cell
586 for (const auto v : cell->vertex_indices())
587 vertices_locally_relevant[cell->vertex_index(v)] = true;
588
589 // save translation for corase grid: lid -> gid
590 construction_data.coarse_cell_index_to_coarse_cell_id.push_back(
591 cell->id().get_coarse_cell_id());
592 }
593
594 add_vertices(vertices_locally_relevant, construction_data);
595 }
596
597
598 // 3) collect info of each cell
599 construction_data.cell_infos.resize(
600 tria.get_triangulation().n_global_levels());
601
602 // collect local vertices on active level
603 std::vector<bool> vertices_owned_by_locally_owned_active_cells(
604 tria.n_vertices());
605 for (const auto &cell : tria.active_cell_iterators())
606 if (subdomain_id_function(cell) == my_rank)
607 add_vertices_of_cell_to_vertices_owned_by_locally_owned_cells(
608 cell, vertices_owned_by_locally_owned_active_cells);
609
610 // helper function to determine if cell is locally relevant
611 // on active level
612 const auto is_locally_relevant_on_active_level = [&](const auto &cell) {
613 if (cell->is_active())
614 for (const auto v : cell->vertex_indices())
615 if (vertices_owned_by_locally_owned_active_cells
616 [cell->vertex_index(v)])
617 return true;
618 return false;
619 };
620
621 for (unsigned int level = 0;
622 level < tria.get_triangulation().n_global_levels();
623 ++level)
624 {
625 // collect local vertices on level
626 std::vector<bool> vertices_owned_by_locally_owned_cells_on_level(
627 tria.n_vertices());
628 for (const auto &cell : tria.cell_iterators_on_level(level))
629 if ((construct_multigrid &&
630 (level_subdomain_id_function(cell) == my_rank)) ||
631 (cell->is_active() && subdomain_id_function(cell) == my_rank))
632 add_vertices_of_cell_to_vertices_owned_by_locally_owned_cells(
633 cell, vertices_owned_by_locally_owned_cells_on_level);
634
635 // helper function to determine if cell is locally relevant
636 // on level
637 const auto is_locally_relevant_on_level = [&](const auto &cell) {
638 for (const auto v : cell->vertex_indices())
639 if (vertices_owned_by_locally_owned_cells_on_level
640 [cell->vertex_index(v)])
641 return true;
642 return false;
643 };
644
645 auto &level_cell_infos = construction_data.cell_infos[level];
646 for (const auto &cell : tria.cell_iterators_on_level(level))
647 {
648 // check if cell is locally relevant
649 if (!cell_marked[cell->level()][cell->index()])
650 continue;
651
652 CellData<dim> cell_info;
653
654 // save coarse-cell id
655 cell_info.id = cell->id().template to_binary<dim>();
656
657 // save boundary_ids of each face of this cell
658 for (const auto f : cell->face_indices())
659 {
660 types::boundary_id boundary_ind =
661 cell->face(f)->boundary_id();
662 if (boundary_ind != numbers::internal_face_boundary_id)
663 cell_info.boundary_ids.emplace_back(f, boundary_ind);
664 }
665
666 cell_info.material_id = cell->material_id();
667
668 // save manifold id
669 {
670 // ... of cell
671 cell_info.manifold_id = cell->manifold_id();
672
673 // ... of lines
674 if (dim >= 2)
675 for (const auto line : cell->line_indices())
676 cell_info.manifold_line_ids[line] =
677 cell->line(line)->manifold_id();
678
679 // ... of quads
680 if (dim == 3)
681 for (const auto f : cell->face_indices())
682 cell_info.manifold_quad_ids[f] =
683 cell->quad(f)->manifold_id();
684 }
685
686 // subdomain and level subdomain id
687 cell_info.subdomain_id = numbers::artificial_subdomain_id;
688 cell_info.level_subdomain_id = numbers::artificial_subdomain_id;
689
690 if (is_locally_relevant_on_active_level(cell))
691 {
692 cell_info.subdomain_id = subdomain_id_function(cell);
693
694 cell_info.level_subdomain_id =
695 level_subdomain_id_function(cell);
696 }
697 else if (is_locally_relevant_on_level(cell))
698 {
699 cell_info.level_subdomain_id =
700 level_subdomain_id_function(cell);
701 }
702 else
703 {
704 // cell is locally relevant but an artificial cell
705 }
706
707 level_cell_infos.emplace_back(cell_info);
708 }
709 }
710
711 return construction_data;
712 }
713 } // namespace
714
715
716 template <int dim, int spacedim>
717 Description<dim, spacedim>
719 const ::Triangulation<dim, spacedim> &tria,
720 const MPI_Comm comm,
722 const unsigned int my_rank_in)
723 {
724 if (const auto ptria =
725 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
726 &tria))
727 {
728 Assert(comm == ptria->get_mpi_communicator(),
729 ExcMessage("MPI communicators do not match."));
733 "For creation from a parallel::Triangulation, "
734 "my_rank has to equal the rank of the current process "
735 "in the given communicator."));
736 }
737
738 if constexpr (running_in_debug_mode())
739 {
740 // If we are dealing with a sequential triangulation, then someone
741 // will have needed to set the subdomain_ids by hand. Make sure that
742 // all ids we see are less than the number of processes we are
743 // supposed to split the triangulation into.
744 if (dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
745 &tria) == nullptr)
746 {
747 const unsigned int n_mpi_processes =
749 for (const auto &cell : tria.active_cell_iterators())
750 Assert(cell->subdomain_id() < n_mpi_processes,
752 "You can't have a cell with subdomain_id of " +
753 std::to_string(cell->subdomain_id()) +
754 " when splitting the triangulation using an MPI "
755 " communicator with only " +
756 std::to_string(n_mpi_processes) + " processes."));
757 }
758 }
759
760 // First, figure out for what rank we are supposed to build the
761 // TriangulationDescription::Description object
762 const unsigned int my_rank =
763 (my_rank_in == numbers::invalid_unsigned_int ?
765 my_rank_in);
766
767 const auto subdomain_id_function = [](const auto &cell) {
768 return cell->subdomain_id();
769 };
770
771 const auto level_subdomain_id_function = [](const auto &cell) {
772 return cell->level_subdomain_id();
773 };
774
775 std::map<unsigned int, std::vector<unsigned int>>
776 coinciding_vertex_groups;
777 std::map<unsigned int, unsigned int> vertex_to_coinciding_vertex_group;
779 coinciding_vertex_groups,
780 vertex_to_coinciding_vertex_group);
781
782 return create_description_for_rank<Description<dim, spacedim>>(
783 tria,
784 subdomain_id_function,
785 level_subdomain_id_function,
786 coinciding_vertex_groups,
787 vertex_to_coinciding_vertex_group,
788 comm,
789 my_rank,
790 settings);
791 }
792
793
794
795 template <int dim, int spacedim>
798 const std::function<void(::Triangulation<dim, spacedim> &)>
799 &serial_grid_generator,
800 const std::function<void(::Triangulation<dim, spacedim> &,
801 const MPI_Comm,
802 const unsigned int)> &serial_grid_partitioner,
803 const MPI_Comm comm,
804 const int group_size,
805 const typename Triangulation<dim, spacedim>::MeshSmoothing smoothing,
807 {
808#ifndef DEAL_II_WITH_MPI
809 (void)serial_grid_generator;
810 (void)serial_grid_partitioner;
811 (void)comm;
812 (void)group_size;
813 (void)smoothing;
814 (void)settings;
815
817#else
818 const unsigned int my_rank =
820 const unsigned int group_root = (my_rank / group_size) * group_size;
821
822 const int mpi_tag =
824
825 // check if process is root of the group
826 if (my_rank == group_root)
827 {
828 // Step 1: create serial triangulation
832 static_cast<
833 typename ::Triangulation<dim, spacedim>::MeshSmoothing>(
834 smoothing |
835 Triangulation<dim,
836 spacedim>::limit_level_difference_at_vertices) :
837 smoothing);
838 serial_grid_generator(tria);
839
840 // Step 2: partition active cells and ...
841 serial_grid_partitioner(tria, comm, group_size);
842
843 // ... cells on the levels if multigrid is required
844 if (settings &
847
848 const unsigned int end_group =
849 std::min(group_root + group_size,
851
852 // 3) create Description for the other processes in group; since
853 // we expect that this function is called for huge meshes, one
854 // Description is created at a time and send away; only once the
855 // Description has been sent away, the next rank is processed.
856 for (unsigned int other_rank = group_root + 1; other_rank < end_group;
857 ++other_rank)
858 {
859 // 3a) create construction data for other ranks
860 const auto construction_data =
862 comm,
863 settings,
864 other_rank);
865 // 3b) pack
866 std::vector<char> buffer;
867 ::Utilities::pack(construction_data, buffer, false);
868
869 // 3c) send TriangulationDescription::Description
870 const auto ierr = MPI_Send(buffer.data(),
871 buffer.size(),
872 MPI_CHAR,
873 other_rank,
874 mpi_tag,
875 comm);
876 AssertThrowMPI(ierr);
877 }
878
879 // 4) create TriangulationDescription::Description for this process
880 // (root of group)
882 comm,
883 settings,
884 my_rank);
885 }
886 else
887 {
888 // 3a) recv packed TriangulationDescription::Description from
889 // group-root process
890 // (counter-part of 3c of root process)
891 MPI_Status status;
892 auto ierr = MPI_Probe(group_root, mpi_tag, comm, &status);
893 AssertThrowMPI(ierr);
894
895 int len;
896 MPI_Get_count(&status, MPI_CHAR, &len);
897
898 std::vector<char> buf(len);
899 ierr = MPI_Recv(buf.data(),
900 len,
901 MPI_CHAR,
902 status.MPI_SOURCE,
903 mpi_tag,
904 comm,
905 &status);
906 AssertThrowMPI(ierr);
907
908 // 3b) unpack TriangulationDescription::Description (counter-part of
909 // 3b of root process)
910 auto construction_data =
911 ::Utilities::template unpack<Description<dim, spacedim>>(
912 buf, false);
913
914 // WARNING: serialization cannot handle the MPI communicator
915 // which is the reason why we have to set it here explicitly
916 construction_data.comm = comm;
917
918 return construction_data;
919 }
920#endif
921 }
922
923
924
925 template <int dim, int spacedim>
931 {
932 const bool construct_multigrid =
933 (partition.size() > 0) &&
934 (settings &
936
937 Assert(
938 construct_multigrid == false ||
939 (tria.get_mesh_smoothing() &
942 "Source triangulation has to be set up with "
943 "limit_level_difference_at_vertices if the construction of the "
944 "multigrid hierarchy is requested!"));
945
946 std::vector<LinearAlgebra::distributed::Vector<double>> partitions_mg;
947
948 // If desired, also create a multigrid hierarchy. For this, we have to
949 // build a hierarchy of partitions (one for each level of the
950 // triangulation) in which each cell is assigned to the same process
951 // as its first child (if not active) or to the same process that already
952 // owns the cell (for an active level-cell).
953 if (construct_multigrid)
954 {
955 const auto tria_parallel =
956 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
957 &tria);
958 Assert(tria_parallel, ExcNotImplemented());
959
960 // Give the level partitioners the right size:
961 partitions_mg.resize(tria.n_global_levels());
962 for (unsigned int l = 0; l < tria.n_global_levels(); ++l)
963 partitions_mg[l].reinit(
964 tria_parallel->global_level_cell_index_partitioner(l).lock());
965
966 // Make sure we know about all of the owners of the active cells,
967 // whether locally owned or not. Then we traverse the triangulation
968 // from the finest level to the coarsest level.
969 //
970 // On each level, traverse the cell. If the cell is not locally
971 // owned, we don't care about it. If it is active, we copy the
972 // owner process from the cell's non-level owner. Otherwise,
973 // use the owner of the first cell.
974 partition.update_ghost_values();
975 for (int level = tria.n_global_levels() - 1; level >= 0; --level)
976 {
977 for (const auto &cell : tria.cell_iterators_on_level(level))
978 {
979 if (cell->is_locally_owned_on_level())
980 {
981 if (cell->is_active())
982 partitions_mg[level][cell->global_level_cell_index()] =
983 partition[cell->global_active_cell_index()];
984 else
985 partitions_mg[level][cell->global_level_cell_index()] =
986 partitions_mg[level + 1]
987 [cell->child(0)
988 ->global_level_cell_index()];
989 }
990 }
991
992 // Having touched all of the locally owned cells on the
993 // current level, exchange information with the other processes
994 // about the cells that are ghosts so that on the next coarser
995 // level we can access information about children again:
996 partitions_mg[level].update_ghost_values();
997 }
998 }
999
1000 // Forward to the other function.
1002 partition,
1003 partitions_mg,
1004 settings);
1005 }
1006
1007
1008
1009 template <int dim, int spacedim>
1012 const Triangulation<dim, spacedim> &tria,
1015 &partitions_mg,
1016 const TriangulationDescription::Settings settings_in)
1017 {
1018#ifdef DEAL_II_WITH_MPI
1019 if (tria.get_mpi_communicator() == MPI_COMM_NULL)
1020 AssertDimension(partition.locally_owned_size(), 0);
1021#endif
1022
1023 if (partition.size() == 0)
1024 {
1025 AssertDimension(partitions_mg.size(), 0);
1027 tria, tria.get_mpi_communicator(), settings_in);
1028 }
1029
1030 // Update partitioner ghost elements because we will later want
1031 // to ask also about the future owners of ghost cells.
1032 partition.update_ghost_values();
1033 for (const auto &partition : partitions_mg)
1034 partition.update_ghost_values();
1035
1036 // 1) Determine process ids that appear on locally owned cells. Create
1037 // a sorted vector by first creating a std::set and then copying
1038 // the result. (Note that we get only locally *owned* cells in
1039 // the output because we only loop over the locally *owned*
1040 // entries of the partitioning vector, even though
1041 // 'partition.local_element(i)' could also return locally relevant
1042 // elements if 'i' were to exceed the number of locally owned
1043 // elements.)
1044 const std::vector<unsigned int> future_owners_of_locally_owned_cells =
1045 [&partition, &partitions_mg]() {
1046 std::set<unsigned int> relevant_process_set;
1047
1048 const unsigned int n_mpi_ranks =
1050 partition.get_mpi_communicator());
1051
1052 for (unsigned int i = 0; i < partition.locally_owned_size(); ++i)
1053 {
1054 Assert(static_cast<unsigned int>(partition.local_element(i)) ==
1055 partition.local_element(i),
1056 ExcMessage(
1057 "The elements of a partition vector must be integers."));
1058 Assert(
1059 partition.local_element(i) < n_mpi_ranks,
1060 ExcMessage(
1061 "The elements of a partition vector must be between zero "
1062 "and the number of processes in the communicator "
1063 "to be used for partitioning the triangulation."));
1064 relevant_process_set.insert(
1065 static_cast<unsigned int>(partition.local_element(i)));
1066 }
1067
1068 for (const auto &partition : partitions_mg)
1069 for (unsigned int i = 0; i < partition.locally_owned_size(); ++i)
1070 {
1071 Assert(
1072 static_cast<unsigned int>(partition.local_element(i)) ==
1073 partition.local_element(i),
1074 ExcMessage(
1075 "The elements of a partition vector must be integers."));
1076 Assert(
1077 partition.local_element(i) < n_mpi_ranks,
1078 ExcMessage(
1079 "The elements of a partition vector must be between zero "
1080 "and the number of processes in the communicator "
1081 "to be used for partitioning the triangulation."));
1082 relevant_process_set.insert(
1083 static_cast<unsigned int>(partition.local_element(i)));
1084 }
1085
1086 return std::vector<unsigned int>(relevant_process_set.begin(),
1087 relevant_process_set.end());
1088 }();
1089
1090 const bool construct_multigrid = (partitions_mg.size() > 0);
1091
1092 const TriangulationDescription::Settings settings =
1093 (construct_multigrid ?
1097 settings_in);
1098
1099
1100 // Set up a function that returns the future owner rank for a cell.
1101 // Same then also for the level owner. These functions work for
1102 // locally owned and ghost cells.
1103 const auto cell_to_future_owner =
1104 [&partition](const auto &cell) -> types::subdomain_id {
1105 if ((cell->is_active() && (cell->is_artificial() == false)))
1106 return static_cast<types::subdomain_id>(
1107 partition[cell->global_active_cell_index()]);
1108 else
1110 };
1111
1112 const auto mg_cell_to_future_owner =
1113 [&construct_multigrid,
1114 &partitions_mg](const auto &cell) -> types::subdomain_id {
1115 if (construct_multigrid && (cell->is_artificial_on_level() == false))
1116 return static_cast<types::subdomain_id>(
1117 partitions_mg[cell->level()][cell->global_level_cell_index()]);
1118 else
1120 };
1121
1122 // Create a description (locally owned cell and a layer of ghost cells
1123 // and all their parents). We first create a description in the
1124 // 'temporary' format (using class DescriptionTemp), which we will
1125 // later convert to its final form.
1126 std::vector<DescriptionTemp<dim, spacedim>> descriptions_per_rank;
1127 descriptions_per_rank.reserve(
1128 future_owners_of_locally_owned_cells.size());
1129
1130 std::map<unsigned int, std::vector<unsigned int>>
1131 coinciding_vertex_groups;
1132 std::map<unsigned int, unsigned int> vertex_to_coinciding_vertex_group;
1134 coinciding_vertex_groups,
1135 vertex_to_coinciding_vertex_group);
1136
1137 for (const auto rank : future_owners_of_locally_owned_cells)
1138 descriptions_per_rank.emplace_back(
1139 create_description_for_rank<DescriptionTemp<dim, spacedim>>(
1140 tria,
1141 cell_to_future_owner,
1142 mg_cell_to_future_owner,
1143 coinciding_vertex_groups,
1144 vertex_to_coinciding_vertex_group,
1145 tria.get_mpi_communicator(),
1146 rank,
1147 settings));
1148
1149 // Collect description from all processes that used to own locally-owned
1150 // active cells of this process in a single description
1151 DescriptionTemp<dim, spacedim> description_merged;
1152 description_merged.collect(
1153 future_owners_of_locally_owned_cells,
1154 descriptions_per_rank,
1155 partition.get_mpi_communicator(),
1156 dynamic_cast<
1158 &tria) == nullptr);
1159
1160 // remove redundant entries
1161 description_merged.reduce();
1162
1163 // convert to actual description
1164 return description_merged.convert(partition.get_mpi_communicator(),
1165 tria.get_mesh_smoothing(),
1166 settings);
1167 }
1168
1169 } // namespace Utilities
1170} // namespace TriangulationDescription
1171
1172
1173
1174/*-------------- Explicit Instantiations -------------------------------*/
1175#include "grid/tria_description.inst"
1176
1177
*  iterator end()
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  *  iterator begin()
Definition point.h:111
virtual const MeshSmoothing & get_mesh_smoothing() const
virtual MPI_Comm get_mpi_communicator() const
std::vector< Point< spacedim > > vertices
Definition tria.h:4609
virtual unsigned int n_global_levels() const
constexpr size_type size() const noexcept
#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
Point< 2 > second
Definition grid_out.cc:4640
unsigned int level
Definition grid_out.cc:4642
unsigned int vertex_indices[2]
IteratorRange< cell_iterator > cell_iterators_on_level(const unsigned int level) const
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
const unsigned int my_rank
Definition mpi.cc:917
const MPI_Comm comm
Definition mpi.cc:912
void partition_multigrid_levels(Triangulation< dim, spacedim > &triangulation)
void collect_coinciding_vertices(const Triangulation< dim, spacedim > &tria, std::map< unsigned int, std::vector< unsigned int > > &coinciding_vertex_groups, std::map< unsigned int, unsigned int > &vertex_to_coinciding_vertex_group)
Tensor< 2, dim, Number > l(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
Description< dim, spacedim > create_description_from_triangulation(const ::Triangulation< dim, spacedim > &tria, const MPI_Comm comm, const TriangulationDescription::Settings settings=TriangulationDescription::Settings::default_setting, const unsigned int my_rank_in=numbers::invalid_unsigned_int)
Description< dim, spacedim > create_description_from_triangulation_in_groups(const std::function< void(::Triangulation< dim, spacedim > &)> &serial_grid_generator, const std::function< void(::Triangulation< dim, spacedim > &, const MPI_Comm, const unsigned int)> &serial_grid_partitioner, const MPI_Comm comm, const int group_size=1, const typename Triangulation< dim, spacedim >::MeshSmoothing smoothing=::Triangulation< dim, spacedim >::none, const TriangulationDescription::Settings setting=TriangulationDescription::Settings::default_setting)
std::vector< unsigned int > selector(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
@ fully_distributed_create
TriangulationDescription::Utilities::create_description_from_triangulation()
Definition mpi_tags.h:97
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
std::size_t pack(const T &object, std::vector< char > &dest_buffer, const bool allow_compression=true)
Definition utilities.h:1352
constexpr TableIndices< 2 > merge(const TableIndices< 2 > &previous_indices, const unsigned int new_index, const unsigned int position)
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
constexpr types::boundary_id internal_face_boundary_id
Definition types.h:319
constexpr types::subdomain_id artificial_subdomain_id
Definition types.h:406
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
unsigned int manifold_id
Definition types.h:171
std::uint64_t global_vertex_index
Definition types.h:55
global_cell_index coarse_cell_id
Definition types.h:145
bool operator==(const CellData< structdim > &other) const
types::manifold_id manifold_id
Definition cell_data.h:125
std_cxx26::inplace_vector< unsigned int, ReferenceCells::max_n_vertices< structdim >()> vertices
Definition cell_data.h:84
types::material_id material_id
Definition cell_data.h:103
types::boundary_id boundary_id
Definition cell_data.h:114
CellData(const unsigned int n_vertices=ReferenceCells::get_hypercube< structdim >().n_vertices())
std::vector< CellData< 2 > > boundary_quads
Definition cell_data.h:247
bool check_consistency(const unsigned int dim) const
std::vector< CellData< 1 > > boundary_lines
Definition cell_data.h:231
std::vector< std::pair< unsigned int, Point< spacedim > > > coarse_cell_vertices
std::vector< types::coarse_cell_id > coarse_cell_index_to_coarse_cell_id
std::vector< std::vector< CellData< dim > > > cell_infos
std::vector<::CellData< dim > > coarse_cells