deal.II version 9.7.0
\(\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
manifold_lib.cc
Go to the documentation of this file.
1// ------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4// Copyright (C) 2014 - 2025 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Part of the source code is dual licensed under Apache-2.0 WITH
9// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10// governing the source code and code contributions can be found in
11// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12//
13// ------------------------------------------------------------------------
14
15#include <deal.II/base/table.h>
16#include <deal.II/base/tensor.h>
17
18#include <deal.II/fe/mapping.h>
20
22#include <deal.II/grid/tria.h>
25
26#include <deal.II/lac/vector.h>
27
29
30#include <boost/container/small_vector.hpp>
31
32#include <cmath>
33#include <limits>
34#include <memory>
35
37
38
39namespace internal
40{
41 // The pull_back function fails regularly in the compute_chart_points
42 // method, and, instead of throwing an exception, returns a point outside
43 // the unit cell. The individual coordinates of that point are given by the
44 // value below.
45 static constexpr double invalid_pull_back_coordinate = 20.0;
46
47 // Rotate a given unit vector u around the axis dir
48 // where the angle is given by the length of dir.
49 // This is the exponential map for a sphere.
52 {
53 const double theta = dir.norm();
54 if (theta < 1.e-10)
55 {
56 return u;
57 }
58 else
59 {
60 const Tensor<1, 3> dirUnit = dir / theta;
61 const Tensor<1, 3> tmp =
62 std::cos(theta) * u + std::sin(theta) * dirUnit;
63 return tmp / tmp.norm();
64 }
65 }
66
67 // Returns the direction to go from v to u
68 // projected to the plane perpendicular to the unit vector v.
69 // This one is more stable when u and v are nearly equal.
72 {
73 Tensor<1, 3> ans = u - v;
74 ans -= (ans * v) * v;
75 return ans; // ans = (u-v) - ((u-v)*v)*v
76 }
77
78 // helper function to compute a vector orthogonal to a given one.
79 // does nothing unless spacedim == 3.
80 template <int spacedim>
83 bool /*normalize*/ = false)
84 {
85 return {};
86 }
87
89 compute_normal(const Tensor<1, 3> &vector, bool normalize = false)
90 {
91 Assert(vector.norm_square() != 0.,
92 ExcMessage("The direction parameter must not be zero!"));
93 Point<3> normal;
94 if (std::abs(vector[0]) >= std::abs(vector[1]) &&
95 std::abs(vector[0]) >= std::abs(vector[2]))
96 {
97 normal[1] = -1.;
98 normal[2] = -1.;
99 normal[0] = (vector[1] + vector[2]) / vector[0];
100 }
101 else if (std::abs(vector[1]) >= std::abs(vector[0]) &&
102 std::abs(vector[1]) >= std::abs(vector[2]))
103 {
104 normal[0] = -1.;
105 normal[2] = -1.;
106 normal[1] = (vector[0] + vector[2]) / vector[1];
107 }
108 else
109 {
110 normal[0] = -1.;
111 normal[1] = -1.;
112 normal[2] = (vector[0] + vector[1]) / vector[2];
113 }
114 if (normalize)
115 normal /= normal.norm();
116 return normal;
117 }
118} // namespace internal
119
120
121
122// ============================================================
123// PolarManifold
124// ============================================================
125
127template <int dim, int spacedim>
129 : ChartManifold<dim, spacedim, spacedim>(
130 PolarManifold<dim, spacedim>::get_periodicity())
131 , center(center)
133{}
135
136
137
138template <int dim, int spacedim>
139std::unique_ptr<Manifold<dim, spacedim>>
141{
142 return std::make_unique<PolarManifold<dim, spacedim>>(*this);
143}
144
145
146
147template <int dim, int spacedim>
150{
151 Tensor<1, spacedim> periodicity;
152 // In two dimensions, theta is periodic.
153 // In three dimensions things are a little more complicated, since the only
154 // variable that is truly periodic is phi, while theta should be bounded
155 // between 0 and pi. There is currently no way to enforce this, so here we
156 // only fix periodicity for the last variable, corresponding to theta in 2d
157 // and phi in 3d.
158 periodicity[spacedim - 1] = 2 * numbers::PI;
159 return periodicity;
160}
161
162
163
164template <int dim, int spacedim>
167 const Point<spacedim> &spherical_point) const
168{
169 Assert(spherical_point[0] >= 0.0,
170 ExcMessage("Negative radius for given point."));
171 const double rho = spherical_point[0];
172 const double theta = spherical_point[1];
173
175 if (rho > 1e-10)
176 switch (spacedim)
177 {
178 case 2:
179 p[0] = rho * std::cos(theta);
180 p[1] = rho * std::sin(theta);
181 break;
182 case 3:
183 {
184 const double phi = spherical_point[2];
185 p[0] = rho * std::sin(theta) * std::cos(phi);
186 p[1] = rho * std::sin(theta) * std::sin(phi);
187 p[2] = rho * std::cos(theta);
188 break;
189 }
190 default:
192 }
193 return p + p_center;
194}
195
196
197
198template <int dim, int spacedim>
201 const Point<spacedim> &space_point) const
202{
203 const Tensor<1, spacedim> R = space_point - p_center;
204 const double rho = R.norm();
205
207 p[0] = rho;
208
209 switch (spacedim)
210 {
211 case 2:
212 {
213 p[1] = std::atan2(R[1], R[0]);
214 if (p[1] < 0)
215 p[1] += 2 * numbers::PI;
216 break;
217 }
218
219 case 3:
220 {
221 const double z = R[2];
222 p[2] = std::atan2(R[1], R[0]); // phi
223 if (p[2] < 0)
224 p[2] += 2 * numbers::PI; // phi is periodic
225 p[1] = std::atan2(std::sqrt(R[0] * R[0] + R[1] * R[1]), z); // theta
226 break;
227 }
228
229 default:
231 }
232 return p;
233}
234
235
236
237template <int dim, int spacedim>
240 const Point<spacedim> &spherical_point) const
241{
242 Assert(spherical_point[0] >= 0.0,
243 ExcMessage("Negative radius for given point."));
244 const double rho = spherical_point[0];
245 const double theta = spherical_point[1];
246
248 if (rho > 1e-10)
249 switch (spacedim)
250 {
251 case 2:
252 {
253 DX[0][0] = std::cos(theta);
254 DX[0][1] = -rho * std::sin(theta);
255 DX[1][0] = std::sin(theta);
256 DX[1][1] = rho * std::cos(theta);
257 break;
258 }
259
260 case 3:
261 {
262 const double phi = spherical_point[2];
263 DX[0][0] = std::sin(theta) * std::cos(phi);
264 DX[0][1] = rho * std::cos(theta) * std::cos(phi);
265 DX[0][2] = -rho * std::sin(theta) * std::sin(phi);
266
267 DX[1][0] = std::sin(theta) * std::sin(phi);
268 DX[1][1] = rho * std::cos(theta) * std::sin(phi);
269 DX[1][2] = rho * std::sin(theta) * std::cos(phi);
270
271 DX[2][0] = std::cos(theta);
272 DX[2][1] = -rho * std::sin(theta);
273 DX[2][2] = 0;
274 break;
275 }
276
277 default:
279 }
280 return DX;
281}
282
283
284
285namespace
286{
287 template <int dim, int spacedim>
288 bool
289 spherical_face_is_horizontal(
291 const Point<spacedim> &manifold_center)
292 {
293 // We test whether a face is horizontal by checking that the vertices
294 // all have roughly the same distance from the center: If the
295 // maximum deviation for the distances from the vertices to the
296 // center is less than 1.e-5 of the distance between vertices (as
297 // measured by the minimum distance from any of the other vertices
298 // to the first vertex), then we call this a horizontal face.
299 constexpr unsigned int n_vertices =
301 std::array<double, n_vertices> sqr_distances_to_center;
302 std::array<double, n_vertices - 1> sqr_distances_to_first_vertex;
303 sqr_distances_to_center[0] =
304 (face->vertex(0) - manifold_center).norm_square();
305 for (unsigned int i = 1; i < n_vertices; ++i)
306 {
307 sqr_distances_to_center[i] =
308 (face->vertex(i) - manifold_center).norm_square();
309 sqr_distances_to_first_vertex[i - 1] =
310 (face->vertex(i) - face->vertex(0)).norm_square();
311 }
312 const auto minmax_sqr_distance =
313 std::minmax_element(sqr_distances_to_center.begin(),
314 sqr_distances_to_center.end());
315 const auto min_sqr_distance_to_first_vertex =
316 std::min_element(sqr_distances_to_first_vertex.begin(),
317 sqr_distances_to_first_vertex.end());
318
319 return (*minmax_sqr_distance.second - *minmax_sqr_distance.first <
320 1.e-10 * *min_sqr_distance_to_first_vertex);
321 }
322} // namespace
323
324
325
326template <int dim, int spacedim>
330 const Point<spacedim> &p) const
331{
332 // Let us first test whether we are on a "horizontal" face
333 // (tangential to the sphere). In this case, the normal vector is
334 // easy to compute since it is proportional to the vector from the
335 // center to the point 'p'.
336 if (spherical_face_is_horizontal<dim, spacedim>(face, p_center))
337 {
338 // So, if this is a "horizontal" face, then just compute the normal
339 // vector as the one from the center to the point 'p', adequately
340 // scaled.
341 const Tensor<1, spacedim> unnormalized_spherical_normal = p - p_center;
342 const Tensor<1, spacedim> normalized_spherical_normal =
343 unnormalized_spherical_normal / unnormalized_spherical_normal.norm();
344 return normalized_spherical_normal;
345 }
346 else
347 // If it is not a horizontal face, just use the machinery of the
348 // base class.
350
351 return Tensor<1, spacedim>();
352}
353
354
355
356// ============================================================
357// SphericalManifold
358// ============================================================
359
361template <int dim, int spacedim>
369
370
371
372template <int dim, int spacedim>
373std::unique_ptr<Manifold<dim, spacedim>>
375{
376 return std::make_unique<SphericalManifold<dim, spacedim>>(*this);
377}
378
379
380
381template <int dim, int spacedim>
384 const Point<spacedim> &p1,
385 const Point<spacedim> &p2,
386 const double w) const
387{
388 const double tol = 1e-10;
389
390 if ((p1 - p2).norm_square() < tol * tol || std::abs(w) < tol)
391 return p1;
392 else if (std::abs(w - 1.0) < tol)
393 return p2;
394
395 // If the points are one dimensional then there is no need for anything but
396 // a linear combination.
397 if (spacedim == 1)
398 return Point<spacedim>(w * p2 + (1 - w) * p1);
399
400 const Tensor<1, spacedim> v1 = p1 - p_center;
401 const Tensor<1, spacedim> v2 = p2 - p_center;
402 const double r1 = v1.norm();
403 const double r2 = v2.norm();
404
405 Assert(r1 > tol && r2 > tol,
406 ExcMessage("p1 and p2 cannot coincide with the center."));
407
408 const Tensor<1, spacedim> e1 = v1 / r1;
409 const Tensor<1, spacedim> e2 = v2 / r2;
410
411 // Find the cosine of the angle gamma described by v1 and v2.
412 const double cosgamma = e1 * e2;
413
414 // Points are collinear with the center (allow for 8*eps as a tolerance)
415 if (cosgamma < -1 + 8. * std::numeric_limits<double>::epsilon())
416 return p_center;
417
418 // Points are along a line, in which case e1 and e2 are essentially the same.
419 if (cosgamma > 1 - 8. * std::numeric_limits<double>::epsilon())
420 return Point<spacedim>(p_center + w * v2 + (1 - w) * v1);
421
422 // Find the angle sigma that corresponds to arclength equal to w. acos
423 // should never be undefined because we have ruled out the two special cases
424 // above.
425 const double sigma = w * std::acos(cosgamma);
426
427 // Normal to v1 in the plane described by v1,v2,and the origin.
428 // Since p1 and p2 do not coincide n is not zero and well defined.
429 Tensor<1, spacedim> n = v2 - (v2 * e1) * e1;
430 const double n_norm = n.norm();
431 Assert(n_norm > 0,
432 ExcInternalError("n should be different from the null vector. "
433 "Probably, this means v1==v2 or v2==0."));
434
435 n /= n_norm;
436
437 // Find the point Q along O,v1 such that
438 // P1,V,P2 has measure sigma.
439 const Tensor<1, spacedim> P = std::cos(sigma) * e1 + std::sin(sigma) * n;
440
441 // Project this point on the manifold.
442 return Point<spacedim>(p_center + (w * r2 + (1.0 - w) * r1) * P);
443}
444
445
446
447template <int dim, int spacedim>
450 const Point<spacedim> &p1,
451 const Point<spacedim> &p2) const
452{
453 [[maybe_unused]] const double tol = 1e-10;
454
455 Assert(p1 != p2, ExcMessage("p1 and p2 should not concide."));
456
457 const Tensor<1, spacedim> v1 = p1 - p_center;
458 const Tensor<1, spacedim> v2 = p2 - p_center;
459 const double r1 = v1.norm();
460 const double r2 = v2.norm();
461
462 Assert(r1 > tol, ExcMessage("p1 cannot coincide with the center."));
463
464 Assert(r2 > tol, ExcMessage("p2 cannot coincide with the center."));
465
466 const Tensor<1, spacedim> e1 = v1 / r1;
467 const Tensor<1, spacedim> e2 = v2 / r2;
468
469 // Find the cosine of the angle gamma described by v1 and v2.
470 const double cosgamma = e1 * e2;
471
472 Assert(cosgamma > -1 + 8. * std::numeric_limits<double>::epsilon(),
473 ExcMessage("p1 and p2 cannot lie on the same diameter and be opposite "
474 "respect to the center."));
475
476 if (cosgamma > 1 - 8. * std::numeric_limits<double>::epsilon())
477 return v2 - v1;
478
479 // Normal to v1 in the plane described by v1,v2,and the origin.
480 // Since p1 and p2 do not coincide n is not zero and well defined.
481 Tensor<1, spacedim> n = v2 - (v2 * e1) * e1;
482 const double n_norm = n.norm();
483 Assert(n_norm > 0,
484 ExcInternalError("n should be different from the null vector. "
485 "Probably, this means v1==v2 or v2==0."));
486
487 n /= n_norm;
488
489 // this is the derivative of the geodesic in get_intermediate_point
490 // derived with respect to w and inserting w=0.
491 const double gamma = std::acos(cosgamma);
492 return (r2 - r1) * e1 + r1 * gamma * n;
493}
494
495
496
497template <int dim, int spacedim>
501 const Point<spacedim> &p) const
502{
503 // Let us first test whether we are on a "horizontal" face
504 // (tangential to the sphere). In this case, the normal vector is
505 // easy to compute since it is proportional to the vector from the
506 // center to the point 'p'.
507 if (spherical_face_is_horizontal<dim, spacedim>(face, p_center))
508 {
509 // So, if this is a "horizontal" face, then just compute the normal
510 // vector as the one from the center to the point 'p', adequately
511 // scaled.
512 const Tensor<1, spacedim> unnormalized_spherical_normal = p - p_center;
513 const Tensor<1, spacedim> normalized_spherical_normal =
514 unnormalized_spherical_normal / unnormalized_spherical_normal.norm();
515 return normalized_spherical_normal;
516 }
517 else
518 // If it is not a horizontal face, just use the machinery of the
519 // base class.
521
522 return Tensor<1, spacedim>();
523}
524
525
526
527template <>
528void
535
536
537
538template <>
539void
546
547
548
549template <int dim, int spacedim>
550void
553 typename Manifold<dim, spacedim>::FaceVertexNormals &face_vertex_normals)
554 const
555{
556 // Let us first test whether we are on a "horizontal" face
557 // (tangential to the sphere). In this case, the normal vector is
558 // easy to compute since it is proportional to the vector from the
559 // center to the point 'p'.
560 if (spherical_face_is_horizontal<dim, spacedim>(face, p_center))
561 {
562 // So, if this is a "horizontal" face, then just compute the normal
563 // vector as the one from the center to the point 'p', adequately
564 // scaled.
565 for (unsigned int vertex = 0;
566 vertex < GeometryInfo<spacedim>::vertices_per_face;
567 ++vertex)
568 face_vertex_normals[vertex] = face->vertex(vertex) - p_center;
569 }
570 else
571 Manifold<dim, spacedim>::get_normals_at_vertices(face, face_vertex_normals);
572}
573
574
575
576template <int dim, int spacedim>
577void
579 const ArrayView<const Point<spacedim>> &surrounding_points,
580 const Table<2, double> &weights,
581 ArrayView<Point<spacedim>> new_points) const
582{
583 AssertDimension(new_points.size(), weights.size(0));
584 AssertDimension(surrounding_points.size(), weights.size(1));
585
586 do_get_new_points(surrounding_points, make_array_view(weights), new_points);
587
588 return;
589}
590
591
592
593template <int dim, int spacedim>
596 const ArrayView<const Point<spacedim>> &vertices,
597 const ArrayView<const double> &weights) const
598{
599 // To avoid duplicating all of the logic in get_new_points, simply call it
600 // for one position.
601 Point<spacedim> new_point;
602 do_get_new_points(vertices,
603 weights,
604 make_array_view(&new_point, &new_point + 1));
605
606 return new_point;
607}
608
609
610
611namespace internal
612{
614 {
615 namespace
616 {
617 template <int spacedim>
619 do_get_new_point(
620 const ArrayView<const Tensor<1, spacedim>> & /*directions*/,
621 const ArrayView<const double> & /*distances*/,
622 const ArrayView<const double> & /*weights*/,
623 const Point<spacedim> & /*candidate_point*/)
624 {
626 return {};
627 }
628
629 template <>
631 do_get_new_point(
632 const ArrayView<const Tensor<1, 3>> &directions,
633 [[maybe_unused]] const ArrayView<const double> &distances,
634 const ArrayView<const double> &weights,
635 const Point<3> &candidate_point)
636 {
637 AssertDimension(directions.size(), distances.size());
638 AssertDimension(directions.size(), weights.size());
639
640 Point<3> candidate = candidate_point;
641 const unsigned int n_merged_points = directions.size();
642 const double tolerance = 1e-10;
643 const int max_iterations = 10;
644
645 {
646 // If the candidate happens to coincide with a normalized
647 // direction, we return it. Otherwise, the Hessian would be singular.
648 for (unsigned int i = 0; i < n_merged_points; ++i)
649 {
650 const double squared_distance =
651 (candidate - directions[i]).norm_square();
652 if (squared_distance < tolerance * tolerance)
653 return candidate;
654 }
655
656 // check if we only have two points now, in which case we can use the
657 // get_intermediate_point function
658 if (n_merged_points == 2)
659 {
660 static const ::SphericalManifold<3, 3> unit_manifold;
661 Assert(std::abs(weights[0] + weights[1] - 1.0) < 1e-13,
662 ExcMessage("Weights do not sum up to 1"));
663 const Point<3> intermediate =
664 unit_manifold.get_intermediate_point(Point<3>(directions[0]),
665 Point<3>(directions[1]),
666 weights[1]);
667 return intermediate;
668 }
669
670 Tensor<1, 3> vPerp;
671 Tensor<2, 2> Hessian;
673 Tensor<1, 2> gradlocal;
674
675 // On success we exit the loop early.
676 // Otherwise, we just take the result after max_iterations steps.
677 for (unsigned int i = 0; i < max_iterations; ++i)
678 {
679 // Step 2a: Find new descent direction
680
681 // Get local basis for the estimate candidate
682 const Tensor<1, 3> Clocalx = internal::compute_normal(candidate);
683 const Tensor<1, 3> Clocaly = cross_product_3d(candidate, Clocalx);
684
685 // For each vertices vector, compute the tangent vector from
686 // candidate towards the vertices vector -- its length is the
687 // spherical length from candidate to the vertices vector. Then
688 // compute its contribution to the Hessian.
689 gradient = 0.;
690 Hessian = 0.;
691 for (unsigned int i = 0; i < n_merged_points; ++i)
692 if (std::abs(weights[i]) > 1.e-15)
693 {
694 vPerp =
695 internal::projected_direction(directions[i], candidate);
696 const double sinthetaSq = vPerp.norm_square();
697 const double sintheta = std::sqrt(sinthetaSq);
698 if (sintheta < tolerance)
699 {
700 Hessian[0][0] += weights[i];
701 Hessian[1][1] += weights[i];
702 }
703 else
704 {
705 const double costheta = (directions[i]) * candidate;
706 const double theta = std::atan2(sintheta, costheta);
707 const double sincthetaInv = theta / sintheta;
708
709 const double cosphi = vPerp * Clocalx;
710 const double sinphi = vPerp * Clocaly;
711
712 gradlocal[0] = cosphi;
713 gradlocal[1] = sinphi;
714 gradient += (weights[i] * sincthetaInv) * gradlocal;
715
716 const double wt = weights[i] / sinthetaSq;
717 const double sinphiSq = sinphi * sinphi;
718 const double cosphiSq = cosphi * cosphi;
719 const double tt = sincthetaInv * costheta;
720 const double offdiag =
721 cosphi * sinphi * wt * (1.0 - tt);
722 Hessian[0][0] += wt * (cosphiSq + tt * sinphiSq);
723 Hessian[0][1] += offdiag;
724 Hessian[1][0] += offdiag;
725 Hessian[1][1] += wt * (sinphiSq + tt * cosphiSq);
726 }
727 }
728
729 Assert(determinant(Hessian) > tolerance, ExcInternalError());
730
731 const Tensor<2, 2> inverse_Hessian = invert(Hessian);
732
733 const Tensor<1, 2> xDisplocal = inverse_Hessian * gradient;
734 const Tensor<1, 3> xDisp =
735 xDisplocal[0] * Clocalx + xDisplocal[1] * Clocaly;
736
737 // Step 2b: rotate candidate in direction xDisp for a new
738 // candidate.
739 const Point<3> candidateOld = candidate;
740 candidate =
741 Point<3>(internal::apply_exponential_map(candidate, xDisp));
742
743 // Step 2c: return the new candidate if we didn't move
744 if ((candidate - candidateOld).norm_square() <
745 tolerance * tolerance)
746 break;
747 }
748 }
749 return candidate;
750 }
751 } // namespace
752 } // namespace SphericalManifold
753} // namespace internal
754
755
756
757template <int dim, int spacedim>
758void
760 const ArrayView<const Point<spacedim>> &surrounding_points,
761 const ArrayView<const double> &weights,
762 ArrayView<Point<spacedim>> new_points) const
763{
764 AssertDimension(weights.size(),
765 new_points.size() * surrounding_points.size());
766 const unsigned int weight_rows = new_points.size();
767 const unsigned int weight_columns = surrounding_points.size();
768
769 if (surrounding_points.size() == 2)
770 {
771 for (unsigned int row = 0; row < weight_rows; ++row)
772 new_points[row] =
774 surrounding_points[0],
775 surrounding_points[1],
776 weights[row * weight_columns + 1]);
777 return;
778 }
779
780 boost::container::small_vector<std::pair<double, Tensor<1, spacedim>>, 100>
781 new_candidates(new_points.size());
782 boost::container::small_vector<Tensor<1, spacedim>, 100> directions(
783 surrounding_points.size(), Point<spacedim>());
784 boost::container::small_vector<double, 100> distances(
785 surrounding_points.size(), 0.0);
786 double max_distance = 0.;
787 for (unsigned int i = 0; i < surrounding_points.size(); ++i)
788 {
789 directions[i] = surrounding_points[i] - p_center;
790 distances[i] = directions[i].norm();
791
792 if (distances[i] != 0.)
793 directions[i] /= distances[i];
794 else
795 Assert(false,
796 ExcMessage("One of the vertices coincides with the center. "
797 "This is not allowed!"));
798
799 // Check if an estimate is good enough,
800 // this is often the case for sufficiently refined meshes.
801 for (unsigned int k = 0; k < i; ++k)
802 {
803 const double squared_distance =
804 (directions[i] - directions[k]).norm_square();
805 max_distance = std::max(max_distance, squared_distance);
806 }
807 }
808
809 // Step 1: Check for some special cases, create simple linear guesses
810 // otherwise.
811 const double tolerance = 1e-10;
812 boost::container::small_vector<bool, 100> accurate_point_was_found(
813 new_points.size(), false);
814 const ArrayView<const Tensor<1, spacedim>> array_directions =
815 make_array_view(directions.begin(), directions.end());
816 const ArrayView<const double> array_distances =
817 make_array_view(distances.begin(), distances.end());
818 for (unsigned int row = 0; row < weight_rows; ++row)
819 {
820 new_candidates[row] =
821 guess_new_point(array_directions,
822 array_distances,
823 ArrayView<const double>(&weights[row * weight_columns],
824 weight_columns));
825
826 // If the candidate is the center, mark it as found to avoid entering
827 // the Newton iteration in step 2, which would crash.
828 if (new_candidates[row].first == 0.0)
829 {
830 new_points[row] = p_center;
831 accurate_point_was_found[row] = true;
832 continue;
833 }
834
835 // If not in 3d, just use the implementation from PolarManifold
836 // after we verified that the candidate is not the center.
837 if (spacedim < 3)
838 new_points[row] = polar_manifold.get_new_point(
839 surrounding_points,
840 ArrayView<const double>(&weights[row * weight_columns],
841 weight_columns));
842 }
843
844 // In this case, we treated the case that the candidate is the center and
845 // obtained the new locations from the PolarManifold object otherwise.
846 if constexpr (spacedim < 3)
847 return;
848 else
849 {
850 // If all the points are close to each other, we expect the estimate to
851 // be good enough. This tolerance was chosen such that the first iteration
852 // for a at least three time refined HyperShell mesh with radii .5 and 1.
853 // doesn't already succeed.
854 if (max_distance < 2e-2)
855 {
856 for (unsigned int row = 0; row < weight_rows; ++row)
857 new_points[row] =
858 p_center + new_candidates[row].first * new_candidates[row].second;
859
860 return;
861 }
862
863 // Step 2:
864 // Do more expensive Newton-style iterations to improve the estimate.
865
866 // Search for duplicate directions and merge them to minimize the cost of
867 // the get_new_point function call below.
868 boost::container::small_vector<double, 1000> merged_weights(
869 weights.size());
870 boost::container::small_vector<Tensor<1, spacedim>, 100>
871 merged_directions(surrounding_points.size(), Point<spacedim>());
872 boost::container::small_vector<double, 100> merged_distances(
873 surrounding_points.size(), 0.0);
874
875 unsigned int n_unique_directions = 0;
876 for (unsigned int i = 0; i < surrounding_points.size(); ++i)
877 {
878 bool found_duplicate = false;
879
880 // This inner loop is of @f$O(N^2)@f$ complexity, but
881 // surrounding_points.size() is usually at most 8 points large.
882 for (unsigned int j = 0; j < n_unique_directions; ++j)
883 {
884 const double squared_distance =
885 (directions[i] - directions[j]).norm_square();
886 if (!found_duplicate && squared_distance < 1e-28)
887 {
888 found_duplicate = true;
889 for (unsigned int row = 0; row < weight_rows; ++row)
890 merged_weights[row * weight_columns + j] +=
891 weights[row * weight_columns + i];
892 }
893 }
894
895 if (found_duplicate == false)
896 {
897 merged_directions[n_unique_directions] = directions[i];
898 merged_distances[n_unique_directions] = distances[i];
899 for (unsigned int row = 0; row < weight_rows; ++row)
900 merged_weights[row * weight_columns + n_unique_directions] =
901 weights[row * weight_columns + i];
902
903 ++n_unique_directions;
904 }
905 }
906
907 // Search for duplicate weight rows and merge them to minimize the cost of
908 // the get_new_point function call below.
909 boost::container::small_vector<unsigned int, 100> merged_weights_index(
910 new_points.size(), numbers::invalid_unsigned_int);
911 for (unsigned int row = 0; row < weight_rows; ++row)
912 {
913 for (unsigned int existing_row = 0; existing_row < row;
914 ++existing_row)
915 {
916 bool identical_weights = true;
917
918 for (unsigned int weight_index = 0;
919 weight_index < n_unique_directions;
920 ++weight_index)
921 if (std::abs(
922 merged_weights[row * weight_columns + weight_index] -
923 merged_weights[existing_row * weight_columns +
924 weight_index]) > tolerance)
925 {
926 identical_weights = false;
927 break;
928 }
929
930 if (identical_weights)
931 {
932 merged_weights_index[row] = existing_row;
933 break;
934 }
935 }
936 }
937
938 // Note that we only use the n_unique_directions first entries in the
939 // ArrayView
940 const ArrayView<const Tensor<1, spacedim>> array_merged_directions =
941 make_array_view(merged_directions.begin(),
942 merged_directions.begin() + n_unique_directions);
943 const ArrayView<const double> array_merged_distances =
944 make_array_view(merged_distances.begin(),
945 merged_distances.begin() + n_unique_directions);
946
947 for (unsigned int row = 0; row < weight_rows; ++row)
948 if (!accurate_point_was_found[row])
949 {
950 if (merged_weights_index[row] == numbers::invalid_unsigned_int)
951 {
952 const ArrayView<const double> array_merged_weights(
953 &merged_weights[row * weight_columns], n_unique_directions);
954 new_candidates[row].second =
955 internal::SphericalManifold::do_get_new_point(
956 array_merged_directions,
957 array_merged_distances,
958 array_merged_weights,
959 Point<spacedim>(new_candidates[row].second));
960 }
961 else
962 new_candidates[row].second =
963 new_candidates[merged_weights_index[row]].second;
964
965 new_points[row] =
966 p_center + new_candidates[row].first * new_candidates[row].second;
967 }
968 }
969}
970
971
972
973template <int dim, int spacedim>
974std::pair<double, Tensor<1, spacedim>>
976 const ArrayView<const Tensor<1, spacedim>> &directions,
977 const ArrayView<const double> &distances,
978 const ArrayView<const double> &weights) const
979{
980 const double tolerance = 1e-10;
981 double rho = 0.;
982 Tensor<1, spacedim> candidate;
983
984 // Perform a simple average ...
985 double total_weights = 0.;
986 for (unsigned int i = 0; i < directions.size(); ++i)
987 {
988 // if one weight is one, return its direction
989 if (std::abs(1 - weights[i]) < tolerance)
990 return std::make_pair(distances[i], directions[i]);
991
992 rho += distances[i] * weights[i];
993 candidate += directions[i] * weights[i];
994 total_weights += weights[i];
995 }
996
997 // ... and normalize if the candidate is different from the origin.
998 const double norm = candidate.norm();
999 if (norm == 0.)
1000 return std::make_pair(0.0, Point<spacedim>());
1001 candidate /= norm;
1002 rho /= total_weights;
1003
1004 return std::make_pair(rho, candidate);
1005}
1006
1007
1008
1009// ============================================================
1010// CylindricalManifold
1011// ============================================================
1012template <int dim, int spacedim>
1014 const double tolerance)
1015 : CylindricalManifold<dim, spacedim>(Point<spacedim>::unit_vector(axis),
1016 Point<spacedim>(),
1017 tolerance)
1018{
1019 // do not use static_assert to make dimension-independent programming
1020 // easier.
1021 Assert(spacedim == 3,
1022 ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1023}
1024
1025
1026
1027template <int dim, int spacedim>
1031 const double tolerance)
1032 : ChartManifold<dim, spacedim, 3>(Tensor<1, 3>({0, 2. * numbers::PI, 0}))
1033 , normal_direction(internal::compute_normal(direction, true))
1034 , direction(direction / direction.norm())
1035 , point_on_axis(point_on_axis)
1036 , tolerance(tolerance)
1037 , dxn(cross_product_3d(this->direction, normal_direction))
1038{
1039 // do not use static_assert to make dimension-independent programming
1040 // easier.
1041 Assert(spacedim == 3,
1042 ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1043}
1044
1045
1046
1047template <int dim, int spacedim>
1048std::unique_ptr<Manifold<dim, spacedim>>
1050{
1051 return std::make_unique<CylindricalManifold<dim, spacedim>>(*this);
1052}
1053
1054
1055
1056template <int dim, int spacedim>
1059 const ArrayView<const Point<spacedim>> &surrounding_points,
1060 const ArrayView<const double> &weights) const
1061{
1062 Assert(spacedim == 3,
1063 ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1064
1065 // First check if the average in space lies on the axis.
1066 Point<spacedim> middle;
1067 double average_length = 0.;
1068 for (unsigned int i = 0; i < surrounding_points.size(); ++i)
1069 {
1070 middle += surrounding_points[i] * weights[i];
1071 average_length += surrounding_points[i].square() * weights[i];
1072 }
1073 middle -= point_on_axis;
1074 const double lambda = middle * direction;
1075
1076 if ((middle - direction * lambda).square() < tolerance * average_length)
1077 return point_on_axis + direction * lambda;
1078 else // If not, using the ChartManifold should yield valid results.
1079 return ChartManifold<dim, spacedim, 3>::get_new_point(surrounding_points,
1080 weights);
1081}
1082
1083
1084
1085template <int dim, int spacedim>
1088 const Point<spacedim> &space_point) const
1089{
1090 Assert(spacedim == 3,
1091 ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1092
1093 // First find the projection of the given point to the axis.
1094 const Tensor<1, spacedim> normalized_point = space_point - point_on_axis;
1095 const double lambda = normalized_point * direction;
1096 const Point<spacedim> projection = point_on_axis + direction * lambda;
1097 const Tensor<1, spacedim> p_diff = space_point - projection;
1098
1099 // Then compute the angle between the projection direction and
1100 // another vector orthogonal to the direction vector.
1102 p_diff,
1103 /*axis=*/direction);
1104
1105 // Return distance from the axis, angle and signed distance on the axis.
1106 return Point<3>(p_diff.norm(), phi, lambda);
1107}
1108
1109
1110
1111template <int dim, int spacedim>
1114 const Point<3> &chart_point) const
1115{
1116 Assert(spacedim == 3,
1117 ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1118
1119 // Rotate the orthogonal direction by the given angle
1120 const double sine_r = std::sin(chart_point[1]) * chart_point[0];
1121 const double cosine_r = std::cos(chart_point[1]) * chart_point[0];
1122 const Tensor<1, spacedim> intermediate =
1123 normal_direction * cosine_r + dxn * sine_r;
1124
1125 // Finally, put everything together.
1126 return point_on_axis + direction * chart_point[2] + intermediate;
1127}
1128
1129
1130
1131template <int dim, int spacedim>
1134 const Point<3> &chart_point) const
1135{
1136 Assert(spacedim == 3,
1137 ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1138
1139 Tensor<2, 3> derivatives;
1140
1141 // Rotate the orthogonal direction by the given angle
1142 const double sine = std::sin(chart_point[1]);
1143 const double cosine = std::cos(chart_point[1]);
1144 const Tensor<1, spacedim> intermediate =
1145 normal_direction * cosine + dxn * sine;
1146
1147 // avoid compiler warnings
1148 constexpr int s0 = 0 % spacedim;
1149 constexpr int s1 = 1 % spacedim;
1150 constexpr int s2 = 2 % spacedim;
1151
1152 // derivative w.r.t the radius
1153 derivatives[s0][s0] = intermediate[s0];
1154 derivatives[s1][s0] = intermediate[s1];
1155 derivatives[s2][s0] = intermediate[s2];
1156
1157 // derivatives w.r.t the angle
1158 derivatives[s0][s1] = -normal_direction[s0] * sine + dxn[s0] * cosine;
1159 derivatives[s1][s1] = -normal_direction[s1] * sine + dxn[s1] * cosine;
1160 derivatives[s2][s1] = -normal_direction[s2] * sine + dxn[s2] * cosine;
1161
1162 // derivatives w.r.t the direction of the axis
1163 derivatives[s0][s2] = direction[s0];
1164 derivatives[s1][s2] = direction[s1];
1165 derivatives[s2][s2] = direction[s2];
1166
1167 return derivatives;
1168}
1169
1170
1171
1172namespace
1173{
1174 template <int dim>
1176 check_and_normalize(const Tensor<1, dim> &t)
1177 {
1178 const double norm = t.norm();
1179 Assert(norm > 0.0, ExcMessage("The major axis must have a positive norm."));
1180 return t / norm;
1181 }
1182} // namespace
1183
1184
1185
1186// ============================================================
1187// EllipticalManifold
1188// ============================================================
1189template <int dim, int spacedim>
1191 const Point<spacedim> &center,
1192 const Tensor<1, spacedim> &major_axis_direction,
1193 const double eccentricity)
1194 : ChartManifold<dim, spacedim, spacedim>(
1195 EllipticalManifold<dim, spacedim>::get_periodicity())
1196 , direction(check_and_normalize(major_axis_direction))
1197 , center(center)
1199 , cosh_u(1.0 / eccentricity)
1200 , sinh_u(std::sqrt(cosh_u * cosh_u - 1.0))
1201{
1202 // Throws an exception if dim!=2 || spacedim!=2.
1203 Assert(dim == 2 && spacedim == 2, ExcNotImplemented());
1204 // Throws an exception if eccentricity is not in range.
1205 Assert(std::signbit(cosh_u * cosh_u - 1.0) == false,
1206 ExcMessage(
1207 "Invalid eccentricity: It must satisfy 0 < eccentricity < 1."));
1208}
1209
1210
1211
1212template <int dim, int spacedim>
1213std::unique_ptr<Manifold<dim, spacedim>>
1215{
1216 return std::make_unique<EllipticalManifold<dim, spacedim>>(*this);
1217}
1218
1219
1220
1221template <int dim, int spacedim>
1224{
1225 Tensor<1, spacedim> periodicity;
1226 // The second elliptical coordinate is periodic, while the first is not.
1227 // Enforce periodicity on the last variable.
1228 periodicity[spacedim - 1] = 2.0 * numbers::PI;
1229 return periodicity;
1230}
1231
1232
1233
1234template <int dim, int spacedim>
1241
1242
1243
1244template <>
1247{
1248 const double cs = std::cos(chart_point[1]);
1249 const double sn = std::sin(chart_point[1]);
1250 // Coordinates in the reference frame (i.e. major axis direction is
1251 // x-axis)
1252 const double x = chart_point[0] * cosh_u * cs;
1253 const double y = chart_point[0] * sinh_u * sn;
1254 // Rotate them according to the major axis direction
1255 const Point<2> p(direction[0] * x - direction[1] * y,
1256 direction[1] * x + direction[0] * y);
1257 return p + center;
1258}
1259
1260
1261
1262template <int dim, int spacedim>
1269
1270
1271
1272template <>
1275{
1276 // Moving space_point in the reference coordinate system.
1277 const double x0 = space_point[0] - center[0];
1278 const double y0 = space_point[1] - center[1];
1279 const double x = direction[0] * x0 + direction[1] * y0;
1280 const double y = -direction[1] * x0 + direction[0] * y0;
1281 const double pt0 =
1282 std::sqrt((x * x) / (cosh_u * cosh_u) + (y * y) / (sinh_u * sinh_u));
1283 // If the radius is exactly zero, the point coincides with the origin.
1284 if (pt0 == 0.0)
1285 {
1286 return center;
1287 }
1288 double cos_eta = x / (pt0 * cosh_u);
1289 if (cos_eta < -1.0)
1290 {
1291 cos_eta = -1.0;
1292 }
1293 if (cos_eta > 1.0)
1294 {
1295 cos_eta = 1.0;
1296 }
1297 const double eta = std::acos(cos_eta);
1298 const double pt1 = (std::signbit(y) ? 2.0 * numbers::PI - eta : eta);
1299 return {pt0, pt1};
1300}
1301
1302
1303
1304template <int dim, int spacedim>
1312
1313
1314
1315template <>
1318 const Point<2> &chart_point) const
1319{
1320 const double cs = std::cos(chart_point[1]);
1321 const double sn = std::sin(chart_point[1]);
1322 Tensor<2, 2> dX;
1323 dX[0][0] = cosh_u * cs;
1324 dX[0][1] = -chart_point[0] * cosh_u * sn;
1325 dX[1][0] = sinh_u * sn;
1326 dX[1][1] = chart_point[0] * sinh_u * cs;
1327
1328 // rotate according to the major axis direction
1330 {{+direction[0], -direction[1]}, {direction[1], direction[0]}}};
1331
1332 return rot * dX;
1333}
1334
1335
1336
1337// ============================================================
1338// FunctionManifold
1339// ============================================================
1340template <int dim, int spacedim, int chartdim>
1344 const Tensor<1, chartdim> &periodicity,
1345 const double tolerance)
1346 : ChartManifold<dim, spacedim, chartdim>(periodicity)
1347 , const_map()
1351 , owns_pointers(false)
1353{
1354 AssertDimension(push_forward_function.n_components, spacedim);
1355 AssertDimension(pull_back_function.n_components, chartdim);
1356}
1357
1358
1359
1360template <int dim, int spacedim, int chartdim>
1362 std::unique_ptr<Function<chartdim>> push_forward,
1363 std::unique_ptr<Function<spacedim>> pull_back,
1364 const Tensor<1, chartdim> &periodicity,
1365 const double tolerance)
1366 : ChartManifold<dim, spacedim, chartdim>(periodicity)
1367 , const_map()
1371 , owns_pointers(true)
1373{
1374 AssertDimension(push_forward_function->n_components, spacedim);
1375 AssertDimension(pull_back_function->n_components, chartdim);
1376}
1377
1378
1379
1380template <int dim, int spacedim, int chartdim>
1382 const std::string push_forward_expression,
1383 const std::string pull_back_expression,
1384 const Tensor<1, chartdim> &periodicity,
1386 const std::string chart_vars,
1387 const std::string space_vars,
1388 const double tolerance,
1389 const double h)
1390 : ChartManifold<dim, spacedim, chartdim>(periodicity)
1393 , owns_pointers(true)
1399{
1400 FunctionParser<chartdim> *pf = new FunctionParser<chartdim>(spacedim, 0.0, h);
1401 FunctionParser<spacedim> *pb = new FunctionParser<spacedim>(chartdim, 0.0, h);
1405 pull_back_function = pb;
1406}
1407
1408
1409
1410template <int dim, int spacedim, int chartdim>
1412{
1413 if (owns_pointers == true)
1414 {
1416 push_forward_function = nullptr;
1417 delete pf;
1418
1420 pull_back_function = nullptr;
1421 delete pb;
1422 }
1423}
1424
1425
1426
1427template <int dim, int spacedim, int chartdim>
1428std::unique_ptr<Manifold<dim, spacedim>>
1430{
1431 // This manifold can be constructed either by providing an expression for the
1432 // push forward and the pull back charts, or by providing two Function
1433 // objects. In the first case, the push_forward and pull_back functions are
1434 // created internally in FunctionManifold, and destroyed when this object is
1435 // deleted. In the second case, the function objects are destroyed if they
1436 // are passed as pointers upon construction.
1437 // We need to make sure that our cloned object is constructed in the
1438 // same way this class was constructed, and that its internal Function
1439 // pointers point either to the same Function objects used to construct this
1440 // function or that the newly generated manifold creates internally the
1441 // push_forward and pull_back functions using the same expressions that were
1442 // used to construct this class.
1443 if (!(push_forward_expression.empty() && pull_back_expression.empty()))
1444 {
1445 return std::make_unique<FunctionManifold<dim, spacedim, chartdim>>(
1448 this->get_periodicity(),
1449 const_map,
1450 chart_vars,
1451 space_vars,
1452 tolerance,
1454 }
1455 else
1456 {
1457 return std::make_unique<FunctionManifold<dim, spacedim, chartdim>>(
1460 this->get_periodicity(),
1461 tolerance);
1462 }
1463}
1464
1465
1466
1467template <int dim, int spacedim, int chartdim>
1470 const Point<chartdim> &chart_point) const
1471{
1472 Vector<double> pf(spacedim);
1473 Point<spacedim> result;
1474 push_forward_function->vector_value(chart_point, pf);
1475 for (unsigned int i = 0; i < spacedim; ++i)
1476 result[i] = pf[i];
1477
1478 if constexpr (running_in_debug_mode())
1479 {
1480 Vector<double> pb(chartdim);
1481 pull_back_function->vector_value(result, pb);
1482 for (unsigned int i = 0; i < chartdim; ++i)
1483 Assert(
1484 (chart_point.norm() > tolerance &&
1485 (std::abs(pb[i] - chart_point[i]) <
1486 tolerance * chart_point.norm())) ||
1487 (std::abs(pb[i] - chart_point[i]) < tolerance),
1488 ExcMessage(
1489 "The push forward is not the inverse of the pull back! Bailing out."));
1490 }
1491
1492 return result;
1493}
1494
1495
1496
1497template <int dim, int spacedim, int chartdim>
1500 const Point<chartdim> &chart_point) const
1501{
1503 for (unsigned int i = 0; i < spacedim; ++i)
1504 {
1505 const auto gradient = push_forward_function->gradient(chart_point, i);
1506 for (unsigned int j = 0; j < chartdim; ++j)
1507 DF[i][j] = gradient[j];
1508 }
1509 return DF;
1510}
1511
1512
1513
1514template <int dim, int spacedim, int chartdim>
1517 const Point<spacedim> &space_point) const
1518{
1519 Vector<double> pb(chartdim);
1520 Point<chartdim> result;
1521 pull_back_function->vector_value(space_point, pb);
1522 for (unsigned int i = 0; i < chartdim; ++i)
1523 result[i] = pb[i];
1524 return result;
1525}
1526
1527
1528
1529// ============================================================
1530// TorusManifold
1531// ============================================================
1532template <int dim>
1535{
1536 double x = p[0];
1537 double z = p[1];
1538 double y = p[2];
1539 double phi = std::atan2(y, x);
1540 double theta = std::atan2(z, std::sqrt(x * x + y * y) - centerline_radius);
1541 double w =
1544 z * z) /
1546 return {phi, theta, w};
1547}
1548
1549
1550
1551template <int dim>
1554{
1555 double phi = chart_point[0];
1556 double theta = chart_point[1];
1557 double w = chart_point[2];
1558
1559 return {std::cos(phi) * centerline_radius +
1560 inner_radius * w * std::cos(theta) * std::cos(phi),
1561 inner_radius * w * std::sin(theta),
1563 inner_radius * w * std::cos(theta) * std::sin(phi)};
1564}
1565
1566
1567
1568template <int dim>
1570 const double inner_radius)
1571 : ChartManifold<dim, 3, 3>(Point<3>(2 * numbers::PI, 2 * numbers::PI, 0.0))
1574{
1576 ExcMessage("The centerline radius must be greater than the "
1577 "inner radius."));
1578 Assert(inner_radius > 0.0, ExcMessage("The inner radius must be positive."));
1579}
1580
1581
1582
1583template <int dim>
1584std::unique_ptr<Manifold<dim, 3>>
1586{
1587 return std::make_unique<TorusManifold<dim>>(centerline_radius, inner_radius);
1588}
1589
1590
1591
1592template <int dim>
1595{
1597
1598 double phi = chart_point[0];
1599 double theta = chart_point[1];
1600 double w = chart_point[2];
1601
1602 DX[0][0] = -std::sin(phi) * centerline_radius -
1603 inner_radius * w * std::cos(theta) * std::sin(phi);
1604 DX[0][1] = -inner_radius * w * std::sin(theta) * std::cos(phi);
1605 DX[0][2] = inner_radius * std::cos(theta) * std::cos(phi);
1606
1607 DX[1][0] = 0;
1608 DX[1][1] = inner_radius * w * std::cos(theta);
1609 DX[1][2] = inner_radius * std::sin(theta);
1610
1611 DX[2][0] = std::cos(phi) * centerline_radius +
1612 inner_radius * w * std::cos(theta) * std::cos(phi);
1613 DX[2][1] = -inner_radius * w * std::sin(theta) * std::sin(phi);
1614 DX[2][2] = inner_radius * std::cos(theta) * std::sin(phi);
1615
1616 return DX;
1617}
1618
1619
1620
1621// ============================================================
1622// TransfiniteInterpolationManifold
1623// ============================================================
1624template <int dim, int spacedim>
1627 : triangulation(nullptr)
1628 , level_coarse(-1)
1629{
1630 AssertThrow(dim > 1, ExcNotImplemented());
1631}
1632
1633
1634
1635template <int dim, int spacedim>
1638{
1639 if (clear_signal.connected())
1640 clear_signal.disconnect();
1641}
1642
1643
1644
1645template <int dim, int spacedim>
1646std::unique_ptr<Manifold<dim, spacedim>>
1648{
1650 if (triangulation)
1651 ptr->initialize(*triangulation);
1652 return std::unique_ptr<Manifold<dim, spacedim>>(ptr);
1653}
1654
1655
1656
1657template <int dim, int spacedim>
1658void
1661{
1662 this->triangulation = &triangulation;
1663 // In case the triangulation is cleared, remove the pointers by a signal:
1664 clear_signal.disconnect();
1665 clear_signal = triangulation.signals.clear.connect([&]() -> void {
1666 this->triangulation = nullptr;
1667 this->level_coarse = -1;
1668 });
1669 level_coarse = triangulation.last()->level();
1670 coarse_cell_is_flat.resize(triangulation.n_cells(level_coarse), false);
1672
1673 // In case of dim == spacedim we perform a quadratic approximation in
1674 // InverseQuadraticApproximation(), thus initialize the unit_points
1675 // vector with one subdivision to get 3^dim unit_points.
1676 //
1677 // In the co-dimension one case (meaning dim < spacedim) we have to fall
1678 // back to a simple GridTools::affine_cell_approximation<dim>() which
1679 // requires 2^dim points, instead. Thus, initialize the QIterated
1680 // quadrature with no subdivisions.
1681 const std::vector<Point<dim>> unit_points =
1682 QIterated<dim>(QTrapezoid<1>(), (dim == spacedim ? 2 : 1)).get_points();
1683 std::vector<Point<spacedim>> real_points(unit_points.size());
1684
1685 for (const auto &cell : triangulation.active_cell_iterators())
1686 {
1687 bool cell_is_flat = true;
1688 for (const auto l : cell->line_indices())
1689 if (cell->line(l)->manifold_id() != cell->manifold_id() &&
1690 cell->line(l)->manifold_id() != numbers::flat_manifold_id)
1691 cell_is_flat = false;
1692 if constexpr (dim > 2)
1693 for (const auto q : cell->face_indices())
1694 if (cell->quad(q)->manifold_id() != cell->manifold_id() &&
1695 cell->quad(q)->manifold_id() != numbers::flat_manifold_id)
1696 cell_is_flat = false;
1697 AssertIndexRange(static_cast<unsigned int>(cell->index()),
1698 coarse_cell_is_flat.size());
1699 coarse_cell_is_flat[cell->index()] = cell_is_flat;
1700
1701 // build quadratic interpolation
1702 for (unsigned int i = 0; i < unit_points.size(); ++i)
1703 real_points[i] = push_forward(cell, unit_points[i]);
1704 quadratic_approximation.emplace_back(real_points, unit_points);
1705 }
1706}
1707
1708
1709
1710namespace
1711{
1712 // version for 1d
1713 template <typename AccessorType>
1715 compute_transfinite_interpolation(const AccessorType &cell,
1716 const Point<1> &chart_point,
1717 const bool /*cell_is_flat*/)
1718 {
1719 return cell.vertex(0) * (1. - chart_point[0]) +
1720 cell.vertex(1) * chart_point[0];
1721 }
1722
1723 // version for 2d
1724 template <typename AccessorType>
1726 compute_transfinite_interpolation(const AccessorType &cell,
1727 const Point<2> &chart_point,
1728 const bool cell_is_flat)
1729 {
1730 const unsigned int dim = AccessorType::dimension;
1731 const unsigned int spacedim = AccessorType::space_dimension;
1732 const types::manifold_id my_manifold_id = cell.manifold_id();
1733 const Triangulation<dim, spacedim> &tria = cell.get_triangulation();
1734
1735 // formula see wikipedia
1736 // https://en.wikipedia.org/wiki/Transfinite_interpolation
1737 // S(u,v) = (1-v)c_1(u)+v c_3(u) + (1-u)c_2(v) + u c_4(v) -
1738 // [(1-u)(1-v)P_0 + u(1-v) P_1 + (1-u)v P_2 + uv P_3]
1739 const std::array<Point<spacedim>, 4> vertices{
1740 {cell.vertex(0), cell.vertex(1), cell.vertex(2), cell.vertex(3)}};
1741
1742 // this evaluates all bilinear shape functions because we need them
1743 // repeatedly. we will update this values in the complicated case with
1744 // curved lines below
1745 std::array<double, 4> weights_vertices{
1746 {(1. - chart_point[0]) * (1. - chart_point[1]),
1747 chart_point[0] * (1. - chart_point[1]),
1748 (1. - chart_point[0]) * chart_point[1],
1749 chart_point[0] * chart_point[1]}};
1750
1751 Point<spacedim> new_point;
1752 if (cell_is_flat)
1753 for (const unsigned int v : GeometryInfo<2>::vertex_indices())
1754 new_point += weights_vertices[v] * vertices[v];
1755 else
1756 {
1757 // The second line in the formula tells us to subtract the
1758 // contribution of the vertices. If a line employs the same manifold
1759 // as the cell, we can merge the weights of the line with the weights
1760 // of the vertex with a negative sign while going through the faces
1761 // (this is a bit artificial in 2d but it becomes clear in 3d where we
1762 // avoid looking at the faces' orientation and other complications).
1763
1764 // add the contribution from the lines around the cell (first line in
1765 // formula)
1766 std::array<double, GeometryInfo<2>::vertices_per_face> weights;
1767 std::array<Point<spacedim>, GeometryInfo<2>::vertices_per_face> points;
1768 // note that the views are immutable, but the arrays are not
1769 const auto weights_view =
1770 make_array_view(weights.begin(), weights.end());
1771 const auto points_view = make_array_view(points.begin(), points.end());
1772
1773 for (unsigned int line = 0; line < GeometryInfo<2>::lines_per_cell;
1774 ++line)
1775 {
1776 const double my_weight =
1777 (line % 2) ? chart_point[line / 2] : 1 - chart_point[line / 2];
1778 const double line_point = chart_point[1 - line / 2];
1779
1780 // Same manifold or invalid id which will go back to the same
1781 // class -> contribution should be added for the final point,
1782 // which means that we subtract the current weight from the
1783 // negative weight applied to the vertex
1784 const types::manifold_id line_manifold_id =
1785 cell.line(line)->manifold_id();
1786 if (line_manifold_id == my_manifold_id ||
1787 line_manifold_id == numbers::flat_manifold_id)
1788 {
1789 weights_vertices[GeometryInfo<2>::line_to_cell_vertices(line,
1790 0)] -=
1791 my_weight * (1. - line_point);
1792 weights_vertices[GeometryInfo<2>::line_to_cell_vertices(line,
1793 1)] -=
1794 my_weight * line_point;
1795 }
1796 else
1797 {
1798 points[0] =
1799 vertices[GeometryInfo<2>::line_to_cell_vertices(line, 0)];
1800 points[1] =
1801 vertices[GeometryInfo<2>::line_to_cell_vertices(line, 1)];
1802 weights[0] = 1. - line_point;
1803 weights[1] = line_point;
1804 new_point +=
1805 my_weight * tria.get_manifold(line_manifold_id)
1806 .get_new_point(points_view, weights_view);
1807 }
1808 }
1809
1810 // subtract contribution from the vertices (second line in formula)
1811 for (const unsigned int v : GeometryInfo<2>::vertex_indices())
1812 new_point -= weights_vertices[v] * vertices[v];
1813 }
1814
1815 return new_point;
1816 }
1817
1818 // this is replicated from GeometryInfo::face_to_cell_vertices since we need
1819 // it very often in compute_transfinite_interpolation and the function is
1820 // performance critical
1821 static constexpr unsigned int face_to_cell_vertices_3d[6][4] = {{0, 2, 4, 6},
1822 {1, 3, 5, 7},
1823 {0, 4, 1, 5},
1824 {2, 6, 3, 7},
1825 {0, 1, 2, 3},
1826 {4, 5, 6, 7}};
1827
1828 // this is replicated from GeometryInfo::face_to_cell_lines since we need it
1829 // very often in compute_transfinite_interpolation and the function is
1830 // performance critical
1831 static constexpr unsigned int face_to_cell_lines_3d[6][4] = {{8, 10, 0, 4},
1832 {9, 11, 1, 5},
1833 {2, 6, 8, 9},
1834 {3, 7, 10, 11},
1835 {0, 1, 2, 3},
1836 {4, 5, 6, 7}};
1837
1838 // version for 3d
1839 template <typename AccessorType>
1841 compute_transfinite_interpolation(const AccessorType &cell,
1842 const Point<3> &chart_point,
1843 const bool cell_is_flat)
1844 {
1845 const unsigned int dim = AccessorType::dimension;
1846 const unsigned int spacedim = AccessorType::space_dimension;
1847 const types::manifold_id my_manifold_id = cell.manifold_id();
1848 const Triangulation<dim, spacedim> &tria = cell.get_triangulation();
1849
1850 // Same approach as in 2d, but adding the faces, subtracting the edges, and
1851 // adding the vertices
1852 const std::array<Point<spacedim>, 8> vertices{{cell.vertex(0),
1853 cell.vertex(1),
1854 cell.vertex(2),
1855 cell.vertex(3),
1856 cell.vertex(4),
1857 cell.vertex(5),
1858 cell.vertex(6),
1859 cell.vertex(7)}};
1860
1861 // store the components of the linear shape functions because we need them
1862 // repeatedly. we allow for 10 such shape functions to wrap around the
1863 // first four once again for easier face access.
1864 double linear_shapes[10];
1865 for (unsigned int d = 0; d < 3; ++d)
1866 {
1867 linear_shapes[2 * d] = 1. - chart_point[d];
1868 linear_shapes[2 * d + 1] = chart_point[d];
1869 }
1870
1871 // wrap linear shape functions around for access in face loop
1872 for (unsigned int d = 6; d < 10; ++d)
1873 linear_shapes[d] = linear_shapes[d - 6];
1874
1875 std::array<double, 8> weights_vertices;
1876 for (unsigned int i2 = 0, v = 0; i2 < 2; ++i2)
1877 for (unsigned int i1 = 0; i1 < 2; ++i1)
1878 for (unsigned int i0 = 0; i0 < 2; ++i0, ++v)
1879 weights_vertices[v] =
1880 (linear_shapes[4 + i2] * linear_shapes[2 + i1]) * linear_shapes[i0];
1881
1882 Point<spacedim> new_point;
1883 if (cell_is_flat)
1884 for (unsigned int v = 0; v < 8; ++v)
1885 new_point += weights_vertices[v] * vertices[v];
1886 else
1887 {
1888 // identify the weights for the lines to be accumulated (vertex
1889 // weights are set outside and coincide with the flat manifold case)
1890
1891 std::array<double, GeometryInfo<3>::lines_per_cell> weights_lines;
1892 std::fill(weights_lines.begin(), weights_lines.end(), 0.0);
1893
1894 // start with the contributions of the faces
1895 std::array<double, GeometryInfo<2>::vertices_per_cell> weights;
1896 std::array<Point<spacedim>, GeometryInfo<2>::vertices_per_cell> points;
1897 // note that the views are immutable, but the arrays are not
1898 const auto weights_view =
1899 make_array_view(weights.begin(), weights.end());
1900 const auto points_view = make_array_view(points.begin(), points.end());
1901
1902 for (const unsigned int face : GeometryInfo<3>::face_indices())
1903 {
1904 const double my_weight = linear_shapes[face];
1905 const unsigned int face_even = face - face % 2;
1906
1907 if (std::abs(my_weight) < 1e-13)
1908 continue;
1909
1910 // same manifold or invalid id which will go back to the same class
1911 // -> face will interpolate from the surrounding lines and vertices
1912 const types::manifold_id face_manifold_id =
1913 cell.face(face)->manifold_id();
1914 if (face_manifold_id == my_manifold_id ||
1915 face_manifold_id == numbers::flat_manifold_id)
1916 {
1917 for (unsigned int line = 0;
1918 line < GeometryInfo<2>::lines_per_cell;
1919 ++line)
1920 {
1921 const double line_weight =
1922 linear_shapes[face_even + 2 + line];
1923 weights_lines[face_to_cell_lines_3d[face][line]] +=
1924 my_weight * line_weight;
1925 }
1926 // as to the indices inside linear_shapes: we use the index
1927 // wrapped around at 2*d, ensuring the correct orientation of
1928 // the face's coordinate system with respect to the
1929 // lexicographic indices
1930 weights_vertices[face_to_cell_vertices_3d[face][0]] -=
1931 linear_shapes[face_even + 2] *
1932 (linear_shapes[face_even + 4] * my_weight);
1933 weights_vertices[face_to_cell_vertices_3d[face][1]] -=
1934 linear_shapes[face_even + 3] *
1935 (linear_shapes[face_even + 4] * my_weight);
1936 weights_vertices[face_to_cell_vertices_3d[face][2]] -=
1937 linear_shapes[face_even + 2] *
1938 (linear_shapes[face_even + 5] * my_weight);
1939 weights_vertices[face_to_cell_vertices_3d[face][3]] -=
1940 linear_shapes[face_even + 3] *
1941 (linear_shapes[face_even + 5] * my_weight);
1942 }
1943 else
1944 {
1945 for (const unsigned int v : GeometryInfo<2>::vertex_indices())
1946 points[v] = vertices[face_to_cell_vertices_3d[face][v]];
1947 weights[0] =
1948 linear_shapes[face_even + 2] * linear_shapes[face_even + 4];
1949 weights[1] =
1950 linear_shapes[face_even + 3] * linear_shapes[face_even + 4];
1951 weights[2] =
1952 linear_shapes[face_even + 2] * linear_shapes[face_even + 5];
1953 weights[3] =
1954 linear_shapes[face_even + 3] * linear_shapes[face_even + 5];
1955 new_point +=
1956 my_weight * tria.get_manifold(face_manifold_id)
1957 .get_new_point(points_view, weights_view);
1958 }
1959 }
1960
1961 // next subtract the contributions of the lines
1962 const auto weights_view_line =
1963 make_array_view(weights.begin(), weights.begin() + 2);
1964 const auto points_view_line =
1965 make_array_view(points.begin(), points.begin() + 2);
1966 for (unsigned int line = 0; line < GeometryInfo<3>::lines_per_cell;
1967 ++line)
1968 {
1969 const double line_point =
1970 (line < 8 ? chart_point[1 - (line % 4) / 2] : chart_point[2]);
1971 double my_weight = 0.;
1972 if (line < 8)
1973 my_weight = linear_shapes[line % 4] * linear_shapes[4 + line / 4];
1974 else
1975 {
1976 const unsigned int subline = line - 8;
1977 my_weight =
1978 linear_shapes[subline % 2] * linear_shapes[2 + subline / 2];
1979 }
1980 my_weight -= weights_lines[line];
1981
1982 if (std::abs(my_weight) < 1e-13)
1983 continue;
1984
1985 const types::manifold_id line_manifold_id =
1986 cell.line(line)->manifold_id();
1987 if (line_manifold_id == my_manifold_id ||
1988 line_manifold_id == numbers::flat_manifold_id)
1989 {
1990 weights_vertices[GeometryInfo<3>::line_to_cell_vertices(line,
1991 0)] -=
1992 my_weight * (1. - line_point);
1993 weights_vertices[GeometryInfo<3>::line_to_cell_vertices(line,
1994 1)] -=
1995 my_weight * (line_point);
1996 }
1997 else
1998 {
1999 points[0] =
2000 vertices[GeometryInfo<3>::line_to_cell_vertices(line, 0)];
2001 points[1] =
2002 vertices[GeometryInfo<3>::line_to_cell_vertices(line, 1)];
2003 weights[0] = 1. - line_point;
2004 weights[1] = line_point;
2005 new_point -= my_weight * tria.get_manifold(line_manifold_id)
2006 .get_new_point(points_view_line,
2007 weights_view_line);
2008 }
2009 }
2010
2011 // finally add the contribution of the
2012 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
2013 new_point += weights_vertices[v] * vertices[v];
2014 }
2015 return new_point;
2016 }
2017} // namespace
2018
2019
2020
2021template <int dim, int spacedim>
2025 const Point<dim> &chart_point) const
2026{
2028
2029 // check that the point is in the unit cell which is the current chart
2030 // Tolerance 5e-4 chosen that the method also works with manifolds
2031 // that have some discretization error like SphericalManifold
2033 ExcMessage("chart_point is not in unit interval"));
2034
2035 return compute_transfinite_interpolation(*cell,
2036 chart_point,
2037 coarse_cell_is_flat[cell->index()]);
2038}
2039
2040
2041
2042template <int dim, int spacedim>
2046 const Point<dim> &chart_point,
2047 const Point<spacedim> &pushed_forward_chart_point) const
2048{
2049 // compute the derivative with the help of finite differences
2051 for (unsigned int d = 0; d < dim; ++d)
2052 {
2053 Point<dim> modified = chart_point;
2054 const double step = chart_point[d] > 0.5 ? -1e-8 : 1e-8;
2055
2056 // avoid checking outside of the unit interval
2057 modified[d] += step;
2058 Tensor<1, spacedim> difference =
2059 compute_transfinite_interpolation(*cell,
2060 modified,
2061 coarse_cell_is_flat[cell->index()]) -
2062 pushed_forward_chart_point;
2063 for (unsigned int e = 0; e < spacedim; ++e)
2064 grad[e][d] = difference[e] / step;
2065 }
2066 return grad;
2067}
2068
2069
2070
2071template <int dim, int spacedim>
2075 const Point<spacedim> &point,
2076 const Point<dim> &initial_guess) const
2077{
2078 Point<dim> outside;
2079 for (unsigned int d = 0; d < dim; ++d)
2081
2082 // project the user-given input to unit cell
2083 Point<dim> chart_point = cell->reference_cell().closest_point(initial_guess);
2084
2085 // run quasi-Newton iteration with a combination of finite differences for
2086 // the exact Jacobian and "Broyden's good method". As opposed to the various
2087 // mapping implementations, this class does not throw exception upon failure
2088 // as those are relatively expensive and failure occurs quite regularly in
2089 // the implementation of the compute_chart_points method.
2090 Tensor<1, spacedim> residual =
2091 point -
2092 compute_transfinite_interpolation(*cell,
2093 chart_point,
2094 coarse_cell_is_flat[cell->index()]);
2095 const double tolerance = 1e-21 * Utilities::fixed_power<2>(cell->diameter());
2096 double residual_norm_square = residual.norm_square();
2098 bool must_recompute_jacobian = true;
2099 for (unsigned int i = 0; i < 100; ++i)
2100 {
2101 if (residual_norm_square < tolerance)
2102 {
2103 // do a final update of the point with the last available Jacobian
2104 // information. The residual is close to zero due to the check
2105 // above, but me might improve some of the last digits by a final
2106 // Newton-like step with step length 1
2107 Tensor<1, dim> update;
2108 for (unsigned int d = 0; d < spacedim; ++d)
2109 for (unsigned int e = 0; e < dim; ++e)
2110 update[e] += inv_grad[d][e] * residual[d];
2111 return chart_point + update;
2112 }
2113
2114 // every 9 iterations, including the first time around, we create an
2115 // approximation of the Jacobian with finite differences. Broyden's
2116 // method usually does not need more than 5-8 iterations, but sometimes
2117 // we might have had a bad initial guess and then we can accelerate
2118 // convergence considerably with getting the actual Jacobian rather than
2119 // using secant-like methods (one gradient calculation in 3d costs as
2120 // much as 3 more iterations). this usually happens close to convergence
2121 // and one more step with the finite-differenced Jacobian leads to
2122 // convergence
2123 if (must_recompute_jacobian || i % 9 == 0)
2124 {
2125 // if the determinant is zero or negative, the mapping is either not
2126 // invertible or already has inverted and we are outside the valid
2127 // chart region. Note that the Jacobian here represents the
2128 // derivative of the forward map and should have a positive
2129 // determinant since we use properly oriented meshes.
2132 chart_point,
2133 Point<spacedim>(point - residual));
2134 if (grad.determinant() <= 0.0)
2135 return outside;
2136 inv_grad = grad.covariant_form();
2137 must_recompute_jacobian = false;
2138 }
2139 Tensor<1, dim> update;
2140 for (unsigned int d = 0; d < spacedim; ++d)
2141 for (unsigned int e = 0; e < dim; ++e)
2142 update[e] += inv_grad[d][e] * residual[d];
2143
2144 // Line search, accept step if the residual has decreased
2145 double alpha = 1.;
2146
2147 // check if point is inside 1.2 times the unit cell to avoid
2148 // hitting points very far away from valid ones in the manifolds
2149 while (
2150 !GeometryInfo<dim>::is_inside_unit_cell(chart_point + alpha * update,
2151 0.2) &&
2152 alpha > 1e-7)
2153 alpha *= 0.5;
2154
2155 const Tensor<1, spacedim> old_residual = residual;
2156 while (alpha > 1e-4)
2157 {
2158 Point<dim> guess = chart_point + alpha * update;
2159 const Tensor<1, spacedim> residual_guess =
2160 point - compute_transfinite_interpolation(
2161 *cell, guess, coarse_cell_is_flat[cell->index()]);
2162 const double residual_norm_new = residual_guess.norm_square();
2163 if (residual_norm_new < residual_norm_square)
2164 {
2165 residual = residual_guess;
2166 residual_norm_square = residual_norm_new;
2167 chart_point += alpha * update;
2168 break;
2169 }
2170 else
2171 alpha *= 0.5;
2172 }
2173 // If alpha got very small, it is likely due to a bad Jacobian
2174 // approximation with Broyden's method (relatively far away from the
2175 // zero), which can be corrected by the outer loop when a Newton update
2176 // is recomputed. The second case is when the Jacobian is actually bad
2177 // and we should fail as early as possible. Since we cannot really
2178 // distinguish the two, we must continue here in any case.
2179 if (alpha <= 1e-4)
2180 must_recompute_jacobian = true;
2181
2182 // update the inverse Jacobian with "Broyden's good method" and
2183 // Sherman-Morrison formula for the update of the inverse, see
2184 // https://en.wikipedia.org/wiki/Broyden%27s_method
2185 // J^{-1}_n = J^{-1}_{n-1} + (delta x_n - J^{-1}_{n-1} delta f_n) /
2186 // (delta x_n^T J_{-1}_{n-1} delta f_n) delta x_n^T J^{-1}_{n-1}
2187
2188 // switch sign in residual as compared to the formula above because we
2189 // use a negative definition of the residual with respect to the
2190 // Jacobian
2191 const Tensor<1, spacedim> delta_f = old_residual - residual;
2192
2193 Tensor<1, dim> Jinv_deltaf;
2194 for (unsigned int d = 0; d < spacedim; ++d)
2195 for (unsigned int e = 0; e < dim; ++e)
2196 Jinv_deltaf[e] += inv_grad[d][e] * delta_f[d];
2197
2198 const Tensor<1, dim> delta_x = alpha * update;
2199
2200 // prevent division by zero. This number should be scale-invariant
2201 // because Jinv_deltaf carries no units and x is in reference
2202 // coordinates.
2203 if (std::abs(delta_x * Jinv_deltaf) > 1e-12 && !must_recompute_jacobian)
2204 {
2205 const Tensor<1, dim> factor =
2206 (delta_x - Jinv_deltaf) / (delta_x * Jinv_deltaf);
2207 Tensor<1, spacedim> jac_update;
2208 for (unsigned int d = 0; d < spacedim; ++d)
2209 for (unsigned int e = 0; e < dim; ++e)
2210 jac_update[d] += delta_x[e] * inv_grad[d][e];
2211 for (unsigned int d = 0; d < spacedim; ++d)
2212 for (unsigned int e = 0; e < dim; ++e)
2213 inv_grad[d][e] += factor[e] * jac_update[d];
2214 }
2215 }
2216 return outside;
2217}
2218
2219
2220
2221template <int dim, int spacedim>
2222std::array<unsigned int, 20>
2225 const ArrayView<const Point<spacedim>> &points) const
2226{
2227 // The methods to identify cells around points in GridTools are all written
2228 // for the active cells, but we are here looking at some cells at the coarse
2229 // level.
2230 Assert(triangulation != nullptr, ExcNotInitialized());
2231 Assert(triangulation->begin_active()->level() >= level_coarse,
2232 ExcMessage("The manifold was initialized with level " +
2233 std::to_string(level_coarse) + " but there are now" +
2234 "active cells on a lower level. Coarsening the mesh is " +
2235 "currently not supported"));
2236
2237 // This computes the distance of the surrounding points transformed to the
2238 // unit cell from the unit cell.
2240 triangulation->begin(
2241 level_coarse),
2242 endc =
2243 triangulation->end(
2244 level_coarse);
2245 boost::container::small_vector<std::pair<double, unsigned int>, 200>
2246 distances_and_cells;
2247 for (; cell != endc; ++cell)
2248 {
2249 // only consider cells where the current manifold is attached
2250 if (&cell->get_manifold() != this)
2251 continue;
2252
2253 std::array<Point<spacedim>, GeometryInfo<dim>::vertices_per_cell>
2254 vertices;
2255 for (const unsigned int vertex_n : GeometryInfo<dim>::vertex_indices())
2256 {
2257 vertices[vertex_n] = cell->vertex(vertex_n);
2258 }
2259
2260 // cheap check: if any of the points is not inside a circle around the
2261 // center of the loop, we can skip the expensive part below (this assumes
2262 // that the manifold does not deform the grid too much)
2263 Point<spacedim> center;
2264 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
2265 center += vertices[v];
2267 double radius_square = 0.;
2268 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
2269 radius_square =
2270 std::max(radius_square, (center - vertices[v]).norm_square());
2271 bool inside_circle = true;
2272 for (unsigned int i = 0; i < points.size(); ++i)
2273 if ((center - points[i]).norm_square() > radius_square * 1.5)
2274 {
2275 inside_circle = false;
2276 break;
2277 }
2278 if (inside_circle == false)
2279 continue;
2280
2281 // slightly more expensive search
2282 double current_distance = 0;
2283 for (unsigned int i = 0; i < points.size(); ++i)
2284 {
2285 Point<dim> point =
2286 quadratic_approximation[cell->index()].compute(points[i]);
2287 current_distance += GeometryInfo<dim>::distance_to_unit_cell(point);
2288 }
2289 distances_and_cells.push_back(
2290 std::make_pair(current_distance, cell->index()));
2291 }
2292 // no coarse cell could be found -> transformation failed
2293 AssertThrow(distances_and_cells.size() > 0,
2295 std::sort(distances_and_cells.begin(), distances_and_cells.end());
2296 std::array<unsigned int, 20> cells;
2298 for (unsigned int i = 0; i < distances_and_cells.size() && i < cells.size();
2299 ++i)
2300 cells[i] = distances_and_cells[i].second;
2301
2302 return cells;
2303}
2304
2305
2306
2307template <int dim, int spacedim>
2310 const ArrayView<const Point<spacedim>> &surrounding_points,
2311 ArrayView<Point<dim>> chart_points) const
2312{
2313 Assert(surrounding_points.size() == chart_points.size(),
2314 ExcMessage("The chart points array view must be as large as the "
2315 "surrounding points array view."));
2316
2317 std::array<unsigned int, 20> nearby_cells =
2318 get_possible_cells_around_points(surrounding_points);
2319
2320 // This function is nearly always called to place new points on a cell or
2321 // cell face. In this case, the general structure of the surrounding points
2322 // is known (i.e., if there are eight surrounding points, then they will
2323 // almost surely be either eight points around a quadrilateral or the eight
2324 // vertices of a cube). Hence, making this assumption, we use two
2325 // optimizations (one for structdim == 2 and one for structdim == 3) that
2326 // guess the locations of some of the chart points more efficiently than the
2327 // affine map approximation. The affine map approximation is used whenever
2328 // we don't have a cheaper guess available.
2329
2330 // Function that can guess the location of a chart point by assuming that
2331 // the eight surrounding points are points on a two-dimensional object
2332 // (either a cell in 2d or the face of a hexahedron in 3d), arranged like
2333 //
2334 // 2 - 7 - 3
2335 // | |
2336 // 4 5
2337 // | |
2338 // 0 - 6 - 1
2339 //
2340 // This function assumes that the first three chart points have been
2341 // computed since there is no effective way to guess them.
2342 auto guess_chart_point_structdim_2 = [&](const unsigned int i) -> Point<dim> {
2343 Assert(surrounding_points.size() == 8 && 2 < i && i < 8,
2344 ExcMessage("This function assumes that there are eight surrounding "
2345 "points around a two-dimensional object. It also assumes "
2346 "that the first three chart points have already been "
2347 "computed."));
2348 switch (i)
2349 {
2350 case 0:
2351 case 1:
2352 case 2:
2354 break;
2355 case 3:
2356 return chart_points[1] + (chart_points[2] - chart_points[0]);
2357 case 4:
2358 return 0.5 * (chart_points[0] + chart_points[2]);
2359 case 5:
2360 return 0.5 * (chart_points[1] + chart_points[3]);
2361 case 6:
2362 return 0.5 * (chart_points[0] + chart_points[1]);
2363 case 7:
2364 return 0.5 * (chart_points[2] + chart_points[3]);
2365 default:
2367 }
2368
2369 return {};
2370 };
2371
2372 // Function that can guess the location of a chart point by assuming that
2373 // the eight surrounding points form the vertices of a hexahedron, arranged
2374 // like
2375 //
2376 // 6-------7
2377 // /| /|
2378 // / / |
2379 // / | / |
2380 // 4-------5 |
2381 // | 2- -|- -3
2382 // | / | /
2383 // | | /
2384 // |/ |/
2385 // 0-------1
2386 //
2387 // (where vertex 2 is the back left vertex) we can estimate where chart
2388 // points 5 - 7 are by computing the height (in chart coordinates) as c4 -
2389 // c0 and then adding that onto the appropriate bottom vertex.
2390 //
2391 // This function assumes that the first five chart points have been computed
2392 // since there is no effective way to guess them.
2393 auto guess_chart_point_structdim_3 = [&](const unsigned int i) -> Point<dim> {
2394 Assert(surrounding_points.size() == 8 && 4 < i && i < 8,
2395 ExcMessage("This function assumes that there are eight surrounding "
2396 "points around a three-dimensional object. It also "
2397 "assumes that the first five chart points have already "
2398 "been computed."));
2399 return chart_points[i - 4] + (chart_points[4] - chart_points[0]);
2400 };
2401
2402 // Check if we can use the two chart point shortcuts above before we start:
2403 bool use_structdim_2_guesses = false;
2404 bool use_structdim_3_guesses = false;
2405 // note that in the structdim 2 case: 0 - 6 and 2 - 7 should be roughly
2406 // parallel, while in the structdim 3 case, 0 - 6 and 2 - 7 should be roughly
2407 // orthogonal. Use the angle between these two vectors to figure out if we
2408 // should turn on either structdim optimization.
2409 if (surrounding_points.size() == 8)
2410 {
2411 const Tensor<1, spacedim> v06 =
2412 surrounding_points[6] - surrounding_points[0];
2413 const Tensor<1, spacedim> v27 =
2414 surrounding_points[7] - surrounding_points[2];
2415
2416 // note that we can save a call to sqrt() by rearranging
2417 const double cosine = scalar_product(v06, v27) /
2418 std::sqrt(v06.norm_square() * v27.norm_square());
2419 if (0.707 < cosine)
2420 // the angle is less than pi/4, so these vectors are roughly parallel:
2421 // enable the structdim 2 optimization
2422 use_structdim_2_guesses = true;
2423 else if (spacedim == 3)
2424 // otherwise these vectors are roughly orthogonal: enable the
2425 // structdim 3 optimization if we are in 3d
2426 use_structdim_3_guesses = true;
2427 }
2428 // we should enable at most one of the optimizations
2429 Assert((!use_structdim_2_guesses && !use_structdim_3_guesses) ||
2430 (use_structdim_2_guesses ^ use_structdim_3_guesses),
2432
2433
2434
2435 auto compute_chart_point =
2436 [&](const typename Triangulation<dim, spacedim>::cell_iterator &cell,
2437 const unsigned int point_index) {
2438 Point<dim> guess;
2439 // an optimization: keep track of whether or not we used the quadratic
2440 // approximation so that we don't call pull_back with the same
2441 // initial guess twice (i.e., if pull_back fails the first time,
2442 // don't try again with the same function arguments).
2443 bool used_quadratic_approximation = false;
2444 // if we have already computed three points, we can guess the fourth
2445 // to be the missing corner point of a rectangle
2446 if (point_index == 3 && surrounding_points.size() >= 8)
2447 guess = chart_points[1] + (chart_points[2] - chart_points[0]);
2448 else if (use_structdim_2_guesses && 3 < point_index)
2449 guess = guess_chart_point_structdim_2(point_index);
2450 else if (use_structdim_3_guesses && 4 < point_index)
2451 guess = guess_chart_point_structdim_3(point_index);
2452 else if (dim == 3 && point_index > 7 && surrounding_points.size() == 26)
2453 {
2454 if (point_index < 20)
2455 guess =
2456 0.5 * (chart_points[GeometryInfo<dim>::line_to_cell_vertices(
2457 point_index - 8, 0)] +
2459 point_index - 8, 1)]);
2460 else
2461 guess =
2462 0.25 * (chart_points[GeometryInfo<dim>::face_to_cell_vertices(
2463 point_index - 20, 0)] +
2465 point_index - 20, 1)] +
2467 point_index - 20, 2)] +
2469 point_index - 20, 3)]);
2470 }
2471 else
2472 {
2473 guess = quadratic_approximation[cell->index()].compute(
2474 surrounding_points[point_index]);
2475 used_quadratic_approximation = true;
2476 }
2477 chart_points[point_index] =
2478 pull_back(cell, surrounding_points[point_index], guess);
2479
2480 // the initial guess may not have been good enough: if applicable,
2481 // try again with the affine approximation (which is more accurate
2482 // than the cheap methods used above)
2483 if (chart_points[point_index][0] ==
2485 !used_quadratic_approximation)
2486 {
2487 guess = quadratic_approximation[cell->index()].compute(
2488 surrounding_points[point_index]);
2489 chart_points[point_index] =
2490 pull_back(cell, surrounding_points[point_index], guess);
2491 }
2492
2493 if (chart_points[point_index][0] ==
2495 {
2496 for (unsigned int d = 0; d < dim; ++d)
2497 guess[d] = 0.5;
2498 chart_points[point_index] =
2499 pull_back(cell, surrounding_points[point_index], guess);
2500 }
2501 };
2502
2503 // check whether all points are inside the unit cell of the current chart
2504 for (unsigned int c = 0; c < nearby_cells.size(); ++c)
2505 {
2507 triangulation, level_coarse, nearby_cells[c]);
2508 bool inside_unit_cell = true;
2509 for (unsigned int i = 0; i < surrounding_points.size(); ++i)
2510 {
2511 compute_chart_point(cell, i);
2512
2513 // Tolerance 5e-4 chosen that the method also works with manifolds
2514 // that have some discretization error like SphericalManifold
2515 if (GeometryInfo<dim>::is_inside_unit_cell(chart_points[i], 5e-4) ==
2516 false)
2517 {
2518 inside_unit_cell = false;
2519 break;
2520 }
2521 }
2522 if (inside_unit_cell == true)
2523 {
2524 return cell;
2525 }
2526
2527 // if we did not find a point and this was the last valid cell (the next
2528 // iterate being the end of the array or an invalid tag), we must stop
2529 if (c == nearby_cells.size() - 1 ||
2530 nearby_cells[c + 1] == numbers::invalid_unsigned_int)
2531 {
2532 // generate additional information to help debugging why we did not
2533 // get a point
2534 std::ostringstream message;
2535 for (unsigned int b = 0; b <= c; ++b)
2536 {
2538 triangulation, level_coarse, nearby_cells[b]);
2539 message << "Looking at cell " << cell->id()
2540 << " with vertices: " << std::endl;
2541 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
2542 message << cell->vertex(v) << " ";
2543 message << std::endl;
2544 message << "Transformation to chart coordinates: " << std::endl;
2545 for (unsigned int i = 0; i < surrounding_points.size(); ++i)
2546 {
2547 compute_chart_point(cell, i);
2548 message << surrounding_points[i] << " -> " << chart_points[i]
2549 << std::endl;
2550 }
2551 }
2552
2553 AssertThrow(false,
2555 message.str())));
2556 }
2557 }
2558
2559 // a valid inversion should have returned a point above. an invalid
2560 // inversion should have triggered the assertion, so we should never end up
2561 // here
2564}
2565
2566
2567
2568template <int dim, int spacedim>
2571 const ArrayView<const Point<spacedim>> &surrounding_points,
2572 const ArrayView<const double> &weights) const
2573{
2574 boost::container::small_vector<Point<dim>, 100> chart_points(
2575 surrounding_points.size());
2576 ArrayView<Point<dim>> chart_points_view =
2577 make_array_view(chart_points.begin(), chart_points.end());
2578 const auto cell = compute_chart_points(surrounding_points, chart_points_view);
2579
2580 const Point<dim> p_chart =
2581 chart_manifold.get_new_point(chart_points_view, weights);
2582
2583 return push_forward(cell, p_chart);
2584}
2585
2586
2587
2588template <int dim, int spacedim>
2589void
2591 const ArrayView<const Point<spacedim>> &surrounding_points,
2592 const Table<2, double> &weights,
2593 ArrayView<Point<spacedim>> new_points) const
2594{
2595 Assert(weights.size(0) > 0, ExcEmptyObject());
2596 AssertDimension(surrounding_points.size(), weights.size(1));
2597
2598 boost::container::small_vector<Point<dim>, 100> chart_points(
2599 surrounding_points.size());
2600 ArrayView<Point<dim>> chart_points_view =
2601 make_array_view(chart_points.begin(), chart_points.end());
2602 const auto cell = compute_chart_points(surrounding_points, chart_points_view);
2603
2604 boost::container::small_vector<Point<dim>, 100> new_points_on_chart(
2605 weights.size(0));
2606 chart_manifold.get_new_points(chart_points_view,
2607 weights,
2608 make_array_view(new_points_on_chart.begin(),
2609 new_points_on_chart.end()));
2610
2611 for (unsigned int row = 0; row < weights.size(0); ++row)
2612 new_points[row] = push_forward(cell, new_points_on_chart[row]);
2613}
2614
2615
2616
2617// explicit instantiations
2618#include "grid/manifold_lib.inst"
2619
ArrayView< std::remove_reference_t< typename std::iterator_traits< Iterator >::reference >, MemorySpaceType > make_array_view(const Iterator begin, const Iterator end)
Definition array_view.h:954
std::size_t size() const
Definition array_view.h:689
double diameter(const Mapping< dim, spacedim > &mapping) const
CellId id() const
ChartManifold(const Tensor< 1, chartdim > &periodicity=Tensor< 1, chartdim >())
Definition manifold.cc:1008
const Tensor< 1, chartdim > & get_periodicity() const
Definition manifold.cc:1123
virtual Point< spacedim > get_new_point(const ArrayView< const Point< spacedim > > &surrounding_points, const ArrayView< const double > &weights) const override
Definition manifold.cc:1031
virtual Point< spacedim > push_forward(const Point< 3 > &chart_point) const override
const Tensor< 1, spacedim > dxn
virtual DerivativeForm< 1, 3, spacedim > push_forward_gradient(const Point< 3 > &chart_point) const override
virtual Point< spacedim > get_new_point(const ArrayView< const Point< spacedim > > &surrounding_points, const ArrayView< const double > &weights) const override
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
const Tensor< 1, spacedim > normal_direction
CylindricalManifold(const unsigned int axis=0, const double tolerance=1e-10)
const Point< spacedim > point_on_axis
const double tolerance
const Tensor< 1, spacedim > direction
virtual Point< 3 > pull_back(const Point< spacedim > &space_point) const override
Number determinant() const
DerivativeForm< 1, dim, spacedim, Number > covariant_form() const
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
const Point< spacedim > center
const double eccentricity
virtual Point< spacedim > push_forward(const Point< spacedim > &chart_point) const override
EllipticalManifold(const Point< spacedim > &center, const Tensor< 1, spacedim > &major_axis_direction, const double eccentricity)
virtual DerivativeForm< 1, spacedim, spacedim > push_forward_gradient(const Point< spacedim > &chart_point) const override
virtual Point< spacedim > pull_back(const Point< spacedim > &space_point) const override
static Tensor< 1, spacedim > get_periodicity()
const Tensor< 1, spacedim > direction
const double sinh_u
const double cosh_u
const double tolerance
const FunctionParser< spacedim >::ConstMap const_map
const std::string chart_vars
const std::string pull_back_expression
virtual DerivativeForm< 1, chartdim, spacedim > push_forward_gradient(const Point< chartdim > &chart_point) const override
const std::string space_vars
virtual Point< spacedim > push_forward(const Point< chartdim > &chart_point) const override
const double finite_difference_step
FunctionManifold(const Function< chartdim > &push_forward_function, const Function< spacedim > &pull_back_function, const Tensor< 1, chartdim > &periodicity=Tensor< 1, chartdim >(), const double tolerance=1e-10)
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
const std::string push_forward_expression
virtual Point< chartdim > pull_back(const Point< spacedim > &space_point) const override
ObserverPointer< const Function< chartdim >, FunctionManifold< dim, spacedim, chartdim > > push_forward_function
ObserverPointer< const Function< spacedim >, FunctionManifold< dim, spacedim, chartdim > > pull_back_function
virtual ~FunctionManifold() override
virtual void initialize(const std::string &vars, const std::vector< std::string > &expressions, const ConstMap &constants, const bool time_dependent=false) override
std::map< std::string, double > ConstMap
virtual void get_normals_at_vertices(const typename Triangulation< dim, spacedim >::face_iterator &face, FaceVertexNormals &face_vertex_normals) const
virtual Tensor< 1, spacedim > normal_vector(const typename Triangulation< dim, spacedim >::face_iterator &face, const Point< spacedim > &p) const
std::array< Tensor< 1, spacedim >, GeometryInfo< dim >::vertices_per_face > FaceVertexNormals
Definition manifold.h:305
virtual Point< spacedim > get_new_point(const ArrayView< const Point< spacedim > > &surrounding_points, const ArrayView< const double > &weights) const
Definition point.h:113
PolarManifold(const Point< spacedim > center=Point< spacedim >())
static Tensor< 1, spacedim > get_periodicity()
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
virtual DerivativeForm< 1, spacedim, spacedim > push_forward_gradient(const Point< spacedim > &chart_point) const override
const Point< spacedim > center
const Point< spacedim > p_center
virtual Tensor< 1, spacedim > normal_vector(const typename Triangulation< dim, spacedim >::face_iterator &face, const Point< spacedim > &p) const override
virtual Point< spacedim > push_forward(const Point< spacedim > &chart_point) const override
virtual Point< spacedim > pull_back(const Point< spacedim > &space_point) const override
Point< dim > closest_point(const Point< dim > &p) const
virtual Point< spacedim > get_new_point(const ArrayView< const Point< spacedim > > &vertices, const ArrayView< const double > &weights) const override
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
const Point< spacedim > p_center
const Point< spacedim > center
virtual Point< spacedim > get_intermediate_point(const Point< spacedim > &p1, const Point< spacedim > &p2, const double w) const override
const PolarManifold< spacedim > polar_manifold
virtual void get_normals_at_vertices(const typename Triangulation< dim, spacedim >::face_iterator &face, typename Manifold< dim, spacedim >::FaceVertexNormals &face_vertex_normals) const override
std::pair< double, Tensor< 1, spacedim > > guess_new_point(const ArrayView< const Tensor< 1, spacedim > > &directions, const ArrayView< const double > &distances, const ArrayView< const double > &weights) const
virtual void get_new_points(const ArrayView< const Point< spacedim > > &surrounding_points, const Table< 2, double > &weights, ArrayView< Point< spacedim > > new_points) const override
SphericalManifold(const Point< spacedim > center=Point< spacedim >())
virtual Tensor< 1, spacedim > get_tangent_vector(const Point< spacedim > &x1, const Point< spacedim > &x2) const override
virtual Tensor< 1, spacedim > normal_vector(const typename Triangulation< dim, spacedim >::face_iterator &face, const Point< spacedim > &p) const override
void do_get_new_points(const ArrayView< const Point< spacedim > > &surrounding_points, const ArrayView< const double > &weights, ArrayView< Point< spacedim > > new_points) const
numbers::NumberTraits< Number >::real_type norm() const
constexpr numbers::NumberTraits< Number >::real_type norm_square() const
virtual Point< 3 > pull_back(const Point< 3 > &p) const override
double inner_radius
virtual DerivativeForm< 1, 3, 3 > push_forward_gradient(const Point< 3 > &chart_point) const override
virtual Point< 3 > push_forward(const Point< 3 > &chart_point) const override
double centerline_radius
TorusManifold(const double centerline_radius, const double inner_radius)
virtual std::unique_ptr< Manifold< dim, 3 > > clone() const override
const Triangulation< dim, spacedim > * triangulation
boost::signals2::connection clear_signal
DerivativeForm< 1, dim, spacedim > push_forward_gradient(const typename Triangulation< dim, spacedim >::cell_iterator &cell, const Point< dim > &chart_point, const Point< spacedim > &pushed_forward_chart_point) const
std::vector< internal::MappingQImplementation::InverseQuadraticApproximation< dim, spacedim > > quadratic_approximation
void initialize(const Triangulation< dim, spacedim > &triangulation)
Triangulation< dim, spacedim >::cell_iterator compute_chart_points(const ArrayView< const Point< spacedim > > &surrounding_points, ArrayView< Point< dim > > chart_points) const
virtual ~TransfiniteInterpolationManifold() override
std::array< unsigned int, 20 > get_possible_cells_around_points(const ArrayView< const Point< spacedim > > &surrounding_points) const
FlatManifold< dim > chart_manifold
Point< dim > pull_back(const typename Triangulation< dim, spacedim >::cell_iterator &cell, const Point< spacedim > &p, const Point< dim > &initial_guess) const
virtual void get_new_points(const ArrayView< const Point< spacedim > > &surrounding_points, const Table< 2, double > &weights, ArrayView< Point< spacedim > > new_points) const override
virtual Point< spacedim > get_new_point(const ArrayView< const Point< spacedim > > &surrounding_points, const ArrayView< const double > &weights) const override
std::vector< bool > coarse_cell_is_flat
Point< spacedim > push_forward(const typename Triangulation< dim, spacedim >::cell_iterator &cell, const Point< dim > &chart_point) const
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
const Manifold< dim, spacedim > & get_manifold() const
Point< spacedim > & vertex(const unsigned int i) const
ReferenceCell reference_cell() const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:40
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition config.h:603
constexpr bool running_in_debug_mode()
Definition config.h:78
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:41
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition config.h:647
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
static ::ExceptionBase & ExcTransformationFailed()
static ::ExceptionBase & ExcNotImplemented()
static ::ExceptionBase & ExcEmptyObject()
#define Assert(cond, exc)
static ::ExceptionBase & ExcImpossibleInDim(int arg1)
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcNotInitialized()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
TriaIterator< TriaAccessor< dim - 1, dim, spacedim > > face_iterator
Definition tria.h:1596
TriaIterator< CellAccessor< dim, spacedim > > cell_iterator
Definition tria.h:1557
const Manifold< dim, spacedim > & get_manifold(const types::manifold_id number) const
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
Number signed_angle(const Tensor< 1, spacedim, Number > &a, const Tensor< 1, spacedim, Number > &b, const Tensor< 1, spacedim, Number > &axis)
constexpr T fixed_power(const T t)
Definition utilities.h:943
Tensor< 1, 3 > projected_direction(const Tensor< 1, 3 > &u, const Tensor< 1, 3 > &v)
Tensor< 1, 3 > apply_exponential_map(const Tensor< 1, 3 > &u, const Tensor< 1, 3 > &dir)
static constexpr double invalid_pull_back_coordinate
Point< spacedim > compute_normal(const Tensor< 1, spacedim > &, bool=false)
constexpr double PI
Definition numbers.h:239
constexpr unsigned int invalid_unsigned_int
Definition types.h:238
constexpr types::manifold_id flat_manifold_id
Definition types.h:342
STL namespace.
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > cos(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
inline ::VectorizedArray< Number, width > acos(const ::VectorizedArray< Number, width > &x)
unsigned int manifold_id
Definition types.h:173
static double distance_to_unit_cell(const Point< dim > &p)
static unsigned int face_to_cell_vertices(const unsigned int face, const unsigned int vertex, const bool face_orientation=true, const bool face_flip=false, const bool face_rotation=false)
static constexpr unsigned int vertices_per_cell
static std_cxx20::ranges::iota_view< unsigned int, unsigned int > face_indices()
static constexpr unsigned int vertices_per_face
static unsigned int line_to_cell_vertices(const unsigned int line, const unsigned int vertex)
static std_cxx20::ranges::iota_view< unsigned int, unsigned int > vertex_indices()
static bool is_inside_unit_cell(const Point< dim > &p)
DEAL_II_HOST constexpr Number determinant(const SymmetricTensor< 2, dim, Number > &)
DEAL_II_HOST constexpr SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &)
constexpr ProductType< Number, OtherNumber >::type scalar_product(const Tensor< rank, dim, Number > &left, const Tensor< rank, dim, OtherNumber > &right)
Definition tensor.h:2547
constexpr Tensor< 1, dim, typename ProductType< Number1, Number2 >::type > cross_product_3d(const Tensor< 1, dim, Number1 > &src1, const Tensor< 1, dim, Number2 > &src2)
Definition tensor.h:2672