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