Wildmeshing Toolkit
Loading...
Searching...
No Matches
wildmeshing3d.cpp
Go to the documentation of this file.
1#include "wildmeshing3d.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
63
65
67#include <wmtk/utils/orient.hpp>
68
71
72#include <queue>
74#include <wmtk/simplex/link.hpp>
75
76#include <fstream>
77
79
80using namespace simplex;
81using namespace operations;
82using namespace operations::tri_mesh;
83using namespace operations::tet_mesh;
84using namespace operations::composite;
85using namespace function;
86using namespace invariants;
87
88std::vector<std::pair<std::shared_ptr<Mesh>, std::string>> wildmeshing3d(
89 const WildMeshingOptions& options)
90{
91 auto mesh = options.input_mesh;
92
93 if (!mesh->is_connectivity_valid()) {
94 throw std::runtime_error("input mesh for wildmeshing connectivity invalid");
95 }
96
97 wmtk::logger().trace("Getting rational point handle");
98
100 // Retriving vertices
101 //
102 if (options.replace_double_coordinate) {
103 wmtk::logger().trace("Found double attribugte");
104 auto pt_double_attribute =
105 mesh->get_attribute_handle<double>(options.input_mesh_position, PrimitiveType::Vertex);
106
107 if (!mesh->has_attribute<Rational>(options.input_mesh_position, PrimitiveType::Vertex)) {
108 wmtk::utils::cast_attribute<wmtk::Rational>(
109 pt_double_attribute,
110 *mesh,
111 options.input_mesh_position);
112
113
114 } else {
115 auto pt_attribute = mesh->get_attribute_handle<Rational>(
116 options.input_mesh_position,
118 wmtk::utils::cast_attribute<wmtk::Rational>(pt_double_attribute, pt_attribute);
119 }
120 mesh->delete_attribute(pt_double_attribute);
121 }
122 auto pt_attribute =
123 mesh->get_attribute_handle<Rational>(options.input_mesh_position, PrimitiveType::Vertex);
124 wmtk::logger().trace("Getting rational point accessor");
125 auto pt_accessor = mesh->create_accessor(pt_attribute.as<Rational>());
126
127 wmtk::logger().trace("Computing bounding box diagonal");
129 // computing bbox diagonal
130 Eigen::VectorXd bmin(mesh->top_cell_dimension());
131 bmin.setConstant(std::numeric_limits<double>::max());
132 Eigen::VectorXd bmax(mesh->top_cell_dimension());
133 bmax.setConstant(std::numeric_limits<double>::lowest());
134
135 const auto vertices = mesh->get_all(PrimitiveType::Vertex);
136 for (const auto& v : vertices) {
137 const auto p = pt_accessor.vector_attribute(v).cast<double>();
138 for (int64_t d = 0; d < bmax.size(); ++d) {
139 bmin[d] = std::min(bmin[d], p[d]);
140 bmax[d] = std::max(bmax[d], p[d]);
141 }
142 }
143
144 const double bbdiag = (bmax - bmin).norm();
145
146 wmtk::logger().info("bbox max {}, bbox min {}, diag {}", bmax, bmin, bbdiag);
147
148 const double target_edge_length = options.target_edge_length * bbdiag;
149
150 // const double target_edge_length =
151 // options.target_edge_length * bbdiag /
152 // std::min(
153 // 2.0,
154 // 1 + options.target_edge_length * 2); // min to prevent bad option.target_edge_length
155
156 wmtk::logger().info("target edge length: {}", target_edge_length);
157
159 // store amips
160 auto amips_attribute =
161 mesh->register_attribute<double>("wildmeshing_amips", mesh->top_simplex_type(), 1);
162 auto amips_accessor = mesh->create_accessor(amips_attribute.as<double>());
163 // amips update
164 auto compute_amips = [](const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
165 assert(P.rows() == 2 || P.rows() == 3); // rows --> attribute dimension
166 assert(P.cols() == P.rows() + 1);
167 if (P.cols() == 3) {
168 // triangle
169 assert(P.rows() == 2);
170 std::array<double, 6> pts;
171 for (size_t i = 0; i < 3; ++i) {
172 for (size_t j = 0; j < 2; ++j) {
173 pts[2 * i + j] = P(j, i).to_double();
174 }
175 }
176 const double a = Tri_AMIPS_energy(pts);
177 return Eigen::VectorXd::Constant(1, a);
178 } else {
179 // tet
180 assert(P.rows() == 3);
181 std::array<double, 12> pts;
182 for (size_t i = 0; i < 4; ++i) {
183 for (size_t j = 0; j < 3; ++j) {
184 pts[3 * i + j] = P(j, i).to_double();
185 }
186 }
187 const double a = Tet_AMIPS_energy(pts);
188 return Eigen::VectorXd::Constant(1, a);
189 }
190 };
191 auto amips_update =
192 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
193 amips_attribute,
194 pt_attribute,
195 compute_amips);
196 amips_update->run_on_all();
197
198 double max_amips = std::numeric_limits<double>::lowest();
199 double min_amips = std::numeric_limits<double>::max();
200
201 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
202 // double e = amips->get_value(simplex::Simplex(mesh->top_simplex_type(), t));
203 double e = amips_accessor.scalar_attribute(t);
204 max_amips = std::max(max_amips, e);
205 min_amips = std::min(min_amips, e);
206 }
207
208 logger().info("Initial Max AMIPS Energy: {}, Min AMIPS Energy: {}", max_amips, min_amips);
209
211 // sizing field scalar
213
214 auto sizing_field_scalar_attribute = mesh->register_attribute<double>(
215 "sizing_field_scalar",
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
267 // default transfer
268 auto pass_through_attributes = options.pass_through;
269 pass_through_attributes.push_back(edge_length_attribute);
270 pass_through_attributes.push_back(amips_attribute);
271 // pass_through_attributes.push_back(target_edge_length_attribute);
272
273
275 // Lambdas for priority
277 auto long_edges_first = [&](const simplex::Simplex& s) {
278 assert(s.primitive_type() == PrimitiveType::Edge);
279 return -edge_length_accessor.scalar_attribute(s.tuple());
280 };
281 auto short_edges_first = [&](const simplex::Simplex& s) {
282 assert(s.primitive_type() == PrimitiveType::Edge);
283 return edge_length_accessor.scalar_attribute(s.tuple());
284 };
285
286
288 // envelopes
290
291 using MeshConstrainPair = ProjectOperation::MeshConstrainPair;
292
293 auto envelope_invariant = std::make_shared<InvariantCollection>(*mesh);
294 std::vector<std::shared_ptr<AttributeTransferStrategyBase>> update_child_position,
295 update_parent_position;
296 std::vector<std::shared_ptr<Mesh>> envelopes;
297 std::vector<MeshConstrainPair> mesh_constraint_pairs;
298
299 std::vector<std::pair<std::shared_ptr<Mesh>, std::string>> multimesh_meshes;
300
301 // assert(options.envelopes.size() == 4);
302 // four kind of envelopes in tetwild [surface_mesh,
303 // open_boudnary, nonmanifold_edges, is_boundary(bbox)]
304 // TODO: add nonmanifold vertex point mesh
305
306 for (const auto& e : options.envelopes) {
307 auto constrained_mesh = e.envelope_constrained_mesh;
308 auto geometry_mesh = e.envelope_geometry_mesh;
309
310 wmtk::logger().info(
311 "wildmeshing3d: registered {} mesh as envelope constraints",
312 e.envelope_name);
313
314 const bool geometry_has_double_pos =
315 geometry_mesh->has_attribute<double>(e.geometry_position_name, PrimitiveType::Vertex);
316 const bool geometry_has_rational_pos =
317 geometry_mesh->has_attribute<Rational>(e.geometry_position_name, PrimitiveType::Vertex);
318 assert(geometry_has_double_pos || geometry_has_rational_pos);
319
320 auto geometry_pt_handle = geometry_has_double_pos
321 ? geometry_mesh->get_attribute_handle<double>(
322 e.geometry_position_name,
324 : geometry_mesh->get_attribute_handle<Rational>(
325 e.geometry_position_name,
327
328 auto constrained_pt_handle = constrained_mesh->get_attribute_handle<Rational>(
329 e.constrained_position_name,
331
332 multimesh_meshes.push_back(std::make_pair(constrained_mesh, e.envelope_name));
333 pass_through_attributes.emplace_back(constrained_pt_handle);
334
335 mesh_constraint_pairs.emplace_back(geometry_pt_handle, constrained_pt_handle);
336
337 envelope_invariant->add(std::make_shared<EnvelopeInvariant>(
338 geometry_pt_handle,
339 e.thickness * bbdiag,
340 constrained_pt_handle));
341
342 update_parent_position.emplace_back(attribute_update::make_cast_attribute_transfer_strategy(
343 /*source=*/constrained_pt_handle,
344 /*target=*/pt_attribute));
345
346 update_child_position.emplace_back(attribute_update::make_cast_attribute_transfer_strategy(
347 /*source=*/pt_attribute,
348 /*target=*/constrained_pt_handle));
349 }
350
352
353
355 // Invariants
357
358 wmtk::logger().trace("Going through invariants");
359 auto inversion_invariant =
360 std::make_shared<SimplexInversionInvariant<Rational>>(*mesh, pt_attribute.as<Rational>());
361
362 std::shared_ptr<function::PerSimplexFunction> amips =
363 std::make_shared<AMIPS>(*mesh, pt_attribute);
364 // auto function_invariant = std::make_shared<FunctionInvariant>(mesh->top_simplex_type(),
365 // amips);
366 auto function_invariant =
367 std::make_shared<MaxFunctionInvariant>(mesh->top_simplex_type(), amips);
368
369 auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(*mesh);
370
371 auto todo_larger = std::make_shared<TodoLargerInvariant>(
372 *mesh,
373 edge_length_attribute.as<double>(),
374 target_edge_length_attribute.as<double>(),
375 4.0 / 3.0);
376
377 auto todo_smaller = std::make_shared<TodoSmallerInvariant>(
378 *mesh,
379 edge_length_attribute.as<double>(),
380 target_edge_length_attribute.as<double>(),
381 4.0 / 5.0);
382
383
384 auto interior_edge = std::make_shared<InteriorEdgeInvariant>(*mesh);
385 auto interior_face = std::make_shared<InteriorSimplexInvariant>(*mesh, PrimitiveType::Triangle);
386
387 for (const auto& em : multimesh_meshes) {
388 interior_edge->add_boundary(*(em.first));
389 interior_face->add_boundary(*(em.first));
390 }
391
392 auto valence_3 = std::make_shared<EdgeValenceInvariant>(*mesh, 3);
393 auto valence_4 = std::make_shared<EdgeValenceInvariant>(*mesh, 4);
394 auto swap44_energy_before =
395 std::make_shared<Swap44EnergyBeforeInvariant>(*mesh, pt_attribute.as<Rational>(), 0);
396 auto swap44_2_energy_before =
397 std::make_shared<Swap44EnergyBeforeInvariant>(*mesh, pt_attribute.as<Rational>(), 1);
398 auto swap32_energy_before =
399 std::make_shared<Swap32EnergyBeforeInvariant>(*mesh, pt_attribute.as<Rational>());
400 auto swap23_energy_before =
401 std::make_shared<Swap23EnergyBeforeInvariant>(*mesh, pt_attribute.as<Rational>());
402
403 auto invariant_separate_substructures =
404 std::make_shared<invariants::SeparateSubstructuresInvariant>(*mesh);
405
407 // renew flags
409 auto visited_edge_flag =
410 mesh->register_attribute<char>("visited_edge", PrimitiveType::Edge, 1, false, char(1));
411
412 auto update_flag_func = [](const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
413 assert(P.cols() == 2);
414 assert(P.rows() == 2 || P.rows() == 3);
415 return Eigen::VectorX<char>::Constant(1, char(1));
416 };
417 auto tag_update =
418 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<char, Rational>>(
419 visited_edge_flag,
420 pt_attribute,
421 update_flag_func);
422
424 // sizing field update flags
426 auto visited_vertex_flag =
427 mesh->register_attribute<char>("visited_vertex", PrimitiveType::Vertex, 1, false, char(1));
428 pass_through_attributes.push_back(visited_vertex_flag);
429
431 // energy filter flag
433 auto energy_filter_attribute =
434 mesh->register_attribute<char>("energy_filter", PrimitiveType::Vertex, 1, false, char(1));
435
436 auto energy_filter_accessor = mesh->create_accessor<char>(energy_filter_attribute);
437
438 auto update_energy_filter_func = [](const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
439 // assert(P.cols() == 2);
440 // assert(P.rows() == 2 || P.rows() == 3);
441 return Eigen::VectorX<char>::Constant(1, char(1));
442 };
443 auto energy_filter_update =
444 std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<char, Rational>>(
445 energy_filter_attribute,
446 pt_attribute,
447 update_energy_filter_func);
448
449 pass_through_attributes.push_back(energy_filter_attribute);
450
452 // Creation of the 4 ops
454 std::vector<std::shared_ptr<Operation>> ops;
455 std::vector<std::string> ops_name;
456
458 // 0) Rounding
460 auto rounding_pt_attribute = mesh->get_attribute_handle_typed<Rational>(
461 options.input_mesh_position,
463 auto rounding = std::make_shared<Rounding>(*mesh, rounding_pt_attribute);
464 rounding->add_invariant(
465 std::make_shared<RoundedInvariant>(*mesh, pt_attribute.as<Rational>(), true));
466 rounding->add_invariant(inversion_invariant);
467
468
470 // 1) EdgeSplit
472 auto split = std::make_shared<EdgeSplit>(*mesh);
473 split->set_priority(long_edges_first);
474
475 split->add_invariant(todo_larger);
476 split->add_invariant(inversion_invariant);
477
478 split->set_new_attribute_strategy(pt_attribute);
479 split->set_new_attribute_strategy(sizing_field_scalar_attribute);
480 split->set_new_attribute_strategy(
481 visited_edge_flag,
484 split->set_new_attribute_strategy(
485 target_edge_length_attribute,
488
489 for (const auto& attr : pass_through_attributes) {
490 split->set_new_attribute_strategy(
491 attr,
494 }
495
496 split->add_transfer_strategy(amips_update);
497 split->add_transfer_strategy(edge_length_update);
498 split->add_transfer_strategy(tag_update); // for renew the queue
499 split->add_transfer_strategy(energy_filter_update);
500
501 // split->add_transfer_strategy(target_edge_length_update);
502
503 auto split_then_round = std::make_shared<AndOperationSequence>(*mesh);
504 split_then_round->add_operation(split);
505 split_then_round->add_operation(rounding);
506
507 for (auto& s : update_child_position) {
508 split_then_round->add_transfer_strategy(s);
509 }
510
511 // split unrounded
512 auto split_unrounded = std::make_shared<EdgeSplit>(*mesh);
513 split_unrounded->set_priority(long_edges_first);
514
515 split_unrounded->add_invariant(todo_larger);
516
517 auto split_unrounded_transfer_strategy =
518 std::make_shared<SplitNewAttributeStrategy<Rational>>(pt_attribute);
519 split_unrounded_transfer_strategy->set_strategy(
520 [](const Eigen::VectorX<Rational>& a, const std::bitset<2>&) {
521 return std::array<Eigen::VectorX<Rational>, 2>{{a, a}};
522 });
523 split_unrounded_transfer_strategy->set_rib_strategy(
524 [](const Eigen::VectorX<Rational>& p0_d,
525 const Eigen::VectorX<Rational>& p1_d,
526 const std::bitset<2>& bs) -> Eigen::VectorX<Rational> {
527 Eigen::VectorX<Rational> p0(p0_d.size());
528 Eigen::VectorX<Rational> p1(p1_d.size());
529 for (int i = 0; i < p0_d.size(); ++i) {
530 p0[i] = Rational(p0_d[i], false);
531 p1[i] = Rational(p1_d[i], false);
532 }
533 if (bs[0] == bs[1]) {
534 return (p0 + p1) / Rational(2, false);
535 } else if (bs[0]) {
536 return p0;
537
538 } else {
539 return p1;
540 }
541 });
542
543 split_unrounded->set_new_attribute_strategy(pt_attribute, split_unrounded_transfer_strategy);
544 split_unrounded->set_new_attribute_strategy(sizing_field_scalar_attribute);
545 split_unrounded->set_new_attribute_strategy(
546 visited_edge_flag,
549 for (const auto& attr : pass_through_attributes) {
550 split_unrounded->set_new_attribute_strategy(
551 attr,
554 }
555 split_unrounded->set_new_attribute_strategy(
556 target_edge_length_attribute,
559
560 split_unrounded->add_transfer_strategy(amips_update);
561 split_unrounded->add_transfer_strategy(edge_length_update);
562 split_unrounded->add_transfer_strategy(tag_update); // for renew the queue
563 split_unrounded->add_transfer_strategy(energy_filter_update);
564
565 // split->add_transfer_strategy(target_edge_length_update);
566
567 auto split_sequence = std::make_shared<OrOperationSequence>(*mesh);
568 split_sequence->add_operation(split_then_round);
569 split_sequence->add_operation(split_unrounded);
570 split_sequence->add_invariant(
571 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<char>()));
572
573 split_sequence->set_priority(long_edges_first);
574
575 if (!options.skip_split) {
576 ops.emplace_back(split_sequence);
577 ops_name.emplace_back("SPLIT");
578
579 ops.emplace_back(rounding);
580 ops_name.emplace_back("rounding");
581 }
582
583
585 // collapse transfer
587 auto clps_strat1 = std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
588 // clps_strat1->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
589 // clps_strat1->set_strategy(CollapseBasicStrategy::Default);
590 clps_strat1->set_strategy(CollapseBasicStrategy::CopyOther);
591
592 auto clps_strat2 = std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
593 // clps_strat2->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
594 // clps_strat2->set_strategy(CollapseBasicStrategy::Default);
595 clps_strat2->set_strategy(CollapseBasicStrategy::CopyTuple);
596
598 // 2) EdgeCollapse
600
601 auto setup_collapse = [&](std::shared_ptr<EdgeCollapse>& collapse) {
602 collapse->add_invariant(invariant_separate_substructures);
603 collapse->add_invariant(std::make_shared<MultiMeshMapValidInvariant>(*mesh));
604 collapse->add_invariant(link_condition);
605 collapse->add_invariant(inversion_invariant);
606 // collapse->add_invariant(function_invariant);
607 collapse->add_invariant(envelope_invariant);
608
609 collapse->set_new_attribute_strategy(
610 visited_edge_flag,
612
613 collapse->add_transfer_strategy(tag_update);
614 collapse->add_transfer_strategy(energy_filter_update);
615 for (const auto& attr : pass_through_attributes) {
616 collapse->set_new_attribute_strategy(
617 attr,
619 }
620 collapse->set_new_attribute_strategy(
621 target_edge_length_attribute,
623 // THis triggers a segfault in release
624 // solved somehow
625 // collapse->set_priority(short_edges_first);
626
627 collapse->add_transfer_strategy(amips_update);
628 collapse->add_transfer_strategy(edge_length_update);
629 // collapse->add_transfer_strategy(target_edge_length_update);
630
631 for (auto& s : update_child_position) {
632 collapse->add_transfer_strategy(s);
633 }
634 };
635
636 auto collapse1 = std::make_shared<EdgeCollapse>(*mesh);
637 collapse1->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
638 *mesh,
639 pt_attribute.as<Rational>(),
640 amips_attribute.as<double>(),
641 1));
642
643 collapse1->set_new_attribute_strategy(pt_attribute, clps_strat1);
644 collapse1->set_new_attribute_strategy(
645 sizing_field_scalar_attribute,
646 CollapseBasicStrategy::CopyOther);
647 setup_collapse(collapse1);
648
649 auto collapse2 = std::make_shared<EdgeCollapse>(*mesh);
650 collapse2->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
651 *mesh,
652 pt_attribute.as<Rational>(),
653 amips_attribute.as<double>(),
654 0));
655
656 collapse2->set_new_attribute_strategy(pt_attribute, clps_strat2);
657 collapse2->set_new_attribute_strategy(
658 sizing_field_scalar_attribute,
659 CollapseBasicStrategy::CopyTuple);
660 setup_collapse(collapse2);
661
662 auto collapse = std::make_shared<OrOperationSequence>(*mesh);
663 collapse->add_operation(collapse1);
664 collapse->add_operation(collapse2);
665 collapse->add_invariant(todo_smaller);
666
667 auto collapse_then_round = std::make_shared<AndOperationSequence>(*mesh);
668 collapse_then_round->add_operation(collapse);
669 collapse_then_round->add_operation(rounding);
670
671 collapse_then_round->set_priority(short_edges_first);
672 collapse_then_round->add_invariant(
673 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<char>()));
674
675
676 for (auto& s : update_child_position) {
677 collapse_then_round->add_transfer_strategy(s);
678 }
679
680 if (!options.skip_collapse) {
681 ops.emplace_back(collapse_then_round);
682 ops_name.emplace_back("COLLAPSE");
683
684 ops.emplace_back(rounding);
685 ops_name.emplace_back("rounding");
686 }
687
689 // 3) Swap
691
692 auto swap56 = std::make_shared<MinOperationSequence>(*mesh);
693 for (int i = 0; i < 5; ++i) {
694 auto swap = std::make_shared<TetEdgeSwap>(*mesh, i);
695 swap->collapse().add_invariant(invariant_separate_substructures);
696 swap->collapse().add_invariant(link_condition);
697 swap->collapse().set_new_attribute_strategy(pt_attribute, CollapseBasicStrategy::CopyOther);
698 swap->collapse().set_new_attribute_strategy(
699 sizing_field_scalar_attribute,
700 CollapseBasicStrategy::CopyOther);
701 swap->split().set_new_attribute_strategy(pt_attribute);
702 swap->split().set_new_attribute_strategy(sizing_field_scalar_attribute);
703 // swap->split().add_transfer_strategy(amips_update);
704 // swap->collapse().add_transfer_strategy(amips_update);
705 swap->split().set_new_attribute_strategy(
706 visited_edge_flag,
709 swap->collapse().set_new_attribute_strategy(
710 visited_edge_flag,
712
713 swap->split().set_new_attribute_strategy(
714 target_edge_length_attribute,
717 swap->collapse().set_new_attribute_strategy(
718 target_edge_length_attribute,
720
721 swap->add_invariant(
722 std::make_shared<Swap56EnergyBeforeInvariant>(*mesh, pt_attribute.as<Rational>(), i));
723
724 swap->add_transfer_strategy(amips_update);
725
726 // swap->add_invariant(inversion_invariant);
727 swap->collapse().add_invariant(inversion_invariant);
728
729 // swap->collapse().add_invariant(envelope_invariant);
730
731 for (const auto& attr : pass_through_attributes) {
732 swap->split().set_new_attribute_strategy(
733 attr,
736 swap->collapse().set_new_attribute_strategy(
737 attr,
739 }
740
741 swap56->add_operation(swap);
742 }
743
744 auto swap56_energy_check = [&](int64_t idx, const simplex::Simplex& t) -> double {
745 constexpr static PrimitiveType PV = PrimitiveType::Vertex;
746 constexpr static PrimitiveType PE = PrimitiveType::Edge;
747 constexpr static PrimitiveType PF = PrimitiveType::Triangle;
749
750 auto accessor = mesh->create_const_accessor(pt_attribute.as<Rational>());
751
752 const Tuple e0 = t.tuple();
753 const Tuple e1 = mesh->switch_tuple(e0, PV);
754
755 std::array<Tuple, 5> v;
756 auto iter_tuple = e0;
757 for (int64_t i = 0; i < 5; ++i) {
758 v[i] = mesh->switch_tuples(iter_tuple, {PE, PV});
759 iter_tuple = mesh->switch_tuples(iter_tuple, {PF, PT});
760 }
761 if (iter_tuple != e0) return 0;
762 assert(iter_tuple == e0);
763
764 // five iterable vertices remap to 0-4 by m_collapse_index, 0: m_collapse_index, 5:
765 // e0, 6: e1
766 std::array<Eigen::Vector3<Rational>, 7> positions = {
767 {accessor.const_vector_attribute(v[(idx + 0) % 5]),
768 accessor.const_vector_attribute(v[(idx + 1) % 5]),
769 accessor.const_vector_attribute(v[(idx + 2) % 5]),
770 accessor.const_vector_attribute(v[(idx + 3) % 5]),
771 accessor.const_vector_attribute(v[(idx + 4) % 5]),
772 accessor.const_vector_attribute(e0),
773 accessor.const_vector_attribute(e1)}};
774
775 std::array<Eigen::Vector3d, 7> positions_double = {
776 {positions[0].cast<double>(),
777 positions[1].cast<double>(),
778 positions[2].cast<double>(),
779 positions[3].cast<double>(),
780 positions[4].cast<double>(),
781 positions[5].cast<double>(),
782 positions[6].cast<double>()}};
783
784 std::array<std::array<int, 4>, 6> new_tets = {
785 {{{0, 1, 2, 5}},
786 {{0, 2, 3, 5}},
787 {{0, 3, 4, 5}},
788 {{0, 1, 2, 6}},
789 {{0, 2, 3, 6}},
790 {{0, 3, 4, 6}}}};
791
792 double new_energy_max = std::numeric_limits<double>::lowest();
793
794 for (int i = 0; i < 6; ++i) {
796 positions[new_tets[i][0]],
797 positions[new_tets[i][1]],
798 positions[new_tets[i][2]],
799 positions[new_tets[i][3]]) > 0) {
801 positions_double[new_tets[i][0]][0],
802 positions_double[new_tets[i][0]][1],
803 positions_double[new_tets[i][0]][2],
804 positions_double[new_tets[i][1]][0],
805 positions_double[new_tets[i][1]][1],
806 positions_double[new_tets[i][1]][2],
807 positions_double[new_tets[i][2]][0],
808 positions_double[new_tets[i][2]][1],
809 positions_double[new_tets[i][2]][2],
810 positions_double[new_tets[i][3]][0],
811 positions_double[new_tets[i][3]][1],
812 positions_double[new_tets[i][3]][2],
813 }});
814
815
816 if (energy > new_energy_max) new_energy_max = energy;
817 } else {
819 positions_double[new_tets[i][1]][0],
820 positions_double[new_tets[i][1]][1],
821 positions_double[new_tets[i][1]][2],
822 positions_double[new_tets[i][0]][0],
823 positions_double[new_tets[i][0]][1],
824 positions_double[new_tets[i][0]][2],
825 positions_double[new_tets[i][2]][0],
826 positions_double[new_tets[i][2]][1],
827 positions_double[new_tets[i][2]][2],
828 positions_double[new_tets[i][3]][0],
829 positions_double[new_tets[i][3]][1],
830 positions_double[new_tets[i][3]][2],
831 }});
832
833
834 if (energy > new_energy_max) new_energy_max = energy;
835 }
836 }
837
838 return new_energy_max;
839 };
840
841 swap56->set_value_function(swap56_energy_check);
842 swap56->add_invariant(std::make_shared<EdgeValenceInvariant>(*mesh, 5));
843
844 // swap44
845
846 auto swap44 = std::make_shared<MinOperationSequence>(*mesh);
847 for (int i = 0; i < 2; ++i) {
848 auto swap = std::make_shared<TetEdgeSwap>(*mesh, i);
849 swap->collapse().add_invariant(invariant_separate_substructures);
850 swap->collapse().add_invariant(link_condition);
851 swap->collapse().set_new_attribute_strategy(pt_attribute, CollapseBasicStrategy::CopyOther);
852 swap->collapse().set_new_attribute_strategy(
853 sizing_field_scalar_attribute,
854 CollapseBasicStrategy::CopyOther);
855 swap->split().set_new_attribute_strategy(pt_attribute);
856 swap->split().set_new_attribute_strategy(sizing_field_scalar_attribute);
857 swap->split().set_new_attribute_strategy(
858 visited_edge_flag,
861 swap->collapse().set_new_attribute_strategy(
862 visited_edge_flag,
864
865 // swap->split().add_transfer_strategy(amips_update);
866 // swap->collapse().add_transfer_strategy(amips_update);
867
868 swap->split().set_new_attribute_strategy(
869 target_edge_length_attribute,
872 swap->collapse().set_new_attribute_strategy(
873 target_edge_length_attribute,
875
876 swap->add_transfer_strategy(amips_update);
877
878 swap->add_invariant(
879 std::make_shared<Swap44EnergyBeforeInvariant>(*mesh, pt_attribute.as<Rational>(), i));
880
881 // swap->add_invariant(inversion_invariant);
882 swap->collapse().add_invariant(inversion_invariant);
883
884 // swap->collapse().add_invariant(envelope_invariant);
885
886 for (const auto& attr : pass_through_attributes) {
887 swap->split().set_new_attribute_strategy(
888 attr,
891 swap->collapse().set_new_attribute_strategy(
892 attr,
894 }
895
896 swap44->add_operation(swap);
897 }
898
899 auto swap44_energy_check = [&](int64_t idx, const simplex::Simplex& t) -> double {
900 constexpr static PrimitiveType PV = PrimitiveType::Vertex;
901 constexpr static PrimitiveType PE = PrimitiveType::Edge;
902 constexpr static PrimitiveType PF = PrimitiveType::Triangle;
904
905 auto accessor = mesh->create_const_accessor(pt_attribute.as<Rational>());
906
907 // get the coords of the vertices
908 // input edge end points
909 const Tuple e0 = t.tuple();
910 const Tuple e1 = mesh->switch_tuple(e0, PV);
911 // other four vertices
912 std::array<Tuple, 4> v;
913 auto iter_tuple = e0;
914 for (int64_t i = 0; i < 4; ++i) {
915 v[i] = mesh->switch_tuples(iter_tuple, {PE, PV});
916 iter_tuple = mesh->switch_tuples(iter_tuple, {PF, PT});
917 }
918
919 if (iter_tuple != e0) return 0;
920 assert(iter_tuple == e0);
921
922 std::array<Eigen::Vector3<Rational>, 6> positions = {
923 {accessor.const_vector_attribute(v[(idx + 0) % 4]),
924 accessor.const_vector_attribute(v[(idx + 1) % 4]),
925 accessor.const_vector_attribute(v[(idx + 2) % 4]),
926 accessor.const_vector_attribute(v[(idx + 3) % 4]),
927 accessor.const_vector_attribute(e0),
928 accessor.const_vector_attribute(e1)}};
929 std::array<Eigen::Vector3d, 6> positions_double = {
930 {positions[0].cast<double>(),
931 positions[1].cast<double>(),
932 positions[2].cast<double>(),
933 positions[3].cast<double>(),
934 positions[4].cast<double>(),
935 positions[5].cast<double>()}};
936
937 std::array<std::array<int, 4>, 4> new_tets = {
938 {{{0, 1, 2, 4}}, {{0, 2, 3, 4}}, {{0, 1, 2, 5}}, {{0, 2, 3, 5}}}};
939
940 double new_energy_max = std::numeric_limits<double>::lowest();
941
942 for (int i = 0; i < 4; ++i) {
944 positions[new_tets[i][0]],
945 positions[new_tets[i][1]],
946 positions[new_tets[i][2]],
947 positions[new_tets[i][3]]) > 0) {
949 positions_double[new_tets[i][0]][0],
950 positions_double[new_tets[i][0]][1],
951 positions_double[new_tets[i][0]][2],
952 positions_double[new_tets[i][1]][0],
953 positions_double[new_tets[i][1]][1],
954 positions_double[new_tets[i][1]][2],
955 positions_double[new_tets[i][2]][0],
956 positions_double[new_tets[i][2]][1],
957 positions_double[new_tets[i][2]][2],
958 positions_double[new_tets[i][3]][0],
959 positions_double[new_tets[i][3]][1],
960 positions_double[new_tets[i][3]][2],
961 }});
962
963 if (energy > new_energy_max) new_energy_max = energy;
964 } else {
966 positions_double[new_tets[i][1]][0],
967 positions_double[new_tets[i][1]][1],
968 positions_double[new_tets[i][1]][2],
969 positions_double[new_tets[i][0]][0],
970 positions_double[new_tets[i][0]][1],
971 positions_double[new_tets[i][0]][2],
972 positions_double[new_tets[i][2]][0],
973 positions_double[new_tets[i][2]][1],
974 positions_double[new_tets[i][2]][2],
975 positions_double[new_tets[i][3]][0],
976 positions_double[new_tets[i][3]][1],
977 positions_double[new_tets[i][3]][2],
978 }});
979
980 if (energy > new_energy_max) new_energy_max = energy;
981 }
982 }
983
984 return new_energy_max;
985 };
986
987 swap44->set_value_function(swap44_energy_check);
988 swap44->add_invariant(std::make_shared<EdgeValenceInvariant>(*mesh, 4));
989
990 // swap 32
991 auto swap32 = std::make_shared<TetEdgeSwap>(*mesh, 0);
992 swap32->add_invariant(std::make_shared<EdgeValenceInvariant>(*mesh, 3));
993 swap32->add_invariant(
994 std::make_shared<Swap32EnergyBeforeInvariant>(*mesh, pt_attribute.as<Rational>()));
995
996 swap32->collapse().add_invariant(invariant_separate_substructures);
997 swap32->collapse().add_invariant(link_condition);
998 swap32->collapse().set_new_attribute_strategy(pt_attribute, CollapseBasicStrategy::CopyOther);
999 swap32->collapse().set_new_attribute_strategy(
1000 sizing_field_scalar_attribute,
1001 CollapseBasicStrategy::CopyOther);
1002 // swap32->add_invariant(inversion_invariant);
1003 swap32->split().set_new_attribute_strategy(pt_attribute);
1004 swap32->split().set_new_attribute_strategy(sizing_field_scalar_attribute);
1005 swap32->split().set_new_attribute_strategy(
1006 visited_edge_flag,
1009 swap32->collapse().set_new_attribute_strategy(
1010 visited_edge_flag,
1012
1013 // swap32->split().add_transfer_strategy(amips_update);
1014 // swap32->collapse().add_transfer_strategy(amips_update);
1015
1016 swap32->split().set_new_attribute_strategy(
1017 target_edge_length_attribute,
1020 swap32->collapse().set_new_attribute_strategy(
1021 target_edge_length_attribute,
1023
1024 swap32->add_transfer_strategy(amips_update);
1025
1026 // hack
1027 swap32->collapse().add_invariant(inversion_invariant);
1028 // swap32->collapse().add_invariant(envelope_invariant);
1029
1030 for (const auto& attr : pass_through_attributes) {
1031 swap32->split().set_new_attribute_strategy(
1032 attr,
1035 swap32->collapse().set_new_attribute_strategy(
1036 attr,
1038 }
1039
1040 // all swaps
1041
1042 auto swap_all = std::make_shared<OrOperationSequence>(*mesh);
1043 swap_all->add_operation(swap32);
1044 swap_all->add_operation(swap44);
1045 swap_all->add_operation(swap56);
1046 swap_all->add_transfer_strategy(tag_update);
1047 swap_all->add_transfer_strategy(energy_filter_update);
1048
1049 auto swap_then_round = std::make_shared<AndOperationSequence>(*mesh);
1050 swap_then_round->add_operation(swap_all);
1051 swap_then_round->add_operation(rounding);
1052 swap_then_round->add_invariant(
1053 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<char>()));
1054 swap_then_round->add_invariant(std::make_shared<InteriorEdgeInvariant>(*mesh));
1055 swap_then_round->add_invariant(std::make_shared<NoChildMeshAttachingInvariant>(*mesh));
1056
1057 swap_then_round->set_priority(long_edges_first);
1058
1059
1060 // swap_then_round->add_invariant(inversion_invariant);
1061 for (auto& s : update_child_position) {
1062 swap_then_round->add_transfer_strategy(s);
1063 }
1064
1065 if (!options.skip_swap) {
1066 ops.push_back(swap_then_round);
1067 ops_name.push_back("EDGE SWAP");
1068 ops.emplace_back(rounding);
1069 ops_name.emplace_back("rounding");
1070 }
1071
1072 // 4) Smoothing
1073 // //////////////////////////////////////
1074 // // special smoothing on surface
1075 // //////////////////////////////////////
1076
1077 // if (mesh->top_simplex_type() == PrimitiveType::Tetrahedron) {
1078 // for (auto& pair : mesh_constraint_pairs) {
1079 // if (pair.second.mesh().top_simplex_type() != PrimitiveType::Triangle) continue;
1080
1081 // auto& child_mesh = pair.second.mesh();
1082 // auto& child_position_handle = pair.second;
1083
1084 // auto lap_smoothing = std::make_shared<TetWildTangentialLaplacianSmoothing>(
1085 // child_mesh,
1086 // child_position_handle.as<Rational>());
1087 // lap_smoothing->add_invariant(std::make_shared<RoundedInvariant>(
1088 // child_mesh,
1089 // child_position_handle.as<Rational>()));
1090 // lap_smoothing->add_invariant(inversion_invariant);
1091
1092 // auto proj_lap_smoothing =
1093 // std::make_shared<ProjectOperation>(lap_smoothing, mesh_constraint_pairs);
1094 // proj_lap_smoothing->use_random_priority() = true;
1095
1096 // proj_lap_smoothing->add_invariant(envelope_invariant);
1097 // proj_lap_smoothing->add_invariant(inversion_invariant);
1098 // proj_lap_smoothing->add_invariant(
1099 // std::make_shared<EnergyFilterInvariant>(*mesh,
1100 // energy_filter_attribute.as<char>()));
1101
1102 // proj_lap_smoothing->add_transfer_strategy(amips_update);
1103 // proj_lap_smoothing->add_transfer_strategy(edge_length_update);
1104 // for (auto& s : update_parent_position) { // TODO::this should from only one child
1105 // proj_lap_smoothing->add_transfer_strategy(s);
1106 // }
1107 // for (auto& s : update_child_position) {
1108 // proj_lap_smoothing->add_transfer_strategy(s);
1109 // }
1110
1111 // ops.push_back(proj_lap_smoothing);
1112 // ops_name.push_back("LAPLACIAN SMOOTHING");
1113 // }
1114 // }
1115
1116 // auto energy =
1117 // std::make_shared<function::LocalNeighborsSumFunction>(*mesh, pt_attribute, *amips);
1118 // auto smoothing = std::make_shared<OptimizationSmoothing>(energy);
1119 auto smoothing = std::make_shared<AMIPSOptimizationSmoothing>(*mesh, pt_attribute);
1120 smoothing->add_invariant(
1121 std::make_shared<RoundedInvariant>(*mesh, pt_attribute.as<Rational>()));
1122 smoothing->add_invariant(inversion_invariant);
1123 for (auto& s : update_child_position) {
1124 smoothing->add_transfer_strategy(s);
1125 }
1126
1127 // test code
1128 // smoothing->add_invariant(envelope_invariant);
1129 // smoothing->add_transfer_strategy(amips_update);
1130 // smoothing->add_transfer_strategy(edge_length_update);
1131 // ops.push_back(smoothing);
1132 // ops_name.push_back("SMOOTHING");
1133 // ops.emplace_back(rounding);
1134 // ops_name.emplace_back("rounding");
1135 //--------
1136
1137 auto proj_smoothing = std::make_shared<ProjectOperation>(smoothing, mesh_constraint_pairs);
1138 proj_smoothing->use_random_priority() = true;
1139
1140 proj_smoothing->add_invariant(envelope_invariant);
1141 proj_smoothing->add_invariant(inversion_invariant);
1142 proj_smoothing->add_invariant(
1143 std::make_shared<EnergyFilterInvariant>(*mesh, energy_filter_attribute.as<char>()));
1144
1145 proj_smoothing->add_transfer_strategy(amips_update);
1146 proj_smoothing->add_transfer_strategy(edge_length_update);
1147 for (auto& s : update_parent_position) {
1148 proj_smoothing->add_transfer_strategy(s);
1149 }
1150
1151 for (auto& s : update_child_position) {
1152 proj_smoothing->add_transfer_strategy(s);
1153 }
1154 // proj_smoothing->add_transfer_strategy(target_edge_length_update);
1155
1156 if (!options.skip_smooth) {
1157 for (int i = 0; i < 1; ++i) {
1158 // some old code to do smoothing several times, maybe useful later
1159 ops.push_back(proj_smoothing);
1160 ops_name.push_back("SMOOTHING");
1161 }
1162
1163 ops.emplace_back(rounding);
1164 ops_name.emplace_back("rounding");
1165 }
1166
1167 write(
1168 mesh,
1171 options.input_mesh_position,
1172 0,
1173 options.intermediate_output);
1174
1176 // Running all ops in order n times
1177 Scheduler scheduler;
1178 {
1179 const size_t freq = options.scheduler_update_frequency;
1180 scheduler.set_update_frequency(freq == 0 ? std::optional<size_t>{} : freq);
1181 }
1182 int64_t success = 10;
1183
1185 // preprocessing
1187
1188 SchedulerStats pre_stats;
1189
1190 logger().info("----------------------- Preprocess Collapse -----------------------");
1191
1192 // TODO: remove for performance
1193 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1194 const auto vertices = mesh->orient_vertices(t);
1195 std::vector<Vector3r> pos;
1196 for (int i = 0; i < vertices.size(); ++i) {
1197 pos.push_back(pt_accessor.const_vector_attribute(vertices[i]));
1198 }
1199 if (wmtk::utils::wmtk_orient3d(pos[0], pos[1], pos[2], pos[3]) <= 0) {
1200 wmtk::logger().error("Flipped tet!");
1201 }
1202 }
1203 logger().info("Executing collapse ...");
1204
1205
1206 wmtk::attribute::TypedAttributeHandle<char> visited_edge_flag_t = visited_edge_flag.as<char>();
1207
1208 pre_stats = scheduler.run_operation_on_all(*collapse_then_round, visited_edge_flag_t);
1209 logger().info(
1210 "Executed {}, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, "
1211 "executing: {}",
1212 "preprocessing collapse",
1215 pre_stats.number_of_failed_operations(),
1216 pre_stats.collecting_time,
1217 pre_stats.sorting_time,
1218 pre_stats.executing_time);
1219
1220 success = pre_stats.number_of_successful_operations();
1221
1222 // verbose logger, can be removed
1223 int64_t unrounded = 0;
1224 for (const auto& v : mesh->get_all(PrimitiveType::Vertex)) {
1225 const auto p = pt_accessor.vector_attribute(v);
1226 for (int64_t d = 0; d < 3; ++d) {
1227 if (!p[d].is_rounded()) {
1228 ++unrounded;
1229 break;
1230 }
1231 }
1232 }
1233
1234 logger().info("Mesh has {} unrounded vertices", unrounded);
1235
1236 // compute max energy
1237 double max_energy = std::numeric_limits<double>::lowest();
1238 double min_energy = std::numeric_limits<double>::max();
1239 double avg_energy = 0;
1240 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1241 // double e = amips->get_value(simplex::Simplex(mesh->top_simplex_type(), t));
1242 double e = amips_accessor.scalar_attribute(t);
1243 max_energy = std::max(max_energy, e);
1244 min_energy = std::min(min_energy, e);
1245 avg_energy += e;
1246 }
1247
1248 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1249
1250 logger().info(
1251 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1252 max_energy,
1253 min_energy,
1254 avg_energy);
1255
1256 // std::ofstream file0("quality_plot_pre.csv");
1257 // file0 << "tid, quality" << std::endl;
1258 // int64_t t_cnt = 0;
1259 // for (const auto& t : mesh->get_all(PrimitiveType::Tetrahedron)) {
1260 // t_cnt++;
1261 // file0 << t_cnt << ", " << amips_accessor.scalar_attribute(t) << std::endl;
1262 // }
1263
1264
1265 double old_max_energy = max_energy;
1266 double old_avg_energy = avg_energy;
1267 int iii = 0;
1268 bool is_double = false;
1269 for (int64_t i = 0; i < options.max_passes; ++i) {
1270 logger().info("--------------------------- Pass {} ---------------------------", i);
1271
1272 SchedulerStats pass_stats;
1273 int jj = 0;
1274 for (auto& op : ops) {
1275 // TODO: remove for performance
1276 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1277 const auto vertices = mesh->orient_vertices(t);
1278 std::vector<Vector3r> pos;
1279 for (int i = 0; i < vertices.size(); ++i) {
1280 pos.push_back(pt_accessor.const_vector_attribute(vertices[i]));
1281 }
1282 if (wmtk::utils::wmtk_orient3d(pos[0], pos[1], pos[2], pos[3]) <= 0) {
1283 wmtk::logger().error("Flipped tet!");
1284 }
1285 }
1286 logger().info("Executing {} ...", ops_name[jj]);
1287 SchedulerStats stats;
1288 if (op->primitive_type() == PrimitiveType::Edge) {
1289 stats = scheduler.run_operation_on_all(*op, visited_edge_flag_t);
1290 // } else if (ops_name[jj] == "SMOOTHING") {
1291 // // stats = scheduler.run_operation_on_all(*op);
1292 // stats =
1293 // scheduler.run_operation_on_all_coloring(*op,
1294 // coloring_attribute.as<int64_t>());
1295 } else {
1296 stats = scheduler.run_operation_on_all(*op);
1297 }
1298 pass_stats += stats;
1299 logger().info(
1300 "Executed {}, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, "
1301 "executing: {}",
1302 ops_name[jj],
1306 stats.collecting_time,
1307 stats.sorting_time,
1308 stats.executing_time);
1309
1310 success = stats.number_of_successful_operations();
1311
1312 // verbose logger, can be removed
1313 int64_t unrounded = 0;
1314 for (const auto& v : mesh->get_all(PrimitiveType::Vertex)) {
1315 const auto p = pt_accessor.vector_attribute(v);
1316 for (int64_t d = 0; d < 3; ++d) {
1317 if (!p[d].is_rounded()) {
1318 ++unrounded;
1319 break;
1320 }
1321 }
1322 }
1323
1324 logger().info("Mesh has {} unrounded vertices", unrounded);
1325
1326 avg_energy = 0;
1327
1328 // compute max energy
1329 max_energy = std::numeric_limits<double>::lowest();
1330 min_energy = std::numeric_limits<double>::max();
1331 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1332 // double e = amips->get_value(simplex::Simplex(mesh->top_simplex_type(), t));
1333 double e = amips_accessor.scalar_attribute(t);
1334 max_energy = std::max(max_energy, e);
1335 min_energy = std::min(min_energy, e);
1336 avg_energy += e;
1337 }
1338
1339 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1340
1341 logger().info(
1342 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1343 max_energy,
1344 min_energy,
1345 avg_energy);
1346
1347
1348 ++jj;
1349 }
1350
1351 logger().info(
1352 "Executed {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, executing: {}",
1353 pass_stats.number_of_performed_operations(),
1355 pass_stats.number_of_failed_operations(),
1356 pass_stats.collecting_time,
1357 pass_stats.sorting_time,
1358 pass_stats.executing_time);
1359
1361
1362 write(
1363 mesh,
1366 options.input_mesh_position,
1367 i + 1,
1368 options.intermediate_output);
1369
1370 assert(mesh->is_connectivity_valid());
1371
1372 // compute max energy
1373 max_energy = std::numeric_limits<double>::lowest();
1374 min_energy = std::numeric_limits<double>::max();
1375 avg_energy = 0;
1376 for (const auto& t : mesh->get_all(mesh->top_simplex_type())) {
1377 // double e = amips->get_value(simplex::Simplex(mesh->top_simplex_type(), t));
1378 double e = amips_accessor.scalar_attribute(t);
1379 max_energy = std::max(max_energy, e);
1380 min_energy = std::min(min_energy, e);
1381 avg_energy += e;
1382 }
1383
1384 avg_energy = avg_energy / mesh->get_all(mesh->top_simplex_type()).size();
1385
1386 int64_t unrounded = 0;
1387 if (!is_double) {
1388 bool rational = false;
1389 for (const auto& v : mesh->get_all(PrimitiveType::Vertex)) {
1390 const auto p = pt_accessor.vector_attribute(v);
1391 for (int64_t d = 0; d < bmax.size(); ++d) {
1392 if (!p[d].is_rounded()) {
1393 rational = true;
1394 ++unrounded;
1395 break;
1396 }
1397 }
1398 }
1399
1400 is_double = !rational;
1401 }
1402
1403 logger().info("Mesh has {} unrounded vertices", unrounded);
1404 logger().info(
1405 "Max AMIPS Energy: {}, Min AMIPS Energy: {}, Avg AMIPS Energy: {}",
1406 max_energy,
1407 min_energy,
1408 avg_energy);
1409
1410
1411 // adjust sizing field
1412 if (i > 0 && old_max_energy - max_energy < 5e-1 &&
1413 (old_avg_energy - avg_energy) / avg_energy < 0.1) {
1414 wmtk::logger().info("adjusting sizing field ...");
1415
1417 *mesh,
1418 pt_attribute.as<Rational>(),
1419 edge_length_attribute.as<double>(),
1420 sizing_field_scalar_attribute.as<double>(),
1421 amips_attribute.as<double>(),
1422 target_edge_length_attribute.as<double>(),
1423 visited_vertex_flag.as<char>(),
1424 target_max_amips,
1425 max_energy,
1426 target_edge_length,
1427 min_edge_length);
1428
1429 wmtk::logger().info("adjusting sizing field finished");
1430
1431 // wmtk::logger().info("setting energy filter ...");
1432 // set_operation_energy_filter_after_sizing_field(
1433 // *mesh,
1434 // pt_attribute.as<Rational>(),
1435 // amips_attribute.as<double>(),
1436 // energy_filter_attribute.as<char>(),
1437 // visited_vertex_flag.as<char>(),
1438 // target_max_amips,
1439 // max_energy,
1440 // target_edge_length);
1441 // wmtk::logger().info("setting energy filter finished");
1442
1443 // int64_t e_cnt = 0;
1444 // for (const auto& e : mesh->get_all(PrimitiveType::Edge)) {
1445 // if (energy_filter_accessor.scalar_attribute(e) == char(1) ||
1446 // energy_filter_accessor.scalar_attribute(
1447 // mesh->switch_tuple(e, PrimitiveType::Vertex)) == char(1)) {
1448 // e_cnt++;
1449 // }
1450 // }
1451 // wmtk::logger().info(
1452 // "{} edges are going to be executed out of {}",
1453 // e_cnt,
1454 // mesh->get_all(PrimitiveType::Edge).size());
1455
1456 for (const auto& v : mesh->get_all(PrimitiveType::Vertex)) {
1457 energy_filter_accessor.scalar_attribute(v) = char(1);
1458 }
1459 wmtk::logger().info("reset energy filter");
1460 } else {
1461 wmtk::logger().info("setting energy filter ...");
1463 *mesh,
1464 pt_attribute.as<Rational>(),
1465 amips_attribute.as<double>(),
1466 energy_filter_attribute.as<char>(),
1467 visited_vertex_flag.as<char>(),
1468 target_max_amips,
1469 max_energy,
1470 target_edge_length);
1471 wmtk::logger().info("setting energy filter finished");
1472
1473 int64_t e_cnt = 0;
1474 for (const auto& e : mesh->get_all(PrimitiveType::Edge)) {
1475 if (energy_filter_accessor.scalar_attribute(e) == char(1) ||
1476 energy_filter_accessor.scalar_attribute(
1477 mesh->switch_tuple(e, PrimitiveType::Vertex)) == char(1)) {
1478 e_cnt++;
1479 }
1480 }
1481 wmtk::logger().info(
1482 "{} edges are going to be executed out of {}",
1483 e_cnt,
1484 mesh->get_all(PrimitiveType::Edge).size());
1485 }
1486
1487 old_max_energy = max_energy;
1488 old_avg_energy = avg_energy;
1489
1490 // stop at good quality
1491 if (max_energy <= target_max_amips && is_double) break;
1492 }
1493
1494 std::vector<std::pair<std::shared_ptr<Mesh>, std::string>> all_meshes;
1495 all_meshes.push_back(std::make_pair(mesh, "main"));
1496
1497 for (const auto& p : multimesh_meshes) {
1498 all_meshes.push_back(p);
1499 }
1500
1501 return all_meshes;
1502}
1503
1504
1505// internal functions
1506
1508 Mesh& m,
1509 const TypedAttributeHandle<Rational>& coordinate_handle,
1510 const TypedAttributeHandle<double>& edge_length_handle,
1511 const TypedAttributeHandle<double>& sizing_field_scalar_handle,
1512 const TypedAttributeHandle<double>& energy_handle,
1513 const TypedAttributeHandle<double>& target_edge_length_handle,
1514 const TypedAttributeHandle<char>& visited_handle,
1515 const double stop_energy,
1516 const double current_max_energy,
1517 const double initial_target_edge_length,
1518 const double min_target_edge_length)
1519{
1521
1522 std::cout << "in here" << std::endl;
1523
1524 const auto coordinate_accessor = m.create_const_accessor<Rational>(coordinate_handle);
1525 const auto edge_length_accessor = m.create_const_accessor<double>(edge_length_handle);
1526 const auto energy_accessor = m.create_const_accessor<double>(energy_handle);
1527
1528 auto sizing_field_scalar_accessor = m.create_accessor<double>(sizing_field_scalar_handle);
1529 auto target_edge_length_accessor = m.create_accessor<double>(target_edge_length_handle);
1530 auto visited_accessor = m.create_accessor<char>(visited_handle);
1531
1532 const double stop_filter_energy = stop_energy * 0.8;
1533 double filter_energy = std::max(current_max_energy / 100, stop_filter_energy);
1534 filter_energy = std::min(filter_energy, 100.0);
1535
1536 const double recover_scalar = 1.5;
1537 const double refine_scalar = 0.5;
1538 const double min_refine_scalar = min_target_edge_length / initial_target_edge_length;
1539
1540 auto vertices_all = m.get_all(PrimitiveType::Vertex);
1541 int64_t v_cnt = vertices_all.size();
1542
1543 std::vector<Vector3d> centroids;
1544 std::queue<Tuple> v_queue;
1545 // std::vector<double> scale_multipliers(v_cnt, recover_scalar);
1546
1547 // get centroids and initial v_queue
1548 for (const auto& t : m.get_all(PrimitiveType::Tetrahedron)) {
1549 // if (std::cbrt(energy_accessor.const_scalar_attribute(t)) < filter_energy) {
1550 if (energy_accessor.const_scalar_attribute(t) < filter_energy) {
1551 // skip good tets
1552 continue;
1553 }
1554 auto vertices = m.orient_vertices(t);
1555 Vector3d c(0, 0, 0);
1556 for (int i = 0; i < 4; ++i) {
1557 c += coordinate_accessor.const_vector_attribute(vertices[i]).cast<double>();
1558 v_queue.emplace(vertices[i]);
1559 }
1560 centroids.emplace_back(c / 4.0);
1561 }
1562
1563 wmtk::logger().info(
1564 "filter energy: {}, low quality tets num: {}",
1565 filter_energy,
1566 centroids.size());
1567
1568 const double R = initial_target_edge_length * 1.8; // update field radius
1569
1570 for (const auto& v : vertices_all) { // reset visited flag
1571 visited_accessor.scalar_attribute(v) = char(0);
1572 }
1573
1574 // TODO: use efficient data structure
1575 auto get_nearest_dist = [&](const Tuple& v) -> double {
1576 Tuple nearest_tuple;
1577 double min_dist = std::numeric_limits<double>::max();
1578 const Vector3d v_pos = coordinate_accessor.const_vector_attribute(v).cast<double>();
1579 for (const auto& c_pos : centroids) {
1580 double dist = (c_pos - v_pos).norm();
1581 min_dist = std::min(min_dist, dist);
1582 }
1583 return min_dist;
1584 };
1585
1586 while (!v_queue.empty()) {
1587 auto v = v_queue.front();
1588 v_queue.pop();
1589
1590 if (visited_accessor.scalar_attribute(v) == char(1)) continue;
1591 visited_accessor.scalar_attribute(v) = char(1);
1592
1593 double dist = std::max(0., get_nearest_dist(v));
1594
1595 if (dist > R) {
1596 visited_accessor.scalar_attribute(v) = char(0);
1597 continue;
1598 }
1599
1600 double scale_multiplier = std::min(
1601 recover_scalar,
1602 dist / R * (1 - refine_scalar) + refine_scalar); // linear interpolate
1603
1604 auto new_scale = sizing_field_scalar_accessor.scalar_attribute(v) * scale_multiplier;
1605 if (new_scale > 1) {
1606 sizing_field_scalar_accessor.scalar_attribute(v) = 1;
1607 } else if (new_scale < min_refine_scalar) {
1608 sizing_field_scalar_accessor.scalar_attribute(v) = min_refine_scalar;
1609 } else {
1610 sizing_field_scalar_accessor.scalar_attribute(v) = new_scale;
1611 }
1612
1613 // push one ring vertices into the queue
1614 for (const auto& v_one_ring : simplex::link(m, simplex::Simplex::vertex(m, v))
1616 if (visited_accessor.scalar_attribute(v_one_ring) == char(1)) continue;
1617 v_queue.push(v_one_ring.tuple());
1618 }
1619 }
1620
1621 // update the rest
1622 for (const auto& v : vertices_all) {
1623 if (visited_accessor.scalar_attribute(v) == char(1)) continue;
1624 auto new_scale = sizing_field_scalar_accessor.scalar_attribute(v) * 1.5;
1625 if (new_scale > 1) {
1626 sizing_field_scalar_accessor.scalar_attribute(v) = 1;
1627 } else if (new_scale < min_refine_scalar) {
1628 sizing_field_scalar_accessor.scalar_attribute(v) = min_refine_scalar;
1629 } else {
1630 sizing_field_scalar_accessor.scalar_attribute(v) = new_scale;
1631 }
1632 }
1633
1634 // update target edge length
1635 for (const auto& e : m.get_all(PrimitiveType::Edge)) {
1636 target_edge_length_accessor.scalar_attribute(e) =
1637 initial_target_edge_length *
1638 (sizing_field_scalar_accessor.scalar_attribute(e) +
1639 sizing_field_scalar_accessor.scalar_attribute(
1641 2;
1642 }
1643}
1644
1646 Mesh& m,
1647 const TypedAttributeHandle<Rational>& coordinate_handle,
1648 const TypedAttributeHandle<double>& energy_handle,
1649 const TypedAttributeHandle<char>& energy_filter_handle,
1650 const TypedAttributeHandle<char>& visited_handle,
1651 const double stop_energy,
1652 const double current_max_energy,
1653 const double initial_target_edge_length)
1654{
1655 // two ring version
1657
1658 const auto coordinate_accessor = m.create_const_accessor<Rational>(coordinate_handle);
1659 const auto energy_accessor = m.create_const_accessor<double>(energy_handle);
1660
1661 auto energy_filter_accessor = m.create_accessor<char>(energy_filter_handle);
1662 auto visited_accessor = m.create_accessor<char>(visited_handle);
1663
1664 const double stop_filter_energy = stop_energy * 0.8;
1665 double filter_energy = std::max(current_max_energy / 100, stop_filter_energy);
1666 filter_energy = std::min(filter_energy, 100.0);
1667
1668 auto vertices_all = m.get_all(PrimitiveType::Vertex);
1669
1670 for (const auto& v : vertices_all) { // reset visited flag
1671 visited_accessor.scalar_attribute(v) = char(0);
1672 energy_filter_accessor.scalar_attribute(v) = char(0);
1673 }
1674
1675 // get centroids and initial v_queue
1676 for (const auto& t : m.get_all(PrimitiveType::Tetrahedron)) {
1677 if (energy_accessor.const_scalar_attribute(t) < filter_energy) {
1678 // skip good tets
1679 continue;
1680 }
1681 auto vertices = m.orient_vertices(t);
1682 for (const auto& v : vertices) {
1683 energy_filter_accessor.scalar_attribute(v) = char(1);
1684 for (const auto& vv : simplex::k_ring(m, simplex::Simplex::vertex(m, v), 1)
1686 energy_filter_accessor.scalar_attribute(vv) = char(1);
1687 }
1688 }
1689 }
1690}
1691
1693 Mesh& m,
1694 const TypedAttributeHandle<Rational>& coordinate_handle,
1695 const TypedAttributeHandle<double>& energy_handle,
1696 const TypedAttributeHandle<char>& energy_filter_handle,
1697 const TypedAttributeHandle<char>& visited_handle,
1698 const double stop_energy,
1699 const double current_max_energy,
1700 const double initial_target_edge_length)
1701{
1703
1704 const auto coordinate_accessor = m.create_const_accessor<Rational>(coordinate_handle);
1705 const auto energy_accessor = m.create_const_accessor<double>(energy_handle);
1706
1707 auto energy_filter_accessor = m.create_accessor<char>(energy_filter_handle);
1708 auto visited_accessor = m.create_accessor<char>(visited_handle);
1709
1710 const double stop_filter_energy = stop_energy * 0.8;
1711 double filter_energy = std::max(current_max_energy / 100, stop_filter_energy);
1712 filter_energy = std::min(filter_energy, 100.0);
1713
1714 auto vertices_all = m.get_all(PrimitiveType::Vertex);
1715 std::vector<Vector3d> centroids;
1716 std::queue<Tuple> v_queue;
1717
1718 // get centroids and initial v_queue
1719 for (const auto& t : m.get_all(PrimitiveType::Tetrahedron)) {
1720 if (energy_accessor.const_scalar_attribute(t) < filter_energy) {
1721 // skip good tets
1722 continue;
1723 }
1724 auto vertices = m.orient_vertices(t);
1725 Vector3d c(0, 0, 0);
1726 for (int i = 0; i < 4; ++i) {
1727 c += coordinate_accessor.const_vector_attribute(vertices[i]).cast<double>();
1728 v_queue.emplace(vertices[i]);
1729 }
1730 centroids.emplace_back(c / 4.0);
1731 }
1732
1733 const double R = initial_target_edge_length * 1.8;
1734
1735 for (const auto& v : vertices_all) { // reset visited flag
1736 visited_accessor.scalar_attribute(v) = char(0);
1737 energy_filter_accessor.scalar_attribute(v) = char(0);
1738 }
1739
1740 // TODO: use efficient data structure
1741 auto get_nearest_dist = [&](const Tuple& v) -> double {
1742 Tuple nearest_tuple;
1743 double min_dist = std::numeric_limits<double>::max();
1744 const Vector3d v_pos = coordinate_accessor.const_vector_attribute(v).cast<double>();
1745 for (const auto& c_pos : centroids) {
1746 double dist = (c_pos - v_pos).norm();
1747 min_dist = std::min(min_dist, dist);
1748 }
1749 return min_dist;
1750 };
1751
1752 while (!v_queue.empty()) {
1753 auto v = v_queue.front();
1754 v_queue.pop();
1755
1756 if (visited_accessor.scalar_attribute(v) == char(1)) continue;
1757 visited_accessor.scalar_attribute(v) = char(1);
1758
1759 double dist = std::max(0., get_nearest_dist(v));
1760
1761 if (dist > R) {
1762 visited_accessor.scalar_attribute(v) = char(0);
1763 continue;
1764 }
1765
1766 energy_filter_accessor.scalar_attribute(v) = char(1);
1767
1768 // push one ring vertices into the queue
1769 for (const auto& v_one_ring : simplex::link(m, simplex::Simplex::vertex(m, v))
1771 if (visited_accessor.scalar_attribute(v_one_ring) == char(1)) continue;
1772 v_queue.push(v_one_ring.tuple());
1773 }
1774 }
1775}
1776
1777
1778} // namespace wmtk::components::internal
const attribute::Accessor< T, Mesh, D > create_const_accessor(const attribute::MeshAttributeHandle &handle) const
attribute::Accessor< T, Mesh, D > create_accessor(const attribute::MeshAttributeHandle &handle)
std::vector< Tuple > get_all(PrimitiveType type) const
Generate a vector of Tuples from global vertex/edge/triangle/tetrahedron index.
Definition Mesh.cpp:18
virtual Tuple switch_tuple(const Tuple &tuple, PrimitiveType type) const =0
switch the orientation of the Tuple of the given dimension
virtual std::vector< Tuple > orient_vertices(const Tuple &t) const =0
PrimitiveType top_simplex_type() const
Definition Mesh.hpp:982
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
The Tuple is the basic navigation tool in our mesh data structure.
Definition Tuple.hpp:19
Handle that represents attributes for some mesh.
std::pair< attribute::MeshAttributeHandle, attribute::MeshAttributeHandle > MeshConstrainPair
const std::vector< Simplex > & simplex_vector() const
Return const reference to the simplex vector.
static Simplex vertex(const Mesh &m, const Tuple &t)
Definition Simplex.hpp:56
constexpr wmtk::PrimitiveType PT
constexpr wmtk::PrimitiveType PF
void set_operation_energy_filter(Mesh &m, const TypedAttributeHandle< double > &coordinate_handle, const TypedAttributeHandle< double > &energy_handle, const TypedAttributeHandle< char > &energy_filter_handle, const TypedAttributeHandle< char > &visited_handle, const double stop_energy, const double current_max_energy, const double initial_target_edge_length)
std::vector< std::pair< std::shared_ptr< Mesh >, std::string > > wildmeshing3d(const WildMeshingOptions &options)
void adjust_sizing_field(Mesh &m, const TypedAttributeHandle< double > &coordinate_handle, const TypedAttributeHandle< double > &edge_length_handle, const TypedAttributeHandle< double > &sizing_field_scalar_handle, const TypedAttributeHandle< double > &energy_handle, const TypedAttributeHandle< double > &target_edge_length_handle, const TypedAttributeHandle< char > &visited_handle, const double stop_energy, const double current_max_energy, const double initial_target_edge_length, const double min_target_edge_length)
void set_operation_energy_filter_after_sizing_field(Mesh &m, const TypedAttributeHandle< Rational > &coordinate_handle, const TypedAttributeHandle< double > &energy_handle, const TypedAttributeHandle< char > &energy_filter_handle, const TypedAttributeHandle< char > &visited_handle, const double stop_energy, const double current_max_energy, const double initial_target_edge_length)
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:380
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.
SimplexCollection link(const Mesh &mesh, const simplex::Simplex &simplex, const bool sort_and_clean)
Definition link.cpp:84
SimplexCollection k_ring(const Mesh &mesh, const Simplex &simplex, int64_t k)
Definition k_ring.cpp:6
int wmtk_orient3d(const Eigen::Ref< const Eigen::Vector3< Rational > > &p0, const Eigen::Ref< const Eigen::Vector3< Rational > > &p1, const Eigen::Ref< const Eigen::Vector3< Rational > > &p2, const Eigen::Ref< const Eigen::Vector3< Rational > > &p3)
Definition orient.cpp:75
constexpr PrimitiveType PE
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:58
Vector< double, 3 > Vector3d
Definition Types.hpp:39
constexpr PrimitiveType PV
std::vector< EnvelopeOptions > envelopes
std::vector< attribute::MeshAttributeHandle > pass_through