libMesh
Loading...
Searching...
No Matches
face_c0polygon.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// Local includes
19#include "libmesh/face_c0polygon.h"
20
21#include "libmesh/edge_edge2.h"
22#include "libmesh/enum_order.h"
23#include "libmesh/tensor_value.h"
24
25// C++ headers
26#include <numeric> // std::iota
27
28namespace libMesh
29{
30
31
32C0Polygon::C0Polygon (const unsigned int num_sides, Elem * p) :
33 Polygon(num_sides, num_sides, p)
34{
35 // A default triangulation is better than nothing
36 for (int i : make_range(num_sides-2))
37 this->_triangulation.push_back({0, i+1, i+2});
38}
39
40
41unsigned int C0Polygon::opposite_node(const unsigned int node_in,
42 const unsigned int side_in) const
43{
44 const auto ns = this->n_sides();
45 if (ns % 2)
46 libmesh_error();
47
48 libmesh_assert_less (node_in, ns);
49 libmesh_assert_less (node_in, this->n_nodes());
50 libmesh_assert_less (side_in, this->n_sides());
51 libmesh_assert(this->is_node_on_side(node_in, side_in));
52
53 if (node_in == side_in)
54 return (node_in + ns/2 + 1) % ns;
55
56 libmesh_assert(node_in == side_in + 1);
57 return (node_in + ns/2 - 1) % ns;
58}
59
60
61
62// ------------------------------------------------------------
63// C0Polygon class member functions
64
65bool C0Polygon::is_vertex(const unsigned int libmesh_dbg_var(i)) const
66{
67 libmesh_assert (i < this->n_nodes());
68
69 return true;
70}
71
72bool C0Polygon::is_edge(const unsigned int libmesh_dbg_var(i)) const
73{
74 libmesh_assert (i < this->n_nodes());
75
76 return false;
77}
78
79bool C0Polygon::is_face(const unsigned int libmesh_dbg_var(i)) const
80{
81 libmesh_assert (i < this->n_nodes());
82
83 return false;
84}
85
86bool C0Polygon::is_node_on_side(const unsigned int n,
87 const unsigned int s) const
88{
89 const auto ns = this->n_sides();
90 libmesh_assert_less (s, ns);
91 libmesh_assert_less (n, this->n_nodes());
92
93 return ((n % ns) == s) ||
94 ((n < ns) && ((s+1)%ns) == n);
95}
96
97std::vector<unsigned int>
98C0Polygon::nodes_on_side(const unsigned int s) const
99{
100 const auto ns = this->n_sides();
101 libmesh_assert(!(this->n_nodes() % ns));
102 libmesh_assert_less(s, ns);
103
104 std::vector<unsigned int> returnval(2);
105 returnval[0] = s;
106 returnval[1] = (s+1)%ns;
107
108 return returnval;
109}
110
111std::vector<unsigned int>
112C0Polygon::nodes_on_edge(const unsigned int e) const
113{
114 return this->nodes_on_side(e);
115}
116
117bool C0Polygon::has_affine_map() const
118{
119 const unsigned int ns = this->n_sides();
120
121 // Get a good tolerance to use
122 Real perimeter_l1 = 0;
123 for (auto s : make_range(ns))
124 perimeter_l1 += (this->point((s+1)%ns) - this->point(s)).l1_norm();
125 const Real tol = perimeter_l1 * affine_tol;
126
127 // Find the map we expect to have
128 const Point veci = this->point(1) - this->point(0);
129 const Point vec12 = this->point(2) - this->point(1);
130
131 // Exterior angle
132 const Real theta = 2 * libMesh::pi / ns;
133 const Real costheta = cos(theta);
134 const Real sintheta = sin(theta);
135
136 RealTensor map;
137 map(0, 0) = veci(0);
138 map(1, 0) = veci(1);
139 map(2, 0) = veci(2);
140 map(0, 1) = (vec12(0) - veci(0)*costheta)/sintheta;
141 map(1, 1) = (vec12(1) - veci(1)*costheta)/sintheta;
142 map(2, 1) = (vec12(2) - veci(2)*costheta)/sintheta;
143
144 libmesh_assert_less((map * this->master_point(1) -
145 (this->point(1) - this->point(0))).l1_norm(),
146 tol);
147 libmesh_assert_less((map * this->master_point(2) -
148 (this->point(2) - this->point(0))).l1_norm(),
149 tol);
150
151 for (auto i : make_range(3u, ns))
152 if ((map * this->master_point(i) -
153 (this->point(i) - this->point(0))).l1_norm() >
154 tol)
155 return false;
156
157 return true;
158}
159
160
161
162Order C0Polygon::default_order() const
163{
164 return FIRST;
165}
166
167
168
169std::unique_ptr<Elem> C0Polygon::build_side_ptr (const unsigned int i)
170{
171 const auto ns = this->n_sides();
172 libmesh_assert_less (i, ns);
173
174 std::unique_ptr<Elem> sidep = std::make_unique<Edge2>();
175 sidep->set_node(0, this->node_ptr(i));
176 sidep->set_node(1, this->node_ptr((i+1)%ns));
177
178 sidep->set_interior_parent(this);
179 sidep->inherit_data_from(*this);
180
181 return sidep;
182}
183
184
185
186void C0Polygon::build_side_ptr (std::unique_ptr<Elem> & side,
187 const unsigned int i)
188{
189 const auto ns = this->n_sides();
190 libmesh_assert_less (i, ns);
191
192 if (!side.get() || side->type() != EDGE2)
193 {
194 side = this->build_side_ptr(i);
195 }
196 else
197 {
198 side->inherit_data_from(*this);
199
200 side->set_node(0, this->node_ptr(i));
201 side->set_node(1, this->node_ptr((i+1)%ns));
202 }
203}
204
205
206
207void C0Polygon::connectivity(const unsigned int /*sf*/,
208 const IOPackage /*iop*/,
209 std::vector<dof_id_type> & /*conn*/) const
210{
211 libmesh_not_implemented();
212}
213
214
215
216Real C0Polygon::volume () const
217{
218 // This specialization is good for non-Lagrange mappings, but
219 // remember to be more careful when we extend it to non-planar or
220 // non p=1 polygon types.
221 // if (this->mapping_type() != LAGRANGE_MAP)
222 // return this->Elem::volume();
223
224 // We use a triangulation to calculate here; assert that it's as
225 // consistent as possible.
226 libmesh_assert_equal_to (this->_triangulation.size(),
227 this->n_nodes() - 2);
228
229 Real double_area = 0;
230 for (const auto & triangle : this->_triangulation)
231 {
232 Point v01 = this->point(triangle[1]) -
233 this->point(triangle[0]);
234 Point v02 = this->point(triangle[2]) -
235 this->point(triangle[0]);
236 double_area += std::sqrt(cross_norm_sq(v01, v02));
237 }
238
239 return double_area/2;
240}
241
242
243
244Point C0Polygon::true_centroid () const
245{
246 // This specialization is good for Lagrange mappings only
247 if (this->mapping_type() != LAGRANGE_MAP)
248 return this->Elem::true_centroid();
249
250 // We use a triangulation to calculate here; assert that it's as
251 // consistent as possible.
252 libmesh_assert_equal_to (this->_triangulation.size(),
253 this->n_nodes() - 2);
254
255 Real double_area = 0;
256 Point double_area_weighted_centroid;
257 for (const auto & triangle : this->_triangulation)
258 {
259 Point v01 = this->point(triangle[1]) -
260 this->point(triangle[0]);
261 Point v02 = this->point(triangle[2]) -
262 this->point(triangle[0]);
263
264 const Real double_tri_area = std::sqrt(cross_norm_sq(v01, v02));
265 const Point tri_centroid = (this->point(triangle[0]) +
266 this->point(triangle[1]) +
267 this->point(triangle[2]))/3;
268
269 double_area += double_tri_area;
270
271 double_area_weighted_centroid += double_tri_area * tri_centroid;
272 }
273
274 return double_area_weighted_centroid / double_area;
275}
276
277
278
279std::pair<unsigned short int, unsigned short int>
280C0Polygon::second_order_child_vertex (const unsigned int /*n*/) const
281{
282 libmesh_not_implemented();
283 return std::pair<unsigned short int, unsigned short int> (0, 0);
284}
285
286
287void C0Polygon::permute(unsigned int perm_num)
288{
289 const auto ns = this->n_sides();
290 libmesh_assert_less (perm_num, ns);
291
292 // This is mostly for unit testing so I'll just make it O(N).
293 for (unsigned int p : make_range(perm_num))
294 {
296
297 Node * tempnode = this->node_ptr(0);
298 Elem * tempneigh = this->neighbor_ptr(0);
299 for (unsigned int s : make_range(ns-1))
300 {
301 this->set_node(s, this->node_ptr((s+1)%ns));
302 this->set_neighbor(s, this->neighbor_ptr(s+1));
303 }
304 this->set_node(ns-1, tempnode);
305 this->set_neighbor(ns-1, tempneigh);
306
307 // Keep the same triangulation, just with permuted node order,
308 // so we can really expect this to act like the *same* polygon
309 for (auto & triangle : this->_triangulation)
310 for (auto i : make_range(3))
311 triangle[i] = (triangle[i]+1)%ns;
312 }
313}
314
315
316void C0Polygon::flip(BoundaryInfo * boundary_info)
317{
318 libmesh_assert(boundary_info);
319
320 const auto ns = this->n_sides();
321
322 // Reorder nodes in such a way as to keep side ns-1 the same
323 for (auto s : make_range(0u, ns/2))
324 {
325 swap2nodes(s, ns-1-s);
326 swap2neighbors(s, ns-2-s);
327 swap2boundarysides(s, ns-2-s, boundary_info);
328 swap2boundaryedges(s, ns-2-s, boundary_info);
329 }
330}
331
332
333
334ElemType C0Polygon::side_type (const unsigned int libmesh_dbg_var(s)) const
335{
336 libmesh_assert_less (s, this->n_sides());
337 return EDGE2;
338}
339
340
341Point
342C0Polygon::side_vertex_average_normal(const unsigned int s) const
343{
344 const auto n_sides = this->n_sides();
345 libmesh_assert_less (s, n_sides);
346 libmesh_assert_equal_to(this->mapping_type(), LAGRANGE_MAP);
347 const Point side_t = this->point((s+1) % n_sides) -
348 this->point(s);
349 Point plane_normal(0, 0, 1);
350 // Find out what plane we're in, if we're in 3D
351 // Note we don't support non-planar C0-polygon at this time
352#if LIBMESH_DIM > 2
353 plane_normal(2) = 0;
354 const auto vavg = this->vertex_average();
355 for (auto i : make_range(n_sides))
356 {
357 const Point vi = this->point(i) - vavg;
358 const Point viplus = this->point((i+1)%n_sides) - vavg;
359 plane_normal += vi.cross(viplus);
360 // Since we know the polygon is planar
361 if (plane_normal.norm_sq() > TOLERANCE)
362 break;
363 }
364 plane_normal = plane_normal.unit();
365#endif
366 const Point v = side_t.cross(plane_normal);
367 return v.unit();
368}
369
370
371void C0Polygon::retriangulate()
372{
373 this->_triangulation.clear();
374
375 // Start with the full polygon
376 std::vector<int> remaining_nodes(this->n_nodes());
377 std::iota(remaining_nodes.begin(), remaining_nodes.end(), 0);
378
379 const auto ns = this->n_sides();
380
381 const Point vavg = this->vertex_average();
382
383 // Find out what plane we're in, if we're in 3D
384#if LIBMESH_DIM > 2
385 Point plane_normal;
386 for (auto i : make_range(ns))
387 {
388 const Point vi = this->point(i) - vavg;
389 const Point viplus = this->point((i+1)%ns) - vavg;
390 plane_normal += vi.cross(viplus);
391 }
392 plane_normal = plane_normal.unit();
393#endif
394
395 // Greedy heuristic: find the node with the most acutely convex
396 // angle and strip it out as a triangle.
397 while (remaining_nodes.size() > 2)
398 {
399 Real min_cos_angle = 1;
400 int best_vertex = -1;
401 for (auto n : index_range(remaining_nodes))
402 {
403 const Point & pn = this->point(remaining_nodes[n]);
404 const Point & pnext = this->point(remaining_nodes[(n+1)%ns]);
405 const Point & pprev = this->point(remaining_nodes[(n+ns-1)%ns]);
406 const Point vprev = (pn - pprev).unit();
407 const Point vnext = (pnext - pn).unit();
408
409 const Real sign_check = (vprev.cross(vnext)) * plane_normal;
410 if (sign_check <= 0)
411 continue;
412
413 const Real cos_angle = vprev * vnext;
414 if (cos_angle < min_cos_angle)
415 {
416 min_cos_angle = cos_angle;
417 best_vertex = n;
418 }
419 }
420
421 libmesh_assert(best_vertex >= 0);
422
423 this->_triangulation.push_back({remaining_nodes[(best_vertex+ns-1)%ns],
424 remaining_nodes[best_vertex],
425 remaining_nodes[(best_vertex+1)%ns]});
426 remaining_nodes.erase(remaining_nodes.begin()+best_vertex);
427 }
428}
429
430
431
432} // namespace libMesh
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
C0Polygon(const unsigned int num_sides, Elem *p=nullptr)
Constructor.
This is the base class from which all geometric element types are derived.
Definition elem.h:96
A Node is like a Point, but with more information.
Definition node.h:55
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
The Polygon is an element in 2D with an arbitrary (but fixed) number of sides.
std::vector< std::array< int, 3 > > _triangulation
Data for a triangulation of the polygon.
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
auto norm_sq() const
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
TypeVector< T > unit() const
The libMesh namespace provides an interface to certain functionality in the library.
auto l1_norm(const NumericVector< T > &vec)
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
IOPackage
libMesh interfaces with several different software packages for the purposes of creating,...
ElemType
Defines an enum for geometric element types.
void libmesh_ignore(const Args &...)
libmesh_assert(ctx)
const Real pi
.
Definition libmesh.h:292
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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
T cross_norm_sq(const TypeVector< T > &b, const TypeVector< T > &c)
Compute |b x c|^2 without creating the extra temporary produced by calling b.cross(c)....
const dof_id_type n_nodes
Definition tecplot_io.C:67