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_base.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) 2015 - 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
13
16#include <deal.II/base/mpi.templates.h>
19
22
25#include <deal.II/grid/tria.h>
28
30
31#include <algorithm>
32#include <cstdint>
33#include <fstream>
34#include <iostream>
35#include <limits>
36#include <numeric>
37
38
40
41namespace parallel
42{
43 template <int dim, int spacedim>
46 const MPI_Comm mpi_communicator,
47 const typename ::Triangulation<dim, spacedim>::MeshSmoothing
48 smooth_grid,
49 const bool check_for_distorted_cells)
50 : ::Triangulation<dim, spacedim>(smooth_grid,
51 check_for_distorted_cells)
52 , mpi_communicator(mpi_communicator)
53 , my_subdomain(Utilities::MPI::this_mpi_process(this->mpi_communicator))
54 , n_subdomains(Utilities::MPI::n_mpi_processes(this->mpi_communicator))
55 {
56#ifndef DEAL_II_WITH_MPI
57 Assert(false, ExcNeedsMPI());
58#endif
59 }
60
61
62
63 template <int dim, int spacedim>
65 void TriangulationBase<dim, spacedim>::copy_triangulation(
66 const ::Triangulation<dim, spacedim> &other_tria)
67 {
68#ifndef DEAL_II_WITH_MPI
69 (void)other_tria;
70 Assert(false, ExcNeedsMPI());
71#else
73
74 if (const ::parallel::TriangulationBase<dim, spacedim> *other_tria_x =
75 dynamic_cast<const ::parallel::TriangulationBase<dim, spacedim>
76 *>(&other_tria))
77 {
78 // release unused vector memory because we will have very different
79 // vectors now
82 }
83#endif
84 }
85
86
87
88 template <int dim, int spacedim>
90 std::size_t TriangulationBase<dim, spacedim>::memory_consumption() const
91 {
92 std::size_t mem =
94 MemoryConsumption::memory_consumption(this->mpi_communicator) +
97 number_cache.n_global_active_cells) +
98 MemoryConsumption::memory_consumption(number_cache.n_global_levels);
99 return mem;
100 }
101
102
103
104 template <int dim, int spacedim>
107 {
108 // release unused vector memory because the vector layout is going to look
109 // very different now
112 }
113
114
116 template <int dim, int spacedim>
119 : n_locally_owned_active_cells(0)
120 , n_global_active_cells(0)
121 , number_of_global_coarse_cells(0)
122 , n_global_levels(0)
123 {}
124
125
126
127 template <int dim, int spacedim>
129 unsigned int TriangulationBase<dim, spacedim>::n_locally_owned_active_cells()
130 const
131 {
132 return number_cache.n_locally_owned_active_cells;
133 }
134
135
136
137 template <int dim, int spacedim>
139 unsigned int TriangulationBase<dim, spacedim>::n_global_levels() const
140 {
141 return number_cache.n_global_levels;
142 }
143
144
145
146 template <int dim, int spacedim>
150 {
151 return number_cache.n_global_active_cells;
152 }
153
154
155
156 template <int dim, int spacedim>
158 MPI_Comm TriangulationBase<dim, spacedim>::get_mpi_communicator() const
159 {
160 return mpi_communicator;
161 }
162
163
164
165#ifdef DEAL_II_WITH_MPI
166 template <int dim, int spacedim>
168 void TriangulationBase<dim, spacedim>::update_number_cache()
169 {
170 number_cache.ghost_owners.clear();
171 number_cache.level_ghost_owners.clear();
172 number_cache.n_locally_owned_active_cells = 0;
173
174 if (this->n_levels() == 0)
175 {
176 // Skip communication done below if we do not have any cells
177 // (meaning the Triangulation is empty on all processors). This will
178 // happen when called from the destructor of Triangulation, which
179 // can get called during exception handling causing a hang in this
180 // function.
181 number_cache.n_global_active_cells = 0;
182 number_cache.n_global_levels = 0;
183 return;
184 }
185
186
187 {
188 // find ghost owners
189 for (const auto &cell : this->active_cell_iterators())
190 if (cell->is_ghost())
191 number_cache.ghost_owners.insert(cell->subdomain_id());
192
193 Assert(number_cache.ghost_owners.size() <
194 Utilities::MPI::n_mpi_processes(this->mpi_communicator),
196 }
197
198 if (this->n_levels() > 0)
199 number_cache.n_locally_owned_active_cells = std::count_if(
200 this->begin_active(),
202 this->end()),
203 [](const auto &i) { return i.is_locally_owned(); });
204 else
205 number_cache.n_locally_owned_active_cells = 0;
206
207 // Potentially cast to a 64 bit type before accumulating to avoid
208 // overflow:
209 number_cache.n_global_active_cells =
211 number_cache.n_locally_owned_active_cells),
212 this->mpi_communicator);
213
214 number_cache.n_global_levels =
215 Utilities::MPI::max(this->n_levels(), this->mpi_communicator);
216
217 // Store MPI ranks of level ghost owners of this processor on all
218 // levels.
219 if (this->is_multilevel_hierarchy_constructed() == true)
220 {
221 number_cache.level_ghost_owners.clear();
222
223 // if there is nothing to do, then do nothing
224 if (this->n_levels() == 0)
225 return;
226
227 // find level ghost owners
228 for (const auto &cell : this->cell_iterators())
229 if (cell->level_subdomain_id() != numbers::artificial_subdomain_id &&
230 cell->level_subdomain_id() != this->locally_owned_subdomain())
231 this->number_cache.level_ghost_owners.insert(
232 cell->level_subdomain_id());
233
234 if constexpr (running_in_debug_mode())
235 {
236 // Check that level_ghost_owners is symmetric by sending a message
237 // to everyone
238 {
239 int ierr = MPI_Barrier(this->mpi_communicator);
240 AssertThrowMPI(ierr);
241
242 const int mpi_tag = Utilities::MPI::internal::Tags::
245 // important: preallocate to avoid (re)allocation:
246 std::vector<MPI_Request> requests(
247 this->number_cache.level_ghost_owners.size());
248 unsigned int dummy = 0;
249 unsigned int req_counter = 0;
250
251 for (const auto &it : this->number_cache.level_ghost_owners)
252 {
253 ierr = MPI_Isend(&dummy,
254 1,
255 MPI_UNSIGNED,
256 it,
257 mpi_tag,
258 this->mpi_communicator,
259 &requests[req_counter]);
260 AssertThrowMPI(ierr);
261 ++req_counter;
263
264 for (const auto &it : this->number_cache.level_ghost_owners)
265 {
266 unsigned int dummy;
267 ierr = MPI_Recv(&dummy,
268 1,
269 MPI_UNSIGNED,
270 it,
271 mpi_tag,
272 this->mpi_communicator,
273 MPI_STATUS_IGNORE);
274 AssertThrowMPI(ierr);
275 }
276
277 if (requests.size() > 0)
278 {
279 ierr = MPI_Waitall(requests.size(),
280 requests.data(),
281 MPI_STATUSES_IGNORE);
282 AssertThrowMPI(ierr);
283 }
284
285 ierr = MPI_Barrier(this->mpi_communicator);
286 AssertThrowMPI(ierr);
287 }
288 }
289
290 Assert(this->number_cache.level_ghost_owners.size() <
291 Utilities::MPI::n_mpi_processes(this->mpi_communicator),
293 }
294
295 this->number_cache.number_of_global_coarse_cells = this->n_cells(0);
296
297 // reset global cell ids
298 this->reset_global_cell_indices();
299 }
300
301#else
302
303 template <int dim, int spacedim>
306 {
307 Assert(false, ExcNeedsMPI());
308 }
309
310#endif
311
312 template <int dim, int spacedim>
316 {
317 return my_subdomain;
318 }
319
320
322 template <int dim, int spacedim>
324 const std::set<types::subdomain_id>
326 {
327 return number_cache.ghost_owners;
328 }
329
331
332 template <int dim, int spacedim>
334 const std::set<types::subdomain_id>
336 {
337 return number_cache.level_ghost_owners;
338 }
339
340
341
342 template <int dim, int spacedim>
344 std::vector<types::boundary_id> TriangulationBase<dim, spacedim>::
345 get_boundary_ids() const
346 {
349 this->mpi_communicator);
350 }
351
352
353
354 template <int dim, int spacedim>
356 std::vector<types::manifold_id> TriangulationBase<dim, spacedim>::
357 get_manifold_ids() const
358 {
361 this->mpi_communicator);
362 }
363
364
365
366 template <int dim, int spacedim>
368 void TriangulationBase<dim, spacedim>::reset_global_cell_indices()
369 {
370#ifndef DEAL_II_WITH_MPI
371 Assert(false, ExcNeedsMPI());
372#else
373 if (const auto pst =
375 this))
376 if (pst->with_artificial_cells() == false)
377 {
378 // Specialization for parallel::shared::Triangulation without
379 // artificial cells. The code below only works if a halo of a single
380 // ghost cells is needed.
381
382 std::vector<unsigned int> cell_counter(n_subdomains + 1);
383
384 // count number of cells of each process
385 for (const auto &cell : this->active_cell_iterators())
386 cell_counter[cell->subdomain_id() + 1]++;
387
388 // take prefix sum to obtain offset of each process
389 for (unsigned int i = 0; i < n_subdomains; ++i)
390 cell_counter[i + 1] += cell_counter[i];
391
392 AssertDimension(cell_counter.back(), this->n_active_cells());
393
394 // create partitioners
395 IndexSet is_local(this->n_active_cells());
396 is_local.add_range(cell_counter[my_subdomain],
397 cell_counter[my_subdomain + 1]);
398 number_cache.active_cell_index_partitioner =
399 std::make_shared<const Utilities::MPI::Partitioner>(
400 is_local,
401 complete_index_set(this->n_active_cells()),
402 this->mpi_communicator);
403
404 // set global active cell indices and increment process-local counters
405 for (const auto &cell : this->active_cell_iterators())
406 cell->set_global_active_cell_index(
407 cell_counter[cell->subdomain_id()]++);
408
409 Assert(this->is_multilevel_hierarchy_constructed() == false,
411
412 return;
414
415 // 1) determine number of active locally-owned cells
416 const types::global_cell_index n_locally_owned_cells =
417 this->n_locally_owned_active_cells();
418
419 // 2) determine the offset of each process
421
422 const int ierr = MPI_Exscan(
423 &n_locally_owned_cells,
424 &cell_index,
425 1,
426 Utilities::MPI::mpi_type_id_for_type<decltype(n_locally_owned_cells)>,
427 MPI_SUM,
428 this->mpi_communicator);
429 AssertThrowMPI(ierr);
430
431 // 3) give global indices to locally-owned cells and mark all other cells as
432 // invalid
433 std::pair<types::global_cell_index, types::global_cell_index> my_range;
434 my_range.first = cell_index;
435
436 for (const auto &cell : this->active_cell_iterators())
437 if (cell->is_locally_owned())
438 cell->set_global_active_cell_index(cell_index++);
439 else
440 cell->set_global_active_cell_index(numbers::invalid_dof_index);
441
442 my_range.second = cell_index;
443
444 // 4) determine the global indices of ghost cells
445 std::vector<types::global_dof_index> is_ghost_vector;
446 GridTools::exchange_cell_data_to_ghosts<types::global_cell_index>(
447 static_cast<::Triangulation<dim, spacedim> &>(*this),
448 [](const auto &cell) { return cell->global_active_cell_index(); },
449 [&is_ghost_vector](const auto &cell, const auto &id) {
450 cell->set_global_active_cell_index(id);
451 is_ghost_vector.push_back(id);
452 });
453
454 // 5) set up new partitioner
455 IndexSet is_local(this->n_global_active_cells());
456 is_local.add_range(my_range.first, my_range.second);
457
458 std::sort(is_ghost_vector.begin(), is_ghost_vector.end());
459 IndexSet is_ghost(this->n_global_active_cells());
460 is_ghost.add_indices(is_ghost_vector.begin(), is_ghost_vector.end());
461
462 number_cache.active_cell_index_partitioner =
463 std::make_shared<const Utilities::MPI::Partitioner>(
464 is_local, is_ghost, this->mpi_communicator);
465
466 // 6) proceed with multigrid levels if requested
467 if (this->is_multilevel_hierarchy_constructed() == true)
468 {
469 // 1) determine number of locally-owned cells on levels
470 std::vector<types::global_cell_index> n_cells_level(
471 this->n_global_levels(), 0);
472
473 for (auto cell : this->cell_iterators())
474 if (cell->level_subdomain_id() == this->locally_owned_subdomain())
475 n_cells_level[cell->level()]++;
476
477 // 2) determine the offset of each process
478 std::vector<types::global_cell_index> cell_index(
479 this->n_global_levels(), 0);
480
481 int ierr = MPI_Exscan(
482 n_cells_level.data(),
483 cell_index.data(),
484 this->n_global_levels(),
485 Utilities::MPI::mpi_type_id_for_type<decltype(*n_cells_level.data())>,
486 MPI_SUM,
487 this->mpi_communicator);
488 AssertThrowMPI(ierr);
489
490 // 3) determine global number of "active" cells on each level
491 Utilities::MPI::sum(n_cells_level,
492 this->mpi_communicator,
493 n_cells_level);
494
495 // 4) give global indices to locally-owned cells on level and mark
496 // all other cells as invalid
497 std::vector<
498 std::pair<types::global_cell_index, types::global_cell_index>>
499 my_ranges(this->n_global_levels());
500 for (unsigned int l = 0; l < this->n_global_levels(); ++l)
501 my_ranges[l].first = cell_index[l];
502
503 for (auto cell : this->cell_iterators())
504 if (cell->level_subdomain_id() == this->locally_owned_subdomain())
505 cell->set_global_level_cell_index(cell_index[cell->level()]++);
506 else
507 cell->set_global_level_cell_index(numbers::invalid_dof_index);
508
509 for (unsigned int l = 0; l < this->n_global_levels(); ++l)
510 my_ranges[l].second = cell_index[l];
511
512 // 5) update the numbers of ghost level cells
513 std::vector<std::vector<types::global_dof_index>> is_ghost_vectors(
514 this->n_global_levels());
518 *this,
519 [](const auto &cell) { return cell->global_level_cell_index(); },
520 [&is_ghost_vectors](const auto &cell, const auto &id) {
521 cell->set_global_level_cell_index(id);
522 is_ghost_vectors[cell->level()].push_back(id);
523 });
524
525 number_cache.level_cell_index_partitioners.resize(
526 this->n_global_levels());
527
528 // 6) set up cell partitioners for each level
529 for (unsigned int l = 0; l < this->n_global_levels(); ++l)
530 {
531 IndexSet is_local(n_cells_level[l]);
532 is_local.add_range(my_ranges[l].first, my_ranges[l].second);
533
534 IndexSet is_ghost(n_cells_level[l]);
535 std::sort(is_ghost_vectors[l].begin(), is_ghost_vectors[l].end());
536 is_ghost.add_indices(is_ghost_vectors[l].begin(),
537 is_ghost_vectors[l].end());
538
539 number_cache.level_cell_index_partitioners[l] =
540 std::make_shared<const Utilities::MPI::Partitioner>(
541 is_local, is_ghost, this->mpi_communicator);
542 }
543 }
544
545#endif
546 }
547
548
549
550 template <int dim, int spacedim>
552 void TriangulationBase<dim, spacedim>::communicate_locally_moved_vertices(
553 const std::vector<bool> &vertex_locally_moved)
554 {
555 AssertDimension(vertex_locally_moved.size(), this->n_vertices());
556 if constexpr (running_in_debug_mode())
557 {
558 {
559 const std::vector<bool> locally_owned_vertices =
561 for (unsigned int i = 0; i < locally_owned_vertices.size(); ++i)
562 Assert((vertex_locally_moved[i] == false) ||
563 (locally_owned_vertices[i] == true),
564 ExcMessage("The vertex_locally_moved argument must not "
565 "contain vertices that are not locally owned"));
566 }
567 }
568
569 Point<spacedim> invalid_point;
570 for (unsigned int d = 0; d < spacedim; ++d)
571 invalid_point[d] = std::numeric_limits<double>::quiet_NaN();
572
573 const auto pack = [&](const auto &cell) {
574 std::vector<Point<spacedim>> vertices(cell->n_vertices());
575
576 for (const auto v : cell->vertex_indices())
577 if (vertex_locally_moved[cell->vertex_index(v)])
578 vertices[v] = cell->vertex(v);
579 else
580 vertices[v] = invalid_point;
581
582 return vertices;
583 };
584
585 const auto unpack = [&](const auto &cell, const auto &vertices) {
586 for (const auto v : cell->vertex_indices())
587 if (numbers::is_nan(vertices[v][0]) == false)
588 cell->vertex(v) = vertices[v];
589 };
590
591 if (this->is_multilevel_hierarchy_constructed())
593 std::vector<Point<spacedim>>>(
594 static_cast<::Triangulation<dim, spacedim> &>(*this),
595 pack,
596 unpack);
597 else
598 GridTools::exchange_cell_data_to_ghosts<std::vector<Point<spacedim>>>(
599 static_cast<::Triangulation<dim, spacedim> &>(*this),
600 pack,
601 unpack);
602 }
603
604
605
606 template <int dim, int spacedim>
608 std::weak_ptr<const Utilities::MPI::Partitioner> TriangulationBase<
609 dim,
610 spacedim>::global_active_cell_index_partitioner() const
611 {
612 return number_cache.active_cell_index_partitioner;
613 }
614
615
616
617 template <int dim, int spacedim>
619 std::weak_ptr<const Utilities::MPI::Partitioner> TriangulationBase<dim,
620 spacedim>::
621 global_level_cell_index_partitioner(const unsigned int level) const
622 {
623 Assert(this->is_multilevel_hierarchy_constructed(), ExcNotImplemented());
624 AssertIndexRange(level, this->n_global_levels());
625
626 return number_cache.level_cell_index_partitioners[level];
627 }
628
629
630
631 template <int dim, int spacedim>
635 {
636 return number_cache.number_of_global_coarse_cells;
637 }
638
639
640
641 template <int dim, int spacedim>
644 const MPI_Comm mpi_communicator,
645 const typename ::Triangulation<dim, spacedim>::MeshSmoothing
646 smooth_grid,
647 const bool check_for_distorted_cells)
648 : ::parallel::TriangulationBase<dim, spacedim>(
649 mpi_communicator,
650 smooth_grid,
651 check_for_distorted_cells)
652 {}
653
654
655
656 template <int dim, int spacedim>
658 void TriangulationBase<dim, spacedim>::clear()
659 {
661
662 number_cache = {};
663 }
664
665
666
667 template <int dim, int spacedim>
669 bool DistributedTriangulationBase<dim, spacedim>::has_hanging_nodes() const
670 {
671 if (this->n_global_levels() <= 1)
672 return false; // can not have hanging nodes without refined cells
673
674 // if there are any active cells with level less than n_global_levels()-1,
675 // then there is obviously also one with level n_global_levels()-1, and
676 // consequently there must be a hanging node somewhere.
677 //
678 // The problem is that we cannot just ask for the first active cell, but
679 // instead need to filter over locally owned cells.
680 const bool have_coarser_cell =
681 std::any_of(this->begin_active(this->n_global_levels() - 2),
682 this->end_active(this->n_global_levels() - 2),
683 [](const CellAccessor<dim, spacedim> &cell) {
684 return cell.is_locally_owned();
685 });
686
687 // return true if at least one process has a coarser cell
688 return Utilities::MPI::logical_or(have_coarser_cell,
689 this->mpi_communicator);
690 }
691
692
693} // end namespace parallel
694
695
696
697/*-------------- Explicit Instantiations -------------------------------*/
698#include "distributed/tria_base.inst"
699
*  iterator end()
*  *  iterator begin()
bool is_locally_owned() const
void add_range(const size_type begin, const size_type end)
Definition index_set.h:1786
Definition point.h:111
virtual void clear()
virtual void copy_triangulation(const Triangulation< dim, spacedim > &other_tria)
virtual std::size_t memory_consumption() const
const std::set< types::subdomain_id > & level_ghost_owners() const
Definition tria_base.cc:335
virtual types::global_cell_index n_global_active_cells() const override
Definition tria_base.cc:149
const std::set< types::subdomain_id > & ghost_owners() const
Definition tria_base.cc:325
types::subdomain_id locally_owned_subdomain() const override
Definition tria_base.cc:315
virtual unsigned int n_global_levels() const override
Definition tria_base.cc:139
unsigned int n_locally_owned_active_cells() const
Definition tria_base.cc:129
virtual types::coarse_cell_id n_global_coarse_cells() const override
Definition tria_base.cc:634
virtual void update_number_cache()
Definition tria_base.cc:168
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_CXX20_REQUIRES(condition)
Definition config.h:249
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
unsigned int level
Definition grid_out.cc:4642
unsigned int cell_index
static ::ExceptionBase & ExcNotImplemented()
static ::ExceptionBase & ExcNeedsMPI()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
IndexSet complete_index_set(const IndexSet::size_type N)
Definition index_set.h:1187
std::vector< bool > get_locally_owned_vertices(const Triangulation< dim, spacedim > &triangulation)
void exchange_cell_data_to_level_ghosts(const MeshType &mesh, const std::function< std::optional< DataType >(const typename MeshType::level_cell_iterator &)> &pack, const std::function< void(const typename MeshType::level_cell_iterator &, const DataType &)> &unpack, const std::function< bool(const typename MeshType::level_cell_iterator &)> &cell_filter=always_return< typename MeshType::level_cell_iterator, bool >{ true})
std::enable_if_t< std::is_fundamental_v< T >, std::size_t > memory_consumption(const T &t)
@ triangulation_base_fill_level_ghost_owners
TriangulationBase<dim, spacedim>::fill_level_ghost_owners()
Definition mpi_tags.h:100
T sum(const T &t, const MPI_Comm mpi_communicator)
T logical_or(const T &t, const MPI_Comm mpi_communicator)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
T max(const T &t, const MPI_Comm mpi_communicator)
std::vector< T > compute_set_union(const std::vector< T > &vec, const MPI_Comm comm)
const MPI_Datatype mpi_type_id_for_type
Definition mpi.h:1685
unsigned int n_cells(const internal::TriangulationImplementation::NumberCache< 1 > &c)
Definition tria.cc:15808
constexpr types::global_dof_index invalid_dof_index
Definition types.h:259
bool is_nan(const double x)
Definition numbers.h:501
STL namespace.
Definition types.h:30
unsigned int global_cell_index
Definition types.h:136