libMesh
inf_fe_map.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
20 // Local includes
21 #include "libmesh/libmesh_config.h"
22 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
23 #include "libmesh/inf_fe_map.h"
24 #include "libmesh/inf_fe.h"
25 #include "libmesh/fe.h"
26 #include "libmesh/elem.h"
27 #include "libmesh/inf_fe_macro.h"
28 #include "libmesh/libmesh_logging.h"
29 
30 #include "libmesh/type_tensor.h"
31 
32 namespace libMesh
33 {
34 
35 // ------------------------------------------------------------
36 // InfFE static class members concerned with coordinate
37 // mapping
38 
39 
40 Point InfFEMap::map (const unsigned int dim,
41  const Elem * inf_elem,
42  const Point & reference_point)
43 {
44  libmesh_assert(inf_elem);
45  libmesh_assert_not_equal_to (dim, 0);
46 
47  std::unique_ptr<const Elem> base_elem = InfFEBase::build_elem (inf_elem);
48 
49  const Real v (reference_point(dim-1));
50 
51  // map in the base face
52  Point base_point;
53  switch (dim)
54  {
55  case 1:
56  base_point = inf_elem->point(0);
57  break;
58  case 2:
59  case 3:
60  base_point = FEMap::map (dim-1, base_elem.get(), reference_point);
61  break;
62  default:
63 #ifdef DEBUG
64  libmesh_error_msg("Unknown dim = " << dim);
65 #endif
66  break;
67  }
68 
69  // This is the same as the algorithm used below,
70  // but is more explicit in the actual form in the end.
71  //
72  // NOTE: the form used below can be implemented to yield
73  // more general/flexible mappings, but the current form is
74  // used e.g. for \p inverse_map() and \p reinit() explicitly.
75  return (base_point-inf_elem->origin())*2/(1-v)+inf_elem->origin();
76 
77 #if 0 // Leave this documented, but w/o unreachable-code warnings
78  const Order radial_mapping_order (InfFERadial::mapping_order());
79 
80  // map in the outer node face not necessary. Simply
81  // compute the outer_point = base_point + (base_point-origin)
82  const Point outer_point (base_point * 2. - inf_elem->origin());
83 
84  Point p;
85 
86  // there are only two mapping shapes in radial direction
87  p.add_scaled (base_point, eval (v, radial_mapping_order, 0));
88  p.add_scaled (outer_point, eval (v, radial_mapping_order, 1));
89 
90  return p;
91 #endif
92 }
93 
94 
95 
96 Point InfFEMap::inverse_map (const unsigned int dim,
97  const Elem * inf_elem,
98  const Point & physical_point,
99  const Real tolerance,
100  const bool secure)
101 {
102  libmesh_assert(inf_elem);
103  libmesh_assert_greater_equal (tolerance, 0.);
104  libmesh_assert(dim > 0);
105 
106  // Start logging the map inversion.
107  LOG_SCOPE("inverse_map()", "InfFEMap");
108 
109  // The strategy is:
110  // compute the intersection of the line
111  // physical_point - origin with the base element,
112  // find its internal coordinatels using FEMap::inverse_map():
113  // The radial part can then be computed directly later on.
114 
115  // 1.)
116  // build a base element to do the map inversion in the base face
117  std::unique_ptr<const Elem> base_elem = InfFEBase::build_elem (inf_elem);
118 
119  // The point on the reference element (which we are looking for).
120  // start with an invalid guess:
121  Point p;
122  p(dim-1)=-2.;
123 
124  // 2.)
125  // Now find the intersection of a plane represented by the base
126  // element nodes and the line given by the origin of the infinite
127  // element and the physical point.
128  Point intersection;
129 
130  // the origin of the infinite element
131  const Point o = inf_elem->origin();
132 
133  switch (dim)
134  {
135  // unnecessary for 1D
136  case 1:
137  break;
138 
139  case 2:
140  libmesh_error_msg("ERROR: InfFE::inverse_map is not yet implemented in 2d");
141 
142  case 3:
143  {
144  const Point xi ( base_elem->point(1) - base_elem->point(0));
145  const Point eta( base_elem->point(2) - base_elem->point(0));
146  const Point zeta( physical_point - o);
147 
148  // normal vector of the base elements plane
149  Point n(xi.cross(eta));
150  Real c_factor = (base_elem->point(0) - o)*n/(zeta*n) - 1.;
151 
152  // Check whether the above system is ill-posed. It should
153  // only happen when \p physical_point is not in \p
154  // inf_elem. In this case, any point that is not in the
155  // element is a valid answer.
156  if (libmesh_isinf(c_factor))
157  return p;
158 
159  // Compute the intersection with
160  // {intersection} = {physical_point} + c*({physical_point}-{o}).
161  intersection.add_scaled(physical_point,1.+c_factor);
162  intersection.add_scaled(o,-c_factor);
163 
164  // For non-planar elements, the intersecting point is obtained via Newton-iteration
165  if (!base_elem->has_affine_map())
166  {
167  unsigned int iter_max = 20;
168 
169  // the number of shape functions needed for the base_elem
170  unsigned int n_sf = FE<2,LAGRANGE>::n_shape_functions(base_elem->type(),base_elem->default_order());
171 
172  // guess base element coordinates: p=xi,eta,0
173  // in first iteration, never run with 'secure' to avoid false warnings.
174  Point ref_point= FEMap::inverse_map(dim-1, base_elem.get(), intersection,
175  tolerance, false);
176 
177  // Newton iteration
178  for(unsigned int it=0; it<iter_max; ++it)
179  {
180  // Get the shape function and derivative values at the reference coordinate
181  // phi.size() == dphi.size()
182  Point dxyz_dxi;
183  Point dxyz_deta;
184 
185  Point intersection_guess;
186  for(unsigned int i=0; i<n_sf; ++i)
187  {
188 
189  intersection_guess += base_elem->node_ref(i) * FE<2,LAGRANGE>::shape(base_elem->type(),
190  base_elem->default_order(),
191  i,
192  ref_point);
193 
194  dxyz_dxi += base_elem->node_ref(i) * FE<2,LAGRANGE>::shape_deriv(base_elem->type(),
195  base_elem->default_order(),
196  i,
197  0, // d()/dxi
198  ref_point);
199 
200  dxyz_deta+= base_elem->node_ref(i) * FE<2,LAGRANGE>::shape_deriv(base_elem->type(),
201  base_elem->default_order(),
202  i,
203  1, // d()/deta
204  ref_point);
205  } // for i
206 
207  TypeVector<Real> F(physical_point + c_factor*(physical_point-o) - intersection_guess);
208 
210  J(0,0) = (physical_point-o)(0);
211  J(0,1) = -dxyz_dxi(0);
212  J(0,2) = -dxyz_deta(0);
213  J(1,0) = (physical_point-o)(1);
214  J(1,1) = -dxyz_dxi(1);
215  J(1,2) = -dxyz_deta(1);
216  J(2,0) = (physical_point-o)(2);
217  J(2,1) = -dxyz_dxi(2);
218  J(2,2) = -dxyz_deta(2);
219 
220  // delta will be the newton step
221  TypeVector<Real> delta;
222  J.solve(F,delta);
223 
224  // check for convergence
225  Real tol = std::min( TOLERANCE*0.01, TOLERANCE*base_elem->hmax() );
226  if ( delta.norm() < tol )
227  {
228  // newton solver converged, now make sure it converged to a point on the base_elem
229  if (base_elem->contains_point(intersection_guess,TOLERANCE*0.1))
230  {
231  intersection = intersection_guess;
232  }
233  //else
234  // {
235  // err<<" Error: inverse_map failed!"<<std::endl;
236  // }
237  break; // break out of 'for it'
238  }
239  else
240  {
241  c_factor -= delta(0);
242  ref_point(0) -= delta(1);
243  ref_point(1) -= delta(2);
244  }
245 
246  }
247 
248  }
249  break;
250  }
251 
252  default:
253  libmesh_error_msg("Invalid dim = " << dim);
254  }
255 
256  // 3.)
257  // Now we have the intersection-point (projection of physical point onto base-element).
258  // Lets compute its internal coordinates (being p(0) and p(1)):
259  p= FEMap::inverse_map(dim-1, base_elem.get(), intersection,
260  tolerance, secure, secure);
261 
262  // 4.
263  // Now that we have the local coordinates in the base,
264  // we compute the radial distance with Newton iteration.
265 
266  // distance from the physical point to the ifem origin
267  const Real fp_o_dist = Point(o-physical_point).norm();
268 
269  // the distance from the intersection on the
270  // base to the origin
271  const Real a_dist = Point(o-intersection).norm();
272 
273  // element coordinate in radial direction
274 
275  // fp_o_dist is at infinity.
276  if (libmesh_isinf(fp_o_dist))
277  {
278  p(dim-1)=1;
279  return p;
280  }
281 
282  // when we are somewhere in this element:
283  Real v = 0;
284 
285  // For now we're sticking with T_map == CARTESIAN
286  // if (T_map == CARTESIAN)
287  v = 1.-2.*a_dist/fp_o_dist;
288  // else
289  // libmesh_not_implemented();
290 
291 
292  p(dim-1)=v;
293 #ifdef DEBUG
294  // first check whether we are in the reference-element:
295  if ((-1.-1.e-5 < v && v < 1.) || secure)
296  {
297  const Point check = map (dim, inf_elem, p);
298  const Point diff = physical_point - check;
299 
300  if (diff.norm() > tolerance)
301  libmesh_warning("WARNING: diff is " << diff.norm());
302  }
303 #endif
304 
305  return p;
306 }
307 
308 
309 
310 void InfFEMap::inverse_map (const unsigned int dim,
311  const Elem * elem,
312  const std::vector<Point> & physical_points,
313  std::vector<Point> & reference_points,
314  const Real tolerance,
315  const bool secure)
316 {
317  // The number of points to find the
318  // inverse map of
319  const std::size_t n_points = physical_points.size();
320 
321  // Resize the vector to hold the points
322  // on the reference element
323  reference_points.resize(n_points);
324 
325  // Find the coordinates on the reference
326  // element of each point in physical space
327  for (unsigned int p=0; p<n_points; p++)
328  reference_points[p] =
329  inverse_map (dim, elem, physical_points[p], tolerance, secure);
330 }
331 
332 
333 } // namespace libMesh
334 
335 
336 #endif //ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
void add_scaled(const TypeVector< T2 > &, const T &)
Add a scaled value to this vector without creating a temporary.
Definition: type_vector.h:629
Order
defines an enum for polynomial orders.
Definition: enum_order.h:40
auto norm() const -> decltype(std::norm(T()))
Definition: type_vector.h:907
virtual Point origin() const
Definition: elem.h:1914
static OutputShape shape(const ElemType t, const Order o, const unsigned int i, const Point &p)
static Point map(const unsigned int dim, const Elem *inf_elem, const Point &reference_point)
Definition: inf_fe_map.C:40
static constexpr Real TOLERANCE
unsigned int dim
static Point inverse_map(const unsigned int dim, const Elem *elem, const Point &p, const Real tolerance=TOLERANCE, const bool secure=true, const bool extra_checks=true)
Definition: fe_map.C:1628
static Real eval(Real v, Order o, unsigned int i)
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
void solve(const TypeVector< T > &b, TypeVector< T > &x) const
Solve the 2x2 or 3x3 system of equations A*x = b for x by directly inverting A and multiplying it by ...
Definition: type_tensor.h:1129
static OutputShape shape_deriv(const ElemType t, const Order o, const unsigned int i, const unsigned int j, const Point &p)
The libMesh namespace provides an interface to certain functionality in the library.
static Point inverse_map(const unsigned int dim, const Elem *elem, const Point &p, const Real tolerance=TOLERANCE, const bool secure=true)
Definition: inf_fe_map.C:96
static std::unique_ptr< const Elem > build_elem(const Elem *inf_elem)
Build the base element of an infinite element.
bool libmesh_isinf(T x)
This class defines a tensor in LIBMESH_DIM dimensional space of type T.
Definition: tensor_tools.h:36
virtual unsigned int n_shape_functions() const override
Definition: fe.C:75
static Order mapping_order()
Definition: inf_fe.h:97
libmesh_assert(ctx)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
static Point map(const unsigned int dim, const Elem *elem, const Point &reference_point)
Definition: fe_map.C:2069
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
const Point & point(const unsigned int i) const
Definition: elem.h:2453