libMesh
fe_side_hierarchic.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/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 
27 namespace libMesh
28 {
29 
30 // ------------------------------------------------------------
31 // Hierarchic-specific implementations
32 
33 // Anonymous namespace for local helper functions
34 namespace {
35 
36 void side_hierarchic_nodal_soln(const Elem * elem,
37  const Order /* order */,
38  const std::vector<Number> & /* elem_soln */,
39  std::vector<Number> & nodal_soln,
40  const bool /*add_p_level*/)
41 {
42  const unsigned int n_nodes = elem->n_nodes();
43 
44  std::fill(nodal_soln.begin(), nodal_soln.end(), 0);
45  nodal_soln.resize(n_nodes, 0);
46 
47  // We request nodal solutions when plotting, for consistency with
48  // other elements; plotting 0 on non-sides makes sense in that
49  // context.
50  // libmesh_warning("Nodal solution requested for a side element; this makes no sense.");
51 } // side_hierarchic_nodal_soln()
52 
53 
54 void side_hierarchic_side_nodal_soln
55  (const Elem * elem, const Order o,
56  const unsigned int side,
57  const std::vector<Number> & elem_soln,
58  std::vector<Number> & nodal_soln_on_side,
59  const bool /*add_p_level*/)
60 {
61  // Cheat here for now: perturb vertices toward the side center so as
62  // to make the values well-defined.
63  const std::vector<unsigned int> side_nodes =
64  elem->nodes_on_side(side);
65  const std::size_t n_side_nodes = side_nodes.size();
66 
67  const FEType fe_type{o, SIDE_HIERARCHIC};
68 
69  Point side_center;
70  for (auto n : side_nodes)
71  side_center += elem->master_point(n);
72  side_center /= n_side_nodes;
73 
74  nodal_soln_on_side.resize(n_side_nodes);
75  for (auto i : make_range(n_side_nodes))
76  {
77  const auto n = side_nodes[i];
78  Point master_p = elem->master_point(n);
79  master_p += TOLERANCE*TOLERANCE*(side_center-master_p);
80 
81  const unsigned int n_sf =
82  FEInterface::n_shape_functions(fe_type, elem);
83 
84  nodal_soln_on_side[i] = 0;
85  for (auto j : make_range(n_sf))
86  nodal_soln_on_side[i] += elem_soln[j] *
87  FEInterface::shape(fe_type, elem, j, master_p);
88  }
89 }
90 
91 
92 
93 unsigned int side_hierarchic_n_dofs_at_node(const ElemType t,
94  const Order o,
95  const unsigned int n)
96 {
97  switch (t)
98  {
99  case EDGE2:
100  case EDGE3:
101  case EDGE4:
102  if (n < 2)
103  return 1; // One per side
104  else
105  return 0;
106  case QUAD8:
107  case QUADSHELL8:
108  case QUAD9:
109  case QUADSHELL9:
110  if (n > 3 && n < 8)
111  return o+1;
112  else
113  return 0;
114  case HEX27:
115  if (n > 19 && n < 26)
116  return (o+1)*(o+1); // (o+1)^2 per side
117  else
118  return 0;
119  case TRI6:
120  case TRI7:
121  if (n > 2 && n < 6)
122  return o+1;
123  else
124  return 0;
125  case TET14:
126  if (n > 9)
127  return (o+1)*(o+2)/2;
128  else
129  return 0;
130  case PRISM20:
131  case PRISM21:
132  if (n > 19)
133  return 0;
134  if (n > 17)
135  return (o+1)*(o+2)/2;
136  if (n > 14)
137  return (o+1)*(o+1);
138  return 0;
139  case INVALID_ELEM:
140  return 0;
141  // Without side nodes on all sides we can't support side elements
142  default:
143  libmesh_error_msg("ERROR: Invalid ElemType " << Utility::enum_to_string(t) << " selected for SIDE_HIERARCHIC FE family!");
144  }
145 } // side_hierarchic_n_dofs()
146 
147 
148 
149 unsigned int side_hierarchic_n_dofs_at_node(const Elem & e,
150  const Order o,
151  const unsigned int n)
152 {
153  return side_hierarchic_n_dofs_at_node(e.type(), o, n);
154 }
155 
156 
157 
158 unsigned int side_hierarchic_n_dofs(const ElemType t, const Order o)
159 {
160  switch (t)
161  {
162  case EDGE2:
163  case EDGE3:
164  case EDGE4:
165  return 2; // One per side
166  case QUAD8:
167  case QUADSHELL8:
168  case QUAD9:
169  case QUADSHELL9:
170  return ((o+1)*4); // o+1 per side
171  case HEX27:
172  return ((o+1)*(o+1)*6); // (o+1)^2 per side
173  case TRI6:
174  case TRI7:
175  return ((o+1)*3); // o+1 per side
176  case TET14:
177  return (o+1)*(o+2)*2; // 4 sides, each (o+1)(o+2)/2
178  case PRISM20:
179  case PRISM21:
180  return (o+1)*(o+1)*3+(o+1)*(o+2); // 2 tris, (o+1)(o+2)/2; 3 quads
181  case INVALID_ELEM:
182  return 0;
183  // Without side nodes on all sides we can't support side elements
184  default:
185  libmesh_error_msg("ERROR: Invalid ElemType " << Utility::enum_to_string(t) << " selected for HIERARCHIC FE family!");
186  }
187 } // side_hierarchic_n_dofs()
188 
189 
190 
191 unsigned int side_hierarchic_n_dofs(const Elem * e, const Order o)
192 {
193  return side_hierarchic_n_dofs(e->type(), o);
194 }
195 
196 
197 } // anonymous namespace
198 
199 
200 // Instantiate nodal_soln() function for every dimension
201 LIBMESH_FE_NODAL_SOLN(SIDE_HIERARCHIC, side_hierarchic_nodal_soln)
202 
203 // side_nodal_soln() has to be manually defined here, since nodal_soln
204 // isn't well-defined so we can't fall back on it.
205 template <>
207  (const Elem *, const Order,
208  const unsigned int,
209  const std::vector<Number> &,
210  std::vector<Number> &,
211  bool,
212  const unsigned)
213 {
214  libmesh_error_msg("No side variables in 0D!");
215 }
216 
217 template <>
219  (const Elem *, const Order,
220  const unsigned int side,
221  const std::vector<Number> & elem_soln,
222  std::vector<Number> & nodal_soln_on_side,
223  const bool /*add_p_level*/,
224  const unsigned)
225 {
226  libmesh_assert_less(side, 2);
227  nodal_soln_on_side.resize(1);
228  nodal_soln_on_side[0] = elem_soln[side];
229 }
230 
231 
232 template <>
234  (const Elem * elem, const Order o,
235  const unsigned int side,
236  const std::vector<Number> & elem_soln,
237  std::vector<Number> & nodal_soln_on_side,
238  const bool add_p_level,
239  const unsigned)
240 {
241  libmesh_assert_equal_to(elem->dim(), 2);
242  side_hierarchic_side_nodal_soln(elem, o, side, elem_soln,
243  nodal_soln_on_side,
244  add_p_level);
245 }
246 
247 
248 template <>
250  (const Elem * elem, const Order o,
251  const unsigned int side,
252  const std::vector<Number> & elem_soln,
253  std::vector<Number> & nodal_soln_on_side,
254  const bool add_p_level,
255  const unsigned)
256 {
257  libmesh_assert_equal_to(elem->dim(), 3);
258  side_hierarchic_side_nodal_soln(elem, o, side, elem_soln,
259  nodal_soln_on_side,
260  add_p_level);
261 }
262 
263 
264 
265 // Full specialization of n_dofs() function for every dimension
266 template <> unsigned int FE<0,SIDE_HIERARCHIC>::n_dofs(const ElemType t, const Order o) { return side_hierarchic_n_dofs(t, o); }
267 template <> unsigned int FE<1,SIDE_HIERARCHIC>::n_dofs(const ElemType t, const Order o) { return side_hierarchic_n_dofs(t, o); }
268 template <> unsigned int FE<2,SIDE_HIERARCHIC>::n_dofs(const ElemType t, const Order o) { return side_hierarchic_n_dofs(t, o); }
269 template <> unsigned int FE<3,SIDE_HIERARCHIC>::n_dofs(const ElemType t, const Order o) { return side_hierarchic_n_dofs(t, o); }
270 
271 template <> unsigned int FE<0,SIDE_HIERARCHIC>::n_dofs(const Elem * e, const Order o) { return side_hierarchic_n_dofs(e, o); }
272 template <> unsigned int FE<1,SIDE_HIERARCHIC>::n_dofs(const Elem * e, const Order o) { return side_hierarchic_n_dofs(e, o); }
273 template <> unsigned int FE<2,SIDE_HIERARCHIC>::n_dofs(const Elem * e, const Order o) { return side_hierarchic_n_dofs(e, o); }
274 template <> unsigned int FE<3,SIDE_HIERARCHIC>::n_dofs(const Elem * e, const Order o) { return side_hierarchic_n_dofs(e, o); }
275 
276 // Full specialization of n_dofs_at_node() function for every dimension.
277 template <> unsigned int FE<0,SIDE_HIERARCHIC>::n_dofs_at_node(const ElemType t, const Order o, const unsigned int n) { return side_hierarchic_n_dofs_at_node(t, o, n); }
278 template <> unsigned int FE<1,SIDE_HIERARCHIC>::n_dofs_at_node(const ElemType t, const Order o, const unsigned int n) { return side_hierarchic_n_dofs_at_node(t, o, n); }
279 template <> unsigned int FE<2,SIDE_HIERARCHIC>::n_dofs_at_node(const ElemType t, const Order o, const unsigned int n) { return side_hierarchic_n_dofs_at_node(t, o, n); }
280 template <> unsigned int FE<3,SIDE_HIERARCHIC>::n_dofs_at_node(const ElemType t, const Order o, const unsigned int n) { return side_hierarchic_n_dofs_at_node(t, o, n); }
281 
282 template <> unsigned int FE<0,SIDE_HIERARCHIC>::n_dofs_at_node(const Elem & e, const Order o, const unsigned int n) { return side_hierarchic_n_dofs_at_node(e, o, n); }
283 template <> unsigned int FE<1,SIDE_HIERARCHIC>::n_dofs_at_node(const Elem & e, const Order o, const unsigned int n) { return side_hierarchic_n_dofs_at_node(e, o, n); }
284 template <> unsigned int FE<2,SIDE_HIERARCHIC>::n_dofs_at_node(const Elem & e, const Order o, const unsigned int n) { return side_hierarchic_n_dofs_at_node(e, o, n); }
285 template <> unsigned int FE<3,SIDE_HIERARCHIC>::n_dofs_at_node(const Elem & e, const Order o, const unsigned int n) { return side_hierarchic_n_dofs_at_node(e, o, n); }
286 
287 // Full specialization of n_dofs_per_elem() function for every dimension.
288 template <> unsigned int FE<0,SIDE_HIERARCHIC>::n_dofs_per_elem(const ElemType, const Order) { return 0; }
289 template <> unsigned int FE<1,SIDE_HIERARCHIC>::n_dofs_per_elem(const ElemType, const Order) { return 0; }
290 template <> unsigned int FE<2,SIDE_HIERARCHIC>::n_dofs_per_elem(const ElemType, const Order) { return 0; }
291 template <> unsigned int FE<3,SIDE_HIERARCHIC>::n_dofs_per_elem(const ElemType, const Order) { return 0; }
292 
293 template <> unsigned int FE<0,SIDE_HIERARCHIC>::n_dofs_per_elem(const Elem &, const Order) { return 0; }
294 template <> unsigned int FE<1,SIDE_HIERARCHIC>::n_dofs_per_elem(const Elem &, const Order) { return 0; }
295 template <> unsigned int FE<2,SIDE_HIERARCHIC>::n_dofs_per_elem(const Elem &, const Order) { return 0; }
296 template <> unsigned int FE<3,SIDE_HIERARCHIC>::n_dofs_per_elem(const Elem &, const Order) { return 0; }
297 
298 // Side FEMs are discontinuous from side to side
303 
304 // Side Hierarchic FEMs are hierarchic (duh!)
305 template <> bool FE<0,SIDE_HIERARCHIC>::is_hierarchic() const { return true; }
306 template <> bool FE<1,SIDE_HIERARCHIC>::is_hierarchic() const { return true; }
307 template <> bool FE<2,SIDE_HIERARCHIC>::is_hierarchic() const { return true; }
308 template <> bool FE<3,SIDE_HIERARCHIC>::is_hierarchic() const { return true; }
309 
310 #ifdef LIBMESH_ENABLE_AMR
311 // compute_constraints() specializations are only needed for 2 and 3D
312 template <>
314  DofMap & dof_map,
315  const unsigned int variable_number,
316  const Elem * elem)
317 { compute_proj_constraints(constraints, dof_map, variable_number, elem); }
318 
319 template <>
321  DofMap & dof_map,
322  const unsigned int variable_number,
323  const Elem * elem)
324 { compute_proj_constraints(constraints, dof_map, variable_number, elem); }
325 #endif // #ifdef LIBMESH_ENABLE_AMR
326 
327 // Hierarchic FEM shapes need reinit
328 template <> bool FE<0,SIDE_HIERARCHIC>::shapes_need_reinit() const { return true; }
329 template <> bool FE<1,SIDE_HIERARCHIC>::shapes_need_reinit() const { return true; }
330 template <> bool FE<2,SIDE_HIERARCHIC>::shapes_need_reinit() const { return true; }
331 template <> bool FE<3,SIDE_HIERARCHIC>::shapes_need_reinit() const { return true; }
332 
333 } // 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 constexpr Real TOLERANCE
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.
LIBMESH_FE_NODAL_SOLN(LIBMESH_FE_SIDE_NODAL_SOLN() LIBMESH_DEFAULT_NDOFS(BERNSTEIN) template<> FEContinuity FE< 0 BERNSTEIN, bernstein_nodal_soln)
Definition: fe_bernstein.C:415
virtual bool is_hierarchic() const override
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
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:272
static Real shape(const unsigned int dim, const FEType &fe_t, const ElemType t, const unsigned int i, const Point &p)
Definition: fe_interface.C:760
static unsigned int n_dofs_per_elem(const ElemType t, const Order o)
virtual FEContinuity get_continuity() const override
static void side_nodal_soln(const Elem *elem, const Order o, const unsigned int side, const std::vector< Number > &elem_soln, std::vector< Number > &nodal_soln_on_side, bool add_p_level=true, const unsigned vdim=1)
Build the nodal soln on one side from the (full) element soln.
std::string enum_to_string(const T e)
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...
virtual unsigned short dim() const =0
FEContinuity
defines an enum for finite element types to libmesh_assert a certain level (or type? Hcurl?) of continuity.
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
The constraint matrix storage format.
Definition: dof_map.h:108