Wildmeshing Toolkit
Loading...
Searching...
No Matches
wildmeshing2d.cpp
Go to the documentation of this file.
1#include "wildmeshing2d.hpp"
2
5
6#include <wmtk/Mesh.hpp>
7#include <wmtk/Scheduler.hpp>
8#include <wmtk/TetMesh.hpp>
9#include <wmtk/TriMesh.hpp>
11
14#include <wmtk/utils/Logger.hpp>
15
16
20
34
35
39
61
63
65#include <wmtk/utils/orient.hpp>
66
69
70#include <queue>
72#include <wmtk/simplex/link.hpp>
73
74#include <fstream>
76
77using namespace simplex;
78using namespace operations;
79using namespace operations::tri_mesh;
80using namespace operations::tet_mesh;
81using namespace operations::composite;
82using namespace function;
83using namespace invariants;
84
85std::vector<std::pair<std::shared_ptr<Mesh>, std::string>> wildmeshing2d(
86 const WildMeshingOptions& options)
87{
88 auto mesh = options.input_mesh;
89
90 if (!mesh->is_connectivity_valid()) {
91 throw std::runtime_error("input mesh for wildmeshing connectivity invalid");
92 }
93
94 wmtk::logger().trace("Getting rational point handle");
95
97 // Retriving vertices
98 //
99 if (options.replace_double_coordinate) {
100 wmtk::logger().trace("Found double attribugte");
101 auto pt_double_attribute =
102 mesh->get_attribute_handle<double>(options.input_mesh_position, PrimitiveType::Vertex);
103
104 if (!mesh->has_attribute<Rational>(options.input_mesh_position, PrimitiveType::Vertex)) {
105 wmtk::utils::cast_attribute<wmtk::Rational>(
106 pt_double_attribute,
107 *mesh,
108 options.input_mesh_position);
109
110
111 } else {
112 auto pt_attribute = mesh->get_attribute_handle<Rational>(
113 options.input_mesh_position,
115 wmtk::utils::cast_attribute<wmtk::Rational>(pt_double_attribute, pt_attribute);
116 }
117 mesh->delete_attribute(pt_double_attribute);
118 }
119 auto pt_attribute =
120 mesh->get_attribute_handle<Rational>(options.input_mesh_position, PrimitiveType::Vertex);
121 wmtk::logger().trace("Getting rational point accessor");
122 auto pt_accessor = mesh->create_accessor(pt_attribute.as<Rational>());
123
124 wmtk::logger().trace("Computing bounding box diagonal");
126 // computing bbox diagonal
127 Eigen::VectorXd bmin(mesh->top_cell_dimension());
128 bmin.setConstant(std::numeric_limits<double>::max());
129 Eigen::VectorXd bmax(mesh->top_cell_dimension());
130 bmax.setConstant(std::numeric_limits<double>::lowest());
131
132 const auto vertices = mesh->get_all(PrimitiveType::Vertex);
133 for (const auto& v : vertices) {
134 const auto p = pt_accessor.vector_attribute(v).cast<double>();
135 for (int64_t d = 0; d < bmax.size(); ++d) {
136 bmin[d] = std::min(bmin[d], p[d]);
137 bmax[d] = std::max(bmax[d], p[d]);
138 }
139 }
140
141
142 const double bbdiag = (bmax - bmin).norm();
143
144 wmtk::logger().info("bbox max {}, bbox min {}, diag {}", bmax, bmin, bbdiag);
145
146 const double target_edge_length = options.target_edge_length * bbdiag;
147
148 // const double target_edge_length =
149 // options.target_edge_length * bbdiag /
150 // std::min(
151 // 2.0,
152 // 1 + options.target_edge_length * 2); // min to prevent bad option.target_edge_length
153
154 wmtk::logger().info("target edge length: {}", target_edge_length);
155
157 // store amips
158 auto amips_attribute =
159 mesh->register_attribute<double>("wildmeshing_amips", mesh->top_simplex_type(), 1);
160 auto amips_accessor = mesh->create_accessor(amips_attribute.as<double>());
161 // amips update
162 auto compute_amips = [](const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
163 assert(P.rows() == 2 || P.rows() == 3); // rows --> attribute dimension
164 assert(P.cols() == P.rows() + 1);
165 if (P.cols() == 3) {
166 // triangle
167 assert(P.rows() == 2);
168 std::array<double, 6> pts;
169 for (size_t i = 0; i < 3; ++i) {
170 for (size_t j = 0; j < 2; ++j) {
171 pts[2 * i + j] = P(j, i).to_double();
172 }
173 }
174 const double a = Tri_AMIPS_energy(pts);
175 return Eigen::VectorXd::Constant(1, a);
176 } else {
177 // tet
178 assert(P.rows() == 3);
179 std::array<double, 12> pts;
180 for (size_t i = 0; i < 4; ++i) {
181 for (size_t j = 0; j < 3; ++j) {
182 pts[3 * i + j] = P(j, i).to_double();
183 }
184 }
185 const double a = Tet_AMIPS_energy(pts);
186 return Eigen::VectorXd::Constant(1, a);
187 }
188 };
189 auto amips_update =
190 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
191 amips_attribute,
192 pt_attribute,
193 compute_amips);
194 amips_update->run_on_all();
195
196 double max_amips = std::numeric_limits<double>::lowest();
197 double min_amips = std::numeric_limits<double>::max();
198
199 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
200 // double e = amips->get_value(simplex::Simplex(mesh->top_simplex_type(), t));
201 double e = amips_accessor.scalar_attribute(t);
202 max_amips = std::max(max_amips, e);
203 min_amips = std::min(min_amips, e);
204 }
205
206 logger().info("Initial Max AMIPS Energy: {}, Min AMIPS Energy: {}", max_amips, min_amips);
207
209 // sizing field scalar
211
212 // TODO: add sizing field for 2d
213
214 // auto sizing_field_scalar_attribute = mesh->register_attribute<double>(
215 // "sizing_field_scalar",
216 // PrimitiveType::Vertex,
217 // 1,
218 // false,
219 // 1); // defaults to 1
220
221
223 // Storing target edge length
224 auto target_edge_length_attribute = mesh->register_attribute<double>(
225 "wildmeshing_target_edge_length",
227 1,
228 false,
229 target_edge_length); // defaults to target edge length
230
231 // Target edge length update
232 const double min_edge_length = [&]() -> double {
233 if (options.envelopes.empty()) {
234 return 1e-6; // some default value if no envelope exists
235 } else {
236 // use envelope thickness if available
237 double r = 0;
238 for (const auto& e : options.envelopes) {
239 r = std::max(r, e.thickness);
240 }
241 assert(r > 0);
242 return r;
243 }
244 }();
245 const double target_max_amips = options.target_max_amips;
246
247
249 // Storing edge lengths
250 auto edge_length_attribute =
251 mesh->register_attribute<double>("edge_length", PrimitiveType::Edge, 1);
252 auto edge_length_accessor = mesh->create_accessor(edge_length_attribute.as<double>());
253 // Edge length update
254 auto compute_edge_length = [](const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
255 assert(P.cols() == 2);
256 assert(P.rows() == 2 || P.rows() == 3);
257 return Eigen::VectorXd::Constant(1, sqrt((P.col(0) - P.col(1)).squaredNorm().to_double()));
258 };
259 auto edge_length_update =
260 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
261 edge_length_attribute,
262 pt_attribute,
263 compute_edge_length);
264 edge_length_update->run_on_all();
265
266
268 // compute frozen vertices
270 auto frozen_vertex_attribute =
271 mesh->register_attribute<int64_t>("frozen_vertex", PrimitiveType::Vertex, 1);
272 auto frozen_vertex_accessor = mesh->create_accessor(frozen_vertex_attribute.as<int64_t>());
273
274 auto input_ptr = mesh->get_child_meshes().front();
275
276 int64_t frozen_cnt = 0;
277 for (const auto& v : input_ptr->get_all(PrimitiveType::Vertex)) {
278 if (input_ptr->is_boundary(PrimitiveType::Vertex, v)) {
279 const auto& parent_v =
280 input_ptr->map_to_parent(simplex::Simplex::vertex(*input_ptr, v));
281 frozen_vertex_accessor.scalar_attribute(parent_v) = 1;
282 frozen_cnt++;
283 } else {
284 // frozen_vertex_accessor.scalar_attribute(parent_v) = 0; // redundant, just for safe
285 }
286 }
287
288 frozen_cnt = 0;
289
290 for (const auto& v : mesh->get_all(PrimitiveType::Vertex)) {
291 if (frozen_vertex_accessor.scalar_attribute(v) == 1) {
292 frozen_cnt++;
293 }
294 }
295
296 wmtk::logger().info("mesh has {} frozen vertices", frozen_cnt);
297
299 // default transfer
300 auto pass_through_attributes = options.pass_through;
301 pass_through_attributes.push_back(edge_length_attribute);
302 pass_through_attributes.push_back(amips_attribute);
303 // pass_through_attributes.push_back(target_edge_length_attribute);
304
305
307 // Lambdas for priority
309 auto long_edges_first = [&](const simplex::Simplex& s) {
310 assert(s.primitive_type() == PrimitiveType::Edge);
311 return -edge_length_accessor.scalar_attribute(s.tuple());
312 };
313 auto short_edges_first = [&](const simplex::Simplex& s) {
314 assert(s.primitive_type() == PrimitiveType::Edge);
315 return edge_length_accessor.scalar_attribute(s.tuple());
316 };
317
318
320 // envelopes
322
323 using MeshConstrainPair = ProjectOperation::MeshConstrainPair;
324
325 auto envelope_invariant = std::make_shared<InvariantCollection>(*mesh);
326 std::vector<std::shared_ptr<AttributeTransferStrategyBase>> update_child_position,
327 update_parent_position;
328 std::vector<std::shared_ptr<Mesh>> envelopes;
329 std::vector<MeshConstrainPair> mesh_constraint_pairs;
330
331 std::vector<std::pair<std::shared_ptr<Mesh>, std::string>> multimesh_meshes;
332
333 for (const auto& e : options.envelopes) {
334 auto constrained_mesh = e.envelope_constrained_mesh;
335 auto geometry_mesh = e.envelope_geometry_mesh;
336
337 wmtk::logger().info(
338 "wildmeshing2d: registered {} mesh as envelope constraints",
339 e.envelope_name);
340
341 const bool geometry_has_double_pos =
342 geometry_mesh->has_attribute<double>(e.geometry_position_name, PrimitiveType::Vertex);
343 const bool geometry_has_rational_pos =
344 geometry_mesh->has_attribute<Rational>(e.geometry_position_name, PrimitiveType::Vertex);
345 assert(geometry_has_double_pos || geometry_has_rational_pos);
346
347 auto geometry_pt_handle = geometry_has_double_pos
348 ? geometry_mesh->get_attribute_handle<double>(
349 e.geometry_position_name,
351 : geometry_mesh->get_attribute_handle<Rational>(
352 e.geometry_position_name,
354
355 auto constrained_pt_handle = constrained_mesh->get_attribute_handle<Rational>(
356 e.constrained_position_name,
358
359 multimesh_meshes.push_back(std::make_pair(constrained_mesh, e.envelope_name));
360 pass_through_attributes.emplace_back(constrained_pt_handle);
361
362 mesh_constraint_pairs.emplace_back(geometry_pt_handle, constrained_pt_handle);
363
364 envelope_invariant->add(std::make_shared<EnvelopeInvariant>(
365 geometry_pt_handle,
366 e.thickness * bbdiag,
367 constrained_pt_handle));
368
369 update_parent_position.emplace_back(attribute_update::make_cast_attribute_transfer_strategy(
370 /*source=*/constrained_pt_handle,
371 /*target=*/pt_attribute));
372
373 update_child_position.emplace_back(attribute_update::make_cast_attribute_transfer_strategy(
374 /*source=*/pt_attribute,
375 /*target=*/constrained_pt_handle));
376 }
377
378
380 // Invariants
382
383 wmtk::logger().trace("Going through invariants");
384 auto inversion_invariant =
385 std::make_shared<SimplexInversionInvariant<Rational>>(*mesh, pt_attribute.as<Rational>());
386
387 std::shared_ptr<function::PerSimplexFunction> amips =
388 std::make_shared<AMIPS>(*mesh, pt_attribute);
389 // auto function_invariant = std::make_shared<FunctionInvariant>(mesh->top_simplex_type(),
390 // amips);
391 auto function_invariant =
392 std::make_shared<MaxFunctionInvariant>(mesh->top_simplex_type(), amips);
393
394 auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(*mesh);
395
396 auto todo_larger = std::make_shared<TodoLargerInvariant>(
397 *mesh,
398 edge_length_attribute.as<double>(),
399 target_edge_length_attribute.as<double>(),
400 4.0 / 3.0);
401
402 auto todo_smaller = std::make_shared<TodoSmallerInvariant>(
403 *mesh,
404 edge_length_attribute.as<double>(),
405 target_edge_length_attribute.as<double>(),
406 4.0 / 5.0);
407
408
409 auto interior_edge = std::make_shared<InteriorEdgeInvariant>(*mesh);
410
411 for (const auto& em : multimesh_meshes) {
412 interior_edge->add_boundary(*(em.first));
413 }
414
415 auto invariant_separate_substructures =
416 std::make_shared<invariants::SeparateSubstructuresInvariant>(*mesh);
417
418 auto frozen_vertex_invariant = std::make_shared<invariants::FrozenVertexInvariant>(
419 *mesh,
420 frozen_vertex_attribute.as<int64_t>());
421 auto frozen_opp_vertex_invariant = std::make_shared<invariants::FrozenOppVertexInvariant>(
422 *mesh,
423 frozen_vertex_attribute.as<int64_t>());
424
426 // renew flags
428 auto visited_edge_flag =
429 mesh->register_attribute<char>("visited_edge", PrimitiveType::Edge, 1, false, char(1));
430
431 auto update_flag_func = [](const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
432 assert(P.cols() == 2);
433 assert(P.rows() == 2 || P.rows() == 3);
434 return Eigen::VectorX<char>::Constant(1, char(1));
435 };
436 auto tag_update =
437 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<char, Rational>>(
438 visited_edge_flag,
439 pt_attribute,
440 update_flag_func);
441
443 // sizing field update flags
445
446
447 // TODO: add with sizing field
448 // auto visited_vertex_flag =
449 // mesh->register_attribute<char>("visited_vertex", PrimitiveType::Vertex, 1, false,
450 // char(1));
451 // pass_through_attributes.push_back(visited_vertex_flag);
452
454 // energy filter flag
456
457 // TODO: add energy filter
458
459 // auto energy_filter_attribute =
460 // mesh->register_attribute<char>("energy_filter", PrimitiveType::Vertex, 1, false,
461 // char(1));
462
463 // auto energy_filter_accessor = mesh->create_accessor<char>(energy_filter_attribute);
464
465 // auto update_energy_filter_func = [](const Eigen::MatrixX<Rational>& P) ->
466 // Eigen::VectorX<char> {
467 // // assert(P.cols() == 2);
468 // // assert(P.rows() == 2 || P.rows() == 3);
469 // return Eigen::VectorX<char>::Constant(1, char(1));
470 // };
471 // auto energy_filter_update =
472 // std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<char, Rational>>(
473 // energy_filter_attribute,
474 // pt_attribute,
475 // update_energy_filter_func);
476
477 // pass_through_attributes.push_back(energy_filter_attribute);
478
480 // Creation of the 4 ops
482 std::vector<std::shared_ptr<Operation>> ops;
483 std::vector<std::string> ops_name;
484
486 // 0) Rounding
488 auto rounding_pt_attribute = mesh->get_attribute_handle_typed<Rational>(
489 options.input_mesh_position,
491 auto rounding = std::make_shared<Rounding>(*mesh, rounding_pt_attribute);
492 rounding->add_invariant(
493 std::make_shared<RoundedInvariant>(*mesh, pt_attribute.as<Rational>(), true));
494 rounding->add_invariant(inversion_invariant);
495
496
498 // 1) EdgeSplit
500 auto split = std::make_shared<EdgeSplit>(*mesh);
501 split->set_priority(long_edges_first);
502
503 split->add_invariant(todo_larger);
504 split->add_invariant(inversion_invariant);
505
506 split->set_new_attribute_strategy(pt_attribute);
507 // split->set_new_attribute_strategy(sizing_field_scalar_attribute);
508 split->set_new_attribute_strategy(
509 visited_edge_flag,
512
513 split->set_new_attribute_strategy(
514 frozen_vertex_attribute,
517 split->set_new_attribute_strategy(
518 target_edge_length_attribute,
521
522 for (const auto& attr : pass_through_attributes) {
523 split->set_new_attribute_strategy(
524 attr,
527 }
528
529 // split->add_transfer_strategy(amips_update);
530 split->add_transfer_strategy(edge_length_update);
531 split->add_transfer_strategy(tag_update); // for renew the queue
532 // split->add_transfer_strategy(energy_filter_update);
533
534 // split->add_transfer_strategy(target_edge_length_update);
535
536 auto split_then_round = std::make_shared<AndOperationSequence>(*mesh);
537 split_then_round->add_operation(split);
538 split_then_round->add_operation(rounding);
539
540 for (auto& s : update_child_position) {
541 split_then_round->add_transfer_strategy(s);
542 }
543
544 // split unrounded
545 auto split_unrounded = std::make_shared<EdgeSplit>(*mesh);
546 split_unrounded->set_priority(long_edges_first);
547
548 split_unrounded->add_invariant(todo_larger);
549
550 auto split_unrounded_transfer_strategy =
551 std::make_shared<SplitNewAttributeStrategy<Rational>>(pt_attribute);
552 split_unrounded_transfer_strategy->set_strategy(
553 [](const Eigen::VectorX<Rational>& a, const std::bitset<2>&) {
554 return std::array<Eigen::VectorX<Rational>, 2>{{a, a}};
555 });
556 split_unrounded_transfer_strategy->set_rib_strategy(
557 [](const Eigen::VectorX<Rational>& p0_d,
558 const Eigen::VectorX<Rational>& p1_d,
559 const std::bitset<2>& bs) -> Eigen::VectorX<Rational> {
560 Eigen::VectorX<Rational> p0(p0_d.size());
561 Eigen::VectorX<Rational> p1(p1_d.size());
562 for (int i = 0; i < p0_d.size(); ++i) {
563 p0[i] = Rational(p0_d[i], false);
564 p1[i] = Rational(p1_d[i], false);
565 }
566 if (bs[0] == bs[1]) {
567 return (p0 + p1) / Rational(2, false);
568 } else if (bs[0]) {
569 return p0;
570
571 } else {
572 return p1;
573 }
574 });
575
576 split_unrounded->set_new_attribute_strategy(pt_attribute, split_unrounded_transfer_strategy);
577 // split_unrounded->set_new_attribute_strategy(sizing_field_scalar_attribute);
578 split_unrounded->set_new_attribute_strategy(
579 visited_edge_flag,
582 split_unrounded->set_new_attribute_strategy(
583 frozen_vertex_attribute,
586 for (const auto& attr : pass_through_attributes) {
587 split_unrounded->set_new_attribute_strategy(
588 attr,
591 }
592 split_unrounded->set_new_attribute_strategy(
593 target_edge_length_attribute,
596
597 split_unrounded->add_transfer_strategy(amips_update);
598 split_unrounded->add_transfer_strategy(edge_length_update);
599 split_unrounded->add_transfer_strategy(tag_update); // for renew the queue
600 // split_unrounded->add_transfer_strategy(energy_filter_update);
601
602 // split->add_transfer_strategy(target_edge_length_update);
603
604 auto split_sequence = std::make_shared<OrOperationSequence>(*mesh);
605 split_sequence->add_operation(split_then_round);
606 split_sequence->add_operation(split_unrounded);
607 // split_sequence->add_invariant(
608 // std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<char>()));
609
610 split_sequence->set_priority(long_edges_first);
611
612
613 if (!options.skip_split) {
614 ops.emplace_back(split_sequence);
615 ops_name.emplace_back("SPLIT");
616
617 ops.emplace_back(rounding);
618 ops_name.emplace_back("rounding");
619 }
620
621
623 // collapse transfer
625 auto clps_strat1 = std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
626 // clps_strat1->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
627 // clps_strat1->set_strategy(CollapseBasicStrategy::Default);
628 clps_strat1->set_strategy(CollapseBasicStrategy::CopyOther);
629
630 auto clps_strat2 = std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
631 // clps_strat2->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
632 // clps_strat2->set_strategy(CollapseBasicStrategy::Default);
633 clps_strat2->set_strategy(CollapseBasicStrategy::CopyTuple);
634
636 // 2) EdgeCollapse
638
639 auto setup_collapse = [&](std::shared_ptr<EdgeCollapse>& collapse) {
640 collapse->add_invariant(invariant_separate_substructures);
641 collapse->add_invariant(std::make_shared<MultiMeshMapValidInvariant>(*mesh));
642 collapse->add_invariant(link_condition);
643 collapse->add_invariant(inversion_invariant);
644 collapse->add_invariant(function_invariant);
645 collapse->add_invariant(envelope_invariant);
646
647 collapse->set_new_attribute_strategy(
648 visited_edge_flag,
650
651 collapse->add_transfer_strategy(tag_update);
652 // collapse->add_transfer_strategy(energy_filter_update);
653 for (const auto& attr : pass_through_attributes) {
654 collapse->set_new_attribute_strategy(
655 attr,
657 }
658 collapse->set_new_attribute_strategy(
659 target_edge_length_attribute,
661 // THis triggers a segfault in release
662 // solved somehow
663 // collapse->set_priority(short_edges_first);
664
665 collapse->add_transfer_strategy(amips_update);
666 collapse->add_transfer_strategy(edge_length_update);
667 // collapse->add_transfer_strategy(target_edge_length_update);
668
669 for (auto& s : update_child_position) {
670 collapse->add_transfer_strategy(s);
671 }
672 };
673
674 auto collapse1 = std::make_shared<EdgeCollapse>(*mesh);
675
676 // TODO: implement for 2d
677 // collapse1->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
678 // *mesh,
679 // pt_attribute.as<Rational>(),
680 // amips_attribute.as<double>(),
681 // 1));
682
683 collapse1->add_invariant(frozen_vertex_invariant);
684 collapse1->set_new_attribute_strategy(pt_attribute, clps_strat1);
685 collapse1->set_new_attribute_strategy(
686 frozen_vertex_attribute,
687 CollapseBasicStrategy::CopyOther);
688 // collapse1->set_new_attribute_strategy(sizing_field_scalar_attribute, clps_strat1);
689 setup_collapse(collapse1);
690
691 auto collapse2 = std::make_shared<EdgeCollapse>(*mesh);
692 // collapse2->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
693 // *mesh,
694 // pt_attribute.as<Rational>(),
695 // amips_attribute.as<double>(),
696 // 0));
697
698 collapse2->add_invariant(frozen_opp_vertex_invariant);
699 collapse2->set_new_attribute_strategy(pt_attribute, clps_strat2);
700 collapse2->set_new_attribute_strategy(
701 frozen_vertex_attribute,
702 CollapseBasicStrategy::CopyTuple);
703 // collapse2->set_new_attribute_strategy(sizing_field_scalar_attribute, clps_strat2);
704 setup_collapse(collapse2);
705
706 auto collapse = std::make_shared<OrOperationSequence>(*mesh);
707 collapse->add_operation(collapse1);
708 collapse->add_operation(collapse2);
709 collapse->add_invariant(todo_smaller);
710
711 auto collapse_then_round = std::make_shared<AndOperationSequence>(*mesh);
712 collapse_then_round->add_operation(collapse);
713 collapse_then_round->add_operation(rounding);
714
715 collapse_then_round->set_priority(short_edges_first);
716 // collapse_then_round->add_invariant(
717 // std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<char>()));
718
719
720 for (auto& s : update_child_position) {
721 collapse_then_round->add_transfer_strategy(s);
722 }
723
724 if (!options.skip_collapse) {
725 ops.emplace_back(collapse_then_round);
726 ops_name.emplace_back("COLLAPSE");
727
728 ops.emplace_back(rounding);
729 ops_name.emplace_back("rounding");
730 }
731
733 // 3) Swap
735
736 auto setup_swap = [&](Operation& op,
737 EdgeCollapse& collapse,
738 EdgeSplit& split,
739 std::shared_ptr<Invariant> simplex_invariant,
740 bool is_edge = true) {
741 if (is_edge) op.set_priority(long_edges_first);
742
743 op.add_invariant(simplex_invariant);
744 op.add_invariant(
745 std::make_shared<Swap2dUnroundedVertexInvariant>(*mesh, pt_attribute.as<Rational>()));
746 op.add_invariant(inversion_invariant);
747 op.add_invariant(function_invariant);
748
749 op.add_transfer_strategy(amips_update);
750 op.add_transfer_strategy(edge_length_update);
751 op.add_transfer_strategy(tag_update);
752 // op.add_transfer_strategy(target_edge_length_update);
753 // for (auto& s : update_child_position) {
754 // op.add_transfer_strategy(s);
755 // }
756
757 collapse.add_invariant(invariant_separate_substructures);
758 collapse.add_invariant(link_condition);
759
760
761 collapse.set_new_attribute_strategy(pt_attribute, CollapseBasicStrategy::CopyOther);
762 split.set_new_attribute_strategy(pt_attribute);
763
764 split.set_new_attribute_strategy(
765 visited_edge_flag,
768
769 collapse.set_new_attribute_strategy(
770 visited_edge_flag,
772
773 split.set_new_attribute_strategy(
774 frozen_vertex_attribute,
777
778 collapse.set_new_attribute_strategy(
779 frozen_vertex_attribute,
780 CollapseBasicStrategy::CopyOther);
781
782 split.set_new_attribute_strategy(
783 target_edge_length_attribute,
786 collapse.set_new_attribute_strategy(
787 target_edge_length_attribute,
789
790 // this might not be necessary
791 // for (auto& s : update_child_position) {
792 // collapse.add_transfer_strategy(s);
793 // split.add_transfer_strategy(s);
794 // }
795
796
797 for (const auto& attr : pass_through_attributes) {
798 split.set_new_attribute_strategy(
799 attr,
802 collapse.set_new_attribute_strategy(
803 attr,
805 }
806 };
807
808
809 auto swap = std::make_shared<TriEdgeSwap>(*mesh);
810 setup_swap(*swap, swap->collapse(), swap->split(), interior_edge);
811
812 if (!options.skip_swap) {
813 ops.push_back(swap);
814 ops_name.push_back("swap");
815
816 ops.emplace_back(rounding);
817 ops_name.emplace_back("rounding");
818 }
819
820 // 4) Smoothing
821 // //////////////////////////////////////
822 // // special smoothing on surface
823 // //////////////////////////////////////
824
825 // if (mesh->top_simplex_type() == PrimitiveType::Tetrahedron) {
826 // for (auto& pair : mesh_constraint_pairs) {
827 // if (pair.second.mesh().top_simplex_type() != PrimitiveType::Triangle) continue;
828
829 // auto& child_mesh = pair.second.mesh();
830 // auto& child_position_handle = pair.second;
831
832 // auto lap_smoothing = std::make_shared<TetWildTangentialLaplacianSmoothing>(
833 // child_mesh,
834 // child_position_handle.as<Rational>());
835 // lap_smoothing->add_invariant(std::make_shared<RoundedInvariant>(
836 // child_mesh,
837 // child_position_handle.as<Rational>()));
838 // lap_smoothing->add_invariant(inversion_invariant);
839
840 // auto proj_lap_smoothing =
841 // std::make_shared<ProjectOperation>(lap_smoothing, mesh_constraint_pairs);
842 // proj_lap_smoothing->use_random_priority() = true;
843
844 // proj_lap_smoothing->add_invariant(envelope_invariant);
845 // proj_lap_smoothing->add_invariant(inversion_invariant);
846 // proj_lap_smoothing->add_invariant(
847 // std::make_shared<EnergyFilterInvariant>(*mesh,
848 // energy_filter_attribute.as<char>()));
849
850 // proj_lap_smoothing->add_transfer_strategy(amips_update);
851 // proj_lap_smoothing->add_transfer_strategy(edge_length_update);
852 // for (auto& s : update_parent_position) { // TODO::this should from only one child
853 // proj_lap_smoothing->add_transfer_strategy(s);
854 // }
855 // for (auto& s : update_child_position) {
856 // proj_lap_smoothing->add_transfer_strategy(s);
857 // }
858
859 // ops.push_back(proj_lap_smoothing);
860 // ops_name.push_back("LAPLACIAN SMOOTHING");
861 // }
862 // }
863
864 // auto energy =
865 // std::make_shared<function::LocalNeighborsSumFunction>(*mesh, pt_attribute, *amips);
866 // auto smoothing = std::make_shared<OptimizationSmoothing>(energy);
867 auto smoothing = std::make_shared<AMIPSOptimizationSmoothing>(*mesh, pt_attribute);
868 smoothing->add_invariant(
869 std::make_shared<RoundedInvariant>(*mesh, pt_attribute.as<Rational>()));
870 smoothing->add_invariant(frozen_vertex_invariant);
871 smoothing->add_invariant(inversion_invariant);
872 for (auto& s : update_child_position) {
873 smoothing->add_transfer_strategy(s);
874 }
875
876 // test code
877 // smoothing->add_invariant(envelope_invariant);
878 // smoothing->add_transfer_strategy(edge_length_update);
879 // ops.push_back(smoothing);
880 // ops_name.push_back("SMOOTHING");
881 // ops.emplace_back(rounding);
882 // ops_name.emplace_back("rounding");
883 //--------
884
885 auto proj_smoothing = std::make_shared<ProjectOperation>(smoothing, mesh_constraint_pairs);
886 proj_smoothing->use_random_priority() = true;
887 proj_smoothing->add_invariant(frozen_vertex_invariant);
888 proj_smoothing->add_invariant(envelope_invariant);
889 proj_smoothing->add_invariant(inversion_invariant);
890 // proj_smoothing->add_invariant(
891 // std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<char>()));
892
893 proj_smoothing->add_transfer_strategy(amips_update);
894 proj_smoothing->add_transfer_strategy(edge_length_update);
895 for (auto& s : update_parent_position) {
896 proj_smoothing->add_transfer_strategy(s);
897 }
898
899 for (auto& s : update_child_position) {
900 proj_smoothing->add_transfer_strategy(s);
901 }
902 // proj_smoothing->add_transfer_strategy(target_edge_length_update);
903
904 if (!options.skip_smooth) {
905 for (int i = 0; i < 1; ++i) {
906 // some old code to do smoothing several times, maybe useful later
907 ops.push_back(proj_smoothing);
908 ops_name.push_back("SMOOTHING");
909 }
910
911 ops.emplace_back(rounding);
912 ops_name.emplace_back("rounding");
913 }
914
915 write(
916 mesh,
919 options.input_mesh_position,
920 0,
921 options.intermediate_output);
922
924 // Running all ops in order n times
925 Scheduler scheduler;
926 {
927 const size_t freq = options.scheduler_update_frequency;
928 scheduler.set_update_frequency(freq == 0 ? std::optional<size_t>{} : freq);
929 }
930 int64_t success = 10;
931
933 // preprocessing
935
936 // debug code
937 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
938 const auto vertices = mesh->orient_vertices(t);
939 std::vector<Vector2r> pos;
940 for (int i = 0; i < vertices.size(); ++i) {
941 pos.push_back(pt_accessor.const_vector_attribute(vertices[i]));
942 }
943 if (wmtk::utils::wmtk_orient2d(pos[0], pos[1], pos[2]) <= 0) {
944 wmtk::logger().error("Flipped triangle!");
945 }
946 }
947
948 SchedulerStats pre_stats;
949
950 logger().info("----------------------- Preprocess Collapse -----------------------");
951
952 logger().info("Executing collapse ...");
953
954
955 wmtk::attribute::TypedAttributeHandle<char> visited_edge_flag_t = visited_edge_flag.as<char>();
956
957 pre_stats = scheduler.run_operation_on_all(*collapse_then_round, visited_edge_flag_t);
958 logger().info(
959 "Executed {}, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, "
960 "executing: {}",
961 "preprocessing collapse",
964 pre_stats.number_of_failed_operations(),
965 pre_stats.collecting_time,
966 pre_stats.sorting_time,
967 pre_stats.executing_time);
968
969 success = pre_stats.number_of_successful_operations();
970
971 // verbose logger, can be removed
972 int64_t unrounded = 0;
973 int64_t frozen = 0;
974 for (const auto& v : mesh->get_all(PrimitiveType::Vertex)) {
975 const auto p = pt_accessor.vector_attribute(v);
976 for (int64_t d = 0; d < 2; ++d) {
977 if (!p[d].is_rounded()) {
978 ++unrounded;
979 break;
980 }
981 }
982
983 if (frozen_vertex_accessor.scalar_attribute(v) == 1) {
984 frozen++;
985 }
986 }
987
988 logger().info("Mesh has {} unrounded vertices", unrounded);
989 logger().error("Mesh has {} frozen vertices", frozen);
990
991 // debug code
992 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
993 const auto vertices = mesh->orient_vertices(t);
994 std::vector<Vector2r> pos;
995 for (int i = 0; i < vertices.size(); ++i) {
996 pos.push_back(pt_accessor.const_vector_attribute(vertices[i]));
997 }
998 if (wmtk::utils::wmtk_orient2d(pos[0], pos[1], pos[2]) <= 0) {
999 wmtk::logger().error("Flipped triangle!");
1000 }
1001 }
1002
1003
1004 // compute max energy
1005 double max_energy = std::numeric_limits<double>::lowest();
1006 double min_energy = std::numeric_limits<double>::max();
1007 double avg_energy = 0;
1008 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1009 // double e = amips->get_value(simplex::Simplex(mesh->top_simplex_type(), t));
1010 double e = amips_accessor.scalar_attribute(t);
1011 max_energy = std::max(max_energy, e);
1012 min_energy = std::min(min_energy, e);
1013 avg_energy += e;
1014 }
1015
1016 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1017
1018 logger().info(
1019 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1020 max_energy,
1021 min_energy,
1022 avg_energy);
1023
1024 // std::ofstream file0("quality_plot_pre.csv");
1025 // file0 << "tid, quality" << std::endl;
1026 // int64_t t_cnt = 0;
1027 // for (const auto& t : mesh->get_all(PrimitiveType::Tetrahedron)) {
1028 // t_cnt++;
1029 // file0 << t_cnt << ", " << amips_accessor.scalar_attribute(t) << std::endl;
1030 // }
1031
1032
1033 double old_max_energy = max_energy;
1034 double old_avg_energy = avg_energy;
1035 int iii = 0;
1036 bool is_double = false;
1037 for (int64_t i = 0; i < options.max_passes; ++i) {
1038 logger().info("--------------------------- Pass {} ---------------------------", i);
1039
1040 SchedulerStats pass_stats;
1041 int jj = 0;
1042 for (auto& op : ops) {
1043 logger().info("Executing {} ...", ops_name[jj]);
1044 SchedulerStats stats;
1045 if (op->primitive_type() == PrimitiveType::Edge) {
1046 stats = scheduler.run_operation_on_all(*op, visited_edge_flag_t);
1047 // } else if (ops_name[jj] == "SMOOTHING") {
1048 // // stats = scheduler.run_operation_on_all(*op);
1049 // stats =
1050 // scheduler.run_operation_on_all_coloring(*op,
1051 // coloring_attribute.as<int64_t>());
1052 } else {
1053 stats = scheduler.run_operation_on_all(*op);
1054 }
1055 pass_stats += stats;
1056 logger().info(
1057 "Executed {}, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, "
1058 "executing: {}",
1059 ops_name[jj],
1063 stats.collecting_time,
1064 stats.sorting_time,
1065 stats.executing_time);
1066
1067 success = stats.number_of_successful_operations();
1068
1069 // verbose logger, can be removed
1070 int64_t unrounded = 0;
1071 for (const auto& v : mesh->get_all(PrimitiveType::Vertex)) {
1072 const auto p = pt_accessor.vector_attribute(v);
1073 for (int64_t d = 0; d < 2; ++d) {
1074 if (!p[d].is_rounded()) {
1075 ++unrounded;
1076 break;
1077 }
1078 }
1079 }
1080
1081 logger().info("Mesh has {} unrounded vertices", unrounded);
1082
1083 // debug code
1084 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1085 const auto vertices = mesh->orient_vertices(t);
1086 std::vector<Vector2r> pos;
1087 for (int i = 0; i < vertices.size(); ++i) {
1088 pos.push_back(pt_accessor.const_vector_attribute(vertices[i]));
1089 }
1090 if (wmtk::utils::wmtk_orient2d(pos[0], pos[1], pos[2]) <= 0) {
1091 wmtk::logger().error("Flipped triangle!");
1092 }
1093 }
1094
1095 avg_energy = 0;
1096
1097 // compute max energy
1098 max_energy = std::numeric_limits<double>::lowest();
1099 min_energy = std::numeric_limits<double>::max();
1100 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1101 // double e = amips->get_value(simplex::Simplex(mesh->top_simplex_type(), t));
1102 double e = amips_accessor.scalar_attribute(t);
1103 max_energy = std::max(max_energy, e);
1104 min_energy = std::min(min_energy, e);
1105 avg_energy += e;
1106 }
1107
1108 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1109
1110 logger().info(
1111 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1112 max_energy,
1113 min_energy,
1114 avg_energy);
1115
1116
1117 ++jj;
1118 }
1119
1120 logger().info(
1121 "Executed {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, executing: {}",
1122 pass_stats.number_of_performed_operations(),
1124 pass_stats.number_of_failed_operations(),
1125 pass_stats.collecting_time,
1126 pass_stats.sorting_time,
1127 pass_stats.executing_time);
1128
1130
1131 write(
1132 mesh,
1135 options.input_mesh_position,
1136 i + 1,
1137 options.intermediate_output);
1138
1139 assert(mesh->is_connectivity_valid());
1140
1141 // compute max energy
1142 max_energy = std::numeric_limits<double>::lowest();
1143 min_energy = std::numeric_limits<double>::max();
1144 avg_energy = 0;
1145 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1146 // double e = amips->get_value(simplex::Simplex(mesh->top_simplex_type(), t));
1147 double e = amips_accessor.scalar_attribute(t);
1148 max_energy = std::max(max_energy, e);
1149 min_energy = std::min(min_energy, e);
1150 avg_energy += e;
1151 }
1152
1153 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1154
1155 int64_t unrounded = 0;
1156 if (!is_double) {
1157 bool rational = false;
1158 for (const auto& v : mesh->get_all(PrimitiveType::Vertex)) {
1159 const auto p = pt_accessor.vector_attribute(v);
1160 for (int64_t d = 0; d < bmax.size(); ++d) {
1161 if (!p[d].is_rounded()) {
1162 rational = true;
1163 ++unrounded;
1164 break;
1165 }
1166 }
1167 }
1168
1169 is_double = !rational;
1170 }
1171
1172 logger().info("Mesh has {} unrounded vertices", unrounded);
1173 logger().info(
1174 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1175 max_energy,
1176 min_energy,
1177 avg_energy);
1178
1179
1180 // adjust sizing field
1181 // if (i > 0 && old_max_energy - max_energy < 5e-1 &&
1182 // (old_avg_energy - avg_energy) / avg_energy < 0.1) {
1183 // wmtk::logger().info("adjusting sizing field ...");
1184
1185 // adjust_sizing_field(
1186 // *mesh,
1187 // pt_attribute.as<Rational>(),
1188 // edge_length_attribute.as<double>(),
1189 // sizing_field_scalar_attribute.as<double>(),
1190 // amips_attribute.as<double>(),
1191 // target_edge_length_attribute.as<double>(),
1192 // visited_vertex_flag.as<char>(),
1193 // target_max_amips,
1194 // max_energy,
1195 // target_edge_length,
1196 // min_edge_length);
1197
1198 // wmtk::logger().info("adjusting sizing field finished");
1199
1200 // // wmtk::logger().info("setting energy filter ...");
1201 // // set_operation_energy_filter_after_sizing_field(
1202 // // *mesh,
1203 // // pt_attribute.as<Rational>(),
1204 // // amips_attribute.as<double>(),
1205 // // energy_filter_attribute.as<char>(),
1206 // // visited_vertex_flag.as<char>(),
1207 // // target_max_amips,
1208 // // max_energy,
1209 // // target_edge_length);
1210 // // wmtk::logger().info("setting energy filter finished");
1211
1212 // // int64_t e_cnt = 0;
1213 // // for (const auto& e : mesh->get_all(PrimitiveType::Edge)) {
1214 // // if (energy_filter_accessor.scalar_attribute(e) == char(1) ||
1215 // // energy_filter_accessor.scalar_attribute(
1216 // // mesh->switch_tuple(e, PrimitiveType::Vertex)) == char(1)) {
1217 // // e_cnt++;
1218 // // }
1219 // // }
1220 // // wmtk::logger().info(
1221 // // "{} edges are going to be executed out of {}",
1222 // // e_cnt,
1223 // // mesh->get_all(PrimitiveType::Edge).size());
1224
1225 // for (const auto& v : mesh->get_all(PrimitiveType::Vertex)) {
1226 // energy_filter_accessor.scalar_attribute(v) = char(1);
1227 // }
1228 // wmtk::logger().info("reset energy filter");
1229 // } else {
1230 // wmtk::logger().info("setting energy filter ...");
1231 // set_operation_energy_filter(
1232 // *mesh,
1233 // pt_attribute.as<Rational>(),
1234 // amips_attribute.as<double>(),
1235 // energy_filter_attribute.as<char>(),
1236 // visited_vertex_flag.as<char>(),
1237 // target_max_amips,
1238 // max_energy,
1239 // target_edge_length);
1240 // wmtk::logger().info("setting energy filter finished");
1241
1242 // int64_t e_cnt = 0;
1243 // for (const auto& e : mesh->get_all(PrimitiveType::Edge)) {
1244 // if (energy_filter_accessor.scalar_attribute(e) == char(1) ||
1245 // energy_filter_accessor.scalar_attribute(
1246 // mesh->switch_tuple(e, PrimitiveType::Vertex)) == char(1)) {
1247 // e_cnt++;
1248 // }
1249 // }
1250 // wmtk::logger().info(
1251 // "{} edges are going to be executed out of {}",
1252 // e_cnt,
1253 // mesh->get_all(PrimitiveType::Edge).size());
1254 // }
1255
1256 old_max_energy = max_energy;
1257 old_avg_energy = avg_energy;
1258
1259 // stop at good quality
1260 if (max_energy <= target_max_amips && is_double) break;
1261 }
1262
1263 logger().info("----------------------- Postprocess Collapse -----------------------");
1264
1265 logger().info("Executing collapse ...");
1266
1267 auto post_stats = scheduler.run_operation_on_all(*collapse_then_round, visited_edge_flag_t);
1268 logger().info(
1269 "Executed {}, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, "
1270 "executing: {}",
1271 "preprocessing collapse",
1272 post_stats.number_of_performed_operations(),
1273 post_stats.number_of_successful_operations(),
1274 post_stats.number_of_failed_operations(),
1275 post_stats.collecting_time,
1276 post_stats.sorting_time,
1277 post_stats.executing_time);
1278
1279 // compute max energy
1280 max_energy = std::numeric_limits<double>::lowest();
1281 min_energy = std::numeric_limits<double>::max();
1282 avg_energy = 0;
1283 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1284 // double e = amips->get_value(simplex::Simplex(mesh->top_simplex_type(), t));
1285 double e = amips_accessor.scalar_attribute(t);
1286 max_energy = std::max(max_energy, e);
1287 min_energy = std::min(min_energy, e);
1288 avg_energy += e;
1289 }
1290
1291 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1292
1293 logger().info(
1294 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1295 max_energy,
1296 min_energy,
1297 avg_energy);
1298
1300
1301 std::vector<std::pair<std::shared_ptr<Mesh>, std::string>> all_meshes;
1302 all_meshes.push_back(std::make_pair(mesh, "main"));
1303
1304 for (const auto& p : multimesh_meshes) {
1305 all_meshes.push_back(p);
1306 }
1307
1308 return all_meshes;
1309}
1310
1311} // namespace wmtk::components::internal
SchedulerStats run_operation_on_all(operations::Operation &op)
Definition Scheduler.cpp:33
void set_update_frequency(std::optional< size_t > &&freq={})
int64_t number_of_failed_operations() const
Returns the number of failed operations performed by the scheduler.
Definition Scheduler.hpp:23
int64_t number_of_performed_operations() const
Returns the number of performed operations performed by the scheduler.
Definition Scheduler.hpp:30
int64_t number_of_successful_operations() const
Returns the number of successful operations performed by the scheduler.
Definition Scheduler.hpp:16
Handle that represents attributes for some mesh.
void add_invariant(std::shared_ptr< Invariant > invariant)
Definition Operation.hpp:48
void set_priority(const std::function< double(const simplex::Simplex &)> &func)
Definition Operation.hpp:50
virtual PrimitiveType primitive_type() const =0
void add_transfer_strategy(const std::shared_ptr< const operations::AttributeTransferStrategyBase > &other)
Definition Operation.cpp:59
std::pair< attribute::MeshAttributeHandle, attribute::MeshAttributeHandle > MeshConstrainPair
static Simplex vertex(const Mesh &m, const Tuple &t)
Definition Simplex.hpp:56
std::vector< std::pair< std::shared_ptr< Mesh >, std::string > > wildmeshing2d(const WildMeshingOptions &options)
void write(const Mesh &mesh, const std::string &out_dir, const std::string &name, const std::string &vname, const int64_t index, const bool intermediate_output)
double Tet_AMIPS_energy(const std::array< double, 12 > &T)
Definition AMIPS.cpp:375
double Tri_AMIPS_energy(const std::array< double, 6 > &T)
Definition AMIPS.cpp:473
void consolidate(Mesh &mesh)
std::vector< Tuple > vertices(const Mesh &m, const Simplex &simplex)
bool link_condition(const EdgeMesh &mesh, const Tuple &edge)
Check if the edge to collapse satisfying the link condition.
int wmtk_orient2d(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y)
Definition orient.cpp:178
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:58
std::vector< EnvelopeOptions > envelopes
std::vector< attribute::MeshAttributeHandle > pass_through