Wildmeshing Toolkit
wildmeshing3d.cpp
Go to the documentation of this file.
1 #include "wildmeshing3d.hpp"
2 
3 #include "WildmeshingOptions.hpp"
4 #include "wildmeshing_utils.hpp"
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 
66 #include <wmtk/utils/Rational.hpp>
67 #include <wmtk/utils/orient.hpp>
68 
69 #include <wmtk/io/MeshReader.hpp>
71 
72 #include <queue>
73 #include <wmtk/simplex/k_ring.hpp>
74 #include <wmtk/simplex/link.hpp>
75 
76 #include <fstream>
77 
79 
80 using namespace simplex;
81 using namespace operations;
82 using namespace operations::tri_mesh;
83 using namespace operations::tet_mesh;
84 using namespace operations::composite;
85 using namespace function;
86 using namespace invariants;
87 
88 std::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;
748  constexpr static PrimitiveType PT = PrimitiveType::Tetrahedron;
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;
903  constexpr static PrimitiveType PT = PrimitiveType::Tetrahedron;
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,
1169  options.intermediate_output_path,
1170  options.intermediate_output_name,
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",
1213  pre_stats.number_of_performed_operations(),
1214  pre_stats.number_of_successful_operations(),
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(),
1354  pass_stats.number_of_successful_operations(),
1355  pass_stats.number_of_failed_operations(),
1356  pass_stats.collecting_time,
1357  pass_stats.sorting_time,
1358  pass_stats.executing_time);
1359 
1360  multimesh::consolidate(*mesh);
1361 
1362  write(
1363  mesh,
1364  options.intermediate_output_path,
1365  options.intermediate_output_name,
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 {
1520  if (m.top_simplex_type() != PrimitiveType::Tetrahedron) return;
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))
1615  .simplex_vector(PrimitiveType::Vertex)) {
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
1656  if (m.top_simplex_type() != PrimitiveType::Tetrahedron) return;
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)
1685  .simplex_vector(PrimitiveType::Vertex)) {
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 {
1702  if (m.top_simplex_type() != PrimitiveType::Tetrahedron) return;
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))
1770  .simplex_vector(PrimitiveType::Vertex)) {
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
virtual std::vector< Tuple > orient_vertices(const Tuple &t) const =0
const attribute::Accessor< T, Mesh, D > create_const_accessor(const attribute::MeshAttributeHandle &handle) const
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
attribute::Accessor< T, Mesh, D > create_accessor(const attribute::MeshAttributeHandle &handle)
PrimitiveType top_simplex_type() const
Definition: Mesh.hpp:996
SchedulerStats run_operation_on_all(operations::Operation &op)
Definition: Scheduler.cpp:33
void set_update_frequency(std::optional< size_t > &&freq={})
Definition: Scheduler.cpp:393
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
std::pair< attribute::MeshAttributeHandle, attribute::MeshAttributeHandle > MeshConstrainPair
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)
auto amips(const Eigen::MatrixBase< Derived > &B)
Definition: amips.hpp:20
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)
Definition: consolidate.cpp:13
std::shared_ptr< AttributeTransferStrategyBase > make_cast_attribute_transfer_strategy(const wmtk::attribute::MeshAttributeHandle &source, const wmtk::attribute::MeshAttributeHandle &target)
bool link_condition(const EdgeMesh &mesh, const Tuple &edge)
Check if the edge to collapse satisfying the link condition.
std::vector< Tuple > vertices(const Mesh &m, const Simplex &simplex)
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 PV
spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:58
Vector< double, 3 > Vector3d
Definition: Types.hpp:39
constexpr PrimitiveType PE
std::vector< EnvelopeOptions > envelopes
std::vector< attribute::MeshAttributeHandle > pass_through