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 prism_edge_order(int cid, int edge_id,
228 const Eigen::VectorXi &discr_ordersp, const Eigen::VectorXi &discr_ordersq,
229 const Mesh3D &mesh)
230 {
231 if (!mesh.is_prism(cid))
232 return discr_ordersp(cid);
233
234 const auto pv = prism_vertices_local_to_global(mesh, cid);
235
236 Eigen::Matrix<int, 9, 2> pev;
237 pev.row(0) << pv[0], pv[1];
238 pev.row(1) << pv[1], pv[2];
239 pev.row(2) << pv[2], pv[0];
240 pev.row(3) << pv[3], pv[4];
241 pev.row(4) << pv[4], pv[5];
242 pev.row(5) << pv[5], pv[3];
243 pev.row(6) << pv[0], pv[3];
244 pev.row(7) << pv[1], pv[4];
245 pev.row(8) << pv[2], pv[5];
246
247 for (int le = 0; le < 9; ++le)
248 {
249 const auto pidx = mesh.get_index_from_element_edge(cid, pev(le, 0), pev(le, 1));
250 if (pidx.edge == edge_id)
251 return le < 6 ? discr_ordersp(cid) : discr_ordersq(cid);
252 }
253
254 assert(false);
255 return discr_ordersp(cid);
256 };
257
258 int lowest_order_elem_on_edge(const polyfem::mesh::NCMesh3D &mesh, const Eigen::VectorXi &discr_orders, const int eid)
259 {
260 auto elem_list = mesh.edge_neighs(eid);
261 int min = std::numeric_limits<int>::max();
262 int elem = -1;
263 for (const auto e : elem_list)
264 if (discr_orders[e] < min)
265 elem = e;
266 return elem;
267 }
268
269 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)
270 {
271 const int n_edge_nodes = p > 1 ? ((p - 1) * 6) : 0;
272 const int nn = p > 2 ? (p - 2) : 0;
273 const int n_loc_f = (nn * (nn + 1) / 2);
274 const int n_face_nodes = n_loc_f * 4;
275 int n_cell_nodes = 0;
276 for (int pp = 4; pp <= p; ++pp)
277 n_cell_nodes += ((pp - 3) * ((pp - 3) + 1) / 2);
278
279 if (p == 0)
280 {
281 res.push_back(nodes.node_id_from_cell(c));
282 return;
283 }
284
285 // std::vector<int> res;
286 res.reserve(4 + n_edge_nodes + n_face_nodes + n_cell_nodes);
287
288 // Edge nodes
289 Eigen::Matrix<Navigation3D::Index, 4, 1> f;
290
291 auto v = tet_vertices_local_to_global(mesh, c);
292 Eigen::Matrix<int, 4, 3> fv;
293 fv.row(0) << v[0], v[1], v[2];
294 fv.row(1) << v[0], v[1], v[3];
295 fv.row(2) << v[1], v[2], v[3];
296 fv.row(3) << v[2], v[0], v[3];
297
298 for (long lf = 0; lf < fv.rows(); ++lf)
299 {
300 const auto index = mesh.get_index_from_element_face(c, fv(lf, 0), fv(lf, 1), fv(lf, 2));
301 f[lf] = index;
302 }
303
304 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
305 Eigen::Matrix<int, 6, 2> ev;
306 ev.row(0) << v[0], v[1];
307 ev.row(1) << v[1], v[2];
308 ev.row(2) << v[2], v[0];
309
310 ev.row(3) << v[0], v[3];
311 ev.row(4) << v[1], v[3];
312 ev.row(5) << v[2], v[3];
313
314 for (int le = 0; le < e.rows(); ++le)
315 {
316 // const auto index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
317 const auto index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
318 e[le] = index;
319 }
320
321 // vertices
322 for (size_t lv = 0; lv < v.size(); ++lv)
323 {
324 if (!mesh.is_conforming() && !is_geom_bases)
325 {
326 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
327 // hanging vertex
328 if (ncmesh.leader_edge_of_vertex(v[lv]) >= 0 || ncmesh.leader_face_of_vertex(v[lv]) >= 0)
329 res.push_back(-lv - 1);
330 else
331 res.push_back(nodes.node_id_from_primitive(v[lv]));
332 }
333 else
334 res.push_back(nodes.node_id_from_primitive(v[lv]));
335 }
336
337 // Edges
338 for (int le = 0; le < e.rows(); ++le)
339 {
340 const auto index = e[le];
341 auto neighs = mesh.edge_neighs(index.edge);
342 int min_p = discr_order.size() > 0 ? discr_order(c) : 0;
343
344 if (is_geom_bases)
345 {
346 auto node_ids = nodes.node_ids_from_edge(index, p - 1);
347 res.insert(res.end(), node_ids.begin(), node_ids.end());
348 }
349 else
350 {
351 if (!mesh.is_conforming() && !is_geom_bases)
352 {
353 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
354 // slave edge
355 if (ncmesh.leader_edge_of_edge(index.edge) >= 0 || ncmesh.leader_face_of_edge(index.edge) >= 0)
356 {
357 for (int tmp = 0; tmp < p - 1; ++tmp)
358 res.push_back(-le - 1);
359 }
360 // master or conforming edge with constrained order
361 else if (edge_orders[index.edge] < discr_order(c))
362 {
363 for (int tmp = 0; tmp < p - 1; ++tmp)
364 res.push_back(-le - 1);
365
366 int min_order_elem = lowest_order_elem_on_edge(ncmesh, discr_order, index.edge);
367 // master edge, add extra nodes
368 if (min_order_elem == c)
369 edge_virtual_nodes[index.edge] = nodes.node_ids_from_edge(index, edge_orders[index.edge] - 1);
370 }
371 else
372 {
373 auto node_ids = nodes.node_ids_from_edge(index, p - 1);
374 res.insert(res.end(), node_ids.begin(), node_ids.end());
375 }
376 }
377 else
378 {
379 for (auto cid : neighs)
380 {
381 min_p = std::min(min_p, discr_order.size() > 0 ? discr_order(cid) : 0);
382 }
383
384 if (discr_order.size() > 0 && discr_order(c) > min_p)
385 {
386 for (int tmp = 0; tmp < p - 1; ++tmp)
387 res.push_back(-le - 10);
388 }
389 else
390 {
391 auto node_ids = nodes.node_ids_from_edge(index, p - 1);
392 res.insert(res.end(), node_ids.begin(), node_ids.end());
393 }
394 }
395 }
396 }
397
398 // faces
399 for (int lf = 0; lf < f.rows(); ++lf)
400 {
401 const auto index = f[lf];
402 const auto other_cell = mesh.switch_element(index).element;
403
404 const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
405
406 if (is_geom_bases)
407 {
408 auto node_ids = nodes.node_ids_from_face(index, p - 2);
409 res.insert(res.end(), node_ids.begin(), node_ids.end());
410 }
411 else
412 {
413 if (!mesh.is_conforming() && !is_geom_bases)
414 {
415 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
416 // slave face
417 if (ncmesh.leader_face_of_face(index.face) >= 0)
418 {
419 for (int tmp = 0; tmp < n_loc_f; ++tmp)
420 res.push_back(-lf - 1);
421 }
422 // master face or conforming face with constrained order
423 else if (face_orders[index.face] < discr_order[c])
424 {
425 for (int tmp = 0; tmp < n_loc_f; ++tmp)
426 res.push_back(-lf - 1);
427 // master face
428 if (ncmesh.n_follower_faces(index.face) > 0 && face_orders[index.face] > 2)
429 face_virtual_nodes[index.face] = nodes.node_ids_from_face(index, face_orders[index.face] - 2);
430 }
431 else
432 {
433 auto node_ids = nodes.node_ids_from_face(index, p - 2);
434 res.insert(res.end(), node_ids.begin(), node_ids.end());
435 }
436 }
437 else
438 {
439 if (skip_other)
440 {
441 for (int tmp = 0; tmp < n_loc_f; ++tmp)
442 res.push_back(-lf - 1);
443 }
444 else
445 {
446 auto node_ids = nodes.node_ids_from_face(index, p - 2);
447 res.insert(res.end(), node_ids.begin(), node_ids.end());
448 }
449 }
450 }
451 }
452
453 // cells
454 if (n_cell_nodes > 0)
455 {
456 const auto index = f[0];
457
458 auto node_ids = nodes.node_ids_from_cell(index, p - 3);
459 res.insert(res.end(), node_ids.begin(), node_ids.end());
460 }
461
462 assert(res.size() == size_t(4 + n_edge_nodes + n_face_nodes + n_cell_nodes));
463 }
464
465 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)
466 {
467 assert(mesh.is_cube(c));
468
469 const int n_edge_nodes = ((q - 1) * 12);
470 const int nn = (q - 1);
471 const int n_loc_f = serendipity ? 0 : (nn * nn);
472 const int n_face_nodes = serendipity ? 0 : (n_loc_f * 6);
473 const int n_cell_nodes = serendipity ? 0 : (nn * nn * nn);
474
475 if (q == 0)
476 {
477 res.push_back(nodes.node_id_from_cell(c));
478 return;
479 }
480
481 // std::vector<int> res;
482 res.reserve(8 + n_edge_nodes + n_face_nodes + n_cell_nodes);
483
484 // Vertex nodes
485 auto v = hex_vertices_local_to_global(mesh, c);
486
487 // Edge nodes
488 Eigen::Matrix<Navigation3D::Index, 12, 1> e;
489 Eigen::Matrix<int, 12, 2> ev;
490 ev.row(0) << v[0], v[1];
491 ev.row(1) << v[1], v[2];
492 ev.row(2) << v[2], v[3];
493 ev.row(3) << v[3], v[0];
494 ev.row(4) << v[0], v[4];
495 ev.row(5) << v[1], v[5];
496 ev.row(6) << v[2], v[6];
497 ev.row(7) << v[3], v[7];
498 ev.row(8) << v[4], v[5];
499 ev.row(9) << v[5], v[6];
500 ev.row(10) << v[6], v[7];
501 ev.row(11) << v[7], v[4];
502 for (int le = 0; le < e.rows(); ++le)
503 {
504 // e[le] = find_edge(mesh, c, ev(le, 0), ev(le, 1)).edge;
505 e[le] = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
506 }
507
508 // Face nodes
509 Eigen::Matrix<Navigation3D::Index, 6, 1> f;
510 Eigen::Matrix<int, 6, 4> fv;
511 fv.row(0) << v[0], v[3], v[4], v[7];
512 fv.row(1) << v[1], v[2], v[5], v[6];
513 fv.row(2) << v[0], v[1], v[5], v[4];
514 fv.row(3) << v[3], v[2], v[6], v[7];
515 fv.row(4) << v[0], v[1], v[2], v[3];
516 fv.row(5) << v[4], v[5], v[6], v[7];
517 for (int lf = 0; lf < f.rows(); ++lf)
518 {
519 const auto index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
520 f[lf] = index;
521 }
522
523 // vertices
524 for (size_t lv = 0; lv < v.size(); ++lv)
525 {
526 res.push_back(nodes.node_id_from_primitive(v[lv]));
527 }
528 assert(res.size() == size_t(8));
529
530 // Edges
531 for (int le = 0; le < e.rows(); ++le)
532 {
533 const auto index = e[le];
534 auto neighs = mesh.edge_neighs(index.edge);
535 int min_q = discr_order.size() > 0 ? discr_order(c) : 0;
536
537 for (auto cid : neighs)
538 {
539 min_q = std::min(min_q, discr_order.size() > 0 ? discr_order(cid) : 0);
540 }
541
542 if (discr_order.size() > 0 && discr_order(c) > min_q)
543 {
544 for (int tmp = 0; tmp < q - 1; ++tmp)
545 res.push_back(-le - 10);
546 }
547 else
548 {
549 auto node_ids = nodes.node_ids_from_edge(index, q - 1);
550 res.insert(res.end(), node_ids.begin(), node_ids.end());
551 }
552 }
553 assert(res.size() == size_t(8 + n_edge_nodes));
554
555 // faces
556 for (int lf = 0; lf < f.rows(); ++lf)
557 {
558 const auto index = f[lf];
559 const auto other_cell = mesh.switch_element(index).element;
560
561 const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
562
563 if (skip_other)
564 {
565 for (int tmp = 0; tmp < n_loc_f; ++tmp)
566 res.push_back(-lf - 1);
567 }
568 else
569 {
570 auto node_ids = nodes.node_ids_from_face(index, serendipity ? 0 : (q - 1));
571 assert(node_ids.size() == n_loc_f);
572 res.insert(res.end(), node_ids.begin(), node_ids.end());
573 }
574 }
575 assert(res.size() == size_t(8 + n_edge_nodes + n_face_nodes));
576
577 // cells
578 if (n_cell_nodes > 0)
579 {
580 const auto index = f[0];
581
582 auto node_ids = nodes.node_ids_from_cell(index, q - 1);
583 res.insert(res.end(), node_ids.begin(), node_ids.end());
584 }
585
586 assert(res.size() == size_t(8 + n_edge_nodes + n_face_nodes + n_cell_nodes));
587 }
588
589 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)
590 {
591 assert(mesh.is_prism(c));
592
593 const int n_edge_nodest = p > 1 ? ((p - 1) * 3) : 0;
594 const int nnt = p > 2 ? (p - 2) : 0;
595 const int n_face_nodest = nnt * (nnt + 1) / 2;
596
597 const int nnq = q > 1 ? (q - 1) : 0;
598 const int n_face_nodesq = nnq * n_edge_nodest / 3;
599
600 const int n_edge_nodes = n_edge_nodest * 2 + nnq * 3;
601 const int n_face_nodes = n_face_nodest * 2 + n_face_nodesq * 3;
602 const int n_cell_nodes = n_face_nodest * nnq;
603
604 if (p == 0 && q == 0)
605 {
606 res.push_back(nodes.node_id_from_cell(c));
607 return;
608 }
609
610 res.reserve(6 + n_edge_nodes + n_face_nodes + n_cell_nodes);
611
612 // Vertex nodes
613 auto v = prism_vertices_local_to_global(mesh, c);
614
615 // Edge nodes
616 Eigen::Matrix<Navigation3D::Index, 9, 1> e;
617 Eigen::Matrix<int, 9, 2> ev;
618 ev.row(0) << v[0], v[1];
619 ev.row(1) << v[1], v[2];
620 ev.row(2) << v[2], v[0];
621 ev.row(3) << v[3], v[4];
622 ev.row(4) << v[4], v[5];
623 ev.row(5) << v[5], v[3];
624 ev.row(6) << v[0], v[3];
625 ev.row(7) << v[1], v[4];
626 ev.row(8) << v[2], v[5];
627
628 for (int le = 0; le < e.rows(); ++le)
629 {
630 e[le] = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
631 }
632
633 // Face nodes
634 Eigen::Matrix<Navigation3D::Index, 5, 1> f;
635 Eigen::Matrix<int, 2, 3> fvt;
636 fvt.row(0) << v[0], v[1], v[2];
637 fvt.row(1) << v[3], v[4], v[5];
638 for (int lf = 0; lf < fvt.rows(); ++lf)
639 {
640 const auto index = mesh.get_index_from_element_face(c, fvt(lf, 0), fvt(lf, 1), fvt(lf, 2));
641 f[lf] = index;
642 }
643
644 Eigen::Matrix<int, 3, 4> fvq;
645 fvq.row(0) << v[0], v[1], v[4], v[3];
646 fvq.row(1) << v[1], v[2], v[5], v[4];
647 fvq.row(2) << v[2], v[0], v[3], v[5];
648 for (int lf = 0; lf < fvq.rows(); ++lf)
649 {
650 const auto index = find_quad_face(mesh, c, fvq(lf, 0), fvq(lf, 1), fvq(lf, 2), fvq(lf, 3));
651 f[lf + 2] = index;
652 }
653
654 // vertices
655 for (size_t lv = 0; lv < v.size(); ++lv)
656 {
657 res.push_back(nodes.node_id_from_primitive(v[lv]));
658 }
659 assert(res.size() == size_t(6));
660
661 // Edges
662 for (int le = 0; le < e.rows(); ++le)
663 {
664 const auto index = e[le];
665 auto neighs = mesh.edge_neighs(index.edge);
666
667 auto node_ids = nodes.node_ids_from_edge(index, (le < 6 ? p : q) - 1);
668 res.insert(res.end(), node_ids.begin(), node_ids.end());
669 }
670 assert(res.size() == size_t(6 + n_edge_nodes));
671
672 // faces
673 for (int lf = 0; lf < f.rows(); ++lf)
674 {
675 const auto index = f[lf];
676
677 // todo prism, nodes are not necessarly a square
678 auto node_ids = nodes.node_ids_from_face(index, lf < 2 ? (p - 2) : (p - 1), lf < 2 ? -1 : (q - 1));
679 assert((lf < 2 && node_ids.size() == n_face_nodest) || (lf >= 2 && node_ids.size() == n_face_nodesq));
680 // assert(node_ids.size() == n_loc_f);
681 res.insert(res.end(), node_ids.begin(), node_ids.end());
682 }
683 assert(res.size() == size_t(6 + n_edge_nodes + n_face_nodes));
684
685 // cells
686 if (n_cell_nodes > 0)
687 {
688 const auto index = f[0];
689
690 auto node_ids = nodes.node_ids_from_cell(index, q - 1);
691 res.insert(res.end(), node_ids.begin(), node_ids.end());
692 }
693
694 // Prism local-node ordering from MeshNodes primitive traversal can differ
695 // from autogen prism basis local ordering, especially for higher q.
696 // Reorder all local nodes by matching autogen local coordinates in physical space.
697 {
698 Eigen::MatrixXd local_nodes;
699 autogen::prism_nodes_3d(p, q, local_nodes);
700 assert(local_nodes.rows() == (int)res.size());
701
702 auto map_ref_to_phys = [&mesh, &v](const Eigen::RowVector3d &uvw) -> Eigen::RowVector3d {
703 const double u = uvw(0);
704 const double vv = uvw(1);
705 const double w = uvw(2);
706
707 const double N0 = (1.0 - u - vv) * (1.0 - w);
708 const double N1 = u * (1.0 - w);
709 const double N2 = vv * (1.0 - w);
710 const double N3 = (1.0 - u - vv) * w;
711 const double N4 = u * w;
712 const double N5 = vv * w;
713
714 return N0 * mesh.point(v[0]) + N1 * mesh.point(v[1]) + N2 * mesh.point(v[2]) + N3 * mesh.point(v[3]) + N4 * mesh.point(v[4]) + N5 * mesh.point(v[5]);
715 };
716
717 std::vector<int> reordered(res.size(), -1);
718 std::vector<bool> used(res.size(), false);
719
720 for (int i = 0; i < local_nodes.rows(); ++i)
721 {
722 const Eigen::RowVector3d target = map_ref_to_phys(local_nodes.row(i));
723 double best = std::numeric_limits<double>::infinity();
724 int best_j = -1;
725 for (int j = 0; j < (int)res.size(); ++j)
726 {
727 if (used[j])
728 continue;
729 const double d2 = (nodes.node_position(res[j]) - target).squaredNorm();
730 if (d2 < best)
731 {
732 best = d2;
733 best_j = j;
734 }
735 }
736 assert(best_j >= 0);
737 reordered[i] = res[best_j];
738 used[best_j] = true;
739 }
740
741 res.swap(reordered);
742 }
743
744 assert(res.size() == size_t(6 + n_edge_nodes + n_face_nodes + n_cell_nodes));
745 }
746
747 void pyramid_local_to_global(const bool is_geom_bases, const int p, const Mesh3D &mesh, int c, const Eigen::VectorXi &discr_order, const Eigen::VectorXi &discr_ordersq, std::vector<int> &res, MeshNodes &nodes)
748 {
749 // discr_ordersq is only used for non conforming bases!!!!
750
751 assert(mesh.is_pyramid(c));
752
753 if (p == 0)
754 {
755 res.push_back(nodes.node_id_from_cell(c));
756 return;
757 }
758
759 // 8 edges × (p-1) interior nodes each
760 const int n_edge_nodes = 8 * (p - 1);
761 // 4 tri faces × (p-1)(p-2)/2 interior nodes each
762 const int n_tri_face_nodes = 4 * (p - 1) * (p - 2) / 2;
763 // 1 quad base face × (p-1)^2 interior nodes
764 const int n_quad_face_nodes = (p - 1) * (p - 1);
765 const int n_face_nodes = n_tri_face_nodes + n_quad_face_nodes;
766 // total pyramid space dim = (p+1)(p+2)(2p+3)/6
767 const int total = (p + 1) * (p + 2) * (2 * p + 3) / 6;
768 const int n_cell_nodes = (p - 1) * (p - 2) * (2 * p - 3) / 6;
769
770 assert(total == 5 + n_edge_nodes + n_face_nodes + n_cell_nodes);
771
772 res.reserve(5 + n_edge_nodes + n_face_nodes + n_cell_nodes);
773
774 // Vertex nodes
775 auto v = pyramid_vertices_local_to_global(mesh, c);
776
777 // Edge nodes
778 Eigen::Matrix<Navigation3D::Index, 8, 1> e;
779 Eigen::Matrix<int, 8, 2> ev;
780 ev.row(0) << v[0], v[1];
781 ev.row(1) << v[1], v[2];
782 ev.row(2) << v[2], v[3];
783 ev.row(3) << v[3], v[0];
784 ev.row(4) << v[0], v[4];
785 ev.row(5) << v[1], v[4];
786 ev.row(6) << v[2], v[4];
787 ev.row(7) << v[3], v[4];
788
789 for (int le = 0; le < e.rows(); ++le)
790 {
791 e[le] = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
792 }
793
794 // Face nodes
795 Eigen::Matrix<Navigation3D::Index, 5, 1> f;
796 Eigen::Matrix<int, 4, 3> fvt; // side face
797 fvt.row(0) << v[0], v[1], v[4];
798 fvt.row(1) << v[1], v[2], v[4];
799 fvt.row(2) << v[2], v[3], v[4];
800 fvt.row(3) << v[3], v[0], v[4];
801 for (int lf = 0; lf < fvt.rows(); ++lf)
802 {
803 const auto index = mesh.get_index_from_element_face(c, fvt(lf, 0), fvt(lf, 1), fvt(lf, 2));
804 f[lf] = index;
805 }
806
807 // Eigen::Matrix<int, 1, 4> fvq; // base face
808 // fvq.row(0) << v[0], v[1], v[2], v[3];
809
810 // for (int lf = 0; lf < fvq.rows(); ++lf)
811 // {
812 // const auto index = find_quad_face(mesh, c, fvq(lf, 0), fvq(lf, 1), fvq(lf, 2), fvq(lf, 3));
813 // f[lf + 4] = index;
814 // }
815
816 // base quad face — pin index.vertex to v[0] so face_node starts at reference corner (0,0,0)
817 {
818 auto base_idx = find_quad_face(mesh, c, v[0], v[1], v[2], v[3]);
819 for (int rv = 0; rv < 4; ++rv)
820 {
821 if (base_idx.vertex == v[0])
822 break;
823 base_idx = mesh.next_around_face(base_idx);
824 }
825 assert(base_idx.vertex == v[0]);
826 f[4] = base_idx;
827 }
828
829 // vertices
830 for (size_t lv = 0; lv < v.size(); ++lv)
831 {
832 res.push_back(nodes.node_id_from_primitive(v[lv]));
833 }
834 assert(res.size() == size_t(5));
835
836 // Edges
837
838 for (int le = 0; le < e.rows(); ++le)
839 {
840 const auto index = e[le];
841
842 auto neighs = mesh.edge_neighs(index.edge);
843 int min_p = discr_order.size() > 0 ? discr_order(c) : 0;
844
845 for (auto cid : neighs)
846 min_p = std::min(min_p, prism_edge_order(cid, index.edge, discr_order, discr_ordersq, mesh));
847
848 if (!is_geom_bases && discr_order.size() > 0 && discr_order(c) > min_p)
849 {
850 for (int tmp = 0; tmp < p - 1; ++tmp)
851 res.push_back(-le - 10);
852 }
853 else
854 {
855 auto node_ids = nodes.node_ids_from_edge(index, p - 1);
856 res.insert(res.end(), node_ids.begin(), node_ids.end());
857 }
858 }
859 assert(res.size() == size_t(5 + n_edge_nodes));
860
861 // faces
862 for (int lf = 0; lf < f.rows(); ++lf)
863 {
864 const auto index = f[lf];
865 const auto other_cell = mesh.switch_element(index).element;
866 const bool is_tri_face = lf < 4;
867
868 const bool skip_other =
869 other_cell >= 0 && (discr_order(c) > discr_order(other_cell) || (!is_tri_face && mesh.is_prism(other_cell) && discr_order(c) > discr_ordersq(other_cell)));
870
871 if (!is_geom_bases && skip_other)
872 {
873 const int nn = is_tri_face ? (p > 2 ? (p - 2) : 0) : (p - 1);
874 const int n_loc_face = is_tri_face ? (nn * (nn + 1) / 2) : (nn * nn);
875
876 for (int tmp = 0; tmp < n_loc_face; ++tmp)
877 res.push_back(-lf - 1);
878 continue;
879 }
880 else
881 {
882 const int n_loc_face = is_tri_face ? (p > 2 ? (p - 2) : 0) : (p - 1);
883
884 auto node_ids = nodes.node_ids_from_face(index, n_loc_face);
885 res.insert(res.end(), node_ids.begin(), node_ids.end());
886 }
887 }
888 assert(res.size() == size_t(5 + n_edge_nodes + n_face_nodes));
889
890 // cells
891 if (n_cell_nodes > 0)
892 {
893 auto node_ids = nodes.node_ids_from_cell(f[0], p - 1);
894 res.insert(res.end(), node_ids.begin(), node_ids.end());
895 }
896 assert(res.size() == size_t(5 + n_edge_nodes + n_face_nodes + n_cell_nodes));
897 }
898 // -----------------------------------------------------------------------------
899
913 void compute_nodes(
914 const Mesh3D &mesh,
915 const Eigen::VectorXi &discr_ordersp,
916 const Eigen::VectorXi &discr_ordersq,
917 const Eigen::VectorXi &edge_orders,
918 const Eigen::VectorXi &face_orders,
919 const bool serendipity,
920 const bool has_polys,
921 const bool is_geom_bases,
922 MeshNodes &nodes,
923 std::vector<std::vector<int>> &edge_virtual_nodes,
924 std::vector<std::vector<int>> &face_virtual_nodes,
925 std::vector<std::vector<int>> &element_nodes_id,
926 std::vector<LocalBoundary> &local_boundary,
927 std::map<int, InterfaceData> &poly_face_to_data)
928 {
929 // Step 1: Assign global node ids for each quads
930 local_boundary.clear();
931 // local_boundary.resize(mesh.n_faces());
932 element_nodes_id.resize(mesh.n_faces());
933
934 if (!mesh.is_conforming())
935 {
936 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
937 edge_virtual_nodes.resize(ncmesh.n_edges());
938 face_virtual_nodes.resize(ncmesh.n_faces());
939 }
940
941 for (int c = 0; c < mesh.n_cells(); ++c)
942 {
943 const int discr_order = discr_ordersp(c);
944 const int discr_orderq = discr_ordersq(c);
945
946 if (mesh.is_cube(c))
947 {
948 hex_local_to_global(serendipity, discr_order, mesh, c, discr_ordersp, element_nodes_id[c], nodes);
949
950 auto v = hex_vertices_local_to_global(mesh, c);
951 Eigen::Matrix<int, 6, 4> fv;
952 fv.row(0) << v[0], v[3], v[4], v[7];
953 fv.row(1) << v[1], v[2], v[5], v[6];
954 fv.row(2) << v[0], v[1], v[5], v[4];
955 fv.row(3) << v[3], v[2], v[6], v[7];
956 fv.row(4) << v[0], v[1], v[2], v[3];
957 fv.row(5) << v[4], v[5], v[6], v[7];
958
959 LocalBoundary lb(c, BoundaryType::QUAD);
960 for (int i = 0; i < fv.rows(); ++i)
961 {
962 const int f = find_quad_face(mesh, c, fv(i, 0), fv(i, 1), fv(i, 2), fv(i, 3)).face;
963
964 if (mesh.is_boundary_face(f) || mesh.get_boundary_id(f) > 0)
965 {
966 lb.add_boundary_primitive(f, i);
967 }
968 }
969
970 if (!lb.empty())
971 local_boundary.emplace_back(lb);
972 }
973 else if (mesh.is_simplex(c))
974 {
975 // element_nodes_id[c] = polyfem::LagrangeBasis3d::tet_local_to_global(discr_order, mesh, c, discr_orders, nodes);
976 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);
977
978 auto v = tet_vertices_local_to_global(mesh, c);
979 Eigen::Matrix<int, 4, 3> fv;
980 fv.row(0) << v[0], v[1], v[2];
981 fv.row(1) << v[0], v[1], v[3];
982 fv.row(2) << v[1], v[2], v[3];
983 fv.row(3) << v[2], v[0], v[3];
984
985 LocalBoundary lb(c, BoundaryType::TRI);
986 for (long i = 0; i < fv.rows(); ++i)
987 {
988 const int f = mesh.get_index_from_element_face(c, fv(i, 0), fv(i, 1), fv(i, 2)).face;
989
990 if (mesh.is_boundary_face(f) || mesh.get_boundary_id(f) > 0)
991 {
992 lb.add_boundary_primitive(f, i);
993 }
994 }
995
996 if (!lb.empty())
997 local_boundary.emplace_back(lb);
998 }
999 else if (mesh.is_prism(c))
1000 {
1001 // todo non conforming prisms
1002 prism_local_to_global(discr_order, discr_orderq, mesh, c, discr_ordersp, element_nodes_id[c], nodes);
1003
1004 auto v = prism_vertices_local_to_global(mesh, c);
1005 Eigen::Matrix<int, 2, 3> fvt;
1006 fvt.row(0) << v[0], v[1], v[2];
1007 fvt.row(1) << v[3], v[4], v[5];
1008
1009 LocalBoundary lb(c, BoundaryType::PRISM);
1010 for (long i = 0; i < fvt.rows(); ++i)
1011 {
1012 const int f = mesh.get_index_from_element_face(c, fvt(i, 0), fvt(i, 1), fvt(i, 2)).face;
1013
1014 if (mesh.is_boundary_face(f) || mesh.get_boundary_id(f) > 0)
1015 {
1016 lb.add_boundary_primitive(f, i);
1017 }
1018 }
1019
1020 Eigen::Matrix<int, 3, 4> fvq;
1021 fvq.row(0) << v[0], v[1], v[4], v[3];
1022 fvq.row(1) << v[1], v[2], v[5], v[4];
1023 fvq.row(2) << v[2], v[0], v[3], v[5];
1024
1025 for (long i = 0; i < fvq.rows(); ++i)
1026 {
1027 const int f = find_quad_face(mesh, c, fvq(i, 0), fvq(i, 1), fvq(i, 2), fvq(i, 3)).face;
1028
1029 if (mesh.is_boundary_face(f) || mesh.get_boundary_id(f) > 0)
1030 {
1031 lb.add_boundary_primitive(f, i + 2);
1032 }
1033 }
1034
1035 if (!lb.empty())
1036 local_boundary.emplace_back(lb);
1037 }
1038
1039 // todo non conforming prisms
1040 else if (mesh.is_pyramid(c))
1041 {
1042 pyramid_local_to_global(is_geom_bases, discr_order, mesh, c, discr_ordersp, discr_ordersq, element_nodes_id[c], nodes);
1043
1044 auto v = pyramid_vertices_local_to_global(mesh, c);
1045 Eigen::Matrix<int, 4, 3> fvt;
1046 fvt.row(0) << v[0], v[1], v[4];
1047 fvt.row(1) << v[1], v[2], v[4];
1048 fvt.row(2) << v[2], v[3], v[4];
1049 fvt.row(3) << v[3], v[0], v[4];
1050
1051 LocalBoundary lb(c, BoundaryType::PYRAMID);
1052 for (long i = 0; i < fvt.rows(); ++i)
1053 {
1054 const int f = mesh.get_index_from_element_face(c, fvt(i, 0), fvt(i, 1), fvt(i, 2)).face;
1055
1056 if (mesh.is_boundary_face(f) || mesh.get_boundary_id(f) > 0)
1057 {
1058 lb.add_boundary_primitive(f, i + 1);
1059 }
1060 }
1061
1062 Eigen::Matrix<int, 1, 4> fvq;
1063 fvq.row(0) << v[0], v[1], v[2], v[3];
1064
1065 for (long i = 0; i < fvq.rows(); ++i)
1066 {
1067 const int f = find_quad_face(mesh, c, fvq(i, 0), fvq(i, 1), fvq(i, 2), fvq(i, 3)).face;
1068
1069 if (mesh.is_boundary_face(f) || mesh.get_boundary_id(f) > 0)
1070 {
1071 lb.add_boundary_primitive(f, 0);
1072 }
1073 }
1074
1075 if (!lb.empty())
1076 local_boundary.emplace_back(lb);
1077 }
1078 }
1079
1080 if (!has_polys)
1081 return;
1082
1083 // Step 2: Iterate over edges of polygons and compute interface weights
1084 Eigen::VectorXi indices;
1085 for (int c = 0; c < mesh.n_cells(); ++c)
1086 {
1087 // Skip non-polytopes
1088 if (!mesh.is_polytope(c))
1089 {
1090 continue;
1091 }
1092
1093 for (int lf = 0; lf < mesh.n_cell_faces(c); ++lf)
1094 {
1095 auto index = mesh.get_index_from_element(c, lf, 0);
1096 auto index2 = mesh.switch_element(index);
1097 int c2 = index2.element;
1098 assert(c2 >= 0);
1099
1100 const int discr_order = discr_ordersp(c2);
1101 const int discr_orderq = discr_ordersq(c2);
1102 if (mesh.is_cube(c2))
1103 {
1104 indices = LagrangeBasis3d::hex_face_local_nodes(serendipity, discr_order, mesh, index2);
1105 }
1106 else if (mesh.is_simplex(c2))
1107 {
1108 indices = LagrangeBasis3d::tet_face_local_nodes(discr_order, mesh, index2);
1109 }
1110 else if (mesh.is_prism(c2))
1111 {
1112 indices = LagrangeBasis3d::prism_face_local_nodes(discr_order, discr_orderq, mesh, index2);
1113 }
1114 else if (mesh.is_pyramid(c2))
1115 {
1116 indices = LagrangeBasis3d::pyramid_face_local_nodes(discr_order, mesh, index2);
1117 }
1118 else
1119 continue;
1120
1121 InterfaceData data;
1122 data.local_indices.insert(data.local_indices.begin(), indices.data(), indices.data() + indices.size());
1123 assert(indices.size() == data.local_indices.size());
1124 poly_face_to_data[index2.face] = data;
1125 }
1126 }
1127 }
1134 void local_to_global(const Eigen::MatrixXd &verts, const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
1135 {
1136 const int dim = verts.cols();
1137 const int N = uv.rows();
1138 assert(dim == 3);
1139 assert(uv.cols() == dim);
1140 assert(verts.rows() == dim + 1);
1141
1142 pts.setZero(N, dim);
1143 for (int i = 0; i < N; i++)
1144 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);
1145 }
1146
1147 void local_to_global_face(const Eigen::MatrixXd &verts, const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
1148 {
1149 const int dim = verts.cols();
1150 const int N = uv.rows();
1151 assert(dim == 3);
1152 assert(uv.cols() == 2);
1153 assert(verts.rows() == 3);
1154
1155 pts.setZero(N, dim);
1156 for (int i = 0; i < N; i++)
1157 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);
1158 }
1159
1166 void global_to_local(const Eigen::MatrixXd &verts, const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
1167 {
1168 const int dim = verts.cols();
1169 const int N = pts.rows();
1170 assert(dim == 3);
1171 assert(verts.rows() == dim + 1);
1172 assert(pts.cols() == dim);
1173
1174 Eigen::Matrix3d J;
1175 for (int i = 0; i < dim; i++)
1176 J.col(i) = verts.row(i + 1) - verts.row(0);
1177
1178 Eigen::Matrix3d Jinv = J.inverse();
1179
1180 uv.setZero(N, dim);
1181 polyfem::utils::maybe_parallel_for(N, [&](int start, int end, int thread_id) {
1182 for (int i = start; i < end; i++)
1183 {
1184 auto point = pts.row(i) - verts.row(0);
1185 uv.row(i) = Jinv * point.transpose();
1186 }
1187 });
1188 }
1189
1190 void global_to_local_face(const Eigen::MatrixXd &verts, const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
1191 {
1192 const int dim = verts.cols();
1193 const int N = pts.rows();
1194 assert(dim == 3);
1195 assert(verts.rows() == 3);
1196 assert(pts.cols() == dim);
1197
1198 Eigen::Matrix3d J;
1199 for (int i = 0; i < 2; i++)
1200 J.col(i) = verts.row(i + 1) - verts.row(0);
1201
1202 Eigen::Vector3d a = J.col(0);
1203 Eigen::Vector3d b = J.col(1);
1204 Eigen::Vector3d virtual_vert = a.cross(b);
1205 J.col(2) = virtual_vert;
1206
1207 uv.setZero(N, 2);
1208 polyfem::utils::maybe_parallel_for(N, [&](int start, int end, int thread_id) {
1209 for (int i = start; i < end; i++)
1210 {
1211 auto point = pts.row(i) - verts.row(0);
1212 Eigen::Vector3d x = J.colPivHouseholderQr().solve(point.transpose());
1213 uv.row(i) = x.block(0, 0, 2, 1);
1214 assert(std::abs(x(2)) < 1e-8);
1215 }
1216 });
1217 }
1218
1219 void global_to_local_edge(const Eigen::MatrixXd &verts, const Eigen::MatrixXd &pts, Eigen::VectorXd &uv)
1220 {
1221 const int dim = verts.cols();
1222 const int N = pts.rows();
1223 assert(dim == 3);
1224 assert(verts.rows() == 2);
1225 assert(pts.cols() == dim);
1226
1227 auto edge = verts.row(1) - verts.row(0);
1228 double squared_length = edge.squaredNorm();
1229
1230 uv.setZero(N);
1231 polyfem::utils::maybe_parallel_for(N, [&](int start, int end, int thread_id) {
1232 for (int i = start; i < end; i++)
1233 {
1234 auto vec = pts.row(i) - verts.row(0);
1235 uv(i) = (vec.dot(edge)) / squared_length;
1236 }
1237 });
1238 }
1239
1240 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)
1241 {
1242 // same order for overlapping faces
1243 for (int i = 0; i < mesh.n_faces(); i++)
1244 if (mesh.leader_face_of_face(i) >= 0)
1245 if (face_orders[mesh.leader_face_of_face(i)] != face_orders[i])
1246 return false;
1247
1248 // face order no smaller than order of its edges
1249 for (int i = 0; i < mesh.n_faces(); i++)
1250 {
1251 if (mesh.n_face_cells(i) == 0)
1252 continue;
1253 for (int j = 0; j < mesh.n_face_vertices(i); j++)
1254 {
1255 const int e_id = mesh.face_edge(i, j);
1256 if (edge_orders[e_id] > face_orders[i])
1257 return false;
1258 }
1259 }
1260
1261 // same order for overlapping edges
1262 for (int i = 0; i < mesh.n_edges(); i++)
1263 if (mesh.leader_edge_of_edge(i) >= 0)
1264 if (edge_orders[mesh.leader_edge_of_edge(i)] != edge_orders[i])
1265 return false;
1266
1267 // face order no larger than order of interior edges
1268 for (int i = 0; i < mesh.n_edges(); i++)
1269 {
1270 if (mesh.n_edge_cells(i) == 0)
1271 continue;
1272 if (mesh.leader_face_of_edge(i) >= 0 && mesh.leader_edge_of_edge(i) < 0)
1273 if (face_orders[mesh.leader_face_of_edge(i)] > edge_orders[i])
1274 return false;
1275 }
1276 return true;
1277 }
1278
1286 void compute_edge_face_orders(const polyfem::mesh::NCMesh3D &mesh, const Eigen::VectorXi &elem_orders, Eigen::VectorXi &edge_orders, Eigen::VectorXi &face_orders)
1287 {
1288 const int max_order = elem_orders.maxCoeff();
1289 edge_orders.setConstant(mesh.n_edges(), max_order);
1290 face_orders.setConstant(mesh.n_faces(), max_order);
1291
1292 for (int i = 0; i < mesh.n_cells(); i++)
1293 for (int j = 0; j < mesh.n_cell_faces(i); j++)
1294 face_orders[mesh.cell_face(i, j)] = std::min(face_orders[mesh.cell_face(i, j)], elem_orders[i]);
1295
1296 for (int i = 0; i < mesh.n_cells(); i++)
1297 for (int j = 0; j < mesh.n_cell_edges(i); j++)
1298 edge_orders[mesh.cell_edge(i, j)] = std::min(edge_orders[mesh.cell_edge(i, j)], elem_orders[i]);
1299
1300 while (!check_edge_face_orders(mesh, elem_orders, edge_orders, face_orders))
1301 {
1302 // same order for overlapping faces
1303 for (int i = 0; i < mesh.n_faces(); i++)
1304 if (mesh.leader_face_of_face(i) >= 0)
1305 face_orders[mesh.leader_face_of_face(i)] = std::min(face_orders[mesh.leader_face_of_face(i)], face_orders[i]);
1306
1307 for (int i = 0; i < mesh.n_faces(); i++)
1308 if (mesh.leader_face_of_face(i) >= 0)
1309 face_orders[i] = std::min(face_orders[mesh.leader_face_of_face(i)], face_orders[i]);
1310
1311 // face order no smaller than order of its edges
1312 for (int i = 0; i < mesh.n_faces(); i++)
1313 {
1314 if (mesh.n_face_cells(i) == 0)
1315 continue;
1316 for (int j = 0; j < mesh.n_face_vertices(i); j++)
1317 {
1318 const int e_id = mesh.face_edge(i, j);
1319 edge_orders[e_id] = std::min(edge_orders[e_id], face_orders[i]);
1320 }
1321 }
1322
1323 // same order for overlapping edges
1324 for (int i = 0; i < mesh.n_edges(); i++)
1325 if (mesh.leader_edge_of_edge(i) >= 0)
1326 edge_orders[mesh.leader_edge_of_edge(i)] = std::min(edge_orders[mesh.leader_edge_of_edge(i)], edge_orders[i]);
1327
1328 for (int i = 0; i < mesh.n_edges(); i++)
1329 if (mesh.leader_edge_of_edge(i) >= 0)
1330 edge_orders[i] = std::min(edge_orders[mesh.leader_edge_of_edge(i)], edge_orders[i]);
1331
1332 // face order no larger than order of interior edges
1333 for (int i = 0; i < mesh.n_edges(); i++)
1334 {
1335 if (mesh.n_edge_cells(i) == 0)
1336 continue;
1337 if (mesh.leader_face_of_edge(i) >= 0 && mesh.leader_edge_of_edge(i) < 0)
1338 face_orders[mesh.leader_face_of_edge(i)] = std::min(face_orders[mesh.leader_face_of_edge(i)], edge_orders[i]);
1339 }
1340 }
1341 }
1342} // anonymous namespace
1343
1344Eigen::VectorXi LagrangeBasis3d::tet_face_local_nodes(const int p, const Mesh3D &mesh, Navigation3D::Index index)
1345{
1346 const int nn = p > 2 ? (p - 2) : 0;
1347 const int n_edge_nodes = (p - 1) * 6;
1348 const int n_face_nodes = nn * (nn + 1) / 2;
1349
1350 const int c = index.element;
1351 assert(mesh.is_simplex(c));
1352
1353 // Local to global mapping of node indices
1354 const auto l2g = tet_vertices_local_to_global(mesh, c);
1355
1356 // Extract requested interface
1357 Eigen::VectorXi result(3 + (p - 1) * 3 + n_face_nodes);
1358 result[0] = find_index(l2g.begin(), l2g.end(), index.vertex);
1359 result[1] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
1360 result[2] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
1361
1362 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
1363 Eigen::Matrix<int, 6, 2> ev;
1364 ev.row(0) << l2g[0], l2g[1];
1365 ev.row(1) << l2g[1], l2g[2];
1366 ev.row(2) << l2g[2], l2g[0];
1367
1368 ev.row(3) << l2g[0], l2g[3];
1369 ev.row(4) << l2g[1], l2g[3];
1370 ev.row(5) << l2g[2], l2g[3];
1371
1372 Navigation3D::Index tmp = index;
1373
1374 for (int le = 0; le < e.rows(); ++le)
1375 {
1376 // const auto index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
1377 const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1378 e[le] = l_index;
1379 }
1380
1381 int ii = 3;
1382 for (int k = 0; k < 3; ++k)
1383 {
1384 bool reverse = false;
1385 int le = 0;
1386 for (; le < ev.rows(); ++le)
1387 {
1388 // const auto l_index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
1389 // const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1390 const auto l_index = e[le];
1391 if (l_index.edge == tmp.edge)
1392 {
1393 if (l_index.vertex == tmp.vertex)
1394 reverse = false;
1395 else
1396 {
1397 reverse = true;
1398 if (mesh.switch_vertex(tmp).vertex != l_index.vertex)
1399 assert(false);
1400 }
1401
1402 break;
1403 }
1404 }
1405 assert(le < 6);
1406
1407 if (!reverse)
1408 {
1409
1410 for (int i = 0; i < p - 1; ++i)
1411 {
1412 result[ii++] = 4 + le * (p - 1) + i;
1413 }
1414 }
1415 else
1416 {
1417 for (int i = 0; i < p - 1; ++i)
1418 {
1419 result[ii++] = 4 + (le + 1) * (p - 1) - i - 1;
1420 }
1421 }
1422
1423 tmp = mesh.next_around_face(tmp);
1424 }
1425
1426 // faces
1427
1428 Eigen::Matrix<int, 4, 3> fv;
1429 fv.row(0) << l2g[0], l2g[1], l2g[2];
1430 fv.row(1) << l2g[0], l2g[1], l2g[3];
1431 fv.row(2) << l2g[1], l2g[2], l2g[3];
1432 fv.row(3) << l2g[2], l2g[0], l2g[3];
1433
1434 long lf = 0;
1435 for (; lf < fv.rows(); ++lf)
1436 {
1437 const auto l_index = mesh.get_index_from_element_face(c, fv(lf, 0), fv(lf, 1), fv(lf, 2));
1438 if (l_index.face == index.face)
1439 break;
1440 }
1441
1442 assert(lf < fv.rows());
1443
1444 if (n_face_nodes == 0)
1445 {
1446 }
1447 else if (n_face_nodes == 1)
1448 result[ii++] = 4 + n_edge_nodes + lf;
1449 else // if (n_face_nodes == 3)
1450 {
1451
1452 const auto get_order = [&p, &nn, &n_face_nodes](const std::array<int, 3> &corners) {
1453 int index;
1454 int start;
1455 int offset;
1456
1457 std::vector<int> order1(n_face_nodes); // A-> B
1458 for (int k = 0; k < n_face_nodes; ++k)
1459 order1[k] = k;
1460
1461 std::vector<int> order2(n_face_nodes); // B-> A
1462 index = 0;
1463 start = nn - 1;
1464 for (int k = 0; k < nn; ++k)
1465 {
1466 for (int l = 0; l < nn - k; ++l)
1467 {
1468 order2[index] = start - l;
1469 index++;
1470 }
1471 start += (nn - 1) - k;
1472 }
1473
1474 std::vector<int> order3(n_face_nodes); // A->C
1475 index = 0;
1476 for (int k = 0; k < nn; ++k)
1477 {
1478 offset = k;
1479 for (int l = 0; l < nn - k; ++l)
1480 {
1481 order3[index] = offset;
1482 offset += nn - l;
1483
1484 index++;
1485 }
1486 }
1487
1488 std::vector<int> order4(n_face_nodes); // C-> A
1489 index = 0;
1490 start = n_face_nodes - 1;
1491 for (int k = 0; k < nn; ++k)
1492 {
1493 offset = 0;
1494 for (int l = 0; l < nn - k; ++l)
1495 {
1496 order4[index] = start - offset;
1497 offset += k + 2 + l;
1498 index++;
1499 }
1500
1501 start += -k - 1;
1502 }
1503
1504 std::vector<int> order5(n_face_nodes); // B-> C
1505 index = 0;
1506 start = nn - 1;
1507 for (int k = 0; k < nn; ++k)
1508 {
1509 offset = 0;
1510 for (int l = 0; l < nn - k; ++l)
1511 {
1512 order5[index] = start + offset;
1513 offset += nn - 1 - l;
1514 index++;
1515 }
1516
1517 start--;
1518 }
1519
1520 std::vector<int> order6(n_face_nodes); // C-> B
1521 index = 0;
1522 start = n_face_nodes;
1523 for (int k = 0; k < nn; ++k)
1524 {
1525 offset = 0;
1526 start = start - k - 1;
1527 for (int l = 0; l < nn - k; ++l)
1528 {
1529 order6[index] = start - offset;
1530 offset += l + 1 + k;
1531 index++;
1532 }
1533 }
1534
1535 if (corners[0] == order1[0] && corners[1] == order1[nn - 1])
1536 {
1537 assert(corners[2] == order1[n_face_nodes - 1]);
1538 return order1;
1539 }
1540
1541 if (corners[0] == order2[0] && corners[1] == order2[nn - 1])
1542 {
1543 assert(corners[2] == order2[n_face_nodes - 1]);
1544 return order2;
1545 }
1546
1547 if (corners[0] == order3[0] && corners[1] == order3[nn - 1])
1548 {
1549 assert(corners[2] == order3[n_face_nodes - 1]);
1550 return order3;
1551 }
1552
1553 if (corners[0] == order4[0] && corners[1] == order4[nn - 1])
1554 {
1555 assert(corners[2] == order4[n_face_nodes - 1]);
1556 return order4;
1557 }
1558
1559 if (corners[0] == order5[0] && corners[1] == order5[nn - 1])
1560 {
1561 assert(corners[2] == order5[n_face_nodes - 1]);
1562 return order5;
1563 }
1564
1565 if (corners[0] == order6[0] && corners[1] == order6[nn - 1])
1566 {
1567 assert(corners[2] == order6[n_face_nodes - 1]);
1568 return order6;
1569 }
1570
1571 assert(false);
1572 return order1;
1573 };
1574
1575 Eigen::MatrixXd nodes;
1576 autogen::p_nodes_3d(p, nodes);
1577 // auto pos = LagrangeBasis3d::linear_tet_face_local_nodes_coordinates(mesh, index);
1578 // Local to global mapping of node indices
1579
1580 // Extract requested interface
1581 std::array<int, 3> idx;
1582 for (int lv = 0; lv < 3; ++lv)
1583 {
1584 idx[lv] = find_index(l2g.begin(), l2g.end(), index.vertex);
1585 index = mesh.next_around_face(index);
1586 }
1587 Eigen::Matrix3d pos(3, 3);
1588 int cnt = 0;
1589 for (int i : idx)
1590 {
1591 pos.row(cnt++) = nodes.row(i);
1592 }
1593
1594 const Eigen::RowVector3d bary = pos.colwise().mean();
1595
1596 const int offset = 4 + n_edge_nodes;
1597 bool found = false;
1598 for (int lff = 0; lff < 4; ++lff)
1599 {
1600 Eigen::MatrixXd loc_nodes = nodes.block(offset + lff * n_face_nodes, 0, n_face_nodes, 3);
1601 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
1602
1603 if ((node_bary - bary).norm() < 1e-10)
1604 {
1605 std::array<int, 3> corners;
1606 int sum = 0;
1607 for (int m = 0; m < 3; ++m)
1608 {
1609 auto t = pos.row(m);
1610 int min_n = -1;
1611 double min_dis = 10000;
1612
1613 for (int n = 0; n < n_face_nodes; ++n)
1614 {
1615 double dis = (loc_nodes.row(n) - t).squaredNorm();
1616 if (dis < min_dis)
1617 {
1618 min_dis = dis;
1619 min_n = n;
1620 }
1621 }
1622
1623 assert(min_n >= 0);
1624 assert(min_n < n_face_nodes);
1625 corners[m] = min_n;
1626 }
1627
1628 const auto indices = get_order(corners);
1629 for (int min_n : indices)
1630 {
1631 sum += min_n;
1632 result[ii++] = 4 + n_edge_nodes + min_n + lf * n_face_nodes;
1633 }
1634
1635 assert(sum == (n_face_nodes - 1) * n_face_nodes / 2);
1636
1637 found = true;
1638 assert(lff == lf);
1639
1640 break;
1641 }
1642 }
1643
1644 assert(found);
1645 }
1646 // else
1647 // {
1648 // assert(n_face_nodes == 0);
1649 // }
1650
1651 assert(ii == result.size());
1652 return result;
1653}
1654
1655Eigen::VectorXi LagrangeBasis3d::hex_face_local_nodes(const bool serendipity, const int q, const Mesh3D &mesh, Navigation3D::Index index)
1656{
1657 const int nn = q - 1;
1658 const int n_edge_nodes = nn * 12;
1659 const int n_face_nodes = serendipity ? 0 : nn * nn;
1660
1661 const int c = index.element;
1662 assert(mesh.is_cube(c));
1663
1664 // Local to global mapping of node indices
1665 const auto l2g = hex_vertices_local_to_global(mesh, c);
1666
1667 // Extract requested interface
1668 Eigen::VectorXi result(4 + nn * 4 + n_face_nodes);
1669 result[0] = find_index(l2g.begin(), l2g.end(), index.vertex);
1670 result[1] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
1671 result[2] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
1672 result[3] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(mesh.next_around_face(index))).vertex);
1673
1674 Eigen::Matrix<Navigation3D::Index, 12, 1> e;
1675 Eigen::Matrix<int, 12, 2> ev;
1676 ev.row(0) << l2g[0], l2g[1];
1677 ev.row(1) << l2g[1], l2g[2];
1678 ev.row(2) << l2g[2], l2g[3];
1679 ev.row(3) << l2g[3], l2g[0];
1680 ev.row(4) << l2g[0], l2g[4];
1681 ev.row(5) << l2g[1], l2g[5];
1682 ev.row(6) << l2g[2], l2g[6];
1683 ev.row(7) << l2g[3], l2g[7];
1684 ev.row(8) << l2g[4], l2g[5];
1685 ev.row(9) << l2g[5], l2g[6];
1686 ev.row(10) << l2g[6], l2g[7];
1687 ev.row(11) << l2g[7], l2g[4];
1688
1689 Navigation3D::Index tmp = index;
1690
1691 for (int le = 0; le < e.rows(); ++le)
1692 {
1693 const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1694 e[le] = l_index;
1695 }
1696
1697 int ii = 4;
1698 for (int k = 0; k < 4; ++k)
1699 {
1700 bool reverse = false;
1701 int le = 0;
1702 for (; le < ev.rows(); ++le)
1703 {
1704 // const auto l_index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
1705 // const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1706 const auto l_index = e[le];
1707 if (l_index.edge == tmp.edge)
1708 {
1709 if (l_index.vertex == tmp.vertex)
1710 reverse = false;
1711 else
1712 {
1713 reverse = true;
1714 assert(mesh.switch_vertex(tmp).vertex == l_index.vertex);
1715 }
1716
1717 break;
1718 }
1719 }
1720 assert(le < 12);
1721
1722 if (!reverse)
1723 {
1724
1725 for (int i = 0; i < q - 1; ++i)
1726 {
1727 result[ii++] = 8 + le * (q - 1) + i;
1728 }
1729 }
1730 else
1731 {
1732 for (int i = 0; i < q - 1; ++i)
1733 {
1734 result[ii++] = 8 + (le + 1) * (q - 1) - i - 1;
1735 }
1736 }
1737
1738 tmp = mesh.next_around_face(tmp);
1739 }
1740
1741 // faces
1742
1743 Eigen::Matrix<int, 6, 4> fv;
1744 fv.row(0) << l2g[0], l2g[3], l2g[4], l2g[7];
1745 fv.row(1) << l2g[1], l2g[2], l2g[5], l2g[6];
1746 fv.row(2) << l2g[0], l2g[1], l2g[5], l2g[4];
1747 fv.row(3) << l2g[3], l2g[2], l2g[6], l2g[7];
1748 fv.row(4) << l2g[0], l2g[1], l2g[2], l2g[3];
1749 fv.row(5) << l2g[4], l2g[5], l2g[6], l2g[7];
1750
1751 long lf = 0;
1752 for (; lf < fv.rows(); ++lf)
1753 {
1754 const auto l_index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
1755 if (l_index.face == index.face)
1756 break;
1757 }
1758
1759 assert(lf < fv.rows());
1760
1761 if (n_face_nodes == 1)
1762 result[ii++] = 8 + n_edge_nodes + lf;
1763 else if (n_face_nodes != 0)
1764 {
1765 Eigen::MatrixXd nodes;
1766 autogen::q_nodes_3d(q, nodes);
1767 // auto pos = LagrangeBasis3d::linear_tet_face_local_nodes_coordinates(mesh, index);
1768 // Local to global mapping of node indices
1769
1770 // Extract requested interface
1771 std::array<int, 4> idx;
1772 for (int lv = 0; lv < 4; ++lv)
1773 {
1774 idx[lv] = find_index(l2g.begin(), l2g.end(), index.vertex);
1775 index = mesh.next_around_face(index);
1776 }
1777 Eigen::Matrix<double, 4, 3> pos(4, 3);
1778 int cnt = 0;
1779 for (int i : idx)
1780 {
1781 pos.row(cnt++) = nodes.row(i);
1782 }
1783
1784 const Eigen::RowVector3d bary = pos.colwise().mean();
1785
1786 const int offset = 8 + n_edge_nodes;
1787 bool found = false;
1788 for (int lff = 0; lff < 6; ++lff)
1789 {
1790 Eigen::Matrix<double, 4, 3> loc_nodes = nodes.block<4, 3>(offset + lff * n_face_nodes, 0);
1791 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
1792
1793 if ((node_bary - bary).norm() < 1e-10)
1794 {
1795 int sum = 0;
1796 for (int m = 0; m < 4; ++m)
1797 {
1798 auto t = pos.row(m);
1799 int min_n = -1;
1800 double min_dis = 10000;
1801
1802 for (int n = 0; n < 4; ++n)
1803 {
1804 double dis = (loc_nodes.row(n) - t).squaredNorm();
1805 if (dis < min_dis)
1806 {
1807 min_dis = dis;
1808 min_n = n;
1809 }
1810 }
1811
1812 assert(min_n >= 0);
1813 assert(min_n < 4);
1814
1815 sum += min_n;
1816
1817 result[ii++] = 8 + n_edge_nodes + min_n + lf * n_face_nodes;
1818 }
1819
1820 assert(sum == 6); // 0 + 1 + 2 + 3
1821
1822 found = true;
1823 assert(lff == lf);
1824 }
1825
1826 if (found)
1827 break;
1828 }
1829
1830 assert(found);
1831 }
1832
1833 assert(ii == result.size());
1834 return result;
1835}
1836
1837Eigen::VectorXi LagrangeBasis3d::prism_face_local_nodes(const int p, const int q, const Mesh3D &mesh, Navigation3D::Index index)
1838{
1839 const int c = index.element; // c is global elem id
1840 assert(mesh.is_prism(c));
1841
1842 // Local to global mapping of node indices
1843 const auto l2g = prism_vertices_local_to_global(mesh, c);
1844 const int global_n_edges_nodes = (p - 1) * 6 + (q - 1) * 3;
1845
1846 if (mesh.n_face_vertices(index.face) == 3)
1847 {
1848 const int nn = p > 2 ? (p - 2) : 0;
1849 const int n_face_nodes = nn * (nn + 1) / 2;
1850
1851 // Extract requested interface
1852 Eigen::VectorXi result(3 + (p - 1) * 3 + n_face_nodes);
1853 result[0] = find_index(l2g.begin(), l2g.end(), index.vertex);
1854 result[1] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
1855 result[2] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
1856
1857 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
1858 Eigen::Matrix<int, 6, 2> ev;
1859 ev.row(0) << l2g[0], l2g[1];
1860 ev.row(1) << l2g[1], l2g[2];
1861 ev.row(2) << l2g[2], l2g[0];
1862
1863 ev.row(3) << l2g[3], l2g[4];
1864 ev.row(4) << l2g[4], l2g[5];
1865 ev.row(5) << l2g[5], l2g[3];
1866
1867 Navigation3D::Index tmp = index;
1868
1869 for (int le = 0; le < e.rows(); ++le)
1870 {
1871 // const auto index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
1872 const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1873 e[le] = l_index;
1874 }
1875
1876 int ii = 3;
1877 for (int k = 0; k < 3; ++k)
1878 {
1879 bool reverse = false;
1880 int le = 0;
1881 for (; le < ev.rows(); ++le)
1882 {
1883 // const auto l_index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
1884 // const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
1885 const auto l_index = e[le];
1886 if (l_index.edge == tmp.edge)
1887 {
1888 if (l_index.vertex == tmp.vertex)
1889 reverse = false;
1890 else
1891 {
1892 reverse = true;
1893 if (mesh.switch_vertex(tmp).vertex != l_index.vertex)
1894 assert(false);
1895 }
1896
1897 break;
1898 }
1899 }
1900 assert(le < 6);
1901
1902 if (!reverse)
1903 {
1904
1905 for (int i = 0; i < p - 1; ++i)
1906 {
1907 result[ii++] = 6 + le * (p - 1) + i;
1908 }
1909 }
1910 else
1911 {
1912 for (int i = 0; i < p - 1; ++i)
1913 {
1914 result[ii++] = 6 + (le + 1) * (p - 1) - i - 1;
1915 }
1916 }
1917
1918 tmp = mesh.next_around_face(tmp);
1919 }
1920
1921 // faces
1922
1923 Eigen::Matrix<int, 2, 3> fv;
1924 fv.row(0) << l2g[0], l2g[1], l2g[2];
1925 fv.row(1) << l2g[3], l2g[4], l2g[5];
1926
1927 long lf = 0;
1928 for (; lf < fv.rows(); ++lf)
1929 {
1930 const auto l_index = mesh.get_index_from_element_face(c, fv(lf, 0), fv(lf, 1), fv(lf, 2));
1931 if (l_index.face == index.face)
1932 break;
1933 }
1934
1935 assert(lf < fv.rows());
1936
1937 if (n_face_nodes == 0)
1938 {
1939 }
1940 else if (n_face_nodes == 1)
1941 result[ii++] = 6 + global_n_edges_nodes + lf;
1942 else // if (n_face_nodes == 3)
1943 {
1944
1945 const auto get_order = [&p, &q, &nn, &n_face_nodes](const std::array<int, 3> &corners) {
1946 int index;
1947 int start;
1948 int offset;
1949
1950 std::vector<int> order1(n_face_nodes); // A-> B
1951 for (int k = 0; k < n_face_nodes; ++k)
1952 order1[k] = k;
1953
1954 std::vector<int> order2(n_face_nodes); // B-> A
1955 index = 0;
1956 start = nn - 1;
1957 for (int k = 0; k < nn; ++k)
1958 {
1959 for (int l = 0; l < nn - k; ++l)
1960 {
1961 order2[index] = start - l;
1962 index++;
1963 }
1964 start += (nn - 1) - k;
1965 }
1966
1967 std::vector<int> order3(n_face_nodes); // A->C
1968 index = 0;
1969 for (int k = 0; k < nn; ++k)
1970 {
1971 offset = k;
1972 for (int l = 0; l < nn - k; ++l)
1973 {
1974 order3[index] = offset;
1975 offset += nn - l;
1976
1977 index++;
1978 }
1979 }
1980
1981 std::vector<int> order4(n_face_nodes); // C-> A
1982 index = 0;
1983 start = n_face_nodes - 1;
1984 for (int k = 0; k < nn; ++k)
1985 {
1986 offset = 0;
1987 for (int l = 0; l < nn - k; ++l)
1988 {
1989 order4[index] = start - offset;
1990 offset += k + 2 + l;
1991 index++;
1992 }
1993
1994 start += -k - 1;
1995 }
1996
1997 std::vector<int> order5(n_face_nodes); // B-> C
1998 index = 0;
1999 start = nn - 1;
2000 for (int k = 0; k < nn; ++k)
2001 {
2002 offset = 0;
2003 for (int l = 0; l < nn - k; ++l)
2004 {
2005 order5[index] = start + offset;
2006 offset += nn - 1 - l;
2007 index++;
2008 }
2009
2010 start--;
2011 }
2012
2013 std::vector<int> order6(n_face_nodes); // C-> B
2014 index = 0;
2015 start = n_face_nodes;
2016 for (int k = 0; k < nn; ++k)
2017 {
2018 offset = 0;
2019 start = start - k - 1;
2020 for (int l = 0; l < nn - k; ++l)
2021 {
2022 order6[index] = start - offset;
2023 offset += l + 1 + k;
2024 index++;
2025 }
2026 }
2027
2028 if (corners[0] == order1[0] && corners[1] == order1[nn - 1])
2029 {
2030 assert(corners[2] == order1[n_face_nodes - 1]);
2031 return order1;
2032 }
2033
2034 if (corners[0] == order2[0] && corners[1] == order2[nn - 1])
2035 {
2036 assert(corners[2] == order2[n_face_nodes - 1]);
2037 return order2;
2038 }
2039
2040 if (corners[0] == order3[0] && corners[1] == order3[nn - 1])
2041 {
2042 assert(corners[2] == order3[n_face_nodes - 1]);
2043 return order3;
2044 }
2045
2046 if (corners[0] == order4[0] && corners[1] == order4[nn - 1])
2047 {
2048 assert(corners[2] == order4[n_face_nodes - 1]);
2049 return order4;
2050 }
2051
2052 if (corners[0] == order5[0] && corners[1] == order5[nn - 1])
2053 {
2054 assert(corners[2] == order5[n_face_nodes - 1]);
2055 return order5;
2056 }
2057
2058 if (corners[0] == order6[0] && corners[1] == order6[nn - 1])
2059 {
2060 assert(corners[2] == order6[n_face_nodes - 1]);
2061 return order6;
2062 }
2063
2064 assert(false);
2065 return order1;
2066 };
2067
2068 Eigen::MatrixXd nodes;
2069 autogen::prism_nodes_3d(p, q, nodes);
2070 // auto pos = LagrangeBasis3d::linear_tet_face_local_nodes_coordinates(mesh, index);
2071 // Local to global mapping of node indices
2072
2073 // Extract requested interface
2074 std::array<int, 3> idx;
2075 for (int lv = 0; lv < 3; ++lv)
2076 {
2077 idx[lv] = find_index(l2g.begin(), l2g.end(), index.vertex);
2078 index = mesh.next_around_face(index);
2079 }
2080 Eigen::Matrix3d pos(3, 3);
2081 int cnt = 0;
2082 for (int i : idx)
2083 {
2084 pos.row(cnt++) = nodes.row(i);
2085 }
2086
2087 const Eigen::RowVector3d bary = pos.colwise().mean();
2088
2089 const int offset = 6 + global_n_edges_nodes;
2090 bool found = false;
2091 for (int lff = 0; lff < 2; ++lff)
2092 {
2093 Eigen::MatrixXd loc_nodes = nodes.block(offset + lff * n_face_nodes, 0, n_face_nodes, 3);
2094 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
2095
2096 if ((node_bary - bary).norm() < 1e-10)
2097 {
2098 std::array<int, 3> corners;
2099 int sum = 0;
2100 for (int m = 0; m < 3; ++m)
2101 {
2102 auto t = pos.row(m);
2103 int min_n = -1;
2104 double min_dis = 10000;
2105
2106 for (int n = 0; n < n_face_nodes; ++n)
2107 {
2108 double dis = (loc_nodes.row(n) - t).squaredNorm();
2109 if (dis < min_dis)
2110 {
2111 min_dis = dis;
2112 min_n = n;
2113 }
2114 }
2115
2116 assert(min_n >= 0);
2117 assert(min_n < n_face_nodes);
2118 corners[m] = min_n;
2119 }
2120
2121 const auto indices = get_order(corners);
2122 for (int min_n : indices)
2123 {
2124 sum += min_n;
2125 result[ii++] = 6 + global_n_edges_nodes + min_n + lf * n_face_nodes;
2126 }
2127
2128 assert(sum == (n_face_nodes - 1) * n_face_nodes / 2);
2129
2130 found = true;
2131 assert(lff == lf);
2132
2133 break;
2134 }
2135 }
2136
2137 assert(found);
2138 }
2139 // else
2140 // {
2141 // assert(n_face_nodes == 0);
2142 // }
2143
2144 assert(ii == result.size());
2145 return result;
2146 }
2147 else
2148 {
2149 const int n_edge_nodes = 2 * (p - 1) + 2 * (q - 1); // 4 edges
2150 const int n_face_nodes = (p - 1) * (q - 1);
2151 const int n_tri_face_nodes = p > 2 ? (p - 2) : 0;
2152
2153 // Extract requested interface
2154 Eigen::VectorXi result(4 + n_edge_nodes + n_face_nodes);
2155 // v nodes
2156 result[0] = find_index(l2g.begin(), l2g.end(), index.vertex);
2157 result[1] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
2158 result[2] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
2159 result[3] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(mesh.next_around_face(index))).vertex);
2160
2161 // e nodes
2162 Eigen::Matrix<Navigation3D::Index, 9, 1> e;
2163 Eigen::Matrix<int, 9, 2> ev;
2164 ev.row(0) << l2g[0], l2g[1];
2165 ev.row(1) << l2g[1], l2g[2];
2166 ev.row(2) << l2g[2], l2g[0];
2167
2168 ev.row(3) << l2g[3], l2g[4];
2169 ev.row(4) << l2g[4], l2g[5];
2170 ev.row(5) << l2g[5], l2g[3];
2171
2172 ev.row(6) << l2g[0], l2g[3];
2173 ev.row(7) << l2g[1], l2g[4];
2174 ev.row(8) << l2g[2], l2g[5];
2175
2176 Navigation3D::Index tmp = index;
2177
2178 for (int le = 0; le < e.rows(); ++le)
2179 {
2180 const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1)); // get random face for an edge
2181 e[le] = l_index;
2182 }
2183
2184 int ii = 4;
2185 for (int k = 0; k < 4; ++k)
2186 {
2187 bool reverse = false;
2188 int le = 0;
2189 for (; le < ev.rows(); ++le)
2190 {
2191 // const auto l_index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
2192 // const auto l_index = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
2193 const auto l_index = e[le];
2194 if (l_index.edge == tmp.edge)
2195 {
2196 if (l_index.vertex == tmp.vertex)
2197 reverse = false;
2198 else
2199 {
2200 reverse = true;
2201 assert(mesh.switch_vertex(tmp).vertex == l_index.vertex);
2202 }
2203
2204 break;
2205 }
2206 }
2207 assert(le < 9);
2208
2209 // found le, know reverse
2210
2211 if (le < 6)
2212 {
2213 if (!reverse)
2214 {
2215
2216 for (int i = 0; i < p - 1; ++i)
2217 {
2218 result[ii++] = 6 + le * (p - 1) + i;
2219 }
2220 }
2221 else
2222 {
2223 for (int i = 0; i < p - 1; ++i)
2224 {
2225 result[ii++] = 6 + (le + 1) * (p - 1) - i - 1;
2226 }
2227 }
2228 }
2229 else
2230 {
2231 if (!reverse)
2232 {
2233
2234 for (int i = 0; i < q - 1; ++i)
2235 {
2236 result[ii++] = 6 + 6 * (p - 1) + (le - 6) * (q - 1) + i;
2237 }
2238 }
2239 else
2240 {
2241 for (int i = 0; i < q - 1; ++i)
2242 {
2243 result[ii++] = 6 + 6 * (p - 1) + (le - 5) * (q - 1) - i - 1;
2244 }
2245 }
2246 }
2247 tmp = mesh.next_around_face(tmp);
2248 }
2249
2250 // faces
2251
2252 Eigen::Matrix<int, 3, 4> fv;
2253 fv.row(0) << l2g[0], l2g[3], l2g[4], l2g[1];
2254 fv.row(1) << l2g[1], l2g[4], l2g[5], l2g[2];
2255 fv.row(2) << l2g[2], l2g[5], l2g[3], l2g[0];
2256
2257 long lf = 0;
2258 for (; lf < fv.rows(); ++lf)
2259 {
2260 const auto l_index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
2261 if (l_index.face == index.face)
2262 break;
2263 }
2264
2265 assert(lf < fv.rows());
2266
2267 if (n_face_nodes == 1)
2268 {
2269 result[ii++] = 6 + global_n_edges_nodes + lf;
2270 }
2271 else if (n_face_nodes == 2)
2272 {
2273 Eigen::MatrixXd nodes;
2274 autogen::prism_nodes_3d(p, q, nodes);
2275
2276 std::array<int, 4> idx; // local node id of the 4 face vertices
2277 for (int lv = 0; lv < 4; ++lv)
2278 {
2279 idx[lv] = find_index(l2g.begin(), l2g.end(), index.vertex);
2280 index = mesh.next_around_face(index);
2281 }
2282
2283 Eigen::Matrix<double, 4, 3> pos(4, 3); // local (on ref) coordinates of the 4 face corner nodes
2284 int cnt = 0;
2285 for (int i : idx)
2286 {
2287 pos.row(cnt++) = nodes.row(i);
2288 }
2289
2290 const Eigen::RowVector3d bary = pos.colwise().mean();
2291
2292 const int offset = 6 + global_n_edges_nodes;
2293
2294 bool found = false;
2295 for (int lff = 0; lff < 3; ++lff)
2296 {
2297 int start_row = offset + lff * n_face_nodes + 2 * n_tri_face_nodes; // skip tri face nodes
2298
2299 Eigen::MatrixXd loc_nodes = nodes.block(start_row, 0, n_face_nodes, 3);
2300 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
2301
2302 double dist = (node_bary - bary).norm();
2303
2304 if (dist < 1e-10) // find which quad face
2305 {
2306 auto t = pos.row(0);
2307 int min_n = -1;
2308 double min_dis = 10000;
2309 for (int n = 0; n < n_face_nodes; ++n)
2310 {
2311 double dis = (loc_nodes.row(n) - t).squaredNorm();
2312 if (dis < min_dis)
2313 {
2314 min_dis = dis;
2315 min_n = n;
2316 }
2317 }
2318
2319 assert(min_n >= 0);
2320 assert(min_n < n_face_nodes);
2321
2322 int final_idx = 6 + global_n_edges_nodes + min_n + lf * n_face_nodes + 2 * n_tri_face_nodes;
2323 result[ii++] = final_idx;
2324
2325 final_idx = 6 + global_n_edges_nodes + (min_n + 1) % 2 + lf * n_face_nodes + 2 * n_tri_face_nodes;
2326 result[ii++] = final_idx;
2327
2328 found = true;
2329 assert(lff == lf);
2330 }
2331
2332 if (found)
2333 break;
2334 }
2335
2336 assert(found);
2337 }
2338 else if (n_face_nodes == 4)
2339 {
2340 assert(p == 3 && q == 3);
2341
2342 Eigen::MatrixXd nodes;
2343 autogen::prism_nodes_3d(p, q, nodes);
2344
2345 std::array<int, 4> idx;
2346 Navigation3D::Index idx_it = index;
2347 for (int lv = 0; lv < 4; ++lv)
2348 {
2349 idx[lv] = find_index(l2g.begin(), l2g.end(), idx_it.vertex);
2350 idx_it = mesh.next_around_face(idx_it);
2351 }
2352
2353 Eigen::Matrix<double, 4, 3> pos;
2354 for (int lv = 0; lv < 4; ++lv)
2355 pos.row(lv) = nodes.row(idx[lv]);
2356
2357 const int start_row = 6 + global_n_edges_nodes + lf * n_face_nodes + 2 * n_tri_face_nodes;
2358 Eigen::MatrixXd loc_nodes = nodes.block(start_row, 0, n_face_nodes, 3);
2359
2360 const std::array<Eigen::Vector2d, 4> uv = {{
2361 Eigen::Vector2d(1.0 / 3.0, 1.0 / 3.0),
2362 Eigen::Vector2d(1.0 / 3.0, 2.0 / 3.0),
2363 Eigen::Vector2d(2.0 / 3.0, 1.0 / 3.0),
2364 Eigen::Vector2d(2.0 / 3.0, 2.0 / 3.0),
2365 }};
2366
2367 std::array<bool, 4> used = {{false, false, false, false}};
2368
2369 for (const auto &st : uv)
2370 {
2371 const double s = st(0);
2372 const double t = st(1);
2373
2374 const Eigen::RowVector3d target =
2375 (1.0 - s) * (1.0 - t) * pos.row(0)
2376 + s * (1.0 - t) * pos.row(1)
2377 + s * t * pos.row(2)
2378 + (1.0 - s) * t * pos.row(3);
2379
2380 int best_n = -1;
2381 double best = std::numeric_limits<double>::infinity();
2382
2383 for (int n = 0; n < n_face_nodes; ++n)
2384 {
2385 if (used[n])
2386 continue;
2387
2388 const double d = (loc_nodes.row(n) - target).squaredNorm();
2389 if (d < best)
2390 {
2391 best = d;
2392 best_n = n;
2393 }
2394 }
2395
2396 assert(best_n >= 0);
2397 assert(best < 1e-12);
2398
2399 used[best_n] = true;
2400 result[ii++] = start_row + best_n;
2401 }
2402 }
2403 else
2404 {
2405 assert(n_face_nodes == 0);
2406 }
2407 assert(ii == result.size());
2408 return result;
2409 }
2410}
2411
2412Eigen::VectorXi LagrangeBasis3d::pyramid_face_local_nodes(const int p, const Mesh3D &mesh, Navigation3D::Index index)
2413{
2414 const int c = index.element;
2415 assert(mesh.is_pyramid(c));
2416
2417 // local-to-global vertex map (5 vertices)
2418 const auto l2g = pyramid_vertices_local_to_global(mesh, c);
2419 const auto &v = l2g;
2420
2421 // build the 8 pyramid edges in a fixed local order (matches pyramid_local_to_global ev)
2422 Eigen::Matrix<int, 8, 2> ev;
2423 ev.row(0) << v[0], v[1];
2424 ev.row(1) << v[1], v[2];
2425 ev.row(2) << v[2], v[3];
2426 ev.row(3) << v[3], v[0];
2427 ev.row(4) << v[0], v[4];
2428 ev.row(5) << v[1], v[4];
2429 ev.row(6) << v[2], v[4];
2430 ev.row(7) << v[3], v[4];
2431
2432 Eigen::Matrix<Navigation3D::Index, 8, 1> e;
2433 for (int le = 0; le < 8; ++le)
2434 e[le] = mesh.get_index_from_element_edge(c, ev(le, 0), ev(le, 1));
2435
2436 const int nei = p - 1; // interior nodes per edge
2437 const int nfi_tri = (p - 1) * (p - 2) / 2; // interior nodes per tri face
2438 const int nfi_quad = (p - 1) * (p - 1); // interior nodes on quad face
2439
2440 // offsets into the local DOF array (matches pyramid_local_to_global ordering):
2441 // [0..4] : vertices
2442 // [5 .. 5+8*nei-1] : edge interiors (le=0..7, nei each)
2443 // [edge_end .. +4*nfi_tri-1] : 4 tri face interiors (lf=0..3)
2444 // [tri_end .. +nfi_quad-1] : quad face interiors
2445 const int edge_start = 5;
2446 const int tri_face_start = edge_start + 8 * nei;
2447 const int quad_face_start = tri_face_start + 4 * nfi_tri;
2448
2449 // Append nei interior nodes for the edge currently pointed to by edge_idx,
2450 // respecting traversal direction vs. canonical edge direction.
2451 auto append_edge_dofs = [&](Eigen::VectorXi &result, int &ii, const Navigation3D::Index &edge_idx) {
2452 if (nei <= 0)
2453 return;
2454 int le = 0;
2455 for (; le < 8; ++le)
2456 {
2457 if (e[le].edge == edge_idx.edge)
2458 break;
2459 }
2460 assert(le < 8);
2461 const bool forward = (edge_idx.vertex == ev(le, 0));
2462 for (int q = 0; q < nei; ++q)
2463 {
2464 const int local_q = forward ? q : (nei - 1 - q);
2465 result[ii++] = edge_start + le * nei + local_q;
2466 }
2467 };
2468
2469 assert(mesh.n_face_vertices(index.face) == 3 || mesh.n_face_vertices(index.face) == 4);
2470
2471 // ---- TRI face ----
2472 if (mesh.n_face_vertices(index.face) == 3)
2473 {
2474 Eigen::VectorXi result(3 + 3 * nei + nfi_tri);
2475 int ii = 0;
2476
2477 // vertices in face traversal order
2478 result[ii++] = find_index(l2g.begin(), l2g.end(), index.vertex);
2479 result[ii++] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
2480 result[ii++] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
2481
2482 // edge interiors in traversal order
2483 Navigation3D::Index tmp = index;
2484 for (int k = 0; k < 3; ++k)
2485 {
2486 append_edge_dofs(result, ii, tmp);
2487 tmp = mesh.next_around_face(tmp);
2488 }
2489
2490 // tri face interior nodes: find lf by matching the set of face vertices
2491 if (nfi_tri > 0)
2492 {
2493 static const int tri_fv[4][3] = {{0, 1, 4}, {1, 2, 4}, {2, 3, 4}, {3, 0, 4}};
2494 const int fv0 = index.vertex;
2495 const int fv1 = mesh.next_around_face(index).vertex;
2496 const int fv2 = mesh.next_around_face(mesh.next_around_face(index)).vertex;
2497 int lf = -1;
2498 for (int f = 0; f < 4; ++f)
2499 {
2500 const int gv0 = v[tri_fv[f][0]], gv1 = v[tri_fv[f][1]], gv2 = v[tri_fv[f][2]];
2501 if ((fv0 == gv0 || fv0 == gv1 || fv0 == gv2) && (fv1 == gv0 || fv1 == gv1 || fv1 == gv2) && (fv2 == gv0 || fv2 == gv1 || fv2 == gv2))
2502 {
2503 lf = f;
2504 break;
2505 }
2506 }
2507 assert(lf >= 0);
2508 for (int q = 0; q < nfi_tri; ++q)
2509 result[ii++] = tri_face_start + lf * nfi_tri + q;
2510 }
2511
2512 assert(ii == result.size());
2513 return result;
2514 }
2515
2516 // ---- QUAD face ----
2517 else
2518 {
2519 Eigen::VectorXi result(4 + 4 * nei + nfi_quad);
2520 int ii = 0;
2521
2522 // vertices in face traversal order
2523 result[ii++] = find_index(l2g.begin(), l2g.end(), index.vertex);
2524 result[ii++] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(index).vertex);
2525 result[ii++] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(index)).vertex);
2526 result[ii++] = find_index(l2g.begin(), l2g.end(), mesh.next_around_face(mesh.next_around_face(mesh.next_around_face(index))).vertex);
2527
2528 // edge interiors in traversal order
2529 Navigation3D::Index tmp = index;
2530 for (int k = 0; k < 4; ++k)
2531 {
2532 append_edge_dofs(result, ii, tmp);
2533 tmp = mesh.next_around_face(tmp);
2534 }
2535
2536 // quad face interior nodes (all belong to the single quad face)
2537 for (int q = 0; q < nfi_quad; ++q)
2538 result[ii++] = quad_face_start + q;
2539
2540 assert(ii == result.size());
2541 return result;
2542 }
2543}
2544
2546 const Mesh3D &mesh,
2547 const std::string &assembler,
2548 const int quadrature_order,
2549 const int mass_quadrature_order,
2550 const int discr_orderp,
2551 const int discr_orderq,
2552 const bool bernstein,
2553 const bool serendipity,
2554 const bool has_polys,
2555 const bool is_geom_bases,
2556 const bool use_corner_quadrature,
2557 std::vector<ElementBases> &bases,
2558 std::vector<LocalBoundary> &local_boundary,
2559 std::map<int, InterfaceData> &poly_face_to_data,
2560 std::shared_ptr<MeshNodes> &mesh_nodes)
2561{
2562 Eigen::VectorXi discr_ordersp(mesh.n_cells());
2563 discr_ordersp.setConstant(discr_orderp);
2564
2565 Eigen::VectorXi discr_ordersq(mesh.n_cells());
2566 discr_ordersq.setConstant(discr_orderq);
2567
2568 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);
2569}
2570
2572 const Mesh3D &mesh,
2573 const std::string &assembler,
2574 const int quadrature_order,
2575 const int mass_quadrature_order,
2576 const Eigen::VectorXi &discr_ordersp,
2577 const Eigen::VectorXi &discr_ordersq,
2578 const bool bernstein,
2579 const bool serendipity,
2580 const bool has_polys,
2581 const bool is_geom_bases,
2582 const bool use_corner_quadrature,
2583 std::vector<ElementBases> &bases,
2584 std::vector<LocalBoundary> &local_boundary,
2585 std::map<int, InterfaceData> &poly_face_to_data,
2586 std::shared_ptr<MeshNodes> &mesh_nodes)
2587{
2588 assert(mesh.is_volume());
2589 assert(discr_ordersp.size() == mesh.n_cells());
2590 assert(discr_ordersq.size() == mesh.n_cells());
2591
2592 // Navigation3D::get_index_from_element_face_time = 0;
2593 // Navigation3D::switch_vertex_time = 0;
2594 // Navigation3D::switch_edge_time = 0;
2595 // Navigation3D::switch_face_time = 0;
2596 // Navigation3D::switch_element_time = 0;
2597
2598 const int max_p = discr_ordersp.maxCoeff();
2599 const int max_q = discr_ordersq.maxCoeff();
2600 const int mmax = std::max(max_p, max_q);
2601 // assert(max_p < 5); //P5 not supported
2602
2603 const int nn = mmax > 1 ? (mmax - 1) : 0;
2604 const int n_face_nodes = nn * nn;
2605 const int n_cells_nodes = nn * nn * nn;
2606
2607 Eigen::VectorXi edge_orders, face_orders;
2608 if (!mesh.is_conforming())
2609 {
2610 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
2611 compute_edge_face_orders(ncmesh, discr_ordersp, edge_orders, face_orders);
2612 }
2613
2614 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);
2615 MeshNodes &nodes = *mesh_nodes;
2616 std::vector<std::vector<int>> element_nodes_id, edge_virtual_nodes, face_virtual_nodes;
2617 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);
2618 // boundary_nodes = nodes.boundary_nodes();
2619
2620 bases.resize(mesh.n_cells());
2621 std::vector<int> interface_elements;
2622 interface_elements.reserve(mesh.n_faces());
2623
2624 for (int e = 0; e < mesh.n_cells(); ++e)
2625 {
2626 ElementBases &b = bases[e];
2627 const int discr_order = discr_ordersp(e);
2628 const int discr_orderq = discr_ordersq(e);
2629 const int n_el_bases = (int)element_nodes_id[e].size();
2630 b.bases.resize(n_el_bases);
2631
2632 bool skip_interface_element = false;
2633
2634 for (int j = 0; j < n_el_bases; ++j)
2635 {
2636 const int global_index = element_nodes_id[e][j];
2637 if (global_index < 0)
2638 {
2639 skip_interface_element = true;
2640 break;
2641 }
2642 }
2643
2644 if (skip_interface_element)
2645 {
2646 interface_elements.push_back(e);
2647 }
2648
2649 if (mesh.is_cube(e))
2650 {
2651 const int real_order = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 3);
2652 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 3);
2653 b.set_quadrature([real_order](Quadrature &quad) {
2654 HexQuadrature hex_quadrature;
2655 hex_quadrature.get_quadrature(real_order, quad);
2656 });
2657 b.set_mass_quadrature([real_mass_order](Quadrature &quad) {
2658 HexQuadrature hex_quadrature;
2659 hex_quadrature.get_quadrature(real_mass_order, quad);
2660 });
2661
2662 b.set_local_node_from_primitive_func([serendipity, discr_order, e](const int primitive_id, const Mesh &mesh) {
2663 const auto &mesh3d = dynamic_cast<const Mesh3D &>(mesh);
2664 Navigation3D::Index index;
2665
2666 for (int lf = 0; lf < 6; ++lf)
2667 {
2668 index = mesh3d.get_index_from_element(e, lf, 0);
2669 if (index.face == primitive_id)
2670 break;
2671 }
2672 assert(index.face == primitive_id);
2673 return hex_face_local_nodes(serendipity, discr_order, mesh3d, index);
2674 });
2675
2676 for (int j = 0; j < n_el_bases; ++j)
2677 {
2678 const int global_index = element_nodes_id[e][j];
2679
2680 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2681
2682 const int dtmp = serendipity ? -2 : discr_order;
2683
2684 b.bases[j].set_basis([dtmp, j](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { autogen::q_basis_value_3d(dtmp, j, uv, val); });
2685 b.bases[j].set_grad([dtmp, j](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { autogen::q_grad_basis_value_3d(dtmp, j, uv, val); });
2686 }
2687 }
2688 else if (mesh.is_simplex(e))
2689 {
2690 const int real_order = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 3);
2691 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 3);
2692
2693 b.set_quadrature([real_order, use_corner_quadrature](Quadrature &quad) {
2694 TetQuadrature tet_quadrature(use_corner_quadrature);
2695 tet_quadrature.get_quadrature(real_order, quad);
2696 });
2697 b.set_mass_quadrature([real_mass_order, use_corner_quadrature](Quadrature &quad) {
2698 TetQuadrature tet_quadrature(use_corner_quadrature);
2699 tet_quadrature.get_quadrature(real_mass_order, quad);
2700 });
2701
2702 b.set_local_node_from_primitive_func([discr_order, e](const int primitive_id, const Mesh &mesh) {
2703 const auto &mesh3d = dynamic_cast<const Mesh3D &>(mesh);
2704 Navigation3D::Index index;
2705
2706 for (int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2707 {
2708 index = mesh3d.get_index_from_element(e, lf, 0);
2709 if (index.face == primitive_id)
2710 break;
2711 }
2712 assert(index.face == primitive_id);
2713 return tet_face_local_nodes(discr_order, mesh3d, index);
2714 });
2715
2716 const bool rational = is_geom_bases && mesh.is_rational() && !mesh.cell_weights(e).empty();
2717 assert(!rational);
2718
2719 for (int j = 0; j < n_el_bases; ++j)
2720 {
2721 const int global_index = element_nodes_id[e][j];
2722 if (!skip_interface_element)
2723 {
2724 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2725 }
2726
2727 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); });
2728 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); });
2729 }
2730 }
2731 else if (mesh.is_prism(e))
2732 {
2733 const int orderp = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::PRISM_LAGRANGE, 2);
2734 const int orderq = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, discr_orderq, AssemblerUtils::BasisType::PRISM_LAGRANGE, 1);
2735
2736 const int mass_orderp = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", discr_order, AssemblerUtils::BasisType::PRISM_LAGRANGE, 2);
2737 const int mass_orderq = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", discr_orderq, AssemblerUtils::BasisType::PRISM_LAGRANGE, 1);
2738
2739 b.set_quadrature([orderp, orderq](Quadrature &quad) {
2740 PrismQuadrature tet_quadrature;
2741 tet_quadrature.get_quadrature(orderp, orderq, quad);
2742 });
2743 b.set_mass_quadrature([mass_orderp, mass_orderq](Quadrature &quad) {
2744 PrismQuadrature tet_quadrature;
2745 tet_quadrature.get_quadrature(mass_orderp, mass_orderq, quad);
2746 });
2747
2748 b.set_local_node_from_primitive_func([discr_order, discr_orderq, e](const int primitive_id, const Mesh &mesh) {
2749 const auto &mesh3d = dynamic_cast<const Mesh3D &>(mesh);
2750 Navigation3D::Index index;
2751
2752 for (int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2753 {
2754 index = mesh3d.get_index_from_element(e, lf, 0);
2755 if (index.face == primitive_id)
2756 break;
2757 }
2758 assert(index.face == primitive_id);
2759 return prism_face_local_nodes(discr_order, discr_orderq, mesh3d, index);
2760 });
2761
2762 for (int j = 0; j < n_el_bases; ++j)
2763 {
2764 const int global_index = element_nodes_id[e][j];
2765 if (!skip_interface_element)
2766 {
2767 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2768 }
2769
2770 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); });
2771 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); });
2772 }
2773 }
2774 else if (mesh.is_pyramid(e))
2775 {
2776 const int orderp = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::PYRAMID_LAGRANGE, 2);
2777 const int mass_orderp = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", discr_order, AssemblerUtils::BasisType::PYRAMID_LAGRANGE, 2);
2778
2779 b.set_quadrature([orderp](Quadrature &quad) {
2780 PyramidQuadrature tet_quadrature;
2781 tet_quadrature.get_quadrature(orderp, quad);
2782 });
2783 b.set_mass_quadrature([mass_orderp](Quadrature &quad) {
2784 PyramidQuadrature p_quadrature;
2785 p_quadrature.get_quadrature(mass_orderp, quad);
2786 });
2787
2788 b.set_local_node_from_primitive_func([discr_order, e](const int primitive_id, const Mesh &mesh) {
2789 const auto &mesh3d = dynamic_cast<const Mesh3D &>(mesh);
2790 Navigation3D::Index index;
2791
2792 for (int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2793 {
2794 index = mesh3d.get_index_from_element(e, lf, 0);
2795 if (index.face == primitive_id)
2796 break;
2797 }
2798 assert(index.face == primitive_id);
2799 return pyramid_face_local_nodes(discr_order, mesh3d, index);
2800 });
2801
2802 for (int j = 0; j < n_el_bases; ++j)
2803 {
2804 const int global_index = element_nodes_id[e][j];
2805 if (!skip_interface_element)
2806 {
2807 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2808 }
2809
2810 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); });
2811 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); });
2812 }
2813 }
2814 else
2815 {
2816 // Polyhedra bases are built later on
2817 // assert(false);
2818 }
2819 }
2820
2821 if (!is_geom_bases)
2822 {
2823 if (!mesh.is_conforming())
2824 {
2825 const auto &ncmesh = dynamic_cast<const NCMesh3D &>(mesh);
2826
2827 std::vector<std::vector<int>> elementOrder;
2828 {
2829 const int max_order = discr_ordersp.maxCoeff(), min_order = discr_ordersp.minCoeff();
2830 int max_level = 0;
2831 for (int e = 0; e < ncmesh.n_cells(); e++)
2832 if (max_level < ncmesh.cell_ref_level(e))
2833 max_level = ncmesh.cell_ref_level(e);
2834
2835 elementOrder.resize((max_level + 1) * (max_order - min_order + 1));
2836 int N = 0;
2837 int cur_level = 0;
2838 while (cur_level <= max_level)
2839 {
2840 int order = min_order;
2841 while (order <= max_order)
2842 {
2843 int cur_bucket = (max_order - min_order + 1) * cur_level + (order - min_order);
2844 for (int i = 0; i < ncmesh.n_cells(); i++)
2845 {
2846 if (ncmesh.cell_ref_level(i) != cur_level || discr_ordersp[i] != order)
2847 continue;
2848
2849 N++;
2850 elementOrder[cur_bucket].push_back(i);
2851 }
2852 order++;
2853 }
2854 cur_level++;
2855 }
2856 }
2857
2858 for (const auto &bucket : elementOrder)
2859 {
2860 if (bucket.size() == 0)
2861 continue;
2862 polyfem::utils::maybe_parallel_for((int)bucket.size(), [&](int start, int end, int thread_id) {
2863 for (int e_aux = start; e_aux < end; e_aux++)
2864 {
2865 const int e = bucket[e_aux];
2866 ElementBases &b = bases[e];
2867 const int discr_order = discr_ordersp(e);
2868 const int n_edge_nodes = discr_order - 1;
2869 const int n_face_nodes = (discr_order - 1) * (discr_order - 2) / 2;
2870 const int n_el_bases = element_nodes_id[e].size();
2871
2872 auto v = tet_vertices_local_to_global(mesh, e);
2873
2874 Eigen::Matrix<Navigation3D::Index, 4, 1> cell_faces;
2875 Eigen::Matrix<int, 4, 3> fv;
2876 fv.row(0) << v[0], v[1], v[2];
2877 fv.row(1) << v[0], v[1], v[3];
2878 fv.row(2) << v[1], v[2], v[3];
2879 fv.row(3) << v[2], v[0], v[3];
2880
2881 for (long lf = 0; lf < fv.rows(); ++lf)
2882 {
2883 const auto index = mesh.get_index_from_element_face(e, fv(lf, 0), fv(lf, 1), fv(lf, 2));
2884 cell_faces[lf] = index;
2885 }
2886
2887 Eigen::Matrix<Navigation3D::Index, 6, 1> cell_edges;
2888 Eigen::Matrix<int, 6, 2> ev;
2889 ev.row(0) << v[0], v[1];
2890 ev.row(1) << v[1], v[2];
2891 ev.row(2) << v[2], v[0];
2892
2893 ev.row(3) << v[0], v[3];
2894 ev.row(4) << v[1], v[3];
2895 ev.row(5) << v[2], v[3];
2896
2897 for (int le = 0; le < ev.rows(); ++le)
2898 {
2899 // const auto index = find_edge(mesh, c, ev(le, 0), ev(le, 1));
2900 const auto index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
2901 cell_edges[le] = index;
2902 }
2903
2904 Eigen::MatrixXd verts(4, 3);
2905 for (int i = 0; i < ncmesh.n_cell_vertices(e); i++)
2906 verts.row(i) = ncmesh.point(v[i]);
2907
2908 for (int j = 0; j < n_el_bases; ++j)
2909 {
2910 const int global_index = element_nodes_id[e][j];
2911
2912 if (global_index >= 0)
2913 {
2914 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2915 }
2916 else
2917 {
2918 // vertex node - hanging vertex
2919 if (j < 4)
2920 {
2921 int large_elem = -1;
2922 if (ncmesh.leader_edge_of_vertex(v[j]) >= 0)
2923 {
2924 large_elem = lowest_order_elem_on_edge(ncmesh, discr_ordersp, ncmesh.leader_edge_of_vertex(v[j]));
2925 }
2926 else if (ncmesh.leader_face_of_vertex(v[j]) >= 0)
2927 {
2928 std::vector<int> ids;
2929 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_vertex(v[j]), ids);
2930 assert(ids.size() == 1);
2931 large_elem = ids[0];
2932 }
2933 else
2934 assert(false);
2935
2936 Eigen::MatrixXd large_elem_verts(4, 3);
2937 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
2938 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
2939 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
2940
2941 Eigen::MatrixXd node_position;
2942 global_to_local(large_elem_verts, verts.row(j), node_position);
2943
2944 // evaluate the basis of the large element at this node
2945 const auto &other_bases = bases[large_elem];
2946 std::vector<AssemblyValues> w;
2947 other_bases.evaluate_bases(node_position, w);
2948
2949 // apply basis projection
2950 for (long i = 0; i < w.size(); ++i)
2951 {
2952 assert(w[i].val.size() == 1);
2953 if (std::abs(w[i].val(0)) < 1e-12)
2954 continue;
2955
2956 assert(other_bases.bases[i].global().size() > 0);
2957 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
2958 {
2959 const auto &other_global = other_bases.bases[i].global()[ii];
2960 assert(other_global.index >= 0);
2961 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
2962 }
2963 }
2964 }
2965 // edge node - slave edge / edge on face / constrained order
2966 else if (j < 4 + 6 * n_edge_nodes)
2967 {
2968 const int local_edge_id = (j - 4) / n_edge_nodes;
2969 const int edge_id = cell_edges[local_edge_id].edge;
2970 bool need_extra_fake_nodes = false;
2971 int large_elem = -1;
2972
2973 // slave edge
2974 if (ncmesh.leader_edge_of_edge(edge_id) >= 0)
2975 {
2976 std::vector<int> ids;
2977 ncmesh.get_edge_elements_neighs(ncmesh.leader_edge_of_edge(edge_id), ids);
2978 large_elem = ids[0];
2979 }
2980 // edge on face
2981 else if (ncmesh.leader_face_of_edge(edge_id) >= 0)
2982 {
2983 std::vector<int> ids;
2984 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_edge(edge_id), ids);
2985 assert(ids.size() == 1);
2986 large_elem = ids[0];
2987 }
2988 // constrained order
2989 else if (discr_order > edge_orders[edge_id])
2990 {
2991 int min_order_elem = lowest_order_elem_on_edge(ncmesh, discr_ordersp, edge_id);
2992 // if haven't built min_order_elem? directly contribute to extra nodes
2993 if (discr_ordersp[min_order_elem] < discr_order)
2994 large_elem = min_order_elem;
2995
2996 // constrained order, master edge -- need extra fake nodes
2997 if (large_elem < 0)
2998 {
2999 // assert((edge.order < 2 || edge.global_ids.size() > 0) && edge.slaves.size() > 0);
3000 need_extra_fake_nodes = true;
3001 }
3002 }
3003 else
3004 assert(false);
3005
3006 assert(large_elem >= 0 || need_extra_fake_nodes);
3007 Eigen::MatrixXd lnodes;
3008 autogen::p_nodes_3d(discr_order, lnodes);
3009 Eigen::MatrixXd local_position = lnodes.row(j);
3010 if (need_extra_fake_nodes)
3011 {
3012 Eigen::MatrixXd global_position, edge_verts(2, 3);
3013 Eigen::VectorXd point_weight;
3014
3015 edge_verts.row(0) = ncmesh.point(ncmesh.edge_vertex(edge_id, 0));
3016 edge_verts.row(1) = ncmesh.point(ncmesh.edge_vertex(edge_id, 1));
3017
3018 local_to_global(verts, local_position, global_position);
3019 global_to_local_edge(edge_verts, global_position, point_weight);
3020
3021 std::function<double(const int, const int, const double)> basis_1d = [](const int order, const int id, const double x) -> double {
3022 assert(id <= order && id >= 0);
3023 double y = 1;
3024 for (int o = 0; o <= order; o++)
3025 {
3026 if (o != id)
3027 y *= (x * order - o) / (id - o);
3028 }
3029 return y;
3030 };
3031
3032 // contribution to edge nodes
3033 for (int i = 0; i < edge_virtual_nodes[edge_id].size(); i++)
3034 {
3035 const int global_index = edge_virtual_nodes[edge_id][i];
3036 // const double weight = basis_1d(edge_orders[edge_id], i+1, edge_weight);
3037 Eigen::VectorXd node_weight;
3038 global_to_local_edge(edge_verts, nodes.node_position(global_index), node_weight);
3039 const int basis_id = std::lround(node_weight(0) * edge_orders[edge_id]);
3040 const double weight = basis_1d(edge_orders[edge_id], basis_id, point_weight(0));
3041 if (std::abs(weight) < 1e-12)
3042 continue;
3043 b.bases[j].global().emplace_back(global_index, nodes.node_position(global_index), weight);
3044 }
3045
3046 // contribution to vertex nodes
3047 for (int i = 0; i < 2; i++)
3048 {
3049 const int lv = ev(local_edge_id, i);
3050 const auto &global_ = b.bases[lv].global();
3051 Eigen::VectorXd node_weight;
3052 global_to_local_edge(edge_verts, verts.row(lv), node_weight);
3053 const int basis_id = std::lround(node_weight(0) * edge_orders[edge_id]);
3054 const double weight = basis_1d(edge_orders[edge_id], basis_id, point_weight(0));
3055 if (std::abs(weight) > 1e-12)
3056 {
3057 assert(global_.size() > 0);
3058 for (size_t ii = 0; ii < global_.size(); ++ii)
3059 b.bases[j].global().emplace_back(global_[ii].index, global_[ii].node, weight * global_[ii].val);
3060 }
3061 }
3062 }
3063 else
3064 {
3065 Eigen::MatrixXd global_position, large_elem_verts(4, 3);
3066 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
3067 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
3068 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
3069 local_to_global(verts, local_position, global_position);
3070 global_to_local(large_elem_verts, global_position, local_position);
3071
3072 // evaluate the basis of the large element at this node
3073 const auto &other_bases = bases[large_elem];
3074 std::vector<AssemblyValues> w;
3075 other_bases.evaluate_bases(local_position, w);
3076
3077 // apply basis projection
3078 for (long i = 0; i < w.size(); ++i)
3079 {
3080 assert(w[i].val.size() == 1);
3081 if (std::abs(w[i].val(0)) < 1e-12)
3082 continue;
3083
3084 assert(other_bases.bases[i].global().size() > 0);
3085 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3086 {
3087 const auto &other_global = other_bases.bases[i].global()[ii];
3088 assert(other_global.index >= 0);
3089 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3090 }
3091 }
3092 }
3093 }
3094 // face node - slave face / constrained order
3095 else if (j < 4 + 6 * n_edge_nodes + 4 * n_face_nodes)
3096 {
3097 const int local_face_id = (j - (4 + 6 * n_edge_nodes)) / n_face_nodes;
3098 const int face_id = cell_faces(local_face_id).face;
3099 int large_elem = -1;
3100 bool need_extra_fake_nodes = false;
3101
3102 std::vector<int> ids;
3103 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_face(face_id), ids);
3104
3105 Eigen::MatrixXd face_verts(3, 3);
3106 for (int i = 0; i < ncmesh.n_face_vertices(face_id); i++)
3107 face_verts.row(i) = ncmesh.point(ncmesh.face_vertex(face_id, i));
3108
3109 // slave face
3110 if (ncmesh.leader_face_of_face(face_id) >= 0)
3111 {
3112 assert(ids.size() == 1);
3113 large_elem = ids[0];
3114 }
3115 // constrained order, conforming face
3116
3117 else if (face_orders[face_id] < discr_order && ids.size() == 2)
3118 {
3119 large_elem = ids[0] == e ? ids[1] : ids[0];
3120 }
3121 // constrained order, master face -- need extra fake nodes
3122 else if (face_orders[face_id] < discr_order && ncmesh.n_follower_faces(face_id) > 0)
3123 {
3124 // assert(ncface.global_ids.size() > 0 || ncface.order < 3);
3125 need_extra_fake_nodes = true;
3126 }
3127 else
3128 assert(false);
3129
3130 assert(large_elem >= 0 || need_extra_fake_nodes);
3131 Eigen::MatrixXd lnodes;
3132 autogen::p_nodes_3d(discr_order, lnodes);
3133 Eigen::MatrixXd local_position = lnodes.row(j);
3134 if (need_extra_fake_nodes)
3135 {
3136 Eigen::MatrixXd global_position;
3137 local_to_global(verts, local_position, global_position);
3138
3139 Eigen::MatrixXd tmp;
3140 global_to_local_face(face_verts, global_position, tmp);
3141 Eigen::VectorXd face_weight = tmp.transpose();
3142
3143 std::function<double(const int, const int, const double)> basis_aux = [](const int order, const int id, const double x) -> double {
3144 assert(id <= order && id >= 0);
3145 double y = 1;
3146 for (int o = 0; o < id; o++)
3147 y *= (x * order - o) / (id - o);
3148 return y;
3149 };
3150
3151 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 {
3152 assert(i + j <= order && i >= 0 && j >= 0);
3153 double u = uv(0), v = uv(1);
3154 return basis_aux(order, i, u) * basis_aux(order, j, v) * basis_aux(order, order - i - j, 1 - u - v);
3155 };
3156
3157 // contribution to face nodes
3158 for (int global_ : face_virtual_nodes[face_id])
3159 {
3160 auto low_order_node = nodes.node_position(global_);
3161 Eigen::MatrixXd low_order_node_face_weight;
3162 global_to_local_face(face_verts, low_order_node, low_order_node_face_weight);
3163 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]);
3164 const double weight = basis_2d(face_orders[face_id], x, y, face_weight);
3165 if (std::abs(weight) < 1e-12)
3166 continue;
3167 b.bases[j].global().emplace_back(global_, nodes.node_position(global_), weight);
3168 }
3169
3170 // contribution to vertex nodes
3171 for (int i = 0; i < 3; i++)
3172 {
3173 const auto &global_ = b.bases[fv(local_face_id, i)].global();
3174 auto low_order_node = ncmesh.point(fv(local_face_id, i));
3175 Eigen::MatrixXd low_order_node_face_weight;
3176 global_to_local_face(face_verts, low_order_node, low_order_node_face_weight);
3177 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]);
3178 double weight = basis_2d(face_orders[face_id], x, y, face_weight);
3179 if (std::abs(weight) > 1e-12)
3180 {
3181 assert(global_.size() > 0);
3182 for (size_t ii = 0; ii < global_.size(); ++ii)
3183 b.bases[j].global().emplace_back(global_[ii].index, global_[ii].node, weight * global_[ii].val);
3184 }
3185 }
3186
3187 // contribution to edge nodes, two steps
3188 for (int x = 0, idx = 0; x <= face_orders[face_id]; x++)
3189 {
3190 for (int y = 0; x + y <= face_orders[face_id]; y++)
3191 {
3192 const int z = face_orders[face_id] - x - y;
3193 int flag = (int)(x == 0) + (int)(y == 0) + (int)(z == 0);
3194 if (flag != 1)
3195 continue;
3196
3197 // first step
3198 const double weight = basis_2d(face_orders[face_id], x, y, face_weight);
3199 if (std::abs(weight) < 1e-12)
3200 continue;
3201 Eigen::MatrixXd face_weight(1, 2);
3202 face_weight << (double)x / face_orders[face_id], (double)y / face_orders[face_id];
3203 Eigen::MatrixXd pos, local_pos;
3204 local_to_global_face(face_verts, face_weight, pos);
3205 global_to_local(verts, pos, local_pos);
3206 Local2Global step1(idx, local_pos, weight);
3207 idx++;
3208
3209 {
3210 // evaluate the basis of the large element at this node
3211 const auto &other_bases = bases[e];
3212 std::vector<AssemblyValues> w;
3213 other_bases.evaluate_bases(local_pos, w);
3214
3215 // apply basis projection
3216 for (long i = 0; i < w.size(); ++i)
3217 {
3218 assert(w[i].val.size() == 1);
3219 if (std::abs(w[i].val(0)) < 1e-12)
3220 continue;
3221
3222 assert(other_bases.bases[i].global().size() > 0);
3223 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3224 {
3225 const auto &other_global = other_bases.bases[i].global()[ii];
3226 assert(other_global.index >= 0);
3227 b.bases[j].global().emplace_back(other_global.index, other_global.node, step1.val * w[i].val(0) * other_global.val);
3228 }
3229 }
3230 }
3231 }
3232 }
3233 }
3234 else
3235 {
3236 Eigen::MatrixXd global_position, large_elem_verts(4, 3);
3237 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
3238 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
3239 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
3240 local_to_global(verts, local_position, global_position);
3241 global_to_local(large_elem_verts, global_position, local_position);
3242
3243 // evaluate the basis of the large element at this node
3244 const auto &other_bases = bases[large_elem];
3245 std::vector<AssemblyValues> w;
3246 other_bases.evaluate_bases(local_position, w);
3247
3248 // apply basis projection
3249 for (long i = 0; i < w.size(); ++i)
3250 {
3251 assert(w[i].val.size() == 1);
3252 if (std::abs(w[i].val(0)) < 1e-12)
3253 continue;
3254
3255 assert(other_bases.bases[i].global().size() > 0);
3256 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3257 {
3258 const auto &other_global = other_bases.bases[i].global()[ii];
3259 assert(other_global.index >= 0);
3260 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3261 }
3262 }
3263 }
3264 }
3265 else
3266 assert(false);
3267
3268 auto &global_ = b.bases[j].global();
3269 if (global_.size() <= 1)
3270 continue;
3271
3272 std::map<int, Local2Global> list;
3273 for (size_t ii = 0; ii < global_.size(); ii++)
3274 {
3275 auto pair = list.insert({global_[ii].index, global_[ii]});
3276 if (!pair.second && pair.first != list.end())
3277 {
3278 assert((pair.first->second.node - global_[ii].node).norm() < 1e-12);
3279 pair.first->second.val += global_[ii].val;
3280 }
3281 }
3282
3283 global_.clear();
3284 for (auto it = list.begin(); it != list.end(); ++it)
3285 {
3286 if (std::abs(it->second.val) > 1e-12)
3287 {
3288 global_.push_back(it->second);
3289 }
3290 }
3291 }
3292 }
3293 }
3294 });
3295 }
3296 }
3297 else
3298 {
3299 for (int pp = 2; pp <= autogen::MAX_P_BASES; ++pp)
3300 {
3301 for (int e : interface_elements)
3302 {
3303 ElementBases &b = bases[e];
3304 // todo non conforming
3305 const int discr_order = discr_ordersp(e);
3306 const int n_el_bases = element_nodes_id[e].size();
3307 assert(discr_order > 1);
3308 if (discr_order != pp)
3309 continue;
3310
3311 if (mesh.is_cube(e) || mesh.is_prism(e))
3312 {
3313 // TODO
3314 assert(false);
3315 }
3316 else if (mesh.is_simplex(e))
3317 {
3318 for (int j = 0; j < n_el_bases; ++j)
3319 {
3320 const int global_index = element_nodes_id[e][j];
3321
3322 if (global_index >= 0)
3323 {
3324 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
3325 }
3326 else
3327 {
3328 const int lnn = max_p > 2 ? (discr_order - 2) : 0;
3329 const int ln_edge_nodes = discr_order - 1;
3330 const int ln_face_nodes = lnn * (lnn + 1) / 2;
3331
3332 const auto v = tet_vertices_local_to_global(mesh, e);
3333 Navigation3D::Index index;
3334 if (global_index <= -30)
3335 {
3336 assert(false);
3337 // const auto lv = -(global_index + 30);
3338 // assert(lv>=0 && lv < 4);
3339 // assert(j < 4);
3340
3341 // if(lv == 3)
3342 // {
3343 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[0]));
3344 // if(index.element < 0)
3345 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[1]));
3346 // if(index.element < 0)
3347 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[2]));
3348 // }
3349 // else
3350 // {
3351 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[(lv+1)%3]));
3352 // if(index.element < 0)
3353 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[(lv+2)%3]));
3354 // if(index.element < 0)
3355 // index = mesh.switch_element(find_edge(mesh, e, v[lv], v[3]));
3356 // }
3357 }
3358 else if (global_index <= -10)
3359 {
3360 const auto le = -(global_index + 10);
3361 assert(le >= 0 && le < 6);
3362 assert(j >= 4 && j < 4 + 6 * ln_edge_nodes);
3363
3364 Eigen::Matrix<int, 6, 2> ev;
3365 ev.row(0) << v[0], v[1];
3366 ev.row(1) << v[1], v[2];
3367 ev.row(2) << v[2], v[0];
3368
3369 ev.row(3) << v[0], v[3];
3370 ev.row(4) << v[1], v[3];
3371 ev.row(5) << v[2], v[3];
3372
3373 // const auto edge_index = find_edge(mesh, e, ev(le, 0), ev(le, 1));
3374 const auto edge_index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
3375 auto neighs = mesh.edge_neighs(edge_index.edge);
3376 int min_p = discr_order;
3377 int min_cell = edge_index.element;
3378
3379 for (auto cid : neighs)
3380 {
3381 if (discr_ordersp[cid] < min_p)
3382 {
3383 min_p = discr_ordersp[cid];
3384 min_cell = cid;
3385 }
3386 }
3387
3388 bool found = false;
3389
3390 // check min neighbour cell type
3391 if (mesh.is_simplex(min_cell))
3392 {
3393 for (int lf = 0; lf < 4; ++lf)
3394 {
3395 for (int lv = 0; lv < 4; ++lv)
3396 {
3397 index = mesh.get_index_from_element(min_cell, lf, lv);
3398
3399 if (index.vertex == edge_index.vertex)
3400 {
3401 if (index.edge != edge_index.edge)
3402 {
3403 auto tmp = index;
3404 index = mesh.switch_edge(tmp);
3405
3406 if (index.edge != edge_index.edge)
3407 {
3408 index = mesh.switch_edge(mesh.switch_face(tmp));
3409 }
3410 }
3411 found = true;
3412 break;
3413 }
3414 }
3415
3416 if (found)
3417 break;
3418 }
3419 }
3420 else if (mesh.is_prism(min_cell))
3421 {
3422 for (int lf = 0; lf < 5; ++lf) // loop only 2 faces
3423 {
3424 for (int lv = 0; lv < 5; ++lv) // quad face 4 vertices + 1 dummmy
3425 {
3426 index = mesh.get_index_from_element(min_cell, lf, lv); // correct for all elem types
3427 if (index.vertex == edge_index.vertex)
3428 {
3429 if (index.edge != edge_index.edge)
3430 {
3431 auto tmp = index;
3432 index = mesh.switch_edge(tmp);
3433
3434 if (index.edge != edge_index.edge)
3435 {
3436 index = mesh.switch_edge(mesh.switch_face(tmp));
3437 }
3438 }
3439 found = true;
3440 break;
3441 }
3442 }
3443 }
3444
3445 if (mesh.n_face_vertices(index.face) == 4)
3446 index = mesh.switch_face(index); // switch to tri face
3447
3448 assert(mesh.n_face_vertices(index.face) == 3);
3449 assert(found);
3450 assert(index.vertex == edge_index.vertex && index.edge == edge_index.edge);
3451 assert(index.element != edge_index.element);
3452 }
3453 else if (mesh.is_pyramid(min_cell))
3454 {
3455 assert(false);
3456 }
3457 }
3458 else
3459 {
3460 const auto lf = -(global_index + 1);
3461 assert(lf >= 0 && lf < 4);
3462 assert(j >= 4 + 6 * ln_edge_nodes && j < 4 + 6 * ln_edge_nodes + 4 * ln_face_nodes);
3463
3464 Eigen::Matrix<int, 4, 3> fv;
3465 fv.row(0) << v[0], v[1], v[2];
3466 fv.row(1) << v[0], v[1], v[3];
3467 fv.row(2) << v[1], v[2], v[3];
3468 fv.row(3) << v[2], v[0], v[3];
3469
3470 index = mesh.switch_element(mesh.get_index_from_element_face(e, fv(lf, 0), fv(lf, 1), fv(lf, 2)));
3471 }
3472
3473 const auto other_cell = index.element;
3474 assert(other_cell >= 0);
3475
3476 Eigen::VectorXi indices;
3477 Eigen::MatrixXd lnodes;
3478 Eigen::RowVector3d node_position; // = lnodes.row(indices(ii));
3479
3480 if (mesh.is_simplex(other_cell))
3481 {
3482 assert(discr_order > discr_ordersp(other_cell));
3483 indices = tet_face_local_nodes(discr_order, mesh, index);
3484 autogen::p_nodes_3d(discr_order, lnodes);
3485 }
3486 else if (mesh.is_prism(other_cell))
3487 {
3488 assert(discr_order > discr_ordersp(other_cell));
3489 indices = prism_face_local_nodes(discr_order, discr_order, mesh, index);
3490 autogen::prism_nodes_3d(discr_order, discr_order, lnodes);
3491 }
3492 else
3493 {
3494 assert(false);
3495 }
3496
3497 if (j < 4)
3498 node_position = lnodes.row(indices(0));
3499 else if (j < 4 + 6 * ln_edge_nodes)
3500 {
3501 if (mesh.is_prism(other_cell))
3502 {
3503 // Locate the shared edge node directly from its two
3504 // endpoint vertices in the prism reference frame. The
3505 // prism-face navigation used below mis-selects the
3506 // triangular face for anisotropic prisms (p != q),
3507 // placing the node on the wrong z-face and flipping the
3508 // tet. The endpoints are shared prism vertices, so this
3509 // is unambiguous; evaluate_bases then handles the prism's
3510 // actual (possibly lower) order along the edge.
3511 static const int tet_edge_v[6][2] =
3512 {{0, 1}, {1, 2}, {2, 0}, {0, 3}, {1, 3}, {2, 3}};
3513 const int le2 = -(global_index + 10);
3514 const auto pl2g = prism_vertices_local_to_global(mesh, other_cell);
3515 const int i0 = find_index(pl2g.begin(), pl2g.end(), v[tet_edge_v[le2][0]]);
3516 const int i1 = find_index(pl2g.begin(), pl2g.end(), v[tet_edge_v[le2][1]]);
3517 const int k = (j - 4) % ln_edge_nodes;
3518 const double t = double(k + 1) / discr_order;
3519 node_position = (1.0 - t) * lnodes.row(i0) + t * lnodes.row(i1);
3520 }
3521 else
3522 node_position = lnodes.row(indices(((j - 4) % ln_edge_nodes) + 3));
3523 }
3524 else if (j < 4 + 6 * ln_edge_nodes + 4 * ln_face_nodes)
3525 {
3526 // node_position = lnodes.row(indices(((j - 4 - 6*ln_edge_nodes) % ln_face_nodes) + 3 + 3*ln_edge_nodes));
3527 auto me_indices = tet_face_local_nodes(discr_order, mesh, mesh.switch_element(index));
3528 int ii;
3529 for (ii = 0; ii < me_indices.size(); ++ii)
3530 {
3531 if (me_indices(ii) == j)
3532 break;
3533 }
3534
3535 assert(ii >= 3 + 3 * ln_edge_nodes);
3536 assert(ii < me_indices.size());
3537
3538 node_position = lnodes.row(indices(ii));
3539 }
3540 else
3541 assert(false);
3542
3543 const auto &other_bases = bases[other_cell];
3544 // Eigen::MatrixXd w;
3545 std::vector<AssemblyValues> w;
3546 other_bases.evaluate_bases(node_position, w);
3547
3548 assert(b.bases[j].global().size() == 0);
3549
3550 for (long i = 0; i < w.size(); ++i)
3551 {
3552 assert(w[i].val.size() == 1);
3553 if (std::abs(w[i].val(0)) < 1e-8)
3554 continue;
3555
3556 // assert(other_bases.bases[i].global().size() == 1);
3557 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3558 {
3559 const auto &other_global = other_bases.bases[i].global()[ii];
3560 // logger().trace("e {} j {} gid {}", e, j, other_global.index);
3561 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3562 }
3563 }
3564 }
3565 }
3566 }
3567 else if (mesh.is_pyramid(e))
3568 {
3569 for (int j = 0; j < n_el_bases; ++j)
3570 {
3571 const int global_index = element_nodes_id[e][j];
3572
3573 if (global_index >= 0)
3574 {
3575 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
3576 }
3577 else
3578 {
3579 const int lnn = discr_order - 1;
3580 const int ln_edge_nodes = discr_order - 1;
3581 const int ln_face_nodes = lnn * lnn;
3582
3583 const auto v = pyramid_vertices_local_to_global(mesh, e);
3584 Navigation3D::Index index;
3585 if (global_index <= -30)
3586 {
3587 assert(false);
3588 }
3589 else if (global_index <= -10)
3590 {
3591 const int le = -(global_index + 10);
3592 assert(le >= 0 && le < 4);
3593 assert(j >= 5 && j < 5 + 8 * ln_edge_nodes);
3594
3595 Eigen::Matrix<int, 4, 2> ev;
3596 ev.row(0) << v[0], v[1];
3597 ev.row(1) << v[1], v[2];
3598 ev.row(2) << v[2], v[3];
3599 ev.row(3) << v[3], v[0];
3600
3601 const auto edge_index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
3602 auto neighs = mesh.edge_neighs(edge_index.edge);
3603 int min_p = discr_order;
3604 int min_cell = edge_index.element;
3605
3606 for (auto cid : neighs)
3607 {
3608 const int cid_order =
3609 prism_edge_order(cid, edge_index.edge, discr_ordersp, discr_ordersq, mesh);
3610
3611 if (cid_order < min_p)
3612 {
3613 min_p = cid_order;
3614 min_cell = cid;
3615 }
3616 }
3617
3618 bool found = false;
3619 if (mesh.is_prism(min_cell))
3620 {
3621 for (int lf = 0; lf < 5; ++lf) // loop only 2 faces
3622 {
3623 for (int lv = 0; lv < 5; ++lv) // quad face 4 vertices + 1 dummmy
3624 {
3625 index = mesh.get_index_from_element(min_cell, lf, lv); // correct for all elem types
3626 if (index.vertex == edge_index.vertex)
3627 {
3628 if (index.edge != edge_index.edge)
3629 {
3630 auto tmp = index;
3631 index = mesh.switch_edge(tmp);
3632
3633 if (index.edge != edge_index.edge)
3634 {
3635 index = mesh.switch_edge(mesh.switch_face(tmp));
3636 }
3637 }
3638 found = true;
3639 break;
3640 }
3641 }
3642 if (found)
3643 break;
3644 }
3645
3646 if (mesh.n_face_vertices(index.face) == 3)
3647 index = mesh.switch_face(index); // switch to quad face
3648
3649 assert(mesh.n_face_vertices(index.face) == 4);
3650 assert(found);
3651 assert(index.vertex == edge_index.vertex && index.edge == edge_index.edge);
3652 assert(index.element != edge_index.element);
3653 }
3654 else
3655 {
3656 assert(false);
3657 }
3658 }
3659 else
3660 {
3661 const auto lf = -(global_index + 1);
3662 assert(lf == 4);
3663
3664 index = mesh.switch_element(find_quad_face(mesh, e, v[0], v[1], v[2], v[3]));
3665 }
3666
3667 const auto other_cell = index.element;
3668 assert(other_cell >= 0);
3669
3670 Eigen::VectorXi indices;
3671 Eigen::MatrixXd lnodes;
3672 Eigen::RowVector3d node_position; // = lnodes.row(indices(ii));
3673
3674 if (mesh.is_prism(other_cell))
3675 {
3676 assert(
3677 (global_index <= -10 && discr_order > prism_edge_order(other_cell, index.edge, discr_ordersp, discr_ordersq, mesh))
3678 || (global_index > -10 && (discr_order > discr_ordersp(other_cell) || discr_order > discr_ordersq(other_cell))));
3679
3680 indices = prism_face_local_nodes(discr_order, discr_order, mesh, index);
3681 autogen::prism_nodes_3d(discr_order, discr_order, lnodes);
3682 }
3683 else
3684 {
3685 assert(false);
3686 }
3687
3688 const int tri_face_nodes = (discr_order - 1) * (discr_order - 2) / 2;
3689 const int quad_face_start = 5 + 8 * ln_edge_nodes + 4 * tri_face_nodes;
3690
3691 if (j < 5)
3692 node_position = lnodes.row(indices(0));
3693 else if (j < 5 + 8 * ln_edge_nodes)
3694 {
3695 const int le = -(global_index + 10);
3696 const int edge_offset = j - (5 + le * ln_edge_nodes);
3697 assert(j >= 5 + le * ln_edge_nodes);
3698 assert(j < 5 + (le + 1) * ln_edge_nodes);
3699
3700 if (mesh.is_prism(other_cell))
3701 {
3702 // Endpoint-based placement (see tet branch): locate the
3703 // shared edge node from its two endpoint vertices in the
3704 // prism reference frame. The prism-face navigation
3705 // mis-selects the face for anisotropic prisms (p != q),
3706 // which for q < p corrupts the prism/pyramid quad-face
3707 // interface.
3708 static const int pyr_edge_v[4][2] =
3709 {{0, 1}, {1, 2}, {2, 3}, {3, 0}};
3710 const auto pl2g = prism_vertices_local_to_global(mesh, other_cell);
3711 const int i0 = find_index(pl2g.begin(), pl2g.end(), v[pyr_edge_v[le][0]]);
3712 const int i1 = find_index(pl2g.begin(), pl2g.end(), v[pyr_edge_v[le][1]]);
3713 const double t = double(edge_offset + 1) / discr_order;
3714 node_position = (1.0 - t) * lnodes.row(i0) + t * lnodes.row(i1);
3715 }
3716 else
3717 // we are on a quad face
3718 node_position = lnodes.row(indices(4 + edge_offset));
3719 }
3720 else if (j >= quad_face_start && j < quad_face_start + ln_face_nodes)
3721 {
3722 auto me_indices = pyramid_face_local_nodes(discr_order, mesh, mesh.switch_element(index));
3723 int ii;
3724 for (ii = 0; ii < me_indices.size(); ++ii)
3725 {
3726 if (me_indices(ii) == j)
3727 break;
3728 }
3729
3730 assert(ii >= 4 + 4 * ln_edge_nodes);
3731 assert(ii < me_indices.size());
3732
3733 node_position = lnodes.row(indices(ii));
3734 }
3735 else
3736 assert(false);
3737
3738 const auto &other_bases = bases[other_cell];
3739 // Eigen::MatrixXd w;
3740 std::vector<AssemblyValues> w;
3741 other_bases.evaluate_bases(node_position, w);
3742
3743 assert(b.bases[j].global().size() == 0);
3744
3745 for (long i = 0; i < w.size(); ++i)
3746 {
3747 assert(w[i].val.size() == 1);
3748 if (std::abs(w[i].val(0)) < 1e-8)
3749 continue;
3750
3751 // assert(other_bases.bases[i].global().size() == 1);
3752 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3753 {
3754 const auto &other_global = other_bases.bases[i].global()[ii];
3755 // logger().trace("e {} j {} gid {}", e, j, other_global.index);
3756 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3757 }
3758 }
3759 }
3760 }
3761 }
3762 else
3763 {
3764 // Polygon bases are built later on
3765 }
3766 }
3767 }
3768 }
3769 }
3770
3771 return nodes.n_nodes();
3772}
Eigen::MatrixXd vec
Definition Assembler.cpp:75
double val
Definition Assembler.cpp:89
Eigen::RowVectorXd point
double J
int edge_id
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:49
bool is_polytope(const int el_id) const
checks if element is polygon compatible
Definition Mesh.cpp:450
bool is_rational() const
check if curved mesh has rational polynomials elements
Definition Mesh.hpp:300
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:437
virtual RowVectorNd point(const int global_index) const =0
point coordinates
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:499
bool is_simplex(const int el_id) const
checks if element is simplex
Definition Mesh.cpp:507
bool is_prism(const int el_id) const
checks if element is a prism
Definition Mesh.cpp:512
const std::vector< double > & cell_weights(const int cell_index) const
weights for rational polynomial meshes
Definition Mesh.hpp:590
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:517
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:67
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:495
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:366
str nodes
Definition p_bases.py:399
list indices
Definition p_bases.py:259
list vv
Definition p_bases.py:263
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)
std::vector< int > local_indices