PolyFEM
Loading...
Searching...
No Matches
SplineBasis2d.cpp
Go to the documentation of this file.
1#include "SplineBasis2d.hpp"
2
3#include "LagrangeBasis2d.hpp"
5
8
10
11#include <polysolve/linear/Solver.hpp>
12
14
15#include <polyfem/Common.hpp>
17
18#include <Eigen/Sparse>
19
20#include <cassert>
21#include <iostream>
22#include <vector>
23#include <array>
24#include <map>
25
26// TODO carefull with simplices
27
28namespace polyfem
29{
30 using namespace polysolve;
31 using namespace Eigen;
32 using namespace assembler;
33 using namespace mesh;
34 using namespace quadrature;
35
36 namespace basis
37 {
38 namespace
39 {
40 typedef Matrix<std::vector<int>, 3, 3> SpaceMatrix;
41 typedef Matrix<RowVectorNd, 3, 3> NodeMatrix;
42
43 void print_local_space(const SpaceMatrix &space)
44 {
45 std::cout << std::endl;
46 for (int j = 2; j >= 0; --j)
47 {
48 for (int i = 0; i < 3; ++i)
49 {
50 if (space(i, j).size() > 0)
51 {
52 for (std::size_t l = 0; l < space(i, j).size(); ++l)
53 std::cout << space(i, j)[l] << ",";
54
55 std::cout << "\t";
56 }
57 else
58 std::cout << "x\t";
59 }
60 std::cout << std::endl;
61 }
62 }
63
64 int node_id_from_edge_index(const Mesh2D &mesh, MeshNodes &mesh_nodes, const Navigation::Index &index)
65 {
66 const int face_id = mesh.switch_face(index).face;
67 if (face_id >= 0 && mesh.is_cube(face_id))
68 return mesh_nodes.node_id_from_face(face_id);
69
70 return mesh_nodes.node_id_from_edge(index.edge);
71 }
72
73 void explore_direction(const Navigation::Index &index, const Mesh2D &mesh, MeshNodes &mesh_nodes, const int x, const int y, const bool is_x, const bool invert, LocalBoundary &lb, SpaceMatrix &space, NodeMatrix &node, std::map<int, InterfaceData> &poly_edge_to_data)
74 {
75 int node_id = node_id_from_edge_index(mesh, mesh_nodes, index);
76 // bool real_boundary = mesh.node_id_from_edge_index(index, node_id);
77
78 assert(std::find(space(x, y).begin(), space(x, y).end(), node_id) == space(x, y).end());
79 space(x, y).push_back(node_id);
80 node(x, y) = mesh_nodes.node_position(node_id);
81 assert(node(x, y).size() == 2);
82
83 const int x1 = is_x ? x : (invert ? 2 : 0);
84 const int y1 = !is_x ? y : (invert ? 2 : 0);
85
86 const int x2 = is_x ? x : (invert ? 0 : 2);
87 const int y2 = !is_x ? y : (invert ? 0 : 2);
88
89 const bool is_boundary = mesh_nodes.is_boundary(node_id);
90 const bool is_interface = mesh_nodes.is_interface(node_id);
91
92 if (is_boundary)
93 {
94 lb.add_boundary_primitive(index.edge, LagrangeBasis2d::quad_edge_local_nodes(2, mesh, index)[1] - 4);
95 // lb.add_boundary_primitive(index.edge, LagrangeBasis2d::quadr_quad_edge_local_nodes(mesh, index)[1]-4);
96 // bounday_nodes.push_back(node_id);
97 }
98 else if (is_interface)
99 {
100 InterfaceData &data = poly_edge_to_data[index.edge];
101 data.local_indices.push_back(y * 3 + x);
102 }
103 else
104 {
105 assert(!is_boundary && !is_interface);
106
107 Navigation::Index start_index = mesh.switch_face(index);
108 assert(start_index.vertex == index.vertex);
109 assert(start_index.face >= 0);
110
111 Navigation::Index edge1 = mesh.switch_edge(start_index);
112 node_id = node_id_from_edge_index(mesh, mesh_nodes, edge1);
113 // if(mesh_nodes.is_boundary(node_id))
114 // bounday_nodes.push_back(node_id);
115
116 if (std::find(space(x1, y1).begin(), space(x1, y1).end(), node_id) == space(x1, y1).end())
117 {
118 space(x1, y1).push_back(node_id);
119 node(x1, y1) = mesh_nodes.node_position(node_id);
120 }
121
122 Navigation::Index edge2 = mesh.switch_edge(mesh.switch_vertex(start_index));
123 node_id = node_id_from_edge_index(mesh, mesh_nodes, edge2);
124 // if(mesh_nodes.is_boundary(node_id))
125 // bounday_nodes.push_back(node_id);
126 if (std::find(space(x2, y2).begin(), space(x2, y2).end(), node_id) == space(x2, y2).end())
127 {
128 space(x2, y2).push_back(node_id);
129 node(x2, y2) = mesh_nodes.node_position(node_id);
130 // node(x2, y2).push_back(mesh.node_from_edge_index(edge2));
131 }
132 }
133 }
134
135 void add_id_for_poly(const Navigation::Index &index, const int x1, const int y1, const int x2, const int y2, const SpaceMatrix &space, std::map<int, InterfaceData> &poly_edge_to_data)
136 {
137 auto it = poly_edge_to_data.find(index.edge);
138 if (it != poly_edge_to_data.end())
139 {
140 InterfaceData &data = it->second;
141
142 assert(space(x1, y1).size() == 1);
143 data.local_indices.push_back(y1 * 3 + x1);
144
145 assert(space(x2, y2).size() == 1);
146 data.local_indices.push_back(y2 * 3 + x2);
147 }
148 }
149
150 void build_local_space(const Mesh2D &mesh, MeshNodes &mesh_nodes, const int el_index, SpaceMatrix &space, NodeMatrix &node, std::vector<LocalBoundary> &local_boundary, std::map<int, InterfaceData> &poly_edge_to_data)
151 {
152 assert(!mesh.is_volume());
153
154 Navigation::Index index;
155 // space.setConstant(-1);
156
157 const int el_node_id = mesh_nodes.node_id_from_face(el_index);
158 space(1, 1).push_back(el_node_id);
159 node(1, 1) = mesh_nodes.node_position(el_node_id);
160 // (mesh.node_from_face(el_index));
161
162 LocalBoundary lb(el_index, BoundaryType::QUAD_LINE);
163
165 index = mesh.get_index_from_face(el_index);
166 explore_direction(index, mesh, mesh_nodes, 1, 0, false, false, lb, space, node, poly_edge_to_data);
167
169 index = mesh.next_around_face(index);
170 explore_direction(index, mesh, mesh_nodes, 2, 1, true, false, lb, space, node, poly_edge_to_data);
171
173 index = mesh.next_around_face(index);
174 explore_direction(index, mesh, mesh_nodes, 1, 2, false, true, lb, space, node, poly_edge_to_data);
175
177 index = mesh.next_around_face(index);
178 explore_direction(index, mesh, mesh_nodes, 0, 1, true, true, lb, space, node, poly_edge_to_data);
179
181 if (mesh_nodes.is_boundary_or_interface(space(1, 2).front()) && mesh_nodes.is_boundary_or_interface(space(2, 1).front()))
182 {
183 assert(space(2, 2).empty());
184
185 Navigation::Index start_index = mesh.get_index_from_face(el_index);
186 start_index = mesh.next_around_face(start_index);
187 start_index = mesh.next_around_face(start_index);
188
189 const int node_id = mesh_nodes.node_id_from_vertex(start_index.vertex);
190 // mesh.vertex_node_id(start_index.vertex);
191 space(2, 2).push_back(node_id);
192 node(2, 2) = mesh_nodes.node_position(node_id);
193 // node(2,2).push_back(mesh.node_from_vertex(start_index.vertex));
194
195 // bounday_nodes.push_back(node_id);
196 }
197
198 if (mesh_nodes.is_boundary_or_interface(space(1, 0).front()) && mesh_nodes.is_boundary_or_interface(space(2, 1).front()))
199 {
200 assert(space(2, 0).empty());
201
202 Navigation::Index start_index = mesh.get_index_from_face(el_index);
203 start_index = mesh.next_around_face(start_index);
204
205 const int node_id = mesh_nodes.node_id_from_vertex(start_index.vertex);
206 // mesh.vertex_node_id(start_index.vertex);
207 space(2, 0).push_back(node_id);
208 node(2, 0) = mesh_nodes.node_position(node_id);
209 // .push_back(mesh.node_from_vertex(start_index.vertex));
210
211 // bounday_nodes.push_back(node_id);
212 }
213
214 if (mesh_nodes.is_boundary_or_interface(space(1, 2).front()) && mesh_nodes.is_boundary_or_interface(space(0, 1).front()))
215 {
216 assert(space(0, 2).empty());
217
218 Navigation::Index start_index = mesh.get_index_from_face(el_index);
219 start_index = mesh.next_around_face(start_index);
220 start_index = mesh.next_around_face(start_index);
221 start_index = mesh.next_around_face(start_index);
222
223 // const int node_id = mesh.vertex_node_id(start_index.vertex);
224 const int node_id = mesh_nodes.node_id_from_vertex(start_index.vertex);
225 space(0, 2).push_back(node_id);
226 node(0, 2) = mesh_nodes.node_position(node_id);
227 // .push_back(mesh.node_from_vertex(start_index.vertex));
228
229 // bounday_nodes.push_back(node_id);
230 }
231
232 if (mesh_nodes.is_boundary_or_interface(space(1, 0).front()) && mesh_nodes.is_boundary_or_interface(space(0, 1).front()))
233 {
234 Navigation::Index start_index = mesh.get_index_from_face(el_index);
235
236 // const int node_id = mesh.vertex_node_id(start_index.vertex);
237 const int node_id = mesh_nodes.node_id_from_vertex(start_index.vertex);
238 space(0, 0).push_back(node_id);
239 node(0, 0) = mesh_nodes.node_position(node_id);
240 //.push_back(mesh.node_from_vertex(start_index.vertex));
241
242 // bounday_nodes.push_back(node_id);
243 }
244
245 // std::cout<<std::endl;
246 // print_local_space(space);
247
249 index = mesh.get_index_from_face(el_index);
250 add_id_for_poly(index, 0, 0, 2, 0, space, poly_edge_to_data);
251
252 index = mesh.next_around_face(index);
253 add_id_for_poly(index, 2, 0, 2, 2, space, poly_edge_to_data);
254
255 index = mesh.next_around_face(index);
256 add_id_for_poly(index, 2, 2, 0, 2, space, poly_edge_to_data);
257
258 index = mesh.next_around_face(index);
259 add_id_for_poly(index, 0, 2, 0, 0, space, poly_edge_to_data);
260
261 if (!lb.empty())
262 local_boundary.emplace_back(lb);
263 }
264
265 void setup_knots_vectors(MeshNodes &mesh_nodes, const SpaceMatrix &space, std::array<std::array<double, 4>, 3> &h_knots, std::array<std::array<double, 4>, 3> &v_knots)
266 {
267 // left and right neigh are absent
268 if (mesh_nodes.is_boundary_or_interface(space(0, 1).front()) && mesh_nodes.is_boundary_or_interface(space(2, 1).front()))
269 {
270 h_knots[0] = {{0, 0, 0, 1}};
271 h_knots[1] = {{0, 0, 1, 1}};
272 h_knots[2] = {{0, 1, 1, 1}};
273 }
274 // left neigh is absent
275 else if (mesh_nodes.is_boundary_or_interface(space(0, 1).front()))
276 {
277 h_knots[0] = {{0, 0, 0, 1}};
278 h_knots[1] = {{0, 0, 1, 2}};
279 h_knots[2] = {{0, 1, 2, 3}};
280 }
281 // right neigh is absent
282 else if (mesh_nodes.is_boundary_or_interface(space(2, 1).front()))
283 {
284 h_knots[0] = {{-2, -1, 0, 1}};
285 h_knots[1] = {{-1, 0, 1, 1}};
286 h_knots[2] = {{0, 1, 1, 1}};
287 }
288 else
289 {
290 h_knots[0] = {{-2, -1, 0, 1}};
291 h_knots[1] = {{-1, 0, 1, 2}};
292 h_knots[2] = {{0, 1, 2, 3}};
293 }
294
295 // top and bottom neigh are absent
296 if (mesh_nodes.is_boundary_or_interface(space(1, 0).front()) && mesh_nodes.is_boundary_or_interface(space(1, 2).front()))
297 {
298 v_knots[0] = {{0, 0, 0, 1}};
299 v_knots[1] = {{0, 0, 1, 1}};
300 v_knots[2] = {{0, 1, 1, 1}};
301 }
302 // bottom neigh is absent
303 else if (mesh_nodes.is_boundary_or_interface(space(1, 0).front()))
304 {
305 v_knots[0] = {{0, 0, 0, 1}};
306 v_knots[1] = {{0, 0, 1, 2}};
307 v_knots[2] = {{0, 1, 2, 3}};
308 }
309 // top neigh is absent
310 else if (mesh_nodes.is_boundary_or_interface(space(1, 2).front()))
311 {
312 v_knots[0] = {{-2, -1, 0, 1}};
313 v_knots[1] = {{-1, 0, 1, 1}};
314 v_knots[2] = {{0, 1, 1, 1}};
315 }
316 else
317 {
318 v_knots[0] = {{-2, -1, 0, 1}};
319 v_knots[1] = {{-1, 0, 1, 2}};
320 v_knots[2] = {{0, 1, 2, 3}};
321 }
322 }
323
324 void basis_for_regular_quad(const SpaceMatrix &space, const NodeMatrix &loc_nodes, const std::array<std::array<double, 4>, 3> &h_knots, const std::array<std::array<double, 4>, 3> &v_knots, ElementBases &b)
325 {
326 for (int y = 0; y < 3; ++y)
327 {
328 for (int x = 0; x < 3; ++x)
329 {
330 if (space(x, y).size() == 1)
331 {
332 const int global_index = space(x, y).front();
333 const Eigen::MatrixXd &node = loc_nodes(x, y);
334 assert(node.size() == 2);
335
336 const int local_index = y * 3 + x;
337 b.bases[local_index].init(2, global_index, local_index, node);
338
339 const QuadraticBSpline2d spline(h_knots[x], v_knots[y]);
340 b.bases[local_index].set_basis([spline](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { spline.interpolate(uv, val); });
341 b.bases[local_index].set_grad([spline](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { spline.derivative(uv, val); });
342 }
343 }
344 }
345 }
346
347 void basis_for_irregulard_quad(const int el_id, const Mesh2D &mesh, MeshNodes &mesh_nodes, const SpaceMatrix &space, const NodeMatrix &loc_nodes, const std::array<std::array<double, 4>, 3> &h_knots, const std::array<std::array<double, 4>, 3> &v_knots, ElementBases &b)
348 {
349 for (int y = 0; y < 3; ++y)
350 {
351 for (int x = 0; x < 3; ++x)
352 {
353 if (space(x, y).size() > 1)
354 {
355 const int mpx = 1;
356 const int mpy = y;
357
358 const int mmx = x;
359 const int mmy = 1;
360
361 std::vector<int> other_indices;
362 const auto &center = b.bases[1 * 3 + 1].global().front();
363
364 const auto &el1 = b.bases[mpy * 3 + mpx].global().front();
365 const auto &el2 = b.bases[mmy * 3 + mmx].global().front();
366
367 Navigation::Index start_index = mesh.get_index_from_face(el_id);
368 bool found = false;
369 for (int i = 0; i < 4; ++i)
370 {
371 other_indices.clear();
372 int n_neighs = 0;
373 Navigation::Index index = start_index;
374 do
375 {
376 const int f_index = mesh_nodes.node_id_from_face(index.face);
377 if (f_index != el1.index && f_index != el2.index && f_index != center.index)
378 other_indices.push_back(f_index);
379
380 ++n_neighs;
381 index = mesh.next_around_vertex(index);
382 } while (index.face != start_index.face);
383 if (n_neighs != 4)
384 {
385 found = true;
386 break;
387 }
388
389 start_index = mesh.next_around_face(start_index);
390 }
391 assert(found);
392
393 const int local_index = y * 3 + x;
394 auto &base = b.bases[local_index];
395
396 const int k = int(other_indices.size()) + 3;
397
398 base.global().resize(k);
399
400 base.global()[0].index = center.index;
401 base.global()[0].val = (4. - k) / k;
402 base.global()[0].node = center.node;
403
404 base.global()[1].index = el1.index;
405 base.global()[1].val = (4. - k) / k;
406 base.global()[1].node = el1.node;
407
408 base.global()[2].index = el2.index;
409 base.global()[2].val = (4. - k) / k;
410 base.global()[2].node = el2.node;
411
412 for (std::size_t n = 0; n < other_indices.size(); ++n)
413 {
414 base.global()[3 + n].index = other_indices[n];
415 base.global()[3 + n].val = 4. / k;
416 base.global()[3 + n].node = mesh_nodes.node_position(other_indices[n]);
417 }
418
419 const QuadraticBSpline2d spline(h_knots[x], v_knots[y]);
420 b.bases[local_index].set_basis([spline](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { spline.interpolate(uv, val); });
421 b.bases[local_index].set_grad([spline](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { spline.derivative(uv, val); });
422 }
423 }
424 }
425 }
426
427 void create_q2_nodes(const Mesh2D &mesh, const int el_index, std::set<int> &vertex_id, std::set<int> &edge_id, ElementBases &b, std::vector<LocalBoundary> &local_boundary, int &n_bases)
428 {
429 b.bases.resize(9);
430
431 LocalBoundary lb(el_index, BoundaryType::QUAD_LINE);
432
433 Navigation::Index index = mesh.get_index_from_face(el_index);
434 for (int j = 0; j < 4; ++j)
435 {
436 int current_vertex_node_id = -1;
437 int current_edge_node_id = -1;
438 Eigen::Matrix<double, 1, 2> current_edge_node;
439 Eigen::MatrixXd current_vertex_node;
440
441 // auto e2l = LagrangeBasis2d::quadr_quad_edge_local_nodes(mesh, index);
442 auto e2l = LagrangeBasis2d::quad_edge_local_nodes(2, mesh, index);
443
444 int vertex_basis_id = e2l[0];
445 int edge_basis_id = e2l[1];
446
447 const int opposite_face = mesh.switch_face(index).face;
448
449 // if the edge/vertex is boundary the it is a Q2 edge
450 bool is_vertex_q2 = true;
451 bool is_vertex_boundary = false;
452
453 Navigation::Index vindex = index;
454
455 do
456 {
457 if (vindex.face < 0)
458 {
459 is_vertex_boundary = true;
460 break;
461 }
462 if (mesh.is_spline_compatible(vindex.face))
463 {
464 is_vertex_q2 = false;
465 break;
466 }
467 vindex = mesh.next_around_vertex(vindex);
468 } while (vindex.edge != index.edge);
469
470 if (is_vertex_q2)
471 {
472 vindex = mesh.switch_face(index);
473 do
474 {
475 if (vindex.face < 0)
476 {
477 is_vertex_boundary = true;
478 break;
479 }
480
481 if (mesh.is_spline_compatible(vindex.face))
482 {
483 is_vertex_q2 = false;
484 break;
485 }
486 vindex = mesh.next_around_vertex(vindex);
487 } while (vindex.edge != index.edge);
488 }
489
490 const bool is_edge_q2 = opposite_face < 0 || !mesh.is_spline_compatible(opposite_face);
491
492 if (is_edge_q2)
493 {
494 const bool is_new_edge = edge_id.insert(index.edge).second;
495
496 if (is_new_edge)
497 {
498 current_edge_node_id = n_bases++;
499 current_edge_node = mesh.edge_barycenter(index.edge);
500
501 if (opposite_face < 0)
502 {
503 // bounday_nodes.push_back(current_edge_node_id);
504 lb.add_boundary_primitive(index.edge, edge_basis_id - 4);
505 }
506 }
507 }
508
509 if (is_vertex_q2)
510 {
511 assert(is_edge_q2);
512 const bool is_new_vertex = vertex_id.insert(index.vertex).second;
513
514 if (is_new_vertex)
515 {
516 current_vertex_node_id = n_bases++;
517 current_vertex_node = mesh.point(index.vertex);
518
519 // if(is_vertex_boundary)//mesh.is_vertex_boundary(index.vertex))
520 // bounday_nodes.push_back(current_vertex_node_id);
521 }
522 }
523
524 // init new Q2 nodes
525 if (current_vertex_node_id >= 0)
526 b.bases[vertex_basis_id].init(2, current_vertex_node_id, vertex_basis_id, current_vertex_node);
527
528 if (current_edge_node_id >= 0)
529 b.bases[edge_basis_id].init(2, current_edge_node_id, edge_basis_id, current_edge_node);
530
531 // set the basis functions
532 b.bases[vertex_basis_id].set_basis([vertex_basis_id](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { polyfem::autogen::q_basis_value_2d(2, vertex_basis_id, uv, val); });
533 b.bases[vertex_basis_id].set_grad([vertex_basis_id](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { polyfem::autogen::q_grad_basis_value_2d(2, vertex_basis_id, uv, val); });
534
535 b.bases[edge_basis_id].set_basis([edge_basis_id](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { polyfem::autogen::q_basis_value_2d(2, edge_basis_id, uv, val); });
536 b.bases[edge_basis_id].set_grad([edge_basis_id](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { polyfem::autogen::q_grad_basis_value_2d(2, edge_basis_id, uv, val); });
537
538 index = mesh.next_around_face(index);
539 }
540
541 // central node always present
542 const int face_basis_id = 8;
543 b.bases[face_basis_id].init(2, n_bases++, face_basis_id, mesh.face_barycenter(el_index));
544 b.bases[face_basis_id].set_basis([face_basis_id](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { polyfem::autogen::q_basis_value_2d(2, face_basis_id, uv, val); });
545 b.bases[face_basis_id].set_grad([face_basis_id](const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) { polyfem::autogen::q_grad_basis_value_2d(2, face_basis_id, uv, val); });
546
547 if (!lb.empty())
548 local_boundary.emplace_back(lb);
549 }
550
551 void insert_into_global(const Local2Global &data, std::vector<Local2Global> &vec)
552 {
553 // ignore small weights
554 if (fabs(data.val) < 1e-10)
555 return;
556
557 bool found = false;
558
559 for (std::size_t i = 0; i < vec.size(); ++i)
560 {
561 if (vec[i].index == data.index)
562 {
563 // std::cout<<vec[i].val <<" "<< data.val<<" "<<fabs(vec[i].val - data.val)<<std::endl;
564 assert(fabs(vec[i].val - data.val) < 1e-10);
565 assert((vec[i].node - data.node).norm() < 1e-10);
566 found = true;
567 break;
568 }
569 }
570
571 if (!found)
572 vec.push_back(data);
573 }
574
575 void assign_q2_weights(const Mesh2D &mesh, const int el_index, std::vector<ElementBases> &bases)
576 {
577 // Eigen::MatrixXd eval_p;
578 std::vector<AssemblyValues> eval_p;
579 Navigation::Index index = mesh.get_index_from_face(el_index);
580 ElementBases &b = bases[el_index];
581
582 for (int j = 0; j < 4; ++j)
583 {
584 const int opposite_face = mesh.switch_face(index).face;
585
586 if (opposite_face < 0 || !mesh.is_cube(opposite_face))
587 {
588 index = mesh.next_around_face(index);
589 continue;
590 }
591
592 // const auto param_p = LagrangeBasis2d::quadr_quad_edge_local_nodes_coordinates(mesh, mesh.switch_face(index));
593 // const auto indices = LagrangeBasis2d::quadr_quad_edge_local_nodes(mesh, index);
594
595 const auto indices = LagrangeBasis2d::quad_edge_local_nodes(2, mesh, index);
596 Eigen::Matrix<double, 3, 2> param_p;
597
598 {
599 Eigen::MatrixXd quad_loc_nodes;
600 polyfem::autogen::q_nodes_2d(2, quad_loc_nodes);
601 const auto opposite_indices = LagrangeBasis2d::quad_edge_local_nodes(2, mesh, mesh.switch_face(index));
602 for (int k = 0; k < 3; ++k)
603 param_p.row(k) = quad_loc_nodes.row(opposite_indices[k]);
604 }
605
606 // std::cout<<param_p<<"\n---------\n"<<std::endl;
607
608 const int i0 = indices[0];
609 const int i1 = indices[1];
610 const int i2 = indices[2];
611
612 const auto &other_bases = bases[opposite_face];
613 other_bases.evaluate_bases(param_p, eval_p);
614
615 for (std::size_t i = 0; i < other_bases.bases.size(); ++i)
616 {
617 const auto &other_b = other_bases.bases[i];
618
619 if (other_b.global().empty())
620 continue;
621
622 assert(eval_p[i].val.size() == 3);
623
624 // basis i of element opposite face is zero on this elements
625 if (eval_p[i].val.cwiseAbs().maxCoeff() <= 1e-10)
626 continue;
627
628 for (std::size_t k = 0; k < other_b.global().size(); ++k)
629 {
630 // auto glob0 = other_b.global()[k]; glob0.val *= eval_p(0,i);
631 // auto glob1 = other_b.global()[k]; glob1.val *= eval_p(1,i);
632 // auto glob2 = other_b.global()[k]; glob2.val *= eval_p(2,i);
633
634 auto glob0 = other_b.global()[k];
635 glob0.val *= eval_p[i].val(0);
636 auto glob1 = other_b.global()[k];
637 glob1.val *= eval_p[i].val(1);
638 auto glob2 = other_b.global()[k];
639 glob2.val *= eval_p[i].val(2);
640
641 insert_into_global(glob0, b.bases[i0].global());
642 insert_into_global(glob1, b.bases[i1].global());
643 insert_into_global(glob2, b.bases[i2].global());
644 }
645 }
646
647 index = mesh.next_around_face(index);
648 }
649 }
650
651 void setup_data_for_polygons(const Mesh2D &mesh, const int el_index, const ElementBases &b, std::map<int, InterfaceData> &poly_edge_to_data)
652 {
653 Navigation::Index index = mesh.get_index_from_face(el_index);
654 for (int j = 0; j < 4; ++j)
655 {
656 const int opposite_face = mesh.switch_face(index).face;
657 const bool is_neigh_poly = opposite_face >= 0 && mesh.is_polytope(opposite_face);
658
659 if (is_neigh_poly)
660 {
661 // auto e2l = LagrangeBasis2d::quadr_quad_edge_local_nodes(mesh, index);
662 auto e2l = LagrangeBasis2d::quad_edge_local_nodes(2, mesh, index);
663 const int vertex_basis_id = e2l[0];
664 const int edge_basis_id = e2l[1];
665 const int vertex_basis_id2 = e2l[2];
666
667 InterfaceData &data = poly_edge_to_data[index.edge];
668
669 data.local_indices.push_back(edge_basis_id);
670 data.local_indices.push_back(vertex_basis_id);
671 data.local_indices.push_back(vertex_basis_id2);
672 }
673
674 index = mesh.next_around_face(index);
675 }
676 }
677 } // namespace
678
680 const std::string &assembler,
681 const int quadrature_order, const int mass_quadrature_order, std::vector<ElementBases> &bases, std::vector<LocalBoundary> &local_boundary, std::map<int, InterfaceData> &poly_edge_to_data)
682 {
683 using std::max;
684 assert(!mesh.is_volume());
685
686 MeshNodes mesh_nodes(mesh, true, true, 1, 1);
687
688 const int n_els = mesh.n_elements();
689 bases.resize(n_els);
690
691 local_boundary.clear();
692
693 // QuadQuadrature quad_quadrature;
694
695 for (int e = 0; e < n_els; ++e)
696 {
697 if (!mesh.is_spline_compatible(e))
698 continue;
699
700 SpaceMatrix space;
701 NodeMatrix loc_nodes;
702
703 // const int max_local_base =
704 build_local_space(mesh, mesh_nodes, e, space, loc_nodes, local_boundary, poly_edge_to_data);
705 // n_bases = max(n_bases, max_local_base);
706
707 ElementBases &b = bases[e];
708 // quad_quadrature.get_quadrature(quadrature_order, b.quadrature);
709 const int real_order = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, 2, AssemblerUtils::BasisType::SPLINE, 2);
710 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", 2, AssemblerUtils::BasisType::SPLINE, 2);
711
712 b.set_quadrature([real_order](Quadrature &quad) {
713 QuadQuadrature quad_quadrature;
714 quad_quadrature.get_quadrature(real_order, quad);
715 });
716 b.set_mass_quadrature([real_mass_order](Quadrature &quad) {
717 QuadQuadrature quad_quadrature;
718 quad_quadrature.get_quadrature(real_mass_order, quad);
719 });
720 b.bases.resize(9);
721
722 b.set_local_node_from_primitive_func([e](const int primitive_id, const Mesh &mesh) {
723 Eigen::VectorXi res(3);
724 const auto &mesh2d = dynamic_cast<const Mesh2D &>(mesh);
725 auto index = mesh2d.get_index_from_face(e);
726 int le;
727 for (le = 0; le < mesh2d.n_face_vertices(e); ++le)
728 {
729 if (index.edge == primitive_id)
730 break;
731 index = mesh2d.next_around_face(index);
732 }
733 assert(index.edge == primitive_id);
734
735 switch (le)
736 {
737 case 3:
738 res << (3 * 0 + 0), (3 * 1 + 0), (3 * 2 + 0);
739 break;
740 case 0:
741 res << (3 * 0 + 0), (3 * 0 + 1), (3 * 0 + 2);
742 break;
743 case 1:
744 res << (3 * 0 + 2), (3 * 1 + 2), (3 * 2 + 2);
745 break;
746 case 2:
747 res << (3 * 2 + 0), (3 * 2 + 1), (3 * 2 + 2);
748 break;
749 default:
750 assert(false);
751 }
752
753 return res;
754 });
755
756 std::array<std::array<double, 4>, 3> h_knots;
757 std::array<std::array<double, 4>, 3> v_knots;
758
759 setup_knots_vectors(mesh_nodes, space, h_knots, v_knots);
760
761 // print_local_space(space);
762
763 basis_for_regular_quad(space, loc_nodes, h_knots, v_knots, b);
764 basis_for_irregulard_quad(e, mesh, mesh_nodes, space, loc_nodes, h_knots, v_knots, b);
765 }
766
767 std::set<int> edge_id;
768 std::set<int> vertex_id;
769
770 int n_bases = mesh_nodes.n_nodes();
771
772 for (int e = 0; e < n_els; ++e)
773 {
774 if (mesh.is_polytope(e) || mesh.is_spline_compatible(e))
775 continue;
776
777 ElementBases &b = bases[e];
778 // quad_quadrature.get_quadrature(quadrature_order, b.quadrature);
779 const int real_order = quadrature_order > 0 ? quadrature_order : AssemblerUtils::quadrature_order(assembler, 2, AssemblerUtils::BasisType::CUBE_LAGRANGE, 2);
780 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order : AssemblerUtils::quadrature_order("Mass", 2, AssemblerUtils::BasisType::CUBE_LAGRANGE, 2);
781
782 b.set_quadrature([real_order](Quadrature &quad) {
783 QuadQuadrature quad_quadrature;
784 quad_quadrature.get_quadrature(real_order, quad);
785 });
786 b.set_mass_quadrature([real_mass_order](Quadrature &quad) {
787 QuadQuadrature quad_quadrature;
788 quad_quadrature.get_quadrature(real_mass_order, quad);
789 });
790
791 b.set_local_node_from_primitive_func([e](const int primitive_id, const Mesh &mesh) {
792 const auto &mesh2d = dynamic_cast<const Mesh2D &>(mesh);
793 auto index = mesh2d.get_index_from_face(e);
794
795 for (int le = 0; le < mesh2d.n_face_vertices(e); ++le)
796 {
797 if (index.edge == primitive_id)
798 break;
799 index = mesh2d.next_around_face(index);
800 }
801 assert(index.edge == primitive_id);
802
803 // const auto indices = LagrangeBasis2d::quadr_quad_edge_local_nodes(mesh2d, index);
804 const auto indices = LagrangeBasis2d::quad_edge_local_nodes(2, mesh2d, index);
805 Eigen::VectorXi res(indices.size());
806
807 for (size_t i = 0; i < indices.size(); ++i)
808 res(i) = indices[i];
809
810 return res;
811 });
812
813 create_q2_nodes(mesh, e, vertex_id, edge_id, b, local_boundary, n_bases);
814 }
815
816 bool missing_bases = false;
817 do
818 {
819 missing_bases = false;
820 for (int e = 0; e < n_els; ++e)
821 {
822 if (mesh.is_polytope(e) || mesh.is_spline_compatible(e))
823 continue;
824
825 auto &b = bases[e];
826 if (b.is_complete())
827 continue;
828
829 assign_q2_weights(mesh, e, bases);
830
831 missing_bases = missing_bases || b.is_complete();
832 }
833 } while (missing_bases);
834
835 for (int e = 0; e < n_els; ++e)
836 {
837 if (mesh.is_polytope(e) || mesh.is_spline_compatible(e))
838 continue;
839 const ElementBases &b = bases[e];
840 setup_data_for_polygons(mesh, e, b, poly_edge_to_data);
841 }
842
843 return n_bases;
844 }
845
846 void SplineBasis2d::fit_nodes(const Mesh2D &mesh, const int n_bases, std::vector<ElementBases> &gbases)
847 {
848 assert(false);
849 // const int dim = 2;
850 // const int n_constraints = 9;
851 // const int n_elements = mesh.n_elements();
852
853 // std::vector< Eigen::Triplet<double> > entries, entries_t;
854
855 // MeshNodes nodes(mesh, 1, 1, 0);
856 // // Eigen::MatrixXd tmp;
857 // std::vector<AssemblyValues> tmp_val;
858
859 // Eigen::MatrixXd node_rhs(n_constraints*n_elements, dim);
860 // Eigen::MatrixXd samples(n_constraints, dim);
861 // polyfem::autogen::q_nodes_2d(2, samples);
862 // // for(int i = 0; i < n_constraints; ++i)
863 // // samples.row(i) = LagrangeBasis2d::quadr_quad_local_node_coordinates(i);
864
865 // for(int i = 0; i < n_elements; ++i)
866 // {
867 // auto &base = gbases[i];
868
869 // if(!mesh.is_cube(i))
870 // continue;
871
872 // auto global_ids = LagrangeBasis2d::quadr_quad_local_to_global(mesh, i);
873 // assert(global_ids.size() == n_constraints);
874
875 // for(int j = 0; j < n_constraints; ++j)
876 // {
877 // auto n_id = nodes.node_id_from_primitive(global_ids[j]);
878 // auto n = nodes.node_position(n_id);
879 // for(int d = 0; d < dim; ++d)
880 // node_rhs(n_constraints*i + j, d) = n(d);
881 // }
882
883 // base.evaluate_bases(samples, tmp_val);
884 // const auto &lbs = base.bases;
885
886 // const int n_local_bases = int(lbs.size());
887 // for(int j = 0; j < n_local_bases; ++j)
888 // {
889 // const Basis &b = lbs[j];
890 // const auto &tmp = tmp_val[j].val;
891
892 // for(std::size_t ii = 0; ii < b.global().size(); ++ii)
893 // {
894 // for (long k = 0; k < tmp.size(); ++k)
895 // {
896 // entries.emplace_back(n_constraints*i + k, b.global()[ii].index, tmp(k)*b.global()[ii].val);
897 // entries_t.emplace_back(b.global()[ii].index, n_constraints*i + k, tmp(k)*b.global()[ii].val);
898 // }
899 // }
900 // }
901 // }
902
903 // Eigen::MatrixXd new_nodes(n_bases, dim);
904
905 // {
906 // StiffnessMatrix mat(n_constraints*n_elements, n_bases);
907 // StiffnessMatrix mat_t(n_bases, n_constraints*n_elements);
908
909 // mat.setFromTriplets(entries.begin(), entries.end());
910 // mat_t.setFromTriplets(entries_t.begin(), entries_t.end());
911
912 // StiffnessMatrix A = mat_t * mat;
913 // Eigen::MatrixXd b = mat_t * node_rhs;
914
915 // json params = {
916 // {"mtype", -2}, // matrix type for Pardiso (2 = SPD)
917 // // {"max_iter", 0}, // for iterative solvers
918 // // {"tolerance", 1e-9}, // for iterative solvers
919 // };
920 // auto solver = LinearSolver::create("", "");
921 // solver->setParameters(params);
922 // solver->analyzePattern(A);
923 // solver->factorize(A);
924
925 // for(int d = 0; d < dim; ++d)
926 // solver->solve(b.col(d), new_nodes.col(d));
927 // }
928
929 // for(int i = 0; i < n_elements; ++i)
930 // {
931 // auto &base = gbases[i];
932
933 // if(!mesh.is_cube(i))
934 // continue;
935
936 // auto &lbs = base.bases;
937 // const int n_local_bases = int(lbs.size());
938 // for(int j = 0; j < n_local_bases; ++j)
939 // {
940 // Basis &b = lbs[j];
941
942 // for(std::size_t ii = 0; ii < b.global().size(); ++ii)
943 // {
944 // // if(nodes.is_primitive_boundary(b.global()[ii].index))
945 // // continue;
946
947 // for(int d = 0; d < dim; ++d)
948 // {
949 // b.global()[ii].node(d) = new_nodes(b.global()[ii].index, d);
950 // }
951 // }
952 // }
953 // }
954 }
955 } // namespace basis
956} // namespace polyfem
Eigen::MatrixXd vec
Definition Assembler.cpp:72
double val
Definition Assembler.cpp:86
Quadrature quadrature
int y
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).
void set_quadrature(const QuadratureFunction &fun)
void set_local_node_from_primitive_func(LocalNodeFromPrimitiveFunc fun)
sets mapping from local nodes to global nodes
std::vector< Basis > bases
one basis function per node in the element
void set_mass_quadrature(const QuadratureFunction &fun)
static Eigen::VectorXi quad_edge_local_nodes(const int q, const mesh::Mesh2D &mesh, mesh::Navigation::Index index)
static void fit_nodes(const mesh::Mesh2D &mesh, const int n_bases, std::vector< ElementBases > &gbases)
static int build_bases(const mesh::Mesh2D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_edge_to_data)
bool is_volume() const override
checks if mesh is volume
Definition Mesh2D.hpp:25
virtual Navigation::Index get_index_from_face(int f, int lv=0) const =0
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:39
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:161
bool is_polytope(const int el_id) const
checks if element is polygon compatible
Definition Mesh.cpp:365
bool is_spline_compatible(const int el_id) const
checks if element is spline compatible
Definition Mesh.cpp:332
void get_quadrature(const int order, Quadrature &quad)
str base
Definition p_bases.py:402
list indices
Definition p_bases.py:232
void q_nodes_2d(const int q, Eigen::MatrixXd &val)
void q_grad_basis_value_2d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void q_basis_value_2d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)