Wildmeshing Toolkit
Loading...
Searching...
No Matches
TopoOffsetTetMesh.h
1#pragma once
2
3#include <wmtk/MeshData.hpp>
4
5#include <array>
6#include <atomic>
7#include <cmath>
8#include <cstdint>
9#include <cstdlib>
10#include <functional>
11#include <future>
12#include <limits>
13#include <map>
14#include <mutex>
15#include <optional>
16#include <set>
17#include <string>
18#include <type_traits>
19
20#include <wmtk/TetMesh.h>
21#include <wmtk/TetOptimizerMesh.h>
22#include <wmtk/envelope/Envelope.hpp>
23#include <wmtk/optimization/EnergySum.hpp>
24#include <wmtk/optimization/solver.hpp>
25#include <wmtk/simplex/Simplex.hpp>
26#include <wmtk/threading/dynamic_parallel_for.hpp>
27#include <wmtk/threading/enumerable_thread_specific.hpp>
28#include "ExactBandIntegral.hpp"
29#include "OffsetPotential.hpp"
30#include "Parameters.h"
31
32// clang-format off
33#include <wmtk/utils/DisableWarnings.hpp>
34#include <wmtk/utils/EnableWarnings.hpp>
35// clang-format on
36
37using CellTag = std::set<int64_t>;
38
39namespace wmtk::utils {
40class WindingNumberHierarchy;
41}
42
43
44namespace wmtk::components::topological_offset {
45
46const int64_t TEMP_OFFSET_TET_TAG = -1;
47const CellTag TEMP_OFFSET_TET_TAG_SET{TEMP_OFFSET_TET_TAG};
48
49
50// for all attributes:
51// label: 0=default, 1=input, 2=offset
52
60inline int closed_label(const int a, const int b)
61{
62 if (a == 1 || b == 1) return 1;
63 if (a == 2 || b == 2) return 2;
64 return 0;
65}
66
77{
78public:
79 int label = 0;
80 bool m_is_on_input = false; // on the input complex
81 bool m_is_on_offset = false; // on the offset surface itself
82 bool m_is_on_region = false; // on some OTHER tag region's boundary
87 int64_t m_corr_input_vid = -1;
88
92 uint32_t m_born_epoch = 0;
93
98 double m_front_change = 0.;
99 int m_front_change_group = -1;
100};
101
102
104{
105public:
106 int label = 0; // label: 0=default, 1=input, 2=offset
107};
108
109
113{
114public:
115 int label = 0; // label: 0=default, 1=input, 2=offset
120 bool on_sheet = false;
121
124 void merge(const FaceExtra& o)
125 {
126 label = closed_label(label, o.label);
128 }
129};
130
131
133{
134public:
135 int label = 0; // label: 0=default, 1=input, 2=offset
136 CellTag tag;
137 double m_quality = 0; // AMIPS energy, kept up to date by smoothing
146 bool rest_valid = false;
147 std::array<Vector3d, 4> rest_pos;
148};
149
150
164{
165public: // mode for splitting in marching tets
166 enum class EdgeSplitMode {
167 Midpoint = 0, // construction: simplicial embedding AND marching_tets
168 SphereTrace = 1, // marching_tets (construction_mode): sphere tracing along the edge to
169 // d(x) = m_construction_distance, midpoint when the trace leaves the
170 // edge
171 Optimization = 5 // the optimization phase; the shared engine places the vertex
172 };
173
174public:
175 std::array<size_t, 4> m_init_counts = {{0, 0, 0, 0}};
176 size_t m_tags_count;
181 int64_t m_sheet_tag = -1;
185 std::vector<std::string> m_face_tag_names;
186 std::map<int64_t, int> m_face_tag_bit;
188 bool is_sheet_tag(int64_t tag) const { return tag == m_sheet_tag || m_face_tag_bit.count(tag); }
190 MatrixXd m_sheet_V;
191 MatrixXi m_sheet_F;
216 double input_complex_distance(const Vector3d& p) const;
220 std::shared_ptr<const wmtk::utils::WindingNumberHierarchy> m_input_solid_winding;
221
230 std::shared_ptr<OffsetPotential3D> m_offset_potential;
231
233 int64_t front_face_band_cell(size_t a, size_t b, size_t c) const;
236 double front_vertex_relative_residual(size_t vid) const;
237 double front_vertex_residual_length(size_t vid) const;
238 Vector3d front_vertex_field_gradient(size_t vid) const;
239
263 std::shared_ptr<Envelope> m_input_complex_envelope;
267
278 int m_n_regions = 0;
279 std::vector<std::shared_ptr<OffsetPotential3D>> m_region_potentials;
284 std::vector<std::shared_ptr<Envelope>> m_region_envelopes;
285 std::vector<int64_t> m_phi_vert_region;
286 std::vector<int64_t> m_phi_seg_region;
287 std::vector<int64_t> m_phi_face_region;
288 std::vector<int64_t> m_phi_point_region;
289 std::vector<int> m_cell_region;
290 std::vector<int> m_vertex_region;
291 void init_region_potentials(double delta, double effective_factor);
294 void assign_band_regions(bool log = true);
296 void log_front_profile(size_t vid);
297 int vertex_region(const size_t vid) const
298 {
299 return vid < m_vertex_region.size() ? m_vertex_region[vid] : -1;
300 }
301 int edge_region(const size_t va, const size_t vb) const
302 {
303 const int a = vertex_region(va), b = vertex_region(vb);
304 return (a >= 0 && a == b) ? a : -1;
305 }
306 const OffsetPotential3D& potential_for_region(const int region) const
307 {
308 return (region >= 0 && size_t(region) < m_region_potentials.size())
309 ? *m_region_potentials[size_t(region)]
311 }
312 const OffsetPotential3D& potential_for(const size_t vid) const
313 {
314 return potential_for_region(vertex_region(vid));
315 }
318 std::shared_ptr<const OffsetPotential3D> potential_ptr_for(const size_t vid) const
319 {
320 const int r = vertex_region(vid);
321 return (r >= 0 && size_t(r) < m_region_potentials.size()) ? m_region_potentials[size_t(r)]
323 }
324 const OffsetPotential3D& potential_for_edge(const size_t va, const size_t vb) const
325 {
326 return potential_for_region(edge_region(va, vb));
327 }
329 const OffsetPotential3D& potential_for_face(const Tuple& f) const;
330
350 bool face_is_held(const Tuple& f) const;
353 bool vertex_is_held(size_t vid) const;
356 bool face_is_complex_boundary(const Tuple& f) const;
360 void build_envelopes();
366 void release_final_envelopes();
378 double tetwild_pass(const char* what, int max_its, bool coarsen = false);
405 {
406 size_t n_vertices = 0;
407 std::vector<std::array<size_t, 4>> tets;
408 std::vector<VertexAttributes> vertex;
409 std::vector<VertexExtra> vertex_extra;
410 std::vector<FaceAttributes> face;
411 std::vector<FaceExtra> face_extra;
412 std::vector<EdgeAttributes> edge;
413 std::vector<TetAttributes> tet;
414 };
425 void restore_snapshot(const MeshSnapshot& s);
427 std::vector<Eigen::Vector3d> m_held_verts;
428 std::vector<Eigen::Vector3i> m_held_tris;
430 std::shared_ptr<Envelope> m_loop_envelope;
431
438 bool m_tetwild_pass = false;
439
463 enum class OpRules { Loop, TetWild };
464 OpRules m_op_rules = OpRules::Loop;
465 bool tetwild_rules() const { return m_op_rules == OpRules::TetWild; }
466
476 std::shared_ptr<Envelope> m_offset_envelope;
477 void build_offset_envelope();
478
481 void check_no_vertex_on_both_surfaces(const char* when) const;
482
492 bool final_stage = true,
493 bool refine = true,
494 const std::string& label = std::string(),
495 int max_turns = 0,
496 bool fold_free = false);
505 void loop_group(const std::array<int, 4>& ops, const std::string& where);
534 void front_pocket_split(const std::string& where);
550 void label_cleanup(const std::string& where);
556 double label_flip_surface_change(size_t tid) const;
558 static constexpr int kEnergyDecreasePasses = 100;
559 static constexpr int kPocketMaxRounds = 10;
561 bool m_pocket_splitting = false;
564 std::vector<std::array<size_t, 2>> m_settle_split_edges;
565
569 bool m_init_optimize = false;
573 std::string m_frame_prefix;
580 void linear_growth();
591 {
592 double base;
593 double delta;
595 int j = 0;
596 int level = 1;
597 double target() const { return j == 0 ? delta : base + (delta - base) / std::ldexp(1., j); }
598 enum class Next { Done, Advance, Retry, GiveUp };
599 Next report(const bool converged)
600 {
601 if (converged) {
602 if (j == 0) return Next::Done;
603 base = target();
604 j = 0;
605 ++level;
606 return Next::Advance;
607 }
608 ++j;
609 return j >= max_fails ? Next::GiveUp : Next::Retry;
610 }
611 };
619 void smart_growth();
622 static double growth_stage_target(double d0, double delta, int k, int n);
623
626 double front_gradient_linf();
629
630 EdgeSplitMode m_edge_split_mode = EdgeSplitMode::Midpoint;
631
632 // tag name maps
633 std::map<std::string, int64_t> m_tag_name_to_id;
634 std::map<int64_t, std::string> m_tag_id_to_name;
635 CellTag m_offset_output_tag_ids;
636
637 // if in 'singlebody' mode
638 bool m_singlebody = false;
639 int64_t m_single_tag;
640
641 // just for retaining in output. dont actually use
642 bool m_has_envelope = false;
643 MatrixXd m_V_envelope;
644 MatrixXi m_F_envelope;
645 // m_envelope itself lives on the base, which is what checks tracked-surface triangles
646 // against it.
647 double m_envelope_eps = -1;
648
658 static constexpr int INPUT_SURFACE_CLASS = 0;
659 static constexpr int OFFSET_SURFACE_CLASS = 1;
660
663
668 // m_vertex_attribute and m_face_attribute are the base's; these are registered alongside
669 // them in its attribute groups.
670 VertexExtraCol m_vertex_extra;
671 FaceExtraCol m_face_extra;
672 EdgeAttCol m_edge_attribute;
673 TetAttCol m_tet_attribute;
674
699 using FaceExtraMap = std::map<std::array<size_t, 3>, FaceExtra>;
701 void face_extra_capture(const std::vector<size_t>& tids, FaceExtraMap& out) const;
703 void face_extra_assign(const std::vector<size_t>& tids, const FaceExtraMap& captured);
708 std::vector<std::pair<FaceExtra, std::array<size_t, 2>>>>
710 using EdgeLabelMap = std::map<std::array<size_t, 2>, int>;
714 const std::vector<size_t>& tids,
715 EdgeLabelMap& out,
716 size_t from = size_t(-1),
717 size_t to = size_t(-1)) const;
719 void edge_label_assign(const std::vector<size_t>& tids, const EdgeLabelMap& captured);
722 m_edge_label_collapse;
729 void close_labels(const std::vector<size_t>& tids);
731
732 TopoOffsetTetMesh(Parameters& _m_offset_params, int _num_threads = 0)
733 : wmtk::TetOptimizerMesh(_m_offset_params, nullptr)
734 , m_offset_params(_m_offset_params)
735 {
736 NUM_THREADS = _num_threads;
737 // The base owns the vertex, edge and face slots; register the offset's own data with
738 // its groups so it is resized, protected and rolled back with them.
739 m_vertex_attr_group.add(&m_vertex_extra);
740 m_face_attr_group.add(&m_face_extra);
741 add_edge_attributes(&m_edge_attribute);
742 p_tet_attrs = &m_tet_attribute;
743
744 m_collapse_check_link_condition = false;
745 m_collapse_check_manifold = false;
746
747 // As in 2D. The per-vertex Newton solver logs a line per smoothing attempt at info level,
748 // which is one line per vertex per pass and buries the run's own output.
749 optimization::deactivate_opt_logger();
750 }
751
752 ~TopoOffsetTetMesh() override = default;
753
755
756 double cell_quality(const size_t tid) const override { return m_tet_attribute[tid].m_quality; }
757 void set_cell_quality(const size_t tid, const double q) override
758 {
759 m_tet_attribute[tid].m_quality = q;
760 }
761
854 double tet_energy(size_t tid, bool band_term = true) const;
857 bool band_term_cancels(const std::vector<size_t>& tids) const
858 {
859 // "exact_swept": every operation's sums leave the band term out, and its rule adds the
860 // band term's exact change (EXACT SWEPT CHANGE below)
861 if (swept_rule()) return true;
862 if (m_offset_params.band_volume_rule != "exact" &&
863 m_offset_params.band_volume_rule != "front_flux")
864 return false;
865 std::vector<size_t> vids;
866 for (const size_t tid : tids) {
867 if (!cell_is_offset_band(tid)) return false;
868 for (const size_t v : oriented_tet_vids(tid)) vids.push_back(v);
869 }
870 wmtk::vector_unique(vids);
871 std::vector<Vector3d> pts;
872 pts.reserve(vids.size());
873 for (const size_t v : vids) pts.push_back(m_vertex_attribute[v].m_posf);
874 const int n = int(pts.size());
875 if (m_band_exact->front_flux_flag(pts.data(), n)) return false;
876 return m_band_exact->distance_bound(pts.data(), n) <= m_band_exact->front_flux_reach();
877 }
918 bool swept_rule() const { return m_offset_params.band_volume_rule == "exact_swept"; }
922 double band_swept_change(
923 size_t vid,
924 const Vector3d& x0,
925 const Vector3d& x1,
926 ExactBandIntegral::Stats* st = nullptr) const;
929 void band_swept_faces(
930 size_t vid,
931 const Vector3d& x0,
932 std::vector<std::array<Vector3d, 2>>& faces) const;
933 double band_swept_sum(
934 const Vector3d& x0,
935 const Vector3d& x1,
936 const std::vector<std::array<Vector3d, 2>>& faces,
937 ExactBandIntegral::Stats* st = nullptr) const;
940 double band_chain_change(
941 const std::vector<std::array<size_t, 4>>& old_band,
942 const std::vector<std::array<size_t, 4>>& new_band,
943 ExactBandIntegral::Stats* st = nullptr) const;
947 double swap_swept_change(const std::vector<std::array<size_t, 4>>& new_band, bool flip);
955 static constexpr int kSweptHalvings = 4;
957 {
958 std::atomic<size_t> solves{0}, refused{0};
959 std::array<std::atomic<size_t>, kSweptHalvings + 1> accepted{};
960 };
961 bool swept_accept(
962 size_t vid,
963 const std::vector<size_t>& ring,
964 const Vector3d& x0,
965 double amips_before,
966 SweptCounters& c);
967 mutable SweptCounters m_swept_front, m_swept_nonfront;
989 double smoothing_ring_energy(const std::vector<Tuple>& ring, const bool stored) const override
990 {
991 // TetWild's rules (m_op_rules): the base's max over the ring.
992 if (tetwild_rules()) return TetOptimizerMesh::smoothing_ring_energy(ring, stored);
993 double s = 0.;
994 if (swept_rule()) {
995 // "exact_swept": the ring's AMIPS terms plus the band term's change from the position
996 // at the `stored` call (EXACT SWEPT CHANGE); the vertex is the one
997 // smooth_nonfront_vertex() is smoothing, the same in both calls
998 for (const Tuple& loc : ring) s += tet_energy(loc.tid(*this), false);
999 auto& st = m_swept_stored.local();
1000 const size_t vid = st.first;
1001 const auto vs = oriented_tet_vids(ring.front().tid(*this));
1002 if (std::find(vs.begin(), vs.end(), vid) == vs.end()) {
1003 log_and_throw_error(
1004 "exact_swept: the projected step's ring is not vertex {}'s",
1005 vid);
1006 }
1007 if (stored) {
1008 st.second = m_vertex_attribute[vid].m_posf;
1009 return s;
1010 }
1011 return s + band_swept_change(vid, st.second, m_vertex_attribute[vid].m_posf);
1012 }
1013 for (const Tuple& loc : ring) s += tet_energy(loc.tid(*this));
1014 // EXPERIMENTAL_band_volume_rule "surface": the offset faces through the vertex move with
1015 // it (smooth_nonfront_vertex() names it).
1016 if (surface_energy()) {
1017 const size_t vid = m_smoothing_vid.local();
1018 if (vid != size_t(-1) && m_vertex_extra[vid].m_is_on_offset) s += surface_term_at(vid);
1019 }
1020 return s;
1021 }
1028 double vol_amips3(const std::array<size_t, 4>& vids, const double amips3) const
1029 {
1030 double tr = 0.;
1031 for (int i = 0; i < 4; ++i) {
1032 for (int j = i + 1; j < 4; ++j) {
1033 tr += (m_vertex_attribute[vids[size_t(i)]].m_posf -
1034 m_vertex_attribute[vids[size_t(j)]].m_posf)
1035 .squaredNorm();
1036 }
1037 }
1038 tr *= 0.5;
1039 return std::sqrt(2.) / 12. * tr * std::sqrt(tr) * std::sqrt(amips3);
1040 }
1041
1080 double band_cell_term(const std::array<size_t, 4>& vids) const
1081 {
1083 log_and_throw_error(
1084 "the energy's band term reads one field, and this input has {} regions",
1085 m_n_regions);
1086 }
1087 if (m_offset_params.band_volume_rule == "exact" ||
1088 m_offset_params.band_volume_rule == "front_flux" || swept_rule()) {
1089 // EXPERIMENTAL_band_volume_rule "exact" (and "front_flux", whose per-cell quantity
1090 // it is, see above): J_t / delta, J_t = int_t (d - delta) dV in closed form
1091 // (ExactBandIntegral), in place of the corner bound
1092 const std::array<Vector3d, 4> p = {
1093 {m_vertex_attribute[vids[0]].m_posf,
1094 m_vertex_attribute[vids[1]].m_posf,
1095 m_vertex_attribute[vids[2]].m_posf,
1096 m_vertex_attribute[vids[3]].m_posf}};
1097 const double delta = m_offset_params.target_distance;
1098 return m_band_exact->value(p, delta) / delta;
1099 }
1100 int64_t best = -1;
1101 return band_cell_corner_bound(vids, best);
1102 }
1104 double band_cell_corner_bound(const std::array<size_t, 4>& vids, int64_t& best) const
1105 {
1106 const double delta = m_offset_params.target_distance;
1107 std::array<Vector3d, 4> p;
1108 for (int j = 0; j < 4; ++j) p[size_t(j)] = m_vertex_attribute[vids[size_t(j)]].m_posf;
1109 const double vol = std::abs((p[1] - p[0]).dot((p[2] - p[0]).cross(p[3] - p[0]))) / 6.;
1110 const double md =
1111 min_mean_distance(*m_input_complex_envelope, m_input_complex_pad, p.data(), 4, best);
1112 return vol * ((md - delta) / delta);
1113 }
1117 std::shared_ptr<const ExactBandIntegral> m_band_exact;
1120 double band_amips_weight() const
1121 {
1122 const double s = optimization_stop_metric();
1123 return m_offset_params.offset_amips_weight / (s * s * s);
1124 }
1126 double cell_energy(const std::array<size_t, 4>& vids, const double amips3, const bool band)
1127 const
1128 {
1129 if (amips3 >= MAX_ENERGY) return MAX_ENERGY;
1130 double a = band_amips_weight() * vol_amips3(vids, amips3);
1131 // EXPERIMENTAL_band_volume_rule "surface": no cell carries an offset term; it lives on the
1132 // front's faces (surface_face_term()), which every rule adds beside the cells' sums.
1133 if (band && !surface_energy()) a += band_cell_term(vids);
1135 return a;
1136 }
1138 double tet_energy_sum(const std::vector<size_t>& tids, bool band_term = true) const;
1141 double candidate_energy(const std::array<size_t, 4>& vids);
1144 int candidate_label(const std::array<size_t, 4>& vids);
1147 mutable std::atomic<int> m_volume_reject_collapse{0}, m_volume_reject_swap{0};
1151 mutable std::atomic<int> m_volume_reject_split{0};
1152
1155 double global_energy() const;
1159 double offset_energy() const;
1160
1174 bool surface_energy() const { return m_offset_params.band_volume_rule == "surface"; }
1179 double surface_term_weight() const;
1183 double surface_face_term(size_t a, size_t b, size_t c) const;
1185 double surface_term_at(size_t vid) const;
1187 double surface_term_total() const;
1192 double stencil_max_sq_error() const;
1196 double face_stencil_max_sq(const std::array<size_t, 3>& f) const;
1200 double surface_integral_raw() const;
1204 double settle_metric() const
1205 {
1206 if (!surface_energy()) return offset_energy();
1209 }
1211 void live_offset_faces_at(size_t vid, std::vector<std::array<size_t, 3>>& out) const;
1218 double offset_term_weight() const
1219 {
1220 const double f = m_offset_params.front_conv_frac();
1221 return 1. / (f * f);
1222 }
1223
1231 void set_vertex_position(const size_t vid, const Vector3d& p)
1232 {
1233 m_vertex_attribute[vid].m_posf = p;
1234 m_vertex_attribute[vid].set_pos_to_posf();
1235 m_vertex_attribute[vid].m_is_rounded = true;
1236 }
1237
1241 bool face_is_offset(const size_t fid) const
1242 {
1243 return m_face_attribute[fid].m_is_surface_fs &&
1244 m_face_attribute[fid].m_surface_class == OFFSET_SURFACE_CLASS;
1245 }
1246 bool face_is_region(const size_t fid) const
1247 {
1248 return m_face_attribute[fid].m_is_surface_fs &&
1249 m_face_attribute[fid].m_surface_class != OFFSET_SURFACE_CLASS;
1250 }
1251
1259 {
1260 FaceAttributes tags;
1261 FaceExtra extra;
1262 };
1263 FaceSnapshot face_snapshot(const size_t fid) const
1264 {
1265 return FaceSnapshot{m_face_attribute[fid], m_face_extra[fid]};
1266 }
1267 void restore_face(const size_t fid, const FaceSnapshot& s)
1268 {
1269 m_face_attribute[fid] = s.tags;
1270 m_face_extra[fid] = s.extra;
1271 }
1272
1280 void label_offset_boundary();
1281
1285 bool cell_in_region(const size_t tid) const
1286 {
1287 const int l = m_tet_attribute[tid].label;
1288 return l == 1 || l == 2;
1289 }
1291 bool cell_is_input_complex(const size_t tid) const { return m_tet_attribute[tid].label == 1; }
1293 bool cell_is_offset_band(const size_t tid) const { return m_tet_attribute[tid].label == 2; }
1294
1295 // ------- the plastic medium (use_rest_pose) -------
1296
1299 bool m_plastic_active = false;
1304 bool cell_is_plastic(const size_t tid) const
1305 {
1306 return m_plastic_active && !cell_in_region(tid);
1307 }
1311 bool plastic_rest(const size_t tid, Eigen::Matrix3d& R) const;
1314 void stamp_plastic_rests();
1318 void stamp_rest_cell(size_t tid);
1320 bool all_plastic(const std::vector<size_t>& tids) const;
1341 bool plastic_tie(const std::vector<size_t>& before, const std::vector<int>& after_labels) const;
1342
1344 void optimize_offset(const std::filesystem::path& output_file);
1345
1352 std::pair<double, double> compute_distance_deviation() const;
1353
1356 mutable size_t m_worst_dist_vid = static_cast<size_t>(-1);
1357 void log_worst_dist_vertex() const;
1358
1367 bool face_is_offset_surface_live(const Tuple& f) const;
1370 static constexpr double kCos150 = -0.86602540378443864676;
1373 bool offset_face_normal_out_of_band(const Tuple& f, Vector3d& n) const;
1378 size_t count_real_folds(
1379 std::vector<char>* corners = nullptr,
1380 std::vector<std::array<size_t, 3>>* faces = nullptr) const;
1382 bool edge_is_offset_surface_live(size_t a, size_t b) const;
1384 std::vector<std::array<size_t, 3>> offset_surface_faces() const;
1386 std::vector<Tuple> offset_surface_faces_live_at(size_t vid) const;
1390 bool vertex_has_live_offset_face(size_t vid) const;
1394 template <typename F>
1395 bool for_each_live_offset_face_at(size_t vid, F&& fn) const;
1412 void refresh_offset_membership(size_t vid);
1415 std::pair<size_t, size_t> offset_membership_mismatches() const;
1418 void check_offset_membership(const char* when) const;
1419
1443 mutable std::atomic<long long> m_offset_face_lookup_misses{0};
1447 mutable std::atomic<long long> m_offset_face_invalid_tuple{0};
1450 void report_offset_face_lookup_misses(const char* when) const;
1451
1463 double front_face_field_angle(size_t a, size_t b, size_t c) const;
1467 bool front_face_out_normal(size_t a, size_t b, size_t c, Vector3d& n) const;
1470 bool front_face_out_normal_in(size_t a, size_t b, size_t c, size_t band_tid, Vector3d& n) const;
1472 double front_face_field_angle_in(size_t a, size_t b, size_t c, size_t band_tid) const;
1475 double front_face_angle_to_field(size_t a, size_t b, size_t c, const Vector3d& n) const;
1482 static double band_side_dihedral(const Vector3d& n1, const Vector3d& n2, const Vector3d& t)
1483 {
1484 return 180. - std::atan2(n1.cross(n2).dot(t), n1.dot(n2)) * 180. / M_PI;
1485 }
1489 static bool dihedral_is_foldover(const double dihedral, const double angle)
1490 {
1491 return dihedral < angle || dihedral > 360. - angle;
1492 }
1494 bool front_face_is_foldover(size_t a, size_t b, size_t c) const
1495 {
1497 }
1503 {
1504 size_t n_fold = 0, n_faces = 0, n_unmeasured = 0;
1505 double worst = 0.;
1506 Vector3d worst_at = Vector3d::Zero();
1507 };
1508 FoldCensus fold_census() const;
1514 {
1515 return m_offset_params.growth_mode == "individual_op_reject" && !m_tetwild_pass &&
1517 }
1522 void folded_front_faces_at(size_t vid, std::set<std::array<size_t, 3>>& out) const;
1525 bool face_folded_in(size_t a, size_t b, size_t c, const std::vector<size_t>& cells) const;
1530 m_swap_folds_before{-1};
1533 mutable std::atomic<int> m_fold_reject_collapse{0}, m_fold_reject_swap{0},
1534 m_fold_reject_smooth{0};
1535
1538 std::vector<std::array<double, 8>> optimization_metrics;
1541 int m_round = 0;
1543 mutable size_t m_debug_seq = 0;
1550 {
1551 std::vector<std::string> labels;
1552 std::map<std::string, std::vector<size_t>> pvd_series;
1553 };
1554 DebugTimeline m_debug_timeline;
1560 std::future<DebugTimeline> m_debug_frame_write;
1566 static void write_debug_pvd(const std::string& out, const DebugTimeline& timeline);
1574 mutable int m_debug_pass = 0;
1575 mutable int m_debug_last_round = -1;
1576 mutable char m_debug_last_tag = '?';
1577 mutable std::string m_debug_last_prefix = "?";
1581 bool m_converged = false;
1586 double m_quality_max_amips = 0.;
1587
1588 std::atomic<int> iter_cnt_split = 0, iter_cnt_collapse = 0, iter_cnt_swap = 0;
1589 std::atomic<int> iter_cnt_collapse_offset_removed{0};
1592 std::atomic<int> iter_cnt_swap_offset_reject{0};
1597 std::atomic<long long> m_swap_scoring_checked{0};
1598 std::atomic<long long> m_swap_scoring_mismatch{0};
1599
1625 mutable std::atomic<long long> funnel_offered{0};
1627 mutable std::array<std::atomic<long long>, 3> funnel_kind{};
1630 mutable std::atomic<long long> funnel_cases{0};
1637 mutable std::atomic<long long> funnel_case_inverted{0};
1638 mutable std::atomic<long long> funnel_case_not_better{0};
1639 mutable std::atomic<long long> funnel_case_better{0};
1640 mutable std::atomic<long long> funnel_after_cells{0};
1641 mutable std::atomic<long long> funnel_energy{0};
1642 mutable std::atomic<long long> funnel_committed{0};
1643 std::string flip_funnel_report() const;
1644 void flip_funnel_reset();
1647 std::atomic<int> iter_cnt_split_offset_before{0};
1648 std::atomic<int> iter_cnt_split_offset{0};
1652 std::atomic<long> m_split_order_waits{0};
1653 mutable std::atomic<long> m_split_off_longest{0};
1656 static bool& split_off_longest()
1657 {
1658 static thread_local bool off = false;
1659 return off;
1660 }
1661 void op_event(OpKind k, OpEvent e) const override;
1664 bool split_edge_is_due(const Tuple& e) const;
1665
1669 {
1670 bool is_edge_on_region = false;
1671 bool is_edge_on_offset = false;
1672 std::map<simplex::Edge, TetAttributes> tets;
1675 double parent_q_max = -1.;
1678 double parent_flatness = 1.;
1679 };
1681
1682 bool marching_split_edge_before(const Tuple& t);
1683 bool marching_split_edge_after(const Tuple& t);
1699 const Vector3d& p_in,
1700 const Vector3d& p_out,
1701 Vector3d& p_new,
1702 size_t& steps) const;
1706 size_t m_marching_root_splits = 0, m_marching_midpoint_splits = 0;
1707 size_t m_marching_trace_steps = 0, m_marching_trace_steps_max = 0;
1715 double m_growth_start = 0.;
1716
1725 bool collapse_edge_before(const Tuple& t) override;
1727 bool collapse_edge_after(const Tuple& t) override;
1728 bool collapse_before_vertex(size_t v1, size_t v2, double edge_length) override;
1732 size_t v1,
1733 size_t v2,
1734 const std::vector<std::array<size_t, 2>>& boundary_edges) override;
1735 bool collapse_is_order_2_edge(const std::array<size_t, 2>& e) override
1736 {
1737 return is_order_2_edge(e);
1738 }
1739 void collapse_after_vertex(size_t v1, size_t v2) override;
1740
1747 bool swap_before_interior(const std::vector<size_t>& tids) override;
1748 bool swap_before_surface(
1749 const std::vector<size_t>& tids,
1750 size_t a,
1751 size_t b,
1752 size_t c,
1753 size_t d) override;
1754 bool swap_after_cells(const std::vector<size_t>& tids, bool is_surface_flip) override;
1755
1760 bool split_before_cells(const Tuple& edge, const std::vector<Tuple>& parents) override;
1761 bool split_after_cells(size_t v1, size_t v2, size_t v_new, const std::vector<Tuple>& children)
1762 override;
1763 bool split_adjust_position(size_t v_new, const std::vector<Tuple>& children) override;
1764 void split_after_vertex(size_t v_new, bool is_edge_open_boundary) override;
1765
1768 bool is_open_boundary_edge(const Tuple& e) override { return is_order_2_edge(e); }
1769
1770 bool smooth_before(const Tuple& t) override;
1771 bool smooth_after(const Tuple& t) override;
1772
1781 bool vertex_is_on_domain_boundary(const size_t vid) const
1782 {
1783 return !m_vertex_attribute[vid].on_bbox_faces.empty();
1784 }
1785
1794
1797 bool is_edge_on_region(const Tuple& loc);
1798 bool is_edge_on_offset(const Tuple& loc);
1799
1803
1812
1817 {
1818 std::atomic<int> attempted{0};
1819 std::atomic<int> before_bbox{0};
1820 std::atomic<int> before_unrounded{0};
1821 std::atomic<int> offset_attempted{0};
1822 std::atomic<int> offset_accepted{0};
1823 std::atomic<int> interior_attempted{0};
1824 std::atomic<int> region_attempted{0};
1825
1826 void reset()
1827 {
1828 for (std::atomic<int>* c :
1829 {&attempted,
1830 &before_bbox,
1835 &region_attempted}) {
1836 c->store(0);
1837 }
1838 }
1839 };
1840 SmoothTrace m_smooth_trace;
1841
1862 {
1863 size_t vid;
1864 int iterations;
1866 };
1867 std::vector<FrontSolveRecord> m_front_solve_log;
1868 std::mutex m_front_solve_log_mutex;
1883 static constexpr double kSmoothRelGradNormTol = 1e-6;
1886 polysolve::nonlinear::Solver& smoothing_solver();
1891 std::atomic<size_t> m_front_veto_asked{0}, m_front_veto_fired{0};
1894 std::atomic<size_t> m_damping_moves{0}, m_damping_bounded{0};
1898 std::shared_ptr<const OffsetPotential3D> m_repulsion_potential;
1902 std::atomic<size_t> m_repulsion_veto_asked{0}, m_repulsion_veto_fired{0};
1904 bool is_marched_edge(const size_t a, const size_t b) const
1905 {
1906 return (m_vertex_extra[a].label == 0) != (m_vertex_extra[b].label == 0);
1907 }
1911 bool is_repulsion_vertex(const size_t vid) const
1912 {
1913 if (!m_repulsion_potential || m_vertex_extra[vid].label != 0 || vertex_is_held(vid)) {
1914 return false;
1915 }
1916 for (const size_t u : get_one_ring_vids_for_vertex(vid)) {
1917 if (m_vertex_extra[u].label != 0) return true;
1918 }
1919 return false;
1920 }
1926 std::shared_ptr<OffsetEnergy3D> m_repulsion_term;
1927 double repulsion_term_at(const size_t vid) const
1928 {
1929 Eigen::VectorXd x = m_vertex_attribute[vid].m_posf;
1930 return m_repulsion_term->value(x);
1931 }
1938 double repulsion_cell_term(const std::array<size_t, 4>& vids) const
1939 {
1940 if (!m_repulsion_potential) return 0.;
1941 bool has_complex = false;
1942 for (const size_t v : vids) has_complex = has_complex || m_vertex_extra[v].label != 0;
1943 if (!has_complex) return 0.;
1944 double e = 0.;
1945 for (const size_t v : vids) {
1946 if (m_vertex_extra[v].label == 0 && !vertex_is_held(v)) {
1947 e += repulsion_term_at(v);
1948 }
1949 }
1950 return e;
1951 }
1954 size_t repulsion_cells_at(const size_t vid) const
1955 {
1956 size_t n = 0;
1957 for (const size_t tid : get_one_ring_tids_for_vertex(vid)) {
1958 for (const size_t u : oriented_tet_vids(tid)) {
1959 if (m_vertex_extra[u].label != 0) {
1960 ++n;
1961 break;
1962 }
1963 }
1964 }
1965 return n;
1966 }
1968 std::shared_ptr<OffsetEnergy3D> repulsion_energy(const size_t vid) const
1969 {
1970 return std::make_shared<OffsetEnergy3D>(
1972 double(repulsion_cells_at(vid)) * 4. * offset_term_weight(),
1973 true,
1974 true,
1975 /*one_sided=*/true);
1976 }
1982 bool repulsion_embedding_kept(const std::vector<size_t>& tids) const;
1986 bool simplex_in_input_complex(const std::vector<size_t>& c) const;
1987 std::atomic<size_t> m_repulsion_embed_refused{0};
1993 static constexpr int kGradBins = 22;
1994 std::array<std::atomic<size_t>, kGradBins> m_front_grad_abs{}, m_front_grad_rel{};
1995 void log_smoothing_pass_accounting() override;
2008 void smoothing_pass_done() override
2009 {
2010 if (m_energy_step_turn > 0) {
2011 log_energy_step(m_energy_step_first ? m_energy_step_group : "smooth");
2012 m_energy_step_first = false;
2013 }
2016 }
2027 void log_energy_step(const char* step) const;
2032 std::pair<double, double> energy_parts() const;
2038 template <typename F>
2039 void diagnostic_parallel_for(const size_t n, const size_t chunk, F&& fn) const
2040 {
2041 wmtk::threading::dynamic_parallel_for(n, std::max(1, NUM_THREADS), chunk, fn);
2042 }
2045 static constexpr size_t kDiagnosticChunk = 64;
2050 const char* m_energy_step_group = "";
2051 bool m_energy_step_first = false;
2052
2059 std::vector<double> m_cross_ring;
2060 std::vector<Vector3d> m_cross_pos;
2061 bool m_cross_valid = false;
2064 std::atomic<size_t> m_cross_own{0}, m_cross_neighbour{0};
2066 std::vector<double> front_ring_measures() const;
2068 double ring_measure_at(size_t vid) const;
2072 void crossing_snapshot(const std::string& pass, bool compare, bool match_positions);
2075 void update_attributes() override;
2076
2082 mutable std::atomic<size_t> m_needle_smooth_offered{0};
2083 mutable std::atomic<size_t> m_needle_smooth_reached{0};
2084 mutable std::atomic<size_t> m_needle_smooth_fixed{0};
2085 mutable std::atomic<size_t> m_needle_smooth_stationary{0};
2086 mutable std::atomic<size_t> m_needle_smooth_reports{0};
2087
2090 double ring_max_quality(size_t vid) const;
2092 double tet_amips(const size_t tid) const { return std::cbrt(cell_quality(tid)); }
2093
2101 double tet_flatness(size_t tid) const;
2102
2104 void needle_forensics() const;
2105
2107 void record_flatness(const char* op, double parent_flat, size_t child_tid) const;
2108 mutable std::atomic<size_t> m_flat_created_split{0};
2109 mutable std::atomic<size_t> m_flat_created_collapse{0};
2110 mutable std::atomic<size_t> m_flat_worsened_split{0};
2111 mutable std::atomic<size_t> m_flat_genesis_reports{0};
2112 static constexpr double kFlatThreshold = 1e-3;
2125 {
2126 double before = 0.;
2127 std::vector<size_t> v2_only;
2130 bool tie = false;
2132 bool band_cancels = false;
2135 double surface_before = 0.;
2141 std::vector<std::array<Vector3d, 2>> swept_faces;
2142 Vector3d swept_x1 = Vector3d::Zero(), swept_x2 = Vector3d::Zero();
2143 };
2158 void log_smooth_trace() const;
2159
2161 void audit_surface_containment(const std::string& when) const;
2162
2164
2167 bool vertex_is_on_region(const size_t vid) const
2168 {
2169 return m_vertex_extra[vid].m_is_on_region || !m_vertex_attribute[vid].on_bbox_faces.empty();
2170 }
2171
2178 std::shared_ptr<Envelope> surface_envelope_for_face(
2179 const std::array<size_t, 3>& vids,
2180 const FaceAttributes& /*attr*/) const override
2181 {
2182 const auto found = try_tuple_from_face(vids);
2183 if (!found) return m_envelope; // not reached: every caller asks about an existing face
2184 const Tuple& f = std::get<0>(*found);
2185 if (f.switch_tetrahedron(*this) && face_is_offset_surface_live(f)) {
2186 return m_tetwild_pass ? m_offset_envelope : nullptr;
2187 }
2188 return face_is_held(f) ? m_envelope : nullptr;
2189 }
2190
2193 bool allow_surface_swap() const override { return true; }
2194
2204 bool swap_quality_allowed(double after, double before, bool is_surface_flip) const override
2205 {
2206 // TetWild's rules (m_op_rules): the base's, after < before.
2207 return !tetwild_rules() ||
2208 TetOptimizerMesh::swap_quality_allowed(after, before, is_surface_flip);
2209 }
2234 double swap_edge_44_energy(const std::vector<std::array<size_t, 4>>& tets, const int op_case)
2235 override;
2236 double swap_edge_56_energy(const std::vector<std::array<size_t, 4>>& tets, const int op_case)
2237 override;
2250 bool swap_face_before(const Tuple& t) override;
2251 bool check_surface_topology() const override { return m_offset_params.perform_sanity_checks; }
2252
2258 std::shared_ptr<Envelope> smoothing_energy_envelope(const size_t vid) const override
2259 {
2260 return smoothing_envelope(vid);
2261 }
2262 std::shared_ptr<Envelope> smoothing_containment_envelope(const size_t vid) const
2263 {
2264 return smoothing_envelope(vid);
2265 }
2266 std::shared_ptr<Envelope> smoothing_envelope(const size_t vid) const
2267 {
2268 if (vertex_is_held(vid)) return m_envelope;
2269 if (m_tetwild_pass && m_vertex_extra[vid].m_is_on_offset) return m_offset_envelope;
2270 return nullptr;
2271 }
2274 std::shared_ptr<Envelope> smoothing_containment_envelope(
2275 const size_t vid,
2276 const std::array<size_t, 3>& /*face*/,
2277 const FaceAttributes& /*attr*/) const override
2278 {
2279 return smoothing_containment_envelope(vid);
2280 }
2281
2286 bool smooth_front_vertex(const Tuple& t);
2294 bool smooth_repulsion_vertex(const Tuple& t);
2302 bool smooth_nonfront_vertex(const Tuple& t);
2308 double damping_step(size_t vid) const;
2312 bool damping_active() const
2313 {
2315 }
2318 void count_damped_move(size_t vid, const Vector3d& x0, double max_step);
2321 std::shared_ptr<polysolve::nonlinear::Problem> amips3_energy(size_t vid) const;
2324 double front_vertex_normal_gradient(size_t vid) const;
2327 Vector3d front_vertex_move_direction(size_t vid) const;
2331 double front_move_alignment(size_t vid) const;
2334 double front_vertex_conv_ratio(size_t vid) const;
2346 bool front_placed_by_ratio(const double ratio) const
2347 {
2348 return std::isfinite(ratio) && ratio <= 1.;
2349 }
2364 double face_offset_term(size_t a, size_t b, size_t c) const;
2365 double face_offset_term(
2366 const OffsetPotential3D& pot,
2367 const Vector3d& pa,
2368 const Vector3d& pb,
2369 const Vector3d& pc) const;
2371 Vector3d front_vertex_normal(size_t vid) const;
2375 std::shared_ptr<polysolve::nonlinear::Problem> front_objective(size_t vid) const;
2378 bool vertex_carries_offset_term(const size_t vid) const
2379 {
2380 return !m_tetwild_pass && m_offset_potential && m_vertex_extra[vid].m_is_on_offset &&
2381 !vertex_is_held(vid);
2382 }
2393 std::shared_ptr<polysolve::nonlinear::Problem> smoothing_extra_energy(
2394 const size_t vid) const override
2395 {
2396 // TetWild's rules (m_op_rules): no extra energy, so the engine's smoother minimises the
2397 // raw sum of AMIPS over the one-ring (AMIPSEnergy3D), as TetWild's does.
2398 if (tetwild_rules()) return nullptr;
2399 auto sum = std::make_shared<optimization::EnergySum>();
2400 sum->add_energy(amips3_energy(vid));
2401 // Before the march (repulsion_smoothing()) there is no front.
2402 if (is_repulsion_vertex(vid)) {
2403 sum->add_energy(repulsion_energy(vid));
2404 return sum;
2405 }
2406 if (vertex_carries_offset_term(vid)) {
2407 sum->add_energy(front_energy(vid));
2408 } else if (!vertex_is_held(vid)) {
2409 // E's band term moves with this vertex too -- except a vertex held on the input.
2410 // There d = 0 at the vertex and d has a kink: the term's slope along the input's
2411 // normal jumps across the surface, and its curvature there is 0 (or ~1e10 near an
2412 // input edge), so the Newton step along the normal is set by AMIPS alone and lands
2413 // far off; the projected step then discards the normal motion anyway. MEASURED
2414 // 2026-10-09, cube, serial, 3 turns: with the term these solves took 24.6 Newton
2415 // iterations and ~20 line-search trials per iteration (72% of the smoother's value
2416 // calls), without it 3.9 and 1; 19.3 s -> 11.3 s. E still guards the move:
2417 // smooth_nonfront_vertex() vetoes on the ring's sum of tet_energy().
2418 // Under "exact", not where it cancels (BAND TERM THAT CANCELS, tet_energy()): a ring
2419 // of band cells clear of the input's bisectors tiles the star of vid, so the term is a
2420 // constant, and its cells' exact integrals would add only work and their rounding to
2421 // every Newton step. The veto's own test (cancels_before in smooth_nonfront_vertex()).
2422 // Constant wherever the move can end, not only here: vid is not held, so not on the
2423 // domain wall (face_is_held()), and its ring closes around it; the engine keeps no
2424 // position that inverts a ring cell, so vid stays inside the convex hull of its link;
2425 // and the test reads the points only through their box, their least side of each
2426 // bisector polytope's planes, and min_P max d_P (d_P convex), none of which a point
2427 // inside that hull changes. So the test here certifies every position the smoother
2428 // can keep. "front_flux" and "exact_swept" leave the constant out by their own forms
2429 // (band_volume_energy_at()). MEASURED (cube 1e-2 / 1e-4, 15 rounds, serial, two runs
2430 // each): 659 of 46,594 non-front solves had such a ring; at every one's end the
2431 // veto's test found it clear again, and every one was accepted; smoothing 7.7-7.9 s ->
2432 // 7.4-7.5 s.
2433 if (m_offset_params.band_volume_rule == "exact" &&
2435 return sum;
2436 if (const auto bv = band_volume_energy_at(vid)) sum->add_energy(bv);
2437 }
2438 return sum;
2439 }
2440
2444 std::shared_ptr<polysolve::nonlinear::Problem> front_energy(size_t vid) const;
2447 std::shared_ptr<polysolve::nonlinear::Problem> band_volume_energy_at(size_t vid) const;
2448
2449
2451 int stencil_order() const { return m_offset_params.stencil_order; }
2456 const std::vector<std::array<double, 3>>& face_stencil() const
2457 {
2458 const std::string& type = m_offset_params.stencil_type;
2459 const int k = m_offset_params.stencil_order;
2460 return type == "fem" ? fem_face_stencil(k)
2461 : type == "interior" ? interior_face_stencil(k)
2462 : subdivision_face_stencil(k);
2463 }
2468 const std::vector<double>* face_stencil_weights() const
2469 {
2470 return m_offset_params.stencil_type == "fem"
2471 ? &fem_face_stencil_weights(m_offset_params.stencil_order)
2472 : nullptr;
2473 }
2481 {
2482 const int k = m_offset_params.stencil_order;
2483 if (m_offset_params.stencil_type == "subdivision") {
2484 if (k < 0) return 0;
2485 if (k == 0) return 3;
2486 const int n = 1 << (k - 1);
2487 return (n + 1) * (n + 2) / 2 + n * n;
2488 }
2489 if (k < 1) return 0;
2490 if (m_offset_params.stencil_type == "fem") return (k + 1) * (k + 2) / 2;
2491 return k * (k + 1) / 2;
2492 }
2493
2496 void check_offset_within_support(const char* when) const;
2497
2501 {
2502 double max_reachable = 0., avg_reachable = 0.;
2503 double max_pinned = 0.;
2504 size_t n_reachable = 0, n_pinned = 0;
2505 double max_at_vertex = 0., max_in_face = 0.;
2506 size_t n_outside_support = 0;
2507 size_t worst_outside_vid = static_cast<size_t>(-1);
2508 double worst_outside_dist = 0.;
2509 };
2510 DistanceSplit distance_deviation_split() const;
2511
2515 std::vector<bool> band_vertex_mask() const;
2516
2519 double max_band_vertex_distance() const;
2521 double band_vertex_distance_error(const size_t vid) const;
2522
2524 double band_vertex_residual(const size_t vid) const;
2525
2529 {
2530 double max = 0.;
2531 double sum = 0.;
2532 size_t n = 0;
2533 };
2534
2549 template <typename Visit>
2551 const Vector3d& p0,
2552 const Vector3d& p1,
2553 const Vector3d& p2,
2554 Visit&& visit) const
2555 {
2556 for (const auto& w : face_stencil()) {
2557 const Vector3d q(w[0] * p0 + w[1] * p1 + w[2] * p2);
2558 visit(q, w[0], w[1], w[2]);
2559 }
2560 }
2561
2563 template <typename Visit>
2564 void for_each_offset_face_sample(const Tuple& f, Visit&& visit) const
2565 {
2566 const auto vs = get_face_vids(f);
2568 m_vertex_attribute[vs[0]].m_posf,
2569 m_vertex_attribute[vs[1]].m_posf,
2570 m_vertex_attribute[vs[2]].m_posf,
2571 std::forward<Visit>(visit));
2572 }
2573
2577 FaceSamples offset_face_samples(const Tuple& f) const;
2578
2585 {
2586 double max_reachable = 0., avg_reachable = 0.;
2587 double max_pinned = 0.;
2588 size_t n_reachable = 0, n_pinned = 0;
2589 double max_at_vertex = 0., max_in_face = 0.;
2590 size_t n_face_samples = 0;
2591 size_t n_skipped_inverted = 0, n_skipped_unrounded = 0;
2592 };
2594 GradientSplit gradient_split(bool include_face_samples = true) const;
2595
2601 {
2602 double max_vertex = 0., max_face = 0.;
2606 double sum_vertex = 0., sum_face = 0.;
2607 double bar = 1.;
2608 size_t n_vertices = 0, n_faces = 0, n_unmeasurable = 0;
2609 size_t worst_vid = static_cast<size_t>(-1);
2615 size_t n_faces_over = 0, n_faces_over_placed = 0;
2616 double max_face_placed = 0.;
2617 Vector3d worst_placed_centroid = Vector3d::Zero();
2618 double bar_length = 0.;
2624 size_t n_at_floor = 0;
2625 double max_face_at_floor = 0.;
2626 Vector3d worst_at_floor_centroid = Vector3d::Zero();
2634 Vector3d worst_corners_at_floor_centroid = Vector3d::Zero();
2636 double floor_scalar = 0.;
2637 bool floor_from_min_edge_length = false;
2638 size_t n_unplaced = 0;
2647 {
2648 size_t a, b, c;
2649 double measure, len;
2650 };
2651 std::vector<Refinable> refinable;
2667 bool ring_exit = false;
2668 static const char* ring_name() { return "ring measure"; }
2669 double max_ring = 0., sum_ring = 0.;
2670 size_t n_rings = 0, n_rings_unmeasurable = 0;
2671 size_t worst_ring_vid = static_cast<size_t>(-1);
2672 size_t n_rings_over = 0;
2675 std::vector<size_t> refinable_vertices;
2679 double max_ring_at_floor = 0.;
2680 Vector3d worst_ring_at_floor_pos = Vector3d::Zero();
2681 bool rings_ok() const { return max_ring <= bar; }
2682 double avg_ring() const { return n_rings ? sum_ring / double(n_rings) : 0.; }
2687 bool vertices_ok() const { return n_unplaced == 0; }
2688 bool faces_ok() const { return max_face <= bar; }
2714 bool converged() const
2715 {
2716 return (ring_exit ? rings_ok() : faces_ok()) && n_unmeasurable == 0;
2717 }
2723 std::string sizing_floor_fact() const;
2724 double ratio() const { return bar > 0. ? std::max(max_vertex, max_face) / bar : 0.; }
2726 double avg_vertex() const { return n_vertices ? sum_vertex / double(n_vertices) : 0.; }
2727 double avg_face() const { return n_faces ? sum_face / double(n_faces) : 0.; }
2728 };
2729 EnergyCriterion energy_criterion();
2736 double front_chord_target(size_t va, size_t vb, double len, double sag, double bar) const;
2737
2741 size_t refine_front_by_halving(const std::vector<EnergyCriterion::Refinable>& faces);
2746 size_t refine_front_by_halving(const std::vector<size_t>& vertices);
2755 bool settle_refinement_turn(int turn, double& e_prev, bool refine_now);
2756
2761 void grade_sizing(double grade, const std::vector<size_t>& seeds);
2767 size_t grade_sizing_by_distance(const std::vector<size_t>& seeds);
2768
2771 std::optional<EnergyCriterion> m_energy_verdict;
2772
2774 void report_outside_support(const char* when, const DistanceSplit& s) const;
2777 bool band_vertex_is_reachable(const size_t vid) const
2778 {
2779 if (m_vertex_extra[vid].m_is_on_offset && vertex_is_held(vid)) return false;
2780 return !vertex_is_on_domain_boundary(vid);
2781 }
2782
2784 size_t refine_sizing_around_worst(double max_metric) override;
2785
2788 void log_stuck_refine_census(double max_metric, double filter_energy);
2789
2792 void log_refine_block_census(const std::string& when, double filter_energy) const;
2793
2803 bool collapse_quality_allowed(size_t v1, double q, double ring_max) const override
2804 {
2805 // TetWild's rules (m_op_rules): the base's, q <= ring_max for a rounded v1.
2806 return !tetwild_rules() || TetOptimizerMesh::collapse_quality_allowed(v1, q, ring_max);
2807 }
2808
2809 mutable std::atomic<size_t> m_deg_split_created{0};
2810 size_t m_deg_prev_split_created = 0;
2811
2813 void report_needle(const char* op, size_t tid, double parent_q) const;
2814 static constexpr size_t kNeedleReports = 12;
2816 static constexpr double kNeedleQuality = 1e6;
2817 mutable std::atomic<size_t> m_needle_reports{0};
2818
2820 void needle_scan(const char* when) const;
2821
2824 std::set<std::tuple<long, long, long>> m_stuck_prev_cells;
2825 size_t m_stuck_calls = 0;
2826
2832 bool optimization_bare_coarsen_passes() const override { return tetwild_rules(); }
2833
2837 bool coarsen_no_rise() const { return m_offset_params.coarsen_guard == "no_rise"; }
2838
2844 double face_criterion_rel(const Tuple& f) const;
2846 double cell_quality_rel(const size_t tid) const;
2848 double amips_rel_at_face(const Tuple& f) const;
2849
2857 double quality_rel(const size_t tid) const override
2858 {
2860 return std::max(1., std::cbrt(cell_quality(tid)) / m_params.stop_energy);
2861 }
2866 std::atomic<size_t> m_coarsen_reject_amips{0}, m_coarsen_reject_offset{0};
2873 {
2874 double amips3_before = 0.;
2875 std::vector<size_t> v2_only;
2876 std::vector<std::array<size_t, 3>> faces_before;
2877 };
2879
2886 void write_optimization_debug_output(const std::string& path) override
2887 {
2888 // DEBUG_remote_output: no per-operation or per-smoothing-pass frame (remote_frames()).
2889 if (remote_frames() && path.rfind("debug_", 0) == 0) return;
2890 const char ph = m_tetwild_pass ? 'F' : 'S'; // a TetWild pass, or the loop
2891 if (m_round != m_debug_last_round || ph != m_debug_last_tag ||
2892 m_frame_prefix != m_debug_last_prefix) {
2893 m_debug_last_round = m_round;
2894 m_debug_last_tag = ph;
2895 m_debug_last_prefix = m_frame_prefix;
2896 m_debug_pass = 0;
2897 }
2898 std::string label = path;
2899 // The prefix leads, since the loop after the init_optimize loop restarts at turn 1.
2900 const std::string& g = m_frame_prefix;
2901 if (path.rfind("debug_", 0) == 0) {
2902 label = fmt::format(
2903 "{}r{}{}{}{}",
2904 g,
2905 m_round,
2906 ph,
2907 ++m_debug_pass,
2908 m_debug_pass_name.empty() ? std::string() : "_" + m_debug_pass_name);
2909 } else if (path.rfind("end_", 0) == 0) {
2910 label = fmt::format("{}r{}{}_end", g, m_round, ph);
2911 }
2912 write_debug_frame(label);
2913 }
2918 bool stage_frames() const { return m_offset_params.debug_output && !remote_frames(); }
2921 void optimization_iteration_done(const int it) override
2922 {
2923 if (remote_frames()) {
2924 write_debug_frame(fmt::format("{}r{}F_it{}", m_frame_prefix, m_round, it + 1));
2925 }
2926 }
2928 void append_frame_label(size_t idx, const std::string& label) const;
2938 void write_debug_frame(const std::string& label);
2939
2948 void init_from_image(
2949 const MatrixXd& V,
2950 const MatrixXi& T,
2951 const MatrixSi& T_tags,
2952 const MatrixXd& V_env,
2953 const MatrixXi F_env,
2954 const std::vector<std::string>& tag_names,
2955 const std::string& sheet_name = "",
2956 const MatrixXi& F_tagged = MatrixXi(),
2957 const MatrixSi& F_tags = MatrixSi(),
2958 const std::vector<std::string>& face_tag_names = {});
2959
2961 bool ambient_assert();
2962
2964 void label_input_complex();
2965
2967 bool empty_input_complex();
2968
2976
2978 void init_offset_potential();
2979
2985 void init_band_exact();
2986
2990 MatrixXd m_phi_V;
2991 MatrixXi m_phi_E;
2992 MatrixXi m_phi_F;
2996 std::vector<int8_t> m_phi_F_inner;
2997 std::vector<int> m_phi_P;
2998
3000 size_t flood_fill();
3001
3003 bool split_edge_before(const Tuple& t) override;
3004 bool split_edge_after(const Tuple& t) override;
3005 bool split_face_before(const Tuple& t) override;
3006 bool split_face_after(const Tuple& t) override;
3007 bool split_tet_before(const Tuple& t) override;
3008 bool split_tet_after(const Tuple& t) override;
3009 bool invariants(const std::vector<Tuple>& tets) override;
3011
3015 void construct_offset(const std::filesystem::path& output_file);
3016
3023 void marching_tets();
3024
3029 void repulsion_smoothing();
3032 void relabel_input_complex();
3033
3035 bool is_simplicially_embedded() const;
3036 bool tet_is_simp_emb(const Tuple& t) const;
3037 void simplicial_embedding();
3039
3041 void set_offset_tet_tags();
3042
3044 bool offset_is_manifold();
3045
3049 void write_phi_grid(const std::string& path, int n) const;
3050
3051 void write_input_complex(const std::string& path);
3053 void write_vtu(const std::string& path, MeshMap* output = nullptr);
3062 {
3063 std::string path;
3064 MatrixXd V;
3065 MatrixXi T, F_in, F_off, E;
3066 // per cell
3067 VectorXd amips, tet_e, amips_term, band_term, band_gap, plastic;
3068 std::vector<MatrixXd> tags;
3069 std::vector<std::string> tag_names;
3070 // per vertex
3071 VectorXd labels, order, vid, sizing;
3072 VectorXd in_dist, dist_ratio, front_err, front_change;
3073 // the _off file's: per offset face, and the ring measure per vertex
3074 VectorXd f_err, f_len, f_inside, f_outside, f_fold, f_field_fold, f_field_angle, f_dihedral,
3075 f_fold_dihedral, ring;
3076 // the _stencil file's: one point per stencil point of every offset face (S, a vertex
3077 // cell each in S_cells), and its fields
3078 MatrixXd S;
3079 MatrixXi S_cells;
3080 VectorXd s_err, s_over, s_signed, s_dist_ratio, s_sq, s_face, s_point;
3081 };
3085 VtuFrame gather_vtu_frame(const std::string& path);
3087 static void write_vtu_frame(const VtuFrame& frame, MeshMap* output = nullptr);
3089
3090private:
3097 {
3098 size_t v1_id;
3099 size_t v2_id;
3100 Vector3d new_v_pos;
3101 VertexExtra new_v_extra;
3102
3103 bool is_edge_on_region = false;
3104 bool is_edge_on_offset = false;
3105 bool is_edge_open_boundary = false;
3106
3107 std::vector<std::pair<FaceAttributes, std::array<size_t, 3>>> changed_faces;
3108
3109 // cache edge attributes
3110 EdgeAttributes split_e;
3111 std::map<size_t, EdgeAttributes> internal_e;
3112 std::map<simplex::Edge, EdgeAttributes> external_e; // edge is boundary edge (not link)
3113 std::map<simplex::Edge, EdgeAttributes> link_e; // link edge around splitted edge
3114
3115 // cache face attributes
3116 std::map<size_t, FaceSnapshot> split_f; // splitted faces
3117 std::map<simplex::Edge, FaceSnapshot> internal_f; // new faces created by split
3118 std::map<std::pair<simplex::Edge, size_t>, FaceSnapshot>
3119 external_f; // closed star boundary faces of splitted edge
3120
3121 // cache tet attributes
3122 std::map<simplex::Edge, TetAttributes> tets;
3123 };
3125
3127 {
3128 size_t v1_id;
3129 size_t v2_id;
3130 size_t v3_id;
3131 std::map<simplex::Edge, EdgeAttributes> existing_e;
3132 std::map<simplex::Face, FaceSnapshot> existing_f;
3133 int splitf_label;
3134 std::map<size_t, TetAttributes> tets;
3135 };
3137
3139 {
3140 std::array<size_t, 4> v_ids;
3141 std::map<simplex::Edge, EdgeAttributes> existing_e;
3142 std::map<simplex::Face, FaceSnapshot> existing_f;
3143 TetAttributes tet;
3144 };
3146
3157 bool swap_capture_tag(const std::vector<size_t>& tids);
3162
3175 {
3176 std::map<size_t, std::pair<CellTag, int>> by_vertex;
3180 std::array<size_t, 4> abcd{};
3184 bool worthwhile = false;
3188 double case0_energy = 0.0;
3193 bool saw_case = false;
3195 int kind = 0;
3196 };
3197 bool swap_capture_surface_sides(
3198 const std::vector<size_t>& tids,
3199 size_t a,
3200 size_t b,
3201 size_t c,
3202 size_t d);
3214 mutable std::atomic<int> m_plastic_tie_swap{0}, m_plastic_tie_collapse{0};
3226 {
3232 bool scored = false;
3233 double scored_energy = std::numeric_limits<double>::max();
3236 bool flip = false;
3239 bool band_cancels = false;
3241 std::vector<std::array<size_t, 4>> old_band;
3242 void clear()
3243 {
3244 flip = false;
3245 band_cancels = false;
3246 old_band.clear();
3247 scored = false;
3248 scored_energy = std::numeric_limits<double>::max();
3249 }
3250 };
3254 void swap_record_fill(bool flip);
3257 void swap_scoring_check(const std::vector<size_t>& tids, double after);
3258
3259public:
3260 // substructure functions
3261
3262 bool is_order_2_edge(const Tuple& e) const;
3263 bool is_order_2_edge(const std::array<size_t, 2>& e) const;
3264
3265 bool vertex_is_on_surface(const size_t vid) const override;
3266
3267 bool face_is_on_surface(const size_t fid) const override;
3268
3269 size_t get_order_of_vertex(const size_t vid) const override;
3271 void init_vertex_order();
3272
3273private: // helpers
3275 void sort_edges_by_length(std::vector<simplex::Edge>& edges)
3276 {
3277 std::sort(
3278 edges.begin(),
3279 edges.end(),
3280 [this](const simplex::Edge& e1, const simplex::Edge& e2) {
3281 double len1 = (m_vertex_attribute[e1.vertices()[0]].m_posf -
3282 m_vertex_attribute[e1.vertices()[1]].m_posf)
3283 .norm();
3284 double len2 = (m_vertex_attribute[e2.vertices()[0]].m_posf -
3285 m_vertex_attribute[e2.vertices()[1]].m_posf)
3286 .norm();
3287 return len1 > len2;
3288 });
3289 }
3290
3291public: // helpers
3293 std::vector<size_t> connected_components_helper(const size_t& v_id)
3294 {
3295 auto onering_v_ids = get_one_ring_vids_for_vertex(v_id);
3296 std::vector<size_t> ret_v_ids;
3297 for (const size_t& other_v_id : onering_v_ids) {
3298 size_t e_id = tuple_from_edge({{v_id, other_v_id}}).eid(*this);
3299 if (m_edge_attribute[e_id].label != 0) { // edge labelled 1 or 2
3300 ret_v_ids.push_back(other_v_id);
3301 }
3302 }
3303 return ret_v_ids;
3304 }
3305};
3306
3307
3308} // namespace wmtk::components::topological_offset
Definition SurfaceTagAttributes.h:24
a Tuple refers to a global vid and a global tet id, and a local edge id and local face id
Definition TetMesh.h:60
std::array< size_t, 4 > oriented_tet_vids(const Tuple &t) const
Definition TetMesh.cpp:797
std::optional< std::tuple< Tuple, size_t > > try_tuple_from_face(const std::array< size_t, 3 > &vids) const
tuple_from_face for callers where a missing face is an answer, not a bug.
Definition TetMesh.cpp:540
std::vector< size_t > get_one_ring_vids_for_vertex(size_t vid, std::vector< size_t > &cache)
Get the one ring vids for vertex.
Definition TetMesh.cpp:910
const std::vector< size_t > & get_one_ring_tids_for_vertex(const Tuple &t) const
Get the one ring tids for vertex.
Definition TetMesh.cpp:838
Tuple tuple_from_edge(size_t tid, int local_eid) const
get a Tuple from global tetra index and local edge index (from 0-5).
Definition TetMesh.cpp:452
What tetwild and simwild's 3D mesh share.
Definition TetOptimizerMesh.h:51
AttributeContainerGroup m_vertex_attr_group
What p_vertex_attrs points at, so a derived class can register more.
Definition TetOptimizerMesh.h:158
std::string m_debug_pass_name
Definition TetOptimizerMesh.h:661
virtual double smoothing_ring_energy(const std::vector< Tuple > &ring, const bool stored) const
The number the shared smoother's projected step compares for a whole one-ring: a vertex held on a sur...
Definition TetOptimizerMesh.h:951
virtual double quality_rel(const size_t tid) const
A cell's quality relative to the quality it is required to reach; <= 1 means it meets it.
Definition TetOptimizerMesh.h:538
AttributeContainerGroup m_face_attr_group
What p_face_attrs points at, so a derived class can register more.
Definition TetOptimizerMesh.h:173
static constexpr double MAX_ENERGY
The sentinel get_quality returns for an element AMIPS cannot score.
Definition TetOptimizerMesh.h:421
void add_edge_attributes(AbstractAttributeContainer *c)
Register an edge collection with m_edge_attr_group and point p_edge_attrs at the group.
Definition TetOptimizerMesh.h:235
virtual bool swap_quality_allowed(double after, double before, bool) const
Definition TetOptimizerMesh.h:1305
OptimizerParameters & m_params
Definition TetOptimizerMesh.h:425
std::shared_ptr< Envelope > m_envelope
Surface envelope: what a surface vertex is pulled toward and checked against.
Definition TetOptimizerMesh.h:428
Definition TopoOffsetTetMesh.h:113
bool on_sheet
Definition TopoOffsetTetMesh.h:120
void merge(const FaceExtra &o)
Definition TopoOffsetTetMesh.h:124
The offset potential: the scalar field on space whose level set the front is placed on.
Definition OffsetPotential.hpp:42
Definition TopoOffsetTetMesh.h:133
bool rest_valid
Definition TopoOffsetTetMesh.h:146
The offset's tet mesh, on the shared 3D optimizer.
Definition TopoOffsetTetMesh.h:164
void stamp_plastic_rests()
Definition Optimize3d.cpp:297
wmtk::threading::enumerable_thread_specific< double > m_collapse_parent_flatness
The flattest tet in the collapse's ring before it ran, for record_flatness().
Definition TopoOffsetTetMesh.h:2114
int64_t m_sheet_tag
Definition TopoOffsetTetMesh.h:181
double smoothing_ring_energy(const std::vector< Tuple > &ring, const bool stored) const override
What the shared smoother's projected step compares for a vertex held on a surface: the sum of tet_ene...
Definition TopoOffsetTetMesh.h:989
static constexpr int kSweptHalvings
Definition TopoOffsetTetMesh.h:955
std::vector< int8_t > m_phi_F_inner
Definition TopoOffsetTetMesh.h:2996
std::shared_ptr< Envelope > m_input_complex_envelope
The input complex as loaded, as an envelope of its primitives (the rows of m_phi_F,...
Definition TopoOffsetTetMesh.h:263
std::shared_ptr< const OffsetPotential3D > m_repulsion_potential
Definition TopoOffsetTetMesh.h:1898
int stencil_points_per_face() const
Definition TopoOffsetTetMesh.h:2480
void swap_scoring_check(const std::vector< size_t > &tids, double after)
Definition Optimize3d.cpp:1276
bool vertex_is_on_domain_boundary(const size_t vid) const
Identification only – no operation refuses the domain wall through these.
Definition TopoOffsetTetMesh.h:1781
optimization::NewtonCounters m_newton_repulsion
Definition TopoOffsetTetMesh.h:1901
std::string m_frame_prefix
Definition TopoOffsetTetMesh.h:573
bool m_init_optimize
Definition TopoOffsetTetMesh.h:569
bool split_edge_after(const Tuple &t) override
This function computes the attributes for the added simplices. User specified modifications and desid...
Definition EdgeSplittingTet.cpp:260
void report_offset_face_lookup_misses(const char *when) const
Definition Optimize3d.cpp:4890
std::future< DebugTimeline > m_debug_frame_write
Definition TopoOffsetTetMesh.h:1560
const std::vector< std::array< double, 3 > > & face_stencil() const
Definition TopoOffsetTetMesh.h:2456
double swap_edge_56_energy(const std::vector< std::array< size_t, 4 > > &tets, const int op_case) override
User specified energy to decide which of the 5 possible orientations should be chosen.
Definition Optimize3d.cpp:1149
std::atomic< int > iter_cnt_collapse_offset_reject
Operations refused because they would have left an offset-surface face over tolerance.
Definition TopoOffsetTetMesh.h:1591
MatrixXd m_phi_V
Definition TopoOffsetTetMesh.h:2990
size_t m_worst_dist_vid
Definition TopoOffsetTetMesh.h:1356
void swap_record_fill(bool flip)
Definition Optimize3d.cpp:621
std::shared_ptr< OffsetEnergy3D > repulsion_energy(const size_t vid) const
O(v) for vid's smoothing objective, charged once per carrying cell.
Definition TopoOffsetTetMesh.h:1968
std::shared_ptr< Envelope > m_offset_envelope
The envelope the offset surface may not leave during a TetWild pass (tetwild_pass()),...
Definition TopoOffsetTetMesh.h:476
std::pair< double, double > energy_parts() const
Definition Optimize3d.cpp:353
size_t grade_sizing_by_distance(const std::vector< size_t > &seeds)
Definition Optimize3d.cpp:4087
double ring_measure_at(size_t vid) const
One vertex's ring measure, from its own live offset faces (NaN if it has none).
Definition Optimize3d.cpp:2166
bool front_face_is_foldover(size_t a, size_t b, size_t c) const
front_face_field_angle() > foldover_angle; an unmeasurable face is not a foldover.
Definition TopoOffsetTetMesh.h:1494
std::shared_ptr< const OffsetPotential3D > potential_ptr_for(const size_t vid) const
Definition TopoOffsetTetMesh.h:318
double settle_metric() const
Definition TopoOffsetTetMesh.h:1204
bool repulsion_embedding_kept(const std::vector< size_t > &tids) const
Definition TopoOffsetTetMesh.cpp:1926
void smoothing_pass_done() override
The engine's end-of-pass hook (TetOptimizerMesh::smooth_all_vertices() calls it after EVERY smoothing...
Definition TopoOffsetTetMesh.h:2008
std::shared_ptr< const ExactBandIntegral > m_band_exact
Definition TopoOffsetTetMesh.h:1117
wmtk::threading::enumerable_thread_specific< double > m_split_energy_before
Definition TopoOffsetTetMesh.h:1150
int m_debug_pass
Definition TopoOffsetTetMesh.h:1574
std::optional< EnergyCriterion > m_energy_verdict
Definition TopoOffsetTetMesh.h:2771
void record_flatness(const char *op, double parent_flat, size_t child_tid) const
Genesis: flatness transitions recorded at the operation hooks. {op, parent, child}.
Definition Optimize3d.cpp:2840
std::atomic< long long > funnel_cases
Definition TopoOffsetTetMesh.h:1630
bool m_pocket_splitting
Set while front_pocket_split() splits: split_edge_before() skips the longest-edge order.
Definition TopoOffsetTetMesh.h:561
double band_cell_corner_bound(const std::array< size_t, 4 > &vids, int64_t &best) const
band_cell_term()'s value, with its minimising triangle in best.
Definition TopoOffsetTetMesh.h:1104
FaceSamples offset_face_samples(const Tuple &f) const
Definition Optimize3d.cpp:3024
static double growth_stage_target(double d0, double delta, int k, int n)
Definition Optimize3d.cpp:6884
bool cell_is_offset_band(const size_t tid) const
Whether tet tid is part of the offset BAND (as opposed to the input complex).
Definition TopoOffsetTetMesh.h:1293
void report_outside_support(const char *when, const DistanceSplit &s) const
Turn a residual_split()'s outside-support tally into the hard error.
Definition Optimize3d.cpp:4198
wmtk::threading::enumerable_thread_specific< double > m_collapse_survivor_sizing
Definition TopoOffsetTetMesh.h:2117
void sort_edges_by_length(std::vector< simplex::Edge > &edges)
sort edge simplices in place by decreasing edge length
Definition TopoOffsetTetMesh.h:3275
wmtk::threading::enumerable_thread_specific< int > m_swap_label
The construction label shared by every cell of an interior swap's ring, captured alongside.
Definition TopoOffsetTetMesh.h:3161
double global_energy() const
Definition Optimize3d.cpp:326
void restore_snapshot(const MeshSnapshot &s)
Definition Optimize3d.cpp:6867
void write_debug_frame(const std::string &label)
One frame of the run's single debug timeline: <output>_NNNNN.vtu with the next sequence number,...
Definition Optimize3d.cpp:5170
std::vector< int > m_cell_region
per tet: band's region, -1 none, -2 reached from two
Definition TopoOffsetTetMesh.h:289
double offset_energy() const
Definition Optimize3d.cpp:337
bool smooth_nonfront_vertex(const Tuple &t)
Every vertex off the front: the shared smoother with TetWild's options (TetOptimizerMesh::smooth_afte...
Definition Optimize3d.cpp:944
void for_each_offset_face_sample(const Tuple &f, Visit &&visit) const
The same lattice over a face the mesh carries. Visitor signature as above.
Definition TopoOffsetTetMesh.h:2564
BandVolumeEnergy3D::SweptDerivCounts m_swept_derivs
Definition TopoOffsetTetMesh.h:970
wmtk::threading::enumerable_thread_specific< double > m_swap_energy_before
The swap energy rule's before half, per thread: the sum of tet_energy() over the cells the swap repla...
Definition TopoOffsetTetMesh.h:3211
double m_growth_start
Definition TopoOffsetTetMesh.h:1715
size_t get_order_of_vertex(const size_t vid) const override
Get the order of a vertex.
Definition TopoOffsetTetMesh.cpp:1400
std::atomic< long long > m_swap_scoring_checked
Definition TopoOffsetTetMesh.h:1597
size_t flood_fill()
label connected simplicial complex components (simplices labelled 1 or 2)
Definition TopoOffsetTetMesh.cpp:1346
void grade_sizing(double grade, const std::vector< size_t > &seeds)
Definition Optimize3d.cpp:4077
double m_construction_envelope_eps
Definition TopoOffsetTetMesh.h:395
std::vector< int64_t > m_phi_vert_region
per m_phi_V row: region index
Definition TopoOffsetTetMesh.h:285
VtuFrame gather_vtu_frame(const std::string &path)
Definition TopoOffsetTetMesh.cpp:2349
bool offset_face_normal_out_of_band(const Tuple &f, Vector3d &n) const
Definition Optimize3d.cpp:4517
void linear_growth()
Definition Optimize3d.cpp:6893
void relabel_input_complex()
Definition TopoOffsetTetMesh.cpp:1624
double input_complex_distance(const Vector3d &p) const
The distance from p to the input complex as loaded: 0 inside its solid (its label-1 cells as init_inp...
Definition TopoOffsetTetMesh.cpp:1118
double surface_integral_raw() const
Definition Optimize3d.cpp:4758
bool stage_frames() const
Definition TopoOffsetTetMesh.h:2918
bool split_after_cells(size_t v1, size_t v2, size_t v_new, const std::vector< Tuple > &children) override
Restore application cell data on the children made by a split.
Definition EdgeSplittingTet.cpp:812
std::vector< bool > band_vertex_mask() const
Which vertices lie on the band's outer surface. Shared by every measurement.
Definition Optimize3d.cpp:3003
std::atomic< size_t > m_cross_own
Definition TopoOffsetTetMesh.h:2064
void build_final_envelopes()
Definition TopoOffsetTetMesh.cpp:387
void label_offset_boundary()
Tag the two tracked surfaces for the optimization phase.
Definition Optimize3d.cpp:131
std::array< std::atomic< long long >, 3 > funnel_kind
offered, split by swap kind: [0] = 3-2, [1] = 4-4, [2] = 5-6.
Definition TopoOffsetTetMesh.h:1627
bool cell_in_region(const size_t tid) const
Definition TopoOffsetTetMesh.h:1285
std::atomic< size_t > m_coarsen_reject_amips
Definition TopoOffsetTetMesh.h:2866
bool collapse_edge_before(const Tuple &t) override
Reject any collapse that violates the substructure link condition, remember the survivor's sizing for...
Definition Optimize3d.cpp:1450
static bool & split_off_longest()
Definition TopoOffsetTetMesh.h:1656
void marching_tets()
Definition TopoOffsetTetMesh.cpp:1943
bool smooth_before(const Tuple &t) override
The smoothing hooks. A derived class overriding them – or invariants(), which a smooth also runs – mu...
Definition Optimize3d.cpp:1905
bool split_edge_before(const Tuple &t) override
User specified preparations and desideratas for an edge split before changing the connectivity.
Definition EdgeSplittingTet.cpp:13
std::shared_ptr< OffsetPotential3D > m_offset_potential
The smooth offset potential, and with it the definition of the offset itself.
Definition TopoOffsetTetMesh.h:230
void init_offset_potential()
Build the smooth offset potential from the extraction init_input_complex_bvh() kept.
Definition TopoOffsetTetMesh.cpp:1202
static constexpr int kGradBins
Definition TopoOffsetTetMesh.h:1993
void set_offset_tet_tags()
update 'tags' data for tets in the offset region (tets labelled 2)
Definition TopoOffsetTetMesh.cpp:2173
bool split_tet_after(const Tuple &t) override
Compute the attributes for the added simplices.
Definition EdgeSplittingTet.cpp:603
wmtk::threading::enumerable_thread_specific< size_t > m_smoothing_vid
Definition TopoOffsetTetMesh.h:1214
std::shared_ptr< polysolve::nonlinear::Problem > front_energy(size_t vid) const
Definition FrontSmooth3d.cpp:310
double tetwild_pass(const char *what, int max_its, bool coarsen=false)
Definition Optimize3d.cpp:6692
bool vertex_is_held(size_t vid) const
Definition TopoOffsetTetMesh.cpp:327
bool face_is_on_surface(const size_t fid) const override
Is a face part of the substructure.
Definition Optimize3d.cpp:126
bool ambient_assert()
check that the ambient tag does not overlap with any other tags
Definition TopoOffsetTetMesh.cpp:445
double front_face_angle_to_field(size_t a, size_t b, size_t c, const Vector3d &n) const
Definition Optimize3d.cpp:4961
bool band_term_cancels(const std::vector< size_t > &tids) const
Definition TopoOffsetTetMesh.h:857
void log_front_profile(size_t vid)
Diagnostic: the front objective of one vertex along its normal, offset term vs total.
Definition Optimize3d.cpp:3586
std::vector< Tuple > offset_surface_faces_live_at(size_t vid) const
The live offset-surface faces incident to vid.
Definition Optimize3d.cpp:4603
MatrixXd m_sheet_V
The surface group as loaded, kept because the classification below is redone on demand.
Definition TopoOffsetTetMesh.h:190
bool smooth_front_vertex(const Tuple &t)
Placement of a front vertex: the shared smoother with the offset's options. See FrontSmooth3d....
Definition FrontSmooth3d.cpp:25
int64_t front_face_band_cell(size_t a, size_t b, size_t c) const
The band cell of front face (a, b, c), -1 if neither side is band.
Definition Optimize3d.cpp:3458
double amips_rel_at_face(const Tuple &f) const
... and the worst of the (up to two) cells a face separates.
Definition Optimize3d.cpp:4272
double max_band_vertex_distance() const
Definition TopoOffsetTetMesh.cpp:1419
std::atomic< int > m_plastic_tie_swap
Definition TopoOffsetTetMesh.h:3214
void build_envelopes()
Definition TopoOffsetTetMesh.cpp:345
static constexpr int kEnergyDecreasePasses
EXPERIMENTAL_label_cleanup "energy_decrease": the most passes one clean-up runs.
Definition TopoOffsetTetMesh.h:558
bool simplex_in_input_complex(const std::vector< size_t > &c) const
Definition TopoOffsetTetMesh.cpp:1890
void mark_input_complex_vertices()
Definition TopoOffsetTetMesh.cpp:430
bool face_is_held(const Tuple &f) const
THE ENVELOPES. One, m_envelope (the base's pointer): an Envelope of half-width envelope_size around e...
Definition TopoOffsetTetMesh.cpp:316
std::atomic< int > iter_cnt_split_offset_before
Definition TopoOffsetTetMesh.h:1647
double surface_term_total() const
... over every live offset face.
Definition Optimize3d.cpp:4743
static constexpr int INPUT_SURFACE_CLASS
SurfaceTagAttributes::m_surface_class: which of the two tracked surfaces a face belongs to....
Definition TopoOffsetTetMesh.h:658
size_t repulsion_cells_at(const size_t vid) const
Definition TopoOffsetTetMesh.h:1954
bool cell_is_input_complex(const size_t tid) const
Whether tet tid is part of the INPUT complex the band wraps.
Definition TopoOffsetTetMesh.h:1291
wmtk::threading::enumerable_thread_specific< std::vector< size_t > > m_collapse_edge_link
The link of the collapsed edge, captured in collapse_before_vertex().
Definition TopoOffsetTetMesh.h:2157
void check_offset_membership(const char *when) const
Definition Optimize3d.cpp:4869
std::vector< std::shared_ptr< Envelope > > m_region_envelopes
Definition TopoOffsetTetMesh.h:284
wmtk::threading::enumerable_thread_specific< std::pair< double, Vector3d > > m_needle_pre
Why smoothing does not repair a sliver in its one-ring. Same counters as 2D: offered / reached / fixe...
Definition TopoOffsetTetMesh.h:2081
bool marching_split_edge_after(const Tuple &t)
Definition EdgeSplittingTet.cpp:284
void folded_front_faces_at(size_t vid, std::set< std::array< size_t, 3 > > &out) const
Definition Optimize3d.cpp:4690
bool vertex_has_live_offset_face(size_t vid) const
Definition Optimize3d.cpp:4682
bool face_is_offset(const size_t fid) const
Definition TopoOffsetTetMesh.h:1241
bool split_face_before(const Tuple &t) override
User specified preparations and desideratas for a face split before changing the connectivity.
Definition EdgeSplittingTet.cpp:397
const OffsetPotential3D & potential_for_face(const Tuple &f) const
The field of the band cell a live offset face belongs to.
Definition Optimize3d.cpp:5007
std::shared_ptr< polysolve::nonlinear::Problem > band_volume_energy_at(size_t vid) const
Definition FrontSmooth3d.cpp:348
bool m_tetwild_pass
Definition TopoOffsetTetMesh.h:438
Parameters & m_offset_params
The base holds only wmtk::OptimizerParameters; this is the same object, typed.
Definition TopoOffsetTetMesh.h:662
void log_energy_step(const char *step) const
Log-only: "[energy step] turn N <step>: E | AMIPS term | band term", E = the sum of tet_energy() over...
Definition Optimize3d.cpp:394
bool m_quality_converged
Definition TopoOffsetTetMesh.h:1585
double front_face_field_angle(size_t a, size_t b, size_t c) const
The foldover measure of front face (a, b, c): the angle, in degrees, between its unit normal,...
Definition Optimize3d.cpp:4936
polysolve::nonlinear::Solver & smoothing_solver()
Definition Optimize3d.cpp:1931
double swap_swept_change(const std::vector< std::array< size_t, 4 > > &new_band, bool flip)
Definition Optimize3d.cpp:536
bool swap_face_before(const Tuple &t) override
The face (2-3) swap's before-hook: TetOptimizerMesh::swap_face_before() with its AMIPS^3 gate replace...
Definition Optimize3d.cpp:1158
bool offset_is_manifold()
verify that the closed offset region (simplices labelled 1 or 2) form a manifold region.
Definition TopoOffsetTetMesh.cpp:2207
void audit_surface_containment(const std::string &when) const
Which held faces are outside m_envelope, and by how much. Diagnostic only.
Definition Optimize3d.cpp:2031
void front_pocket_split(const std::string &where)
EXPERIMENTAL_front_pocket_split. Invariant: no cell is PINNED when a smoothing pass of the loop start...
Definition Optimize3d.cpp:5307
void smart_growth()
Definition Optimize3d.cpp:6977
double front_vertex_conv_ratio(size_t vid) const
Definition Optimize3d.cpp:3437
void log_refine_block_census(const std::string &when, double filter_energy) const
Definition Optimize3d.cpp:2336
double candidate_energy(const std::array< size_t, 4 > &vids)
Definition Optimize3d.cpp:597
void write_optimization_debug_output(const std::string &path) override
Put the optimization's frames on the run's single debug timeline (see write_debug_frame()),...
Definition TopoOffsetTetMesh.h:2886
double m_input_complex_pad
Definition TopoOffsetTetMesh.h:266
bool all_plastic(const std::vector< size_t > &tids) const
Whether every cell of tids is plastic (cell_is_plastic()). False for an empty set.
Definition Optimize3d.cpp:304
double band_cell_term(const std::array< size_t, 4 > &vids) const
b(t), one band cell's part of int_B (d - delta) / delta dV, as an upper bound:
Definition TopoOffsetTetMesh.h:1080
bool swept_rule() const
"exact_swept" (EXACT SWEPT CHANGE).
Definition TopoOffsetTetMesh.h:918
void init_vertex_order()
Compute the vertex order for every vertex.
Definition TopoOffsetTetMesh.cpp:1405
void needle_forensics() const
The full post-mortem on why nothing removes the flat cells; see the 2D twin.
Definition Optimize3d.cpp:2890
int m_energy_step_turn
Definition TopoOffsetTetMesh.h:2048
bool vertex_carries_offset_term(const size_t vid) const
Definition TopoOffsetTetMesh.h:2378
static constexpr size_t kDiagnosticChunk
Definition TopoOffsetTetMesh.h:2045
double surface_term_at(size_t vid) const
surface_face_term() summed over the live offset faces through vid.
Definition Optimize3d.cpp:4733
void append_frame_label(size_t idx, const std::string &label) const
One line of <output>_frames.txt; truncates the file on the first frame.
Definition Optimize3d.cpp:5162
double band_vertex_distance_error(const size_t vid) const
|dist(vid, input complex) - target_distance|. Diagnostic: the Euclidean offset.
Definition Optimize3d.cpp:3013
void optimize_construction_pass()
optimize_construction: TetWild on the mesh as constructed, before the loop (and before an init_optimi...
Definition Optimize3d.cpp:6765
double front_chord_target(size_t va, size_t vb, double len, double sag, double bar) const
Definition Optimize3d.cpp:3619
std::atomic< long long > funnel_offered
[flip funnel]: of the flips of the offset surface, how many survive each stage. Reset per turn and re...
Definition TopoOffsetTetMesh.h:1625
double tet_energy(size_t tid, bool band_term=true) const
THE per-tet energy E(t), read from the mesh as it is – the cell's corners and its label,...
Definition Optimize3d.cpp:255
double face_offset_term(size_t a, size_t b, size_t c) const
THE ONE FACE FUNCTION: the mean over the face's stencil of r^2, r = relative_residual(q) / front_conv...
Definition Optimize3d.cpp:246
std::atomic< long > m_split_order_waits
Definition TopoOffsetTetMesh.h:1652
bool fold_reject_active() const
Definition TopoOffsetTetMesh.h:1513
bool is_edge_on_region(const Tuple &loc)
Definition Optimize3d.cpp:65
double tet_amips(const size_t tid) const
AMIPS of one tet, the cube root of cell_quality(); the number every log line reports.
Definition TopoOffsetTetMesh.h:2092
std::shared_ptr< polysolve::nonlinear::Problem > amips3_energy(size_t vid) const
Definition Optimize3d.cpp:895
std::atomic< int > m_fold_reject_collapse
Definition TopoOffsetTetMesh.h:1533
bool band_vertex_is_reachable(const size_t vid) const
Definition TopoOffsetTetMesh.h:2777
std::shared_ptr< Envelope > smoothing_energy_envelope(const size_t vid) const override
Definition TopoOffsetTetMesh.h:2258
void band_swept_faces(size_t vid, const Vector3d &x0, std::vector< std::array< Vector3d, 2 > > &faces) const
Definition Optimize3d.cpp:427
std::vector< std::shared_ptr< OffsetPotential3D > > m_region_potentials
Definition TopoOffsetTetMesh.h:279
size_t count_real_folds(std::vector< char > *corners=nullptr, std::vector< std::array< size_t, 3 > > *faces=nullptr) const
Definition Optimize3d.cpp:4538
void write_vtu(const std::string &path, MeshMap *output=nullptr)
<path>{,_surf,_off,_edge}.vtu, now: write_vtu_frame(gather_vtu_frame(path)).
Definition TopoOffsetTetMesh.cpp:2343
void set_vertex_position(const size_t vid, const Vector3d &p)
Place a vertex, keeping its exact and rounded coordinates in step.
Definition TopoOffsetTetMesh.h:1231
void count_damped_move(size_t vid, const Vector3d &x0, double max_step)
Definition Optimize3d.cpp:1071
std::shared_ptr< polysolve::nonlinear::Problem > smoothing_extra_energy(const size_t vid) const override
Definition TopoOffsetTetMesh.h:2393
std::vector< size_t > connected_components_helper(const size_t &v_id)
all one-ring vertices through input simplices (labelled 1 or 2)
Definition TopoOffsetTetMesh.h:3293
void update_attributes() override
Definition Optimize3d.cpp:2241
static bool dihedral_is_foldover(const double dihedral, const double angle)
Definition TopoOffsetTetMesh.h:1489
void init_band_exact()
Definition TopoOffsetTetMesh.cpp:1127
double front_vertex_relative_residual(size_t vid) const
Definition Optimize3d.cpp:3472
bool for_each_live_offset_face_at(size_t vid, F &&fn) const
Definition Optimize3d.cpp:4628
bool collapse_before_vertex(size_t v1, size_t v2, double edge_length) override
Definition Optimize3d.cpp:1486
bool smooth_repulsion_vertex(const Tuple &t)
A repulsion vertex in the passes and rounds before the march (repulsion_smoothing()): the shared smoo...
Definition FrontSmooth3d.cpp:176
std::vector< int > m_vertex_region
per vertex: region of its band cells, -1 / -2 as above
Definition TopoOffsetTetMesh.h:290
std::vector< Eigen::Vector3d > m_held_verts
The held triangle soup as build_envelopes() captured it, for build_final_envelopes().
Definition TopoOffsetTetMesh.h:427
std::shared_ptr< polysolve::nonlinear::Problem > front_objective(size_t vid) const
Definition FrontSmooth3d.cpp:298
double repulsion_cell_term(const std::array< size_t, 4 > &vids) const
Definition TopoOffsetTetMesh.h:1938
static void write_vtu_frame(const VtuFrame &frame, MeshMap *output=nullptr)
The files' part: paraviewo encodes and writes frame, nothing else is read.
Definition TopoOffsetTetMesh.cpp:2938
Vector3d front_vertex_move_direction(size_t vid) const
Definition FrontSmooth3d.cpp:231
double band_amips_weight() const
Definition TopoOffsetTetMesh.h:1120
void edge_label_assign(const std::vector< size_t > &tids, const EdgeLabelMap &captured)
Every edge of tids: its captured label when it was captured, else 0.
Definition EdgeSplittingTet.cpp:722
void face_extra_capture(const std::vector< size_t > &tids, FaceExtraMap &out) const
The faces of tids, by sorted vertices, first value kept (as face_attribute_tracker()).
Definition EdgeSplittingTet.cpp:669
void log_smoothing_pass_accounting() override
Definition Optimize3d.cpp:2253
const std::vector< double > * face_stencil_weights() const
Definition TopoOffsetTetMesh.h:2468
void warn_if_offset_reaches_domain_boundary() const
Warn if the offset band has grown into the domain boundary.
Definition Optimize3d.cpp:862
bool swap_quality_allowed(double after, double before, bool is_surface_flip) const override
In the loop (m_op_rules Loop) admits every swap; under TetWild's rules, the base's....
Definition TopoOffsetTetMesh.h:2204
void close_labels(const std::vector< size_t > &tids)
Definition EdgeSplittingTet.cpp:737
void stamp_rest_cell(size_t tid)
Definition Optimize3d.cpp:287
double band_vertex_residual(const size_t vid) const
How far vid is from the level set Phi = c, as a length.
Definition Optimize3d.cpp:3019
double face_stencil_max_sq(const std::array< size_t, 3 > &f) const
Definition Optimize3d.cpp:4804
double m_construction_distance
Definition TopoOffsetTetMesh.h:1710
std::set< std::tuple< long, long, long > > m_stuck_prev_cells
Definition TopoOffsetTetMesh.h:2824
bool is_marched_edge(const size_t a, const size_t b) const
The edges marching_tets() splits: exactly one end in the input complex (label != 0).
Definition TopoOffsetTetMesh.h:1904
std::pair< size_t, size_t > offset_membership_mismatches() const
Definition Optimize3d.cpp:4856
void refresh_offset_membership(size_t vid)
Re-derive m_is_on_offset for one vertex from the cell labels, exactly.
Definition Optimize3d.cpp:4851
wmtk::threading::enumerable_thread_specific< EdgeLabelMap > m_edge_label_split
The split's, the swap's and the collapse's edge capture, per thread.
Definition TopoOffsetTetMesh.h:721
bool optimization_bare_coarsen_passes() const override
Definition TopoOffsetTetMesh.h:2832
bool optimize_offset_loop(bool final_stage=true, bool refine=true, const std::string &label=std::string(), int max_turns=0, bool fold_free=false)
Definition Optimize3d.cpp:6079
bool collapse_after_connectivity(size_t v1, size_t v2, const std::vector< std::array< size_t, 2 > > &boundary_edges) override
Definition Optimize3d.cpp:1668
bool front_face_out_normal(size_t a, size_t b, size_t c, Vector3d &n) const
Definition Optimize3d.cpp:4905
void assign_band_regions(bool log=true)
Definition Optimize3d.cpp:3487
double surface_face_term(size_t a, size_t b, size_t c) const
Definition Optimize3d.cpp:4717
std::vector< int64_t > m_phi_seg_region
per m_phi_E row: region index, -1 unknown
Definition TopoOffsetTetMesh.h:286
bool settle_refinement_turn(int turn, double &e_prev, bool refine_now)
Definition Optimize3d.cpp:3688
bool front_face_out_normal_in(size_t a, size_t b, size_t c, size_t band_tid, Vector3d &n) const
Definition Optimize3d.cpp:4916
double front_move_alignment(size_t vid) const
Definition FrontSmooth3d.cpp:217
void init_input_complex_bvh()
Build the input complex's BVH and keep the extraction the potential needs, and number the complex's c...
Definition TopoOffsetTetMesh.cpp:770
void label_cleanup(const std::string &where)
EXPERIMENTAL_label_cleanup (see the spec). The one exception to labels being fixed after construction...
Definition Optimize3d.cpp:5624
double offset_term_weight() const
Definition TopoOffsetTetMesh.h:1218
double quality_rel(const size_t tid) const override
Definition TopoOffsetTetMesh.h:2857
wmtk::threading::enumerable_thread_specific< FaceExtraMap > m_face_extra_split
The split's and the swap's capture, per thread.
Definition TopoOffsetTetMesh.h:705
void optimization_iteration_done(const int it) override
Definition TopoOffsetTetMesh.h:2921
double swap_edge_44_energy(const std::vector< std::array< size_t, 4 > > &tets, const int op_case) override
The 4-4 / 5-6 case search's score, on the energy (under TetWild's rules, m_op_rules,...
Definition Optimize3d.cpp:1082
std::vector< double > front_ring_measures() const
The exit test's ring measure of every vertex (energy_criterion()), face for face.
Definition Optimize3d.cpp:2136
bool allow_surface_swap() const override
Definition TopoOffsetTetMesh.h:2193
bool is_open_boundary_edge(const Tuple &e) override
Definition TopoOffsetTetMesh.h:1768
bool cell_is_plastic(const size_t tid) const
Definition TopoOffsetTetMesh.h:1304
std::atomic< size_t > m_front_veto_asked
Definition TopoOffsetTetMesh.h:1891
bool swap_before_interior(const std::vector< size_t > &tids) override
Which tag the tets a swap creates should carry, and the topology half of the surface-flip refusal (cl...
Definition Optimize3d.cpp:689
bool front_placed_by_ratio(const double ratio) const
THE definition of "placed" for a vertex on the offset surface, applied to its already-measured front_...
Definition TopoOffsetTetMesh.h:2346
static double band_side_dihedral(const Vector3d &n1, const Vector3d &n2, const Vector3d &t)
Definition TopoOffsetTetMesh.h:1482
void label_input_complex()
label input simplicial complex simplices, as defined in m_offset_params.offset_selection
Definition TopoOffsetTetMesh.cpp:503
std::pair< double, double > compute_distance_deviation() const
How far the offset surface is from where it should be: {max, avg} over vertices.
Definition Optimize3d.cpp:4219
bool collapse_quality_allowed(size_t v1, double q, double ring_max) const override
In the loop (m_op_rules Loop) admits every collapse; under TetWild's rules, the base's....
Definition TopoOffsetTetMesh.h:2803
std::shared_ptr< OffsetEnergy3D > m_repulsion_term
Definition TopoOffsetTetMesh.h:1926
void split_after_vertex(size_t v_new, bool is_edge_open_boundary) override
Application metadata not represented by the shared vertex attributes.
Definition Optimize3d.cpp:1861
void for_each_face_sample(const Vector3d &p0, const Vector3d &p1, const Vector3d &p2, Visit &&visit) const
The stencil_order stencil a triangle is sampled on, handed to visit one point at a time as (point,...
Definition TopoOffsetTetMesh.h:2550
static constexpr double kSmoothRelGradNormTol
The Newton stopping rule every smoother of this component runs with, set on the thread's shared solve...
Definition TopoOffsetTetMesh.h:1883
double stencil_max_sq_error() const
Definition Optimize3d.cpp:4787
static constexpr double kNeedleQuality
What counts as a needle for the tripwire, in AMIPS – deliberately far below MAX_ENERGY.
Definition TopoOffsetTetMesh.h:2816
bool m_converged
Definition TopoOffsetTetMesh.h:1581
size_t refine_front_by_halving(const std::vector< EnergyCriterion::Refinable > &faces)
Definition Optimize3d.cpp:3653
GradientSplit gradient_split(bool include_face_samples=true) const
Definition Optimize3d.cpp:3087
bool edge_is_offset_surface_live(size_t a, size_t b) const
Whether edge (a, b) lies on the band's outer surface: some incident face does.
Definition Optimize3d.cpp:4577
bool swap_capture_tag(const std::vector< size_t > &tids)
Definition Optimize3d.cpp:655
std::vector< double > m_cross_ring
Definition TopoOffsetTetMesh.h:2059
void repulsion_smoothing()
Definition TopoOffsetTetMesh.cpp:1635
std::shared_ptr< Envelope > m_loop_envelope
The loop's m_envelope while the final pass holds its own.
Definition TopoOffsetTetMesh.h:430
int candidate_label(const std::array< size_t, 4 > &vids)
Definition Optimize3d.cpp:576
void edge_label_capture(const std::vector< size_t > &tids, EdgeLabelMap &out, size_t from=size_t(-1), size_t to=size_t(-1)) const
Definition EdgeSplittingTet.cpp:699
double band_swept_change(size_t vid, const Vector3d &x0, const Vector3d &x1, ExactBandIntegral::Stats *st=nullptr) const
Definition Optimize3d.cpp:414
bool face_is_complex_boundary(const Tuple &f) const
Definition TopoOffsetTetMesh.cpp:304
bool damping_active() const
Definition TopoOffsetTetMesh.h:2312
DistanceSplit residual_split() const
The same split over the quantity the loop converges on: the Phi residual, as a length.
Definition Optimize3d.cpp:3041
std::vector< std::string > m_face_tag_names
Definition TopoOffsetTetMesh.h:185
bool collapse_edge_after(const Tuple &t) override
The coarsening bar, the sizing restore and the rest re-stamp, after the base accepted.
Definition Optimize3d.cpp:1430
bool split_tet_before(const Tuple &t) override
User specified preparations and desideratas for a tet split before changing the connectivity.
Definition EdgeSplittingTet.cpp:567
int stencil_order() const
Samples per offset face; see offset_face_samples().
Definition TopoOffsetTetMesh.h:2451
void diagnostic_parallel_for(const size_t n, const size_t chunk, F &&fn) const
Definition TopoOffsetTetMesh.h:2039
std::vector< std::array< size_t, 3 > > offset_surface_faces() const
Every live offset-surface face as a sorted vertex triple.
Definition Optimize3d.cpp:4590
bool plastic_tie(const std::vector< size_t > &before, const std::vector< int > &after_labels) const
THE PLASTIC TIE: whether an operation's energy is exactly the same before and after....
Definition Optimize3d.cpp:314
bool is_repulsion_vertex(const size_t vid) const
Definition TopoOffsetTetMesh.h:1911
static constexpr double kCos150
Definition TopoOffsetTetMesh.h:1370
void init_from_image(const MatrixXd &V, const MatrixXi &T, const MatrixSi &T_tags, const MatrixXd &V_env, const MatrixXi F_env, const std::vector< std::string > &tag_names, const std::string &sheet_name="", const MatrixXi &F_tagged=MatrixXi(), const MatrixSi &F_tags=MatrixSi(), const std::vector< std::string > &face_tag_names={})
initialize TetMesh from vertex, tet, and tag data
Definition TopoOffsetTetMesh.cpp:98
bool edge_split_sphere_trace(const Vector3d &p_in, const Vector3d &p_out, Vector3d &p_new, size_t &steps) const
Construction placement in the marching: sphere tracing along the edge from p_in (the endpoint in the ...
Definition EdgeSplittingTet.cpp:225
wmtk::threading::enumerable_thread_specific< std::pair< size_t, Vector3d > > m_swept_stored
Definition TopoOffsetTetMesh.h:974
void wait_debug_frame_write()
Definition Optimize3d.cpp:5231
double vol_amips3(const std::array< size_t, 4 > &vids, const double amips3) const
Definition TopoOffsetTetMesh.h:1028
bool coarsen_no_rise() const
Definition TopoOffsetTetMesh.h:2837
void optimize_offset(const std::filesystem::path &output_file)
The 3D optimization phase: split / collapse / swap / smooth on the shared driver.
Definition Optimize3d.cpp:7127
double front_face_field_angle_in(size_t a, size_t b, size_t c, size_t band_tid) const
front_face_field_angle() with the face's band cell given (front_face_out_normal_in()).
Definition Optimize3d.cpp:4947
bool vertex_is_on_region(const size_t vid) const
Definition TopoOffsetTetMesh.h:2167
void crossing_snapshot(const std::string &pass, bool compare, bool match_positions)
Definition Optimize3d.cpp:2184
bool plastic_rest(const size_t tid, Eigen::Matrix3d &R) const
Definition Optimize3d.cpp:277
wmtk::threading::enumerable_thread_specific< int > m_collapse_folds_before
Definition TopoOffsetTetMesh.h:1529
double tet_flatness(size_t tid) const
Scale-invariant flatness: 6 * volume / longest_edge^3.
Definition Optimize3d.cpp:2822
void log_stuck_refine_census(double max_metric, double filter_energy)
Definition Optimize3d.cpp:2527
bool split_before_cells(const Tuple &edge, const std::vector< Tuple > &parents) override
Split policy that is the offset's own: which region tag the two child tets inherit,...
Definition EdgeSplittingTet.cpp:769
void live_offset_faces_at(size_t vid, std::vector< std::array< size_t, 3 > > &out) const
The live offset faces through vid, as sorted vertex triples (for_each_live_offset_face_at()).
Definition Optimize3d.cpp:4821
MeshSnapshot take_snapshot()
Definition Optimize3d.cpp:6839
bool vertex_is_on_surface(const size_t vid) const override
Is a vertex part of the substructure.
Definition Optimize3d.cpp:119
std::atomic< long long > m_offset_face_lookup_misses
Faces that offset_surface_faces_live_at() asked for and the connectivity did not have.
Definition TopoOffsetTetMesh.h:1443
double front_vertex_normal_gradient(size_t vid) const
Definition Optimize3d.cpp:2018
double damping_step(size_t vid) const
Definition Optimize3d.cpp:1056
bool empty_input_complex()
check if the input complex is empty. Only valid after calling init_from_image(...).
Definition TopoOffsetTetMesh.cpp:758
double ring_max_quality(size_t vid) const
Definition Optimize3d.cpp:2813
void needle_scan(const char *when) const
Population scan at a named moment. Reports the count and the worst few.
Definition Optimize3d.cpp:2764
double m_front_gradient_reference
Its value on the band as constructed, measured once before turn 1.
Definition TopoOffsetTetMesh.h:628
static void write_debug_pvd(const std::string &out, const DebugTimeline &timeline)
Definition Optimize3d.cpp:5236
double surface_term_weight() const
Definition Optimize3d.cpp:4707
bool split_face_after(const Tuple &t) override
Compute the attributes for the added simplices.
Definition EdgeSplittingTet.cpp:470
double face_criterion_rel(const Tuple &f) const
Definition Optimize3d.cpp:4281
bool swap_after_cells(const std::vector< size_t > &tids, bool is_surface_flip) override
Propagate application data to the cells made by a successful topological swap.
Definition Optimize3d.cpp:1299
std::shared_ptr< const wmtk::utils::WindingNumberHierarchy > m_input_solid_winding
Definition TopoOffsetTetMesh.h:220
std::vector< int64_t > m_phi_face_region
per m_phi_F row: region index, -1 unknown
Definition TopoOffsetTetMesh.h:287
std::vector< std::array< double, 8 > > optimization_metrics
Definition TopoOffsetTetMesh.h:1538
bool split_edge_is_due(const Tuple &e) const
Definition EdgeSplittingTet.cpp:70
std::atomic< size_t > m_damping_moves
Definition TopoOffsetTetMesh.h:1894
double cell_quality_rel(const size_t tid) const
AMIPS of a cell over stop_energy – the 3D twin of TriOptimizerMesh::quality_rel().
Definition Optimize3d.cpp:4265
bool is_sheet_tag(int64_t tag) const
Whether tag names an open sheet: the envelope surface group or a face tag.
Definition TopoOffsetTetMesh.h:188
double cell_energy(const std::array< size_t, 4 > &vids, const double amips3, const bool band) const
One cell's energy from its corners, its AMIPS^3 and its band label; see tet_energy().
Definition TopoOffsetTetMesh.h:1126
size_t m_marching_root_splits
Definition TopoOffsetTetMesh.h:1706
std::vector< std::array< size_t, 2 > > m_settle_split_edges
Definition TopoOffsetTetMesh.h:564
OpRules
Which rules the operations and the smoother follow – the one switch for them.
Definition TopoOffsetTetMesh.h:463
const char * m_energy_step_group
The running group's name, and whether its first smoothing pass is still to come.
Definition TopoOffsetTetMesh.h:2050
size_t m_debug_seq
Monotonic frame counter for the debug timeline.
Definition TopoOffsetTetMesh.h:1543
int m_smooth_group
Definition TopoOffsetTetMesh.h:2338
size_t refine_sizing_around_worst(double max_metric) override
TetWild's stall-driven sizing refinement, verbatim; final pass only. See the 2D twin.
Definition Optimize3d.cpp:4295
void face_extra_assign(const std::vector< size_t > &tids, const FaceExtraMap &captured)
Every face of tids: its captured value when it was captured, else reset.
Definition EdgeSplittingTet.cpp:683
void check_offset_within_support(const char *when) const
Definition Optimize3d.cpp:4193
bool split_adjust_position(size_t v_new, const std::vector< Tuple > &children) override
Definition Optimize3d.cpp:1891
void loop_group(const std::array< int, 4 > &ops, const std::string &where)
The loop's local_operations(ops). Under EXPERIMENTAL_front_pocket_split, the group's operation passes...
Definition Optimize3d.cpp:5273
bool face_is_offset_surface_live(const Tuple &f) const
The band's outer surface, recomputed live rather than read from the cached class.
Definition Optimize3d.cpp:4491
double tet_energy_sum(const std::vector< size_t > &tids, bool band_term=true) const
The sum of tet_energy() over tids, in their order: what every rule compares.
Definition Optimize3d.cpp:406
double label_flip_surface_change(size_t tid) const
Definition Optimize3d.cpp:5606
std::shared_ptr< Envelope > surface_envelope_for_face(const std::array< size_t, 3 > &vids, const FaceAttributes &) const override
The containment of a tracked face an operation has just made (the base asks only after the fact)....
Definition TopoOffsetTetMesh.h:2178
std::atomic< int > m_volume_reject_collapse
Definition TopoOffsetTetMesh.h:1147
std::shared_ptr< Envelope > smoothing_containment_envelope(const size_t vid, const std::array< size_t, 3 > &, const FaceAttributes &) const override
Definition TopoOffsetTetMesh.h:2274
double front_gradient_linf()
Definition Optimize3d.cpp:3423
int m_round
Definition TopoOffsetTetMesh.h:1541
wmtk::threading::enumerable_thread_specific< CellTag > m_swap_tag
The tag swap_after_cells writes onto the tets an INTERIOR swap created, chosen in before.
Definition TopoOffsetTetMesh.h:3159
double band_chain_change(const std::vector< std::array< size_t, 4 > > &old_band, const std::vector< std::array< size_t, 4 > > &new_band, ExactBandIntegral::Stats *st=nullptr) const
Definition Optimize3d.cpp:486
void init_surfaces_and_boundaries()
Classify every region boundary and tag the domain wall – once, from the input mesh,...
Definition TopoOffsetTetMesh.cpp:231
wmtk::threading::enumerable_thread_specific< std::vector< std::pair< FaceExtra, std::array< size_t, 2 > > > > m_face_extra_collapse
The collapse's merged faces around the edge: (merged value, the two link vertices).
Definition TopoOffsetTetMesh.h:709
Vector3d front_vertex_normal(size_t vid) const
The field's unit direction at front vertex vid (zero where grad Phi vanishes).
Definition FrontSmooth3d.cpp:291
double cell_quality(const size_t tid) const override
The quality of cell tid, and how to write it.
Definition TopoOffsetTetMesh.h:756
void classify_sheet_faces()
Mark the mesh faces that lie on the input's envelope surface group (FaceExtra::on_sheet).
Definition TopoOffsetTetMesh.cpp:463
std::atomic< long long > funnel_case_inverted
Definition TopoOffsetTetMesh.h:1637
void report_needle(const char *op, size_t tid, double parent_q) const
Where the first needles come from – a tripwire, capped at kNeedleReports.
Definition Optimize3d.cpp:2711
int m_n_regions
One field per connected piece of the input complex, and which one each band vertex is placed on....
Definition TopoOffsetTetMesh.h:278
std::atomic< long long > m_offset_face_invalid_tuple
Definition TopoOffsetTetMesh.h:1447
optimization::NewtonCounters m_newton_front
Definition TopoOffsetTetMesh.h:1846
bool m_plastic_active
Definition TopoOffsetTetMesh.h:1299
bool remote_frames() const
DEBUG_remote_output: the sparse frame schedule (see the spec doc) instead of DEBUG_output's.
Definition TopoOffsetTetMesh.h:2915
void write_phi_grid(const std::string &path, int n) const
Definition TopoOffsetTetMesh.cpp:3019
bool smooth_after(const Tuple &t) override
User specified modifications and desideratas for after smoothing a vertex.
Definition Optimize3d.cpp:1950
bool face_folded_in(size_t a, size_t b, size_t c, const std::vector< size_t > &cells) const
Definition Optimize3d.cpp:4832
std::vector< int64_t > m_phi_point_region
per m_phi_P entry: region index, -1 unknown
Definition TopoOffsetTetMesh.h:288
void construct_offset(const std::filesystem::path &output_file)
Definition TopoOffsetTetMesh.cpp:1439
void check_no_vertex_on_both_surfaces(const char *when) const
Definition Optimize3d.cpp:5017
Per-vertex data the shared 3D optimizer knows nothing about.
Definition TopoOffsetTetMesh.h:77
int64_t m_corr_input_vid
Definition TopoOffsetTetMesh.h:87
uint32_t m_born_epoch
Definition TopoOffsetTetMesh.h:92
double m_front_change
Definition TopoOffsetTetMesh.h:98
Definition Simplex.hpp:46
Definition enumerable_thread_specific.hpp:37
What the offset needs on top of the parameters every wmtk optimizer shares.
Definition Parameters.h:24
bool allow_mult_comp
Definition Parameters.h:290
bool damping
Definition Parameters.h:245
double foldover_angle
Definition Parameters.h:279
double offset_amips_weight
Definition Parameters.h:91
double front_conv_frac() const
Definition Parameters.h:78
std::string growth_mode
Definition Parameters.h:265
std::string stencil_type
Definition Parameters.h:154
bool debug_remote_output
Definition Parameters.h:284
std::string coarsen_guard
Definition Parameters.h:110
std::string settle_test
Definition Parameters.h:223
std::string band_volume_rule
Definition Parameters.h:213
std::vector< std::array< Vector3d, 2 > > swept_faces
Definition TopoOffsetTetMesh.h:2141
bool band_cancels
band_term_cancels() for v1's ring: both sums leave the band term out.
Definition TopoOffsetTetMesh.h:2132
The "energy_gradient" criterion: the front's Newton-step ratios and the refinable faces....
Definition TopoOffsetTetMesh.h:2601
bool ring_exit
front_measure "vertex_ring"
Definition TopoOffsetTetMesh.h:2667
std::vector< size_t > refinable_vertices
Definition TopoOffsetTetMesh.h:2675
double max_ring_at_floor
the worst of them, as a ratio to the bar
Definition TopoOffsetTetMesh.h:2679
std::string sizing_floor_fact() const
Definition Optimize3d.cpp:3369
double floor_scalar
The floor, max(min_sizing_scalar, min_edge_length / l), and which of the two it is.
Definition TopoOffsetTetMesh.h:2636
double sum_ring
ratios to the bar (1 = bar)
Definition TopoOffsetTetMesh.h:2669
bool vertices_ok() const
Definition TopoOffsetTetMesh.h:2687
double avg_vertex() const
Means over the measurable front vertices / offset faces; 0 when there are none.
Definition TopoOffsetTetMesh.h:2726
double max_face_corners_at_floor
the worst of them, as a ratio to the bar
Definition TopoOffsetTetMesh.h:2633
double max_face_at_floor
the worst of them, as a ratio to the bar
Definition TopoOffsetTetMesh.h:2625
bool converged() const
Definition TopoOffsetTetMesh.h:2714
A face's shared surface tags together with the offset's own label.
Definition TopoOffsetTetMesh.h:1259
DEBUG_output only: every front solve since the last debug frame, so the frames show per vertex what m...
Definition TopoOffsetTetMesh.h:1862
int status
polysolve's stop status + 1: NewtonCounters::status_name()'s numbering
Definition TopoOffsetTetMesh.h:1865
The convergence criterion's own split: ||grad (Phi - c)^2|| at band vertices plus the face-interior c...
Definition TopoOffsetTetMesh.h:2585
An in-memory image of the mesh, to put it back as it was (for growth_mode "smart": the revert after a...
Definition TopoOffsetTetMesh.h:405
growth_mode "smart": which target to try next from the last converged distance, and what an attempt's...
Definition TopoOffsetTetMesh.h:591
double delta
target_distance
Definition TopoOffsetTetMesh.h:593
int max_fails
smart_growth_max_fails
Definition TopoOffsetTetMesh.h:594
int j
failed attempts from base, so halvings of the step
Definition TopoOffsetTetMesh.h:595
int level
level 0 is the loop at d_0; each success below delta adds one
Definition TopoOffsetTetMesh.h:596
double base
the last converged distance
Definition TopoOffsetTetMesh.h:592
What smoothing did with each class of vertex, per pass. Same fields as 2D.
Definition TopoOffsetTetMesh.h:1817
std::atomic< int > before_bbox
base smooth_before said no: on the bounding box
Definition TopoOffsetTetMesh.h:1819
std::atomic< int > before_unrounded
base smooth_before said no: could not round
Definition TopoOffsetTetMesh.h:1820
std::atomic< int > interior_attempted
reached it without one
Definition TopoOffsetTetMesh.h:1823
std::atomic< int > attempted
smooth_before() entered
Definition TopoOffsetTetMesh.h:1818
std::atomic< int > offset_accepted
... and the smoother kept the new position
Definition TopoOffsetTetMesh.h:1822
std::atomic< int > region_attempted
... of which sat on another region's boundary
Definition TopoOffsetTetMesh.h:1824
std::atomic< int > offset_attempted
reached the smoother with the offset term
Definition TopoOffsetTetMesh.h:1821
The swap in flight on this thread, as candidate_energy() and the sanity check read it (see tet_energy...
Definition TopoOffsetTetMesh.h:3226
std::vector< std::array< size_t, 4 > > old_band
"exact_swept", a flip: the band cells it replaces (swap_swept_change()).
Definition TopoOffsetTetMesh.h:3241
int kind
tids.size() as swap_before_surface() saw it: 3, 4 or 5, i.e. which swap this is.
Definition TopoOffsetTetMesh.h:3195
std::array< size_t, 4 > abcd
Definition TopoOffsetTetMesh.h:3180
Everything write_vtu() writes, as arrays: the mesh is read once, by gather_vtu_frame(),...
Definition TopoOffsetTetMesh.h:3062
How the vertex solves of a smoothing pass ended: Newton iterations taken and the status polysolve sto...
Definition SmoothVertex.hpp:76