PolyFEM
Loading...
Searching...
No Matches
LagrangeBasis3d.cpp
Go to the documentation of this file.
1
3#include "LagrangeBasis3d.hpp"
4
10
12
17
19
20#include <cassert>
21#include <array>
23
24using namespace polyfem;
25using namespace polyfem::assembler;
26using namespace polyfem::basis;
27using namespace polyfem::mesh;
28using namespace polyfem::quadrature;
29
30/*
31Axes:
32 z y
33 │╱
34 o──x
35Boundaries:
36X axis: left/right
37Y axis: front/back
38Z axis: bottom/top
39Corner nodes:
40v3──x──v2
41 │⋱ ╱ ╲
42 x ⋱╱ ╲
43 │ x x x
44 │ ╱ ⋱ ╲
45 │╱ ⋱╲
46v0─────x──────v1
47v0 = (0, 0, 0)
48v1 = (1, 0, 0)
49v2 = (0, 1, 0)
50v3 = (0, 0, 1)
51Edge nodes:
52e0 = (0.5, 0, 0)
53e1 = (0.5, 0.5, 0)
54e2 = ( 0, 0.5, 0)
55e3 = ( 0, 0, 0.5)
56e4 = (0.5, 0, 0.5)
57e5 = ( 0, 0.5, 0.5)
58Corner nodes:
59 v7──────x─────v6
60 ╱┆ ╱ ╱│
61 ╱ ┆ ╱ ╱ │
62 x┄┄┼┄┄┄x┄┄┄┄┄┄x │
63 ╱ x ╱ ╱┆ x
64 ╱ ┆ ╱ ╱ ┆ ╱│
65v4─────┼x─────v5 ┆╱ │
66 │ ┆┆ │ x │
67 │ v3┼┄┄┄┄┄x┼┄⌿┼┄v2
68 │ ╱ ┆ │╱ ┆ ╱
69 x┄┄┄⌿┄┄x┄┄┄┄┄┄x ┆╱
70 │ x ┆ │ x
71 │ ╱ ┆ │ ╱
72 │╱ ┆ │╱
73v0──────x─────v1
74v0 = (0, 0, 0)
75v1 = (1, 0, 0)
76v2 = (1, 1, 0)
77v3 = (0, 1, 0)
78v4 = (0, 0, 1)
79v5 = (1, 0, 1)
80v6 = (1, 1, 1)
81v7 = (0, 1, 1)
82Edge nodes:
83 x─────e10─────x
84 ╱┆ ╱ ╱│
85 ╱ ┆ ╱ ╱ │
86 e11┄┼┄┄┄x┄┄┄┄┄e9 │
87 ╱ e7 ╱ ╱┆ e6
88 ╱ ┆ ╱ ╱ ┆ ╱│
89 x─────e8──────x ┆╱ │
90 │ ┆┆ │ x │
91 │ x┼┄┄┄┄┄e2┄⌿┼┄┄x
92 │ ╱ ┆ │╱ ┆ ╱
93e4┄┄┄⌿┄┄x┄┄┄┄┄e5 ┆╱
94 │ e3 ┆ │ e1
95 │ ╱ ┆ │ ╱
96 │╱ ┆ │╱
97 x─────e0──────x
98e0 = (0.5, 0, 0)
99e1 = ( 1, 0.5, 0)
100e2 = (0.5, 1, 0)
101e3 = ( 0, 0.5, 0)
102e4 = ( 0, 0, 0.5)
103e5 = ( 1, 0, 0.5)
104e6 = ( 1, 1, 0.5)
105e7 = ( 0, 1, 0.5)
106e8 = (0.5, 0, 1)
107e9 = ( 1, 0.5, 1)
108e10 = (0.5, 1, 1)
109e11 = ( 0, 0.5, 1)
110Face nodes:
111 v7──────x─────v6
112 ╱┆ ╱ ╱│
113 ╱ ┆ ╱ ╱ │
114 x┄┄┼┄┄f5┄┄┄┄┄┄x │
115 ╱ x ╱ f3 ╱┆ x
116 ╱ ┆ ╱ ╱ ┆ ╱│
117v4─────┼x─────v5 ┆╱ │
118 │ f0 ┆┆ │ f1 │
119 │ v3┼┄┄┄┄┄x┼┄⌿┼┄v2
120 │ ╱ ┆ │╱ ┆ ╱
121 x┄┄┄⌿┄f2┄┄┄┄┄┄x ┆╱
122 │ x ┆ f4 │ x
123 │ ╱ ┆ │ ╱
124 │╱ ┆ │╱
125v0──────x─────v1
126f0 = ( 0, 0.5, 0.5)
127f1 = ( 1, 0.5, 0.5)
128f2 = (0.5, 0, 0.5)
129f3 = (0.5, 1, 0.5)
130f4 = (0.5, 0.5, 0)
131f5 = (0.5, 0.5, 1)
132*/
133
134namespace
135{
136 template <class InputIterator, class T>
137 int find_index(InputIterator first, InputIterator last, const T &val)
138 {
139 return std::distance(first, std::find(first, last, val));
140 }
141
142 Navigation3D::Index find_quad_face(const Mesh3D &mesh, int c, int v1, int v2, int v3, int v4)
143 {
144 std::array<int, 4> v = {{v1, v2, v3, v4}};
145 std::sort(v.begin(), v.end());
146 for (int lf = 0; lf < mesh.n_cell_faces(c); ++lf)
147 {
148 auto idx = mesh.get_index_from_element(c, lf, 0);
149 if (mesh.n_face_vertices(idx.face) != 4)
150 continue;
151
152 std::array<int, 4> u;
153 for (int lv = 0; lv < mesh.n_face_vertices(idx.face); ++lv)
154 {
155 u[lv] = idx.vertex;
156 idx = mesh.next_around_face(idx);
157 }
158 std::sort(u.begin(), u.end());
159 if (u == v)
160 {
161 return idx;
162 }
163 }
164 assert(false);
165 return Navigation3D::Index();
166 }
167
168 std::array<int, 4> tet_vertices_local_to_global(const Mesh3D &mesh, int c)
169 {
170 // Vertex nodes
171 assert(mesh.is_simplex(c));
172 std::array<int, 4> l2g;
173 int lv = 0;
174 for (int vi : mesh.get_ordered_vertices_from_tet(c))
175 {
176 l2g[lv++] = vi;
177 }
178
179 return l2g;
180 }
181
182 std::array<int, 8> hex_vertices_local_to_global(const Mesh3D &mesh, int c)
183 {
184 assert(mesh.is_cube(c));
185
186 // Vertex nodes
187 std::array<int, 8> l2g;
188 int lv = 0;
189 for (int vi : mesh.get_ordered_vertices_from_hex(c))
190 {
191 l2g[lv++] = vi;
192 }
193
194 return l2g;
195 }
196
197 std::array<int, 6> prism_vertices_local_to_global(const Mesh3D &mesh, int c)
198 {
199 assert(mesh.is_prism(c));
200
201 // Vertex nodes
202 std::array<int, 6> l2g;
203 int lv = 0;
204 for (int vi : mesh.get_ordered_vertices_from_prism(c))
205 {
206 l2g[lv++] = vi;
207 }
208
209 return l2g;
210 }
211
212 std::array<int, 5> pyramid_vertices_local_to_global(const Mesh3D &mesh, int c)
213 {
214 assert(mesh.is_pyramid(c));
215
216 // Vertex nodes
217 std::array<int, 5> l2g;
218 int lv = 0;
219 for (int vi : mesh.get_ordered_vertices_from_pyramid(c))
220 {
221 l2g[lv++] = vi;
222 }
223
224 return l2g;
225 }
226
227 int lowest_order_elem_on_edge(const polyfem::mesh::NCMesh3D &mesh, const Eigen::VectorXi &discr_orders, const int eid)
228 {
229 auto elem_list = mesh.edge_neighs(eid);
230 int min = std::numeric_limits<int>::max();
231 int elem = -1;
232 for (const auto e : elem_list)
233 if (discr_orders[e] < min)
234 elem = e;
235 return elem;
236 }
237
238 void tet_local_to_global(const bool is_geom_bases, const int p, const Mesh3D &mesh, int c, const Eigen::VectorXi &discr_order, const Eigen::VectorXi &edge_orders, const Eigen::VectorXi &face_orders, std::vector<int> &res, polyfem::mesh::MeshNodes &nodes, std::vector<std::vector<int>> &edge_virtual_nodes, std::vector<std::vector<int>> &face_virtual_nodes)
239 {
240 const int n_edge_nodes = p > 1 ? ((p - 1) * 6) : 0;
241 const int nn = p > 2 ? (p - 2) : 0;
242 const int n_loc_f = (nn * (nn + 1) / 2);
243 const int n_face_nodes = n_loc_f * 4;
244 int n_cell_nodes = 0;
245 for (int pp = 4; pp <= p; ++pp)
246 n_cell_nodes += ((pp - 3) * ((pp - 3) + 1) / 2);
247
248 if (p == 0)
249 {
250 res.push_back(nodes.node_id_from_cell(c));
251 return;
252 }
253
254 // std::vector<int> res;
255 res.reserve(4 + n_edge_nodes + n_face_nodes + n_cell_nodes);
256
257 // Edge nodes
258 Eigen::Matrix<Navigation3D::Index, 4, 1> f;
259
260 auto v = tet_vertices_local_to_global(mesh, c);
261 Eigen::Matrix<int, 4, 3> fv;
262 fv.row(0) << v[0], v[1], v[2];
263 fv.row(1) << v[0], v[1], v[3];
264 fv.row(2) << v[1], v[2], v[3];
265 fv.row(3) << v[2], v[0], v[3];
266
267 for (long lf = 0; lf < fv.rows(); ++lf)
268 {
269 const auto index = mesh.get_index_from_element_face(c, fv(lf, 0), fv(lf, 1), fv(lf, 2));
270 f[lf] = index;
271 }
272
273 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
274 Eigen::Matrix<int, 6, 2> ev;
275 ev.row(0) << v[0], v[1];
276 ev.row(1) << v[1], v[2];
277 ev.row(2) << v[2], v[0];
278
279 ev.row(3) << v[0], v[3];
280 ev.row(4) << v[1], v[3];
281 ev.row(5) << v[2], v[3];
282
283 for (int le = 0; le < e.rows(); ++le)
284 {
285 // const auto index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
286 const auto index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
287 e[le] = index;
288 }
289
290 // vertices
291 for (size_t lv = 0; lv < v.size(); ++lv)
292 {
293 if (!mesh.is_conforming() && !is_geom_bases)
294 {
295 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
296 // hanging vertex
297 if (ncmesh.leader_edge_of_vertex(v[lv]) >= 0 || ncmesh.leader_face_of_vertex(v[lv]) >= 0)
298 res.push_back(-lv - 1);
299 else
300 res.push_back(nodes.node_id_from_primitive(v[lv]));
301 }
302 else
303 res.push_back(nodes.node_id_from_primitive(v[lv]));
304 }
305
306 // Edges
307 for (int le = 0; le < e.rows(); ++le)
308 {
309 const auto index = e[le];
310 auto neighs = mesh.edge_neighs(index.edge);
311 int min_p = discr_order.size() > 0 ? discr_order(c) : 0;
312
313 if (is_geom_bases)
314 {
315 auto node_ids = nodes.node_ids_from_edge(index, p - 1);
316 res.insert(res.end(), node_ids.begin(), node_ids.end());
317 }
318 else
319 {
320 if (!mesh.is_conforming() && !is_geom_bases)
321 {
322 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
323 // slave edge
324 if (ncmesh.leader_edge_of_edge(index.edge) >= 0 || ncmesh.leader_face_of_edge(index.edge) >= 0)
325 {
326 for (int tmp = 0; tmp < p - 1; ++tmp)
327 res.push_back(-le - 1);
328 }
329 // master or conforming edge with constrained order
330 else if (edge_orders[index.edge] < discr_order(c))
331 {
332 for (int tmp = 0; tmp < p - 1; ++tmp)
333 res.push_back(-le - 1);
334
335 int min_order_elem = lowest_order_elem_on_edge(ncmesh, discr_order, index.edge);
336 // master edge, add extra nodes
337 if (min_order_elem == c)
338 edge_virtual_nodes[index.edge] = nodes.node_ids_from_edge(index, edge_orders[index.edge] - 1);
339 }
340 else
341 {
342 auto node_ids = nodes.node_ids_from_edge(index, p - 1);
343 res.insert(res.end(), node_ids.begin(), node_ids.end());
344 }
345 }
346 else
347 {
348 for (auto cid : neighs)
349 {
350 min_p = std::min(min_p, discr_order.size() > 0 ? discr_order(cid) : 0);
351 }
352
353 if (discr_order.size() > 0 && discr_order(c) > min_p)
354 {
355 for (int tmp = 0; tmp < p - 1; ++tmp)
356 res.push_back(-le - 10);
357 }
358 else
359 {
360 auto node_ids = nodes.node_ids_from_edge(index, p - 1);
361 res.insert(res.end(), node_ids.begin(), node_ids.end());
362 }
363 }
364 }
365 }
366
367 // faces
368 for (int lf = 0; lf < f.rows(); ++lf)
369 {
370 const auto index = f[lf];
371 const auto other_cell = mesh.switch_element(index).element;
372
373 const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
374
375 if (is_geom_bases)
376 {
377 auto node_ids = nodes.node_ids_from_face(index, p - 2);
378 res.insert(res.end(), node_ids.begin(), node_ids.end());
379 }
380 else
381 {
382 if (!mesh.is_conforming() && !is_geom_bases)
383 {
384 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
385 // slave face
386 if (ncmesh.leader_face_of_face(index.face) >= 0)
387 {
388 for (int tmp = 0; tmp < n_loc_f; ++tmp)
389 res.push_back(-lf - 1);
390 }
391 // master face or conforming face with constrained order
392 else if (face_orders[index.face] < discr_order[c])
393 {
394 for (int tmp = 0; tmp < n_loc_f; ++tmp)
395 res.push_back(-lf - 1);
396 // master face
397 if (ncmesh.n_follower_faces(index.face) > 0 && face_orders[index.face] > 2)
398 face_virtual_nodes[index.face] = nodes.node_ids_from_face(index, face_orders[index.face] - 2);
399 }
400 else
401 {
402 auto node_ids = nodes.node_ids_from_face(index, p - 2);
403 res.insert(res.end(), node_ids.begin(), node_ids.end());
404 }
405 }
406 else
407 {
408 if (skip_other)
409 {
410 for (int tmp = 0; tmp < n_loc_f; ++tmp)
411 res.push_back(-lf - 1);
412 }
413 else
414 {
415 auto node_ids = nodes.node_ids_from_face(index, p - 2);
416 res.insert(res.end(), node_ids.begin(), node_ids.end());
417 }
418 }
419 }
420 }
421
422 // cells
423 if (n_cell_nodes > 0)
424 {
425 const auto index = f[0];
426
427 auto node_ids = nodes.node_ids_from_cell(index, p - 3);
428 res.insert(res.end(), node_ids.begin(), node_ids.end());
429 }
430
431 assert(res.size() == size_t(4 + n_edge_nodes + n_face_nodes + n_cell_nodes));
432 }
433
434 void hex_local_to_global(const bool serendipity, const int q, const Mesh3D &mesh, int c, const Eigen::VectorXi &discr_order, std::vector<int> &res, MeshNodes &nodes)
435 {
436 assert(mesh.is_cube(c));
437
438 const int n_edge_nodes = ((q - 1) * 12);
439 const int nn = (q - 1);
440 const int n_loc_f = serendipity ? 0 : (nn * nn);
441 const int n_face_nodes = serendipity ? 0 : (n_loc_f * 6);
442 const int n_cell_nodes = serendipity ? 0 : (nn * nn * nn);
443
444 if (q == 0)
445 {
446 res.push_back(nodes.node_id_from_cell(c));
447 return;
448 }
449
450 // std::vector<int> res;
451 res.reserve(8 + n_edge_nodes + n_face_nodes + n_cell_nodes);
452
453 // Vertex nodes
454 auto v = hex_vertices_local_to_global(mesh, c);
455
456 // Edge nodes
457 Eigen::Matrix<Navigation3D::Index, 12, 1> e;
458 Eigen::Matrix<int, 12, 2> ev;
459 ev.row(0) << v[0], v[1];
460 ev.row(1) << v[1], v[2];
461 ev.row(2) << v[2], v[3];
462 ev.row(3) << v[3], v[0];
463 ev.row(4) << v[0], v[4];
464 ev.row(5) << v[1], v[5];
465 ev.row(6) << v[2], v[6];
466 ev.row(7) << v[3], v[7];
467 ev.row(8) << v[4], v[5];
468 ev.row(9) << v[5], v[6];
469 ev.row(10) << v[6], v[7];
470 ev.row(11) << v[7], v[4];
471 for (int le = 0; le < e.rows(); ++le)
472 {
473 // e[le] = find_edge(mesh, c, ev(le, 0), ev(le, 1)).edge;
474 e[le] = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
475 }
476
477 // Face nodes
478 Eigen::Matrix<Navigation3D::Index, 6, 1> f;
479 Eigen::Matrix<int, 6, 4> fv;
480 fv.row(0) << v[0], v[3], v[4], v[7];
481 fv.row(1) << v[1], v[2], v[5], v[6];
482 fv.row(2) << v[0], v[1], v[5], v[4];
483 fv.row(3) << v[3], v[2], v[6], v[7];
484 fv.row(4) << v[0], v[1], v[2], v[3];
485 fv.row(5) << v[4], v[5], v[6], v[7];
486 for (int lf = 0; lf < f.rows(); ++lf)
487 {
488 const auto index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
489 f[lf] = index;
490 }
491
492 // vertices
493 for (size_t lv = 0; lv < v.size(); ++lv)
494 {
495 res.push_back(nodes.node_id_from_primitive(v[lv]));
496 }
497 assert(res.size() == size_t(8));
498
499 // Edges
500 for (int le = 0; le < e.rows(); ++le)
501 {
502 const auto index = e[le];
503 auto neighs = mesh.edge_neighs(index.edge);
504 int min_q = discr_order.size() > 0 ? discr_order(c) : 0;
505
506 for (auto cid : neighs)
507 {
508 min_q = std::min(min_q, discr_order.size() > 0 ? discr_order(cid) : 0);
509 }
510
511 if (discr_order.size() > 0 && discr_order(c) > min_q)
512 {
513 for (int tmp = 0; tmp < q - 1; ++tmp)
514 res.push_back(-le - 10);
515 }
516 else
517 {
518 auto node_ids = nodes.node_ids_from_edge(index, q - 1);
519 res.insert(res.end(), node_ids.begin(), node_ids.end());
520 }
521 }
522 assert(res.size() == size_t(8 + n_edge_nodes));
523
524 // faces
525 for (int lf = 0; lf < f.rows(); ++lf)
526 {
527 const auto index = f[lf];
528 const auto other_cell = mesh.switch_element(index).element;
529
530 const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
531
532 if (skip_other)
533 {
534 for (int tmp = 0; tmp < n_loc_f; ++tmp)
535 res.push_back(-lf - 1);
536 }
537 else
538 {
539 auto node_ids = nodes.node_ids_from_face(index, serendipity ? 0 : (q - 1));
540 assert(node_ids.size() == n_loc_f);
541 res.insert(res.end(), node_ids.begin(), node_ids.end());
542 }
543 }
544 assert(res.size() == size_t(8 + n_edge_nodes + n_face_nodes));
545
546 // cells
547 if (n_cell_nodes > 0)
548 {
549 const auto index = f[0];
550
551 auto node_ids = nodes.node_ids_from_cell(index, q - 1);
552 res.insert(res.end(), node_ids.begin(), node_ids.end());
553 }
554
555 assert(res.size() == size_t(8 + n_edge_nodes + n_face_nodes + n_cell_nodes));
556 }
557
558 void prism_local_to_global(const int p, const int q, const Mesh3D &mesh, int c, const Eigen::VectorXi &discr_order, std::vector<int> &res, MeshNodes &nodes)
559 {
560 assert(mesh.is_prism(c));
561
562 const int n_edge_nodest = p > 1 ? ((p - 1) * 3) : 0;
563 const int nnt = p > 2 ? (p - 2) : 0;
564 const int n_face_nodest = nnt * (nnt + 1) / 2;
565
566 const int nnq = q > 1 ? (q - 1) : 0;
567 const int n_face_nodesq = nnq * n_edge_nodest / 3;
568
569 const int n_edge_nodes = n_edge_nodest * 2 + nnq * 3;
570 const int n_face_nodes = n_face_nodest * 2 + n_face_nodesq * 3;
571 const int n_cell_nodes = n_face_nodest * nnq;
572
573 if (p == 0 && q == 0)
574 {
575 res.push_back(nodes.node_id_from_cell(c));
576 return;
577 }
578
579 res.reserve(6 + n_edge_nodes + n_face_nodes + n_cell_nodes);
580
581 // Vertex nodes
582 auto v = prism_vertices_local_to_global(mesh, c);
583
584 // Edge nodes
585 Eigen::Matrix<Navigation3D::Index, 9, 1> e;
586 Eigen::Matrix<int, 9, 2> ev;
587 ev.row(0) << v[0], v[1];
588 ev.row(1) << v[1], v[2];
589 ev.row(2) << v[2], v[0];
590 ev.row(3) << v[3], v[4];
591 ev.row(4) << v[4], v[5];
592 ev.row(5) << v[5], v[3];
593 ev.row(6) << v[0], v[3];
594 ev.row(7) << v[1], v[4];
595 ev.row(8) << v[2], v[5];
596
597 for (int le = 0; le < e.rows(); ++le)
598 {
599 e[le] = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
600 }
601
602 // Face nodes
603 Eigen::Matrix<Navigation3D::Index, 5, 1> f;
604 Eigen::Matrix<int, 2, 3> fvt;
605 fvt.row(0) << v[0], v[1], v[2];
606 fvt.row(1) << v[3], v[4], v[5];
607 for (int lf = 0; lf < fvt.rows(); ++lf)
608 {
609 const auto index = mesh.get_index_from_element_face(c, fvt(lf, 0), fvt(lf, 1), fvt(lf, 2));
610 f[lf] = index;
611 }
612
613 Eigen::Matrix<int, 3, 4> fvq;
614 fvq.row(0) << v[0], v[1], v[4], v[3];
615 fvq.row(1) << v[1], v[2], v[5], v[4];
616 fvq.row(2) << v[2], v[0], v[3], v[5];
617 for (int lf = 0; lf < fvq.rows(); ++lf)
618 {
619 const auto index = find_quad_face(mesh, c, fvq(lf, 0), fvq(lf, 1), fvq(lf, 2), fvq(lf, 3));
620 f[lf + 2] = index;
621 }
622
623 // vertices
624 for (size_t lv = 0; lv < v.size(); ++lv)
625 {
626 res.push_back(nodes.node_id_from_primitive(v[lv]));
627 }
628 assert(res.size() == size_t(6));
629
630 // Edges
631 for (int le = 0; le < e.rows(); ++le)
632 {
633 const auto index = e[le];
634 auto neighs = mesh.edge_neighs(index.edge);
635 // int min_q = discr_order.size() > 0 ? discr_order(c) : 0;
636
637 // TODO prism: not conforming
638 // for (auto cid : neighs)
639 // {
640 // min_q = std::min(min_q, discr_order.size() > 0 ? discr_order(cid) : 0);
641 // }
642
643 // if (discr_order.size() > 0 && discr_order(c) > min_q)
644 // {
645 // for (int tmp = 0; tmp < q - 1; ++tmp)
646 // res.push_back(-le - 10);
647 // }
648 // else
649 {
650 auto node_ids = nodes.node_ids_from_edge(index, (le < 6 ? p : q) - 1);
651 res.insert(res.end(), node_ids.begin(), node_ids.end());
652 }
653 }
654 assert(res.size() == size_t(6 + n_edge_nodes));
655
656 // faces
657 for (int lf = 0; lf < f.rows(); ++lf)
658 {
659 const auto index = f[lf];
660 const auto other_cell = mesh.switch_element(index).element;
661
662 // TODO prism: not conforming
663 // const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
664
665 // if (skip_other)
666 // {
667 // for (int tmp = 0; tmp < n_loc_f; ++tmp)
668 // res.push_back(-lf - 1);
669 // }
670 // else
671 {
672 // todo prism, nodes are not necessarly a square
673 auto node_ids = nodes.node_ids_from_face(index, lf < 2 ? (p - 2) : ((q - 1) * (p - 1)));
674 // assert(node_ids.size() == n_loc_f);
675 res.insert(res.end(), node_ids.begin(), node_ids.end());
676 }
677 }
678 assert(res.size() == size_t(6 + n_edge_nodes + n_face_nodes));
679
680 // cells
681 if (n_cell_nodes > 0)
682 {
683 // TODO: implement me internal prism nodes
684 log_and_throw_error("Prism internal nodes not implemented yet");
685
686 const auto index = f[0];
687
688 auto node_ids = nodes.node_ids_from_cell(index, q - 1);
689 res.insert(res.end(), node_ids.begin(), node_ids.end());
690 }
691
692 assert(res.size() == size_t(6 + n_edge_nodes + n_face_nodes + n_cell_nodes));
693 }
694
695 void pyramid_local_to_global(const int p, const Mesh3D &mesh, int c, const Eigen::VectorXi &discr_order, std::vector<int> &res, MeshNodes &nodes)
696 {
697 assert(mesh.is_pyramid(c));
698
699 if (p == 0)
700 {
701 res.push_back(nodes.node_id_from_cell(c));
702 return;
703 }
704
705 // 8 edges × (p-1) interior nodes each
706 const int n_edge_nodes = 8 * (p - 1);
707 // 4 tri faces × (p-1)(p-2)/2 interior nodes each
708 const int n_tri_face_nodes = 4 * (p - 1) * (p - 2) / 2;
709 // 1 quad base face × (p-1)^2 interior nodes
710 const int n_quad_face_nodes = (p - 1) * (p - 1);
711 const int n_face_nodes = n_tri_face_nodes + n_quad_face_nodes;
712 // total pyramid space dim = (p+1)(p+2)(2p+3)/6
713 const int total = (p + 1) * (p + 2) * (2 * p + 3) / 6;
714 const int n_cell_nodes = (p - 1) * (p - 2) * (2 * p - 3) / 6;
715
716 assert(total == 5 + n_edge_nodes + n_face_nodes + n_cell_nodes);
717
718 res.reserve(5 + n_edge_nodes + n_face_nodes + n_cell_nodes);
719
720 // Vertex nodes
721 auto v = pyramid_vertices_local_to_global(mesh, c);
722
723 // Edge nodes
724 Eigen::Matrix<Navigation3D::Index, 8, 1> e;
725 Eigen::Matrix<int, 8, 2> ev;
726 ev.row(0) << v[0], v[1];
727 ev.row(1) << v[1], v[2];
728 ev.row(2) << v[2], v[3];
729 ev.row(3) << v[3], v[0];
730 ev.row(4) << v[0], v[4];
731 ev.row(5) << v[1], v[4];
732 ev.row(6) << v[2], v[4];
733 ev.row(7) << v[3], v[4];
734
735 for (int le = 0; le < e.rows(); ++le)
736 {
737 e[le] = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
738 }
739
740 // Face nodes
741 Eigen::Matrix<Navigation3D::Index, 5, 1> f;
742 Eigen::Matrix<int, 4, 3> fvt; // side face
743 fvt.row(0) << v[0], v[1], v[4];
744 fvt.row(1) << v[1], v[2], v[4];
745 fvt.row(2) << v[2], v[3], v[4];
746 fvt.row(3) << v[3], v[0], v[4];
747 for (int lf = 0; lf < fvt.rows(); ++lf)
748 {
749 const auto index = mesh.get_index_from_element_face(c, fvt(lf, 0), fvt(lf, 1), fvt(lf, 2));
750 f[lf] = index;
751 }
752
753 // Eigen::Matrix<int, 1, 4> fvq; // base face
754 // fvq.row(0) << v[0], v[1], v[2], v[3];
755
756 // for (int lf = 0; lf < fvq.rows(); ++lf)
757 // {
758 // const auto index = find_quad_face(mesh, c, fvq(lf, 0), fvq(lf, 1), fvq(lf, 2), fvq(lf, 3));
759 // f[lf + 4] = index;
760 // }
761
762 // base quad face — pin index.vertex to v[0] so face_node starts at reference corner (0,0,0)
763 {
764 auto base_idx = find_quad_face(mesh, c, v[0], v[1], v[2], v[3]);
765 for (int rv = 0; rv < 4; ++rv)
766 {
767 if (base_idx.vertex == v[0])
768 break;
769 base_idx = mesh.next_around_face(base_idx);
770 }
771 assert(base_idx.vertex == v[0]);
772 f[4] = base_idx;
773 }
774
775 // vertices
776 for (size_t lv = 0; lv < v.size(); ++lv)
777 {
778 res.push_back(nodes.node_id_from_primitive(v[lv]));
779 }
780 assert(res.size() == size_t(5));
781
782 // Edges
783 for (int le = 0; le < e.rows(); ++le)
784 {
785 const auto index = e[le];
786 auto node_ids = nodes.node_ids_from_edge(index, p - 1);
787 res.insert(res.end(), node_ids.begin(), node_ids.end());
788 }
789 assert(res.size() == size_t(5 + n_edge_nodes));
790
791 // faces
792 for (int lf = 0; lf < f.rows(); ++lf)
793 {
794 const auto index = f[lf];
795
796 int n_loc_face = (lf < 4) ? (p - 2) : (p - 1);
797 auto node_ids = nodes.node_ids_from_face(index, n_loc_face);
798 res.insert(res.end(), node_ids.begin(), node_ids.end());
799 }
800 assert(res.size() == size_t(5 + n_edge_nodes + n_face_nodes));
801
802 // cells
803 if (n_cell_nodes > 0)
804 {
805 auto node_ids = nodes.node_ids_from_cell(f[0], p - 1);
806 res.insert(res.end(), node_ids.begin(), node_ids.end());
807 }
808 assert(res.size() == size_t(5 + n_edge_nodes + n_face_nodes + n_cell_nodes));
809 }
810 // -----------------------------------------------------------------------------
811
825 void compute_nodes(
826 const Mesh3D &mesh,
827 const Eigen::VectorXi &discr_ordersp,
828 const Eigen::VectorXi &discr_ordersq,
829 const Eigen::VectorXi &edge_orders,
830 const Eigen::VectorXi &face_orders,
831 const bool serendipity,
832 const bool has_polys,
833 const bool is_geom_bases,
834 MeshNodes &nodes,
835 std::vector<std::vector<int>> &edge_virtual_nodes,
836 std::vector<std::vector<int>> &face_virtual_nodes,
837 std::vector<std::vector<int>> &element_nodes_id,
838 std::vector<LocalBoundary> &local_boundary,
839 std::map<int, InterfaceData> &poly_face_to_data)
840 {
841 // Step 1: Assign global node ids for each quads
842 local_boundary.clear();
843 // local_boundary.resize(mesh.n_faces());
844 element_nodes_id.resize(mesh.n_faces());
845
846 if (!mesh.is_conforming())
847 {
848 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
849 edge_virtual_nodes.resize(ncmesh.n_edges());
850 face_virtual_nodes.resize(ncmesh.n_faces());
851 }
852
853 for (int c = 0; c < mesh.n_cells(); ++c)
854 {
855 const int discr_order = discr_ordersp(c);
856 const int discr_orderq = discr_ordersq(c);
857
858 if (mesh.is_cube(c))
859 {
860 hex_local_to_global(serendipity, discr_order, mesh, c, discr_ordersp, element_nodes_id[c], nodes);
861
862 auto v = hex_vertices_local_to_global(mesh, c);
863 Eigen::Matrix<int, 6, 4> fv;
864 fv.row(0) << v[0], v[3], v[4], v[7];
865 fv.row(1) << v[1], v[2], v[5], v[6];
866 fv.row(2) << v[0], v[1], v[5], v[4];
867 fv.row(3) << v[3], v[2], v[6], v[7];
868 fv.row(4) << v[0], v[1], v[2], v[3];
869 fv.row(5) << v[4], v[5], v[6], v[7];
870
871 LocalBoundary lb(c, BoundaryType::QUAD);
872 for (int i = 0; i < fv.rows(); ++i)
873 {
874 const int f = find_quad_face(mesh, c, fv(i, 0), fv(i, 1), fv(i, 2), fv(i, 3)).face;
875
876 if (mesh.is_boundary_face(f) || mesh.get_boundary_id(f) > 0)
877 {
878 lb.add_boundary_primitive(f, i);
879 }
880 }
881
882 if (!lb.empty())
883 local_boundary.emplace_back(lb);
884 }
885 else if (mesh.is_simplex(c))
886 {
887 // element_nodes_id[c] = polyfem::LagrangeBasis3d::tet_local_to_global(discr_order, mesh, c, discr_orders, nodes);
888 tet_local_to_global(is_geom_bases, discr_order, mesh, c, discr_ordersp, edge_orders, face_orders, element_nodes_id[c], nodes, edge_virtual_nodes, face_virtual_nodes);
889
890 auto v = tet_vertices_local_to_global(mesh, c);
891 Eigen::Matrix<int, 4, 3> fv;
892 fv.row(0) << v[0], v[1], v[2];
893 fv.row(1) << v[0], v[1], v[3];
894 fv.row(2) << v[1], v[2], v[3];
895 fv.row(3) << v[2], v[0], v[3];
896
897 LocalBoundary lb(c, BoundaryType::TRI);
898 for (long i = 0; i < fv.rows(); ++i)
899 {
900 const int f = mesh.get_index_from_element_face(c, fv(i, 0), fv(i, 1), fv(i, 2)).face;
901
902 if (mesh.is_boundary_face(f))
903 {
904 lb.add_boundary_primitive(f, i);
905 }
906 }
907
908 if (!lb.empty())
909 local_boundary.emplace_back(lb);
910 }
911 else if (mesh.is_prism(c))
912 {
913 // todo non conforming prisms
914 prism_local_to_global(discr_order, discr_orderq, mesh, c, discr_ordersp, element_nodes_id[c], nodes);
915
916 auto v = prism_vertices_local_to_global(mesh, c);
917 Eigen::Matrix<int, 2, 3> fvt;
918 fvt.row(0) << v[0], v[1], v[2];
919 fvt.row(1) << v[3], v[4], v[5];
920
921 LocalBoundary lb(c, BoundaryType::PRISM);
922 for (long i = 0; i < fvt.rows(); ++i)
923 {
924 const int f = mesh.get_index_from_element_face(c, fvt(i, 0), fvt(i, 1), fvt(i, 2)).face;
925
926 if (mesh.is_boundary_face(f))
927 {
928 lb.add_boundary_primitive(f, i);
929 }
930 }
931
932 Eigen::Matrix<int, 3, 4> fvq;
933 fvq.row(0) << v[0], v[1], v[4], v[3];
934 fvq.row(1) << v[1], v[2], v[5], v[4];
935 fvq.row(2) << v[2], v[0], v[3], v[5];
936
937 for (long i = 0; i < fvq.rows(); ++i)
938 {
939 const int f = find_quad_face(mesh, c, fvq(i, 0), fvq(i, 1), fvq(i, 2), fvq(i, 3)).face;
940
941 if (mesh.is_boundary_face(f))
942 {
943 lb.add_boundary_primitive(f, i + 2);
944 }
945 }
946
947 if (!lb.empty())
948 local_boundary.emplace_back(lb);
949 }
950
951 // todo non conforming prisms
952 else if (mesh.is_pyramid(c))
953 {
954 pyramid_local_to_global(discr_order, mesh, c, discr_ordersp, element_nodes_id[c], nodes);
955
956 auto v = pyramid_vertices_local_to_global(mesh, c);
957 Eigen::Matrix<int, 4, 3> fvt;
958 fvt.row(0) << v[0], v[1], v[4];
959 fvt.row(1) << v[1], v[2], v[4];
960 fvt.row(2) << v[2], v[3], v[4];
961 fvt.row(3) << v[3], v[0], v[4];
962
963 LocalBoundary lb(c, BoundaryType::PYRAMID);
964 for (long i = 0; i < fvt.rows(); ++i)
965 {
966 const int f = mesh.get_index_from_element_face(c, fvt(i, 0), fvt(i, 1), fvt(i, 2)).face;
967
968 if (mesh.is_boundary_face(f))
969 {
970 lb.add_boundary_primitive(f, i + 1);
971 }
972 }
973
974 Eigen::Matrix<int, 1, 4> fvq;
975 fvq.row(0) << v[0], v[1], v[2], v[3];
976
977 for (long i = 0; i < fvq.rows(); ++i)
978 {
979 const int f = find_quad_face(mesh, c, fvq(i, 0), fvq(i, 1), fvq(i, 2), fvq(i, 3)).face;
980
981 if (mesh.is_boundary_face(f))
982 {
983 lb.add_boundary_primitive(f, 0);
984 }
985 }
986
987 if (!lb.empty())
988 local_boundary.emplace_back(lb);
989 }
990 }
991
992 if (!has_polys)
993 return;
994
995 // Step 2: Iterate over edges of polygons and compute interface weights
996 Eigen::VectorXi indices;
997 for (int c = 0; c < mesh.n_cells(); ++c)
998 {
999 // Skip non-polytopes
1000 if (!mesh.is_polytope(c))
1001 {
1002 continue;
1003 }
1004
1005 for (int lf = 0; lf < mesh.n_cell_faces(c); ++lf)
1006 {
1007 auto index = mesh.get_index_from_element(c, lf, 0);
1008 auto index2 = mesh.switch_element(index);
1009 int c2 = index2.element;
1010 assert(c2 >= 0);
1011
1012 const int discr_order = discr_ordersp(c2);
1013 const int discr_orderq = discr_ordersq(c2);
1014 if (mesh.is_cube(c2))
1015 {
1016 indices = LagrangeBasis3d::hex_face_local_nodes(serendipity, discr_order, mesh, index2);
1017 }
1018 else if (mesh.is_simplex(c2))
1019 {
1020 indices = LagrangeBasis3d::tet_face_local_nodes(discr_order, mesh, index2);
1021 }
1022 else if (mesh.is_prism(c2))
1023 {
1024 indices = LagrangeBasis3d::prism_face_local_nodes(discr_order, discr_orderq, mesh, index2);
1025 }
1026 else if (mesh.is_pyramid(c2))
1027 {
1028 indices = LagrangeBasis3d::pyramid_face_local_nodes(discr_order, mesh, index2);
1029 }
1030 else
1031 continue;
1032
1033 InterfaceData data;
1034 data.local_indices.insert(data.local_indices.begin(), indices.data(), indices.data() + indices.size());
1035 assert(indices.size() == data.local_indices.size());
1036 poly_face_to_data[index2.face] = data;
1037 }
1038 }
1039 }
1046 void local_to_global(const Eigen::MatrixXd &verts, const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
1047 {
1048 const int dim = verts.cols();
1049 const int N = uv.rows();
1050 assert(dim == 3);
1051 assert(uv.cols() == dim);
1052 assert(verts.rows() == dim + 1);
1053
1054 pts.setZero(N, dim);
1055 for (int i = 0; i < N; i++)
1056 pts.row(i) = uv(i, 0) * verts.row(1) + uv(i, 1) * verts.row(2) + uv(i, 2) * verts.row(3) + (1.0 - uv(i, 0) - uv(i, 1) - uv(i, 2)) * verts.row(0);
1057 }
1058
1059 void local_to_global_face(const Eigen::MatrixXd &verts, const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
1060 {
1061 const int dim = verts.cols();
1062 const int N = uv.rows();
1063 assert(dim == 3);
1064 assert(uv.cols() == 2);
1065 assert(verts.rows() == 3);
1066
1067 pts.setZero(N, dim);
1068 for (int i = 0; i < N; i++)
1069 pts.row(i) = uv(i, 0) * verts.row(1) + uv(i, 1) * verts.row(2) + (1.0 - uv(i, 0) - uv(i, 1)) * verts.row(0);
1070 }
1071
1078 void global_to_local(const Eigen::MatrixXd &verts, const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
1079 {
1080 const int dim = verts.cols();
1081 const int N = pts.rows();
1082 assert(dim == 3);
1083 assert(verts.rows() == dim + 1);
1084 assert(pts.cols() == dim);
1085
1086 Eigen::Matrix3d J;
1087 for (int i = 0; i < dim; i++)
1088 J.col(i) = verts.row(i + 1) - verts.row(0);
1089
1090 Eigen::Matrix3d Jinv = J.inverse();
1091
1092 uv.setZero(N, dim);
1093 polyfem::utils::maybe_parallel_for(N, [&](int start, int end, int thread_id) {
1094 for (int i = start; i < end; i++)
1095 {
1096 auto point = pts.row(i) - verts.row(0);
1097 uv.row(i) = Jinv * point.transpose();
1098 }
1099 });
1100 }
1101
1102 void global_to_local_face(const Eigen::MatrixXd &verts, const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
1103 {
1104 const int dim = verts.cols();
1105 const int N = pts.rows();
1106 assert(dim == 3);
1107 assert(verts.rows() == 3);
1108 assert(pts.cols() == dim);
1109
1110 Eigen::Matrix3d J;
1111 for (int i = 0; i < 2; i++)
1112 J.col(i) = verts.row(i + 1) - verts.row(0);
1113
1114 Eigen::Vector3d a = J.col(0);
1115 Eigen::Vector3d b = J.col(1);
1116 Eigen::Vector3d virtual_vert = a.cross(b);
1117 J.col(2) = virtual_vert;
1118
1119 uv.setZero(N, 2);
1120 polyfem::utils::maybe_parallel_for(N, [&](int start, int end, int thread_id) {
1121 for (int i = start; i < end; i++)
1122 {
1123 auto point = pts.row(i) - verts.row(0);
1124 Eigen::Vector3d x = J.colPivHouseholderQr().solve(point.transpose());
1125 uv.row(i) = x.block(0, 0, 2, 1);
1126 assert(std::abs(x(2)) < 1e-8);
1127 }
1128 });
1129 }
1130
1131 void global_to_local_edge(const Eigen::MatrixXd &verts, const Eigen::MatrixXd &pts, Eigen::VectorXd &uv)
1132 {
1133 const int dim = verts.cols();
1134 const int N = pts.rows();
1135 assert(dim == 3);
1136 assert(verts.rows() == 2);
1137 assert(pts.cols() == dim);
1138
1139 auto edge = verts.row(1) - verts.row(0);
1140 double squared_length = edge.squaredNorm();
1141
1142 uv.setZero(N);
1143 polyfem::utils::maybe_parallel_for(N, [&](int start, int end, int thread_id) {
1144 for (int i = start; i < end; i++)
1145 {
1146 auto vec = pts.row(i) - verts.row(0);
1147 uv(i) = (vec.dot(edge)) / squared_length;
1148 }
1149 });
1150 }
1151
1152 bool check_edge_face_orders(const polyfem::mesh::NCMesh3D &mesh, const Eigen::VectorXi &elem_orders, const Eigen::VectorXi &edge_orders, const Eigen::VectorXi &face_orders)
1153 {
1154 // same order for overlapping faces
1155 for (int i = 0; i < mesh.n_faces(); i++)
1156 if (mesh.leader_face_of_face(i) >= 0)
1157 if (face_orders[mesh.leader_face_of_face(i)] != face_orders[i])
1158 return false;
1159
1160 // face order no smaller than order of its edges
1161 for (int i = 0; i < mesh.n_faces(); i++)
1162 {
1163 if (mesh.n_face_cells(i) == 0)
1164 continue;
1165 for (int j = 0; j < mesh.n_face_vertices(i); j++)
1166 {
1167 const int e_id = mesh.face_edge(i, j);
1168 if (edge_orders[e_id] > face_orders[i])
1169 return false;
1170 }
1171 }
1172
1173 // same order for overlapping edges
1174 for (int i = 0; i < mesh.n_edges(); i++)
1175 if (mesh.leader_edge_of_edge(i) >= 0)
1176 if (edge_orders[mesh.leader_edge_of_edge(i)] != edge_orders[i])
1177 return false;
1178
1179 // face order no larger than order of interior edges
1180 for (int i = 0; i < mesh.n_edges(); i++)
1181 {
1182 if (mesh.n_edge_cells(i) == 0)
1183 continue;
1184 if (mesh.leader_face_of_edge(i) >= 0 && mesh.leader_edge_of_edge(i) < 0)
1185 if (face_orders[mesh.leader_face_of_edge(i)] > edge_orders[i])
1186 return false;
1187 }
1188 return true;
1189 }
1190
1198 void compute_edge_face_orders(const polyfem::mesh::NCMesh3D &mesh, const Eigen::VectorXi &elem_orders, Eigen::VectorXi &edge_orders, Eigen::VectorXi &face_orders)
1199 {
1200 const int max_order = elem_orders.maxCoeff();
1201 edge_orders.setConstant(mesh.n_edges(), max_order);
1202 face_orders.setConstant(mesh.n_faces(), max_order);
1203
1204 for (int i = 0; i < mesh.n_cells(); i++)
1205 for (int j = 0; j < mesh.n_cell_faces(i); j++)
1206 face_orders[mesh.cell_face(i, j)] = std::min(face_orders[mesh.cell_face(i, j)], elem_orders[i]);
1207
1208 for (int i = 0; i < mesh.n_cells(); i++)
1209 for (int j = 0; j < mesh.n_cell_edges(i); j++)
1210 edge_orders[mesh.cell_edge(i, j)] = std::min(edge_orders[mesh.cell_edge(i, j)], elem_orders[i]);
1211
1212 while (!check_edge_face_orders(mesh, elem_orders, edge_orders, face_orders))
1213 {
1214 // same order for overlapping faces
1215 for (int i = 0; i < mesh.n_faces(); i++)
1216 if (mesh.leader_face_of_face(i) >= 0)
1217 face_orders[mesh.leader_face_of_face(i)] = std::min(face_orders[mesh.leader_face_of_face(i)], face_orders[i]);
1218
1219 for (int i = 0; i < mesh.n_faces(); i++)
1220 if (mesh.leader_face_of_face(i) >= 0)
1221 face_orders[i] = std::min(face_orders[mesh.leader_face_of_face(i)], face_orders[i]);
1222
1223 // face order no smaller than order of its edges
1224 for (int i = 0; i < mesh.n_faces(); i++)
1225 {
1226 if (mesh.n_face_cells(i) == 0)
1227 continue;
1228 for (int j = 0; j < mesh.n_face_vertices(i); j++)
1229 {
1230 const int e_id = mesh.face_edge(i, j);
1231 edge_orders[e_id] = std::min(edge_orders[e_id], face_orders[i]);
1232 }
1233 }
1234
1235 // same order for overlapping edges
1236 for (int i = 0; i < mesh.n_edges(); i++)
1237 if (mesh.leader_edge_of_edge(i) >= 0)
1238 edge_orders[mesh.leader_edge_of_edge(i)] = std::min(edge_orders[mesh.leader_edge_of_edge(i)], edge_orders[i]);
1239
1240 for (int i = 0; i < mesh.n_edges(); i++)
1241 if (mesh.leader_edge_of_edge(i) >= 0)
1242 edge_orders[i] = std::min(edge_orders[mesh.leader_edge_of_edge(i)], edge_orders[i]);
1243
1244 // face order no larger than order of interior edges
1245 for (int i = 0; i < mesh.n_edges(); i++)
1246 {
1247 if (mesh.n_edge_cells(i) == 0)
1248 continue;
1249 if (mesh.leader_face_of_edge(i) >= 0 && mesh.leader_edge_of_edge(i) < 0)
1250 face_orders[mesh.leader_face_of_edge(i)] = std::min(face_orders[mesh.leader_face_of_edge(i)], edge_orders[i]);
1251 }
1252 }
1253 }
1254} // anonymous namespace
1255
1256Eigen::VectorXi LagrangeBasis3d::tet_face_local_nodes(const int p, const Mesh3D &mesh, Navigation3D::Index index)
1257{
1258 const int nn = p > 2 ? (p - 2) : 0;
1259 const int n_edge_nodes = (p - 1) * 6;
1260 const int n_face_nodes = nn * (nn + 1) / 2;
1261
1262 const int c = index.element;
1263 assert(mesh.is_simplex(c));
1264
1265 // Local to global mapping of node indices
1266 const auto l2g = tet_vertices_local_to_global(mesh, c);
1267
1268 // Extract requested interface
1269 Eigen::VectorXi result(3 + (p - 1) * 3 + n_face_nodes);
1270 result[0] = find_index(l2g.begin(), l2g.end(), index.vertex);
1271 result[1] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
1272 result[2] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
1273
1274 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
1275 Eigen::Matrix<int, 6, 2> ev;
1276 ev.row(0) << l2g[0], l2g[1];
1277 ev.row(1) << l2g[1], l2g[2];
1278 ev.row(2) << l2g[2], l2g[0];
1279
1280 ev.row(3) << l2g[0], l2g[3];
1281 ev.row(4) << l2g[1], l2g[3];
1282 ev.row(5) << l2g[2], l2g[3];
1283
1284 Navigation3D::Index tmp = index;
1285
1286 for (int le = 0; le < e.rows(); ++le)
1287 {
1288 // const auto index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
1289 const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1290 e[le] = l_index;
1291 }
1292
1293 int ii = 3;
1294 for (int k = 0; k < 3; ++k)
1295 {
1296 bool reverse = false;
1297 int le = 0;
1298 for (; le < ev.rows(); ++le)
1299 {
1300 // const auto l_index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
1301 // const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1302 const auto l_index = e[le];
1303 if (l_index.edge == tmp.edge)
1304 {
1305 if (l_index.vertex == tmp.vertex)
1306 reverse = false;
1307 else
1308 {
1309 reverse = true;
1310 if (mesh.switch_vertex(tmp).vertex != l_index.vertex)
1311 assert(false);
1312 }
1313
1314 break;
1315 }
1316 }
1317 assert(le < 6);
1318
1319 if (!reverse)
1320 {
1321
1322 for (int i = 0; i < p - 1; ++i)
1323 {
1324 result[ii++] = 4 + le * (p - 1) + i;
1325 }
1326 }
1327 else
1328 {
1329 for (int i = 0; i < p - 1; ++i)
1330 {
1331 result[ii++] = 4 + (le + 1) * (p - 1) - i - 1;
1332 }
1333 }
1334
1335 tmp = mesh.next_around_face(tmp);
1336 }
1337
1338 // faces
1339
1340 Eigen::Matrix<int, 4, 3> fv;
1341 fv.row(0) << l2g[0], l2g[1], l2g[2];
1342 fv.row(1) << l2g[0], l2g[1], l2g[3];
1343 fv.row(2) << l2g[1], l2g[2], l2g[3];
1344 fv.row(3) << l2g[2], l2g[0], l2g[3];
1345
1346 long lf = 0;
1347 for (; lf < fv.rows(); ++lf)
1348 {
1349 const auto l_index = mesh.get_index_from_element_face(c, fv(lf, 0), fv(lf, 1), fv(lf, 2));
1350 if (l_index.face == index.face)
1351 break;
1352 }
1353
1354 assert(lf < fv.rows());
1355
1356 if (n_face_nodes == 0)
1357 {
1358 }
1359 else if (n_face_nodes == 1)
1360 result[ii++] = 4 + n_edge_nodes + lf;
1361 else // if (n_face_nodes == 3)
1362 {
1363
1364 const auto get_order = [&p, &nn, &n_face_nodes](const std::array<int, 3> &corners) {
1365 int index;
1366 int start;
1367 int offset;
1368
1369 std::vector<int> order1(n_face_nodes); // A-> B
1370 for (int k = 0; k < n_face_nodes; ++k)
1371 order1[k] = k;
1372
1373 std::vector<int> order2(n_face_nodes); // B-> A
1374 index = 0;
1375 start = nn - 1;
1376 for (int k = 0; k < nn; ++k)
1377 {
1378 for (int l = 0; l < nn - k; ++l)
1379 {
1380 order2[index] = start - l;
1381 index++;
1382 }
1383 start += (nn - 1) - k;
1384 }
1385
1386 std::vector<int> order3(n_face_nodes); // A->C
1387 index = 0;
1388 for (int k = 0; k < nn; ++k)
1389 {
1390 offset = k;
1391 for (int l = 0; l < nn - k; ++l)
1392 {
1393 order3[index] = offset;
1394 offset += nn - l;
1395
1396 index++;
1397 }
1398 }
1399
1400 std::vector<int> order4(n_face_nodes); // C-> A
1401 index = 0;
1402 start = n_face_nodes - 1;
1403 for (int k = 0; k < nn; ++k)
1404 {
1405 offset = 0;
1406 for (int l = 0; l < nn - k; ++l)
1407 {
1408 order4[index] = start - offset;
1409 offset += k + 2 + l;
1410 index++;
1411 }
1412
1413 start += -k - 1;
1414 }
1415
1416 std::vector<int> order5(n_face_nodes); // B-> C
1417 index = 0;
1418 start = nn - 1;
1419 for (int k = 0; k < nn; ++k)
1420 {
1421 offset = 0;
1422 for (int l = 0; l < nn - k; ++l)
1423 {
1424 order5[index] = start + offset;
1425 offset += nn - 1 - l;
1426 index++;
1427 }
1428
1429 start--;
1430 }
1431
1432 std::vector<int> order6(n_face_nodes); // C-> B
1433 index = 0;
1434 start = n_face_nodes;
1435 for (int k = 0; k < nn; ++k)
1436 {
1437 offset = 0;
1438 start = start - k - 1;
1439 for (int l = 0; l < nn - k; ++l)
1440 {
1441 order6[index] = start - offset;
1442 offset += l + 1 + k;
1443 index++;
1444 }
1445 }
1446
1447 if (corners[0] == order1[0] && corners[1] == order1[nn - 1])
1448 {
1449 assert(corners[2] == order1[n_face_nodes - 1]);
1450 return order1;
1451 }
1452
1453 if (corners[0] == order2[0] && corners[1] == order2[nn - 1])
1454 {
1455 assert(corners[2] == order2[n_face_nodes - 1]);
1456 return order2;
1457 }
1458
1459 if (corners[0] == order3[0] && corners[1] == order3[nn - 1])
1460 {
1461 assert(corners[2] == order3[n_face_nodes - 1]);
1462 return order3;
1463 }
1464
1465 if (corners[0] == order4[0] && corners[1] == order4[nn - 1])
1466 {
1467 assert(corners[2] == order4[n_face_nodes - 1]);
1468 return order4;
1469 }
1470
1471 if (corners[0] == order5[0] && corners[1] == order5[nn - 1])
1472 {
1473 assert(corners[2] == order5[n_face_nodes - 1]);
1474 return order5;
1475 }
1476
1477 if (corners[0] == order6[0] && corners[1] == order6[nn - 1])
1478 {
1479 assert(corners[2] == order6[n_face_nodes - 1]);
1480 return order6;
1481 }
1482
1483 assert(false);
1484 return order1;
1485 };
1486
1487 Eigen::MatrixXd nodes;
1488 autogen::p_nodes_3d(p, nodes);
1489 // auto pos = LagrangeBasis3d::linear_tet_face_local_nodes_coordinates(mesh, index);
1490 // Local to global mapping of node indices
1491
1492 // Extract requested interface
1493 std::array<int, 3> idx;
1494 for (int lv = 0; lv < 3; ++lv)
1495 {
1496 idx[lv] = find_index(l2g.begin(), l2g.end(), index.vertex);
1497 index = mesh.next_around_face(index);
1498 }
1499 Eigen::Matrix3d pos(3, 3);
1500 int cnt = 0;
1501 for (int i : idx)
1502 {
1503 pos.row(cnt++) = nodes.row(i);
1504 }
1505
1506 const Eigen::RowVector3d bary = pos.colwise().mean();
1507
1508 const int offset = 4 + n_edge_nodes;
1509 bool found = false;
1510 for (int lff = 0; lff < 4; ++lff)
1511 {
1512 Eigen::MatrixXd loc_nodes = nodes.block(offset + lff * n_face_nodes, 0, n_face_nodes, 3);
1513 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
1514
1515 if ((node_bary - bary).norm() < 1e-10)
1516 {
1517 std::array<int, 3> corners;
1518 int sum = 0;
1519 for (int m = 0; m < 3; ++m)
1520 {
1521 auto t = pos.row(m);
1522 int min_n = -1;
1523 double min_dis = 10000;
1524
1525 for (int n = 0; n < n_face_nodes; ++n)
1526 {
1527 double dis = (loc_nodes.row(n) - t).squaredNorm();
1528 if (dis < min_dis)
1529 {
1530 min_dis = dis;
1531 min_n = n;
1532 }
1533 }
1534
1535 assert(min_n >= 0);
1536 assert(min_n < n_face_nodes);
1537 corners[m] = min_n;
1538 }
1539
1540 const auto indices = get_order(corners);
1541 for (int min_n : indices)
1542 {
1543 sum += min_n;
1544 result[ii++] = 4 + n_edge_nodes + min_n + lf * n_face_nodes;
1545 }
1546
1547 assert(sum == (n_face_nodes - 1) * n_face_nodes / 2);
1548
1549 found = true;
1550 assert(lff == lf);
1551
1552 break;
1553 }
1554 }
1555
1556 assert(found);
1557 }
1558 // else
1559 // {
1560 // assert(n_face_nodes == 0);
1561 // }
1562
1563 assert(ii == result.size());
1564 return result;
1565}
1566
1567Eigen::VectorXi LagrangeBasis3d::hex_face_local_nodes(const bool serendipity, const int q, const Mesh3D &mesh, Navigation3D::Index index)
1568{
1569 const int nn = q - 1;
1570 const int n_edge_nodes = nn * 12;
1571 const int n_face_nodes = serendipity ? 0 : nn * nn;
1572
1573 const int c = index.element;
1574 assert(mesh.is_cube(c));
1575
1576 // Local to global mapping of node indices
1577 const auto l2g = hex_vertices_local_to_global(mesh, c);
1578
1579 // Extract requested interface
1580 Eigen::VectorXi result(4 + nn * 4 + n_face_nodes);
1581 result[0] = find_index(l2g.begin(), l2g.end(), index.vertex);
1582 result[1] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
1583 result[2] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
1584 result[3] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(mesh.next_around_face(index))).vertex);
1585
1586 Eigen::Matrix<Navigation3D::Index, 12, 1> e;
1587 Eigen::Matrix<int, 12, 2> ev;
1588 ev.row(0) << l2g[0], l2g[1];
1589 ev.row(1) << l2g[1], l2g[2];
1590 ev.row(2) << l2g[2], l2g[3];
1591 ev.row(3) << l2g[3], l2g[0];
1592 ev.row(4) << l2g[0], l2g[4];
1593 ev.row(5) << l2g[1], l2g[5];
1594 ev.row(6) << l2g[2], l2g[6];
1595 ev.row(7) << l2g[3], l2g[7];
1596 ev.row(8) << l2g[4], l2g[5];
1597 ev.row(9) << l2g[5], l2g[6];
1598 ev.row(10) << l2g[6], l2g[7];
1599 ev.row(11) << l2g[7], l2g[4];
1600
1601 Navigation3D::Index tmp = index;
1602
1603 for (int le = 0; le < e.rows(); ++le)
1604 {
1605 const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1606 e[le] = l_index;
1607 }
1608
1609 int ii = 4;
1610 for (int k = 0; k < 4; ++k)
1611 {
1612 bool reverse = false;
1613 int le = 0;
1614 for (; le < ev.rows(); ++le)
1615 {
1616 // const auto l_index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
1617 // const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1618 const auto l_index = e[le];
1619 if (l_index.edge == tmp.edge)
1620 {
1621 if (l_index.vertex == tmp.vertex)
1622 reverse = false;
1623 else
1624 {
1625 reverse = true;
1626 assert(mesh.switch_vertex(tmp).vertex == l_index.vertex);
1627 }
1628
1629 break;
1630 }
1631 }
1632 assert(le < 12);
1633
1634 if (!reverse)
1635 {
1636
1637 for (int i = 0; i < q - 1; ++i)
1638 {
1639 result[ii++] = 8 + le * (q - 1) + i;
1640 }
1641 }
1642 else
1643 {
1644 for (int i = 0; i < q - 1; ++i)
1645 {
1646 result[ii++] = 8 + (le + 1) * (q - 1) - i - 1;
1647 }
1648 }
1649
1650 tmp = mesh.next_around_face(tmp);
1651 }
1652
1653 // faces
1654
1655 Eigen::Matrix<int, 6, 4> fv;
1656 fv.row(0) << l2g[0], l2g[3], l2g[4], l2g[7];
1657 fv.row(1) << l2g[1], l2g[2], l2g[5], l2g[6];
1658 fv.row(2) << l2g[0], l2g[1], l2g[5], l2g[4];
1659 fv.row(3) << l2g[3], l2g[2], l2g[6], l2g[7];
1660 fv.row(4) << l2g[0], l2g[1], l2g[2], l2g[3];
1661 fv.row(5) << l2g[4], l2g[5], l2g[6], l2g[7];
1662
1663 long lf = 0;
1664 for (; lf < fv.rows(); ++lf)
1665 {
1666 const auto l_index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
1667 if (l_index.face == index.face)
1668 break;
1669 }
1670
1671 assert(lf < fv.rows());
1672
1673 if (n_face_nodes == 1)
1674 result[ii++] = 8 + n_edge_nodes + lf;
1675 else if (n_face_nodes != 0)
1676 {
1677 Eigen::MatrixXd nodes;
1678 autogen::q_nodes_3d(q, nodes);
1679 // auto pos = LagrangeBasis3d::linear_tet_face_local_nodes_coordinates(mesh, index);
1680 // Local to global mapping of node indices
1681
1682 // Extract requested interface
1683 std::array<int, 4> idx;
1684 for (int lv = 0; lv < 4; ++lv)
1685 {
1686 idx[lv] = find_index(l2g.begin(), l2g.end(), index.vertex);
1687 index = mesh.next_around_face(index);
1688 }
1689 Eigen::Matrix<double, 4, 3> pos(4, 3);
1690 int cnt = 0;
1691 for (int i : idx)
1692 {
1693 pos.row(cnt++) = nodes.row(i);
1694 }
1695
1696 const Eigen::RowVector3d bary = pos.colwise().mean();
1697
1698 const int offset = 8 + n_edge_nodes;
1699 bool found = false;
1700 for (int lff = 0; lff < 6; ++lff)
1701 {
1702 Eigen::Matrix<double, 4, 3> loc_nodes = nodes.block<4, 3>(offset + lff * n_face_nodes, 0);
1703 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
1704
1705 if ((node_bary - bary).norm() < 1e-10)
1706 {
1707 int sum = 0;
1708 for (int m = 0; m < 4; ++m)
1709 {
1710 auto t = pos.row(m);
1711 int min_n = -1;
1712 double min_dis = 10000;
1713
1714 for (int n = 0; n < 4; ++n)
1715 {
1716 double dis = (loc_nodes.row(n) - t).squaredNorm();
1717 if (dis < min_dis)
1718 {
1719 min_dis = dis;
1720 min_n = n;
1721 }
1722 }
1723
1724 assert(min_n >= 0);
1725 assert(min_n < 4);
1726
1727 sum += min_n;
1728
1729 result[ii++] = 8 + n_edge_nodes + min_n + lf * n_face_nodes;
1730 }
1731
1732 assert(sum == 6); // 0 + 1 + 2 + 3
1733
1734 found = true;
1735 assert(lff == lf);
1736 }
1737
1738 if (found)
1739 break;
1740 }
1741
1742 assert(found);
1743 }
1744
1745 assert(ii == result.size());
1746 return result;
1747}
1748
1749Eigen::VectorXi LagrangeBasis3d::prism_face_local_nodes(const int p, const int q, const Mesh3D &mesh, Navigation3D::Index index)
1750{
1751 const int c = index.element;
1752 assert(mesh.is_prism(c));
1753
1754 // Local to global mapping of node indices
1755 const auto l2g = prism_vertices_local_to_global(mesh, c);
1756
1757 const int global_n_edges_nodes = (p - 1) * 6 + (q - 1) * 3;
1758
1759 if (mesh.n_face_vertices(index.face) == 3)
1760 {
1761 const int nn = p > 2 ? (p - 2) : 0;
1762 const int n_edge_nodes = (p - 1) * 6;
1763 const int n_face_nodes = nn * (nn + 1) / 2;
1764
1765 // Extract requested interface
1766 Eigen::VectorXi result(3 + (p - 1) * 3 + n_face_nodes);
1767 result[0] = find_index(l2g.begin(), l2g.end(), index.vertex);
1768 result[1] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
1769 result[2] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
1770
1771 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
1772 Eigen::Matrix<int, 6, 2> ev;
1773 ev.row(0) << l2g[0], l2g[1];
1774 ev.row(1) << l2g[1], l2g[2];
1775 ev.row(2) << l2g[2], l2g[0];
1776
1777 ev.row(3) << l2g[3], l2g[4];
1778 ev.row(4) << l2g[4], l2g[5];
1779 ev.row(5) << l2g[5], l2g[3];
1780
1781 Navigation3D::Index tmp = index;
1782
1783 for (int le = 0; le < e.rows(); ++le)
1784 {
1785 // const auto index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
1786 const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1787 e[le] = l_index;
1788 }
1789
1790 int ii = 3;
1791 for (int k = 0; k < 3; ++k)
1792 {
1793 bool reverse = false;
1794 int le = 0;
1795 for (; le < ev.rows(); ++le)
1796 {
1797 // const auto l_index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
1798 // const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1799 const auto l_index = e[le];
1800 if (l_index.edge == tmp.edge)
1801 {
1802 if (l_index.vertex == tmp.vertex)
1803 reverse = false;
1804 else
1805 {
1806 reverse = true;
1807 if (mesh.switch_vertex(tmp).vertex != l_index.vertex)
1808 assert(false);
1809 }
1810
1811 break;
1812 }
1813 }
1814 assert(le < 6);
1815
1816 if (!reverse)
1817 {
1818
1819 for (int i = 0; i < p - 1; ++i)
1820 {
1821 result[ii++] = 6 + le * (p - 1) + i;
1822 }
1823 }
1824 else
1825 {
1826 for (int i = 0; i < p - 1; ++i)
1827 {
1828 result[ii++] = 6 + (le + 1) * (p - 1) - i - 1;
1829 }
1830 }
1831
1832 tmp = mesh.next_around_face(tmp);
1833 }
1834
1835 // faces
1836
1837 Eigen::Matrix<int, 2, 3> fv;
1838 fv.row(0) << l2g[0], l2g[1], l2g[2];
1839 fv.row(1) << l2g[3], l2g[4], l2g[5];
1840
1841 long lf = 0;
1842 for (; lf < fv.rows(); ++lf)
1843 {
1844 const auto l_index = mesh.get_index_from_element_face(c, fv(lf, 0), fv(lf, 1), fv(lf, 2));
1845 if (l_index.face == index.face)
1846 break;
1847 }
1848
1849 assert(lf < fv.rows());
1850
1851 if (n_face_nodes == 0)
1852 {
1853 }
1854 else if (n_face_nodes == 1)
1855 result[ii++] = 6 + global_n_edges_nodes + lf;
1856 else // if (n_face_nodes == 3)
1857 {
1858
1859 const auto get_order = [&p, &q, &nn, &n_face_nodes](const std::array<int, 3> &corners) {
1860 int index;
1861 int start;
1862 int offset;
1863
1864 std::vector<int> order1(n_face_nodes); // A-> B
1865 for (int k = 0; k < n_face_nodes; ++k)
1866 order1[k] = k;
1867
1868 std::vector<int> order2(n_face_nodes); // B-> A
1869 index = 0;
1870 start = nn - 1;
1871 for (int k = 0; k < nn; ++k)
1872 {
1873 for (int l = 0; l < nn - k; ++l)
1874 {
1875 order2[index] = start - l;
1876 index++;
1877 }
1878 start += (nn - 1) - k;
1879 }
1880
1881 std::vector<int> order3(n_face_nodes); // A->C
1882 index = 0;
1883 for (int k = 0; k < nn; ++k)
1884 {
1885 offset = k;
1886 for (int l = 0; l < nn - k; ++l)
1887 {
1888 order3[index] = offset;
1889 offset += nn - l;
1890
1891 index++;
1892 }
1893 }
1894
1895 std::vector<int> order4(n_face_nodes); // C-> A
1896 index = 0;
1897 start = n_face_nodes - 1;
1898 for (int k = 0; k < nn; ++k)
1899 {
1900 offset = 0;
1901 for (int l = 0; l < nn - k; ++l)
1902 {
1903 order4[index] = start - offset;
1904 offset += k + 2 + l;
1905 index++;
1906 }
1907
1908 start += -k - 1;
1909 }
1910
1911 std::vector<int> order5(n_face_nodes); // B-> C
1912 index = 0;
1913 start = nn - 1;
1914 for (int k = 0; k < nn; ++k)
1915 {
1916 offset = 0;
1917 for (int l = 0; l < nn - k; ++l)
1918 {
1919 order5[index] = start + offset;
1920 offset += nn - 1 - l;
1921 index++;
1922 }
1923
1924 start--;
1925 }
1926
1927 std::vector<int> order6(n_face_nodes); // C-> B
1928 index = 0;
1929 start = n_face_nodes;
1930 for (int k = 0; k < nn; ++k)
1931 {
1932 offset = 0;
1933 start = start - k - 1;
1934 for (int l = 0; l < nn - k; ++l)
1935 {
1936 order6[index] = start - offset;
1937 offset += l + 1 + k;
1938 index++;
1939 }
1940 }
1941
1942 if (corners[0] == order1[0] && corners[1] == order1[nn - 1])
1943 {
1944 assert(corners[2] == order1[n_face_nodes - 1]);
1945 return order1;
1946 }
1947
1948 if (corners[0] == order2[0] && corners[1] == order2[nn - 1])
1949 {
1950 assert(corners[2] == order2[n_face_nodes - 1]);
1951 return order2;
1952 }
1953
1954 if (corners[0] == order3[0] && corners[1] == order3[nn - 1])
1955 {
1956 assert(corners[2] == order3[n_face_nodes - 1]);
1957 return order3;
1958 }
1959
1960 if (corners[0] == order4[0] && corners[1] == order4[nn - 1])
1961 {
1962 assert(corners[2] == order4[n_face_nodes - 1]);
1963 return order4;
1964 }
1965
1966 if (corners[0] == order5[0] && corners[1] == order5[nn - 1])
1967 {
1968 assert(corners[2] == order5[n_face_nodes - 1]);
1969 return order5;
1970 }
1971
1972 if (corners[0] == order6[0] && corners[1] == order6[nn - 1])
1973 {
1974 assert(corners[2] == order6[n_face_nodes - 1]);
1975 return order6;
1976 }
1977
1978 assert(false);
1979 return order1;
1980 };
1981
1982 Eigen::MatrixXd nodes;
1983 autogen::prism_nodes_3d(p, q, nodes);
1984 // auto pos = LagrangeBasis3d::linear_tet_face_local_nodes_coordinates(mesh, index);
1985 // Local to global mapping of node indices
1986
1987 // Extract requested interface
1988 std::array<int, 3> idx;
1989 for (int lv = 0; lv < 3; ++lv)
1990 {
1991 idx[lv] = find_index(l2g.begin(), l2g.end(), index.vertex);
1992 index = mesh.next_around_face(index);
1993 }
1994 Eigen::Matrix3d pos(3, 3);
1995 int cnt = 0;
1996 for (int i : idx)
1997 {
1998 pos.row(cnt++) = nodes.row(i);
1999 }
2000
2001 const Eigen::RowVector3d bary = pos.colwise().mean();
2002
2003 const int offset = 6 + global_n_edges_nodes;
2004 bool found = false;
2005 for (int lff = 0; lff < 2; ++lff)
2006 {
2007 Eigen::MatrixXd loc_nodes = nodes.block(offset + lff * n_face_nodes, 0, n_face_nodes, 3);
2008 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
2009
2010 if ((node_bary - bary).norm() < 1e-10)
2011 {
2012 std::array<int, 3> corners;
2013 int sum = 0;
2014 for (int m = 0; m < 3; ++m)
2015 {
2016 auto t = pos.row(m);
2017 int min_n = -1;
2018 double min_dis = 10000;
2019
2020 for (int n = 0; n < n_face_nodes; ++n)
2021 {
2022 double dis = (loc_nodes.row(n) - t).squaredNorm();
2023 if (dis < min_dis)
2024 {
2025 min_dis = dis;
2026 min_n = n;
2027 }
2028 }
2029
2030 assert(min_n >= 0);
2031 assert(min_n < n_face_nodes);
2032 corners[m] = min_n;
2033 }
2034
2035 const auto indices = get_order(corners);
2036 for (int min_n : indices)
2037 {
2038 sum += min_n;
2039 result[ii++] = 6 + global_n_edges_nodes + min_n + lf * n_face_nodes;
2040 }
2041
2042 assert(sum == (n_face_nodes - 1) * n_face_nodes / 2);
2043
2044 found = true;
2045 assert(lff == lf);
2046
2047 break;
2048 }
2049 }
2050
2051 assert(found);
2052 }
2053 // else
2054 // {
2055 // assert(n_face_nodes == 0);
2056 // }
2057
2058 assert(ii == result.size());
2059 return result;
2060 }
2061 else
2062 {
2063 const int nn = q - 1;
2064 const int n_edge_nodes = nn * 12;
2065 const int n_face_nodes = nn * nn;
2066
2067 // Extract requested interface
2068 Eigen::VectorXi result(4 + nn * 4 + n_face_nodes);
2069 result[0] = find_index(l2g.begin(), l2g.end(), index.vertex);
2070 result[1] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
2071 result[2] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
2072 result[3] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(mesh.next_around_face(index))).vertex);
2073
2074 Eigen::Matrix<Navigation3D::Index, 9, 1> e;
2075 Eigen::Matrix<int, 9, 2> ev;
2076 ev.row(0) << l2g[0], l2g[1];
2077 ev.row(1) << l2g[1], l2g[2];
2078 ev.row(2) << l2g[2], l2g[0];
2079
2080 ev.row(3) << l2g[3], l2g[4];
2081 ev.row(4) << l2g[4], l2g[5];
2082 ev.row(5) << l2g[5], l2g[3];
2083
2084 ev.row(6) << l2g[0], l2g[3];
2085 ev.row(7) << l2g[1], l2g[4];
2086 ev.row(8) << l2g[2], l2g[5];
2087
2088 Navigation3D::Index tmp = index;
2089
2090 for (int le = 0; le < e.rows(); ++le)
2091 {
2092 const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
2093 e[le] = l_index;
2094 }
2095
2096 int ii = 4;
2097 for (int k = 0; k < 4; ++k)
2098 {
2099 bool reverse = false;
2100 int le = 0;
2101 for (; le < ev.rows(); ++le)
2102 {
2103 // const auto l_index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
2104 // const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
2105 const auto l_index = e[le];
2106 if (l_index.edge == tmp.edge)
2107 {
2108 if (l_index.vertex == tmp.vertex)
2109 reverse = false;
2110 else
2111 {
2112 reverse = true;
2113 assert(mesh.switch_vertex(tmp).vertex == l_index.vertex);
2114 }
2115
2116 break;
2117 }
2118 }
2119 assert(le < 9);
2120
2121 if (!reverse)
2122 {
2123
2124 for (int i = 0; i < q - 1; ++i)
2125 {
2126 result[ii++] = 6 + le * (q - 1) + i;
2127 }
2128 }
2129 else
2130 {
2131 for (int i = 0; i < q - 1; ++i)
2132 {
2133 result[ii++] = 6 + (le + 1) * (q - 1) - i - 1;
2134 }
2135 }
2136
2137 tmp = mesh.next_around_face(tmp);
2138 }
2139
2140 // faces
2141
2142 Eigen::Matrix<int, 3, 4> fv;
2143 fv.row(0) << l2g[0], l2g[3], l2g[4], l2g[1];
2144 fv.row(1) << l2g[1], l2g[4], l2g[5], l2g[2];
2145 fv.row(2) << l2g[2], l2g[5], l2g[3], l2g[0];
2146
2147 long lf = 0;
2148 for (; lf < fv.rows(); ++lf)
2149 {
2150 const auto l_index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
2151 if (l_index.face == index.face)
2152 break;
2153 }
2154
2155 assert(lf < fv.rows());
2156
2157 if (n_face_nodes == 1)
2158 result[ii++] = 6 + global_n_edges_nodes + lf;
2159 else if (n_face_nodes != 0)
2160 {
2161 Eigen::MatrixXd nodes;
2162 autogen::q_nodes_3d(q, nodes);
2163 // auto pos = LagrangeBasis3d::linear_tet_face_local_nodes_coordinates(mesh, index);
2164 // Local to global mapping of node indices
2165
2166 // Extract requested interface
2167 std::array<int, 4> idx;
2168 for (int lv = 0; lv < 4; ++lv)
2169 {
2170 idx[lv] = find_index(l2g.begin(), l2g.end(), index.vertex);
2171 index = mesh.next_around_face(index);
2172 }
2173 Eigen::Matrix<double, 4, 3> pos(4, 3);
2174 int cnt = 0;
2175 for (int i : idx)
2176 {
2177 pos.row(cnt++) = nodes.row(i);
2178 }
2179
2180 const Eigen::RowVector3d bary = pos.colwise().mean();
2181
2182 const int offset = 8 + n_edge_nodes;
2183 bool found = false;
2184 for (int lff = 0; lff < 6; ++lff)
2185 {
2186 Eigen::Matrix<double, 4, 3> loc_nodes = nodes.block<4, 3>(offset + lff * n_face_nodes, 0);
2187 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
2188
2189 if ((node_bary - bary).norm() < 1e-10)
2190 {
2191 int sum = 0;
2192 for (int m = 0; m < 4; ++m)
2193 {
2194 auto t = pos.row(m);
2195 int min_n = -1;
2196 double min_dis = 10000;
2197
2198 for (int n = 0; n < 4; ++n)
2199 {
2200 double dis = (loc_nodes.row(n) - t).squaredNorm();
2201 if (dis < min_dis)
2202 {
2203 min_dis = dis;
2204 min_n = n;
2205 }
2206 }
2207
2208 assert(min_n >= 0);
2209 assert(min_n < 4);
2210
2211 sum += min_n;
2212
2213 result[ii++] = 8 + n_edge_nodes + min_n + lf * n_face_nodes;
2214 }
2215
2216 assert(sum == 6); // 0 + 1 + 2 + 3
2217
2218 found = true;
2219 assert(lff == lf);
2220 }
2221
2222 if (found)
2223 break;
2224 }
2225
2226 assert(found);
2227 }
2228
2229 assert(ii == result.size());
2230 return result;
2231 }
2232}
2233
2234Eigen::VectorXi LagrangeBasis3d::pyramid_face_local_nodes(const int p, const Mesh3D &mesh, Navigation3D::Index index)
2235{
2236 const int c = index.element;
2237 assert(mesh.is_pyramid(c));
2238
2239 // local-to-global vertex map (5 vertices)
2240 const auto l2g = pyramid_vertices_local_to_global(mesh, c);
2241 const auto &v = l2g;
2242
2243 // build the 8 pyramid edges in a fixed local order (matches pyramid_local_to_global ev)
2244 Eigen::Matrix<int, 8, 2> ev;
2245 ev.row(0) << v[0], v[1];
2246 ev.row(1) << v[1], v[2];
2247 ev.row(2) << v[2], v[3];
2248 ev.row(3) << v[3], v[0];
2249 ev.row(4) << v[0], v[4];
2250 ev.row(5) << v[1], v[4];
2251 ev.row(6) << v[2], v[4];
2252 ev.row(7) << v[3], v[4];
2253
2254 Eigen::Matrix<Navigation3D::Index, 8, 1> e;
2255 for (int le = 0; le < 8; ++le)
2256 e[le] = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
2257
2258 const int nei = p - 1; // interior nodes per edge
2259 const int nfi_tri = (p - 1) * (p - 2) / 2; // interior nodes per tri face
2260 const int nfi_quad = (p - 1) * (p - 1); // interior nodes on quad face
2261
2262 // offsets into the local DOF array (matches pyramid_local_to_global ordering):
2263 // [0..4] : vertices
2264 // [5 .. 5+8*nei-1] : edge interiors (le=0..7, nei each)
2265 // [edge_end .. +4*nfi_tri-1] : 4 tri face interiors (lf=0..3)
2266 // [tri_end .. +nfi_quad-1] : quad face interiors
2267 const int edge_start = 5;
2268 const int tri_face_start = edge_start + 8 * nei;
2269 const int quad_face_start = tri_face_start + 4 * nfi_tri;
2270
2271 // Append nei interior nodes for the edge currently pointed to by edge_idx,
2272 // respecting traversal direction vs. canonical edge direction.
2273 auto append_edge_dofs = [&](Eigen::VectorXi &result, int &ii, const Navigation3D::Index &edge_idx) {
2274 if (nei <= 0)
2275 return;
2276 int le = 0;
2277 for (; le < 8; ++le)
2278 {
2279 if (e[le].edge == edge_idx.edge)
2280 break;
2281 }
2282 assert(le < 8);
2283 const bool forward = (edge_idx.vertex == ev(le, 0));
2284 for (int q = 0; q < nei; ++q)
2285 {
2286 const int local_q = forward ? q : (nei - 1 - q);
2287 result[ii++] = edge_start + le * nei + local_q;
2288 }
2289 };
2290
2291 assert(mesh.n_face_vertices(index.face) == 3 || mesh.n_face_vertices(index.face) == 4);
2292
2293 // ---- TRI face ----
2294 if (mesh.n_face_vertices(index.face) == 3)
2295 {
2296 Eigen::VectorXi result(3 + 3 * nei + nfi_tri);
2297 int ii = 0;
2298
2299 // vertices in face traversal order
2300 result[ii++] = find_index(l2g.begin(), l2g.end(), index.vertex);
2301 result[ii++] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
2302 result[ii++] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
2303
2304 // edge interiors in traversal order
2305 Navigation3D::Index tmp = index;
2306 for (int k = 0; k < 3; ++k)
2307 {
2308 append_edge_dofs(result, ii, tmp);
2309 tmp = mesh.next_around_face(tmp);
2310 }
2311
2312 // tri face interior nodes: find lf by matching the set of face vertices
2313 if (nfi_tri > 0)
2314 {
2315 static const int tri_fv[4][3] = {{0, 1, 4}, {1, 2, 4}, {2, 3, 4}, {3, 0, 4}};
2316 const int fv0 = index.vertex;
2317 const int fv1 = mesh.next_around_face(index).vertex;
2318 const int fv2 = mesh.next_around_face(mesh.next_around_face(index)).vertex;
2319 int lf = -1;
2320 for (int f = 0; f < 4; ++f)
2321 {
2322 const int gv0 = v[tri_fv[f][0]], gv1 = v[tri_fv[f][1]], gv2 = v[tri_fv[f][2]];
2323 if ((fv0 == gv0 || fv0 == gv1 || fv0 == gv2) && (fv1 == gv0 || fv1 == gv1 || fv1 == gv2) && (fv2 == gv0 || fv2 == gv1 || fv2 == gv2))
2324 {
2325 lf = f;
2326 break;
2327 }
2328 }
2329 assert(lf >= 0);
2330 for (int q = 0; q < nfi_tri; ++q)
2331 result[ii++] = tri_face_start + lf * nfi_tri + q;
2332 }
2333
2334 assert(ii == result.size());
2335 return result;
2336 }
2337
2338 // ---- QUAD face ----
2339 else
2340 {
2341 Eigen::VectorXi result(4 + 4 * nei + nfi_quad);
2342 int ii = 0;
2343
2344 // vertices in face traversal order
2345 result[ii++] = find_index(l2g.begin(), l2g.end(), index.vertex);
2346 result[ii++] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
2347 result[ii++] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
2348 result[ii++] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(mesh.next_around_face(index))).vertex);
2349
2350 // edge interiors in traversal order
2351 Navigation3D::Index tmp = index;
2352 for (int k = 0; k < 4; ++k)
2353 {
2354 append_edge_dofs(result, ii, tmp);
2355 tmp = mesh.next_around_face(tmp);
2356 }
2357
2358 // quad face interior nodes (all belong to the single quad face)
2359 for (int q = 0; q < nfi_quad; ++q)
2360 result[ii++] = quad_face_start + q;
2361
2362 assert(ii == result.size());
2363 return result;
2364 }
2365}
2366
2368 const Mesh3D &mesh,
2369 const std::string &assembler,
2370 const int quadrature_order,
2371 const int mass_quadrature_order,
2372 const int discr_orderp,
2373 const int discr_orderq,
2374 const bool bernstein,
2375 const bool serendipity,
2376 const bool has_polys,
2377 const bool is_geom_bases,
2378 const bool use_corner_quadrature,
2379 std::vector<ElementBases> &bases,
2380 std::vector<LocalBoundary> &local_boundary,
2381 std::map<int, InterfaceData> &poly_face_to_data,
2382 std::shared_ptr<MeshNodes> &mesh_nodes)
2383{
2384 Eigen::VectorXi discr_ordersp(mesh.n_cells());
2385 discr_ordersp.setConstant(discr_orderp);
2386
2387 Eigen::VectorXi discr_ordersq(mesh.n_cells());
2388 discr_ordersq.setConstant(discr_orderq);
2389
2390 return build_bases(mesh, assembler, quadrature_order, mass_quadrature_order, discr_ordersp, discr_ordersq, bernstein, serendipity, has_polys, is_geom_bases, use_corner_quadrature, bases, local_boundary, poly_face_to_data, mesh_nodes);
2391}
2392
2394 const Mesh3D &mesh,
2395 const std::string &assembler,
2396 const int quadrature_order,
2397 const int mass_quadrature_order,
2398 const Eigen::VectorXi &discr_ordersp,
2399 const Eigen::VectorXi &discr_ordersq,
2400 const bool bernstein,
2401 const bool serendipity,
2402 const bool has_polys,
2403 const bool is_geom_bases,
2404 const bool use_corner_quadrature,
2405 std::vector<ElementBases> &bases,
2406 std::vector<LocalBoundary> &local_boundary,
2407 std::map<int, InterfaceData> &poly_face_to_data,
2408 std::shared_ptr<MeshNodes> &mesh_nodes)
2409{
2410 assert(mesh.is_volume());
2411 assert(discr_ordersp.size() == mesh.n_cells());
2412 assert(discr_ordersq.size() == mesh.n_cells());
2413
2414 // Navigation3D::get_index_from_element_face_time = 0;
2415 // Navigation3D::switch_vertex_time = 0;
2416 // Navigation3D::switch_edge_time = 0;
2417 // Navigation3D::switch_face_time = 0;
2418 // Navigation3D::switch_element_time = 0;
2419
2420 const int max_p = discr_ordersp.maxCoeff();
2421 const int max_q = discr_ordersq.maxCoeff();
2422 const int mmax = std::max(max_p, max_q);
2423 // assert(max_p < 5); //P5 not supported
2424
2425 const int nn = mmax > 1 ? (mmax - 1) : 0;
2426 const int n_face_nodes = nn * nn;
2427 const int n_cells_nodes = nn * nn * nn;
2428
2429 Eigen::VectorXi edge_orders, face_orders;
2430 if (!mesh.is_conforming())
2431 {
2432 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
2433 compute_edge_face_orders(ncmesh, discr_ordersp, edge_orders, face_orders);
2434 }
2435
2436 mesh_nodes = std::make_shared<MeshNodes>(mesh, has_polys, !is_geom_bases, nn, n_face_nodes * (is_geom_bases ? 2 : 1), mmax == 0 ? 1 : n_cells_nodes);
2437 MeshNodes &nodes = *mesh_nodes;
2438 std::vector<std::vector<int>> element_nodes_id, edge_virtual_nodes, face_virtual_nodes;
2439 compute_nodes(mesh, discr_ordersp, discr_ordersq, edge_orders, face_orders, serendipity, has_polys, is_geom_bases, nodes, edge_virtual_nodes, face_virtual_nodes, element_nodes_id, local_boundary, poly_face_to_data);
2440 // boundary_nodes = nodes.boundary_nodes();
2441
2442 bases.resize(mesh.n_cells());
2443 std::vector<int> interface_elements;
2444 interface_elements.reserve(mesh.n_faces());
2445
2446 for (int e = 0; e < mesh.n_cells(); ++e)
2447 {
2448 ElementBases &b = bases[e];
2449 const int discr_order = discr_ordersp(e);
2450 const int discr_orderq = discr_ordersq(e);
2451 const int n_el_bases = (int)element_nodes_id[e].size();
2452 b.bases.resize(n_el_bases);
2453
2454 bool skip_interface_element = false;
2455
2456 for (int j = 0; j < n_el_bases; ++j)
2457 {
2458 const int global_index = element_nodes_id[e][j];
2459 if (global_index < 0)
2460 {
2461 skip_interface_element = true;
2462 break;
2463 }
2464 }
2465
2466 if (skip_interface_element)
2467 {
2468 interface_elements.push_back(e);
2469 }
2470
2471 if (mesh.is_cube(e))
2472 {
2473 const int real_order = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 3);
2474 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 3);
2475 b.set_quadrature([real_order](Quadrature &quad) {
2476 HexQuadrature hex_quadrature;
2477 hex_quadrature.get_quadrature(real_order, quad);
2478 });
2479 b.set_mass_quadrature([real_mass_order](Quadrature &quad) {
2480 HexQuadrature hex_quadrature;
2481 hex_quadrature.get_quadrature(real_mass_order, quad);
2482 });
2483
2484 b.set_local_node_from_primitive_func([serendipity, discr_order, e](const int primitive_id, const Mesh &mesh) {
2485 const auto &mesh3d = dynamic_cast<const Mesh3D &>(mesh);
2486 Navigation3D::Index index;
2487
2488 for (int lf = 0; lf < 6; ++lf)
2489 {
2490 index = mesh3d.get_index_from_element(e, lf, 0);
2491 if (index.face == primitive_id)
2492 break;
2493 }
2494 assert(index.face == primitive_id);
2495 return hex_face_local_nodes(serendipity, discr_order, mesh3d, index);
2496 });
2497
2498 for (int j = 0; j < n_el_bases; ++j)
2499 {
2500 const int global_index = element_nodes_id[e][j];
2501
2502 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2503
2504 const int dtmp = serendipity ? -2 : discr_order;
2505
2506 b.bases[j].set_basis([dtmp, j](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { autogen::q_basis_value_3d(dtmp, j, uv, val); });
2507 b.bases[j].set_grad([dtmp, j](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { autogen::q_grad_basis_value_3d(dtmp, j, uv, val); });
2508 }
2509 }
2510 else if (mesh.is_simplex(e))
2511 {
2512 const int real_order = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 3);
2513 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 3);
2514
2515 b.set_quadrature([real_order, use_corner_quadrature](Quadrature &quad) {
2516 TetQuadrature tet_quadrature(use_corner_quadrature);
2517 tet_quadrature.get_quadrature(real_order, quad);
2518 });
2519 b.set_mass_quadrature([real_mass_order, use_corner_quadrature](Quadrature &quad) {
2520 TetQuadrature tet_quadrature(use_corner_quadrature);
2521 tet_quadrature.get_quadrature(real_mass_order, quad);
2522 });
2523
2524 b.set_local_node_from_primitive_func([discr_order, e](const int primitive_id, const Mesh &mesh) {
2525 const auto &mesh3d = dynamic_cast<const Mesh3D &>(mesh);
2526 Navigation3D::Index index;
2527
2528 for (int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2529 {
2530 index = mesh3d.get_index_from_element(e, lf, 0);
2531 if (index.face == primitive_id)
2532 break;
2533 }
2534 assert(index.face == primitive_id);
2535 return tet_face_local_nodes(discr_order, mesh3d, index);
2536 });
2537
2538 const bool rational = is_geom_bases && mesh.is_rational() && !mesh.cell_weights(e).empty();
2539 assert(!rational);
2540
2541 for (int j = 0; j < n_el_bases; ++j)
2542 {
2543 const int global_index = element_nodes_id[e][j];
2544 if (!skip_interface_element)
2545 {
2546 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2547 }
2548
2549 b.bases[j].set_basis([bernstein, discr_order, j](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { autogen::p_basis_value_3d(bernstein, discr_order, j, uv, val); });
2550 b.bases[j].set_grad([bernstein, discr_order, j](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { autogen::p_grad_basis_value_3d(bernstein, discr_order, j, uv, val); });
2551 }
2552 }
2553 else if (mesh.is_prism(e))
2554 {
2555 const int orderp = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::PRISM_LAGRANGE, 2);
2556 const int orderq = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, discr_orderq, AssemblerUtils::BasisType::PRISM_LAGRANGE, 1);
2557
2558 const int mass_orderp = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", discr_order, AssemblerUtils::BasisType::PRISM_LAGRANGE, 2);
2559 const int mass_orderq = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", discr_orderq, AssemblerUtils::BasisType::PRISM_LAGRANGE, 1);
2560
2561 b.set_quadrature([orderp, orderq](Quadrature &quad) {
2562 PrismQuadrature tet_quadrature;
2563 tet_quadrature.get_quadrature(orderp, orderq, quad);
2564 });
2565 b.set_mass_quadrature([mass_orderp, mass_orderq](Quadrature &quad) {
2566 PrismQuadrature tet_quadrature;
2567 tet_quadrature.get_quadrature(mass_orderp, mass_orderq, quad);
2568 });
2569
2570 b.set_local_node_from_primitive_func([discr_order, discr_orderq, e](const int primitive_id, const Mesh &mesh) {
2571 const auto &mesh3d = dynamic_cast<const Mesh3D &>(mesh);
2572 Navigation3D::Index index;
2573
2574 for (int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2575 {
2576 index = mesh3d.get_index_from_element(e, lf, 0);
2577 if (index.face == primitive_id)
2578 break;
2579 }
2580 assert(index.face == primitive_id);
2581 return prism_face_local_nodes(discr_order, discr_orderq, mesh3d, index);
2582 });
2583
2584 for (int j = 0; j < n_el_bases; ++j)
2585 {
2586 const int global_index = element_nodes_id[e][j];
2587 if (!skip_interface_element)
2588 {
2589 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2590 }
2591
2592 b.bases[j].set_basis([discr_order, discr_orderq, j](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { autogen::prism_basis_value_3d(discr_order, discr_orderq, j, uv, val); });
2593 b.bases[j].set_grad([discr_order, discr_orderq, j](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { autogen::prism_grad_basis_value_3d(discr_order, discr_orderq, j, uv, val); });
2594 }
2595 }
2596 else if (mesh.is_pyramid(e))
2597 {
2598 const int orderp = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::PYRAMID_LAGRANGE, 2);
2599 const int mass_orderp = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", discr_order, AssemblerUtils::BasisType::PYRAMID_LAGRANGE, 2);
2600
2601 b.set_quadrature([orderp](Quadrature &quad) {
2602 PyramidQuadrature tet_quadrature;
2603 tet_quadrature.get_quadrature(orderp, quad);
2604 });
2605 b.set_mass_quadrature([mass_orderp](Quadrature &quad) {
2606 PyramidQuadrature p_quadrature;
2607 p_quadrature.get_quadrature(mass_orderp, quad);
2608 });
2609
2610 b.set_local_node_from_primitive_func([discr_order, e](const int primitive_id, const Mesh &mesh) {
2611 const auto &mesh3d = dynamic_cast<const Mesh3D &>(mesh);
2612 Navigation3D::Index index;
2613
2614 for (int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2615 {
2616 index = mesh3d.get_index_from_element(e, lf, 0);
2617 if (index.face == primitive_id)
2618 break;
2619 }
2620 assert(index.face == primitive_id);
2621 return pyramid_face_local_nodes(discr_order, mesh3d, index);
2622 });
2623
2624 for (int j = 0; j < n_el_bases; ++j)
2625 {
2626 const int global_index = element_nodes_id[e][j];
2627 if (!skip_interface_element)
2628 {
2629 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2630 }
2631
2632 b.bases[j].set_basis([discr_order, j](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { autogen::pyramid_basis_value_3d(discr_order, j, uv, val); });
2633 b.bases[j].set_grad([discr_order, j](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { autogen::pyramid_grad_basis_value_3d(discr_order, j, uv, val); });
2634 }
2635 }
2636 else
2637 {
2638 // Polyhedra bases are built later on
2639 // assert(false);
2640 }
2641 }
2642
2643 if (!is_geom_bases)
2644 {
2645 if (!mesh.is_conforming())
2646 {
2647 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
2648
2649 std::vector<std::vector<int>> elementOrder;
2650 {
2651 const int max_order = discr_ordersp.maxCoeff(), min_order = discr_ordersp.minCoeff();
2652 int max_level = 0;
2653 for (int e = 0; e < ncmesh.n_cells(); e++)
2654 if (max_level < ncmesh.cell_ref_level(e))
2655 max_level = ncmesh.cell_ref_level(e);
2656
2657 elementOrder.resize((max_level + 1) * (max_order - min_order + 1));
2658 int N = 0;
2659 int cur_level = 0;
2660 while (cur_level <= max_level)
2661 {
2662 int order = min_order;
2663 while (order <= max_order)
2664 {
2665 int cur_bucket = (max_order - min_order + 1) * cur_level + (order - min_order);
2666 for (int i = 0; i < ncmesh.n_cells(); i++)
2667 {
2668 if (ncmesh.cell_ref_level(i) != cur_level || discr_ordersp[i] != order)
2669 continue;
2670
2671 N++;
2672 elementOrder[cur_bucket].push_back(i);
2673 }
2674 order++;
2675 }
2676 cur_level++;
2677 }
2678 }
2679
2680 for (const auto &bucket : elementOrder)
2681 {
2682 if (bucket.size() == 0)
2683 continue;
2684 polyfem::utils::maybe_parallel_for((int)bucket.size(), [&](int start, int end, int thread_id) {
2685 for (int e_aux = start; e_aux < end; e_aux++)
2686 {
2687 const int e = bucket[e_aux];
2688 ElementBases &b = bases[e];
2689 const int discr_order = discr_ordersp(e);
2690 const int n_edge_nodes = discr_order - 1;
2691 const int n_face_nodes = (discr_order - 1) * (discr_order - 2) / 2;
2692 const int n_el_bases = element_nodes_id[e].size();
2693
2694 auto v = tet_vertices_local_to_global(mesh, e);
2695
2696 Eigen::Matrix<Navigation3D::Index, 4, 1> cell_faces;
2697 Eigen::Matrix<int, 4, 3> fv;
2698 fv.row(0) << v[0], v[1], v[2];
2699 fv.row(1) << v[0], v[1], v[3];
2700 fv.row(2) << v[1], v[2], v[3];
2701 fv.row(3) << v[2], v[0], v[3];
2702
2703 for (long lf = 0; lf < fv.rows(); ++lf)
2704 {
2705 const auto index = mesh.get_index_from_element_face(e, fv(lf, 0), fv(lf, 1), fv(lf, 2));
2706 cell_faces[lf] = index;
2707 }
2708
2709 Eigen::Matrix<Navigation3D::Index, 6, 1> cell_edges;
2710 Eigen::Matrix<int, 6, 2> ev;
2711 ev.row(0) << v[0], v[1];
2712 ev.row(1) << v[1], v[2];
2713 ev.row(2) << v[2], v[0];
2714
2715 ev.row(3) << v[0], v[3];
2716 ev.row(4) << v[1], v[3];
2717 ev.row(5) << v[2], v[3];
2718
2719 for (int le = 0; le < ev.rows(); ++le)
2720 {
2721 // const auto index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
2722 const auto index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
2723 cell_edges[le] = index;
2724 }
2725
2726 Eigen::MatrixXd verts(4, 3);
2727 for (int i = 0; i < ncmesh.n_cell_vertices(e); i++)
2728 verts.row(i) = ncmesh.point(v[i]);
2729
2730 for (int j = 0; j < n_el_bases; ++j)
2731 {
2732 const int global_index = element_nodes_id[e][j];
2733
2734 if (global_index >= 0)
2735 {
2736 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2737 }
2738 else
2739 {
2740 // vertex node - hanging vertex
2741 if (j < 4)
2742 {
2743 int large_elem = -1;
2744 if (ncmesh.leader_edge_of_vertex(v[j]) >= 0)
2745 {
2746 large_elem = lowest_order_elem_on_edge(ncmesh, discr_ordersp, ncmesh.leader_edge_of_vertex(v[j]));
2747 }
2748 else if (ncmesh.leader_face_of_vertex(v[j]) >= 0)
2749 {
2750 std::vector<int> ids;
2751 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_vertex(v[j]), ids);
2752 assert(ids.size() == 1);
2753 large_elem = ids[0];
2754 }
2755 else
2756 assert(false);
2757
2758 Eigen::MatrixXd large_elem_verts(4, 3);
2759 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
2760 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
2761 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
2762
2763 Eigen::MatrixXd node_position;
2764 global_to_local(large_elem_verts, verts.row(j), node_position);
2765
2766 // evaluate the basis of the large element at this node
2767 const auto &other_bases = bases[large_elem];
2768 std::vector<AssemblyValues> w;
2769 other_bases.evaluate_bases(node_position, w);
2770
2771 // apply basis projection
2772 for (long i = 0; i < w.size(); ++i)
2773 {
2774 assert(w[i].val.size() == 1);
2775 if (std::abs(w[i].val(0)) < 1e-12)
2776 continue;
2777
2778 assert(other_bases.bases[i].global().size() > 0);
2779 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
2780 {
2781 const auto &other_global = other_bases.bases[i].global()[ii];
2782 assert(other_global.index >= 0);
2783 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
2784 }
2785 }
2786 }
2787 // edge node - slave edge / edge on face / constrained order
2788 else if (j < 4 + 6 * n_edge_nodes)
2789 {
2790 const int local_edge_id = (j - 4) / n_edge_nodes;
2791 const int edge_id = cell_edges[local_edge_id].edge;
2792 bool need_extra_fake_nodes = false;
2793 int large_elem = -1;
2794
2795 // slave edge
2796 if (ncmesh.leader_edge_of_edge(edge_id) >= 0)
2797 {
2798 std::vector<int> ids;
2799 ncmesh.get_edge_elements_neighs(ncmesh.leader_edge_of_edge(edge_id), ids);
2800 large_elem = ids[0];
2801 }
2802 // edge on face
2803 else if (ncmesh.leader_face_of_edge(edge_id) >= 0)
2804 {
2805 std::vector<int> ids;
2806 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_edge(edge_id), ids);
2807 assert(ids.size() == 1);
2808 large_elem = ids[0];
2809 }
2810 // constrained order
2811 else if (discr_order > edge_orders[edge_id])
2812 {
2813 int min_order_elem = lowest_order_elem_on_edge(ncmesh, discr_ordersp, edge_id);
2814 // if haven't built min_order_elem? directly contribute to extra nodes
2815 if (discr_ordersp[min_order_elem] < discr_order)
2816 large_elem = min_order_elem;
2817
2818 // constrained order, master edge -- need extra fake nodes
2819 if (large_elem < 0)
2820 {
2821 // assert((edge.order < 2 || edge.global_ids.size() > 0) && edge.slaves.size() > 0);
2822 need_extra_fake_nodes = true;
2823 }
2824 }
2825 else
2826 assert(false);
2827
2828 assert(large_elem >= 0 || need_extra_fake_nodes);
2829 Eigen::MatrixXd lnodes;
2830 autogen::p_nodes_3d(discr_order, lnodes);
2831 Eigen::MatrixXd local_position = lnodes.row(j);
2832 if (need_extra_fake_nodes)
2833 {
2834 Eigen::MatrixXd global_position, edge_verts(2, 3);
2835 Eigen::VectorXd point_weight;
2836
2837 edge_verts.row(0) = ncmesh.point(ncmesh.edge_vertex(edge_id, 0));
2838 edge_verts.row(1) = ncmesh.point(ncmesh.edge_vertex(edge_id, 1));
2839
2840 local_to_global(verts, local_position, global_position);
2841 global_to_local_edge(edge_verts, global_position, point_weight);
2842
2843 std::function<double(const int, const int, const double)> basis_1d = [](const int order, const int id, const double x) -> double {
2844 assert(id <= order && id >= 0);
2845 double y = 1;
2846 for (int o = 0; o <= order; o++)
2847 {
2848 if (o != id)
2849 y *= (x * order - o) / (id - o);
2850 }
2851 return y;
2852 };
2853
2854 // contribution to edge nodes
2855 for (int i = 0; i < edge_virtual_nodes[edge_id].size(); i++)
2856 {
2857 const int global_index = edge_virtual_nodes[edge_id][i];
2858 // const double weight = basis_1d(edge_orders[edge_id], i+1, edge_weight);
2859 Eigen::VectorXd node_weight;
2860 global_to_local_edge(edge_verts, nodes.node_position(global_index), node_weight);
2861 const int basis_id = std::lround(node_weight(0) * edge_orders[edge_id]);
2862 const double weight = basis_1d(edge_orders[edge_id], basis_id, point_weight(0));
2863 if (std::abs(weight) < 1e-12)
2864 continue;
2865 b.bases[j].global().emplace_back(global_index, nodes.node_position(global_index), weight);
2866 }
2867
2868 // contribution to vertex nodes
2869 for (int i = 0; i < 2; i++)
2870 {
2871 const int lv = ev(local_edge_id, i);
2872 const auto &global_ = b.bases[lv].global();
2873 Eigen::VectorXd node_weight;
2874 global_to_local_edge(edge_verts, verts.row(lv), node_weight);
2875 const int basis_id = std::lround(node_weight(0) * edge_orders[edge_id]);
2876 const double weight = basis_1d(edge_orders[edge_id], basis_id, point_weight(0));
2877 if (std::abs(weight) > 1e-12)
2878 {
2879 assert(global_.size() > 0);
2880 for (size_t ii = 0; ii < global_.size(); ++ii)
2881 b.bases[j].global().emplace_back(global_[ii].index, global_[ii].node, weight * global_[ii].val);
2882 }
2883 }
2884 }
2885 else
2886 {
2887 Eigen::MatrixXd global_position, large_elem_verts(4, 3);
2888 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
2889 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
2890 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
2891 local_to_global(verts, local_position, global_position);
2892 global_to_local(large_elem_verts, global_position, local_position);
2893
2894 // evaluate the basis of the large element at this node
2895 const auto &other_bases = bases[large_elem];
2896 std::vector<AssemblyValues> w;
2897 other_bases.evaluate_bases(local_position, w);
2898
2899 // apply basis projection
2900 for (long i = 0; i < w.size(); ++i)
2901 {
2902 assert(w[i].val.size() == 1);
2903 if (std::abs(w[i].val(0)) < 1e-12)
2904 continue;
2905
2906 assert(other_bases.bases[i].global().size() > 0);
2907 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
2908 {
2909 const auto &other_global = other_bases.bases[i].global()[ii];
2910 assert(other_global.index >= 0);
2911 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
2912 }
2913 }
2914 }
2915 }
2916 // face node - slave face / constrained order
2917 else if (j < 4 + 6 * n_edge_nodes + 4 * n_face_nodes)
2918 {
2919 const int local_face_id = (j - (4 + 6 * n_edge_nodes)) / n_face_nodes;
2920 const int face_id = cell_faces(local_face_id).face;
2921 int large_elem = -1;
2922 bool need_extra_fake_nodes = false;
2923
2924 std::vector<int> ids;
2925 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_face(face_id), ids);
2926
2927 Eigen::MatrixXd face_verts(3, 3);
2928 for (int i = 0; i < ncmesh.n_face_vertices(face_id); i++)
2929 face_verts.row(i) = ncmesh.point(ncmesh.face_vertex(face_id, i));
2930
2931 // slave face
2932 if (ncmesh.leader_face_of_face(face_id) >= 0)
2933 {
2934 assert(ids.size() == 1);
2935 large_elem = ids[0];
2936 }
2937 // constrained order, conforming face
2938
2939 else if (face_orders[face_id] < discr_order && ids.size() == 2)
2940 {
2941 large_elem = ids[0] == e ? ids[1] : ids[0];
2942 }
2943 // constrained order, master face -- need extra fake nodes
2944 else if (face_orders[face_id] < discr_order && ncmesh.n_follower_faces(face_id) > 0)
2945 {
2946 // assert(ncface.global_ids.size() > 0 || ncface.order < 3);
2947 need_extra_fake_nodes = true;
2948 }
2949 else
2950 assert(false);
2951
2952 assert(large_elem >= 0 || need_extra_fake_nodes);
2953 Eigen::MatrixXd lnodes;
2954 autogen::p_nodes_3d(discr_order, lnodes);
2955 Eigen::MatrixXd local_position = lnodes.row(j);
2956 if (need_extra_fake_nodes)
2957 {
2958 Eigen::MatrixXd global_position;
2959 local_to_global(verts, local_position, global_position);
2960
2961 Eigen::MatrixXd tmp;
2962 global_to_local_face(face_verts, global_position, tmp);
2963 Eigen::VectorXd face_weight = tmp.transpose();
2964
2965 std::function<double(const int, const int, const double)> basis_aux = [](const int order, const int id, const double x) -> double {
2966 assert(id <= order && id >= 0);
2967 double y = 1;
2968 for (int o = 0; o < id; o++)
2969 y *= (x * order - o) / (id - o);
2970 return y;
2971 };
2972
2973 std::function<double(const int, const int, const int, const Eigen::Vector2d)> basis_2d = [&basis_aux](const int order, const int i, const int j, const Eigen::Vector2d uv) -> double {
2974 assert(i + j <= order && i >= 0 && j >= 0);
2975 double u = uv(0), v = uv(1);
2976 return basis_aux(order, i, u) * basis_aux(order, j, v) * basis_aux(order, order - i - j, 1 - u - v);
2977 };
2978
2979 // contribution to face nodes
2980 for (int global_ : face_virtual_nodes[face_id])
2981 {
2982 auto low_order_node = nodes.node_position(global_);
2983 Eigen::MatrixXd low_order_node_face_weight;
2984 global_to_local_face(face_verts, low_order_node, low_order_node_face_weight);
2985 int x = round(low_order_node_face_weight(0) * face_orders[face_id]), y = round(low_order_node_face_weight(1) * face_orders[face_id]);
2986 const double weight = basis_2d(face_orders[face_id], x, y, face_weight);
2987 if (std::abs(weight) < 1e-12)
2988 continue;
2989 b.bases[j].global().emplace_back(global_, nodes.node_position(global_), weight);
2990 }
2991
2992 // contribution to vertex nodes
2993 for (int i = 0; i < 3; i++)
2994 {
2995 const auto &global_ = b.bases[fv(local_face_id, i)].global();
2996 auto low_order_node = ncmesh.point(fv(local_face_id, i));
2997 Eigen::MatrixXd low_order_node_face_weight;
2998 global_to_local_face(face_verts, low_order_node, low_order_node_face_weight);
2999 int x = round(low_order_node_face_weight(0) * face_orders[face_id]), y = round(low_order_node_face_weight(1) * face_orders[face_id]);
3000 double weight = basis_2d(face_orders[face_id], x, y, face_weight);
3001 if (std::abs(weight) > 1e-12)
3002 {
3003 assert(global_.size() > 0);
3004 for (size_t ii = 0; ii < global_.size(); ++ii)
3005 b.bases[j].global().emplace_back(global_[ii].index, global_[ii].node, weight * global_[ii].val);
3006 }
3007 }
3008
3009 // contribution to edge nodes, two steps
3010 for (int x = 0, idx = 0; x <= face_orders[face_id]; x++)
3011 {
3012 for (int y = 0; x + y <= face_orders[face_id]; y++)
3013 {
3014 const int z = face_orders[face_id] - x - y;
3015 int flag = (int)(x == 0) + (int)(y == 0) + (int)(z == 0);
3016 if (flag != 1)
3017 continue;
3018
3019 // first step
3020 const double weight = basis_2d(face_orders[face_id], x, y, face_weight);
3021 if (std::abs(weight) < 1e-12)
3022 continue;
3023 Eigen::MatrixXd face_weight(1, 2);
3024 face_weight << (double)x / face_orders[face_id], (double)y / face_orders[face_id];
3025 Eigen::MatrixXd pos, local_pos;
3026 local_to_global_face(face_verts, face_weight, pos);
3027 global_to_local(verts, pos, local_pos);
3028 Local2Global step1(idx, local_pos, weight);
3029 idx++;
3030
3031 {
3032 // evaluate the basis of the large element at this node
3033 const auto &other_bases = bases[e];
3034 std::vector<AssemblyValues> w;
3035 other_bases.evaluate_bases(local_pos, w);
3036
3037 // apply basis projection
3038 for (long i = 0; i < w.size(); ++i)
3039 {
3040 assert(w[i].val.size() == 1);
3041 if (std::abs(w[i].val(0)) < 1e-12)
3042 continue;
3043
3044 assert(other_bases.bases[i].global().size() > 0);
3045 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3046 {
3047 const auto &other_global = other_bases.bases[i].global()[ii];
3048 assert(other_global.index >= 0);
3049 b.bases[j].global().emplace_back(other_global.index, other_global.node, step1.val * w[i].val(0) * other_global.val);
3050 }
3051 }
3052 }
3053 }
3054 }
3055 }
3056 else
3057 {
3058 Eigen::MatrixXd global_position, large_elem_verts(4, 3);
3059 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
3060 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
3061 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
3062 local_to_global(verts, local_position, global_position);
3063 global_to_local(large_elem_verts, global_position, local_position);
3064
3065 // evaluate the basis of the large element at this node
3066 const auto &other_bases = bases[large_elem];
3067 std::vector<AssemblyValues> w;
3068 other_bases.evaluate_bases(local_position, w);
3069
3070 // apply basis projection
3071 for (long i = 0; i < w.size(); ++i)
3072 {
3073 assert(w[i].val.size() == 1);
3074 if (std::abs(w[i].val(0)) < 1e-12)
3075 continue;
3076
3077 assert(other_bases.bases[i].global().size() > 0);
3078 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3079 {
3080 const auto &other_global = other_bases.bases[i].global()[ii];
3081 assert(other_global.index >= 0);
3082 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3083 }
3084 }
3085 }
3086 }
3087 else
3088 assert(false);
3089
3090 auto &global_ = b.bases[j].global();
3091 if (global_.size() <= 1)
3092 continue;
3093
3094 std::map<int, Local2Global> list;
3095 for (size_t ii = 0; ii < global_.size(); ii++)
3096 {
3097 auto pair = list.insert({global_[ii].index, global_[ii]});
3098 if (!pair.second && pair.first != list.end())
3099 {
3100 assert((pair.first->second.node - global_[ii].node).norm() < 1e-12);
3101 pair.first->second.val += global_[ii].val;
3102 }
3103 }
3104
3105 global_.clear();
3106 for (auto it = list.begin(); it != list.end(); ++it)
3107 {
3108 if (std::abs(it->second.val) > 1e-12)
3109 {
3110 global_.push_back(it->second);
3111 }
3112 }
3113 }
3114 }
3115 }
3116 });
3117 }
3118 }
3119 else
3120 {
3121 for (int pp = 2; pp <= autogen::MAX_P_BASES; ++pp)
3122 {
3123 for (int e : interface_elements)
3124 {
3125 ElementBases &b = bases[e];
3126 // todo non conforming
3127 const int discr_order = discr_ordersp(e);
3128 const int n_el_bases = element_nodes_id[e].size();
3129 assert(discr_order > 1);
3130 if (discr_order != pp)
3131 continue;
3132
3133 if (mesh.is_cube(e) || mesh.is_prism(e))
3134 {
3135 // TODO
3136 assert(false);
3137 }
3138 else if (mesh.is_simplex(e))
3139 {
3140 for (int j = 0; j < n_el_bases; ++j)
3141 {
3142 const int global_index = element_nodes_id[e][j];
3143
3144 if (global_index >= 0)
3145 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
3146 else
3147 {
3148 const int lnn = max_p > 2 ? (discr_order - 2) : 0;
3149 const int ln_edge_nodes = discr_order - 1;
3150 const int ln_face_nodes = lnn * (lnn + 1) / 2;
3151
3152 const auto v = tet_vertices_local_to_global(mesh, e);
3153 Navigation3D::Index index;
3154 if (global_index <= -30)
3155 {
3156 assert(false);
3157 // const auto lv = -(global_index + 30);
3158 // assert(lv>=0 && lv < 4);
3159 // assert(j < 4);
3160
3161 // if(lv == 3)
3162 // {
3163 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[0]));
3164 // if(index.element < 0)
3165 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[1]));
3166 // if(index.element < 0)
3167 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[2]));
3168 // }
3169 // else
3170 // {
3171 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[(lv+1)%3]));
3172 // if(index.element < 0)
3173 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[(lv+2)%3]));
3174 // if(index.element < 0)
3175 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[3]));
3176 // }
3177 }
3178 else if (global_index <= -10)
3179 {
3180 const auto le = -(global_index + 10);
3181 assert(le >= 0 && le < 6);
3182 assert(j >= 4 && j < 4 + 6 * ln_edge_nodes);
3183
3184 Eigen::Matrix<int, 6, 2> ev;
3185 ev.row(0) << v[0], v[1];
3186 ev.row(1) << v[1], v[2];
3187 ev.row(2) << v[2], v[0];
3188
3189 ev.row(3) << v[0], v[3];
3190 ev.row(4) << v[1], v[3];
3191 ev.row(5) << v[2], v[3];
3192
3193 // const auto edge_index = find_edge(mesh, e, ev(le, 0), ev(le, 1));
3194 const auto edge_index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
3195 auto neighs = mesh.edge_neighs(edge_index.edge);
3196 int min_p = discr_order;
3197 int min_cell = edge_index.element;
3198
3199 for (auto cid : neighs)
3200 {
3201 if (discr_ordersp[cid] < min_p)
3202 {
3203 min_p = discr_ordersp[cid];
3204 min_cell = cid;
3205 }
3206 }
3207
3208 bool found = false;
3209 for (int lf = 0; lf < 4; ++lf)
3210 {
3211 for (int lv = 0; lv < 4; ++lv)
3212 {
3213 index = mesh.get_index_from_element(min_cell, lf, lv);
3214
3215 if (index.vertex == edge_index.vertex)
3216 {
3217 if (index.edge != edge_index.edge)
3218 {
3219 auto tmp = index;
3220 index = mesh.switch_edge(tmp);
3221
3222 if (index.edge != edge_index.edge)
3223 {
3224 index = mesh.switch_edge(mesh.switch_face(tmp));
3225 }
3226 }
3227 found = true;
3228 break;
3229 }
3230 }
3231
3232 if (found)
3233 break;
3234 }
3235
3236 assert(found);
3237 assert(index.vertex == edge_index.vertex && index.edge == edge_index.edge);
3238 assert(index.element != edge_index.element);
3239 }
3240 else
3241 {
3242 const auto lf = -(global_index + 1);
3243 assert(lf >= 0 && lf < 4);
3244 assert(j >= 4 + 6 * ln_edge_nodes && j < 4 + 6 * ln_edge_nodes + 4 * ln_face_nodes);
3245
3246 Eigen::Matrix<int, 4, 3> fv;
3247 fv.row(0) << v[0], v[1], v[2];
3248 fv.row(1) << v[0], v[1], v[3];
3249 fv.row(2) << v[1], v[2], v[3];
3250 fv.row(3) << v[2], v[0], v[3];
3251
3252 index = mesh.switch_element(mesh.get_index_from_element_face(e, fv(lf, 0), fv(lf, 1), fv(lf, 2)));
3253 }
3254
3255 const auto other_cell = index.element;
3256 assert(other_cell >= 0);
3257 assert(discr_order > discr_ordersp(other_cell));
3258
3259 auto indices = tet_face_local_nodes(discr_order, mesh, index);
3260 Eigen::MatrixXd lnodes;
3261 autogen::p_nodes_3d(discr_order, lnodes);
3262 Eigen::RowVector3d node_position; // = lnodes.row(indices(ii));
3263
3264 if (j < 4)
3265 node_position = lnodes.row(indices(0));
3266 else if (j < 4 + 6 * ln_edge_nodes)
3267 node_position = lnodes.row(indices(((j - 4) % ln_edge_nodes) + 3));
3268 else if (j < 4 + 6 * ln_edge_nodes + 4 * ln_face_nodes)
3269 {
3270 // node_position = lnodes.row(indices(((j - 4 - 6*ln_edge_nodes) % ln_face_nodes) + 3 + 3*ln_edge_nodes));
3271 auto me_indices = tet_face_local_nodes(discr_order, mesh, mesh.switch_element(index));
3272 int ii;
3273 for (ii = 0; ii < me_indices.size(); ++ii)
3274 {
3275 if (me_indices(ii) == j)
3276 break;
3277 }
3278
3279 assert(ii >= 3 + 3 * ln_edge_nodes);
3280 assert(ii < me_indices.size());
3281
3282 node_position = lnodes.row(indices(ii));
3283 }
3284 else
3285 assert(false);
3286
3287 const auto &other_bases = bases[other_cell];
3288 // Eigen::MatrixXd w;
3289 std::vector<AssemblyValues> w;
3290 other_bases.evaluate_bases(node_position, w);
3291
3292 assert(b.bases[j].global().size() == 0);
3293
3294 for (long i = 0; i < w.size(); ++i)
3295 {
3296 assert(w[i].val.size() == 1);
3297 if (std::abs(w[i].val(0)) < 1e-8)
3298 continue;
3299
3300 // assert(other_bases.bases[i].global().size() == 1);
3301 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3302 {
3303 const auto &other_global = other_bases.bases[i].global()[ii];
3304 // logger().trace("e {} j {} gid {}", e, j, other_global.index);
3305 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3306 }
3307 }
3308 }
3309 }
3310 }
3311 else if (mesh.is_pyramid(e))
3312 {
3313 // TODO
3314 assert(false);
3315 }
3316 else
3317 {
3318 // Polygon bases are built later on
3319 }
3320 }
3321 }
3322 }
3323 }
3324
3325 return nodes.n_nodes();
3326}
Eigen::MatrixXd vec
Definition Assembler.cpp:72
double val
Definition Assembler.cpp:86
int x
static int quadrature_order(const std::string &assembler, const int basis_degree, const BasisType &b_type, const int dim)
utility for retrieving the needed quadrature order to precisely integrate the given form on the given...
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
static Eigen::VectorXi pyramid_face_local_nodes(const int p, const mesh::Mesh3D &mesh, mesh::Navigation3D::Index index)
static Eigen::VectorXi hex_face_local_nodes(const bool serendipity, const int q, const mesh::Mesh3D &mesh, mesh::Navigation3D::Index index)
static Eigen::VectorXi prism_face_local_nodes(const int p, const int q, const mesh::Mesh3D &mesh, mesh::Navigation3D::Index index)
static int build_bases(const mesh::Mesh3D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, const int discr_orderp, const int discr_orderq, const bool bernstein, const bool serendipity, const bool has_polys, const bool is_geom_bases, const bool use_corner_quadrature, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_face_to_data, std::shared_ptr< mesh::MeshNodes > &mesh_nodes)
Builds FE basis functions over the entire mesh (P1, P2 over tets, Q1, Q2 over hes).
static Eigen::VectorXi tet_face_local_nodes(const int p, const mesh::Mesh3D &mesh, mesh::Navigation3D::Index index)
Boundary primitive IDs for a single element.
virtual Navigation3D::Index get_index_from_element(int hi, int lf, int lv) const =0
virtual Navigation3D::Index switch_element(Navigation3D::Index idx) const =0
virtual Navigation3D::Index get_index_from_element_edge(int hi, int v0, int v1) const =0
virtual int n_cell_faces(const int c_id) const =0
virtual std::vector< uint32_t > edge_neighs(const int e_gid) const =0
bool is_volume() const override
checks if mesh is volume
Definition Mesh3D.hpp:28
virtual Navigation3D::Index next_around_face(Navigation3D::Index idx) const =0
virtual Navigation3D::Index get_index_from_element_face(int hi, int v0, int v1, int v2) const =0
virtual Navigation3D::Index switch_vertex(Navigation3D::Index idx) const =0
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:41
bool is_polytope(const int el_id) const
checks if element is polygon compatible
Definition Mesh.cpp:365
bool is_rational() const
check if curved mesh has rational polynomials elements
Definition Mesh.hpp:289
virtual bool is_conforming() const =0
if the mesh is conforming
bool is_cube(const int el_id) const
checks if element is cube compatible
Definition Mesh.cpp:352
virtual bool is_boundary_face(const int face_global_id) const =0
is face boundary
virtual int get_boundary_id(const int primitive) const
Get the boundary selection of an element (face in 3d, edge in 2d)
Definition Mesh.hpp:488
bool is_simplex(const int el_id) const
checks if element is simplex
Definition Mesh.cpp:422
bool is_prism(const int el_id) const
checks if element is a prism
Definition Mesh.cpp:427
const std::vector< double > & cell_weights(const int cell_index) const
weights for rational polynomial meshes
Definition Mesh.hpp:563
virtual int n_cells() const =0
number of cells
virtual int n_faces() const =0
number of faces
bool is_pyramid(const int el_id) const
checks if element is a pyramid
Definition Mesh.cpp:432
virtual int n_face_vertices(const int f_id) const =0
number of vertices of a face
int n_faces() const override
number of faces
Definition NCMesh3D.hpp:189
int face_edge(const int f_id, const int le_id) const
Definition NCMesh3D.cpp:18
int n_cell_faces(const int c_id) const override
Definition NCMesh3D.hpp:219
int n_edge_cells(const int e_id) const
Definition NCMesh3D.hpp:216
int n_cells() const override
number of cells
Definition NCMesh3D.hpp:188
int n_edges() const override
number of edges
Definition NCMesh3D.hpp:197
int n_face_vertices(const int f_id) const override
number of vertices of a face
Definition NCMesh3D.hpp:214
std::vector< uint32_t > edge_neighs(const int e_gid) const override
Definition NCMesh3D.cpp:446
int leader_face_of_edge(const int e_id) const
Definition NCMesh3D.hpp:298
int cell_face(const int c_id, const int lf_id) const override
Definition NCMesh3D.hpp:221
int leader_edge_of_edge(const int e_id) const
Definition NCMesh3D.hpp:286
int n_cell_edges(const int c_id) const override
Definition NCMesh3D.hpp:218
int cell_edge(const int c_id, const int le_id) const override
Definition NCMesh3D.hpp:222
int leader_face_of_face(const int f_id) const
Definition NCMesh3D.hpp:304
int n_face_cells(const int f_id) const
Definition NCMesh3D.hpp:215
void get_quadrature(const int order, Quadrature &quad)
void get_quadrature(const int order, const int order_h, Quadrature &quad)
void get_quadrature(const int order, Quadrature &quad)
list tmp
Definition p_bases.py:365
str nodes
Definition p_bases.py:398
list indices
Definition p_bases.py:258
Used for test only.
void q_grad_basis_value_3d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void prism_basis_value_3d(const int p, const int q, const int li, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void pyramid_grad_basis_value_3d(const int pyramid, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void pyramid_basis_value_3d(const int pyramid, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void prism_grad_basis_value_3d(const int p, const int q, const int li, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void prism_nodes_3d(const int p, const int q, Eigen::MatrixXd &val)
void p_grad_basis_value_3d(const bool bernstein, const int p, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void p_nodes_3d(const int p, Eigen::MatrixXd &val)
void p_basis_value_3d(const bool bernstein, const int p, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void q_nodes_3d(const int q, Eigen::MatrixXd &val)
void q_basis_value_3d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void maybe_parallel_for(int size, const std::function< void(int, int, int)> &partial_for)
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
std::vector< int > local_indices