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
grid_tools_geometry.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) 2023 - 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
15#include <deal.II/base/mpi.h>
17
19
24
26#include <deal.II/grid/tria.h>
27
29
31
32#include <functional>
33
35
36
37namespace GridTools
38{
39 template <int dim, int spacedim>
40 double
42 {
43 // we can't deal with distributed meshes since we don't have all
44 // vertices locally. there is one exception, however: if the mesh has
45 // never been refined. the way to test this is not to ask
46 // tria.n_levels()==1, since this is something that can happen on one
47 // processor without being true on all. however, we can ask for the
48 // global number of active cells and use that
49 if constexpr (running_in_debug_mode())
50 {
51 if (const auto *p_tria = dynamic_cast<
53 &tria))
54 Assert(p_tria->n_global_active_cells() == tria.n_cells(0),
56 }
57
58 // the algorithm used simply traverses all cells and picks out the
59 // boundary vertices. it may or may not be faster to simply get all
60 // vectors, don't mark boundary vertices, and compute the distances
61 // thereof, but at least as the mesh is refined, it seems better to
62 // first mark boundary nodes, as marking is O(N) in the number of
63 // cells/vertices, while computing the maximal distance is O(N*N)
64 const std::vector<Point<spacedim>> &vertices = tria.get_vertices();
65 std::vector<bool> boundary_vertices(vertices.size(), false);
66
68 tria.begin_active();
70 tria.end();
71 for (; cell != endc; ++cell)
72 for (const unsigned int face : cell->face_indices())
73 if (cell->face(face)->at_boundary())
74 for (unsigned int i = 0; i < cell->face(face)->n_vertices(); ++i)
75 boundary_vertices[cell->face(face)->vertex_index(i)] = true;
76
77 // now traverse the list of boundary vertices and check distances.
78 // since distances are symmetric, we only have to check one half
79 double max_distance_sqr = 0;
80 std::vector<bool>::const_iterator pi = boundary_vertices.begin();
81 const unsigned int N = boundary_vertices.size();
82 for (unsigned int i = 0; i < N; ++i, ++pi)
83 {
84 std::vector<bool>::const_iterator pj = pi + 1;
85 for (unsigned int j = i + 1; j < N; ++j, ++pj)
86 if ((*pi == true) && (*pj == true) &&
87 ((vertices[i] - vertices[j]).norm_square() > max_distance_sqr))
88 max_distance_sqr = (vertices[i] - vertices[j]).norm_square();
89 }
90
91 return std::sqrt(max_distance_sqr);
92 }
93
94
95
96 template <int dim, int spacedim>
97 double
99 {
100 Assert(triangulation.get_reference_cells().size() == 1,
102 const ReferenceCell reference_cell = triangulation.get_reference_cells()[0];
103 return volume(
104 triangulation,
105 reference_cell.template get_default_linear_mapping<spacedim>());
106 }
107
108
109
110 template <int dim, int spacedim>
111 double
113 const Mapping<dim, spacedim> &mapping)
114 {
115 // get the degree of the mapping if possible. if not, just assume 1
116 unsigned int mapping_degree = 1;
117 if (const auto *p = dynamic_cast<const MappingQ<dim, spacedim> *>(&mapping))
118 mapping_degree = p->get_degree();
119 else if (const auto *p =
120 dynamic_cast<const MappingFE<dim, spacedim> *>(&mapping))
121 mapping_degree = p->get_degree();
122
123 // then initialize an appropriate quadrature formula
124 Assert(triangulation.get_reference_cells().size() == 1,
126 const ReferenceCell reference_cell = triangulation.get_reference_cells()[0];
127 const Quadrature<dim> quadrature_formula =
128 reference_cell.get_gauss_type_quadrature(mapping_degree + 1);
129 const unsigned int n_q_points = quadrature_formula.size();
130
131 // we really want the JxW values from the FEValues object, but it
132 // wants a finite element. create a cheap element as a dummy
133 // element
134 FE_Nothing<dim, spacedim> dummy_fe(reference_cell);
135 FEValues<dim, spacedim> fe_values(mapping,
136 dummy_fe,
137 quadrature_formula,
139
140 double local_volume = 0;
141
142 // compute the integral quantities by quadrature
143 for (const auto &cell : triangulation.active_cell_iterators())
144 if (cell->is_locally_owned())
145 {
146 fe_values.reinit(cell);
147 for (unsigned int q = 0; q < n_q_points; ++q)
148 local_volume += fe_values.JxW(q);
149 }
150
151 const double global_volume =
152 Utilities::MPI::sum(local_volume, triangulation.get_mpi_communicator());
153
154 return global_volume;
155 }
156
157
158
159 template <int dim, int spacedim>
160 std::pair<unsigned int, double>
163 {
164 double max_ratio = 1;
165 unsigned int index = 0;
166
167 for (unsigned int i = 0; i < dim; ++i)
168 for (unsigned int j = i + 1; j < dim; ++j)
169 {
170 unsigned int ax = i % dim;
171 unsigned int next_ax = j % dim;
172
173 double ratio =
174 cell->extent_in_direction(ax) / cell->extent_in_direction(next_ax);
175
176 if (ratio > max_ratio)
177 {
178 max_ratio = ratio;
179 index = ax;
180 }
181 else if (1.0 / ratio > max_ratio)
182 {
183 max_ratio = 1.0 / ratio;
184 index = next_ax;
185 }
186 }
187 return std::make_pair(index, max_ratio);
188 }
189
190
191
192 namespace
193 {
208 template <int dim>
209 struct TransformR2UAffine
210 {
211 static const double KA[GeometryInfo<dim>::vertices_per_cell][dim];
213 };
214
215
216 /*
217 Octave code:
218 M=[0 1; 1 1];
219 K1 = transpose(M) * inverse (M*transpose(M));
220 printf ("{%f, %f},\n", K1' );
221 */
222 template <>
223 const double TransformR2UAffine<1>::KA[GeometryInfo<1>::vertices_per_cell]
224 [1] = {{-1.000000}, {1.000000}};
225
226 template <>
227 const double TransformR2UAffine<1>::Kb[GeometryInfo<1>::vertices_per_cell] =
228 {1.000000, 0.000000};
229
230
231 /*
232 Octave code:
233 M=[0 1 0 1;0 0 1 1;1 1 1 1];
234 K2 = transpose(M) * inverse (M*transpose(M));
235 printf ("{%f, %f, %f},\n", K2' );
236 */
237 template <>
238 const double TransformR2UAffine<2>::KA[GeometryInfo<2>::vertices_per_cell]
239 [2] = {{-0.500000, -0.500000},
240 {0.500000, -0.500000},
241 {-0.500000, 0.500000},
242 {0.500000, 0.500000}};
243
244 /*
245 Octave code:
246 M=[0 1 0 1 0 1 0 1;0 0 1 1 0 0 1 1; 0 0 0 0 1 1 1 1; 1 1 1 1 1 1 1 1];
247 K3 = transpose(M) * inverse (M*transpose(M))
248 printf ("{%f, %f, %f, %f},\n", K3' );
249 */
250 template <>
251 const double TransformR2UAffine<2>::Kb[GeometryInfo<2>::vertices_per_cell] =
252 {0.750000, 0.250000, 0.250000, -0.250000};
253
254
255 template <>
256 const double TransformR2UAffine<3>::KA[GeometryInfo<3>::vertices_per_cell]
257 [3] = {
258 {-0.250000, -0.250000, -0.250000},
259 {0.250000, -0.250000, -0.250000},
260 {-0.250000, 0.250000, -0.250000},
261 {0.250000, 0.250000, -0.250000},
262 {-0.250000, -0.250000, 0.250000},
263 {0.250000, -0.250000, 0.250000},
264 {-0.250000, 0.250000, 0.250000},
265 {0.250000, 0.250000, 0.250000}
266
267 };
268
269
270 template <>
271 const double TransformR2UAffine<3>::Kb[GeometryInfo<3>::vertices_per_cell] =
272 {0.500000,
273 0.250000,
274 0.250000,
275 0.000000,
276 0.250000,
277 0.000000,
278 0.000000,
279 -0.250000};
280 } // namespace
281
282
283
284 template <int dim, int spacedim>
285 std::pair<DerivativeForm<1, dim, spacedim>, Tensor<1, spacedim>>
287 {
289
290 // A = vertex * KA
292
293 for (unsigned int d = 0; d < spacedim; ++d)
294 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_cell; ++v)
295 for (unsigned int e = 0; e < dim; ++e)
296 A[d][e] += vertices[v][d] * TransformR2UAffine<dim>::KA[v][e];
297
298 // b = vertex * Kb
300 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_cell; ++v)
301 b += vertices[v] * TransformR2UAffine<dim>::Kb[v];
302
303 return std::make_pair(A, b);
304 }
305
306
307
308 template <int dim>
311 const Triangulation<dim> &triangulation,
312 const Quadrature<dim> &quadrature)
313 {
315 FEValues<dim> fe_values(mapping, fe, quadrature, update_jacobians);
316
317 Vector<double> aspect_ratio_vector(triangulation.n_active_cells());
318
319 // loop over cells of processor
320 for (const auto &cell : triangulation.active_cell_iterators())
321 {
322 if (cell->is_locally_owned())
323 {
324 double aspect_ratio_cell = 0.0;
325
326 fe_values.reinit(cell);
327
328 // loop over quadrature points
329 for (unsigned int q = 0; q < quadrature.size(); ++q)
330 {
331 const Tensor<2, dim, double> jacobian =
332 Tensor<2, dim, double>(fe_values.jacobian(q));
333
334 // We intentionally do not want to throw an exception in case of
335 // inverted elements since this is not the task of this
336 // function. Instead, inf is written into the vector in case of
337 // inverted elements.
338 if (determinant(jacobian) <= 0)
339 {
340 aspect_ratio_cell = std::numeric_limits<double>::infinity();
341 }
342 else
343 {
345 for (unsigned int i = 0; i < dim; ++i)
346 for (unsigned int j = 0; j < dim; ++j)
347 J(i, j) = jacobian[i][j];
348
349 J.compute_svd();
350
351 const double max_sv = J.singular_value(0);
352 const double min_sv = J.singular_value(dim - 1);
353 const double ar = max_sv / min_sv;
354
355 // Take the max between the previous and the current
356 // aspect ratio value; if we had previously encountered
357 // an inverted cell, we will have placed an infinity
358 // in the aspect_ratio_cell variable, and that value
359 // will survive this max operation.
360 aspect_ratio_cell = std::max(aspect_ratio_cell, ar);
361 }
362 }
363
364 // fill vector
365 aspect_ratio_vector(cell->active_cell_index()) = aspect_ratio_cell;
366 }
367 }
368
369 return aspect_ratio_vector;
370 }
371
372
373
374 template <int dim>
375 double
377 const Triangulation<dim> &triangulation,
378 const Quadrature<dim> &quadrature)
379 {
380 Vector<double> aspect_ratio_vector =
381 compute_aspect_ratio_of_cells(mapping, triangulation, quadrature);
382
383 return VectorTools::compute_global_error(triangulation,
384 aspect_ratio_vector,
386 }
387
388
389
390 template <int dim, int spacedim>
393 {
394 using iterator =
396 const auto predicate = [](const iterator &) { return true; };
397
399 tria, std::function<bool(const iterator &)>(predicate));
400 }
401
402
403
404 template <int dim, int spacedim>
405 double
407 const Mapping<dim, spacedim> &mapping)
408 {
409 double min_diameter = std::numeric_limits<double>::max();
410 for (const auto &cell : triangulation.active_cell_iterators())
411 if (!cell->is_artificial())
412 min_diameter = std::min(min_diameter, cell->diameter(mapping));
413
414 const double global_min_diameter =
415 Utilities::MPI::min(min_diameter, triangulation.get_mpi_communicator());
416 return global_min_diameter;
417 }
418
419
420
421 template <int dim, int spacedim>
422 double
424 const Mapping<dim, spacedim> &mapping)
425 {
426 double max_diameter = 0.;
427 for (const auto &cell : triangulation.active_cell_iterators())
428 if (!cell->is_artificial())
429 max_diameter = std::max(max_diameter, cell->diameter(mapping));
430
431 const double global_max_diameter =
432 Utilities::MPI::max(max_diameter, triangulation.get_mpi_communicator());
433 return global_max_diameter;
434 }
435} /* namespace GridTools */
436
437
438// explicit instantiations
439#include "grid/grid_tools_geometry.inst"
440
*  *  iterator()=default
const DerivativeForm< 1, dim, spacedim > & jacobian(const unsigned int q_point) const
double JxW(const unsigned int q_point) const
void reinit(const TriaIterator< DoFCellAccessor< dim, spacedim, level_dof_access > > &cell)
Abstract base class for mapping classes.
Definition mapping.h:318
Definition point.h:111
unsigned int size() const
virtual MPI_Comm get_mpi_communicator() const
unsigned int n_active_cells() const
const std::vector< Point< spacedim > > & get_vertices() const
cell_iterator end() const
unsigned int n_cells() const
const std::vector< ReferenceCell< dim > > & get_reference_cells() const
active_cell_iterator begin_active(const unsigned int level=0) 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 const double KA[GeometryInfo< dim >::vertices_per_cell][dim]
static const double Kb[GeometryInfo< dim >::vertices_per_cell]
IteratorRange< active_cell_iterator > active_cell_iterators() const
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
@ update_JxW_values
Transformed quadrature weights.
@ update_jacobians
Volume element.
double maximal_cell_diameter(const Triangulation< dim, spacedim > &triangulation, const Mapping< dim, spacedim > &mapping=(ReferenceCells::get_hypercube< dim >() .template get_default_linear_mapping< spacedim >()))
std::pair< DerivativeForm< 1, dim, spacedim >, Tensor< 1, spacedim > > affine_cell_approximation(const ArrayView< const Point< spacedim > > &vertices)
Vector< double > compute_aspect_ratio_of_cells(const Mapping< dim > &mapping, const Triangulation< dim > &triangulation, const Quadrature< dim > &quadrature)
double compute_maximum_aspect_ratio(const Mapping< dim > &mapping, const Triangulation< dim > &triangulation, const Quadrature< dim > &quadrature)
double minimal_cell_diameter(const Triangulation< dim, spacedim > &triangulation, const Mapping< dim, spacedim > &mapping=(ReferenceCells::get_hypercube< dim >() .template get_default_linear_mapping< spacedim >()))
double volume(const Triangulation< dim, spacedim > &tria)
double diameter(const Triangulation< dim, spacedim > &tria)
BoundingBox< spacedim > compute_bounding_box(const Triangulation< dim, spacedim > &triangulation)
std::pair< unsigned int, double > get_longest_direction(typename Triangulation< dim, spacedim >::active_cell_iterator cell)
T sum(const T &t, const MPI_Comm mpi_communicator)
T max(const T &t, const MPI_Comm mpi_communicator)
T min(const T &t, const MPI_Comm mpi_communicator)
double compute_global_error(const Triangulation< dim, spacedim > &tria, const InVector &cellwise_error, const NormType &norm, const double exponent=2.)
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
constexpr Number determinant(const SymmetricTensor< 2, dim, Number > &)