deal.II version GIT relicensing-6816-g8d70a4508a 2026-09-28 16: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
grid_tools_geometry.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) 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
13#ifndef dealii_grid_tools_geometry_h
14#define dealii_grid_tools_geometry_h
15
16#include <deal.II/base/config.h>
17
19#include <deal.II/base/point.h>
21#include <deal.II/base/tensor.h>
22
23#include <deal.II/fe/mapping.h>
24
26#include <deal.II/grid/tria.h>
27
28#include <algorithm>
29#include <array>
30#include <cmath>
31#include <numeric>
32#include <utility>
33#include <vector>
34
36
37namespace GridTools
38{
50 template <int dim, int spacedim>
51 double
53
77 template <int dim, int spacedim>
78 double
80
108 template <int dim, int spacedim>
109 double
111 const Mapping<dim, spacedim> &mapping);
112
123 template <int dim, int spacedim>
124 double
126 const Mapping<dim, spacedim> &mapping =
127 (ReferenceCells::get_hypercube<dim>()
128#ifndef _MSC_VER
129 .template get_default_linear_mapping<spacedim>()
130#else
132 spacedim>()
133#endif
134 ));
135
146 template <int dim, int spacedim>
147 double
149 const Mapping<dim, spacedim> &mapping =
150 (ReferenceCells::get_hypercube<dim>()
151#ifndef _MSC_VER
152 .template get_default_linear_mapping<spacedim>()
153#else
155 spacedim>()
156#endif
157 ));
158
178 template <int dim>
179 double
180 cell_measure(const std::vector<Point<dim>> &all_vertices,
182
194 template <int dim, int spacedim>
195 std::pair<unsigned int, double>
198
221 template <int dim, int spacedim>
222 std::pair<DerivativeForm<1, dim, spacedim>, Tensor<1, spacedim>>
224
254 template <int dim>
257 const Triangulation<dim> &triangulation,
258 const Quadrature<dim> &quadrature);
259
267 template <int dim>
268 double
270 const Triangulation<dim> &triangulation,
271 const Quadrature<dim> &quadrature);
272
286 template <int dim, int spacedim>
289
307 template <typename MeshType>
309 std::pair<
311 Point<MeshType::
312 space_dimension>> compute_bounding_box(const MeshType &mesh,
313 const std::function<bool(
314 const typename MeshType::
315 active_cell_iterator &)>
316 &predicate);
317
335 template <typename Iterator>
338 const Iterator &object,
341} // namespace GridTools
342
343#ifndef DOXYGEN
344namespace GridTools
345{
346 namespace internal
347 {
348 namespace ProjectToObject
349 {
362 struct CrossDerivative
363 {
364 const unsigned int direction_0;
365 const unsigned int direction_1;
366
367 CrossDerivative(const unsigned int d0, const unsigned int d1);
368 };
369
370 inline CrossDerivative::CrossDerivative(const unsigned int d0,
371 const unsigned int d1)
372 : direction_0(d0)
373 , direction_1(d1)
374 {}
375
376
377
382 template <typename F>
383 inline auto
384 centered_first_difference(const double center,
385 const double step,
386 const F &f) -> decltype(f(center) - f(center))
387 {
388 return (f(center + step) - f(center - step)) / (2.0 * step);
389 }
390
391
392
397 template <typename F>
398 inline auto
399 centered_second_difference(const double center,
400 const double step,
401 const F &f) -> decltype(f(center) - f(center))
402 {
403 return (f(center + step) - 2.0 * f(center) + f(center - step)) /
404 (step * step);
405 }
406
407
408
418 template <int structdim, typename F>
419 inline auto
420 cross_stencil(
421 const CrossDerivative cross_derivative,
423 const double step,
424 const F &f) -> decltype(f(center) - f(center))
425 {
427 simplex_vector[cross_derivative.direction_0] = 0.5 * step;
428 simplex_vector[cross_derivative.direction_1] = -0.5 * step;
429 return (-4.0 * f(center) - 1.0 * f(center + simplex_vector) -
430 1.0 / 3.0 * f(center - simplex_vector) +
431 16.0 / 3.0 * f(center + 0.5 * simplex_vector)) /
432 step;
433 }
434
435
436
443 template <int spacedim, int structdim, typename F>
444 inline double
445 gradient_entry(
446 const unsigned int row_n,
447 const unsigned int dependent_direction,
448 const Point<spacedim> &p0,
450 const double step,
451 const F &f)
452 {
454 dependent_direction <
456 ExcMessage("This function assumes that the last weight is a "
457 "dependent variable (and hence we cannot take its "
458 "derivative directly)."));
459 Assert(row_n != dependent_direction,
461 "We cannot differentiate with respect to the variable "
462 "that is assumed to be dependent."));
463
464 const Point<spacedim> manifold_point = f(center);
465 const Tensor<1, spacedim> stencil_value = cross_stencil<structdim>(
466 {row_n, dependent_direction}, center, step, f);
467 double entry = 0.0;
468 for (unsigned int dim_n = 0; dim_n < spacedim; ++dim_n)
469 entry +=
470 -2.0 * (p0[dim_n] - manifold_point[dim_n]) * stencil_value[dim_n];
471 return entry;
472 }
473
479 template <typename Iterator, int spacedim, int structdim>
481 project_to_d_linear_object(const Iterator &object,
482 const Point<spacedim> &trial_point)
483 {
484 // let's look at this for simplicity for a quadrilateral
485 // (structdim==2) in a space with spacedim>2 (notate trial_point by
486 // y): all points on the surface are given by
487 // x(\xi) = sum_i v_i phi_x(\xi)
488 // where v_i are the vertices of the quadrilateral, and
489 // \xi=(\xi_1,\xi_2) are the reference coordinates of the
490 // quadrilateral. so what we are trying to do is find a point x on the
491 // surface that is closest to the point y. there are different ways to
492 // solve this problem, but in the end it's a nonlinear problem and we
493 // have to find reference coordinates \xi so that J(\xi) = 1/2 ||
494 // x(\xi)-y ||^2 is minimal. x(\xi) is a function that is
495 // structdim-linear in \xi, so J(\xi) is a polynomial of degree
496 // 2*structdim that we'd like to minimize. unless structdim==1, we'll
497 // have to use a Newton method to find the answer. This leads to the
498 // following formulation of Newton steps:
499 //
500 // Given \xi_k, find \delta\xi_k so that
501 // H_k \delta\xi_k = - F_k
502 // where H_k is an approximation to the second derivatives of J at
503 // \xi_k, and F_k is the first derivative of J. We'll iterate this a
504 // number of times until the right hand side is small enough. As a
505 // stopping criterion, we terminate if ||\delta\xi||<eps.
506 //
507 // As for the Hessian, the best choice would be
508 // H_k = J''(\xi_k)
509 // but we'll opt for the simpler Gauss-Newton form
510 // H_k = A^T A
511 // i.e.
512 // (H_k)_{nm} = \sum_{i,j} v_i*v_j *
513 // \partial_n phi_i *
514 // \partial_m phi_j
515 // we start at xi=(0.5, 0.5).
517 for (unsigned int d = 0; d < structdim; ++d)
518 xi[d] = 0.5;
519
520 Point<spacedim> x_k;
521 for (const unsigned int i : GeometryInfo<structdim>::vertex_indices())
522 x_k += object->vertex(i) *
523 GeometryInfo<structdim>::d_linear_shape_function(xi, i);
524
525 do
526 {
528 for (const unsigned int i :
529 GeometryInfo<structdim>::vertex_indices())
530 F_k +=
531 (x_k - trial_point) * object->vertex(i) *
532 GeometryInfo<structdim>::d_linear_shape_function_gradient(xi,
533 i);
534
536 for (const unsigned int i :
537 GeometryInfo<structdim>::vertex_indices())
538 for (const unsigned int j :
539 GeometryInfo<structdim>::vertex_indices())
540 {
543 xi, i),
545 xi, j));
546 H_k += (object->vertex(i) * object->vertex(j)) * tmp;
547 }
548
549 const Tensor<1, structdim> delta_xi = -invert(H_k) * F_k;
550 xi += delta_xi;
551
552 x_k = Point<spacedim>();
553 for (const unsigned int i :
554 GeometryInfo<structdim>::vertex_indices())
555 x_k += object->vertex(i) *
556 GeometryInfo<structdim>::d_linear_shape_function(xi, i);
557
558 if (delta_xi.norm() < 1e-7)
559 break;
560 }
561 while (true);
562
563 return x_k;
564 }
565 } // namespace ProjectToObject
566
567 // We hit an internal compiler error in ICC 15 if we define this as a lambda
568 // inside the project_to_object function below.
569 template <int structdim>
570 inline bool
571 weights_are_ok(
573 {
574 // clang has trouble figuring out structdim here, so define it
575 // again:
576 static const std::size_t n_vertices_per_cell =
578 n_independent_components;
579 std::array<double, n_vertices_per_cell> copied_weights;
580 for (unsigned int i = 0; i < n_vertices_per_cell; ++i)
581 {
582 copied_weights[i] = v[i];
583 if (v[i] < 0.0 || v[i] > 1.0)
584 return false;
585 }
586
587 // check the sum: try to avoid some roundoff errors by summing in order
588 std::sort(copied_weights.begin(), copied_weights.end());
589 const double sum =
590 std::accumulate(copied_weights.begin(), copied_weights.end(), 0.0);
591 return std::abs(sum - 1.0) < 1e-10; // same tolerance used in manifold.cc
592 }
593 } // namespace internal
594
595
596
597 template <typename Iterator>
600 const Iterator &object,
602 {
603 const int spacedim = Iterator::AccessorType::space_dimension;
604 const int structdim = Iterator::AccessorType::structure_dimension;
605
606 Point<spacedim> projected_point = trial_point;
607
608 if (structdim >= spacedim)
609 return projected_point;
610 else if (structdim == 1 || structdim == 2)
611 {
612 using namespace internal::ProjectToObject;
613 // Try to use the special flat algorithm for quads (this is better
614 // than the general algorithm in 3d). This does not take into account
615 // whether projected_point is outside the quad, but we optimize along
616 // lines below anyway:
617 const int dim = Iterator::AccessorType::dimension;
618 const Manifold<dim, spacedim> &manifold = object->get_manifold();
619 if (structdim == 2 && dynamic_cast<const FlatManifold<dim, spacedim> *>(
620 &manifold) != nullptr)
621 {
622 projected_point =
623 project_to_d_linear_object<Iterator, spacedim, structdim>(
624 object, trial_point);
625 }
626 else
627 {
628 // We want to find a point on the convex hull (defined by the
629 // vertices of the object and the manifold description) that is
630 // relatively close to the trial point. This has a few issues:
631 //
632 // 1. For a general convex hull we are not guaranteed that a unique
633 // minimum exists.
634 // 2. The independent variables in the optimization process are the
635 // weights given to Manifold::get_new_point, which must sum to 1,
636 // so we cannot use standard finite differences to approximate a
637 // gradient.
638 //
639 // There is not much we can do about 1., but for 2. we can derive
640 // finite difference stencils that work on a structdim-dimensional
641 // simplex and rewrite the optimization problem to use those
642 // instead. Consider the structdim 2 case and let
643 //
644 // F(c0, c1, c2, c3) = Manifold::get_new_point(vertices, {c0, c1,
645 // c2, c3})
646 //
647 // where {c0, c1, c2, c3} are the weights for the four vertices on
648 // the quadrilateral. We seek to minimize the Euclidean distance
649 // between F(...) and trial_point. We can solve for c3 in terms of
650 // the other weights and get, for one coordinate direction
651 //
652 // d/dc0 ((x0 - F(c0, c1, c2, 1 - c0 - c1 - c2))^2)
653 // = -2(x0 - F(...)) (d/dc0 F(...) - d/dc3 F(...))
654 //
655 // where we substitute back in for c3 after taking the
656 // derivative. We can compute a stencil for the cross derivative
657 // d/dc0 - d/dc3: this is exactly what cross_stencil approximates
658 // (and gradient_entry computes the sum over the independent
659 // variables). Below, we somewhat arbitrarily pick the last
660 // component as the dependent one.
661 //
662 // Since we can now calculate derivatives of the objective
663 // function we can use gradient descent to minimize it.
664 //
665 // Of course, this is much simpler in the structdim = 1 case (we
666 // could rewrite the projection as a 1d optimization problem), but
667 // to reduce the potential for bugs we use the same code in both
668 // cases.
669 const double step_size = object->diameter() / 64.0;
670
671 constexpr unsigned int n_vertices_per_cell =
673
674 std::array<Point<spacedim>, n_vertices_per_cell> vertices;
675 for (unsigned int vertex_n = 0; vertex_n < n_vertices_per_cell;
676 ++vertex_n)
677 vertices[vertex_n] = object->vertex(vertex_n);
678
679 auto get_point_from_weights =
680 [&](const Tensor<1, n_vertices_per_cell> &weights)
681 -> Point<spacedim> {
682 return object->get_manifold().get_new_point(
683 make_array_view(vertices.begin(), vertices.end()),
684 make_array_view(weights.begin_raw(), weights.end_raw()));
685 };
686
687 // pick the initial weights as (normalized) inverse distances from
688 // the trial point:
689 Tensor<1, n_vertices_per_cell> guess_weights;
690 double guess_weights_sum = 0.0;
691 for (unsigned int vertex_n = 0; vertex_n < n_vertices_per_cell;
692 ++vertex_n)
693 {
694 const double distance =
695 vertices[vertex_n].distance(trial_point);
696 if (distance == 0.0)
697 {
698 guess_weights = 0.0;
699 guess_weights[vertex_n] = 1.0;
700 guess_weights_sum = 1.0;
701 break;
702 }
703 else
704 {
705 guess_weights[vertex_n] = 1.0 / distance;
706 guess_weights_sum += guess_weights[vertex_n];
707 }
708 }
709 guess_weights /= guess_weights_sum;
710 Assert(internal::weights_are_ok<structdim>(guess_weights),
712
713 // The optimization algorithm consists of two parts:
714 //
715 // 1. An outer loop where we apply the gradient descent algorithm.
716 // 2. An inner loop where we do a line search to find the optimal
717 // length of the step one should take in the gradient direction.
718 //
719 for (unsigned int outer_n = 0; outer_n < 40; ++outer_n)
720 {
721 const unsigned int dependent_direction =
722 n_vertices_per_cell - 1;
723 Tensor<1, n_vertices_per_cell> current_gradient;
724 for (unsigned int row_n = 0; row_n < n_vertices_per_cell;
725 ++row_n)
726 {
727 if (row_n != dependent_direction)
728 {
729 current_gradient[row_n] =
730 gradient_entry<spacedim, structdim>(
731 row_n,
732 dependent_direction,
733 trial_point,
734 guess_weights,
735 step_size,
736 get_point_from_weights);
737
738 current_gradient[dependent_direction] -=
739 current_gradient[row_n];
740 }
741 }
742
743 // We need to travel in the -gradient direction, as noted
744 // above, but we may not want to take a full step in that
745 // direction; instead, guess that we will go -0.5*gradient and
746 // do quasi-Newton iteration to pick the best multiplier. The
747 // goal is to find a scalar alpha such that
748 //
749 // F(x - alpha g)
750 //
751 // is minimized, where g is the gradient and F is the
752 // objective function. To find the optimal value we find roots
753 // of the derivative of the objective function with respect to
754 // alpha by Newton iteration, where we approximate the first
755 // and second derivatives of F(x - alpha g) with centered
756 // finite differences.
757 double gradient_weight = -0.5;
758 auto gradient_weight_objective_function =
759 [&](const double gradient_weight_guess) -> double {
760 return (trial_point -
761 get_point_from_weights(guess_weights +
762 gradient_weight_guess *
763 current_gradient))
764 .norm_square();
765 };
766
767 for (unsigned int inner_n = 0; inner_n < 10; ++inner_n)
768 {
769 const double update_numerator = centered_first_difference(
770 gradient_weight,
771 step_size,
772 gradient_weight_objective_function);
773 const double update_denominator =
774 centered_second_difference(
775 gradient_weight,
776 step_size,
777 gradient_weight_objective_function);
778
779 // avoid division by zero. Note that we limit the gradient
780 // weight below
781 if (std::abs(update_denominator) == 0.0)
782 break;
783 gradient_weight =
784 gradient_weight - update_numerator / update_denominator;
785
786 // Put a fairly lenient bound on the largest possible
787 // gradient (things tend to be locally flat, so the gradient
788 // itself is usually small)
789 if (std::abs(gradient_weight) > 10)
790 {
791 gradient_weight = -10.0;
792 break;
793 }
794 }
795
796 // It only makes sense to take convex combinations with weights
797 // between zero and one. If the update takes us outside of this
798 // region then rescale the update to stay within the region and
799 // try again
800 Tensor<1, n_vertices_per_cell> tentative_weights =
801 guess_weights + gradient_weight * current_gradient;
802
803 double new_gradient_weight = gradient_weight;
804 for (unsigned int iteration_count = 0; iteration_count < 40;
805 ++iteration_count)
806 {
807 if (internal::weights_are_ok<structdim>(tentative_weights))
808 break;
809
810 for (unsigned int i = 0; i < n_vertices_per_cell; ++i)
811 {
812 if (tentative_weights[i] < 0.0)
813 {
814 tentative_weights -=
815 (tentative_weights[i] / current_gradient[i]) *
816 current_gradient;
817 }
818 if (tentative_weights[i] < 0.0 ||
819 1.0 < tentative_weights[i])
820 {
821 new_gradient_weight /= 2.0;
822 tentative_weights =
823 guess_weights +
824 new_gradient_weight * current_gradient;
825 }
826 }
827 }
828
829 // the update might still send us outside the valid region, so
830 // check again and quit if the update is still not valid
831 if (!internal::weights_are_ok<structdim>(tentative_weights))
832 break;
833
834 // if we cannot get closer by traveling in the gradient
835 // direction then quit
836 if (get_point_from_weights(tentative_weights)
837 .distance(trial_point) <
838 get_point_from_weights(guess_weights).distance(trial_point))
839 guess_weights = tentative_weights;
840 else
841 break;
842 Assert(internal::weights_are_ok<structdim>(guess_weights),
844 }
845 Assert(internal::weights_are_ok<structdim>(guess_weights),
847 projected_point = get_point_from_weights(guess_weights);
848 }
849
850 // if structdim == 2 and the optimal point is not on the interior then
851 // we may be able to get a more accurate result by projecting onto the
852 // lines.
853 if (structdim == 2)
854 {
855 std::array<Point<spacedim>, GeometryInfo<structdim>::lines_per_cell>
856 line_projections;
857 for (unsigned int line_n = 0;
858 line_n < GeometryInfo<structdim>::lines_per_cell;
859 ++line_n)
860 {
861 line_projections[line_n] =
862 project_to_object(object->line(line_n), trial_point);
863 }
864 std::sort(line_projections.begin(),
865 line_projections.end(),
866 [&](const Point<spacedim> &a, const Point<spacedim> &b) {
867 return a.distance(trial_point) <
868 b.distance(trial_point);
869 });
870 if (line_projections[0].distance(trial_point) <
871 projected_point.distance(trial_point))
872 projected_point = line_projections[0];
873 }
874 }
875 else
876 {
878 return projected_point;
879 }
880
881 return projected_point;
882 }
883} // namespace GridTools
884#endif // DOXYGEN
885
887
888#endif
*  *  for(const auto &cell :triangulation.active_cell_iterators())
ArrayView< std::remove_reference_t< typename std::iterator_traits< Iterator >::reference >, MemorySpaceType > make_array_view(const Iterator begin, const Iterator end)
Abstract base class for mapping classes.
Definition mapping.h:318
Definition point.h:111
numbers::NumberTraits< Number >::real_type distance(const Point< dim, Number > &p) const
numbers::NumberTraits< Number >::real_type norm() const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_CXX20_REQUIRES(condition)
Definition config.h:249
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_NOT_IMPLEMENTED()
unsigned int vertex_indices[2]
#define Assert(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
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)
Point< Iterator::AccessorType::space_dimension > project_to_object(const Iterator &object, const Point< Iterator::AccessorType::space_dimension > &trial_point)
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)
double cell_measure(const std::vector< Point< dim > > &all_vertices, const ArrayView< const unsigned int > &vertex_indices)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
*  *  *  *  std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters   const
T sum(const T &t, const MPI_Comm mpi_communicator)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
constexpr SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &)
constexpr SymmetricTensor< 4, dim, Number > outer_product(const SymmetricTensor< 2, dim, Number > &t1, const SymmetricTensor< 2, dim, Number > &t2)