PolyFEM
Loading...
Searching...
No Matches
BoundarySampler.cpp
Go to the documentation of this file.
1#include "BoundarySampler.hpp"
2
7
12
15
16#include <cassert>
17
18namespace polyfem
19{
20 using namespace mesh;
21 using namespace quadrature;
22 namespace utils
23 {
24 namespace
25 {
26 Eigen::RowVector2d quad_local_node_coordinates(int local_index)
27 {
28 assert(local_index >= 0 && local_index < 4);
29 Eigen::MatrixXd p;
31 return Eigen::RowVector2d(p(local_index, 0), p(local_index, 1));
32 }
33
34 Eigen::RowVector2d tri_local_node_coordinates(int local_index)
35 {
36 Eigen::MatrixXd p;
38 return Eigen::RowVector2d(p(local_index, 0), p(local_index, 1));
39 }
40
41 Eigen::RowVector3d linear_tet_local_node_coordinates(int local_index)
42 {
43 Eigen::MatrixXd p;
45 return Eigen::RowVector3d(p(local_index, 0), p(local_index, 1), p(local_index, 2));
46 }
47
48 Eigen::RowVector3d linear_hex_local_node_coordinates(int local_index)
49 {
50 Eigen::MatrixXd p;
52 return Eigen::RowVector3d(p(local_index, 0), p(local_index, 1), p(local_index, 2));
53 }
54
55 Eigen::RowVector3d linear_prism_local_node_coordinates(int local_index)
56 {
57 Eigen::MatrixXd p;
59 return Eigen::RowVector3d(p(local_index, 0), p(local_index, 1), p(local_index, 2));
60 }
61
62 Eigen::RowVector3d linear_pyramid_local_node_coordinates(int local_index)
63 {
64 Eigen::MatrixXd p;
66 return Eigen::RowVector3d(p(local_index, 0), p(local_index, 1), p(local_index, 2));
67 }
68
69 Eigen::Matrix2d quad_local_node_coordinates_from_edge(int le)
70 {
71 Eigen::Matrix2d res(2, 2);
72 res.row(0) = quad_local_node_coordinates(le);
73 res.row(1) = quad_local_node_coordinates((le + 1) % 4);
74
75 return res;
76 }
77
78 Eigen::Matrix2d tri_local_node_coordinates_from_edge(int le)
79 {
80 Eigen::Matrix2d res(2, 2);
81 res.row(0) = tri_local_node_coordinates(le);
82 res.row(1) = tri_local_node_coordinates((le + 1) % 3);
83
84 return res;
85 }
86 } // namespace
87
89 {
90 Eigen::Matrix2d res(2, 2);
91 res.row(0) = quad_local_node_coordinates(le);
92 res.row(1) = quad_local_node_coordinates((le + 1) % 4);
93
94 return res;
95 }
96
98 {
99 Eigen::Matrix2d res(2, 2);
100 res.row(0) = tri_local_node_coordinates(le);
101 res.row(1) = tri_local_node_coordinates((le + 1) % 3);
102
103 return res;
104 }
105
107 {
108 Eigen::Matrix<int, 4, 3> fv;
109 fv.row(0) << 0, 1, 2;
110 fv.row(1) << 0, 1, 3;
111 fv.row(2) << 1, 2, 3;
112 fv.row(3) << 2, 0, 3;
113
114 Eigen::MatrixXd res(3, 3);
115 for (int i = 0; i < 3; ++i)
116 res.row(i) = linear_tet_local_node_coordinates(fv(lf, i));
117
118 return res;
119 }
120
122 {
123 Eigen::Matrix<int, 6, 4> fv;
124 fv.row(0) << 0, 3, 7, 4;
125 fv.row(1) << 1, 2, 6, 5;
126 fv.row(2) << 0, 1, 5, 4;
127 fv.row(3) << 3, 2, 6, 7;
128 fv.row(4) << 0, 1, 2, 3;
129 fv.row(5) << 4, 5, 6, 7;
130
131 Eigen::MatrixXd res(4, 3);
132 for (int i = 0; i < 4; ++i)
133 res.row(i) = linear_hex_local_node_coordinates(fv(lf, i));
134
135 return res;
136 }
137
139 {
140 Eigen::Matrix<int, 5, 4> fv;
141 fv.row(0) << 0, 1, 2, -1;
142 fv.row(1) << 3, 4, 5, -1;
143
144 fv.row(2) << 0, 1, 4, 3;
145 fv.row(3) << 1, 2, 5, 4;
146 fv.row(4) << 2, 0, 3, 5;
147
148 const int nv = lf < 2 ? 3 : 4;
149 Eigen::MatrixXd res(nv, 3);
150 for (int i = 0; i < nv; ++i)
151 res.row(i) = linear_prism_local_node_coordinates(fv(lf, i));
152
153 return res;
154 }
155
157 {
158 Eigen::Matrix<int, 5, 4> fv;
159 fv.row(0) << 0, 3, 2, 1;
160 fv.row(1) << 0, 1, 4, -1;
161 fv.row(2) << 1, 2, 4, -1;
162 fv.row(3) << 2, 3, 4, -1;
163 fv.row(4) << 3, 0, 4, -1;
164
165 const int nv = (lf == 0) ? 4 : 3; // 0 is quad face
166 Eigen::MatrixXd res(nv, 3);
167 for (int i = 0; i < nv; ++i)
168 res.row(i) = linear_pyramid_local_node_coordinates(fv(lf, i));
169
170 return res;
171 }
172
173 void utils::BoundarySampler::quadrature_for_quad_edge(int index, int order, int gid, const Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
174 {
175 auto endpoints = quad_local_node_coordinates_from_edge(index);
176
177 Quadrature quad;
178 LineQuadrature quad_rule;
179 quad_rule.get_quadrature(order, quad);
180
181 points.resize(quad.points.rows(), endpoints.cols());
182 uv.resize(quad.points.rows(), 2);
183
184 uv.col(0) = (1.0 - quad.points.array());
185 uv.col(1) = quad.points.array();
186
187 for (int c = 0; c < 2; ++c)
188 {
189 points.col(c) = (1.0 - quad.points.array()) * endpoints(0, c) + quad.points.array() * endpoints(1, c);
190 }
191
192 weights = quad.weights;
193 weights *= mesh.edge_length(gid);
194 }
195
196 void utils::BoundarySampler::quadrature_for_tri_edge(int index, int order, int gid, const Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
197 {
198 auto endpoints = tri_local_node_coordinates_from_edge(index);
199
200 Quadrature quad;
201 LineQuadrature quad_rule;
202 quad_rule.get_quadrature(order, quad);
203
204 points.resize(quad.points.rows(), endpoints.cols());
205 uv.resize(quad.points.rows(), 2);
206
207 uv.col(0) = (1.0 - quad.points.array());
208 uv.col(1) = quad.points.array();
209
210 for (int c = 0; c < 2; ++c)
211 {
212 points.col(c) = (1.0 - quad.points.array()) * endpoints(0, c) + quad.points.array() * endpoints(1, c);
213 }
214
215 weights = quad.weights;
216 weights *= mesh.edge_length(gid);
217 }
218
219 void utils::BoundarySampler::quadrature_for_quad_face(int index, int order, int gid, const Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
220 {
221 auto endpoints = hex_local_node_coordinates_from_face(index);
222
223 Quadrature quad;
224 QuadQuadrature quad_rule;
225 quad_rule.get_quadrature(order, quad);
226
227 const int n_pts = quad.points.rows();
228 points.resize(n_pts, endpoints.cols());
229
230 uv.resize(quad.points.rows(), 4);
231 uv.col(0) = quad.points.col(0);
232 uv.col(1) = 1 - uv.col(0).array();
233 uv.col(2) = quad.points.col(1);
234 uv.col(3) = 1 - uv.col(2).array();
235
236 for (int i = 0; i < n_pts; ++i)
237 {
238 const double b1 = quad.points(i, 0);
239 const double b2 = 1 - b1;
240
241 const double b3 = quad.points(i, 1);
242 const double b4 = 1 - b3;
243
244 for (int c = 0; c < 3; ++c)
245 {
246 points(i, c) = b3 * (b1 * endpoints(0, c) + b2 * endpoints(1, c)) + b4 * (b1 * endpoints(3, c) + b2 * endpoints(2, c));
247 }
248 }
249
250 weights = quad.weights;
251 weights *= mesh.quad_area(gid);
252 }
253
254 void utils::BoundarySampler::quadrature_for_tri_face(int index, int order, int gid, const Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
255 {
256 auto endpoints = tet_local_node_coordinates_from_face(index);
257 Quadrature quad;
258 TriQuadrature quad_rule;
259 quad_rule.get_quadrature(order, quad);
260
261 const int n_pts = quad.points.rows();
262 points.resize(n_pts, endpoints.cols());
263
264 uv.resize(quad.points.rows(), 3);
265 uv.col(0) = quad.points.col(0);
266 uv.col(1) = quad.points.col(1);
267 uv.col(2) = 1 - uv.col(0).array() - uv.col(1).array();
268
269 for (int i = 0; i < n_pts; ++i)
270 {
271 const double b1 = quad.points(i, 0);
272 const double b3 = quad.points(i, 1);
273 const double b2 = 1 - b1 - b3;
274
275 for (int c = 0; c < 3; ++c)
276 {
277 points(i, c) = b1 * endpoints(0, c) + b2 * endpoints(1, c) + b3 * endpoints(2, c);
278 }
279 }
280
281 weights = quad.weights;
282 // 2 * because weights sum to 1/2 already
283 weights *= 2 * mesh.tri_area(gid);
284 }
285
286 void utils::BoundarySampler::quadrature_for_prism_face(int index, int orderp, int orderq, int gid, const Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
287 {
288 auto endpoints = prism_local_node_coordinates_from_face(index);
289
290 if (index < 2)
291 {
292 Quadrature quad;
293 TriQuadrature quad_rule;
294 quad_rule.get_quadrature(orderp, quad);
295
296 const int n_pts = quad.points.rows();
297 points.resize(n_pts, endpoints.cols());
298
299 uv.resize(quad.points.rows(), 3);
300 uv.col(0) = quad.points.col(0);
301 uv.col(1) = quad.points.col(1);
302 uv.col(2) = 1 - uv.col(0).array() - uv.col(1).array();
303
304 for (int i = 0; i < n_pts; ++i)
305 {
306 const double b1 = quad.points(i, 0);
307 const double b3 = quad.points(i, 1);
308 const double b2 = 1 - b1 - b3;
309
310 for (int c = 0; c < 3; ++c)
311 {
312 points(i, c) = b1 * endpoints(0, c) + b2 * endpoints(1, c) + b3 * endpoints(2, c);
313 }
314 }
315
316 weights = quad.weights;
317 // 2 * because weights sum to 1/2 already
318 weights *= 2 * mesh.tri_area(gid);
319 }
320 else
321 {
322 Quadrature quad;
323 QuadQuadrature quad_rule;
324 quad_rule.get_quadrature(orderq, quad);
325
326 const int n_pts = quad.points.rows();
327 points.resize(n_pts, endpoints.cols());
328
329 uv.resize(quad.points.rows(), 4);
330 uv.col(0) = quad.points.col(0);
331 uv.col(1) = 1 - uv.col(0).array();
332 uv.col(2) = quad.points.col(1);
333 uv.col(3) = 1 - uv.col(2).array();
334
335 for (int i = 0; i < n_pts; ++i)
336 {
337 const double b1 = quad.points(i, 0);
338 const double b2 = 1 - b1;
339
340 const double b3 = quad.points(i, 1);
341 const double b4 = 1 - b3;
342
343 for (int c = 0; c < 3; ++c)
344 {
345 points(i, c) = b3 * (b1 * endpoints(0, c) + b2 * endpoints(1, c)) + b4 * (b1 * endpoints(3, c) + b2 * endpoints(2, c));
346 }
347 }
348
349 weights = quad.weights;
350 weights *= mesh.quad_area(gid);
351 }
352 }
353
354 void utils::BoundarySampler::quadrature_for_pyramid_face(int index, int orderp, int gid, const Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
355 {
356 auto endpoints = pyramid_local_node_coordinates_from_face(index);
357
358 if (index > 0)
359 {
360 Quadrature quad;
361 TriQuadrature quad_rule;
362 quad_rule.get_quadrature(orderp, quad);
363
364 const int n_pts = quad.points.rows();
365 points.resize(n_pts, endpoints.cols());
366
367 uv.resize(quad.points.rows(), 3);
368 uv.col(0) = quad.points.col(0);
369 uv.col(1) = quad.points.col(1);
370 uv.col(2) = 1 - uv.col(0).array() - uv.col(1).array();
371
372 for (int i = 0; i < n_pts; ++i)
373 {
374 const double b1 = quad.points(i, 0);
375 const double b3 = quad.points(i, 1);
376 const double b2 = 1 - b1 - b3;
377
378 for (int c = 0; c < 3; ++c)
379 {
380 points(i, c) = b1 * endpoints(0, c) + b2 * endpoints(1, c) + b3 * endpoints(2, c);
381 }
382 }
383
384 weights = quad.weights;
385 // 2 * because weights sum to 1/2 already
386 weights *= 2 * mesh.tri_area(gid);
387 }
388 else
389 {
390 Quadrature quad;
391 QuadQuadrature quad_rule;
392 quad_rule.get_quadrature(orderp, quad);
393
394 const int n_pts = quad.points.rows();
395 points.resize(n_pts, endpoints.cols());
396
397 uv.resize(quad.points.rows(), 4);
398 uv.col(0) = quad.points.col(0);
399 uv.col(1) = 1 - uv.col(0).array();
400 uv.col(2) = quad.points.col(1);
401 uv.col(3) = 1 - uv.col(2).array();
402
403 for (int i = 0; i < n_pts; ++i)
404 {
405 const double b1 = quad.points(i, 0);
406 const double b2 = 1 - b1;
407
408 const double b3 = quad.points(i, 1);
409 const double b4 = 1 - b3;
410
411 for (int c = 0; c < 3; ++c)
412 {
413 points(i, c) = b3 * (b1 * endpoints(0, c) + b2 * endpoints(1, c)) + b4 * (b1 * endpoints(3, c) + b2 * endpoints(2, c));
414 }
415 }
416
417 weights = quad.weights;
418 weights *= mesh.quad_area(gid);
419 }
420 }
421
422 void utils::BoundarySampler::sample_parametric_quad_edge(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
423 {
424 auto endpoints = quad_local_node_coordinates_from_edge(index);
425 const Eigen::VectorXd t = Eigen::VectorXd::LinSpaced(n_samples, 0, 1);
426 samples.resize(n_samples, endpoints.cols());
427
428 uv.resize(n_samples, 2);
429
430 uv.col(0) = (1.0 - t.array());
431 uv.col(1) = t.array();
432
433 for (int c = 0; c < 2; ++c)
434 {
435 samples.col(c) = (1.0 - t.array()).matrix() * endpoints(0, c) + t * endpoints(1, c);
436 }
437 }
438
439 void utils::BoundarySampler::sample_parametric_tri_edge(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
440 {
441 auto endpoints = tri_local_node_coordinates_from_edge(index);
442 const Eigen::VectorXd t = Eigen::VectorXd::LinSpaced(n_samples, 0, 1);
443 samples.resize(n_samples, endpoints.cols());
444
445 uv.resize(n_samples, 2);
446
447 uv.col(0) = (1.0 - t.array());
448 uv.col(1) = t.array();
449
450 for (int c = 0; c < 2; ++c)
451 {
452 samples.col(c) = (1.0 - t.array()).matrix() * endpoints(0, c) + t * endpoints(1, c);
453 }
454 }
455
456 void utils::BoundarySampler::sample_parametric_quad_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
457 {
458 auto endpoints = hex_local_node_coordinates_from_face(index);
459 const Eigen::VectorXd t = Eigen::VectorXd::LinSpaced(n_samples, 0, 1);
460 samples.resize(n_samples * n_samples, endpoints.cols());
461 Eigen::MatrixXd left(n_samples, endpoints.cols());
462 Eigen::MatrixXd right(n_samples, endpoints.cols());
463
464 uv.resize(n_samples * n_samples, endpoints.cols());
465 uv.setZero();
466
467 for (int c = 0; c < 3; ++c)
468 {
469 left.col(c) = (1.0 - t.array()).matrix() * endpoints(0, c) + t * endpoints(1, c);
470 right.col(c) = (1.0 - t.array()).matrix() * endpoints(3, c) + t * endpoints(2, c);
471 }
472 for (int c = 0; c < 3; ++c)
473 {
474 Eigen::MatrixXd x = (1.0 - t.array()).matrix() * left.col(c).transpose() + t * right.col(c).transpose();
475 assert(x.size() == n_samples * n_samples);
476
477 samples.col(c) = Eigen::Map<const Eigen::VectorXd>(x.data(), x.size());
478 }
479 }
480
481 void utils::BoundarySampler::sample_parametric_tri_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
482 {
483 auto endpoints = tet_local_node_coordinates_from_face(index);
484 const Eigen::VectorXd t = Eigen::VectorXd::LinSpaced(n_samples, 0, 1);
485 samples.resize(n_samples * n_samples, endpoints.cols());
486
487 uv.resize(n_samples * n_samples, 3);
488
489 int counter = 0;
490 for (int u = 0; u < n_samples; ++u)
491 {
492 for (int v = 0; v < n_samples; ++v)
493 {
494 if (t(u) + t(v) > 1)
495 continue;
496
497 uv(counter, 0) = t(u);
498 uv(counter, 1) = t(v);
499 uv(counter, 2) = 1 - t(u) - t(v);
500
501 for (int c = 0; c < 3; ++c)
502 {
503 samples(counter, c) = t(u) * endpoints(0, c) + t(v) * endpoints(1, c) + (1 - t(u) - t(v)) * endpoints(2, c);
504 }
505 ++counter;
506 }
507 }
508 samples.conservativeResize(counter, 3);
509 uv.conservativeResize(counter, 3);
510 }
511
512 void utils::BoundarySampler::sample_parametric_prism_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
513 {
514 auto endpoints = prism_local_node_coordinates_from_face(index);
515
516 if (index < 2)
517 {
518 const Eigen::VectorXd t = Eigen::VectorXd::LinSpaced(n_samples, 0, 1);
519 samples.resize(n_samples * n_samples, endpoints.cols());
520
521 uv.resize(n_samples * n_samples, 3);
522
523 int counter = 0;
524 for (int u = 0; u < n_samples; ++u)
525 {
526 for (int v = 0; v < n_samples; ++v)
527 {
528 if (t(u) + t(v) > 1)
529 continue;
530
531 uv(counter, 0) = t(u);
532 uv(counter, 1) = t(v);
533 uv(counter, 2) = 1 - t(u) - t(v);
534
535 for (int c = 0; c < 3; ++c)
536 {
537 samples(counter, c) = t(u) * endpoints(0, c) + t(v) * endpoints(1, c) + (1 - t(u) - t(v)) * endpoints(2, c);
538 }
539 ++counter;
540 }
541 }
542 samples.conservativeResize(counter, 3);
543 uv.conservativeResize(counter, 3);
544 }
545 else
546 {
547 const Eigen::VectorXd t = Eigen::VectorXd::LinSpaced(n_samples, 0, 1);
548 samples.resize(n_samples * n_samples, endpoints.cols());
549 Eigen::MatrixXd left(n_samples, endpoints.cols());
550 Eigen::MatrixXd right(n_samples, endpoints.cols());
551
552 uv.resize(n_samples * n_samples, endpoints.cols());
553 uv.setZero();
554
555 for (int c = 0; c < 3; ++c)
556 {
557 left.col(c) = (1.0 - t.array()).matrix() * endpoints(0, c) + t * endpoints(1, c);
558 right.col(c) = (1.0 - t.array()).matrix() * endpoints(3, c) + t * endpoints(2, c);
559 }
560 for (int c = 0; c < 3; ++c)
561 {
562 Eigen::MatrixXd x = (1.0 - t.array()).matrix() * left.col(c).transpose() + t * right.col(c).transpose();
563 assert(x.size() == n_samples * n_samples);
564
565 samples.col(c) = Eigen::Map<const Eigen::VectorXd>(x.data(), x.size());
566 }
567 }
568 }
569
570 void utils::BoundarySampler::sample_parametric_pyramid_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
571 {
572 auto endpoints = pyramid_local_node_coordinates_from_face(index);
573
574 if (index > 0)
575 {
576 const Eigen::VectorXd t = Eigen::VectorXd::LinSpaced(n_samples, 0, 1);
577 samples.resize(n_samples * n_samples, endpoints.cols());
578
579 uv.resize(n_samples * n_samples, 3);
580
581 int counter = 0;
582 for (int u = 0; u < n_samples; ++u)
583 {
584 for (int v = 0; v < n_samples; ++v)
585 {
586 if (t(u) + t(v) > 1)
587 continue;
588
589 uv(counter, 0) = t(u);
590 uv(counter, 1) = t(v);
591 uv(counter, 2) = 1 - t(u) - t(v);
592
593 for (int c = 0; c < 3; ++c)
594 {
595 samples(counter, c) = t(u) * endpoints(0, c) + t(v) * endpoints(1, c) + (1 - t(u) - t(v)) * endpoints(2, c);
596 }
597 ++counter;
598 }
599 }
600 samples.conservativeResize(counter, 3);
601 uv.conservativeResize(counter, 3);
602 }
603 else
604 {
605 const Eigen::VectorXd t = Eigen::VectorXd::LinSpaced(n_samples, 0, 1);
606 samples.resize(n_samples * n_samples, endpoints.cols());
607 Eigen::MatrixXd left(n_samples, endpoints.cols());
608 Eigen::MatrixXd right(n_samples, endpoints.cols());
609
610 uv.resize(n_samples * n_samples, endpoints.cols());
611 uv.setZero();
612
613 for (int c = 0; c < 3; ++c)
614 {
615 left.col(c) = (1.0 - t.array()).matrix() * endpoints(0, c) + t * endpoints(1, c);
616 right.col(c) = (1.0 - t.array()).matrix() * endpoints(3, c) + t * endpoints(2, c);
617 }
618 for (int c = 0; c < 3; ++c)
619 {
620 Eigen::MatrixXd x = (1.0 - t.array()).matrix() * left.col(c).transpose() + t * right.col(c).transpose();
621 assert(x.size() == n_samples * n_samples);
622
623 samples.col(c) = Eigen::Map<const Eigen::VectorXd>(x.data(), x.size());
624 }
625 }
626 }
627
628 void utils::BoundarySampler::sample_polygon_edge(int face_id, int edge_id, int n_samples, const Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
629 {
630 assert(!mesh.is_volume());
631
632 const CMesh2D &mesh2d = dynamic_cast<const CMesh2D &>(mesh);
633
634 auto index = mesh2d.get_index_from_face(face_id);
635
636 bool found = false;
637 for (int i = 0; i < mesh2d.n_face_vertices(face_id); ++i)
638 {
639 if (index.edge == edge_id)
640 {
641 found = true;
642 break;
643 }
644
645 index = mesh2d.next_around_face(index);
646 }
647
648 assert(found);
649
650 auto p0 = mesh2d.point(index.vertex);
651 auto p1 = mesh2d.point(mesh2d.switch_edge(index).vertex);
652 const Eigen::VectorXd t = Eigen::VectorXd::LinSpaced(n_samples, 0, 1);
653 samples.resize(n_samples, p0.cols());
654
655 uv.resize(n_samples, 2);
656
657 uv.col(0) = (1.0 - t.array());
658 uv.col(1) = t.array();
659
660 for (int c = 0; c < 2; ++c)
661 {
662 samples.col(c) = (1.0 - t.array()) * p0(c) + t.array() * p1(c);
663 }
664 }
665
666 void utils::BoundarySampler::quadrature_for_polygon_edge(int face_id, int edge_id, int order, const Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
667 {
668 assert(!mesh.is_volume());
669
670 const CMesh2D &mesh2d = dynamic_cast<const CMesh2D &>(mesh);
671
672 auto index = mesh2d.get_index_from_face(face_id);
673
674 bool found = false;
675 for (int i = 0; i < mesh2d.n_face_vertices(face_id); ++i)
676 {
677 if (index.edge == edge_id)
678 {
679 found = true;
680 break;
681 }
682
683 index = mesh2d.next_around_face(index);
684 }
685
686 assert(found);
687
688 auto p0 = mesh2d.point(index.vertex);
689 auto p1 = mesh2d.point(mesh2d.switch_edge(index).vertex);
690 Quadrature quad;
691 LineQuadrature quad_rule;
692 quad_rule.get_quadrature(order, quad);
693
694 points.resize(quad.points.rows(), p0.cols());
695 uv.resize(quad.points.rows(), 2);
696
697 uv.col(0) = (1.0 - quad.points.array());
698 uv.col(1) = quad.points.array();
699
700 for (int c = 0; c < 2; ++c)
701 {
702 points.col(c) = (1.0 - quad.points.array()) * p0(c) + quad.points.array() * p1(c);
703 }
704
705 weights = quad.weights;
706 weights *= mesh.edge_length(edge_id);
707 }
708
709 void utils::BoundarySampler::normal_for_quad_edge(int index, Eigen::MatrixXd &normal)
710 {
711 auto endpoints = quad_local_node_coordinates_from_edge(index);
712 const Eigen::MatrixXd e = endpoints.row(0) - endpoints.row(1);
713 normal.resize(1, 2);
714 normal(0) = -e(1);
715 normal(1) = e(0);
716 normal.normalize();
717 }
718
719 void utils::BoundarySampler::normal_for_tri_edge(int index, Eigen::MatrixXd &normal)
720 {
721 auto endpoints = tri_local_node_coordinates_from_edge(index);
722 const Eigen::MatrixXd e = endpoints.row(0) - endpoints.row(1);
723 normal.resize(1, 2);
724 normal(0) = -e(1);
725 normal(1) = e(0);
726 normal.normalize();
727 }
728
729 void utils::BoundarySampler::normal_for_quad_face(int index, Eigen::MatrixXd &normal)
730 {
731 auto endpoints = hex_local_node_coordinates_from_face(index);
732 const Eigen::RowVector3d e1 = endpoints.row(0) - endpoints.row(1);
733 const Eigen::RowVector3d e2 = endpoints.row(0) - endpoints.row(2);
734 normal = e1.cross(e2);
735 normal.normalize();
736
737 if (index == 0 || index == 3 || index == 4)
738 normal *= -1;
739 }
740
741 void utils::BoundarySampler::normal_for_tri_face(int index, Eigen::MatrixXd &normal)
742 {
743 auto endpoints = tet_local_node_coordinates_from_face(index);
744 const Eigen::RowVector3d e1 = endpoints.row(0) - endpoints.row(1);
745 const Eigen::RowVector3d e2 = endpoints.row(0) - endpoints.row(2);
746 normal = e1.cross(e2);
747 normal.normalize();
748
749 if (index == 0)
750 normal *= -1;
751 }
752
753 void utils::BoundarySampler::normal_for_prism_face(int index, Eigen::MatrixXd &normal)
754 {
755 auto endpoints = prism_local_node_coordinates_from_face(index);
756 const Eigen::RowVector3d e1 = endpoints.row(0) - endpoints.row(1);
757 const Eigen::RowVector3d e2 = endpoints.row(0) - endpoints.row(2);
758 normal = e1.cross(e2);
759 normal.normalize();
760
761 if (index == 0)
762 normal *= -1;
763 }
764
765 void utils::BoundarySampler::normal_for_pyramid_face(int index, Eigen::MatrixXd &normal)
766 {
767 auto endpoints = pyramid_local_node_coordinates_from_face(index);
768 const Eigen::RowVector3d e1 = endpoints.row(0) - endpoints.row(1);
769 const Eigen::RowVector3d e2 = endpoints.row(0) - endpoints.row(2);
770 normal = e1.cross(e2);
771 normal.normalize();
772
773 if (index == 0) // the quad face
774 normal *= -1;
775 }
776
777 void utils::BoundarySampler::normal_for_polygon_edge(int face_id, int edge_id, const Mesh &mesh, Eigen::MatrixXd &normal)
778 {
779 assert(!mesh.is_volume());
780
781 const CMesh2D &mesh2d = dynamic_cast<const CMesh2D &>(mesh);
782
783 auto index = mesh2d.get_index_from_face(face_id);
784
785 bool found = false;
786 for (int i = 0; i < mesh2d.n_face_vertices(face_id); ++i)
787 {
788 if (index.edge == edge_id)
789 {
790 found = true;
791 break;
792 }
793
794 index = mesh2d.next_around_face(index);
795 }
796
797 assert(found);
798
799 auto p0 = mesh2d.point(index.vertex);
800 auto p1 = mesh2d.point(mesh2d.switch_edge(index).vertex);
801 const Eigen::MatrixXd e = p0 - p1;
802 normal.resize(1, 2);
803 normal(0) = -e(1);
804 normal(1) = e(0);
805 normal.normalize();
806 }
807
808 bool utils::BoundarySampler::boundary_quadrature(const LocalBoundary &local_boundary, const QuadratureOrders &order, const Mesh &mesh, const bool skip_computation, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::MatrixXd &normals, Eigen::VectorXd &weights, Eigen::VectorXi &global_primitive_ids)
809 {
810 uv.resize(0, 0);
811 points.resize(0, 0);
812 normals.resize(0, 0);
813 weights.resize(0);
814 global_primitive_ids.resize(0);
815
816 for (int i = 0; i < local_boundary.size(); ++i)
817 {
818 const int gid = local_boundary.global_primitive_id(i);
819 Eigen::MatrixXd tmp_p, tmp_uv, tmp_n;
820 Eigen::VectorXd tmp_w;
821 switch (local_boundary.type())
822 {
823 case BoundaryType::TRI_LINE:
824 quadrature_for_tri_edge(local_boundary[i], order[0], gid, mesh, tmp_uv, tmp_p, tmp_w);
825 normal_for_tri_edge(local_boundary[i], tmp_n);
826 break;
827 case BoundaryType::QUAD_LINE:
828 quadrature_for_quad_edge(local_boundary[i], order[0], gid, mesh, tmp_uv, tmp_p, tmp_w);
829 normal_for_quad_edge(local_boundary[i], tmp_n);
830 break;
831 case BoundaryType::QUAD:
832 quadrature_for_quad_face(local_boundary[i], order[0], gid, mesh, tmp_uv, tmp_p, tmp_w);
833 normal_for_quad_face(local_boundary[i], tmp_n);
834 break;
835 case BoundaryType::TRI:
836 quadrature_for_tri_face(local_boundary[i], order[0], gid, mesh, tmp_uv, tmp_p, tmp_w);
837 normal_for_tri_face(local_boundary[i], tmp_n);
838 break;
839 case BoundaryType::PRISM:
840 quadrature_for_prism_face(local_boundary[i], order[0], order[1], gid, mesh, tmp_uv, tmp_p, tmp_w);
841 normal_for_prism_face(local_boundary[i], tmp_n);
842 break;
843 case BoundaryType::PYRAMID:
844 quadrature_for_pyramid_face(local_boundary[i], order[0], gid, mesh, tmp_uv, tmp_p, tmp_w);
845 normal_for_pyramid_face(local_boundary[i], tmp_n);
846 break;
847 case BoundaryType::POLYGON:
848 quadrature_for_polygon_edge(local_boundary.element_id(), local_boundary.global_primitive_id(i), order[0], mesh, tmp_uv, tmp_p, tmp_w);
849 normal_for_polygon_edge(local_boundary.element_id(), local_boundary.global_primitive_id(i), mesh, tmp_n);
850 break;
851 case BoundaryType::INVALID:
852 assert(false);
853 break;
854 default:
855 assert(false);
856 }
857
858 uv.conservativeResize(uv.rows() + tmp_uv.rows(), tmp_uv.cols());
859 uv.bottomRows(tmp_uv.rows()) = tmp_uv;
860
861 points.conservativeResize(points.rows() + tmp_p.rows(), tmp_p.cols());
862 points.bottomRows(tmp_p.rows()) = tmp_p;
863
864 normals.conservativeResize(normals.rows() + tmp_p.rows(), tmp_p.cols());
865 for (int k = normals.rows() - tmp_p.rows(); k < normals.rows(); ++k)
866 normals.row(k) = tmp_n;
867
868 weights.conservativeResize(weights.rows() + tmp_w.rows(), tmp_w.cols());
869 weights.bottomRows(tmp_w.rows()) = tmp_w;
870
871 global_primitive_ids.conservativeResize(global_primitive_ids.rows() + tmp_p.rows());
872 global_primitive_ids.bottomRows(tmp_p.rows()).setConstant(gid);
873 }
874
875 assert(uv.rows() == global_primitive_ids.size());
876 assert(points.rows() == global_primitive_ids.size());
877 assert(normals.rows() == global_primitive_ids.size());
878 assert(weights.size() == global_primitive_ids.size());
879
880 return true;
881 }
882
883 bool utils::BoundarySampler::sample_boundary(const LocalBoundary &local_boundary, const int n_samples, const Mesh &mesh, const bool skip_computation, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples, Eigen::VectorXi &global_primitive_ids)
884 {
885 uv.resize(0, 0);
886 samples.resize(0, 0);
887 global_primitive_ids.resize(0);
888
889 for (int i = 0; i < local_boundary.size(); ++i)
890 {
891 Eigen::MatrixXd tmp, tmp_uv;
892 switch (local_boundary.type())
893 {
894 case BoundaryType::TRI_LINE:
895 sample_parametric_tri_edge(local_boundary[i], n_samples, tmp_uv, tmp);
896 break;
897 case BoundaryType::QUAD_LINE:
898 sample_parametric_quad_edge(local_boundary[i], n_samples, tmp_uv, tmp);
899 break;
900 case BoundaryType::QUAD:
901 sample_parametric_quad_face(local_boundary[i], n_samples, tmp_uv, tmp);
902 break;
903 case BoundaryType::TRI:
904 sample_parametric_tri_face(local_boundary[i], n_samples, tmp_uv, tmp);
905 break;
906 case BoundaryType::PRISM:
907 sample_parametric_prism_face(local_boundary[i], n_samples, tmp_uv, tmp);
908 break;
909 case BoundaryType::PYRAMID:
910 sample_parametric_pyramid_face(local_boundary[i], n_samples, tmp_uv, tmp);
911 break;
912 case BoundaryType::POLYGON:
913 sample_polygon_edge(local_boundary.element_id(), local_boundary.global_primitive_id(i), n_samples, mesh, tmp_uv, tmp);
914 break;
915 case BoundaryType::INVALID:
916 assert(false);
917 break;
918 default:
919 assert(false);
920 }
921
922 uv.conservativeResize(uv.rows() + tmp_uv.rows(), tmp_uv.cols());
923 uv.bottomRows(tmp_uv.rows()) = tmp_uv;
924
925 samples.conservativeResize(samples.rows() + tmp.rows(), tmp.cols());
926 samples.bottomRows(tmp.rows()) = tmp;
927
928 global_primitive_ids.conservativeResize(global_primitive_ids.rows() + tmp.rows());
929 global_primitive_ids.bottomRows(tmp.rows()).setConstant(local_boundary.global_primitive_id(i));
930 }
931
932 assert(uv.rows() == global_primitive_ids.size());
933 assert(samples.rows() == global_primitive_ids.size());
934
935 return true;
936 }
937
938 bool utils::BoundarySampler::boundary_quadrature(const mesh::LocalBoundary &local_boundary, const QuadratureOrders &order, const mesh::Mesh &mesh, const int i, const bool skip_computation, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::MatrixXd &normals, Eigen::VectorXd &weights)
939 {
940 assert(local_boundary.size() > i);
941
942 uv.resize(0, 0);
943 points.resize(0, 0);
944 weights.resize(0);
945 const int gid = local_boundary.global_primitive_id(i);
946
947 Eigen::MatrixXd normal;
948 switch (local_boundary.type())
949 {
950 case BoundaryType::TRI_LINE:
951 quadrature_for_tri_edge(local_boundary[i], order[0], gid, mesh, uv, points, weights);
952 normal_for_tri_edge(local_boundary[i], normal);
953 break;
954 case BoundaryType::QUAD_LINE:
955 quadrature_for_quad_edge(local_boundary[i], order[0], gid, mesh, uv, points, weights);
956 normal_for_quad_edge(local_boundary[i], normal);
957 break;
958 case BoundaryType::QUAD:
959 quadrature_for_quad_face(local_boundary[i], order[0], gid, mesh, uv, points, weights);
960 normal_for_quad_face(local_boundary[i], normal);
961 break;
962 case BoundaryType::TRI:
963 quadrature_for_tri_face(local_boundary[i], order[0], gid, mesh, uv, points, weights);
964 normal_for_tri_face(local_boundary[i], normal);
965 break;
966 case BoundaryType::PRISM:
967 quadrature_for_prism_face(local_boundary[i], order[0], order[1], gid, mesh, uv, points, weights);
968 normal_for_prism_face(local_boundary[i], normal);
969 break;
970 case BoundaryType::PYRAMID:
971 quadrature_for_pyramid_face(local_boundary[i], order[0], gid, mesh, uv, points, weights);
972 normal_for_pyramid_face(local_boundary[i], normal);
973 break;
974 case BoundaryType::POLYGON:
975 quadrature_for_polygon_edge(local_boundary.element_id(), gid, order[0], mesh, uv, points, weights);
976 normal_for_polygon_edge(local_boundary.element_id(), gid, mesh, normal);
977 break;
978 case BoundaryType::INVALID:
979 assert(false);
980 break;
981 default:
982 assert(false);
983 }
984
985 normals.resize(points.rows(), normal.size());
986 for (int k = 0; k < normals.rows(); ++k)
987 normals.row(k) = normal;
988
989 return true;
990 }
991 } // namespace utils
992} // namespace polyfem
Quadrature quadrature
int edge_id
int x
Navigation::Index switch_edge(Navigation::Index idx) const override
Definition CMesh2D.hpp:87
int n_face_vertices(const int f_id) const override
number of vertices of a face
Definition CMesh2D.hpp:37
Navigation::Index get_index_from_face(int f, int lv=0) const override
Definition CMesh2D.hpp:83
virtual RowVectorNd point(const int global_index) const override
point coordinates
Definition CMesh2D.cpp:477
Boundary primitive IDs for a single element.
int element_id() const
Get the element's ID.
int size() const
Number of boundary primitives for the element.
int global_primitive_id(const int index) const
Get the i-th boundary primitive's global ID.
BoundaryType type() const
Get the type of boundary for the element.
Navigation::Index next_around_face(Navigation::Index idx) const
Definition Mesh2D.hpp:61
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:41
virtual double edge_length(const int gid) const
edge length
Definition Mesh.hpp:308
virtual double tri_area(const int gid) const
area of a tri face of a tet mesh
Definition Mesh.hpp:326
virtual double quad_area(const int gid) const
area of a quad face of an hex mesh
Definition Mesh.hpp:317
virtual bool is_volume() const =0
checks if mesh is volume
void get_quadrature(const int order, Quadrature &quad)
void get_quadrature(const int order, Quadrature &quad)
void get_quadrature(const int order, Quadrature &quad)
static void quadrature_for_polygon_edge(int face_id, int edge_id, int order, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
static Eigen::MatrixXd prism_local_node_coordinates_from_face(int lf)
static void quadrature_for_quad_face(int index, int order, const int gid, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
static bool sample_boundary(const mesh::LocalBoundary &local_boundary, const int n_samples, const mesh::Mesh &mesh, const bool skip_computation, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples, Eigen::VectorXi &global_primitive_ids)
static void sample_parametric_prism_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static Eigen::MatrixXd pyramid_local_node_coordinates_from_face(int lf)
static void quadrature_for_quad_edge(int index, int order, const int gid, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
static void normal_for_quad_edge(int index, Eigen::MatrixXd &normal)
static void normal_for_tri_edge(int index, Eigen::MatrixXd &normal)
static void quadrature_for_tri_face(int index, int order, const int gid, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
static void normal_for_quad_face(int index, Eigen::MatrixXd &normal)
static void sample_parametric_pyramid_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void sample_parametric_tri_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static bool boundary_quadrature(const mesh::LocalBoundary &local_boundary, const QuadratureOrders &order, const mesh::Mesh &mesh, const bool skip_computation, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::MatrixXd &normals, Eigen::VectorXd &weights, Eigen::VectorXi &global_primitive_ids)
static Eigen::MatrixXd hex_local_node_coordinates_from_face(int lf)
static void normal_for_prism_face(int index, Eigen::MatrixXd &normal)
static void normal_for_tri_face(int index, Eigen::MatrixXd &normal)
static void sample_parametric_quad_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void quadrature_for_prism_face(int index, int orderp, int orderq, const int gid, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
static void normal_for_polygon_edge(int face_id, int edge_id, const mesh::Mesh &mesh, Eigen::MatrixXd &normal)
static void normal_for_pyramid_face(int index, Eigen::MatrixXd &normal)
static Eigen::MatrixXd tet_local_node_coordinates_from_face(int lf)
static void sample_parametric_quad_edge(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void sample_polygon_edge(int face_id, int edge_id, int n_samples, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void quadrature_for_tri_edge(int index, int order, const int gid, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
static void sample_parametric_tri_edge(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static Eigen::Matrix2d quad_local_node_coordinates_from_edge(int le)
static Eigen::Matrix2d tri_local_node_coordinates_from_edge(int le)
static void quadrature_for_pyramid_face(int index, int orderp, const int gid, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::VectorXd &weights)
void q_nodes_2d(const int q, Eigen::MatrixXd &val)
void pyramid_nodes_3d(const int pyramid, Eigen::MatrixXd &val)
void prism_nodes_3d(const int p, const int q, Eigen::MatrixXd &val)
void p_nodes_2d(const int p, Eigen::MatrixXd &val)
void p_nodes_3d(const int p, Eigen::MatrixXd &val)
void q_nodes_3d(const int q, Eigen::MatrixXd &val)
std::array< int, 2 > QuadratureOrders
Definition Types.hpp:19