libMesh
fe_xyz.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2024 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/elem.h"
22 #include "libmesh/enum_to_string.h"
23 #include "libmesh/fe.h"
24 #include "libmesh/fe_interface.h"
25 #include "libmesh/fe_macro.h"
26 #include "libmesh/int_range.h"
27 #include "libmesh/libmesh_logging.h"
28 
29 namespace libMesh
30 {
31 
32 // ------------------------------------------------------------
33 // XYZ-specific implementations
34 
35 // Anonymous namespace for local helper functions
36 namespace {
37 
38 void xyz_nodal_soln(const Elem * elem,
39  const Order order,
40  const std::vector<Number> & elem_soln,
41  std::vector<Number> & nodal_soln,
42  const bool add_p_level)
43 {
44  const unsigned int n_nodes = elem->n_nodes();
45 
46  nodal_soln.resize(n_nodes);
47 
48  const Order totalorder = static_cast<Order>(order + add_p_level*elem->p_level());
49 
50  switch (totalorder)
51  {
52  // Constant shape functions
53  case CONSTANT:
54  {
55  libmesh_assert_equal_to (elem_soln.size(), 1);
56 
57  const Number val = elem_soln[0];
58 
59  for (unsigned int n=0; n<n_nodes; n++)
60  nodal_soln[n] = val;
61 
62  return;
63  }
64 
65 
66  // For other orders do interpolation at the nodes
67  // explicitly.
68  default:
69  {
70  // FEType object to be passed to various FEInterface functions below.
71  FEType fe_type(order, XYZ);
72 
73  const unsigned int n_sf =
74  FEInterface::n_shape_functions(fe_type, elem);
75 
76  for (unsigned int n=0; n<n_nodes; n++)
77  {
78  libmesh_assert_equal_to (elem_soln.size(), n_sf);
79 
80  // Zero before summation
81  nodal_soln[n] = 0;
82 
83  // u_i = Sum (alpha_i phi_i)
84  for (unsigned int i=0; i<n_sf; i++)
85  nodal_soln[n] += elem_soln[i] *
86  FEInterface::shape(fe_type, elem, i, elem->point(n));
87  }
88 
89  return;
90  } // default
91  } // switch
92 } // xyz_nodal_soln()
93 
94 
95 } // anonymous namespace
96 
97 
98 
99 
100 
101 
102 
103 template <unsigned int Dim>
104 void FEXYZ<Dim>::init_shape_functions(const std::vector<Point> & qp,
105  const Elem * libmesh_dbg_var(elem))
106 {
107  libmesh_assert(elem);
108 
109  // FIXME: Is this redundant here? Who's calling init_shape_functions
110  // from code that hasn't already done a determine_calculations()?
111  this->determine_calculations();
112 
113  // Start logging the shape function initialization
114  LOG_SCOPE("init_shape_functions()", "FE");
115 
116  // The number of quadrature points.
117  const std::size_t n_qp = qp.size();
118 
119  // Number of shape functions in the finite element approximation
120  // space.
121  const unsigned int n_approx_shape_functions =
122  this->n_shape_functions(this->get_type(),
123  this->get_order());
124 
125  // resize the vectors to hold current data
126  // Phi are the shape functions used for the FE approximation
127  {
128  // (note: GCC 3.4.0 requires the use of this-> here)
129  if (this->calculate_phi)
130  this->phi.resize (n_approx_shape_functions);
131  if (this->calculate_dphi)
132  {
133  this->dphi.resize (n_approx_shape_functions);
134  this->dphidx.resize (n_approx_shape_functions);
135  this->dphidy.resize (n_approx_shape_functions);
136  this->dphidz.resize (n_approx_shape_functions);
137  }
138 
139 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
140  if (this->calculate_d2phi)
141  {
142  this->d2phi.resize (n_approx_shape_functions);
143  this->d2phidx2.resize (n_approx_shape_functions);
144  this->d2phidxdy.resize (n_approx_shape_functions);
145  this->d2phidxdz.resize (n_approx_shape_functions);
146  this->d2phidy2.resize (n_approx_shape_functions);
147  this->d2phidydz.resize (n_approx_shape_functions);
148  this->d2phidz2.resize (n_approx_shape_functions);
149  this->d2phidxi2.resize (n_approx_shape_functions);
150  }
151 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
152 
153  for (unsigned int i=0; i<n_approx_shape_functions; i++)
154  {
155  if (this->calculate_phi)
156  this->phi[i].resize (n_qp);
157  if (this->calculate_dphi)
158  {
159  this->dphi[i].resize (n_qp);
160  this->dphidx[i].resize (n_qp);
161  this->dphidy[i].resize (n_qp);
162  this->dphidz[i].resize (n_qp);
163  }
164 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
165  if (this->calculate_d2phi)
166  {
167  this->d2phi[i].resize (n_qp);
168  this->d2phidx2[i].resize (n_qp);
169  this->d2phidxdy[i].resize (n_qp);
170  this->d2phidxdz[i].resize (n_qp);
171  this->d2phidy2[i].resize (n_qp);
172  this->d2phidydz[i].resize (n_qp);
173  this->d2phidz2[i].resize (n_qp);
174  }
175 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
176  }
177  }
178 
179 
180 
181 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
182  //------------------------------------------------------------
183  // Initialize the data fields, which should only be used for infinite
184  // elements, to some sensible values, so that using a FE with the
185  // variational formulation of an InfFE, correct element matrices are
186  // returned
187 
188  {
189  this->weight.resize (n_qp);
190  this->dweight.resize (n_qp);
191  this->dphase.resize (n_qp);
192 
193  for (unsigned int p=0; p<n_qp; p++)
194  {
195  this->weight[p] = 1.;
196  this->dweight[p].zero();
197  this->dphase[p].zero();
198  }
199 
200  }
201 #endif // ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
202 
203  if (this->calculate_dual)
204  this->init_dual_shape_functions(n_approx_shape_functions, n_qp);
205 }
206 
207 
208 
209 
210 template <unsigned int Dim>
212  const std::vector<Point> &)
213 {
214  libmesh_assert(elem);
215 
216  //-------------------------------------------------------------------------
217  // Compute the shape function values (and derivatives)
218  // at the Quadrature points. Note that the actual values
219  // have already been computed via init_shape_functions
220 
221  // Start logging the shape function computation
222  LOG_SCOPE("compute_shape_functions()", "FE");
223 
224  const std::vector<Point> & xyz_qp = this->get_xyz();
225 
226  // Compute the value of the derivative shape function i at quadrature point p
227  switch (this->dim)
228  {
229 
230  case 1:
231  {
232  if (this->calculate_phi)
233  for (auto i : index_range(this->phi))
234  for (auto p : index_range(this->phi[i]))
235  this->phi[i][p] = FE<Dim,XYZ>::shape (elem, this->fe_type.order, i, xyz_qp[p]);
236 
237  if (this->calculate_dphi)
238  for (auto i : index_range(this->dphi))
239  for (auto p : index_range(this->dphi[i]))
240  {
241  this->dphi[i][p](0) =
242  this->dphidx[i][p] = FE<Dim,XYZ>::shape_deriv (elem, this->fe_type.order, i, 0, xyz_qp[p]);
243 
244  this->dphi[i][p](1) = this->dphidy[i][p] = 0.;
245  this->dphi[i][p](2) = this->dphidz[i][p] = 0.;
246  }
247 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
248  if (this->calculate_d2phi)
249  for (auto i : index_range(this->d2phi))
250  for (auto p : index_range(this->d2phi[i]))
251  {
252  this->d2phi[i][p](0,0) =
253  this->d2phidx2[i][p] = FE<Dim,XYZ>::shape_second_deriv (elem, this->fe_type.order, i, 0, xyz_qp[p]);
254 
255 #if LIBMESH_DIM>1
256  this->d2phi[i][p](0,1) = this->d2phidxdy[i][p] =
257  this->d2phi[i][p](1,0) = 0.;
258  this->d2phi[i][p](1,1) = this->d2phidy2[i][p] = 0.;
259 #if LIBMESH_DIM>2
260  this->d2phi[i][p](0,2) = this->d2phidxdz[i][p] =
261  this->d2phi[i][p](2,0) = 0.;
262  this->d2phi[i][p](1,2) = this->d2phidydz[i][p] =
263  this->d2phi[i][p](2,1) = 0.;
264  this->d2phi[i][p](2,2) = this->d2phidz2[i][p] = 0.;
265 #endif
266 #endif
267  }
268 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
269 
270  // All done
271  break;
272  }
273 
274  case 2:
275  {
276  if (this->calculate_phi)
277  for (auto i : index_range(this->phi))
278  for (auto p : index_range(this->phi[i]))
279  this->phi[i][p] = FE<Dim,XYZ>::shape (elem, this->fe_type.order, i, xyz_qp[p]);
280 
281  if (this->calculate_dphi)
282  for (auto i : index_range(this->dphi))
283  for (auto p : index_range(this->dphi[i]))
284  {
285  this->dphi[i][p](0) =
286  this->dphidx[i][p] = FE<Dim,XYZ>::shape_deriv (elem, this->fe_type.order, i, 0, xyz_qp[p]);
287 
288  this->dphi[i][p](1) =
289  this->dphidy[i][p] = FE<Dim,XYZ>::shape_deriv (elem, this->fe_type.order, i, 1, xyz_qp[p]);
290 
291 #if LIBMESH_DIM == 3
292  this->dphi[i][p](2) = // can only assign to the Z component if LIBMESH_DIM==3
293 #endif
294  this->dphidz[i][p] = 0.;
295  }
296 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
297  if (this->calculate_d2phi)
298  for (auto i : index_range(this->d2phi))
299  for (auto p : index_range(this->d2phi[i]))
300  {
301  this->d2phi[i][p](0,0) =
302  this->d2phidx2[i][p] = FE<Dim,XYZ>::shape_second_deriv (elem, this->fe_type.order, i, 0, xyz_qp[p]);
303 
304  this->d2phi[i][p](0,1) = this->d2phidxdy[i][p] =
305  this->d2phi[i][p](1,0) = FE<Dim,XYZ>::shape_second_deriv (elem, this->fe_type.order, i, 1, xyz_qp[p]);
306  this->d2phi[i][p](1,1) =
307  this->d2phidy2[i][p] = FE<Dim,XYZ>::shape_second_deriv (elem, this->fe_type.order, i, 2, xyz_qp[p]);
308 #if LIBMESH_DIM>2
309  this->d2phi[i][p](0,2) = this->d2phidxdz[i][p] =
310  this->d2phi[i][p](2,0) = 0.;
311  this->d2phi[i][p](1,2) = this->d2phidydz[i][p] =
312  this->d2phi[i][p](2,1) = 0.;
313  this->d2phi[i][p](2,2) = this->d2phidz2[i][p] = 0.;
314 #endif
315  }
316 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
317 
318  // All done
319  break;
320  }
321 
322  case 3:
323  {
324  if (this->calculate_phi)
325  for (auto i : index_range(this->phi))
326  for (auto p : index_range(this->phi[i]))
327  this->phi[i][p] = FE<Dim,XYZ>::shape (elem, this->fe_type.order, i, xyz_qp[p]);
328 
329  if (this->calculate_dphi)
330  for (auto i : index_range(this->dphi))
331  for (auto p : index_range(this->dphi[i]))
332  {
333  this->dphi[i][p](0) =
334  this->dphidx[i][p] = FE<Dim,XYZ>::shape_deriv (elem, this->fe_type.order, i, 0, xyz_qp[p]);
335 
336  this->dphi[i][p](1) =
337  this->dphidy[i][p] = FE<Dim,XYZ>::shape_deriv (elem, this->fe_type.order, i, 1, xyz_qp[p]);
338 
339  this->dphi[i][p](2) =
340  this->dphidz[i][p] = FE<Dim,XYZ>::shape_deriv (elem, this->fe_type.order, i, 2, xyz_qp[p]);
341  }
342 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
343  if (this->calculate_d2phi)
344  for (auto i : index_range(this->d2phi))
345  for (auto p : index_range(this->d2phi[i]))
346  {
347  this->d2phi[i][p](0,0) =
348  this->d2phidx2[i][p] = FE<Dim,XYZ>::shape_second_deriv (elem, this->fe_type.order, i, 0, xyz_qp[p]);
349 
350  this->d2phi[i][p](0,1) = this->d2phidxdy[i][p] =
351  this->d2phi[i][p](1,0) = FE<Dim,XYZ>::shape_second_deriv (elem, this->fe_type.order, i, 1, xyz_qp[p]);
352  this->d2phi[i][p](1,1) =
353  this->d2phidy2[i][p] = FE<Dim,XYZ>::shape_second_deriv (elem, this->fe_type.order, i, 2, xyz_qp[p]);
354  this->d2phi[i][p](0,2) = this->d2phidxdz[i][p] =
355  this->d2phi[i][p](2,0) = FE<Dim,XYZ>::shape_second_deriv (elem, this->fe_type.order, i, 3, xyz_qp[p]);
356  this->d2phi[i][p](1,2) = this->d2phidydz[i][p] =
357  this->d2phi[i][p](2,1) = FE<Dim,XYZ>::shape_second_deriv (elem, this->fe_type.order, i, 4, xyz_qp[p]);
358  this->d2phi[i][p](2,2) = this->d2phidz2[i][p] = FE<Dim,XYZ>::shape_second_deriv (elem, this->fe_type.order, i, 5, xyz_qp[p]);
359  }
360 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
361 
362  // All done
363  break;
364  }
365 
366  default:
367  libmesh_error_msg("ERROR: Invalid dimension " << this->dim);
368  }
369 }
370 
371 
372 // Instantiate (side_) nodal_soln() function for every dimension
373 LIBMESH_FE_NODAL_SOLN(XYZ, xyz_nodal_soln)
375 
376 
377 // Full specialization of n_dofs() function for every dimension
378 template <> unsigned int FE<0,XYZ>::n_dofs(const ElemType t, const Order o) { return monomial_n_dofs(t, o); }
379 template <> unsigned int FE<1,XYZ>::n_dofs(const ElemType t, const Order o) { return monomial_n_dofs(t, o); }
380 template <> unsigned int FE<2,XYZ>::n_dofs(const ElemType t, const Order o) { return monomial_n_dofs(t, o); }
381 template <> unsigned int FE<3,XYZ>::n_dofs(const ElemType t, const Order o) { return monomial_n_dofs(t, o); }
382 
383 // Full specialization of n_dofs_at_node() function for every dimension.
384 // XYZ FEMs have no dofs at nodes, only element dofs.
385 template <> unsigned int FE<0,XYZ>::n_dofs_at_node(const ElemType, const Order, const unsigned int) { return 0; }
386 template <> unsigned int FE<1,XYZ>::n_dofs_at_node(const ElemType, const Order, const unsigned int) { return 0; }
387 template <> unsigned int FE<2,XYZ>::n_dofs_at_node(const ElemType, const Order, const unsigned int) { return 0; }
388 template <> unsigned int FE<3,XYZ>::n_dofs_at_node(const ElemType, const Order, const unsigned int) { return 0; }
389 
390 // Full specialization of n_dofs_per_elem() function for every dimension.
391 template <> unsigned int FE<0,XYZ>::n_dofs_per_elem(const ElemType t, const Order o) { return monomial_n_dofs(t, o); }
392 template <> unsigned int FE<1,XYZ>::n_dofs_per_elem(const ElemType t, const Order o) { return monomial_n_dofs(t, o); }
393 template <> unsigned int FE<2,XYZ>::n_dofs_per_elem(const ElemType t, const Order o) { return monomial_n_dofs(t, o); }
394 template <> unsigned int FE<3,XYZ>::n_dofs_per_elem(const ElemType t, const Order o) { return monomial_n_dofs(t, o); }
395 
396 // Full specialization of get_continuity() function for every dimension.
397 template <> FEContinuity FE<0,XYZ>::get_continuity() const { return DISCONTINUOUS; }
398 template <> FEContinuity FE<1,XYZ>::get_continuity() const { return DISCONTINUOUS; }
399 template <> FEContinuity FE<2,XYZ>::get_continuity() const { return DISCONTINUOUS; }
400 template <> FEContinuity FE<3,XYZ>::get_continuity() const { return DISCONTINUOUS; }
401 
402 // Full specialization of is_hierarchic() function for every dimension.
403 // The XYZ shape functions are hierarchic!
404 template <> bool FE<0,XYZ>::is_hierarchic() const { return true; }
405 template <> bool FE<1,XYZ>::is_hierarchic() const { return true; }
406 template <> bool FE<2,XYZ>::is_hierarchic() const { return true; }
407 template <> bool FE<3,XYZ>::is_hierarchic() const { return true; }
408 
409 #ifdef LIBMESH_ENABLE_AMR
410 
411 // Full specialization of compute_constraints() function for 2D and
412 // 3D only. There are no constraints for discontinuous elements, so
413 // we do nothing.
414 template <> void FE<2,XYZ>::compute_constraints (DofConstraints &, DofMap &, const unsigned int, const Elem *) {}
415 template <> void FE<3,XYZ>::compute_constraints (DofConstraints &, DofMap &, const unsigned int, const Elem *) {}
416 
417 #endif // #ifdef LIBMESH_ENABLE_AMR
418 
419 // Full specialization of shapes_need_reinit() function for every dimension.
420 template <> bool FE<0,XYZ>::shapes_need_reinit() const { return true; }
421 template <> bool FE<1,XYZ>::shapes_need_reinit() const { return true; }
422 template <> bool FE<2,XYZ>::shapes_need_reinit() const { return true; }
423 template <> bool FE<3,XYZ>::shapes_need_reinit() const { return true; }
424 
425 
426 // Explicit instantiations for non-static FEXYZ member functions.
427 // These non-static member functions map more naturally to explicit
428 // instantiations than the functions above:
429 //
430 // 1.) Since they are member functions, they rely on
431 // private/protected member data, and therefore do not work well
432 // with the "anonymous function call" model we've used above for
433 // the specializations.
434 //
435 // 2.) There is (IMHO) less chance of the linker calling the
436 // wrong version of one of these member functions, since there is
437 // only one FEXYZ.
438 template LIBMESH_EXPORT void FEXYZ<0>::init_shape_functions(const std::vector<Point> &, const Elem *);
439 template LIBMESH_EXPORT void FEXYZ<1>::init_shape_functions(const std::vector<Point> &, const Elem *);
440 template LIBMESH_EXPORT void FEXYZ<2>::init_shape_functions(const std::vector<Point> &, const Elem *);
441 template LIBMESH_EXPORT void FEXYZ<3>::init_shape_functions(const std::vector<Point> &, const Elem *);
442 
443 template LIBMESH_EXPORT void FEXYZ<0>::compute_shape_functions(const Elem *,const std::vector<Point> &);
444 template LIBMESH_EXPORT void FEXYZ<1>::compute_shape_functions(const Elem *,const std::vector<Point> &);
445 template LIBMESH_EXPORT void FEXYZ<2>::compute_shape_functions(const Elem *,const std::vector<Point> &);
446 template LIBMESH_EXPORT void FEXYZ<3>::compute_shape_functions(const Elem *,const std::vector<Point> &);
447 
448 } // namespace libMesh
static unsigned int n_dofs(const ElemType t, const Order o)
ElemType
Defines an enum for geometric element types.
Order
defines an enum for polynomial orders.
Definition: enum_order.h:40
static OutputShape shape(const ElemType t, const Order o, const unsigned int i, const Point &p)
unsigned int dim
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
static unsigned int n_dofs_at_node(const ElemType t, const Order o, const unsigned int n)
virtual bool shapes_need_reinit() const override
The libMesh namespace provides an interface to certain functionality in the library.
virtual void compute_shape_functions(const Elem *elem, const std::vector< Point > &qp) override
After having updated the jacobian and the transformation from local to global coordinates in FEAbstra...
Definition: fe_xyz.C:211
virtual bool is_hierarchic() const override
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:169
A specific instantiation of the FEBase class.
Definition: fe.h:127
LIBMESH_FE_NODAL_SOLN(BERNSTEIN, bernstein_nodal_soln)
Definition: fe_bernstein.C:397
LIBMESH_FE_SIDE_NODAL_SOLN(HIERARCHIC_VEC)
const dof_id_type n_nodes
Definition: tecplot_io.C:67
static unsigned int n_shape_functions(const unsigned int dim, const FEType &fe_t, const ElemType t)
Definition: fe_interface.C:515
dof_id_type weight(const MeshBase &mesh, const processor_id_type pid)
Definition: mesh_tools.C:436
static Real shape(const unsigned int dim, const FEType &fe_t, const ElemType t, const unsigned int i, const Point &p)
libmesh_assert(ctx)
unsigned int monomial_n_dofs(const ElemType t, const Order o)
Helper functions for Discontinuous-Pn type basis functions.
Definition: fe_monomial.C:30
static unsigned int n_dofs_per_elem(const ElemType t, const Order o)
virtual FEContinuity get_continuity() const override
static void compute_constraints(DofConstraints &constraints, DofMap &dof_map, const unsigned int variable_number, const Elem *elem)
Computes the constraint matrix contributions (for non-conforming adapted meshes) corresponding to var...
FEContinuity
defines an enum for finite element types to libmesh_assert a certain level (or type? Hcurl?) of continuity.
virtual void init_shape_functions(const std::vector< Point > &qp, const Elem *e) override
Update the various member data fields phi, dphidxi, dphideta, dphidzeta, etc.
Definition: fe_xyz.C:104
The constraint matrix storage format.
Definition: dof_map.h:98
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:111
static OutputShape shape_second_deriv(const ElemType t, const Order o, const unsigned int i, const unsigned int j, const Point &p)