deal.II version GIT relicensing-7058-gb35ee29762 2026-11-11 09:30: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
mesh_classifier.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) 2021 - 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
14
17
19#include <deal.II/fe/fe_q.h>
22
33#include <deal.II/lac/vector.h>
35
37
38#include <algorithm>
39
41
42namespace NonMatching
43{
44 namespace internal
45 {
46 namespace MeshClassifierImplementation
47 {
50 "The Triangulation has not been classified. You need to call the "
51 "reclassify()-function before using this function.");
52
55 "The incoming cell does not belong to the triangulation passed to "
56 "the constructor.");
57
58 /*
59 * Return LocationToLevelSet::inside/outside if all values in the vector
60 * are negative/positive.
61 * Return LocationToLevelSet::intersected if the values in the vector
62 * include both negative and positive numbers.
63 * Return LocationToLevelSet::aligned if all values in the vector are
64 * exactly zero.
65 */
66 template <typename VectorType>
68 location_from_dof_signs(const VectorType &local_levelset_values)
69 {
70 const auto [min_element, max_element] =
71 std::minmax_element(local_levelset_values.begin(),
72 local_levelset_values.end());
73
74 // Note that we actually want to compare that the values are exactly
75 // floating point zero here, even if the rule of thumb is that one never
76 // should.
77 if (*min_element == 0.0 && *max_element == 0.0)
79 if (*max_element <= 0)
81 if (0 <= *min_element)
83
85 }
86
87
88
89 /*
90 * Type trait to identify a LinearAlgebra::distributed::Vector on the
91 * host, whose ghost entries are described by a
92 * Utilities::MPI::Partitioner.
93 */
94 template <typename VectorType>
95 struct IsHostDistributedVector : std::false_type
96 {};
97
98 template <typename Number>
100 LinearAlgebra::distributed::Vector<Number, MemorySpace::Host>>
101 : std::true_type
102 {};
103
104
105
106 /*
107 * Check that the incoming level set vector contains all locally relevant
108 * DoFs of the incoming DoFHandler, either as locally owned or as ghost
109 * entries. This is required since MeshClassifier::reclassify() reads the
110 * level set values on the faces of all non-artificial cells, i.e., also
111 * on the ghost cells. The check is only performed for
112 * LinearAlgebra::distributed::Vector, for which the ghost entries might
113 * be a subset of the locally relevant DoFs, e.g., if the vector was
114 * initialized by MatrixFree.
115 */
116 template <int dim, typename VectorType>
117 void
119 [[maybe_unused]] const DoFHandler<dim> &dof_handler,
120 [[maybe_unused]] const VectorType &level_set)
121 {
123 {
124 IndexSet required_ghost_dofs =
126 required_ghost_dofs.subtract_set(
127 level_set.locally_owned_elements());
128
129 Assert(
130 required_ghost_dofs.is_subset_of(
131 level_set.get_partitioner()->ghost_indices()),
133 "The level set vector does not contain all locally relevant "
134 "DoFs of the DoFHandler as locally owned or ghost entries. "
135 "However, the MeshClassifier needs to access the level set "
136 "values on the faces of all ghost cells. This typically "
137 "happens if the vector was initialized via "
138 "MatrixFree::initialize_dof_vector(), whose ghost entries only "
139 "cover the DoFs of the locally owned cells. Copy the level set "
140 "field into a vector whose ghost entries are given by "
141 "DoFTools::extract_locally_relevant_dofs()."));
142 }
143 }
144
145
146
151 template <int dim, typename VectorType>
153 {
154 public:
159 const VectorType &level_set);
160
165 get_fe_collection() const override;
166
171 unsigned int
173 &cell) const override;
174
179 void
181 const typename Triangulation<dim>::active_cell_iterator &cell,
182 const unsigned int face_index,
183 Vector<double> &local_levelset_values) override;
184
205 void
206 check_consistency() const override;
207
208 private:
213
219 };
220
221
222
223 template <int dim, typename VectorType>
225 const DoFHandler<dim> &dof_handler,
226 const VectorType &level_set)
227 : dof_handler(&dof_handler)
228 , level_set(&level_set)
229 {}
230
231
232
233 template <int dim, typename VectorType>
236 {
237 return dof_handler->get_fe_collection();
238 }
239
240
241
242 template <int dim, typename VectorType>
243 void
245 const typename Triangulation<dim>::active_cell_iterator &cell,
246 const unsigned int face_index,
247 Vector<double> &local_levelset_values)
248 {
249 const auto cell_with_dofs = cell->as_dof_handler_iterator(*dof_handler);
250
251 const unsigned int n_dofs_per_face =
252 dof_handler->get_fe().n_dofs_per_face();
253 std::vector<types::global_dof_index> dof_indices(n_dofs_per_face);
254 cell_with_dofs->face(face_index)->get_dof_indices(dof_indices);
255
256 local_levelset_values.reinit(dof_indices.size());
257
258 for (unsigned int i = 0; i < dof_indices.size(); i++)
259 local_levelset_values[i] =
261 dof_indices[i]);
262 }
263
264
265
266 template <int dim, typename VectorType>
267 void
273
274
275 template <int dim, typename VectorType>
276 unsigned int
278 const typename Triangulation<dim>::active_cell_iterator &cell) const
279 {
280 const auto cell_with_dofs = cell->as_dof_handler_iterator(*dof_handler);
281
282 return cell_with_dofs->active_fe_index();
283 }
284
285
290 template <int dim>
292 {
293 public:
299 const FiniteElement<dim> &element);
300
306 get_fe_collection() const override;
307
312 unsigned int
314 &cell) const override;
315
320 void
322 const typename Triangulation<dim>::active_cell_iterator &cell,
323 const unsigned int face_index,
324 Vector<double> &local_levelset_values) override;
325
326 private:
331
337
343 };
344
345
346
347 template <int dim>
349 const Function<dim> &level_set,
350 const FiniteElement<dim> &element)
351 : level_set(&level_set)
352 , fe_collection(element)
353 , fe_face_values(element,
354 Quadrature<dim - 1>(
355 element.get_unit_face_support_points()),
357 {}
358
359
360
361 template <int dim>
362 void
364 const typename Triangulation<dim>::active_cell_iterator &cell,
365 const unsigned int face_index,
366 Vector<double> &local_levelset_values)
367 {
368 AssertDimension(local_levelset_values.size(),
369 fe_face_values.n_quadrature_points);
370
371 fe_face_values.reinit(cell, face_index);
372 const std::vector<Point<dim>> &points =
373 fe_face_values.get_quadrature_points();
374
375 for (unsigned int i = 0; i < points.size(); i++)
376 local_levelset_values[i] = level_set->value(points[i]);
377 }
378
379
380
381 template <int dim>
384 {
385 return fe_collection;
386 }
387
388
389
390 template <int dim>
391 unsigned int
397 } // namespace MeshClassifierImplementation
398 } // namespace internal
399
400
401
402 template <int dim>
403 template <typename VectorType>
405 const VectorType &level_set)
406 : triangulation(&dof_handler.get_triangulation())
407 , level_set_description(
408 std::make_unique<internal::MeshClassifierImplementation::
409 DiscreteLevelSetDescription<dim, VectorType>>(
410 dof_handler,
411 level_set))
412 {
413#ifdef DEAL_II_WITH_LAPACK
414 const hp::FECollection<dim> &fe_collection =
415 dof_handler.get_fe_collection();
416 for (unsigned int i = 0; i < fe_collection.size(); i++)
417 {
418 // The level set function must be scalar.
419 AssertDimension(fe_collection[i].n_components(), 1);
420
421 Assert(fe_collection[i].has_face_support_points(),
423 "The elements in the FECollection of the incoming DoFHandler "
424 "must have face support points."));
425 }
426#else
427 AssertThrow(false, ExcNeedsLAPACK());
428#endif
429 }
430
431
432
433 template <int dim>
435 const Function<dim> &level_set,
436 const FiniteElement<dim> &element)
437 : triangulation(&triangulation)
438 , level_set_description(
439 std::make_unique<internal::MeshClassifierImplementation::
440 AnalyticLevelSetDescription<dim>>(level_set,
441 element))
442 {
443 // The level set function must be scalar.
444 AssertDimension(level_set.n_components, 1);
445 AssertDimension(element.n_components(), 1);
446 }
447
448
449
450 template <int dim>
451 void
453 {
454 level_set_description->check_consistency();
455 initialize();
456 cell_locations.assign(triangulation->n_active_cells(),
458 face_locations.assign(triangulation->n_raw_faces(),
460
461 // Returns weather the incoming LocationToLevelSet is in the incoming set.
462 // This lambda can be factored away once C++20 is enabled, since set then
463 // has a contains function.
464 const auto contains =
465 [](const std::set<LocationToLevelSet> &local_face_locations,
466 const LocationToLevelSet &location) {
467 return local_face_locations.count(location) > 0;
468 };
469
470 // Loop over all cells and determine the location of all non artificial
471 // cells and faces.
472 for (const auto &cell : triangulation->active_cell_iterators())
473 if (!cell->is_artificial())
474 {
475 std::set<LocationToLevelSet> local_face_locations;
476
477 for (unsigned int f = 0; f < GeometryInfo<dim>::faces_per_cell; ++f)
478 {
479 const LocationToLevelSet face_location =
480 determine_face_location_to_levelset(cell, f);
481
482 face_locations[cell->face(f)->index()] = face_location;
483 local_face_locations.insert(face_location);
484 }
485
487
488 const bool all_faces_have_same_location =
489 local_face_locations.size() == 1;
490
491 if (all_faces_have_same_location)
492 {
493 cell_location = *local_face_locations.cbegin();
494 }
495 else if (contains(local_face_locations,
497 {
498 cell_location = LocationToLevelSet::intersected;
499 }
500 else if (contains(local_face_locations, LocationToLevelSet::aligned))
501 {
502 Assert(local_face_locations.size() == 2, ExcNotImplemented());
503
504 if (contains(local_face_locations, LocationToLevelSet::outside))
505 cell_location = LocationToLevelSet::outside;
506 else // contains LocationToLevelSet::inside
507 cell_location = LocationToLevelSet::intersected;
508 }
509 else
510 {
511 // In 2D, a cell can be intersected without any faces being
512 // intersected, if the zero contour cuts the cell diagonally
513 // through the vertices. In this case, two faces are inside and
514 // two are outside.
515 Assert(local_face_locations.size() == 2, ExcNotImplemented());
516 cell_location = LocationToLevelSet::intersected;
517 }
518
519
520 cell_locations[cell->active_cell_index()] = cell_location;
521 }
522 }
523
524
525
526 template <int dim>
529 const typename Triangulation<dim>::active_cell_iterator &cell,
530 const unsigned int face_index)
531 {
532 // The location of the face might already be computed on the neighboring
533 // cell. If this is the case we just return the value.
534 const LocationToLevelSet location =
535 face_locations.at(cell->face(face_index)->index());
536 if (location != LocationToLevelSet::unassigned)
537 return location;
538
539 // Determine the location by changing basis to FE_Bernstein and checking
540 // the signs of the dofs.
541 const unsigned int fe_index = level_set_description->active_fe_index(cell);
542 const unsigned int n_local_dofs =
543 lagrange_to_bernstein_face[fe_index][face_index].m();
544
545 Vector<double> local_levelset_values(n_local_dofs);
546 level_set_description->get_local_level_set_values(cell,
547 face_index,
548 local_levelset_values);
549
550 const FiniteElement<dim> &fe =
551 level_set_description->get_fe_collection()[fe_index];
552
553 const FE_Q_iso_Q1<dim> *fe_q_iso_q1 =
554 dynamic_cast<const FE_Q_iso_Q1<dim> *>(&fe);
555
556 const FE_Poly<dim> *fe_poly = dynamic_cast<const FE_Poly<dim> *>(&fe);
557
558 const bool is_linear = fe_q_iso_q1 != nullptr ||
559 (fe_poly != nullptr && fe_poly->get_degree() == 1);
560
561 // shortcut for linear elements
562 if (is_linear)
563 {
565 local_levelset_values);
566 }
567
568 lagrange_to_bernstein_face[fe_index][face_index].solve(
569 local_levelset_values);
570
572 local_levelset_values);
573 }
574
575
576
577 template <int dim>
580 const typename Triangulation<dim>::cell_iterator &cell) const
581 {
582 Assert(cell_locations.size() == triangulation->n_active_cells(),
584 Assert(&cell->get_triangulation() == triangulation,
586
587 return cell_locations.at(cell->active_cell_index());
588 }
589
590
591
592 template <int dim>
595 const typename Triangulation<dim>::cell_iterator &cell,
596 const unsigned int face_index) const
597 {
599 Assert(face_locations.size() == triangulation->n_raw_faces(),
601 Assert(&cell->get_triangulation() == triangulation,
603
604 return face_locations.at(cell->face(face_index)->index());
605 }
606
607
608
609 template <int dim>
610 void
612 {
613 const hp::FECollection<dim> &fe_collection =
614 level_set_description->get_fe_collection();
615
616 // The level set function must be scalar.
617 AssertDimension(fe_collection.n_components(), 1);
618
619 lagrange_to_bernstein_face.resize(fe_collection.size());
620
621 for (unsigned int i = 0; i < fe_collection.size(); i++)
622 {
623 const FiniteElement<dim> &element = fe_collection[i];
624 const FE_Q_Base<dim> *fe_q =
625 dynamic_cast<const FE_Q_Base<dim> *>(&element);
626 Assert(fe_q != nullptr, ExcNotImplemented());
627
628 const FE_Bernstein<dim> fe_bernstein(fe_q->get_degree());
629
630 const unsigned int dofs_per_face = fe_q->dofs_per_face;
631 for (unsigned int f = 0; f < GeometryInfo<dim>::faces_per_cell; f++)
632 {
633 FullMatrix<double> face_interpolation_matrix(dofs_per_face,
634 dofs_per_face);
635
637 *fe_q, face_interpolation_matrix, f);
638 lagrange_to_bernstein_face[i][f].reinit(dofs_per_face);
639 lagrange_to_bernstein_face[i][f] = face_interpolation_matrix;
640 lagrange_to_bernstein_face[i][f].compute_lu_factorization();
641 }
642 }
643 }
644
645} // namespace NonMatching
646
647#include "non_matching/mesh_classifier.inst"
648
const hp::FECollection< dim, spacedim > & get_fe_collection() const
virtual void get_face_interpolation_matrix(const FiniteElement< dim, spacedim > &source, FullMatrix< double > &matrix, const unsigned int face_no=0) const override
unsigned int get_degree() const
const unsigned int dofs_per_face
Definition fe_data.h:420
unsigned int n_components() const
const unsigned int n_components
Definition function.h:162
bool is_subset_of(const IndexSet &other) const
Definition index_set.cc:691
void subtract_set(const IndexSet &other)
Definition index_set.cc:496
LocationToLevelSet location_to_level_set(const typename Triangulation< dim >::cell_iterator &cell) const
LocationToLevelSet determine_face_location_to_levelset(const typename Triangulation< dim >::active_cell_iterator &cell, const unsigned int face_index)
MeshClassifier(const DoFHandler< dim > &level_set_dof_handler, const VectorType &level_set)
AnalyticLevelSetDescription(const Function< dim > &level_set, const FiniteElement< dim > &element)
unsigned int active_fe_index(const typename Triangulation< dim >::active_cell_iterator &cell) const override
void get_local_level_set_values(const typename Triangulation< dim >::active_cell_iterator &cell, const unsigned int face_index, Vector< double > &local_levelset_values) override
void get_local_level_set_values(const typename Triangulation< dim >::active_cell_iterator &cell, const unsigned int face_index, Vector< double > &local_levelset_values) override
unsigned int active_fe_index(const typename Triangulation< dim >::active_cell_iterator &cell) const override
DiscreteLevelSetDescription(const DoFHandler< dim > &dof_handler, const VectorType &level_set)
virtual size_type size() const override
virtual void reinit(const size_type N, const bool omit_zeroing_entries=false)
unsigned int size() const
Definition collection.h:314
unsigned int n_components() const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
#define DeclExceptionMsg(Exception, defaulttext)
static ::ExceptionBase & ExcNeedsLAPACK()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
@ update_quadrature_points
Transformed quadrature points.
IndexSet extract_locally_relevant_dofs(const DoFHandler< dim, spacedim > &dof_handler)
LocationToLevelSet location_from_dof_signs(const VectorType &local_levelset_values)
void assert_locally_relevant_dofs_are_accessible(const DoFHandler< dim > &dof_handler, const VectorType &level_set)
STL namespace.