libMesh
inf_fe_boundary.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 // C++ includes
21 
22 
23 // Local includes
24 #include "libmesh/libmesh_config.h"
25 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
26 #include "libmesh/inf_fe.h"
27 #include "libmesh/inf_fe_macro.h"
28 #include "libmesh/quadrature.h"
29 #include "libmesh/enum_quadrature_type.h"
30 #include "libmesh/elem.h"
31 
32 namespace libMesh
33 {
34 
35 // Method for 2D, 3D -- see inf_fe_1D.C for a 1D version of this
36 template <unsigned int Dim, FEFamily T_radial, InfMapType T_base>
38  const unsigned int s,
39  const Real /*tolerance*/,
40  const std::vector<Point> * const pts,
41  const std::vector<Real> * const weights)
42 {
43  if (weights != nullptr)
44  libmesh_not_implemented_msg("ERROR: User-specified weights for infinite elements are not implemented!");
45 
46  if (pts != nullptr)
47  libmesh_not_implemented_msg("ERROR: User-specified points for infinite elements are not implemented!");
48 
49  // We don't do this for 1D elements!
50  libmesh_assert_not_equal_to (Dim, 1);
51 
52  libmesh_assert(inf_elem);
53  libmesh_assert(qrule);
54 
55  // Build the side of interest
56  const std::unique_ptr<const Elem> side(inf_elem->build_side_ptr(s));
57 
58  // set the element and type
59  this->_elem = inf_elem;
60  this->_elem_type = inf_elem->type();
61 
62  // eventually initialize radial quadrature rule
63  bool radial_qrule_initialized = false;
64 
65  // if we are working on the base-side, the radial function is constant.
66  // With this, we ensure that at least for base elements we reinitialize all quantities
67  // when we enter for the first time.
68  if (s == 0)
69  current_fe_type.radial_order = 0;
70  else
75  libmesh_not_implemented();
76 
77  if (current_fe_type.radial_order != fe_type.radial_order)
78  {
79  if (s > 0)
80  {
81  current_fe_type.radial_order = fe_type.radial_order;
82  radial_qrule->init(EDGE2, inf_elem->p_level());
83  }
84  else
85  {
86  // build a new 0-dimensional quadrature-rule:
87  radial_qrule=QBase::build(QGAUSS, 0, fe_type.radial_order);
88  radial_qrule->init(NODEELEM, 0, /*simple_type_only=*/true);
89 
90  //the base_qrule is set up with dim-1, but apparently we need dim, so we replace it:
91  base_qrule=QBase::build(qrule->type(), side->dim(), qrule->get_order());
92 
93  unsigned int side_p_level = inf_elem->p_level();
94  if (inf_elem->neighbor_ptr(s) != nullptr)
95  side_p_level = std::max(side_p_level, inf_elem->neighbor_ptr(s)->p_level());
96  base_qrule->init(*side, side_p_level);
97  }
98  radial_qrule_initialized = true;
99  }
100 
101  // Initialize the face shape functions
102  if (this->get_type() != inf_elem->type() ||
103  base_fe->shapes_need_reinit() ||
104  radial_qrule_initialized)
105  this->init_face_shape_functions (qrule->get_points(), side.get());
106 
107  // The reinit() function computes all what we want except for
108  // - normal, tangents: They are not considered
109  // This is done below:
110  compute_face_functions();
111 }
112 
113 
114 
115 // Method for 2D, 3D -- see inf_fe_1D.C for a 1D version of this
116 template <unsigned int Dim, FEFamily T_radial, InfMapType T_base>
118  const unsigned int,
119  const Real,
120  const std::vector<Point> * const pts,
121  const std::vector<Real> * const /*weights*/)
122 {
123  // We don't do this for 1D elements!
124  //libmesh_assert_not_equal_to (Dim, 1);
125  libmesh_not_implemented_msg("ERROR: Edge conditions for infinite elements not implemented!");
126 
127  if (pts != nullptr)
128  libmesh_not_implemented_msg("ERROR: User-specified points for infinite elements not implemented!");
129 }
130 
131 
132 
133 
134 template <unsigned int Dim, FEFamily T_radial, InfMapType T_base>
136  const Elem * inf_side)
137 {
138  libmesh_assert(inf_side);
139 
140  // Currently, this makes only sense in 3-D!
141  libmesh_assert_equal_to (Dim, 3);
142 
143  // Initialize the radial shape functions (in particular som)
144  this->init_radial_shape_functions(inf_side);
145 
146  // Initialize the base shape functions
147  if (inf_side->infinite())
148  this->update_base_elem(inf_side);
149  else
150  // in this case, I need the 2D base
151  this->update_base_elem(inf_side->interior_parent());
152 
153  // Initialize the base quadrature rule
154  base_qrule->init(*base_elem, inf_side->p_level());
155 
156  // base_fe still corresponds to the (dim-1)-dimensional base of the InfFE object,
157  // so update the fe_base.
158  if (inf_side->infinite())
159  {
160  base_fe = FEBase::build(Dim-2, this->fe_type);
161  base_fe->attach_quadrature_rule(base_qrule.get());
162  }
163  else
164  {
165  base_fe = FEBase::build(Dim-1, this->fe_type);
166  base_fe->attach_quadrature_rule(base_qrule.get());
167  }
168 
169  if (this->calculate_map || this->calculate_map_scaled)
170  {
171  //before initializing, we should say what to compute:
172  base_fe->_fe_map->get_xyz();
173  base_fe->_fe_map->get_JxW();
174  }
175 
176  if (calculate_phi || calculate_dphi || calculate_phi_scaled || calculate_dphi_scaled)
177  // initialize the shape functions on the base
178  base_fe->init_base_shape_functions(base_fe->qrule->get_points(),
179  base_elem.get());
180 
181  // the number of quadrature points
182  const unsigned int n_radial_qp = radial_qrule->n_points();
183  const unsigned int n_base_qp = base_qrule->n_points();
184  const unsigned int n_total_qp = n_radial_qp * n_base_qp;
185 
186 #ifdef DEBUG
187  if (som.size() > 0)
188  libmesh_assert_equal_to(n_radial_qp, som.size());
189  // when evaluating the base side, there should be only one radial point.
190  if (!inf_side->infinite())
191  libmesh_assert_equal_to (n_radial_qp, 1);
192 #endif
193 
194  // the quadrature weights
195  _total_qrule_weights.resize(n_total_qp);
196  std::vector<Point> qp(n_total_qp);
197 
198  // quadrature rule weights
199  if (Dim < 3)
200  {
201  // the quadrature points must be assembled differently for lower dims.
202  libmesh_not_implemented();
203  }
204  else
205  {
206  const std::vector<Real> & radial_qw = radial_qrule->get_weights();
207  const std::vector<Real> & base_qw = base_qrule->get_weights();
208  const std::vector<Point> & radial_qp = radial_qrule->get_points();
209  const std::vector<Point> & base_qp = base_qrule->get_points();
210 
211  libmesh_assert_equal_to (radial_qw.size(), n_radial_qp);
212  libmesh_assert_equal_to (base_qw.size(), n_base_qp);
213 
214  for (unsigned int rp=0; rp<n_radial_qp; rp++)
215  for (unsigned int bp=0; bp<n_base_qp; bp++)
216  {
217  _total_qrule_weights[bp + rp*n_base_qp] = radial_qw[rp] * base_qw[bp];
218  // initialize the quadrature-points for the 2D side element
219  // - either the base element or it has a 1D base + radial direction.
220  if (inf_side->infinite())
221  qp[bp + rp*n_base_qp]=Point(base_qp[bp](0),
222  0.,
223  radial_qp[rp](0));
224  else
225  qp[bp + rp*n_base_qp]=Point(base_qp[bp](0),
226  base_qp[bp](1),
227  -1.);
228  }
229  }
230 
231  this->reinit(inf_side->interior_parent(), &qp);
232 
233 }
234 
235 template <unsigned int Dim, FEFamily T_radial, InfMapType T_base>
237 {
238 
239  if (!calculate_map && !calculate_map_scaled)
240  return; // we didn't ask for any quantity computed here.
241 
242  const unsigned int n_qp = cast_int<unsigned int>(_total_qrule_weights.size());
243  this->normals.resize(n_qp);
244 
245  if (Dim > 1)
246  {
247  this->tangents.resize(n_qp);
248  for (unsigned int p=0; p<n_qp; ++p)
249  this->tangents[p].resize(LIBMESH_DIM-1);
250  }
251  else
252  {
253  libMesh::err << "tangents have no sense in 1-dimensional elements!"<<std::endl;
254  libmesh_error_msg("Exiting...");
255  }
256 
257  // the dimension of base indicates which side we have:
258  // if base_dim == Dim -1 : base
259  // base_dim == Dim -2 : one of the other sides.
260  unsigned int base_dim =base_fe->dim;
261  // If we have no quadrature points, there's nothing else to do
262  if (!n_qp)
263  return;
264 
265  switch(Dim)
266  {
267  case 1:
268  case 2:
269  {
270  libmesh_not_implemented();
271  break;
272  }
273  case 3:
274  {
275  // Below, we assume a 2D base, i.e. we compute the side s=0.
276  if (base_dim==Dim-1)
277  for (unsigned int p=0; p<n_qp; ++p)
278  {
279  //
280  // seeking dxyzdx, dxyzdeta means to compute
281  // / dx/dxi dy/dxi dz/dxi \.
282  // J^-1= | |
283  // \ dx/deta dy/deta dz/deta /.
284  // which is the psudo-inverse of J, i.e.
285  //
286  // J^-1 = (J^T J)^-1 J^T
287  //
288  // where J^T T is the 2x2 matrix 'g' used to compute the
289  // Jacobian determinant; thus
290  //
291  // J^-1 = ________1________ / g22 -g21 \ / dxi/dx dxi/dy dxi/dz \.
292  // g11*g22 - g21*g12 \-g12 g11 / \ deta/dx deta/dy deta/dz /.
293  const std::vector<Real> & base_dxidx = base_fe->get_dxidx();
294  const std::vector<Real> & base_dxidy = base_fe->get_dxidy();
295  const std::vector<Real> & base_dxidz = base_fe->get_dxidz();
296  const std::vector<Real> & base_detadx = base_fe->get_detadx();
297  const std::vector<Real> & base_detady = base_fe->get_detady();
298  const std::vector<Real> & base_detadz = base_fe->get_detadz();
299 
300  const Real g11 = (base_dxidx[p]*base_dxidx[p] +
301  base_dxidy[p]*base_dxidy[p] +
302  base_dxidz[p]*base_dxidz[p]);
303  const Real g12 = (base_dxidx[p]*base_detadx[p] +
304  base_dxidy[p]*base_detady[p] +
305  base_dxidz[p]*base_detadz[p]);
306  const Real g21 = g12;
307  const Real g22 = (base_detadx[p]*base_detadx[p] +
308  base_detady[p]*base_detady[p] +
309  base_detadz[p]*base_detadz[p]);
310 
311  const Real det = (g11*g22 - g12*g21);
312 
313  Point dxyzdxi_map((g22*base_dxidx[p]-g21*base_detadx[p])/det,
314  (g22*base_dxidy[p]-g21*base_detady[p])/det,
315  (g22*base_dxidz[p]-g21*base_detadz[p])/det);
316 
317  Point dxyzdeta_map((g11*base_detadx[p] - g12*base_dxidx[p])/det,
318  (g11*base_detady[p] - g12*base_dxidy[p])/det,
319  (g11*base_detadz[p] - g12*base_dxidz[p])/det);
320 
321  this->tangents[p][0] = dxyzdxi_map.unit();
322 
323  this->tangents[p][1] = (dxyzdeta_map - (dxyzdeta_map*tangents[p][0])*tangents[p][0] ).unit();
324 
325  this->normals[p] = tangents[p][0].cross(tangents[p][1]).unit();
326  // recompute JxW using the 2D Jacobian:
327  // Since we are at the base, there is no difference between scaled and unscaled jacobian
328  if (calculate_jxw)
329  this->JxW[p] = _total_qrule_weights[p]/std::sqrt(det);
330 
331  if (calculate_map_scaled)
332  this->JxWxdecay[p] = _total_qrule_weights[p]/std::sqrt(det);
333 
334  }
335  else if (base_dim == Dim -2)
336  {
337  libmesh_not_implemented();
338  }
339  else
340  {
341  // in this case something went completely wrong.
342  libmesh_not_implemented();
343  }
344  break;
345  }
346  default:
347  libmesh_error_msg("Unsupported dim = " << dim);
348  }
349 
350 }
351 
352 
353 
354 // Explicit instantiations - doesn't make sense in 1D, but as
355 // long as we only return errors, we are fine... ;-)
356 //#include "libmesh/inf_fe_instantiate_1D.h"
357 //#include "libmesh/inf_fe_instantiate_2D.h"
358 //#include "libmesh/inf_fe_instantiate_3D.h"
359 INSTANTIATE_INF_FE_MBRF(1, CARTESIAN, void, reinit(const Elem *, const unsigned int, const Real, const std::vector<Point> * const, const std::vector<Real> * const));
360 INSTANTIATE_INF_FE_MBRF(2, CARTESIAN, void, reinit(const Elem *, const unsigned int, const Real, const std::vector<Point> * const, const std::vector<Real> * const));
361 INSTANTIATE_INF_FE_MBRF(3, CARTESIAN, void, reinit(const Elem *, const unsigned int, const Real, const std::vector<Point> * const, const std::vector<Real> * const));
362 INSTANTIATE_INF_FE_MBRF(1, CARTESIAN, void, edge_reinit(const Elem *, const unsigned int, const Real, const std::vector<Point> * const, const std::vector<Real> * const));
363 INSTANTIATE_INF_FE_MBRF(2, CARTESIAN, void, edge_reinit(const Elem *, const unsigned int, const Real, const std::vector<Point> * const, const std::vector<Real> * const));
364 INSTANTIATE_INF_FE_MBRF(3, CARTESIAN, void, edge_reinit(const Elem *, const unsigned int, const Real, const std::vector<Point> * const, const std::vector<Real> * const));
365 INSTANTIATE_INF_FE_MBRF(1, CARTESIAN, void, init_face_shape_functions(const std::vector<Point> &, const Elem *));
366 INSTANTIATE_INF_FE_MBRF(2, CARTESIAN, void, init_face_shape_functions(const std::vector<Point> &, const Elem *));
367 INSTANTIATE_INF_FE_MBRF(3, CARTESIAN, void, init_face_shape_functions(const std::vector<Point> &, const Elem *));
368 INSTANTIATE_INF_FE_MBRF(1, CARTESIAN, void, compute_face_functions());
369 INSTANTIATE_INF_FE_MBRF(2, CARTESIAN, void, compute_face_functions());
370 INSTANTIATE_INF_FE_MBRF(3, CARTESIAN, void, compute_face_functions());
371 
372 } // namespace libMesh
373 
374 #endif //ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
OStreamProxy err
const Elem * interior_parent() const
Definition: elem.C:1186
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
unsigned int dim
INSTANTIATE_INF_FE_MBRF(1, CARTESIAN, void, reinit(const Elem *, const unsigned int, const Real, const std::vector< Point > *const, const std::vector< Real > *const))
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
unsigned int p_level() const
Definition: elem.h:3108
The libMesh namespace provides an interface to certain functionality in the library.
void init_face_shape_functions(const std::vector< Point > &, const Elem *inf_side)
Initialize all the data fields like weight, phi, etc for the side s.
void compute_face_functions()
TypeVector< T > unit() const
Definition: type_vector.h:1104
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
libmesh_assert(ctx)
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
Definition: type_vector.h:884
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2598
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual void edge_reinit(const Elem *elem, const unsigned int edge, const Real tolerance=TOLERANCE, const std::vector< Point > *const pts=nullptr, const std::vector< Real > *const weights=nullptr) override
Not implemented yet.
static std::unique_ptr< QBase > build(std::string_view name, const unsigned int dim, const Order order=INVALID_ORDER)
Builds a specific quadrature rule based on the name string.
virtual void reinit(const Elem *elem, const std::vector< Point > *const pts=nullptr, const std::vector< Real > *const weights=nullptr) override
This is at the core of this class.
Definition: inf_fe.C:120
virtual bool infinite() const =0
virtual ElemType type() const =0
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39