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
face_setup_internal.h
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) 2018 - 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
14#ifndef dealii_face_setup_internal_h
15#define dealii_face_setup_internal_h
16
17#include <deal.II/base/config.h>
18
20
22
23#include <deal.II/grid/tria.h>
25
29
30#include <fstream>
31#include <set>
32
33
35
36
37namespace internal
38{
39 namespace MatrixFreeFunctions
40 {
56
57
58
67 template <int dim>
68 struct FaceSetup
69 {
71
78 void
80 const ::Triangulation<dim> &triangulation,
81 const unsigned int mg_level,
82 const bool hold_all_faces_to_owned_cells,
83 const bool build_inner_faces,
84 std::vector<std::pair<unsigned int, unsigned int>> &cell_levels);
85
92 void
94 const ::Triangulation<dim> &triangulation,
95 const std::vector<std::pair<unsigned int, unsigned int>> &cell_levels,
96 TaskInfo &task_info);
97
106 const unsigned int face_no,
107 const typename ::Triangulation<dim>::cell_iterator &cell,
108 const unsigned int number_cell_interior,
109 const typename ::Triangulation<dim>::cell_iterator &neighbor,
110 const unsigned int number_cell_exterior,
111 const bool is_mixed_mesh);
112
114
127
128 std::vector<FaceCategory> face_is_owned;
129 std::vector<bool> at_processor_boundary;
130 std::vector<FaceToCellTopology<1>> inner_faces;
131 std::vector<FaceToCellTopology<1>> boundary_faces;
132 std::vector<FaceToCellTopology<1>> inner_ghost_faces;
133 std::vector<FaceToCellTopology<1>> refinement_edge_faces;
134 };
135
136
137
141 template <int vectorization_width>
142 void
144 const std::vector<FaceToCellTopology<1>> &faces_in,
145 const std::vector<bool> &hard_vectorization_boundary,
146 std::vector<unsigned int> &face_partition_data,
147 std::vector<FaceToCellTopology<vectorization_width>> &faces_out);
148
149
150
151 /* -------------------------------------------------------------------- */
152
153#ifndef DOXYGEN
154
155 template <int dim>
157 : use_active_cells(true)
158 {}
159
160
161
162 template <int dim>
163 void
164 FaceSetup<dim>::initialize(
165 const ::Triangulation<dim> &triangulation,
166 const unsigned int mg_level,
167 const bool hold_all_faces_to_owned_cells,
168 const bool build_inner_faces,
169 std::vector<std::pair<unsigned int, unsigned int>> &cell_levels)
170 {
171 use_active_cells = mg_level == numbers::invalid_unsigned_int;
172
173 if constexpr (running_in_debug_mode())
174 {
175 // safety check
176 if (use_active_cells)
177 for (const auto &cell_level : cell_levels)
178 {
179 typename ::Triangulation<dim>::cell_iterator dcell(
180 &triangulation, cell_level.first, cell_level.second);
181 Assert(dcell->is_active(), ExcInternalError());
182 }
183 }
184
185 // step 1: add ghost cells for those cells that we identify as
186 // interesting
187
188 at_processor_boundary.resize(cell_levels.size(), false);
189 face_is_owned.resize(dim > 1 ? triangulation.n_raw_faces() :
190 triangulation.n_vertices(),
191 FaceCategory::locally_active_done_elsewhere);
192
193 // go through the mesh and divide the faces on the processor
194 // boundaries as evenly as possible between the processors
195 std::map<types::subdomain_id, FaceIdentifier>
196 inner_faces_at_proc_boundary;
197 if (triangulation.locally_owned_subdomain() !=
199 {
200 const types::subdomain_id my_domain =
201 triangulation.locally_owned_subdomain();
202 for (unsigned int i = 0; i < cell_levels.size(); ++i)
203 {
204 if (i > 0 && cell_levels[i] == cell_levels[i - 1])
205 continue;
206 typename ::Triangulation<dim>::cell_iterator dcell(
207 &triangulation, cell_levels[i].first, cell_levels[i].second);
208 for (const unsigned int f : dcell->face_indices())
209 {
210 if (dcell->at_boundary(f) && !dcell->has_periodic_neighbor(f))
211 continue;
212 typename ::Triangulation<dim>::cell_iterator neighbor =
213 dcell->neighbor_or_periodic_neighbor(f);
214
215 // faces at hanging nodes are always treated by the processor
216 // who owns the element on the fine side. but we need to count
217 // the number of inner faces in order to balance the remaining
218 // faces properly
219 const CellId id_mine = dcell->id();
220 if (use_active_cells && neighbor->has_children())
221 for (unsigned int c = 0;
222 c < (dcell->has_periodic_neighbor(f) ?
223 dcell->periodic_neighbor(f)
224 ->face(dcell->periodic_neighbor_face_no(f))
225 ->n_children() :
226 dcell->face(f)->n_children());
227 ++c)
228 {
229 typename ::Triangulation<dim>::cell_iterator
230 neighbor_c =
231 dcell->at_boundary(f) ?
232 dcell->periodic_neighbor_child_on_subface(f, c) :
233 dcell->neighbor_child_on_subface(f, c);
234 const types::subdomain_id neigh_domain =
235 neighbor_c->subdomain_id();
236 if (my_domain < neigh_domain)
237 inner_faces_at_proc_boundary[neigh_domain]
238 .n_hanging_faces_larger_subdomain++;
239 else if (my_domain > neigh_domain)
240 inner_faces_at_proc_boundary[neigh_domain]
241 .n_hanging_faces_smaller_subdomain++;
242 }
243 else
244 {
245 const types::subdomain_id neigh_domain =
246 use_active_cells ? neighbor->subdomain_id() :
247 neighbor->level_subdomain_id();
248 if (neighbor->level() < dcell->level() &&
249 use_active_cells)
250 {
251 if (my_domain < neigh_domain)
252 inner_faces_at_proc_boundary[neigh_domain]
253 .n_hanging_faces_smaller_subdomain++;
254 else if (my_domain > neigh_domain)
255 inner_faces_at_proc_boundary[neigh_domain]
256 .n_hanging_faces_larger_subdomain++;
257 }
258 else if (neighbor->level() == dcell->level() &&
259 my_domain != neigh_domain)
260 {
261 // always list the cell whose owner has the lower
262 // subdomain id first. this applies to both processors
263 // involved, so both processors will generate the same
264 // list that we will later order
265 const CellId id_neigh = neighbor->id();
266 if (my_domain < neigh_domain)
267 inner_faces_at_proc_boundary[neigh_domain]
268 .shared_faces.emplace_back(id_mine, id_neigh);
269 else
270 inner_faces_at_proc_boundary[neigh_domain]
271 .shared_faces.emplace_back(id_neigh, id_mine);
272 }
273 }
274 }
275 }
276
277 // sort the cell ids related to each neighboring processor. This
278 // algorithm is symmetric so every processor combination should
279 // arrive here and no deadlock should be possible
280 for (auto &inner_face : inner_faces_at_proc_boundary)
281 {
282 Assert(inner_face.first != my_domain,
283 ExcInternalError("Should not send info to myself"));
284 std::sort(inner_face.second.shared_faces.begin(),
285 inner_face.second.shared_faces.end());
286 inner_face.second.shared_faces.erase(
287 std::unique(inner_face.second.shared_faces.begin(),
288 inner_face.second.shared_faces.end()),
289 inner_face.second.shared_faces.end());
290
291 // safety check: both involved processors should see the same list
292 // because the pattern of ghosting is symmetric. We test this by
293 // looking at the length of the lists of faces
294# if defined(DEAL_II_WITH_MPI) && defined(DEBUG)
295 MPI_Comm comm = MPI_COMM_SELF;
296 if (const ::parallel::TriangulationBase<dim> *ptria =
297 dynamic_cast<const ::parallel::TriangulationBase<dim>
298 *>(&triangulation))
299 comm = ptria->get_mpi_communicator();
300
301 MPI_Status status;
302 unsigned int mysize = inner_face.second.shared_faces.size();
303 unsigned int othersize = numbers::invalid_unsigned_int;
304
305 int ierr = MPI_Sendrecv(&mysize,
306 1,
307 MPI_UNSIGNED,
308 inner_face.first,
309 600 + my_domain,
310 &othersize,
311 1,
312 MPI_UNSIGNED,
313 inner_face.first,
314 600 + inner_face.first,
315 comm,
316 &status);
317 AssertThrowMPI(ierr);
318 AssertDimension(mysize, othersize);
319 mysize = inner_face.second.n_hanging_faces_smaller_subdomain;
320 ierr = MPI_Sendrecv(&mysize,
321 1,
322 MPI_UNSIGNED,
323 inner_face.first,
324 700 + my_domain,
325 &othersize,
326 1,
327 MPI_UNSIGNED,
328 inner_face.first,
329 700 + inner_face.first,
330 comm,
331 &status);
332 AssertThrowMPI(ierr);
333 AssertDimension(mysize, othersize);
334 mysize = inner_face.second.n_hanging_faces_larger_subdomain;
335 ierr = MPI_Sendrecv(&mysize,
336 1,
337 MPI_UNSIGNED,
338 inner_face.first,
339 800 + my_domain,
340 &othersize,
341 1,
342 MPI_UNSIGNED,
343 inner_face.first,
344 800 + inner_face.first,
345 comm,
346 &status);
347 AssertThrowMPI(ierr);
348 AssertDimension(mysize, othersize);
349# endif
350
351 // Arrange the face "ownership" such that cells that are access
352 // by more than one face (think of a cell in a corner) get
353 // ghosted. This arrangement has the advantage that we need to
354 // send less data because the same data is used twice. The
355 // strategy applied here is to ensure the same order of face
356 // pairs on both processors that share some faces, and make the
357 // same decision on both sides.
358
359 // Create a vector with cell ids sorted over the processor with
360 // the larger rank. In the code below we need to be able to
361 // identify the same cell once for the processor with higher
362 // rank and once for the processor with the lower rank. The
363 // format for the processor with the higher rank is already
364 // contained in `shared_faces`, whereas we need a copy that we
365 // sort differently for the other way around.
366 std::vector<std::tuple<CellId, CellId, unsigned int>> other_range(
367 inner_face.second.shared_faces.size());
368 for (unsigned int i = 0; i < other_range.size(); ++i)
369 other_range[i] =
370 std::make_tuple(inner_face.second.shared_faces[i].second,
371 inner_face.second.shared_faces[i].first,
372 i);
373 std::sort(other_range.begin(), other_range.end());
374
375 // the vector 'assignment' sets whether a particular cell
376 // appears more often and acts as a pre-selection of the rank. A
377 // value of 1 means that the process with the higher rank gets
378 // those faces, a value -1 means that the process with the lower
379 // rank gets it, whereas a value 0 means that the decision can
380 // be made in an arbitrary way.
381 unsigned int n_faces_lower_proc = 0, n_faces_higher_proc = 0;
382 std::vector<signed char> assignment(other_range.size(), 0);
383 if (inner_face.second.shared_faces.size() > 0)
384 {
385 // identify faces that go to the processor with the higher
386 // rank
387 unsigned int count = 0;
388 for (unsigned int i = 1;
389 i < inner_face.second.shared_faces.size();
390 ++i)
391 if (inner_face.second.shared_faces[i].first ==
392 inner_face.second.shared_faces[i - 1 - count].first)
393 ++count;
394 else
395 {
396 AssertThrow(count < 2 * dim, ExcInternalError());
397 if (count > 0)
398 {
399 for (unsigned int k = 0; k <= count; ++k)
400 assignment[i - 1 - k] = 1;
401 n_faces_higher_proc += count + 1;
402 }
403 count = 0;
404 }
405
406 // identify faces that definitely go to the processor with
407 // the lower rank - this must use the sorting of CellId
408 // variables from the processor with the higher rank, i.e.,
409 // other_range rather than `shared_faces`.
410 count = 0;
411 for (unsigned int i = 1; i < other_range.size(); ++i)
412 if (std::get<0>(other_range[i]) ==
413 std::get<0>(other_range[i - 1 - count]))
414 ++count;
415 else
416 {
417 AssertThrow(count < 2 * dim, ExcInternalError());
418 if (count > 0)
419 {
420 for (unsigned int k = 0; k <= count; ++k)
421 {
422 Assert(inner_face.second
423 .shared_faces[std::get<2>(
424 other_range[i - 1])]
425 .second ==
426 inner_face.second
427 .shared_faces[std::get<2>(
428 other_range[i - 1 - k])]
429 .second,
431 // only assign to -1 if higher rank was not
432 // yet set
433 if (assignment[std::get<2>(
434 other_range[i - 1 - k])] == 0)
435 {
436 assignment[std::get<2>(
437 other_range[i - 1 - k])] = -1;
438 ++n_faces_lower_proc;
439 }
440 }
441 }
442 count = 0;
443 }
444 }
445
446
447 // divide the faces evenly between the two processors. the
448 // processor with small rank takes the first half, the processor
449 // with larger rank the second half. Adjust for the hanging
450 // faces that always get assigned to one side, and the faces we
451 // have already assigned due to the criterion above
452 n_faces_lower_proc +=
453 inner_face.second.n_hanging_faces_smaller_subdomain;
454 n_faces_higher_proc +=
455 inner_face.second.n_hanging_faces_larger_subdomain;
456 const unsigned int n_total_faces_at_proc_boundary =
457 (inner_face.second.shared_faces.size() +
458 inner_face.second.n_hanging_faces_smaller_subdomain +
459 inner_face.second.n_hanging_faces_larger_subdomain);
460 unsigned int split_index = n_total_faces_at_proc_boundary / 2;
461 if (split_index < n_faces_lower_proc)
462 split_index = 0;
463 else if (split_index <
464 n_total_faces_at_proc_boundary - n_faces_higher_proc)
465 split_index -= n_faces_lower_proc;
466 else
467 split_index = n_total_faces_at_proc_boundary -
468 n_faces_higher_proc - n_faces_lower_proc;
469
470 // make sure the splitting is consistent between both sides
471# if defined(DEAL_II_WITH_MPI) && defined(DEBUG)
472 ierr = MPI_Sendrecv(&split_index,
473 1,
474 MPI_UNSIGNED,
475 inner_face.first,
476 900 + my_domain,
477 &othersize,
478 1,
479 MPI_UNSIGNED,
480 inner_face.first,
481 900 + inner_face.first,
482 comm,
483 &status);
484 AssertThrowMPI(ierr);
485 AssertDimension(split_index, othersize);
486 ierr = MPI_Sendrecv(&n_faces_lower_proc,
487 1,
488 MPI_UNSIGNED,
489 inner_face.first,
490 1000 + my_domain,
491 &othersize,
492 1,
493 MPI_UNSIGNED,
494 inner_face.first,
495 1000 + inner_face.first,
496 comm,
497 &status);
498 AssertThrowMPI(ierr);
499 AssertDimension(n_faces_lower_proc, othersize);
500 ierr = MPI_Sendrecv(&n_faces_higher_proc,
501 1,
502 MPI_UNSIGNED,
503 inner_face.first,
504 1100 + my_domain,
505 &othersize,
506 1,
507 MPI_UNSIGNED,
508 inner_face.first,
509 1100 + inner_face.first,
510 comm,
511 &status);
512 AssertThrowMPI(ierr);
513 AssertDimension(n_faces_higher_proc, othersize);
514# endif
515
516 // collect the faces on both sides
517 std::vector<std::pair<CellId, CellId>> owned_faces_lower,
518 owned_faces_higher;
519 for (unsigned int i = 0; i < assignment.size(); ++i)
520 if (assignment[i] < 0)
521 owned_faces_lower.push_back(
522 inner_face.second.shared_faces[i]);
523 else if (assignment[i] > 0)
524 owned_faces_higher.push_back(
525 inner_face.second.shared_faces[i]);
526 AssertIndexRange(split_index,
527 inner_face.second.shared_faces.size() + 1 -
528 owned_faces_lower.size() -
529 owned_faces_higher.size());
530
531 unsigned int i = 0, c = 0;
532 for (; i < assignment.size() && c < split_index; ++i)
533 if (assignment[i] == 0)
534 {
535 owned_faces_lower.push_back(
536 inner_face.second.shared_faces[i]);
537 ++c;
538 }
539 for (; i < assignment.size(); ++i)
540 if (assignment[i] == 0)
541 {
542 owned_faces_higher.push_back(
543 inner_face.second.shared_faces[i]);
544 }
545
546 if constexpr (running_in_debug_mode())
547 {
548 // check consistency of faces on both sides
549 std::vector<std::pair<CellId, CellId>> check_faces;
550 check_faces.insert(check_faces.end(),
551 owned_faces_lower.begin(),
552 owned_faces_lower.end());
553 check_faces.insert(check_faces.end(),
554 owned_faces_higher.begin(),
555 owned_faces_higher.end());
556 std::sort(check_faces.begin(), check_faces.end());
557 AssertDimension(check_faces.size(),
558 inner_face.second.shared_faces.size());
559 for (unsigned int i = 0; i < check_faces.size(); ++i)
560 Assert(check_faces[i] == inner_face.second.shared_faces[i],
562 }
563
564 // now only set half of the faces as the ones to keep
565 if (my_domain < inner_face.first)
566 inner_face.second.shared_faces.swap(owned_faces_lower);
567 else
568 inner_face.second.shared_faces.swap(owned_faces_higher);
569
570 std::sort(inner_face.second.shared_faces.begin(),
571 inner_face.second.shared_faces.end());
572 }
573 }
574
575 // fill in the additional cells that we need access to via ghosting to
576 // cell_levels
577 std::set<std::pair<unsigned int, unsigned int>> ghost_cells;
578 for (unsigned int i = 0; i < cell_levels.size(); ++i)
579 {
580 typename ::Triangulation<dim>::cell_iterator dcell(
581 &triangulation, cell_levels[i].first, cell_levels[i].second);
582 if (use_active_cells)
583 Assert(dcell->is_active(), ExcNotImplemented());
584 for (const auto f : dcell->face_indices())
585 {
586 if (dcell->at_boundary(f) && !dcell->has_periodic_neighbor(f))
587 face_is_owned[dcell->face(f)->index()] =
588 FaceCategory::locally_active_at_boundary;
589 else if (!build_inner_faces)
590 continue;
591
592 // treat boundaries of cells of different refinement level
593 // inside the domain in case of multigrid separately
594 else if ((dcell->at_boundary(f) == false ||
595 dcell->has_periodic_neighbor(f)) &&
596 mg_level != numbers::invalid_unsigned_int &&
597 dcell->neighbor_or_periodic_neighbor(f)->level() <
598 dcell->level())
599 {
600 face_is_owned[dcell->face(f)->index()] =
601 FaceCategory::multigrid_refinement_edge;
602 }
603 else
604 {
605 typename ::Triangulation<dim>::cell_iterator neighbor =
606 dcell->neighbor_or_periodic_neighbor(f);
607
608 // neighbor is refined -> face will be treated by neighbor
609 if (use_active_cells && neighbor->has_children() &&
610 hold_all_faces_to_owned_cells == false)
611 continue;
612
613 bool add_to_ghost = false;
615 id1 = use_active_cells ? dcell->subdomain_id() :
616 dcell->level_subdomain_id(),
617 id2 = use_active_cells ?
618 (neighbor->has_children() ?
619 dcell->neighbor_child_on_subface(f, 0)
620 ->subdomain_id() :
621 neighbor->subdomain_id()) :
622 neighbor->level_subdomain_id();
623
624 // Check whether the current face should be processed
625 // locally (instead of being processed from the other
626 // side). We process a face locally when we are more refined
627 // (in the active cell case) or when the face is listed in
628 // the `shared_faces` data structure that we built above.
629 if ((id1 == id2 &&
630 (use_active_cells == false || neighbor->is_active())) ||
631 dcell->level() > neighbor->level() ||
632 std::binary_search(
633 inner_faces_at_proc_boundary[id2].shared_faces.begin(),
634 inner_faces_at_proc_boundary[id2].shared_faces.end(),
635 std::make_pair(id1 < id2 ? dcell->id() : neighbor->id(),
636 id1 < id2 ? neighbor->id() :
637 dcell->id())))
638 {
639 face_is_owned[dcell->face(f)->index()] =
640 FaceCategory::locally_active_done_here;
641 if (dcell->level() == neighbor->level() ||
642 dcell->has_periodic_neighbor(f))
643 face_is_owned
644 [neighbor
645 ->face(dcell->has_periodic_neighbor(f) ?
646 dcell->periodic_neighbor_face_no(f) :
647 dcell->neighbor_face_no(f))
648 ->index()] =
649 FaceCategory::locally_active_done_here;
650
651 // If neighbor is a ghost element (i.e.
652 // dcell->subdomain_id !
653 // dcell->neighbor(f)->subdomain_id()), we need to add its
654 // index into cell level list.
655 if (use_active_cells)
656 add_to_ghost =
657 (dcell->subdomain_id() != neighbor->subdomain_id());
658 else
659 add_to_ghost = (dcell->level_subdomain_id() !=
660 neighbor->level_subdomain_id());
661 }
662 else if (hold_all_faces_to_owned_cells == true)
663 {
664 // add all cells to ghost layer...
665 face_is_owned[dcell->face(f)->index()] =
666 FaceCategory::ghosted;
667 if (use_active_cells)
668 {
669 if (neighbor->has_children())
670 for (unsigned int s = 0;
671 s < dcell->face(f)->n_children();
672 ++s)
673 if (dcell->at_boundary(f))
674 {
675 if (dcell
676 ->periodic_neighbor_child_on_subface(f,
677 s)
678 ->subdomain_id() !=
679 dcell->subdomain_id())
680 add_to_ghost = true;
681 }
682 else
683 {
684 if (dcell->neighbor_child_on_subface(f, s)
685 ->subdomain_id() !=
686 dcell->subdomain_id())
687 add_to_ghost = true;
688 }
689 else
690 add_to_ghost = (dcell->subdomain_id() !=
691 neighbor->subdomain_id());
692 }
693 else
694 add_to_ghost = (dcell->level_subdomain_id() !=
695 neighbor->level_subdomain_id());
696 }
697
698 if (add_to_ghost)
699 {
700 if (use_active_cells && neighbor->has_children())
701 for (unsigned int s = 0;
702 s < dcell->face(f)->n_children();
703 ++s)
704 {
705 typename ::Triangulation<dim>::cell_iterator
706 neighbor_child =
707 dcell->at_boundary(f) ?
708 dcell->periodic_neighbor_child_on_subface(f,
709 s) :
710 dcell->neighbor_child_on_subface(f, s);
711 if (neighbor_child->subdomain_id() !=
712 dcell->subdomain_id())
713 ghost_cells.insert(
714 std::pair<unsigned int, unsigned int>(
715 neighbor_child->level(),
716 neighbor_child->index()));
717 }
718 else
719 ghost_cells.insert(
720 std::pair<unsigned int, unsigned int>(
721 neighbor->level(), neighbor->index()));
722 at_processor_boundary[i] = true;
723 }
724 }
725 }
726 }
727
728 // step 2: append the ghost cells at the end of the locally owned
729 // cells
730 for (const auto &ghost_cell : ghost_cells)
731 cell_levels.push_back(ghost_cell);
732 }
733
734
735
736 template <int dim>
737 void
738 FaceSetup<dim>::generate_faces(
739 const ::Triangulation<dim> &triangulation,
740 const std::vector<std::pair<unsigned int, unsigned int>> &cell_levels,
741 TaskInfo &task_info)
742 {
743 const bool is_mixed_mesh = triangulation.is_mixed_mesh();
744
745 // step 1: create the inverse map between cell iterators and the
746 // cell_level_index field
747 std::map<std::pair<unsigned int, unsigned int>, unsigned int>
748 map_to_vectorized;
749 for (unsigned int cell = 0; cell < cell_levels.size(); ++cell)
750 if (cell == 0 || cell_levels[cell] != cell_levels[cell - 1])
751 {
752 typename ::Triangulation<dim>::cell_iterator dcell(
753 &triangulation,
754 cell_levels[cell].first,
755 cell_levels[cell].second);
756 std::pair<unsigned int, unsigned int> level_index(dcell->level(),
757 dcell->index());
758 map_to_vectorized[level_index] = cell;
759 }
760
761 // step 2: fill the information about inner faces and boundary faces
762 const unsigned int vectorization_length = task_info.vectorization_length;
763 task_info.face_partition_data.resize(
764 task_info.cell_partition_data.size() - 1, 0);
765 task_info.boundary_partition_data.resize(
766 task_info.cell_partition_data.size() - 1, 0);
767 std::vector<unsigned char> face_visited(face_is_owned.size(), 0);
768 for (unsigned int partition = 0;
769 partition < task_info.cell_partition_data.size() - 2;
770 ++partition)
771 {
772 unsigned int boundary_counter = 0;
773 unsigned int inner_counter = 0;
774 for (unsigned int cell = task_info.cell_partition_data[partition] *
775 vectorization_length;
776 cell < task_info.cell_partition_data[partition + 1] *
777 vectorization_length;
778 ++cell)
779 if (cell == 0 || cell_levels[cell] != cell_levels[cell - 1])
780 {
781 typename ::Triangulation<dim>::cell_iterator dcell(
782 &triangulation,
783 cell_levels[cell].first,
784 cell_levels[cell].second);
785 for (const auto f : dcell->face_indices())
786 {
787 // boundary face
788 if (face_is_owned[dcell->face(f)->index()] ==
789 FaceCategory::locally_active_at_boundary)
790 {
791 Assert(dcell->at_boundary(f), ExcInternalError());
792 ++boundary_counter;
793 FaceToCellTopology<1> info;
794 info.cells_interior[0] = cell;
795 info.cells_exterior[0] = numbers::invalid_unsigned_int;
796 info.interior_face_no = f;
797 info.exterior_face_no = dcell->face(f)->boundary_id();
798 info.face_type =
799 is_mixed_mesh ?
800 (dcell->face(f)->reference_cell() !=
802 0;
803 info.subface_index =
805 info.face_orientation = 0;
806 boundary_faces.push_back(info);
807
808 face_visited[dcell->face(f)->index()]++;
809 }
810 // interior face, including faces over periodic boundaries
811 else
812 {
813 typename ::Triangulation<dim>::cell_iterator
814 neighbor = dcell->neighbor_or_periodic_neighbor(f);
815 if (use_active_cells && neighbor->has_children())
816 {
817 const unsigned int n_children =
818 (dim == 1) ? 1 : dcell->face(f)->n_children();
819 for (unsigned int c = 0; c < n_children; ++c)
820 {
821 typename ::Triangulation<
822 dim>::cell_iterator neighbor_c;
823 if (dim > 1)
824 neighbor_c =
825 (dcell->at_boundary(f) ?
826 dcell
827 ->periodic_neighbor_child_on_subface(
828 f, c) :
829 dcell->neighbor_child_on_subface(f, c));
830 else
831 {
832 // in 1D, adjacent cells can differ by
833 // more than 1 level
834 neighbor_c = neighbor->child(1 - f);
835 while (!neighbor_c->is_active())
836 neighbor_c = neighbor_c->child(1 - f);
837 }
838 const types::subdomain_id neigh_domain =
839 neighbor_c->subdomain_id();
840 const unsigned int neighbor_face_no =
841 dcell->has_periodic_neighbor(f) ?
842 dcell->periodic_neighbor_face_no(f) :
843 dcell->neighbor_face_no(f);
844 const unsigned int child_face_index =
845 dim > 1 ? dcell->face(f)->child(c)->index() :
846 dcell->face(f)->index();
847 if (neigh_domain != dcell->subdomain_id() ||
848 face_visited[child_face_index] == 1)
849 {
850 std::pair<unsigned int, unsigned int>
851 level_index(neighbor_c->level(),
852 neighbor_c->index());
853 if (face_is_owned[child_face_index] ==
854 FaceCategory::locally_active_done_here)
855 {
856 ++inner_counter;
857 inner_faces.push_back(create_face(
858 neighbor_face_no,
859 neighbor_c,
860 map_to_vectorized[level_index],
861 dcell,
862 cell,
863 is_mixed_mesh));
864 }
865 else if (face_is_owned[child_face_index] ==
866 FaceCategory::ghosted ||
867 face_is_owned[dcell->face(f)
868 ->index()] ==
869 FaceCategory::ghosted)
870 {
871 inner_ghost_faces.push_back(create_face(
872 neighbor_face_no,
873 neighbor_c,
874 map_to_vectorized[level_index],
875 dcell,
876 cell,
877 is_mixed_mesh));
878 }
879 else
880 Assert(face_is_owned[dcell->face(f)
881 ->index()] ==
882 FaceCategory::
883 locally_active_done_elsewhere,
885 }
886 else
887 {
888 face_visited[child_face_index] = 1;
889 if (dcell->has_periodic_neighbor(f))
890 face_visited[dcell->face(f)->index()] = 1;
891 }
892 }
893 }
894 else
895 {
896 const types::subdomain_id my_domain =
897 use_active_cells ? dcell->subdomain_id() :
898 dcell->level_subdomain_id();
899 const types::subdomain_id neigh_domain =
900 use_active_cells ? neighbor->subdomain_id() :
901 neighbor->level_subdomain_id();
902 const unsigned int face_index =
903 dcell->face(f)->index();
904 if (neigh_domain != my_domain ||
905 face_visited[face_index] == 1)
906 {
907 std::pair<unsigned int, unsigned int>
908 level_index(neighbor->level(),
909 neighbor->index());
910 if (face_is_owned[dcell->face(f)->index()] ==
911 FaceCategory::locally_active_done_here)
912 {
913 Assert(use_active_cells ||
914 dcell->level() ==
915 neighbor->level(),
917 ++inner_counter;
918 inner_faces.push_back(create_face(
919 f,
920 dcell,
921 cell,
922 neighbor,
923 map_to_vectorized[level_index],
924 is_mixed_mesh));
925 }
926 else if (face_is_owned[face_index] ==
927 FaceCategory::ghosted)
928 {
929 inner_ghost_faces.push_back(create_face(
930 f,
931 dcell,
932 cell,
933 neighbor,
934 map_to_vectorized[level_index],
935 is_mixed_mesh));
936 }
937 }
938 else
939 {
940 face_visited[face_index] = 1;
941 if (dcell->has_periodic_neighbor(f))
942 face_visited
943 [neighbor
944 ->face(
945 dcell->periodic_neighbor_face_no(f))
946 ->index()] = 1;
947 }
948 if (face_is_owned[face_index] ==
949 FaceCategory::multigrid_refinement_edge)
950 {
951 refinement_edge_faces.push_back(
952 create_face(f,
953 dcell,
954 cell,
955 neighbor,
956 refinement_edge_faces.size(),
957 is_mixed_mesh));
958 }
959 }
960 }
961 }
962 }
963 task_info.face_partition_data[partition + 1] =
964 task_info.face_partition_data[partition] + inner_counter;
965 task_info.boundary_partition_data[partition + 1] =
966 task_info.boundary_partition_data[partition] + boundary_counter;
967 }
968 task_info.ghost_face_partition_data.resize(2);
969 task_info.ghost_face_partition_data[0] = 0;
970 task_info.ghost_face_partition_data[1] = inner_ghost_faces.size();
971 task_info.refinement_edge_face_partition_data.resize(2);
972 task_info.refinement_edge_face_partition_data[0] = 0;
973 task_info.refinement_edge_face_partition_data[1] =
974 refinement_edge_faces.size();
975 }
976
977
978
979 template <int dim>
980 FaceToCellTopology<1>
981 FaceSetup<dim>::create_face(
982 const unsigned int face_no,
983 const typename ::Triangulation<dim>::cell_iterator &cell,
984 const unsigned int number_cell_interior,
985 const typename ::Triangulation<dim>::cell_iterator &neighbor,
986 const unsigned int number_cell_exterior,
987 const bool is_mixed_mesh)
988 {
989 FaceToCellTopology<1> info;
990 info.cells_interior[0] = number_cell_interior;
991 info.cells_exterior[0] = number_cell_exterior;
992 info.interior_face_no = face_no;
993 if (cell->has_periodic_neighbor(face_no))
994 info.exterior_face_no = cell->periodic_neighbor_face_no(face_no);
995 else
996 info.exterior_face_no = cell->neighbor_face_no(face_no);
997
998 info.face_type = is_mixed_mesh ?
999 (cell->face(face_no)->reference_cell() !=
1000 ReferenceCells::get_hypercube<dim - 1>()) :
1001 0;
1002
1003 info.subface_index = GeometryInfo<dim>::max_children_per_cell;
1004 Assert(neighbor->level() <= cell->level(), ExcInternalError());
1005
1006 // for dim > 1 and hanging faces we must set a subface index
1007 if (dim > 1 && cell->level() > neighbor->level())
1008 {
1009 if (cell->has_periodic_neighbor(face_no))
1010 info.subface_index =
1011 cell->periodic_neighbor_of_coarser_periodic_neighbor(face_no)
1012 .second;
1013 else
1014 info.subface_index =
1015 cell->neighbor_of_coarser_neighbor(face_no).second;
1016 }
1017
1018 // special treatment of periodic boundaries
1019 if (cell->has_periodic_neighbor(face_no))
1020 {
1021 info.face_orientation = cell->get_triangulation()
1022 .get_periodic_face_map()
1023 .at({cell, face_no})
1024 .second;
1025 }
1026 else
1027 {
1028 const auto interior_face_orientation =
1029 cell->combined_face_orientation(face_no);
1030 const auto exterior_face_orientation =
1031 neighbor->combined_face_orientation(info.exterior_face_no);
1032 if (interior_face_orientation !=
1034 {
1035 info.face_orientation = 8 + interior_face_orientation;
1036 Assert(exterior_face_orientation ==
1038 ExcMessage(
1039 "Face seems to be wrongly oriented from both sides"));
1040 }
1041 else
1042 info.face_orientation = exterior_face_orientation;
1043
1044 // make sure to select correct subface index in case of non-standard
1045 // orientation of the coarser neighbor face
1046 if (cell->level() > neighbor->level() &&
1047 exterior_face_orientation > 0)
1048 {
1049 const Table<2, unsigned int> orientation =
1050 ShapeInfo<double>::compute_orientation_table(2);
1051 const auto face_reference_cell =
1052 cell->face(face_no)->reference_cell();
1053 info.subface_index = orientation(
1054 face_reference_cell.get_inverse_combined_orientation(
1055 exterior_face_orientation),
1056 info.subface_index);
1057 }
1058 }
1059
1060 return info;
1061 }
1062
1063
1064
1071 inline bool
1072 compare_faces_for_vectorization(
1073 const FaceToCellTopology<1> &face1,
1074 const FaceToCellTopology<1> &face2,
1075 const std::vector<unsigned int> &active_fe_indices,
1076 const unsigned int length)
1077 {
1078 if (face1.interior_face_no != face2.interior_face_no)
1079 return false;
1080 if (face1.exterior_face_no != face2.exterior_face_no)
1081 return false;
1082 if (face1.subface_index != face2.subface_index)
1083 return false;
1084 if (face1.face_orientation != face2.face_orientation)
1085 return false;
1086 if (face1.face_type != face2.face_type)
1087 return false;
1088
1089 if (active_fe_indices.size() > 0)
1090 {
1091 if (active_fe_indices[face1.cells_interior[0] / length] !=
1092 active_fe_indices[face2.cells_interior[0] / length])
1093 return false;
1094
1095 if (face2.cells_exterior[0] != numbers::invalid_unsigned_int)
1096 if (active_fe_indices[face1.cells_exterior[0] / length] !=
1097 active_fe_indices[face2.cells_exterior[0] / length])
1098 return false;
1099 }
1100
1101 return true;
1102 }
1103
1104
1105
1112 template <int length>
1113 struct FaceComparator
1114 {
1115 FaceComparator(const std::vector<unsigned int> &active_fe_indices)
1116 : active_fe_indices(active_fe_indices)
1117 {}
1118
1119 bool
1120 operator()(const FaceToCellTopology<length> &face1,
1121 const FaceToCellTopology<length> &face2) const
1122 {
1123 // check if active FE indices match
1124 if (face1.face_type < face2.face_type)
1125 return true;
1126 else if (face1.face_type > face2.face_type)
1127 return false;
1128
1129 // check if active FE indices match
1130 if (active_fe_indices.size() > 0)
1131 {
1132 // ... for interior faces
1133 if (active_fe_indices[face1.cells_interior[0] / length] <
1134 active_fe_indices[face2.cells_interior[0] / length])
1135 return true;
1136 else if (active_fe_indices[face1.cells_interior[0] / length] >
1137 active_fe_indices[face2.cells_interior[0] / length])
1138 return false;
1139
1140 // ... for exterior faces
1141 if (face2.cells_exterior[0] != numbers::invalid_unsigned_int)
1142 {
1143 if (active_fe_indices[face1.cells_exterior[0] / length] <
1144 active_fe_indices[face2.cells_exterior[0] / length])
1145 return true;
1146 else if (active_fe_indices[face1.cells_exterior[0] / length] >
1147 active_fe_indices[face2.cells_exterior[0] / length])
1148 return false;
1149 }
1150 }
1151
1152 for (unsigned int i = 0; i < length; ++i)
1153 if (face1.cells_interior[i] < face2.cells_interior[i])
1154 return true;
1155 else if (face1.cells_interior[i] > face2.cells_interior[i])
1156 return false;
1157 for (unsigned int i = 0; i < length; ++i)
1158 if (face1.cells_exterior[i] < face2.cells_exterior[i])
1159 return true;
1160 else if (face1.cells_exterior[i] > face2.cells_exterior[i])
1161 return false;
1162 if (face1.interior_face_no < face2.interior_face_no)
1163 return true;
1164 else if (face1.interior_face_no > face2.interior_face_no)
1165 return false;
1166 if (face1.exterior_face_no < face2.exterior_face_no)
1167 return true;
1168 else if (face1.exterior_face_no > face2.exterior_face_no)
1169 return false;
1170
1171 // we do not need to check for subface_index and orientation because
1172 // those cannot be different if when all the other values are the
1173 // same.
1174 AssertDimension(face1.subface_index, face2.subface_index);
1175 AssertDimension(face1.face_orientation, face2.face_orientation);
1176
1177 return false;
1178 }
1179
1180 private:
1181 const std::vector<unsigned int> &active_fe_indices;
1182 };
1183
1184
1185
1186 template <int vectorization_width>
1187 void
1189 const std::vector<FaceToCellTopology<1>> &faces_in,
1190 const std::vector<bool> &hard_vectorization_boundary,
1191 std::vector<unsigned int> &face_partition_data,
1192 std::vector<FaceToCellTopology<vectorization_width>> &faces_out,
1193 const std::vector<unsigned int> &active_fe_indices)
1194 {
1195 FaceToCellTopology<vectorization_width> face_batch;
1196 std::vector<std::vector<unsigned int>> faces_type;
1197
1198 unsigned int face_start = face_partition_data[0],
1199 face_end = face_partition_data[0];
1200
1201 face_partition_data[0] = faces_out.size();
1202 for (unsigned int partition = 0;
1203 partition < face_partition_data.size() - 1;
1204 ++partition)
1205 {
1206 std::vector<std::vector<unsigned int>> new_faces_type;
1207
1208 // start with the end point for the last partition
1209 face_start = face_end;
1210 face_end = face_partition_data[partition + 1];
1211
1212 // set the partitioner to the new vectorized lengths
1213 face_partition_data[partition + 1] = face_partition_data[partition];
1214
1215 // loop over the faces in the current partition and reorder according
1216 // to the face type
1217 for (unsigned int face = face_start; face < face_end; ++face)
1218 {
1219 for (auto &face_type : faces_type)
1220 {
1221 // Compare current face with first face of type type
1222 if (compare_faces_for_vectorization(faces_in[face],
1223 faces_in[face_type[0]],
1224 active_fe_indices,
1225 vectorization_width))
1226 {
1227 face_type.push_back(face);
1228 goto face_found;
1229 }
1230 }
1231 faces_type.emplace_back(1, face);
1232 face_found:
1233 {}
1234 }
1235
1236 // insert new faces in sorted list to get good data locality
1237 FaceComparator<vectorization_width> face_comparator(
1238 active_fe_indices);
1239 std::set<FaceToCellTopology<vectorization_width>,
1240 FaceComparator<vectorization_width>>
1241 new_faces(face_comparator);
1242 for (const auto &face_type : faces_type)
1243 {
1244 face_batch.face_type = faces_in[face_type[0]].face_type;
1245 face_batch.interior_face_no =
1246 faces_in[face_type[0]].interior_face_no;
1247 face_batch.exterior_face_no =
1248 faces_in[face_type[0]].exterior_face_no;
1249 face_batch.subface_index = faces_in[face_type[0]].subface_index;
1250 face_batch.face_orientation =
1251 faces_in[face_type[0]].face_orientation;
1252 unsigned int no_faces = face_type.size();
1253 std::vector<unsigned char> touched(no_faces, 0);
1254
1255 // do two passes through the data. The first is to identify
1256 // similar faces within the same index range as the cells which
1257 // will allow for vectorized read operations, the second picks up
1258 // all the rest
1259 unsigned int n_vectorized = 0;
1260 for (unsigned int f = 0; f < no_faces; ++f)
1261 if (faces_in[face_type[f]].cells_interior[0] %
1262 vectorization_width ==
1263 0)
1264 {
1265 bool is_contiguous = true;
1266 if (f + vectorization_width > no_faces)
1267 is_contiguous = false;
1268 else
1269 for (unsigned int v = 1; v < vectorization_width; ++v)
1270 if (faces_in[face_type[f + v]].cells_interior[0] !=
1271 faces_in[face_type[f]].cells_interior[0] + v)
1272 is_contiguous = false;
1273 if (is_contiguous)
1274 {
1276 face_type.size() -
1277 vectorization_width + 1);
1278 for (unsigned int v = 0; v < vectorization_width; ++v)
1279 {
1280 face_batch.cells_interior[v] =
1281 faces_in[face_type[f + v]].cells_interior[0];
1282 face_batch.cells_exterior[v] =
1283 faces_in[face_type[f + v]].cells_exterior[0];
1284 touched[f + v] = 1;
1285 }
1286 new_faces.insert(face_batch);
1287 f += vectorization_width - 1;
1288 n_vectorized += vectorization_width;
1289 }
1290 }
1291
1292 std::vector<unsigned int> untouched;
1293 untouched.reserve(no_faces - n_vectorized);
1294 for (unsigned int f = 0; f < no_faces; ++f)
1295 if (touched[f] == 0)
1296 untouched.push_back(f);
1297 unsigned int v = 0;
1298 for (const auto f : untouched)
1299 {
1300 face_batch.cells_interior[v] =
1301 faces_in[face_type[f]].cells_interior[0];
1302 face_batch.cells_exterior[v] =
1303 faces_in[face_type[f]].cells_exterior[0];
1304 ++v;
1305 if (v == vectorization_width)
1306 {
1307 new_faces.insert(face_batch);
1308 v = 0;
1309 }
1310 }
1311 if (v > 0 && v < vectorization_width)
1312 {
1313 // must add non-filled face
1314 if (hard_vectorization_boundary[partition + 1] ||
1315 partition == face_partition_data.size() - 2)
1316 {
1317 for (; v < vectorization_width; ++v)
1318 {
1319 // Dummy cell, not used
1320 face_batch.cells_interior[v] =
1322 face_batch.cells_exterior[v] =
1324 }
1325 new_faces.insert(face_batch);
1326 }
1327 else
1328 {
1329 // postpone to the next partition
1330 std::vector<unsigned int> untreated(v);
1331 for (unsigned int f = 0; f < v; ++f)
1332 untreated[f] = face_type[*(untouched.end() - 1 - f)];
1333 new_faces_type.push_back(untreated);
1334 }
1335 }
1336 }
1337
1338 // insert sorted list to vector of faces
1339 for (auto it = new_faces.begin(); it != new_faces.end(); ++it)
1340 faces_out.push_back(*it);
1341 face_partition_data[partition + 1] += new_faces.size();
1342
1343 // set the faces that were left over to faces_type for the next round
1344 faces_type = std::move(new_faces_type);
1345 }
1346
1347 if constexpr (running_in_debug_mode())
1348 {
1349 // final safety checks
1350 for (const auto &face_type : faces_type)
1351 AssertDimension(face_type.size(), 0U);
1352
1353 AssertDimension(faces_out.size(), face_partition_data.back());
1354 unsigned int nfaces = 0;
1355 for (unsigned int i = face_partition_data[0];
1356 i < face_partition_data.back();
1357 ++i)
1358 for (unsigned int v = 0; v < vectorization_width; ++v)
1359 nfaces += (faces_out[i].cells_interior[v] !=
1361 AssertDimension(nfaces, faces_in.size());
1362
1363 std::vector<std::pair<unsigned int, unsigned int>> in_faces,
1364 out_faces;
1365 for (const auto &face_in : faces_in)
1366 in_faces.emplace_back(face_in.cells_interior[0],
1367 face_in.cells_exterior[0]);
1368 for (unsigned int i = face_partition_data[0];
1369 i < face_partition_data.back();
1370 ++i)
1371 for (unsigned int v = 0;
1372 v < vectorization_width && faces_out[i].cells_interior[v] !=
1374 ++v)
1375 out_faces.emplace_back(faces_out[i].cells_interior[v],
1376 faces_out[i].cells_exterior[v]);
1377 std::sort(in_faces.begin(), in_faces.end());
1378 std::sort(out_faces.begin(), out_faces.end());
1379 AssertDimension(in_faces.size(), out_faces.size());
1380 for (unsigned int i = 0; i < in_faces.size(); ++i)
1381 {
1382 AssertDimension(in_faces[i].first, out_faces[i].first);
1383 AssertDimension(in_faces[i].second, out_faces[i].second);
1384 }
1385 }
1386 }
1387
1388#endif // ifndef DOXYGEN
1389
1390 } // namespace MatrixFreeFunctions
1391} // namespace internal
1392
1393
1395
1396#endif
*  *  Point< dim > operator()(const Point< dim > &p) const * 
***mech_lbc_system increment_interpolation_handlers push_back(scale_z_handler)
#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
Point< 2 > first
Definition grid_out.cc:4639
static ::ExceptionBase & ExcNotImplemented()
#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)
#define AssertThrow(cond, exc)
std::size_t size
Definition mpi.cc:733
const MPI_Comm comm
Definition mpi.cc:912
constexpr char U
constexpr const ReferenceCell< dim > & get_hypercube()
void partition(const SparsityPattern &sparsity_pattern, const unsigned int n_partitions, std::vector< unsigned int > &partition_indices, const Partitioner partitioner=Partitioner::metis)
void collect_faces_vectorization(const std::vector< FaceToCellTopology< 1 > > &faces_in, const std::vector< bool > &hard_vectorization_boundary, std::vector< unsigned int > &face_partition_data, std::vector< FaceToCellTopology< vectorization_width > > &faces_out)
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
constexpr types::subdomain_id invalid_subdomain_id
Definition types.h:385
constexpr types::geometric_orientation default_geometric_orientation
Definition types.h:342
std::vector< std::pair< CellId, CellId > > shared_faces
std::vector< FaceToCellTopology< 1 > > inner_faces
std::vector< FaceToCellTopology< 1 > > boundary_faces
std::vector< FaceToCellTopology< 1 > > refinement_edge_faces
FaceToCellTopology< 1 > create_face(const unsigned int face_no, const typename ::Triangulation< dim >::cell_iterator &cell, const unsigned int number_cell_interior, const typename ::Triangulation< dim >::cell_iterator &neighbor, const unsigned int number_cell_exterior, const bool is_mixed_mesh)
void initialize(const ::Triangulation< dim > &triangulation, const unsigned int mg_level, const bool hold_all_faces_to_owned_cells, const bool build_inner_faces, std::vector< std::pair< unsigned int, unsigned int > > &cell_levels)
std::vector< FaceToCellTopology< 1 > > inner_ghost_faces
void generate_faces(const ::Triangulation< dim > &triangulation, const std::vector< std::pair< unsigned int, unsigned int > > &cell_levels, TaskInfo &task_info)