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