Reference documentation for deal.II version 9.1.0-pre
manifold_lib.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2013 - 2018 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 #include <deal.II/base/std_cxx14/memory.h>
17 #include <deal.II/base/table.h>
18 #include <deal.II/base/tensor.h>
19 
20 #include <deal.II/fe/mapping.h>
21 
22 #include <deal.II/grid/manifold_lib.h>
23 #include <deal.II/grid/tria.h>
24 #include <deal.II/grid/tria_accessor.h>
25 #include <deal.II/grid/tria_iterator.h>
26 
27 #include <deal.II/lac/vector.h>
28 
29 #include <cmath>
30 
31 DEAL_II_NAMESPACE_OPEN
32 
33 
34 namespace internal
35 {
36  // The pull_back function fails regularly in the compute_chart_points
37  // method, and, instead of throwing an exception, returns a point outside
38  // the unit cell. The individual coordinates of that point are given by the
39  // value below.
40  static constexpr double invalid_pull_back_coordinate = 20.0;
41 
42  // Rotate a given unit vector u around the axis dir
43  // where the angle is given by the length of dir.
44  // This is the exponential map for a sphere.
46  apply_exponential_map(const Tensor<1, 3> &u, const Tensor<1, 3> &dir)
47  {
48  const double theta = dir.norm();
49  if (theta < 1.e-10)
50  {
51  return u;
52  }
53  else
54  {
55  const Tensor<1, 3> dirUnit = dir / theta;
56  const Tensor<1, 3> tmp = cos(theta) * u + sin(theta) * dirUnit;
57  return tmp / tmp.norm();
58  }
59  }
60 
61  // Returns the direction to go from v to u
62  // projected to the plane perpendicular to the unit vector v.
63  // This one is more stable when u and v are nearly equal.
65  projected_direction(const Tensor<1, 3> &u, const Tensor<1, 3> &v)
66  {
67  Tensor<1, 3> ans = u - v;
68  ans -= (ans * v) * v;
69  return ans; // ans = (u-v) - ((u-v)*v)*v
70  }
71 
72  // helper function to compute a vector orthogonal to a given one.
73  // does nothing unless spacedim == 3.
74  template <int spacedim>
76  compute_normal(const Tensor<1, spacedim> & /*vector*/,
77  bool /*normalize*/ = false)
78  {
79  return {};
80  }
81 
82  Point<3>
83  compute_normal(const Tensor<1, 3> &vector, bool normalize = false)
84  {
85  Assert(vector.norm_square() != 0.,
86  ExcMessage("The direction parameter must not be zero!"));
87  Point<3> normal;
88  if (std::abs(vector[0]) >= std::abs(vector[1]) &&
89  std::abs(vector[0]) >= std::abs(vector[2]))
90  {
91  normal[1] = -1.;
92  normal[2] = -1.;
93  normal[0] = (vector[1] + vector[2]) / vector[0];
94  }
95  else if (std::abs(vector[1]) >= std::abs(vector[0]) &&
96  std::abs(vector[1]) >= std::abs(vector[2]))
97  {
98  normal[0] = -1.;
99  normal[2] = -1.;
100  normal[1] = (vector[0] + vector[2]) / vector[1];
101  }
102  else
103  {
104  normal[0] = -1.;
105  normal[1] = -1.;
106  normal[2] = (vector[0] + vector[1]) / vector[2];
107  }
108  if (normalize)
109  normal /= normal.norm();
110  return normal;
111  }
112 } // namespace internal
113 
114 
115 
116 // ============================================================
117 // PolarManifold
118 // ============================================================
119 
120 template <int dim, int spacedim>
122  : ChartManifold<dim, spacedim, spacedim>(
123  PolarManifold<dim, spacedim>::get_periodicity())
124  , center(center)
125 {}
126 
127 
128 
129 template <int dim, int spacedim>
130 std::unique_ptr<Manifold<dim, spacedim>>
132 {
133  return std_cxx14::make_unique<PolarManifold<dim, spacedim>>(center);
134 }
135 
136 
137 
138 template <int dim, int spacedim>
141 {
142  Tensor<1, spacedim> periodicity;
143  // In two dimensions, theta is periodic.
144  // In three dimensions things are a little more complicated, since the only
145  // variable that is truly periodic is phi, while theta should be bounded
146  // between 0 and pi. There is currently no way to enforce this, so here we
147  // only fix periodicity for the last variable, corresponding to theta in 2d
148  // and phi in 3d.
149  periodicity[spacedim - 1] = 2 * numbers::PI;
150  return periodicity;
151 }
152 
153 
154 
155 template <int dim, int spacedim>
158  const Point<spacedim> &spherical_point) const
159 {
160  Assert(spherical_point[0] >= 0.0,
161  ExcMessage("Negative radius for given point."));
162  const double rho = spherical_point[0];
163  const double theta = spherical_point[1];
164 
165  Point<spacedim> p;
166  if (rho > 1e-10)
167  switch (spacedim)
168  {
169  case 2:
170  p[0] = rho * cos(theta);
171  p[1] = rho * sin(theta);
172  break;
173  case 3:
174  {
175  const double phi = spherical_point[2];
176  p[0] = rho * sin(theta) * cos(phi);
177  p[1] = rho * sin(theta) * sin(phi);
178  p[2] = rho * cos(theta);
179  break;
180  }
181  default:
182  Assert(false, ExcNotImplemented());
183  }
184  return p + center;
185 }
186 
187 
188 
189 template <int dim, int spacedim>
192  const Point<spacedim> &space_point) const
193 {
194  const Tensor<1, spacedim> R = space_point - center;
195  const double rho = R.norm();
196 
197  Point<spacedim> p;
198  p[0] = rho;
199 
200  switch (spacedim)
201  {
202  case 2:
203  {
204  p[1] = atan2(R[1], R[0]);
205  if (p[1] < 0)
206  p[1] += 2 * numbers::PI;
207  break;
208  }
209 
210  case 3:
211  {
212  const double z = R[2];
213  p[2] = atan2(R[1], R[0]); // phi
214  if (p[2] < 0)
215  p[2] += 2 * numbers::PI; // phi is periodic
216  p[1] = atan2(sqrt(R[0] * R[0] + R[1] * R[1]), z); // theta
217  break;
218  }
219 
220  default:
221  Assert(false, ExcNotImplemented());
222  }
223  return p;
224 }
225 
226 
227 
228 template <int dim, int spacedim>
231  const Point<spacedim> &spherical_point) const
232 {
233  Assert(spherical_point[0] >= 0.0,
234  ExcMessage("Negative radius for given point."));
235  const double rho = spherical_point[0];
236  const double theta = spherical_point[1];
237 
239  if (rho > 1e-10)
240  switch (spacedim)
241  {
242  case 2:
243  {
244  DX[0][0] = cos(theta);
245  DX[0][1] = -rho * sin(theta);
246  DX[1][0] = sin(theta);
247  DX[1][1] = rho * cos(theta);
248  break;
249  }
250 
251  case 3:
252  {
253  const double phi = spherical_point[2];
254  DX[0][0] = sin(theta) * cos(phi);
255  DX[0][1] = rho * cos(theta) * cos(phi);
256  DX[0][2] = -rho * sin(theta) * sin(phi);
257 
258  DX[1][0] = sin(theta) * sin(phi);
259  DX[1][1] = rho * cos(theta) * sin(phi);
260  DX[1][2] = rho * sin(theta) * cos(phi);
261 
262  DX[2][0] = cos(theta);
263  DX[2][1] = -rho * sin(theta);
264  DX[2][2] = 0;
265  break;
266  }
267 
268  default:
269  Assert(false, ExcNotImplemented());
270  }
271  return DX;
272 }
273 
274 
275 
276 // ============================================================
277 // SphericalManifold
278 // ============================================================
279 
280 template <int dim, int spacedim>
282  const Point<spacedim> center)
283  : center(center)
284  , polar_manifold(center)
285 {}
286 
287 
288 
289 template <int dim, int spacedim>
290 std::unique_ptr<Manifold<dim, spacedim>>
292 {
293  return std_cxx14::make_unique<SphericalManifold<dim, spacedim>>(center);
294 }
295 
296 
297 
298 template <int dim, int spacedim>
301  const Point<spacedim> &p1,
302  const Point<spacedim> &p2,
303  const double w) const
304 {
305  const double tol = 1e-10;
306 
307  if ((p1 - p2).norm_square() < tol * tol || std::abs(w) < tol)
308  return p1;
309  else if (std::abs(w - 1.0) < tol)
310  return p2;
311 
312  // If the points are one dimensional then there is no need for anything but
313  // a linear combination.
314  if (spacedim == 1)
315  return Point<spacedim>(w * p2 + (1 - w) * p1);
316 
317  const Tensor<1, spacedim> v1 = p1 - center;
318  const Tensor<1, spacedim> v2 = p2 - center;
319  const double r1 = v1.norm();
320  const double r2 = v2.norm();
321 
322  Assert(r1 > tol && r2 > tol,
323  ExcMessage("p1 and p2 cannot coincide with the center."));
324 
325  const Tensor<1, spacedim> e1 = v1 / r1;
326  const Tensor<1, spacedim> e2 = v2 / r2;
327 
328  // Find the cosine of the angle gamma described by v1 and v2.
329  const double cosgamma = e1 * e2;
330 
331  // Points are collinear with the center (allow for 8*eps as a tolerance)
332  if (cosgamma < -1 + 8. * std::numeric_limits<double>::epsilon())
333  return center;
334 
335  // Points are along a line, in which case e1 and e2 are essentially the same.
336  if (cosgamma > 1 - 8. * std::numeric_limits<double>::epsilon())
337  return Point<spacedim>(center + w * v2 + (1 - w) * v1);
338 
339  // Find the angle sigma that corresponds to arclength equal to w. acos
340  // should never be undefined because we have ruled out the two special cases
341  // above.
342  const double sigma = w * std::acos(cosgamma);
343 
344  // Normal to v1 in the plane described by v1,v2,and the origin.
345  // Since p1 and p2 do not coincide n is not zero and well defined.
346  Tensor<1, spacedim> n = v2 - (v2 * e1) * e1;
347  const double n_norm = n.norm();
348  Assert(n_norm > 0,
349  ExcInternalError("n should be different from the null vector. "
350  "Probably, this means v1==v2 or v2==0."));
351 
352  n /= n_norm;
353 
354  // Find the point Q along O,v1 such that
355  // P1,V,P2 has measure sigma.
356  const Tensor<1, spacedim> P = std::cos(sigma) * e1 + std::sin(sigma) * n;
357 
358  // Project this point on the manifold.
359  return Point<spacedim>(center + (w * r2 + (1.0 - w) * r1) * P);
360 }
361 
362 
363 
364 template <int dim, int spacedim>
367  const Point<spacedim> &p1,
368  const Point<spacedim> &p2) const
369 {
370  const double tol = 1e-10;
371  (void)tol;
372 
373  Assert(p1 != p2, ExcMessage("p1 and p2 should not concide."));
374 
375  const Tensor<1, spacedim> v1 = p1 - center;
376  const Tensor<1, spacedim> v2 = p2 - center;
377  const double r1 = v1.norm();
378  const double r2 = v2.norm();
379 
380  Assert(r1 > tol, ExcMessage("p1 cannot coincide with the center."));
381 
382  Assert(r2 > tol, ExcMessage("p2 cannot coincide with the center."));
383 
384  const Tensor<1, spacedim> e1 = v1 / r1;
385  const Tensor<1, spacedim> e2 = v2 / r2;
386 
387  // Find the cosine of the angle gamma described by v1 and v2.
388  const double cosgamma = e1 * e2;
389 
390  Assert(cosgamma > -1 + 8. * std::numeric_limits<double>::epsilon(),
391  ExcMessage("p1 and p2 cannot lie on the same diameter and be opposite "
392  "respect to the center."));
393 
394  if (cosgamma > 1 - 8. * std::numeric_limits<double>::epsilon())
395  return v2 - v1;
396 
397  // Normal to v1 in the plane described by v1,v2,and the origin.
398  // Since p1 and p2 do not coincide n is not zero and well defined.
399  Tensor<1, spacedim> n = v2 - (v2 * e1) * e1;
400  const double n_norm = n.norm();
401  Assert(n_norm > 0,
402  ExcInternalError("n should be different from the null vector. "
403  "Probably, this means v1==v2 or v2==0."));
404 
405  n /= n_norm;
406 
407  // this is the derivative of the geodesic in get_intermediate_point
408  // derived with respect to w and inserting w=0.
409  const double gamma = std::acos(cosgamma);
410  return (r2 - r1) * e1 + r1 * gamma * n;
411 }
412 
413 
414 
415 template <int dim, int spacedim>
418  const typename Triangulation<dim, spacedim>::face_iterator &face,
419  const Point<spacedim> & p) const
420 {
421  // if the maximum deviation for the distance from the vertices to the center
422  // is less than 1.e-5 of the minimum distance to the first vertex, assume we
423  // can simply return p-center. otherwise, we compute the normal using
424  // get_normal_vector
425  constexpr unsigned int n_vertices = GeometryInfo<spacedim>::vertices_per_face;
426  std::array<double, n_vertices> distances_to_center;
427  std::array<double, n_vertices - 1> distances_to_first_vertex;
428  distances_to_center[0] = (face->vertex(0) - center).norm_square();
429  for (unsigned int i = 1; i < n_vertices; ++i)
430  {
431  distances_to_center[i] = (face->vertex(i) - center).norm_square();
432  distances_to_first_vertex[i - 1] =
433  (face->vertex(i) - face->vertex(0)).norm_square();
434  }
435  const auto minmax_distance =
436  std::minmax_element(distances_to_center.begin(), distances_to_center.end());
437  const auto min_distance_to_first_vertex =
438  std::min_element(distances_to_first_vertex.begin(),
439  distances_to_first_vertex.end());
440 
441  if (*minmax_distance.second - *minmax_distance.first <
442  1.e-10 * *min_distance_to_first_vertex)
443  {
444  const Tensor<1, spacedim> unnormalized_spherical_normal = p - center;
445  const Tensor<1, spacedim> normalized_spherical_normal =
446  unnormalized_spherical_normal / unnormalized_spherical_normal.norm();
447  return normalized_spherical_normal;
448  }
450 }
451 
452 
453 
454 template <>
455 void
459 {
460  Assert(false, ExcImpossibleInDim(1));
461 }
462 
463 
464 
465 template <>
466 void
470 {
471  Assert(false, ExcImpossibleInDim(1));
472 }
473 
474 
475 
476 template <int dim, int spacedim>
477 void
479  const typename Triangulation<dim, spacedim>::face_iterator &face,
480  typename Manifold<dim, spacedim>::FaceVertexNormals &face_vertex_normals)
481  const
482 {
483  // if the maximum deviation for the distance from the vertices to the center
484  // is less than 1.e-5 of the minimum distance to the first vertex, assume we
485  // can simply return vertex-center. otherwise, we compute the normal using
486  // get_normal_vector
487  constexpr unsigned int n_vertices = GeometryInfo<spacedim>::vertices_per_face;
488  std::array<double, n_vertices> distances_to_center;
489  std::array<double, n_vertices - 1> distances_to_first_vertex;
490  distances_to_center[0] = (face->vertex(0) - center).norm_square();
491  for (unsigned int i = 1; i < n_vertices; ++i)
492  {
493  distances_to_center[i] = (face->vertex(i) - center).norm_square();
494  distances_to_first_vertex[i - 1] =
495  (face->vertex(i) - face->vertex(0)).norm_square();
496  }
497  const auto minmax_distance =
498  std::minmax_element(distances_to_center.begin(), distances_to_center.end());
499  const auto min_distance_to_first_vertex =
500  std::min_element(distances_to_first_vertex.begin(),
501  distances_to_first_vertex.end());
502 
503  if (*minmax_distance.second - *minmax_distance.first <
504  1.e-10 * *min_distance_to_first_vertex)
505  {
506  for (unsigned int vertex = 0; vertex < n_vertices; ++vertex)
507  face_vertex_normals[vertex] = face->vertex(vertex) - center;
508  }
509  else
510  Manifold<dim, spacedim>::get_normals_at_vertices(face, face_vertex_normals);
511 }
512 
513 
514 
515 template <int dim, int spacedim>
516 void
518  const ArrayView<const Point<spacedim>> &surrounding_points,
519  const Table<2, double> & weights,
520  ArrayView<Point<spacedim>> new_points) const
521 {
522  AssertDimension(new_points.size(), weights.size(0));
523  AssertDimension(surrounding_points.size(), weights.size(1));
524 
525  get_new_points(surrounding_points, make_array_view(weights), new_points);
526 
527  return;
528 }
529 
530 
531 
532 template <int dim, int spacedim>
535  const ArrayView<const Point<spacedim>> &vertices,
536  const ArrayView<const double> & weights) const
537 {
538  // To avoid duplicating all of the logic in get_new_points, simply call it
539  // for one position.
540  Point<spacedim> new_point;
541  get_new_points(vertices,
542  weights,
543  make_array_view(&new_point, &new_point + 1));
544 
545  return new_point;
546 }
547 
548 
549 
550 template <int dim, int spacedim>
551 void
553  const ArrayView<const Point<spacedim>> &surrounding_points,
554  const ArrayView<const double> & weights,
555  ArrayView<Point<spacedim>> new_points) const
556 {
557  AssertDimension(weights.size(),
558  new_points.size() * surrounding_points.size());
559  const unsigned int weight_rows = new_points.size();
560  const unsigned int weight_columns = surrounding_points.size();
561 
562  if (surrounding_points.size() == 2)
563  {
564  for (unsigned int row = 0; row < weight_rows; ++row)
565  new_points[row] =
566  get_intermediate_point(surrounding_points[0],
567  surrounding_points[1],
568  weights[row * weight_columns + 1]);
569  return;
570  }
571 
572  boost::container::small_vector<std::pair<double, Tensor<1, spacedim>>, 100>
573  new_candidates(new_points.size());
574  boost::container::small_vector<Tensor<1, spacedim>, 100> directions(
575  surrounding_points.size(), Point<spacedim>());
576  boost::container::small_vector<double, 100> distances(
577  surrounding_points.size(), 0.0);
578  double max_distance = 0.;
579  for (unsigned int i = 0; i < surrounding_points.size(); ++i)
580  {
581  directions[i] = surrounding_points[i] - center;
582  distances[i] = directions[i].norm();
583 
584  if (distances[i] != 0.)
585  directions[i] /= distances[i];
586  else
587  Assert(false,
588  ExcMessage("One of the vertices coincides with the center. "
589  "This is not allowed!"));
590 
591  // Check if an estimate is good enough,
592  // this is often the case for sufficiently refined meshes.
593  for (unsigned int k = 0; k < i; ++k)
594  {
595  const double squared_distance =
596  (directions[i] - directions[k]).norm_square();
597  max_distance = std::max(max_distance, squared_distance);
598  }
599  }
600 
601  // Step 1: Check for some special cases, create simple linear guesses
602  // otherwise.
603  const double tolerance = 1e-10;
604  boost::container::small_vector<bool, 100> accurate_point_was_found(
605  new_points.size(), false);
606  const ArrayView<const Tensor<1, spacedim>> array_directions =
607  make_array_view(directions.begin(), directions.end());
608  const ArrayView<const double> array_distances =
609  make_array_view(distances.begin(), distances.end());
610  for (unsigned int row = 0; row < weight_rows; ++row)
611  {
612  new_candidates[row] =
613  guess_new_point(array_directions,
614  array_distances,
615  ArrayView<const double>(&weights[row * weight_columns],
616  weight_columns));
617 
618  // If the candidate is the center, mark it as found to avoid entering
619  // the Newton iteration in step 2, which would crash.
620  if (new_candidates[row].first == 0.0)
621  {
622  new_points[row] = center;
623  accurate_point_was_found[row] = true;
624  continue;
625  }
626 
627  // If not in 3D, just use the implementation from PolarManifold
628  // after we verified that the candidate is not the center.
629  if (spacedim < 3)
630  new_points[row] = polar_manifold.get_new_point(
631  surrounding_points,
632  ArrayView<const double>(&weights[row * weight_columns],
633  weight_columns));
634  }
635 
636  // In this case, we treated the case that the candidate is the center and
637  // obtained the new locations from the PolarManifold object otherwise.
638  if (spacedim < 3)
639  return;
640 
641  // If all the points are close to each other, we expect the estimate to
642  // be good enough. This tolerance was chosen such that the first iteration
643  // for a at least three time refined HyperShell mesh with radii .5 and 1.
644  // doesn't already succeed.
645  if (max_distance < 2e-2)
646  {
647  for (unsigned int row = 0; row < weight_rows; ++row)
648  new_points[row] =
649  center + new_candidates[row].first * new_candidates[row].second;
650 
651  return;
652  }
653 
654  // Step 2:
655  // Do more expensive Newton-style iterations to improve the estimate.
656 
657  // Search for duplicate directions and merge them to minimize the cost of
658  // the get_new_point function call below.
659  boost::container::small_vector<double, 1000> merged_weights(weights.size());
660  boost::container::small_vector<Tensor<1, spacedim>, 100> merged_directions(
661  surrounding_points.size(), Point<spacedim>());
662  boost::container::small_vector<double, 100> merged_distances(
663  surrounding_points.size(), 0.0);
664 
665  unsigned int n_unique_directions = 0;
666  for (unsigned int i = 0; i < surrounding_points.size(); ++i)
667  {
668  bool found_duplicate = false;
669 
670  // This inner loop is of @f$O(N^2)@f$ complexity, but
671  // surrounding_points.size() is usually at most 8 points large.
672  for (unsigned int j = 0; j < n_unique_directions; ++j)
673  {
674  const double squared_distance =
675  (directions[i] - directions[j]).norm_square();
676  if (!found_duplicate && squared_distance < 1e-28)
677  {
678  found_duplicate = true;
679  for (unsigned int row = 0; row < weight_rows; ++row)
680  merged_weights[row * weight_columns + j] +=
681  weights[row * weight_columns + i];
682  }
683  }
684 
685  if (found_duplicate == false)
686  {
687  merged_directions[n_unique_directions] = directions[i];
688  merged_distances[n_unique_directions] = distances[i];
689  for (unsigned int row = 0; row < weight_rows; ++row)
690  merged_weights[row * weight_columns + n_unique_directions] =
691  weights[row * weight_columns + i];
692 
693  ++n_unique_directions;
694  }
695  }
696 
697  // Search for duplicate weight rows and merge them to minimize the cost of
698  // the get_new_point function call below.
699  boost::container::small_vector<unsigned int, 100> merged_weights_index(
700  new_points.size(), numbers::invalid_unsigned_int);
701  for (unsigned int row = 0; row < weight_rows; ++row)
702  {
703  for (unsigned int existing_row = 0; existing_row < row; ++existing_row)
704  {
705  bool identical_weights = true;
706 
707  for (unsigned int weight_index = 0;
708  weight_index < n_unique_directions;
709  ++weight_index)
710  if (std::abs(merged_weights[row * weight_columns + weight_index] -
711  merged_weights[existing_row * weight_columns +
712  weight_index]) > tolerance)
713  {
714  identical_weights = false;
715  break;
716  }
717 
718  if (identical_weights)
719  {
720  merged_weights_index[row] = existing_row;
721  break;
722  }
723  }
724  }
725 
726  // Note that we only use the n_unique_directions first entries in the
727  // ArrayView
728  const ArrayView<const Tensor<1, spacedim>> array_merged_directions =
729  make_array_view(merged_directions.begin(),
730  merged_directions.begin() + n_unique_directions);
731  const ArrayView<const double> array_merged_distances =
732  make_array_view(merged_distances.begin(),
733  merged_distances.begin() + n_unique_directions);
734 
735  for (unsigned int row = 0; row < weight_rows; ++row)
736  if (!accurate_point_was_found[row])
737  {
738  if (merged_weights_index[row] == numbers::invalid_unsigned_int)
739  {
740  const ArrayView<const double> array_merged_weights(
741  &merged_weights[row * weight_columns], n_unique_directions);
742  new_candidates[row].second =
743  get_new_point(array_merged_directions,
744  array_merged_distances,
745  array_merged_weights,
746  Point<spacedim>(new_candidates[row].second));
747  }
748  else
749  new_candidates[row].second =
750  new_candidates[merged_weights_index[row]].second;
751 
752  new_points[row] =
753  center + new_candidates[row].first * new_candidates[row].second;
754  }
755 }
756 
757 
758 
759 template <int dim, int spacedim>
760 std::pair<double, Tensor<1, spacedim>>
762  const ArrayView<const Tensor<1, spacedim>> &directions,
763  const ArrayView<const double> & distances,
764  const ArrayView<const double> & weights) const
765 {
766  const double tolerance = 1e-10;
767  double rho = 0.;
768  Tensor<1, spacedim> candidate;
769 
770  // Perform a simple average ...
771  double total_weights = 0.;
772  for (unsigned int i = 0; i < directions.size(); i++)
773  {
774  // if one weight is one, return its direction
775  if (std::abs(1 - weights[i]) < tolerance)
776  return std::make_pair(distances[i], directions[i]);
777 
778  rho += distances[i] * weights[i];
779  candidate += directions[i] * weights[i];
780  total_weights += weights[i];
781  }
782 
783  // ... and normalize if the candidate is different from the origin.
784  const double norm = candidate.norm();
785  if (norm == 0.)
786  return std::make_pair(0.0, Point<spacedim>());
787  candidate /= norm;
788  rho /= total_weights;
789 
790  return std::make_pair(rho, candidate);
791 }
792 
793 
794 namespace
795 {
796  template <int spacedim>
798  do_get_new_point(const ArrayView<const Tensor<1, spacedim>> & /*directions*/,
799  const ArrayView<const double> & /*distances*/,
800  const ArrayView<const double> & /*weights*/,
801  const Point<spacedim> & /*candidate_point*/)
802  {
803  Assert(false, ExcNotImplemented());
804  return Point<spacedim>();
805  }
806 
807  template <>
808  Point<3>
809  do_get_new_point(const ArrayView<const Tensor<1, 3>> &directions,
810  const ArrayView<const double> & distances,
811  const ArrayView<const double> & weights,
812  const Point<3> & candidate_point)
813  {
814  (void)distances;
815 
816  AssertDimension(directions.size(), distances.size());
817  AssertDimension(directions.size(), weights.size());
818 
819  Point<3> candidate = candidate_point;
820  const unsigned int n_merged_points = directions.size();
821  const double tolerance = 1e-10;
822  const int max_iterations = 10;
823 
824  {
825  // If the candidate happens to coincide with a normalized
826  // direction, we return it. Otherwise, the Hessian would be singular.
827  for (unsigned int i = 0; i < n_merged_points; ++i)
828  {
829  const double squared_distance =
830  (candidate - directions[i]).norm_square();
831  if (squared_distance < tolerance * tolerance)
832  return candidate;
833  }
834 
835  // check if we only have two points now, in which case we can use the
836  // get_intermediate_point function
837  if (n_merged_points == 2)
838  {
839  SphericalManifold<3, 3> unit_manifold;
840  Assert(std::abs(weights[0] + weights[1] - 1.0) < 1e-13,
841  ExcMessage("Weights do not sum up to 1"));
842  Point<3> intermediate =
843  unit_manifold.get_intermediate_point(Point<3>(directions[0]),
844  Point<3>(directions[1]),
845  weights[1]);
846  return intermediate;
847  }
848 
849  Tensor<1, 3> vPerp;
850  Tensor<2, 2> Hessian;
851  Tensor<1, 2> gradient;
852  Tensor<1, 2> gradlocal;
853 
854  // On success we exit the loop early.
855  // Otherwise, we just take the result after max_iterations steps.
856  for (unsigned int i = 0; i < max_iterations; ++i)
857  {
858  // Step 2a: Find new descent direction
859 
860  // Get local basis for the estimate candidate
861  const Tensor<1, 3> Clocalx = internal::compute_normal(candidate);
862  const Tensor<1, 3> Clocaly = cross_product_3d(candidate, Clocalx);
863 
864  // For each vertices vector, compute the tangent vector from candidate
865  // towards the vertices vector -- its length is the spherical length
866  // from candidate to the vertices vector.
867  // Then compute its contribution to the Hessian.
868  gradient = 0.;
869  Hessian = 0.;
870  for (unsigned int i = 0; i < n_merged_points; ++i)
871  if (std::abs(weights[i]) > 1.e-15)
872  {
873  vPerp = internal::projected_direction(directions[i], candidate);
874  const double sinthetaSq = vPerp.norm_square();
875  const double sintheta = std::sqrt(sinthetaSq);
876  if (sintheta < tolerance)
877  {
878  Hessian[0][0] += weights[i];
879  Hessian[1][1] += weights[i];
880  }
881  else
882  {
883  const double costheta = (directions[i]) * candidate;
884  const double theta = atan2(sintheta, costheta);
885  const double sincthetaInv = theta / sintheta;
886 
887  const double cosphi = vPerp * Clocalx;
888  const double sinphi = vPerp * Clocaly;
889 
890  gradlocal[0] = cosphi;
891  gradlocal[1] = sinphi;
892  gradient += (weights[i] * sincthetaInv) * gradlocal;
893 
894  const double wt = weights[i] / sinthetaSq;
895  const double sinphiSq = sinphi * sinphi;
896  const double cosphiSq = cosphi * cosphi;
897  const double tt = sincthetaInv * costheta;
898  const double offdiag = cosphi * sinphi * wt * (1.0 - tt);
899  Hessian[0][0] += wt * (cosphiSq + tt * sinphiSq);
900  Hessian[0][1] += offdiag;
901  Hessian[1][0] += offdiag;
902  Hessian[1][1] += wt * (sinphiSq + tt * cosphiSq);
903  }
904  }
905 
906  Assert(determinant(Hessian) > tolerance, ExcInternalError());
907 
908  const Tensor<2, 2> inverse_Hessian = invert(Hessian);
909 
910  const Tensor<1, 2> xDisplocal = inverse_Hessian * gradient;
911  const Tensor<1, 3> xDisp =
912  xDisplocal[0] * Clocalx + xDisplocal[1] * Clocaly;
913 
914  // Step 2b: rotate candidate in direction xDisp for a new candidate.
915  const Point<3> candidateOld = candidate;
916  candidate =
917  Point<3>(internal::apply_exponential_map(candidate, xDisp));
918 
919  // Step 2c: return the new candidate if we didn't move
920  if ((candidate - candidateOld).norm_square() < tolerance * tolerance)
921  break;
922  }
923  }
924  return candidate;
925  }
926 } // namespace
927 
928 
929 
930 template <int dim, int spacedim>
933  const ArrayView<const Tensor<1, spacedim>> &,
934  const ArrayView<const double> &,
935  const ArrayView<const double> &,
936  const Point<spacedim> &) const
937 {
938  Assert(false, ExcNotImplemented());
939  return Point<spacedim>();
940 }
941 
942 
943 
944 template <>
945 Point<3>
947  const ArrayView<const Tensor<1, 3>> &directions,
948  const ArrayView<const double> & distances,
949  const ArrayView<const double> & weights,
950  const Point<3> & candidate_point) const
951 {
952  return do_get_new_point(directions, distances, weights, candidate_point);
953 }
954 
955 
956 
957 template <>
958 Point<3>
960  const ArrayView<const Tensor<1, 3>> &directions,
961  const ArrayView<const double> & distances,
962  const ArrayView<const double> & weights,
963  const Point<3> & candidate_point) const
964 {
965  return do_get_new_point(directions, distances, weights, candidate_point);
966 }
967 
968 
969 
970 template <>
971 Point<3>
973  const ArrayView<const Tensor<1, 3>> &directions,
974  const ArrayView<const double> & distances,
975  const ArrayView<const double> & weights,
976  const Point<3> & candidate_point) const
977 {
978  return do_get_new_point(directions, distances, weights, candidate_point);
979 }
980 
981 
982 
983 // ============================================================
984 // CylindricalManifold
985 // ============================================================
986 template <int dim, int spacedim>
988  const double tolerance)
989  : CylindricalManifold<dim, spacedim>(Point<spacedim>::unit_vector(axis),
990  Point<spacedim>(),
991  tolerance)
992 {
993  // do not use static_assert to make dimension-independent programming
994  // easier.
995  Assert(spacedim == 3,
996  ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
997 }
998 
999 
1000 
1001 template <int dim, int spacedim>
1005  const double tolerance)
1006  : ChartManifold<dim, spacedim, 3>(Tensor<1, 3>({0, 2. * numbers::PI, 0}))
1007  , normal_direction(internal::compute_normal(direction, true))
1010  , tolerance(tolerance)
1011 {
1012  // do not use static_assert to make dimension-independent programming
1013  // easier.
1014  Assert(spacedim == 3,
1015  ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1016 }
1017 
1018 
1019 
1020 template <int dim, int spacedim>
1021 std::unique_ptr<Manifold<dim, spacedim>>
1023 {
1024  return std_cxx14::make_unique<CylindricalManifold<dim, spacedim>>(
1026 }
1027 
1028 
1029 
1030 template <int dim, int spacedim>
1033  const ArrayView<const Point<spacedim>> &surrounding_points,
1034  const ArrayView<const double> & weights) const
1035 {
1036  Assert(spacedim == 3,
1037  ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1038 
1039  // First check if the average in space lies on the axis.
1040  Point<spacedim> middle;
1041  double average_length = 0.;
1042  for (unsigned int i = 0; i < surrounding_points.size(); ++i)
1043  {
1044  middle += surrounding_points[i] * weights[i];
1045  average_length += surrounding_points[i].square() * weights[i];
1046  }
1047  middle -= point_on_axis;
1048  const double lambda = middle * direction;
1049 
1050  if ((middle - direction * lambda).square() < tolerance * average_length)
1051  return Point<spacedim>() + direction * lambda;
1052  else // If not, using the ChartManifold should yield valid results.
1053  return ChartManifold<dim, spacedim, 3>::get_new_point(surrounding_points,
1054  weights);
1055 }
1056 
1057 
1058 
1059 template <int dim, int spacedim>
1060 Point<3>
1062  const Point<spacedim> &space_point) const
1063 {
1064  Assert(spacedim == 3,
1065  ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1066 
1067  // First find the projection of the given point to the axis.
1068  const Tensor<1, spacedim> normalized_point = space_point - point_on_axis;
1069  const double lambda = normalized_point * direction;
1070  const Point<spacedim> projection = point_on_axis + direction * lambda;
1071  const Tensor<1, spacedim> p_diff = space_point - projection;
1072 
1073  // Then compute the angle between the projection direction and
1074  // another vector orthogonal to the direction vector.
1075  const double dot = normal_direction * p_diff;
1076  const double det = direction * cross_product_3d(normal_direction, p_diff);
1077  const double phi = std::atan2(det, dot);
1078 
1079  // Return distance from the axis, angle and signed distance on the axis.
1080  return Point<3>(p_diff.norm(), phi, lambda);
1081 }
1082 
1083 
1084 
1085 template <int dim, int spacedim>
1088  const Point<3> &chart_point) const
1089 {
1090  Assert(spacedim == 3,
1091  ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1092 
1093  // Rotate the orthogonal direction by the given angle.
1094  // Formula from Section 5.2 in
1095  // http://inside.mines.edu/fs_home/gmurray/ArbitraryAxisRotation/
1096  // simplified assuming normal_direction and direction are orthogonal
1097  // and unit vectors.
1098  const double sine_r = std::sin(chart_point(1)) * chart_point(0);
1099  const double cosine_r = std::cos(chart_point(1)) * chart_point(0);
1100  const Tensor<1, spacedim> dxn = cross_product_3d(direction, normal_direction);
1101  const Tensor<1, spacedim> intermediate =
1102  normal_direction * cosine_r + dxn * sine_r;
1103 
1104  // Finally, put everything together.
1105  return point_on_axis + direction * chart_point(2) + intermediate;
1106 }
1107 
1108 
1109 
1110 template <int dim, int spacedim>
1113  const Point<3> &chart_point) const
1114 {
1115  Assert(spacedim == 3,
1116  ExcMessage("CylindricalManifold can only be used for spacedim==3!"));
1117 
1118  Tensor<2, 3> derivatives;
1119 
1120  // Rotate the orthogonal direction by the given angle.
1121  // Formula from Section 5.2 in
1122  // http://inside.mines.edu/fs_home/gmurray/ArbitraryAxisRotation/
1123  // simplified assuming normal_direction and direction are orthogonal
1124  // and unit vectors.
1125  const double sine = std::sin(chart_point(1));
1126  const double cosine = std::cos(chart_point(1));
1127  const Tensor<1, spacedim> dxn = cross_product_3d(direction, normal_direction);
1128  const Tensor<1, spacedim> intermediate =
1129  normal_direction * cosine + dxn * sine;
1130 
1131  // derivative w.r.t the radius
1132  derivatives[0][0] = intermediate[0];
1133  derivatives[1][0] = intermediate[1];
1134  derivatives[2][0] = intermediate[2];
1135 
1136  // derivatives w.r.t the angle
1137  derivatives[0][1] = -normal_direction[0] * sine + dxn[0] * cosine;
1138  derivatives[1][1] = -normal_direction[1] * sine + dxn[1] * cosine;
1139  derivatives[2][1] = -normal_direction[2] * sine + dxn[2] * cosine;
1140 
1141  // derivatives w.r.t the direction of the axis
1142  derivatives[0][2] = direction[0];
1143  derivatives[1][2] = direction[1];
1144  derivatives[2][2] = direction[2];
1145 
1146  return derivatives;
1147 }
1148 
1149 
1150 
1151 // ============================================================
1152 // FunctionManifold
1153 // ============================================================
1154 template <int dim, int spacedim, int chartdim>
1156  const Function<chartdim> & push_forward_function,
1157  const Function<spacedim> & pull_back_function,
1158  const Tensor<1, chartdim> &periodicity,
1159  const double tolerance)
1160  : ChartManifold<dim, spacedim, chartdim>(periodicity)
1161  , push_forward_function(&push_forward_function)
1162  , pull_back_function(&pull_back_function)
1163  , tolerance(tolerance)
1164  , owns_pointers(false)
1165  , finite_difference_step(0)
1166 {
1167  AssertDimension(push_forward_function.n_components, spacedim);
1168  AssertDimension(pull_back_function.n_components, chartdim);
1169 }
1170 
1171 
1172 
1173 template <int dim, int spacedim, int chartdim>
1175  const std::string push_forward_expression,
1176  const std::string pull_back_expression,
1177  const Tensor<1, chartdim> & periodicity,
1179  const std::string chart_vars,
1180  const std::string space_vars,
1181  const double tolerance,
1182  const double h)
1183  : ChartManifold<dim, spacedim, chartdim>(periodicity)
1184  , const_map(const_map)
1185  , tolerance(tolerance)
1186  , owns_pointers(true)
1187  , push_forward_expression(push_forward_expression)
1188  , pull_back_expression(pull_back_expression)
1189  , chart_vars(chart_vars)
1190  , space_vars(space_vars)
1192 {
1193  FunctionParser<chartdim> *pf = new FunctionParser<chartdim>(spacedim, 0.0, h);
1194  FunctionParser<spacedim> *pb = new FunctionParser<spacedim>(chartdim, 0.0, h);
1195  pf->initialize(chart_vars, push_forward_expression, const_map);
1196  pb->initialize(space_vars, pull_back_expression, const_map);
1197  push_forward_function = pf;
1198  pull_back_function = pb;
1199 }
1200 
1201 
1202 
1203 template <int dim, int spacedim, int chartdim>
1205 {
1206  if (owns_pointers == true)
1207  {
1209  push_forward_function = nullptr;
1210  delete pf;
1211 
1213  pull_back_function = nullptr;
1214  delete pb;
1215  }
1216 }
1217 
1218 
1219 
1220 template <int dim, int spacedim, int chartdim>
1221 std::unique_ptr<Manifold<dim, spacedim>>
1223 {
1224  // This manifold can be constructed either by providing an expression for the
1225  // push forward and the pull back charts, or by providing two Function
1226  // objects. In the first case, the push_forward and pull_back functions are
1227  // created internally in FunctionManifold, and destroyed when this object is
1228  // deleted. We need to make sure that our cloned object is constructed in the
1229  // same way this class was constructed, and that its internal Function
1230  // pointers point either to the same Function objects used to construct this
1231  // function (owns_pointers == false) or that the newly generated manifold
1232  // creates internally the push_forward and pull_back functions using the same
1233  // expressions that were used to construct this class (own_pointers == true).
1234  if (owns_pointers == true)
1235  {
1236  return std_cxx14::make_unique<FunctionManifold<dim, spacedim, chartdim>>(
1239  this->get_periodicity(),
1240  const_map,
1241  chart_vars,
1242  space_vars,
1243  tolerance,
1245  }
1246  else
1247  return std_cxx14::make_unique<FunctionManifold<dim, spacedim, chartdim>>(
1250  this->get_periodicity(),
1251  tolerance);
1252 }
1253 
1254 
1255 
1256 template <int dim, int spacedim, int chartdim>
1259  const Point<chartdim> &chart_point) const
1260 {
1261  Vector<double> pf(spacedim);
1262  Point<spacedim> result;
1263  push_forward_function->vector_value(chart_point, pf);
1264  for (unsigned int i = 0; i < spacedim; ++i)
1265  result[i] = pf[i];
1266 
1267 #ifdef DEBUG
1268  Vector<double> pb(chartdim);
1269  pull_back_function->vector_value(result, pb);
1270  for (unsigned int i = 0; i < chartdim; ++i)
1271  Assert(
1272  (chart_point.norm() > tolerance &&
1273  (std::abs(pb[i] - chart_point[i]) < tolerance * chart_point.norm())) ||
1274  (std::abs(pb[i] - chart_point[i]) < tolerance),
1275  ExcMessage(
1276  "The push forward is not the inverse of the pull back! Bailing out."));
1277 #endif
1278 
1279  return result;
1280 }
1281 
1282 
1283 
1284 template <int dim, int spacedim, int chartdim>
1287  const Point<chartdim> &chart_point) const
1288 {
1290  for (unsigned int i = 0; i < spacedim; ++i)
1291  {
1292  const auto gradient = push_forward_function->gradient(chart_point, i);
1293  for (unsigned int j = 0; j < chartdim; ++j)
1294  DF[i][j] = gradient[j];
1295  }
1296  return DF;
1297 }
1298 
1299 
1300 
1301 template <int dim, int spacedim, int chartdim>
1304  const Point<spacedim> &space_point) const
1305 {
1306  Vector<double> pb(chartdim);
1307  Point<chartdim> result;
1308  pull_back_function->vector_value(space_point, pb);
1309  for (unsigned int i = 0; i < chartdim; ++i)
1310  result[i] = pb[i];
1311  return result;
1312 }
1313 
1314 
1315 
1316 // ============================================================
1317 // TorusManifold
1318 // ============================================================
1319 template <int dim>
1320 Point<3>
1322 {
1323  double x = p(0);
1324  double z = p(1);
1325  double y = p(2);
1326  double phi = atan2(y, x);
1327  double theta = atan2(z, std::sqrt(x * x + y * y) - R);
1328  double w =
1329  std::sqrt(pow(y - sin(phi) * R, 2.0) + pow(x - cos(phi) * R, 2.0) + z * z) /
1330  r;
1331  return Point<3>(phi, theta, w);
1332 }
1333 
1334 
1335 
1336 template <int dim>
1337 Point<3>
1339 {
1340  double phi = chart_point(0);
1341  double theta = chart_point(1);
1342  double w = chart_point(2);
1343 
1344  return Point<3>(cos(phi) * R + r * w * cos(theta) * cos(phi),
1345  r * w * sin(theta),
1346  sin(phi) * R + r * w * cos(theta) * sin(phi));
1347 }
1348 
1349 
1350 
1351 template <int dim>
1352 TorusManifold<dim>::TorusManifold(const double R, const double r)
1353  : ChartManifold<dim, 3, 3>(Point<3>(2 * numbers::PI, 2 * numbers::PI, 0.0))
1354  , r(r)
1355  , R(R)
1356 {
1357  Assert(R > r,
1358  ExcMessage("Outer radius R must be greater than the inner "
1359  "radius r."));
1360  Assert(r > 0.0, ExcMessage("inner radius must be positive."));
1361 }
1362 
1363 
1364 
1365 template <int dim>
1366 std::unique_ptr<Manifold<dim, 3>>
1368 {
1369  return std_cxx14::make_unique<TorusManifold<dim>>(R, r);
1370 }
1371 
1372 
1373 
1374 template <int dim>
1377 {
1379 
1380  double phi = chart_point(0);
1381  double theta = chart_point(1);
1382  double w = chart_point(2);
1383 
1384  DX[0][0] = -sin(phi) * R - r * w * cos(theta) * sin(phi);
1385  DX[0][1] = -r * w * sin(theta) * cos(phi);
1386  DX[0][2] = r * cos(theta) * cos(phi);
1387 
1388  DX[1][0] = 0;
1389  DX[1][1] = r * w * cos(theta);
1390  DX[1][2] = r * sin(theta);
1391 
1392  DX[2][0] = cos(phi) * R + r * w * cos(theta) * cos(phi);
1393  DX[2][1] = -r * w * sin(theta) * sin(phi);
1394  DX[2][2] = r * cos(theta) * sin(phi);
1395 
1396  return DX;
1397 }
1398 
1399 
1400 
1401 // ============================================================
1402 // TransfiniteInterpolationManifold
1403 // ============================================================
1404 template <int dim, int spacedim>
1407  : triangulation(nullptr)
1408  , level_coarse(-1)
1409 {
1410  AssertThrow(dim > 1, ExcNotImplemented());
1411 }
1412 
1413 
1414 
1415 template <int dim, int spacedim>
1418 {
1419  if (clear_signal.connected())
1420  clear_signal.disconnect();
1421 }
1422 
1423 
1424 
1425 template <int dim, int spacedim>
1426 std::unique_ptr<Manifold<dim, spacedim>>
1428 {
1430  if (triangulation)
1431  ptr->initialize(*triangulation);
1432  return std::unique_ptr<Manifold<dim, spacedim>>(ptr);
1433 }
1434 
1435 
1436 
1437 template <int dim, int spacedim>
1438 void
1441 {
1442  this->triangulation = &triangulation;
1443  // in case the triangulatoin is cleared, remove the pointers by a signal
1444  clear_signal = triangulation.signals.clear.connect([&]() -> void {
1445  this->triangulation = nullptr;
1446  this->level_coarse = -1;
1447  });
1448  level_coarse = triangulation.last()->level();
1449  coarse_cell_is_flat.resize(triangulation.n_cells(level_coarse), false);
1451  cell = triangulation.begin(level_coarse),
1452  endc = triangulation.end(level_coarse);
1453  for (; cell != endc; ++cell)
1454  {
1455  bool cell_is_flat = true;
1456  for (unsigned int l = 0; l < GeometryInfo<dim>::lines_per_cell; ++l)
1457  if (cell->line(l)->manifold_id() != cell->manifold_id() &&
1458  cell->line(l)->manifold_id() != numbers::invalid_manifold_id)
1459  cell_is_flat = false;
1460  if (dim > 2)
1461  for (unsigned int q = 0; q < GeometryInfo<dim>::quads_per_cell; ++q)
1462  if (cell->quad(q)->manifold_id() != cell->manifold_id() &&
1463  cell->quad(q)->manifold_id() != numbers::invalid_manifold_id)
1464  cell_is_flat = false;
1465  AssertIndexRange(static_cast<unsigned int>(cell->index()),
1466  coarse_cell_is_flat.size());
1467  coarse_cell_is_flat[cell->index()] = cell_is_flat;
1468  }
1469 }
1470 
1471 
1472 
1473 namespace
1474 {
1475  // version for 1D
1476  template <typename AccessorType>
1478  compute_transfinite_interpolation(const AccessorType &cell,
1479  const Point<1> & chart_point,
1480  const bool /*cell_is_flat*/)
1481  {
1482  return cell.vertex(0) * (1. - chart_point[0]) +
1483  cell.vertex(1) * chart_point[0];
1484  }
1485 
1486  // version for 2D
1487  template <typename AccessorType>
1489  compute_transfinite_interpolation(const AccessorType &cell,
1490  const Point<2> & chart_point,
1491  const bool cell_is_flat)
1492  {
1493  const unsigned int dim = AccessorType::dimension;
1494  const unsigned int spacedim = AccessorType::space_dimension;
1495  const types::manifold_id my_manifold_id = cell.manifold_id();
1496  const Triangulation<dim, spacedim> &tria = cell.get_triangulation();
1497 
1498  // formula see wikipedia
1499  // https://en.wikipedia.org/wiki/Transfinite_interpolation
1500  // S(u,v) = (1-v)c_1(u)+v c_3(u) + (1-u)c_2(v) + u c_4(v) -
1501  // [(1-u)(1-v)P_0 + u(1-v) P_1 + (1-u)v P_2 + uv P_3]
1502  const std::array<Point<spacedim>, 4> vertices{
1503  {cell.vertex(0), cell.vertex(1), cell.vertex(2), cell.vertex(3)}};
1504 
1505  // this evaluates all bilinear shape functions because we need them
1506  // repeatedly. we will update this values in the complicated case with
1507  // curved lines below
1508  std::array<double, 4> weights_vertices{
1509  {(1. - chart_point[0]) * (1. - chart_point[1]),
1510  chart_point[0] * (1. - chart_point[1]),
1511  (1. - chart_point[0]) * chart_point[1],
1512  chart_point[0] * chart_point[1]}};
1513 
1514  Point<spacedim> new_point;
1515  if (cell_is_flat)
1516  for (unsigned int v = 0; v < GeometryInfo<2>::vertices_per_cell; ++v)
1517  new_point += weights_vertices[v] * vertices[v];
1518  else
1519  {
1520  // The second line in the formula tells us to subtract the
1521  // contribution of the vertices. If a line employs the same manifold
1522  // as the cell, we can merge the weights of the line with the weights
1523  // of the vertex with a negative sign while going through the faces
1524  // (this is a bit artificial in 2D but it becomes clear in 3D where we
1525  // avoid looking at the faces' orientation and other complications).
1526 
1527  // add the contribution from the lines around the cell (first line in
1528  // formula)
1529  std::array<double, GeometryInfo<2>::vertices_per_face> weights;
1530  std::array<Point<spacedim>, GeometryInfo<2>::vertices_per_face> points;
1531  // note that the views are immutable, but the arrays are not
1532  const auto weights_view =
1533  make_array_view(weights.begin(), weights.end());
1534  const auto points_view = make_array_view(points.begin(), points.end());
1535 
1536  for (unsigned int line = 0; line < GeometryInfo<2>::lines_per_cell;
1537  ++line)
1538  {
1539  const double my_weight =
1540  (line % 2) ? chart_point[line / 2] : 1 - chart_point[line / 2];
1541  const double line_point = chart_point[1 - line / 2];
1542 
1543  // Same manifold or invalid id which will go back to the same
1544  // class -> contribution should be added for the final point,
1545  // which means that we subtract the current weight from the
1546  // negative weight applied to the vertex
1547  const types::manifold_id line_manifold_id =
1548  cell.line(line)->manifold_id();
1549  if (line_manifold_id == my_manifold_id ||
1550  line_manifold_id == numbers::invalid_manifold_id)
1551  {
1552  weights_vertices[GeometryInfo<2>::line_to_cell_vertices(line,
1553  0)] -=
1554  my_weight * (1. - line_point);
1555  weights_vertices[GeometryInfo<2>::line_to_cell_vertices(line,
1556  1)] -=
1557  my_weight * line_point;
1558  }
1559  else
1560  {
1561  points[0] =
1562  vertices[GeometryInfo<2>::line_to_cell_vertices(line, 0)];
1563  points[1] =
1564  vertices[GeometryInfo<2>::line_to_cell_vertices(line, 1)];
1565  weights[0] = 1. - line_point;
1566  weights[1] = line_point;
1567  new_point +=
1568  my_weight * tria.get_manifold(line_manifold_id)
1569  .get_new_point(points_view, weights_view);
1570  }
1571  }
1572 
1573  // subtract contribution from the vertices (second line in formula)
1574  for (unsigned int v = 0; v < GeometryInfo<2>::vertices_per_cell; ++v)
1575  new_point -= weights_vertices[v] * vertices[v];
1576  }
1577 
1578  return new_point;
1579  }
1580 
1581  // this is replicated from GeometryInfo::face_to_cell_vertices since we need
1582  // it very often in compute_transfinite_interpolation and the function is
1583  // performance critical
1584  static constexpr unsigned int face_to_cell_vertices_3d[6][4] = {{0, 2, 4, 6},
1585  {1, 3, 5, 7},
1586  {0, 4, 1, 5},
1587  {2, 6, 3, 7},
1588  {0, 1, 2, 3},
1589  {4, 5, 6, 7}};
1590 
1591  // this is replicated from GeometryInfo::face_to_cell_lines since we need it
1592  // very often in compute_transfinite_interpolation and the function is
1593  // performance critical
1594  static constexpr unsigned int face_to_cell_lines_3d[6][4] = {{8, 10, 0, 4},
1595  {9, 11, 1, 5},
1596  {2, 6, 8, 9},
1597  {3, 7, 10, 11},
1598  {0, 1, 2, 3},
1599  {4, 5, 6, 7}};
1600 
1601  // version for 3D
1602  template <typename AccessorType>
1604  compute_transfinite_interpolation(const AccessorType &cell,
1605  const Point<3> & chart_point,
1606  const bool cell_is_flat)
1607  {
1608  const unsigned int dim = AccessorType::dimension;
1609  const unsigned int spacedim = AccessorType::space_dimension;
1610  const types::manifold_id my_manifold_id = cell.manifold_id();
1611  const Triangulation<dim, spacedim> &tria = cell.get_triangulation();
1612 
1613  // Same approach as in 2D, but adding the faces, subtracting the edges, and
1614  // adding the vertices
1615  const std::array<Point<spacedim>, 8> vertices{{cell.vertex(0),
1616  cell.vertex(1),
1617  cell.vertex(2),
1618  cell.vertex(3),
1619  cell.vertex(4),
1620  cell.vertex(5),
1621  cell.vertex(6),
1622  cell.vertex(7)}};
1623 
1624  // store the components of the linear shape functions because we need them
1625  // repeatedly. we allow for 10 such shape functions to wrap around the
1626  // first four once again for easier face access.
1627  double linear_shapes[10];
1628  for (unsigned int d = 0; d < 3; ++d)
1629  {
1630  linear_shapes[2 * d] = 1. - chart_point[d];
1631  linear_shapes[2 * d + 1] = chart_point[d];
1632  }
1633 
1634  // wrap linear shape functions around for access in face loop
1635  for (unsigned int d = 6; d < 10; ++d)
1636  linear_shapes[d] = linear_shapes[d - 6];
1637 
1638  std::array<double, 8> weights_vertices;
1639  for (unsigned int i2 = 0, v = 0; i2 < 2; ++i2)
1640  for (unsigned int i1 = 0; i1 < 2; ++i1)
1641  for (unsigned int i0 = 0; i0 < 2; ++i0, ++v)
1642  weights_vertices[v] =
1643  (linear_shapes[4 + i2] * linear_shapes[2 + i1]) * linear_shapes[i0];
1644 
1645  Point<spacedim> new_point;
1646  if (cell_is_flat)
1647  for (unsigned int v = 0; v < 8; ++v)
1648  new_point += weights_vertices[v] * vertices[v];
1649  else
1650  {
1651  // identify the weights for the lines to be accumulated (vertex
1652  // weights are set outside and coincide with the flat manifold case)
1653 
1654  double weights_lines[GeometryInfo<3>::lines_per_cell];
1655  for (unsigned int line = 0; line < GeometryInfo<3>::lines_per_cell;
1656  ++line)
1657  weights_lines[line] = 0;
1658 
1659  // start with the contributions of the faces
1660  std::array<double, GeometryInfo<2>::vertices_per_cell> weights;
1661  std::array<Point<spacedim>, GeometryInfo<2>::vertices_per_cell> points;
1662  // note that the views are immutable, but the arrays are not
1663  const auto weights_view =
1664  make_array_view(weights.begin(), weights.end());
1665  const auto points_view = make_array_view(points.begin(), points.end());
1666 
1667  for (unsigned int face = 0; face < GeometryInfo<3>::faces_per_cell;
1668  ++face)
1669  {
1670  const double my_weight = linear_shapes[face];
1671  const unsigned int face_even = face - face % 2;
1672 
1673  if (std::abs(my_weight) < 1e-13)
1674  continue;
1675 
1676  // same manifold or invalid id which will go back to the same class
1677  // -> face will interpolate from the surrounding lines and vertices
1678  const types::manifold_id face_manifold_id =
1679  cell.face(face)->manifold_id();
1680  if (face_manifold_id == my_manifold_id ||
1681  face_manifold_id == numbers::invalid_manifold_id)
1682  {
1683  for (unsigned int line = 0;
1684  line < GeometryInfo<2>::lines_per_cell;
1685  ++line)
1686  {
1687  const double line_weight =
1688  linear_shapes[face_even + 2 + line];
1689  weights_lines[face_to_cell_lines_3d[face][line]] +=
1690  my_weight * line_weight;
1691  }
1692  // as to the indices inside linear_shapes: we use the index
1693  // wrapped around at 2*d, ensuring the correct orientation of
1694  // the face's coordinate system with respect to the
1695  // lexicographic indices
1696  weights_vertices[face_to_cell_vertices_3d[face][0]] -=
1697  linear_shapes[face_even + 2] *
1698  (linear_shapes[face_even + 4] * my_weight);
1699  weights_vertices[face_to_cell_vertices_3d[face][1]] -=
1700  linear_shapes[face_even + 3] *
1701  (linear_shapes[face_even + 4] * my_weight);
1702  weights_vertices[face_to_cell_vertices_3d[face][2]] -=
1703  linear_shapes[face_even + 2] *
1704  (linear_shapes[face_even + 5] * my_weight);
1705  weights_vertices[face_to_cell_vertices_3d[face][3]] -=
1706  linear_shapes[face_even + 3] *
1707  (linear_shapes[face_even + 5] * my_weight);
1708  }
1709  else
1710  {
1711  for (unsigned int v = 0; v < GeometryInfo<2>::vertices_per_cell;
1712  ++v)
1713  points[v] = vertices[face_to_cell_vertices_3d[face][v]];
1714  weights[0] =
1715  linear_shapes[face_even + 2] * linear_shapes[face_even + 4];
1716  weights[1] =
1717  linear_shapes[face_even + 3] * linear_shapes[face_even + 4];
1718  weights[2] =
1719  linear_shapes[face_even + 2] * linear_shapes[face_even + 5];
1720  weights[3] =
1721  linear_shapes[face_even + 3] * linear_shapes[face_even + 5];
1722  new_point +=
1723  my_weight * tria.get_manifold(face_manifold_id)
1724  .get_new_point(points_view, weights_view);
1725  }
1726  }
1727 
1728  // next subtract the contributions of the lines
1729  const auto weights_view_line =
1730  make_array_view(weights.begin(), weights.begin() + 2);
1731  const auto points_view_line =
1732  make_array_view(points.begin(), points.begin() + 2);
1733  for (unsigned int line = 0; line < GeometryInfo<3>::lines_per_cell;
1734  ++line)
1735  {
1736  const double line_point =
1737  (line < 8 ? chart_point[1 - (line % 4) / 2] : chart_point[2]);
1738  double my_weight = 0.;
1739  if (line < 8)
1740  my_weight = linear_shapes[line % 4] * linear_shapes[4 + line / 4];
1741  else
1742  {
1743  const unsigned int subline = line - 8;
1744  my_weight =
1745  linear_shapes[subline % 2] * linear_shapes[2 + subline / 2];
1746  }
1747  my_weight -= weights_lines[line];
1748 
1749  if (std::abs(my_weight) < 1e-13)
1750  continue;
1751 
1752  const types::manifold_id line_manifold_id =
1753  cell.line(line)->manifold_id();
1754  if (line_manifold_id == my_manifold_id ||
1755  line_manifold_id == numbers::invalid_manifold_id)
1756  {
1757  weights_vertices[GeometryInfo<3>::line_to_cell_vertices(line,
1758  0)] -=
1759  my_weight * (1. - line_point);
1760  weights_vertices[GeometryInfo<3>::line_to_cell_vertices(line,
1761  1)] -=
1762  my_weight * (line_point);
1763  }
1764  else
1765  {
1766  points[0] =
1767  vertices[GeometryInfo<3>::line_to_cell_vertices(line, 0)];
1768  points[1] =
1769  vertices[GeometryInfo<3>::line_to_cell_vertices(line, 1)];
1770  weights[0] = 1. - line_point;
1771  weights[1] = line_point;
1772  new_point -= my_weight * tria.get_manifold(line_manifold_id)
1773  .get_new_point(points_view_line,
1774  weights_view_line);
1775  }
1776  }
1777 
1778  // finally add the contribution of the
1779  for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_cell; ++v)
1780  new_point += weights_vertices[v] * vertices[v];
1781  }
1782  return new_point;
1783  }
1784 } // namespace
1785 
1786 
1787 
1788 template <int dim, int spacedim>
1791  const typename Triangulation<dim, spacedim>::cell_iterator &cell,
1792  const Point<dim> & chart_point) const
1793 {
1794  AssertDimension(cell->level(), level_coarse);
1795 
1796  // check that the point is in the unit cell which is the current chart
1797  // Tolerance 1e-6 chosen that the method also works with
1798  // SphericalManifold
1800  ExcMessage("chart_point is not in unit interval"));
1801 
1802  return compute_transfinite_interpolation(*cell,
1803  chart_point,
1804  coarse_cell_is_flat[cell->index()]);
1805 }
1806 
1807 
1808 
1809 template <int dim, int spacedim>
1812  const typename Triangulation<dim, spacedim>::cell_iterator &cell,
1813  const Point<dim> & chart_point,
1814  const Point<spacedim> &pushed_forward_chart_point) const
1815 {
1816  // compute the derivative with the help of finite differences
1818  for (unsigned int d = 0; d < dim; ++d)
1819  {
1820  Point<dim> modified = chart_point;
1821  const double step = chart_point[d] > 0.5 ? -1e-8 : 1e-8;
1822 
1823  // avoid checking outside of the unit interval
1824  modified[d] += step;
1825  Tensor<1, spacedim> difference =
1826  compute_transfinite_interpolation(*cell,
1827  modified,
1828  coarse_cell_is_flat[cell->index()]) -
1829  pushed_forward_chart_point;
1830  for (unsigned int e = 0; e < spacedim; ++e)
1831  grad[e][d] = difference[e] / step;
1832  }
1833  return grad;
1834 }
1835 
1836 
1837 
1838 template <int dim, int spacedim>
1839 Point<dim>
1841  const typename Triangulation<dim, spacedim>::cell_iterator &cell,
1842  const Point<spacedim> & point,
1843  const Point<dim> &initial_guess) const
1844 {
1845  Point<dim> outside;
1846  for (unsigned int d = 0; d < dim; ++d)
1847  outside[d] = internal::invalid_pull_back_coordinate;
1848 
1849  // project the user-given input to unit cell
1850  Point<dim> chart_point =
1852 
1853  // run quasi-Newton iteration with a combination of finite differences for
1854  // the exact Jacobian and "Broyden's good method". As opposed to the various
1855  // mapping implementations, this class does not throw exception upon failure
1856  // as those are relatively expensive and failure occurs quite regularly in
1857  // the implementation of the compute_chart_points method.
1858  Tensor<1, spacedim> residual =
1859  point -
1860  compute_transfinite_interpolation(*cell,
1861  chart_point,
1862  coarse_cell_is_flat[cell->index()]);
1863  const double tolerance = 1e-21 * Utilities::fixed_power<2>(cell->diameter());
1864  double residual_norm_square = residual.norm_square();
1866  for (unsigned int i = 0; i < 100; ++i)
1867  {
1868  if (residual_norm_square < tolerance)
1869  {
1870  // do a final update of the point with the last available Jacobian
1871  // information. The residual is close to zero due to the check
1872  // above, but me might improve some of the last digits by a final
1873  // Newton-like step with step length 1
1874  Tensor<1, dim> update;
1875  for (unsigned int d = 0; d < spacedim; ++d)
1876  for (unsigned int e = 0; e < dim; ++e)
1877  update[e] += inv_grad[d][e] * residual[d];
1878  return chart_point + update;
1879  }
1880 
1881  // every 8 iterations, including the first time around, we create an
1882  // approximation of the Jacobian with finite differences. Broyden's
1883  // method usually does not need more than 5-8 iterations, but sometimes
1884  // we might have had a bad initial guess and then we can accelerate
1885  // convergence considerably with getting the actual Jacobian rather than
1886  // using secant-like methods (one gradient calculation in 3D costs as
1887  // much as 3 more iterations). this usually happens close to convergence
1888  // and one more step with the finite-differenced Jacobian leads to
1889  // convergence
1890  if (i % 8 == 0)
1891  {
1892  // if the determinant is zero or negative, the mapping is either not
1893  // invertible or already has inverted and we are outside the valid
1894  // chart region. Note that the Jacobian here represents the
1895  // derivative of the forward map and should have a positive
1896  // determinant since we use properly oriented meshes.
1898  push_forward_gradient(cell,
1899  chart_point,
1900  Point<spacedim>(point - residual));
1901  if (grad.determinant() <= 0.0)
1902  return outside;
1903  inv_grad = grad.covariant_form();
1904  }
1905  Tensor<1, dim> update;
1906  for (unsigned int d = 0; d < spacedim; ++d)
1907  for (unsigned int e = 0; e < dim; ++e)
1908  update[e] += inv_grad[d][e] * residual[d];
1909 
1910  // Line search, accept step if the residual has decreased
1911  double alpha = 1.;
1912 
1913  // check if point is inside 1.2 times the unit cell to avoid
1914  // hitting points very far away from valid ones in the manifolds
1915  while (
1916  !GeometryInfo<dim>::is_inside_unit_cell(chart_point + alpha * update,
1917  0.2) &&
1918  alpha > 1e-7)
1919  alpha *= 0.5;
1920 
1921  const Tensor<1, spacedim> old_residual = residual;
1922  while (alpha > 1e-7)
1923  {
1924  Point<dim> guess = chart_point + alpha * update;
1925  residual =
1926  point - compute_transfinite_interpolation(
1927  *cell, guess, coarse_cell_is_flat[cell->index()]);
1928  const double residual_norm_new = residual.norm_square();
1929  if (residual_norm_new < residual_norm_square)
1930  {
1931  residual_norm_square = residual_norm_new;
1932  chart_point += alpha * update;
1933  break;
1934  }
1935  else
1936  alpha *= 0.5;
1937  }
1938  if (alpha < 1e-7)
1939  return outside;
1940 
1941  // update the inverse Jacobian with "Broyden's good method" and
1942  // Sherman-Morrison formula for the update of the inverse, see
1943  // https://en.wikipedia.org/wiki/Broyden%27s_method
1944  const Tensor<1, dim> delta_x = alpha * update;
1945 
1946  // switch sign in residual as compared to the wikipedia article because
1947  // we use a negative definition of the residual with respect to the
1948  // Jacobian
1949  const Tensor<1, spacedim> delta_f = old_residual - residual;
1950 
1951  Tensor<1, dim> Jinv_deltaf;
1952  for (unsigned int d = 0; d < spacedim; ++d)
1953  for (unsigned int e = 0; e < dim; ++e)
1954  Jinv_deltaf[e] += inv_grad[d][e] * delta_f[d];
1955  const Tensor<1, dim> factor =
1956  (delta_x - Jinv_deltaf) / (delta_x * Jinv_deltaf);
1957  Tensor<1, spacedim> jac_update;
1958  for (unsigned int d = 0; d < spacedim; ++d)
1959  for (unsigned int e = 0; e < dim; ++e)
1960  jac_update[d] += delta_x[e] * inv_grad[d][e];
1961  for (unsigned int d = 0; d < spacedim; ++d)
1962  for (unsigned int e = 0; e < dim; ++e)
1963  inv_grad[d][e] += factor[e] * jac_update[d];
1964  }
1965  return outside;
1966 }
1967 
1968 
1969 
1970 template <int dim, int spacedim>
1971 std::array<unsigned int, 20>
1974  const ArrayView<const Point<spacedim>> &points) const
1975 {
1976  // The methods to identify cells around points in GridTools are all written
1977  // for the active cells, but we are here looking at some cells at the coarse
1978  // level.
1979  Assert(triangulation != nullptr, ExcNotInitialized());
1980  Assert(triangulation->begin_active()->level() >= level_coarse,
1981  ExcMessage("The manifold was initialized with level " +
1982  Utilities::to_string(level_coarse) + " but there are now" +
1983  "active cells on a lower level. Coarsening the mesh is " +
1984  "currently not supported"));
1985 
1986  // This computes the distance of the surrounding points transformed to the
1987  // unit cell from the unit cell.
1989  triangulation->begin(
1990  level_coarse),
1991  endc =
1992  triangulation->end(
1993  level_coarse);
1994  boost::container::small_vector<std::pair<double, unsigned int>, 200>
1995  distances_and_cells;
1996  for (; cell != endc; ++cell)
1997  {
1998  // only consider cells where the current manifold is attached
1999  if (&cell->get_manifold() != this)
2000  continue;
2001 
2002  std::array<Point<spacedim>, GeometryInfo<dim>::vertices_per_cell>
2003  vertices;
2004  for (unsigned int vertex_n = 0;
2005  vertex_n < GeometryInfo<dim>::vertices_per_cell;
2006  ++vertex_n)
2007  {
2008  vertices[vertex_n] = cell->vertex(vertex_n);
2009  }
2010 
2011  // cheap check: if any of the points is not inside a circle around the
2012  // center of the loop, we can skip the expensive part below (this assumes
2013  // that the manifold does not deform the grid too much)
2014  Point<spacedim> center;
2015  for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_cell; ++v)
2016  center += vertices[v];
2017  center *= 1. / GeometryInfo<dim>::vertices_per_cell;
2018  double radius_square = 0.;
2019  for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_cell; ++v)
2020  radius_square =
2021  std::max(radius_square, (center - vertices[v]).norm_square());
2022  bool inside_circle = true;
2023  for (unsigned int i = 0; i < points.size(); ++i)
2024  if ((center - points[i]).norm_square() > radius_square * 1.5)
2025  {
2026  inside_circle = false;
2027  break;
2028  }
2029  if (inside_circle == false)
2030  continue;
2031 
2032  // slightly more expensive search
2033  double current_distance = 0;
2034  for (unsigned int i = 0; i < points.size(); ++i)
2035  {
2036  Point<dim> point =
2037  cell->real_to_unit_cell_affine_approximation(points[i]);
2038  current_distance += GeometryInfo<dim>::distance_to_unit_cell(point);
2039  }
2040  distances_and_cells.emplace_back(current_distance, cell->index());
2041  }
2042  // no coarse cell could be found -> transformation failed
2043  AssertThrow(distances_and_cells.size() > 0,
2045  std::sort(distances_and_cells.begin(), distances_and_cells.end());
2046  std::array<unsigned int, 20> cells;
2047  cells.fill(numbers::invalid_unsigned_int);
2048  for (unsigned int i = 0; i < distances_and_cells.size() && i < cells.size();
2049  ++i)
2050  cells[i] = distances_and_cells[i].second;
2051 
2052  return cells;
2053 }
2054 
2055 
2056 
2057 template <int dim, int spacedim>
2060  const ArrayView<const Point<spacedim>> &surrounding_points,
2061  ArrayView<Point<dim>> chart_points) const
2062 {
2063  Assert(surrounding_points.size() == chart_points.size(),
2064  ExcMessage("The chart points array view must be as large as the "
2065  "surrounding points array view."));
2066 
2067  std::array<unsigned int, 20> nearby_cells =
2068  get_possible_cells_around_points(surrounding_points);
2069 
2070  // This function is nearly always called to place new points on a cell or
2071  // cell face. In this case, the general structure of the surrounding points
2072  // is known (i.e., if there are eight surrounding points, then they will
2073  // almost surely be either eight points around a quadrilateral or the eight
2074  // vertices of a cube). Hence, making this assumption, we use two
2075  // optimizations (one for structdim == 2 and one for structdim == 3) that
2076  // guess the locations of some of the chart points more efficiently than the
2077  // affine map approximation. The affine map approximation is used whenever
2078  // we don't have a cheaper guess available.
2079 
2080  // Function that can guess the location of a chart point by assuming that
2081  // the eight surrounding points are points on a two-dimensional object
2082  // (either a cell in 2D or the face of a hexahedron in 3D), arranged like
2083  //
2084  // 2 - 7 - 3
2085  // | |
2086  // 4 5
2087  // | |
2088  // 0 - 6 - 1
2089  //
2090  // This function assumes that the first three chart points have been
2091  // computed since there is no effective way to guess them.
2092  auto guess_chart_point_structdim_2 = [&](const unsigned int i) -> Point<dim> {
2093  Assert(surrounding_points.size() == 8 && 2 < i && i < 8,
2094  ExcMessage("This function assumes that there are eight surrounding "
2095  "points around a two-dimensional object. It also assumes "
2096  "that the first three chart points have already been "
2097  "computed."));
2098  switch (i)
2099  {
2100  case 0:
2101  case 1:
2102  case 2:
2103  Assert(false, ExcInternalError());
2104  break;
2105  case 3:
2106  return chart_points[1] + (chart_points[2] - chart_points[0]);
2107  case 4:
2108  return 0.5 * (chart_points[0] + chart_points[2]);
2109  case 5:
2110  return 0.5 * (chart_points[1] + chart_points[3]);
2111  case 6:
2112  return 0.5 * (chart_points[0] + chart_points[1]);
2113  case 7:
2114  return 0.5 * (chart_points[2] + chart_points[3]);
2115  default:
2116  Assert(false, ExcInternalError());
2117  }
2118 
2119  return Point<dim>();
2120  };
2121 
2122  // Function that can guess the location of a chart point by assuming that
2123  // the eight surrounding points form the vertices of a hexahedron, arranged
2124  // like
2125  //
2126  // 6-------7
2127  // /| /|
2128  // / / |
2129  // / | / |
2130  // 4-------5 |
2131  // | 2- -|- -3
2132  // | / | /
2133  // | | /
2134  // |/ |/
2135  // 0-------1
2136  //
2137  // (where vertex 2 is the back left vertex) we can estimate where chart
2138  // points 5 - 7 are by computing the height (in chart coordinates) as c4 -
2139  // c0 and then adding that onto the appropriate bottom vertex.
2140  //
2141  // This function assumes that the first five chart points have been computed
2142  // since there is no effective way to guess them.
2143  auto guess_chart_point_structdim_3 = [&](const unsigned int i) -> Point<dim> {
2144  Assert(surrounding_points.size() == 8 && 4 < i && i < 8,
2145  ExcMessage("This function assumes that there are eight surrounding "
2146  "points around a three-dimensional object. It also "
2147  "assumes that the first five chart points have already "
2148  "been computed."));
2149  return chart_points[i - 4] + (chart_points[4] - chart_points[0]);
2150  };
2151 
2152  // Check if we can use the two chart point shortcuts above before we start:
2153  bool use_structdim_2_guesses = false;
2154  bool use_structdim_3_guesses = false;
2155  // note that in the structdim 2 case: 0 - 6 and 2 - 7 should be roughly
2156  // parallel, while in the structdim 3 case, 0 - 6 and 2 - 7 should be roughly
2157  // orthogonal. Use the angle between these two vectors to figure out if we
2158  // should turn on either structdim optimization.
2159  if (surrounding_points.size() == 8)
2160  {
2161  const Tensor<1, spacedim> v06 =
2162  surrounding_points[6] - surrounding_points[0];
2163  const Tensor<1, spacedim> v27 =
2164  surrounding_points[7] - surrounding_points[2];
2165 
2166  // note that we can save a call to sqrt() by rearranging
2167  const double cosine = scalar_product(v06, v27) /
2168  std::sqrt(v06.norm_square() * v27.norm_square());
2169  if (0.707 < cosine)
2170  // the angle is less than pi/4, so these vectors are roughly parallel:
2171  // enable the structdim 2 optimization
2172  use_structdim_2_guesses = true;
2173  else if (spacedim == 3)
2174  // otherwise these vectors are roughly orthogonal: enable the
2175  // structdim 3 optimization if we are in 3D
2176  use_structdim_3_guesses = true;
2177  }
2178  // we should enable at most one of the optimizations
2179  Assert((!use_structdim_2_guesses && !use_structdim_3_guesses) ||
2180  (use_structdim_2_guesses ^ use_structdim_3_guesses),
2181  ExcInternalError());
2182 
2183  // check whether all points are inside the unit cell of the current chart
2184  for (unsigned int c = 0; c < nearby_cells.size(); ++c)
2185  {
2187  triangulation, level_coarse, nearby_cells[c]);
2188  bool inside_unit_cell = true;
2189  for (unsigned int i = 0; i < surrounding_points.size(); ++i)
2190  {
2191  Point<dim> guess;
2192  // an optimization: keep track of whether or not we used the affine
2193  // approximation so that we don't call pull_back with the same
2194  // initial guess twice (i.e., if pull_back fails the first time,
2195  // don't try again with the same function arguments).
2196  bool used_affine_approximation = false;
2197  // if we have already computed three points, we can guess the fourth
2198  // to be the missing corner point of a rectangle
2199  if (i == 3 && surrounding_points.size() == 8)
2200  guess = chart_points[1] + (chart_points[2] - chart_points[0]);
2201  else if (use_structdim_2_guesses && 3 < i)
2202  guess = guess_chart_point_structdim_2(i);
2203  else if (use_structdim_3_guesses && 4 < i)
2204  guess = guess_chart_point_structdim_3(i);
2205  else
2206  {
2207  guess = cell->real_to_unit_cell_affine_approximation(
2208  surrounding_points[i]);
2209  used_affine_approximation = true;
2210  }
2211  chart_points[i] = pull_back(cell, surrounding_points[i], guess);
2212 
2213  // the initial guess may not have been good enough: if applicable,
2214  // try again with the affine approximation (which is more accurate
2215  // than the cheap methods used above)
2216  if (chart_points[i][0] == internal::invalid_pull_back_coordinate &&
2217  !used_affine_approximation)
2218  {
2219  guess = cell->real_to_unit_cell_affine_approximation(
2220  surrounding_points[i]);
2221  chart_points[i] = pull_back(cell, surrounding_points[i], guess);
2222  }
2223 
2224  // Tolerance 1e-6 chosen that the method also works with
2225  // SphericalManifold
2226  if (GeometryInfo<dim>::is_inside_unit_cell(chart_points[i], 1e-6) ==
2227  false)
2228  {
2229  inside_unit_cell = false;
2230  break;
2231  }
2232  }
2233  if (inside_unit_cell == true)
2234  {
2235  return cell;
2236  }
2237 
2238  // if we did not find a point and this was the last valid cell (the next
2239  // iterate being the end of the array or an unvalid tag), we must stop
2240  if (c == nearby_cells.size() - 1 ||
2241  nearby_cells[c + 1] == numbers::invalid_unsigned_int)
2242  {
2243  // generate additional information to help debugging why we did not
2244  // get a point
2245  std::ostringstream message;
2246  for (unsigned int b = 0; b <= c; ++b)
2247  {
2249  triangulation, level_coarse, nearby_cells[b]);
2250  message << "Looking at cell " << cell->id()
2251  << " with vertices: " << std::endl;
2252  for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_cell;
2253  ++v)
2254  message << cell->vertex(v) << " ";
2255  message << std::endl;
2256  message << "Transformation to chart coordinates: " << std::endl;
2257  for (unsigned int i = 0; i < surrounding_points.size(); ++i)
2258  {
2259  message
2260  << surrounding_points[i] << " -> "
2261  << pull_back(cell,
2262  surrounding_points[i],
2263  cell->real_to_unit_cell_affine_approximation(
2264  surrounding_points[i]))
2265  << std::endl;
2266  }
2267  }
2268 
2269  AssertThrow(false,
2271  message.str())));
2272  }
2273  }
2274 
2275  // a valid inversion should have returned a point above. an invalid
2276  // inversion should have triggered the assertion, so we should never end up
2277  // here
2278  Assert(false, ExcInternalError());
2280 }
2281 
2282 
2283 
2284 template <int dim, int spacedim>
2287  const ArrayView<const Point<spacedim>> &surrounding_points,
2288  const ArrayView<const double> & weights) const
2289 {
2290  boost::container::small_vector<Point<dim>, 100> chart_points(
2291  surrounding_points.size());
2292  ArrayView<Point<dim>> chart_points_view =
2293  make_array_view(chart_points.begin(), chart_points.end());
2294  const auto cell = compute_chart_points(surrounding_points, chart_points_view);
2295 
2296  const Point<dim> p_chart =
2297  chart_manifold.get_new_point(chart_points_view, weights);
2298 
2299  return push_forward(cell, p_chart);
2300 }
2301 
2302 
2303 
2304 template <int dim, int spacedim>
2305 void
2307  const ArrayView<const Point<spacedim>> &surrounding_points,
2308  const Table<2, double> & weights,
2309  ArrayView<Point<spacedim>> new_points) const
2310 {
2311  Assert(weights.size(0) > 0, ExcEmptyObject());
2312  AssertDimension(surrounding_points.size(), weights.size(1));
2313 
2314  boost::container::small_vector<Point<dim>, 100> chart_points(
2315  surrounding_points.size());
2316  ArrayView<Point<dim>> chart_points_view =
2317  make_array_view(chart_points.begin(), chart_points.end());
2318  const auto cell = compute_chart_points(surrounding_points, chart_points_view);
2319 
2320  boost::container::small_vector<Point<dim>, 100> new_points_on_chart(
2321  weights.size(0));
2322  chart_manifold.get_new_points(chart_points_view,
2323  weights,
2324  make_array_view(new_points_on_chart.begin(),
2325  new_points_on_chart.end()));
2326 
2327  for (unsigned int row = 0; row < weights.size(0); ++row)
2328  new_points[row] = push_forward(cell, new_points_on_chart[row]);
2329 }
2330 
2331 
2332 
2333 // explicit instantiations
2334 #include "manifold_lib.inst"
2335 
2336 DEAL_II_NAMESPACE_CLOSE
Point< dim > pull_back(const typename Triangulation< dim, spacedim >::cell_iterator &cell, const Point< spacedim > &p, const Point< dim > &initial_guess) const
FlatManifold< dim > chart_manifold
std::array< unsigned int, 20 > get_possible_cells_around_points(const ArrayView< const Point< spacedim >> &surrounding_points) const
static::ExceptionBase & ExcTransformationFailed()
static const unsigned int invalid_unsigned_int
Definition: types.h:173
unsigned int manifold_id
Definition: types.h:123
const double tolerance
Definition: manifold_lib.h:603
virtual Point< spacedim > push_forward(const Point< 3 > &chart_point) const override
std::size_t size() const
Definition: array_view.h:371
#define AssertDimension(dim1, dim2)
Definition: exceptions.h:1366
const unsigned int n_components
Definition: function.h:160
Number determinant(const SymmetricTensor< 2, dim, Number > &)
cell_iterator end() const
Definition: tria.cc:12004
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)
static Tensor< 1, spacedim > get_periodicity()
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
const Triangulation< dim, spacedim > * triangulation
Definition: manifold_lib.h:997
virtual Tensor< 1, dim, RangeNumberType > gradient(const Point< dim > &p, const unsigned int component=0) const
virtual Point< spacedim > get_new_point(const ArrayView< const Point< spacedim >> &surrounding_points, const ArrayView< const double > &weights) const override
const Point< spacedim > point_on_axis
Definition: manifold_lib.h:455
std::vector< bool > coarse_cell_is_flat
virtual std::unique_ptr< Manifold< dim, 3 > > clone() const override
static unsigned int line_to_cell_vertices(const unsigned int line, const unsigned int vertex)
virtual void get_new_points(const ArrayView< const Point< spacedim >> &surrounding_points, const Table< 2, double > &weights, ArrayView< Point< spacedim >> new_points) const override
#define AssertIndexRange(index, range)
Definition: exceptions.h:1407
const double finite_difference_step
Definition: manifold_lib.h:637
const std::string pull_back_expression
Definition: manifold_lib.h:622
Number determinant() const
Point< spacedim > push_forward(const typename Triangulation< dim, spacedim >::cell_iterator &cell, const Point< dim > &chart_point) const
virtual DerivativeForm< 1, 3, 3 > push_forward_gradient(const Point< 3 > &chart_point) const override
static::ExceptionBase & ExcNotInitialized()
virtual Point< spacedim > get_new_point(const ArrayView< const Point< spacedim >> &vertices, const ArrayView< const double > &weights) const override
cell_iterator last() const
Definition: tria.cc:11955
virtual Point< 3 > pull_back(const Point< 3 > &p) const override
Triangulation< dim, spacedim >::cell_iterator compute_chart_points(const ArrayView< const Point< spacedim >> &surrounding_points, ArrayView< Point< dim >> chart_points) const
numbers::NumberTraits< Number >::real_type norm() const
Definition: tensor.h:1301
#define AssertThrow(cond, exc)
Definition: exceptions.h:1329
TorusManifold(const double R, const double r)
cell_iterator begin(const unsigned int level=0) const
Definition: tria.cc:11915
std::string to_string(const number value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition: utilities.cc:105
void initialize(const Triangulation< dim, spacedim > &triangulation)
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
const Point< spacedim > center
Definition: manifold_lib.h:116
static double distance_to_unit_cell(const Point< dim > &p)
SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &)
static const double PI
Definition: numbers.h:143
virtual Point< 3 > push_forward(const Point< 3 > &chart_point) const override
static::ExceptionBase & ExcMessage(std::string arg1)
static::ExceptionBase & ExcImpossibleInDim(int arg1)
virtual DerivativeForm< 1, 3, spacedim > push_forward_gradient(const Point< 3 > &chart_point) const override
CylindricalManifold(const unsigned int axis=0, const double tolerance=1e-10)
unsigned int n_cells() const
Definition: tria.cc:12501
SmartPointer< const Function< spacedim >, FunctionManifold< dim, spacedim, chartdim > > pull_back_function
Definition: manifold_lib.h:596
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
PolarManifold(const Point< spacedim > center=Point< spacedim >())
size_type size(const unsigned int i) const
const PolarManifold< spacedim > polar_manifold
Definition: manifold_lib.h:360
#define Assert(cond, exc)
Definition: exceptions.h:1227
virtual ~FunctionManifold() override
Signals signals
Definition: tria.h:2287
const std::string chart_vars
Definition: manifold_lib.h:627
virtual Point< chartdim > pull_back(const Point< spacedim > &space_point) const override
Abstract base class for mapping classes.
Definition: dof_tools.h:57
virtual Point< 3 > pull_back(const Point< spacedim > &space_point) const override
virtual Tensor< 1, spacedim > get_tangent_vector(const Point< spacedim > &x1, const Point< spacedim > &x2) const override
virtual Point< spacedim > push_forward(const Point< spacedim > &chart_point) const override
SmartPointer< const Function< chartdim >, FunctionManifold< dim, spacedim, chartdim > > push_forward_function
Definition: manifold_lib.h:589
virtual Point< spacedim > get_new_point(const ArrayView< const Point< spacedim >> &surrounding_points, const ArrayView< const double > &weights) const override
const Manifold< dim, spacedim > & get_manifold(const types::manifold_id number) const
Definition: tria.cc:10394
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
virtual void vector_value(const Point< dim > &p, Vector< RangeNumberType > &values) const
virtual void get_normals_at_vertices(const typename Triangulation< dim, spacedim >::face_iterator &face, typename Manifold< dim, spacedim >::FaceVertexNormals &face_vertex_normals) const override
const Point< spacedim > center
Definition: manifold_lib.h:304
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 Tensor< 1, spacedim > normal_vector(const typename Triangulation< dim, spacedim >::face_iterator &face, const Point< spacedim > &p) const
Definition: manifold.cc:231
static Point< dim > project_to_unit_cell(const Point< dim > &p)
const types::manifold_id invalid_manifold_id
Definition: types.h:234
virtual ~TransfiniteInterpolationManifold() override
virtual Point< spacedim > push_forward(const Point< chartdim > &chart_point) const override
virtual Point< spacedim > pull_back(const Point< spacedim > &space_point) const override
void initialize(const std::string &vars, const std::vector< std::string > &expressions, const ConstMap &constants, const bool time_dependent=false)
virtual DerivativeForm< 1, chartdim, spacedim > push_forward_gradient(const Point< chartdim > &chart_point) const override
const std::string space_vars
Definition: manifold_lib.h:632
Definition: mpi.h:55
virtual Point< spacedim > get_new_point(const ArrayView< const Point< spacedim >> &surrounding_points, const ArrayView< const double > &weights) const override
Definition: manifold.cc:974
const Tensor< 1, chartdim > & get_periodicity() const
Definition: manifold.cc:1069
virtual Point< spacedim > get_new_point(const ArrayView< const Point< spacedim >> &surrounding_points, const ArrayView< const double > &weights) const override
Definition: manifold.cc:532
static::ExceptionBase & ExcEmptyObject()
const Tensor< 1, spacedim > direction
Definition: manifold_lib.h:450
const std::string push_forward_expression
Definition: manifold_lib.h:617
std::array< Tensor< 1, spacedim >, GeometryInfo< dim >::vertices_per_face > FaceVertexNormals
Definition: manifold.h:350
const Tensor< 1, spacedim > normal_direction
Definition: manifold_lib.h:445
DerivativeForm< 1, dim, spacedim, Number > covariant_form() const
static::ExceptionBase & ExcNotImplemented()
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
const FunctionParser< spacedim >::ConstMap const_map
Definition: manifold_lib.h:582
virtual void get_new_points(const ArrayView< const Point< spacedim >> &surrounding_points, const Table< 2, double > &weights, ArrayView< Point< spacedim >> new_points) const override
Definition: manifold.cc:593
boost::signals2::connection clear_signal
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
numbers::NumberTraits< Number >::real_type norm_square() const
Definition: tensor.h:1309
const bool owns_pointers
Definition: manifold_lib.h:612
SphericalManifold(const Point< spacedim > center=Point< spacedim >())
ProductType< Number, OtherNumber >::type scalar_product(const SymmetricTensor< 2, dim, Number > &t1, const SymmetricTensor< 2, dim, OtherNumber > &t2)
virtual Point< spacedim > get_intermediate_point(const Point< spacedim > &p1, const Point< spacedim > &p2, const double w) const override
virtual void get_normals_at_vertices(const typename Triangulation< dim, spacedim >::face_iterator &face, FaceVertexNormals &face_vertex_normals) const
Definition: manifold.cc:295
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
TriaIterator< CellAccessor< dim, spacedim >> cell_iterator
Definition: tria.h:1507
virtual DerivativeForm< 1, spacedim, spacedim > push_forward_gradient(const Point< spacedim > &chart_point) const override
virtual Tensor< 1, spacedim > normal_vector(const typename Triangulation< dim, spacedim >::face_iterator &face, const Point< spacedim > &p) const override
static::ExceptionBase & ExcInternalError()
Triangulation< dim, spacedim > & get_triangulation()
Definition: tria.cc:13182