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