Wildmeshing Toolkit
Loading...
Searching...
No Matches
TopoOffsetTetMesh.h
1#pragma once
2
3#include <array>
4#include <cstdint>
5#include <cstdlib>
6#include <map>
7#include <mutex>
8#include <string>
9
10#include <wmtk/TetMesh.h>
11#include <wmtk/TetOptimizerMesh.h>
12#include <wmtk/envelope/Envelope.hpp>
13#include <wmtk/optimization/solver.hpp>
14#include <wmtk/simplex/Simplex.hpp>
15#include <wmtk/threading/enumerable_thread_specific.hpp>
16#include "OffsetPotential.hpp"
17#include "Parameters.h"
18#include "SimplicialComplexBVH.hpp"
19
20// clang-format off
21#include <wmtk/utils/DisableWarnings.hpp>
22#include <wmtk/utils/EnableWarnings.hpp>
23// clang-format on
24
25using CellTag = std::set<int64_t>;
26
27
28namespace wmtk::components::topological_offset {
29
30const int64_t TEMP_OFFSET_TET_TAG = -1;
31const CellTag TEMP_OFFSET_TET_TAG_SET{TEMP_OFFSET_TET_TAG};
32
33
34// for all attributes:
35// label: 0=default, 1=input, 2=offset
36
46{
47public:
48 int label = 0;
49 size_t component_id = 0;
53 bool m_is_on_region = false;
54 bool m_is_on_offset = false; // is this vertex on the offset surface
55
62
71 uint64_t m_boundary_mask = 0;
72
79 uint32_t m_born_epoch = 0;
80};
81
82
84{
85public:
86 int label = 0; // label: 0=default, 1=input, 2=offset
87};
88
89
99{
100public:
101 int label = 0; // label: 0=default, 1=input, 2=offset
102};
103
104
106{
107public:
108 int label = 0; // label: 0=default, 1=input, 2=offset
109 CellTag tag;
110 double m_quality = 0; // AMIPS energy, kept up to date by smoothing
111};
112
113
125{
126public:
133 static constexpr int OFFSET_SURFACE_CLASS = 1;
134
152 enum class OptPhase { A, B };
153
155 OptPhase m_phase = OptPhase::A;
156
170 enum class PhaseBSub { Offset, Background };
171 PhaseBSub m_phase_b_sub = PhaseBSub::Offset;
172
182 std::shared_ptr<SampleEnvelope> m_offset_envelope;
183
187
191 void check_no_vertex_on_both_surfaces(const char* when) const;
192
197
204 size_t phase_b_smooth();
205
219
228
252
253public: // mode for splitting in marching tets
254 enum class EdgeSplitMode {
255 Midpoint = 0, // construction: simplicial embedding AND marching_tets
256 Optimization = 5 // this is used in the optimization phase of the algorithm
257 };
258
259
260public:
261 int m_vtu_counter = 0;
262 std::array<size_t, 4> m_init_counts = {{0, 0, 0, 0}};
263 size_t m_tags_count;
264 SimplicialComplexBVH m_input_complex_bvh;
265
274 std::shared_ptr<OffsetPotential3D> m_offset_potential;
275
285 std::shared_ptr<SampleEnvelope> m_input_complex_envelope;
286
304 std::map<int64_t, std::shared_ptr<SampleEnvelope>> m_tag_envelopes;
305
308 std::map<int64_t, int> m_tag_bit;
309
312 mutable std::map<uint64_t, std::shared_ptr<SampleEnvelope>> m_isect_cache;
313 mutable std::mutex m_isect_mutex;
314
315 EdgeSplitMode m_edge_split_mode = EdgeSplitMode::Midpoint;
316
317 // tag map stuff
318 std::map<std::string, int64_t> m_tag_name_to_id;
319 std::map<int64_t, std::string> m_tag_id_to_name;
320 CellTag m_offset_output_tag_ids;
321
322 // if in 'singlebody' mode
323 bool m_singlebody = false;
324 int64_t m_single_tag;
325
326 // dont actually use, just for retaining in output
327 bool m_has_envelope;
328 std::vector<Vector3d> m_V_envelope;
329 std::vector<Vector3i> m_F_envelope;
330 // m_envelope itself lives on the base, which is what checks tracked-surface triangles
331 // against it.
332 double m_envelope_eps = -1;
333
337
342 // m_vertex_attribute and m_face_attribute are the base's; these two are registered
343 // alongside them in its attribute groups.
344 VertexExtraCol m_vertex_extra;
345 FaceExtraCol m_face_extra;
346 EdgeAttCol m_edge_attribute;
347 TetAttCol m_tet_attribute;
348
356 {
357 FaceAttributes tags;
358 FaceExtra extra;
359 };
360 FaceSnapshot face_snapshot(const size_t fid) const
361 {
362 return FaceSnapshot{m_face_attribute[fid], m_face_extra[fid]};
363 }
364 void restore_face(const size_t fid, const FaceSnapshot& s)
365 {
366 m_face_attribute[fid] = s.tags;
367 m_face_extra[fid] = s.extra;
368 }
369
379 void set_vertex_position(const size_t vid, const Vector3d& p)
380 {
381 m_vertex_attribute[vid].m_posf = p;
382 m_vertex_attribute[vid].m_pos = to_rational(p);
383 m_vertex_attribute[vid].m_is_rounded = true;
384 }
385
394 bool cell_in_region(const size_t tid) const
395 {
396 const int l = m_tet_attribute[tid].label;
397 return l == 1 || l == 2; // input complex, or the offset band
398 }
399
401 bool face_is_offset(const size_t fid) const
402 {
403 return m_face_attribute[fid].m_is_surface_fs &&
404 m_face_attribute[fid].m_surface_class == OFFSET_SURFACE_CLASS;
405 }
407 bool face_is_region(const size_t fid) const
408 {
409 return m_face_attribute[fid].m_is_surface_fs &&
410 m_face_attribute[fid].m_surface_class != OFFSET_SURFACE_CLASS;
411 }
412
413 // debug use
414 std::atomic<int> cnt_split = 0, cnt_collapse = 0;
415
416 TopoOffsetTetMesh(Parameters& _m_offset_params, int _num_threads = 0)
417 : wmtk::TetOptimizerMesh(_m_offset_params, nullptr)
418 , m_offset_params(_m_offset_params)
419 {
420 NUM_THREADS = _num_threads;
421 // The base owns the vertex and face slots; register the offset's own data with its
422 // groups so it is resized, protected and rolled back with them.
423 m_vertex_attr_group.add(&m_vertex_extra);
424 m_face_attr_group.add(&m_face_extra);
425 p_edge_attrs = &m_edge_attribute;
426 p_tet_attrs = &m_tet_attribute;
427
428 m_collapse_check_link_condition = false;
429 m_collapse_check_manifold = false;
430
431 // TetWild parity, forced on for 3D regardless of the config key: Phase B places the
432 // offset surface with its own root find, which never consults this flag, and Phase A is
433 // pure TetWild quality work, where the veto is on. The spec default is false for the 2D
434 // pipeline, whose offset-boundary placement the veto would freeze.
435 _m_offset_params.smooth_quality_veto = true;
436
437 optimization::deactivate_opt_logger();
438 }
439
440 ~TopoOffsetTetMesh() override = default;
441
443
444 double cell_quality(const size_t tid) const override { return m_tet_attribute[tid].m_quality; }
445 void set_cell_quality(const size_t tid, const double q) override
446 {
447 // Spike tracker. Every operation that writes a quality comes through here, so this is
448 // the one place that sees a tet cross from ordinary into absurd, and the spike's
449 // position in the log names the pass that caused it. Only the crossing is reported, and
450 // only the first few in full, or the flood would be the whole log.
451 if (m_spike_track) {
452 const double before = m_tet_attribute[tid].m_quality;
453 const double a = spike_amips(q), b = spike_amips(before);
454 // Two events, not a fixed bar -- a fixed bar spends the whole dump budget on the
455 // mildest tets and never reaches the extremes.
456 // genesis: an ordinary tet ruined by one operation, i.e. the causal event.
457 // record: a new global maximum, so the dumps end at the true worst and, since
458 // records only increase, the output stays bounded.
459 if (b < m_spike_ordinary && a > m_spike_threshold) {
460 log_quality_spike(tid, before, q, "genesis");
461 }
462 if (a > m_spike_record) {
463 m_spike_record = a;
464 log_quality_spike(tid, before, q, "record");
465 }
466 if (a > m_spike_threshold) ++m_spike_count;
467 }
468 m_tet_attribute[tid].m_quality = q;
469 }
470
473 static double spike_amips(const double q)
474 {
475 return std::isfinite(q) ? std::cbrt(q) : std::numeric_limits<double>::infinity();
476 }
477
487 void log_quality_spike(size_t tid, double before, double after, const char* kind) const;
488
490 bool m_spike_track = true;
492 double m_spike_threshold = 1e5;
494 double m_spike_ordinary = 100.;
496 mutable double m_spike_record = 0.;
497 mutable std::atomic<int> m_spike_dumps{0};
498 mutable std::atomic<int> m_spike_genesis_dumps{0};
499 mutable std::atomic<int> m_spike_count{0};
500 mutable std::atomic<int> m_spike_genesis{0};
501 int m_spike_dump_budget = 24;
502
512 bool vertex_is_on_region(const size_t vid) const
513 {
514 return m_vertex_extra[vid].m_is_on_region || !m_vertex_attribute[vid].on_bbox_faces.empty();
515 }
516
519 uint64_t tag_bits(const CellTag& tags) const
520 {
521 uint64_t bits = 0;
522 for (const int64_t t : tags) {
523 const auto it = m_tag_bit.find(t);
524 if (it != m_tag_bit.end()) bits |= (uint64_t(1) << it->second);
525 }
526 return bits;
527 }
528
540 uint64_t vertex_boundary_mask(const size_t vid) const
541 {
542 return vertex_is_on_region(vid) ? m_vertex_extra[vid].m_boundary_mask : uint64_t(0);
543 }
544
546 uint64_t face_mask(const std::array<size_t, 3>& vids) const
547 {
548 return vertex_boundary_mask(vids[0]) & vertex_boundary_mask(vids[1]) &
549 vertex_boundary_mask(vids[2]);
550 }
551
561 std::shared_ptr<SampleEnvelope> envelope_for_mask(uint64_t mask) const;
562
563 std::shared_ptr<SampleEnvelope> surface_envelope_for_face(
564 const std::array<size_t, 3>& vids) const override
565 {
566 // Boundary geometry first, in both phases: a face on any tag-region boundary may not
567 // drift out of that boundary's tube, and a face on several is held in their
568 // intersection. The mask carries the input complex too -- every complex simplex lies on
569 // tag boundaries -- and E_t is built from the input mesh before construction touches it.
570 if (const uint64_t mask = face_mask(vids)) {
571 return envelope_for_mask(mask);
572 }
573 // Phase A holds the offset where Phase B left it; Phase B is what moves it, so it is
574 // unconstrained there. Null when there is no offset envelope yet -- the construction
575 // phase runs before the first one is built.
576 bool all_offset = true;
577 for (const size_t v : vids) {
578 all_offset = all_offset && m_vertex_extra[v].m_is_on_offset;
579 }
580 if (all_offset && m_phase == OptPhase::A) return m_offset_envelope;
581 return nullptr;
582 }
583
587 bool allow_surface_swap() const override { return true; }
588 bool check_surface_topology() const override { return m_offset_params.perform_sanity_checks; }
589
604 std::shared_ptr<SampleEnvelope> smoothing_energy_envelope(const size_t vid) const override
605 {
606 if (m_vertex_extra[vid].m_is_on_offset && !vertex_is_on_region(vid)) {
607 return nullptr;
608 }
609 const uint64_t mask = vertex_boundary_mask(vid);
610 if (mask == 0) {
611 // Reachable only for a construction artefact (a wall-chord midpoint flagged
612 // on-surface with disjoint endpoint masks); its containment is vacuous too, so no
613 // pull is behavior-neutral. Not an error.
614 return nullptr;
615 }
616 std::shared_ptr<SampleEnvelope> best;
617 double worst_d2 = -1.;
618 for (const auto& [tag, env] : m_tag_envelopes) {
619 const auto it = m_tag_bit.find(tag);
620 if (it == m_tag_bit.end() || !(mask & (uint64_t(1) << it->second))) continue;
621 if (!best) {
622 best = env;
623 if ((mask & (mask - 1)) == 0) break; // single bit: no violation contest to run
624 worst_d2 = env->squared_distance(m_vertex_attribute[vid].m_posf);
625 continue;
626 }
627 const double d2 = env->squared_distance(m_vertex_attribute[vid].m_posf);
628 if (d2 > worst_d2) {
629 worst_d2 = d2;
630 best = env;
631 }
632 }
633 return best;
634 }
635
654 std::shared_ptr<SampleEnvelope> smoothing_containment_envelope(const size_t vid) const override
655 {
656 if (m_vertex_extra[vid].m_is_on_offset && !vertex_is_on_region(vid)) {
657 return m_phase == OptPhase::A ? m_offset_envelope : nullptr;
658 }
659 // Boundary geometry is contained in the intersection of its tags' tubes, in BOTH
660 // phases -- the caller only asks is_outside(triangle), which composites answer, so
661 // unlike the pull this side may hand out an IntersectionEnvelope. Vertices with no
662 // boundary bits (offset handled above; construction artefacts) have nothing to be
663 // contained in.
665 }
666
667 // Do not re-add a smoothing_extra_energy() override; measured worse -- see git history of
668 // this file. Phase B places the offset surface with its own root find and Phase A holds it
669 // in m_offset_envelope, so the base's nullptr is correct in both phases.
670
674 void write_optimization_debug_output(const std::string& path) override
675 {
676 // The per-pass debug_{N} series is opt-in: the driver's phase_{round}{A|B} frames are
677 // the timeline the viewer shows, and the per-pass frames cost more time and disk than
678 // the rest of the run put together.
679 if (!m_offset_params.debug_output_per_pass && path.rfind("debug_", 0) == 0) {
680 return;
681 }
682 write_vtu(m_offset_params.output_path + "_" + path);
683 }
684
693 size_t refine_sizing_around_worst(double) override;
694
695 // optimization_bare_coarsen_passes() is deliberately not overridden: TetWild's opening and
696 // closing unlimited-length collapse passes apply. Phase A rebuilds m_offset_envelope before
697 // every mesh_improvement(), so the offset surface is held during them exactly as TetWild's
698 // input surface is, and decimating to what the envelope tolerates is the contract.
699
704 std::tuple<double, double> optimization_quality_stats() override;
705
710 double optimization_stop_metric() const override
711 {
712 return m_phase == OptPhase::A ? wmtk::TetOptimizerMesh::optimization_stop_metric() : 1.;
713 }
714
721 {
722 return std::max(
723 0.5 * m_offset_params.front_conv_rel * m_offset_params.target_distance,
724 1e-16);
725 }
726
739 {
740 // Normalized by the field's slope s (OffsetPotential::level_set_slope()), so
741 // front_conv_rel means the same thing whichever field is in use: |Phi - c| is a field
742 // difference, not a length, and s is the conversion. s == 1 for `euclidean`, where this
743 // is a no-op; for `smooth` Phi is a barrier with s ~ 1/delta, growing as the offset gets
744 // finer, so without the factor rel silently asks for a tiny fraction of what it names.
745 //
746 // At the field's reference slope the bound reduces to |Phi - c| / s <= (rel / 2) * delta,
747 // so rel 0.2 means "within 10% of target_distance"; elsewhere it is that bound relaxed by
748 // s / |grad Phi|. The relaxation is deliberate, not slack: it is what lets the criterion
749 // be met at a stationary point of Phi, where |grad Phi| -> 0 and no residual bound is
750 // achievable at all. Two bodies close enough that Phi's minimum contour between them
751 // exceeds c have no level set in the gap; the sheets converge onto that contour from
752 // either side and rel controls how close they get. Expect `converged: true` beside a
753 // plateaued max_phi_residual there -- that geometry being reported, not a placement
754 // failure.
755 const double s = m_offset_potential ? m_offset_potential->level_set_slope() : 1.;
756 return std::max(
757 m_offset_params.front_conv_rel * m_offset_params.target_distance * s * s,
758 1e-16);
759 }
760
770 void check_offset_within_support(const char* when) const;
771
774 double band_vertex_residual(const size_t vid) const;
775
783 bool band_vertex_is_reachable(const size_t vid) const
784 {
785 return m_vertex_attribute[vid].on_bbox_faces.empty();
786 }
787
790 std::vector<bool> band_vertex_mask() const;
791
794 double max_band_vertex_distance() const;
795
798 void write_phi_grid(const std::string& path, int n) const;
799
803 {
804 double max = 0.;
805 double sum = 0.;
806 size_t n = 0;
807 };
808
822 template <typename Visit>
823 void for_each_offset_face_sample(const Tuple& f, Visit&& visit) const
824 {
825 const int k = m_offset_params.offset_residual_samples;
826 if (k <= 0) return;
827
828 const auto vs = get_face_vids(f);
829 const Vector3d p0 = m_vertex_attribute[vs[0]].m_posf;
830 const Vector3d p1 = m_vertex_attribute[vs[1]].m_posf;
831 const Vector3d p2 = m_vertex_attribute[vs[2]].m_posf;
832
833 const int n = k + 2;
834 for (int i = 1; i < n; ++i) {
835 for (int j = 1; j < n - i; ++j) {
836 const int l = n - i - j;
837 if (l < 1) continue;
838 visit(Vector3d((double(i) * p0 + double(j) * p1 + double(l) * p2) / double(n)));
839 }
840 }
841 }
842
858 FaceSamples offset_face_samples(const Tuple& f) const;
859
864 double face_criterion_rel(const Tuple& f) const;
867 double cell_quality_rel(const size_t tid) const;
869 double amips_rel_at_face(const Tuple& f) const;
870
878 {
879 double max_reachable = 0., avg_reachable = 0.;
880 double max_pinned = 0.;
881 size_t n_reachable = 0, n_pinned = 0;
885 double max_at_vertex = 0., max_in_face = 0.;
889 size_t worst_outside_vid = static_cast<size_t>(-1);
890 double worst_outside_dist = 0.;
891 };
892 DistanceSplit residual_split() const;
893
915 {
916 double max_reachable = 0., avg_reachable = 0.;
917 double max_pinned = 0.;
918 size_t n_reachable = 0, n_pinned = 0;
920 double max_at_vertex = 0., max_in_face = 0.;
922 size_t n_face_samples = 0;
925 size_t n_skipped_inverted = 0, n_skipped_unrounded = 0;
928 size_t worst_vid = static_cast<size_t>(-1);
929 };
933 GradientSplit gradient_split(bool include_face_samples = true) const;
936 void report_outside_support(const char* when, const DistanceSplit& s) const;
937
947 bool swap_before_interior(const std::vector<size_t>& tids) override;
948 bool swap_before_surface(
949 const std::vector<size_t>& tids,
950 size_t a,
951 size_t b,
952 size_t c,
953 size_t d) override;
954 bool swap_after_cells(const std::vector<size_t>& tids, bool is_surface_flip) override;
955
964 bool collapse_edge_before(const Tuple& t) override;
965 bool collapse_before_vertex(size_t v1, size_t v2, double edge_length) override;
966 bool collapse_after_connectivity(
967 size_t v1,
968 size_t v2,
969 const std::vector<std::array<size_t, 2>>& boundary_edges) override;
970 bool collapse_is_order_2_edge(const std::array<size_t, 2>& e) override
971 {
972 return is_order_2_edge(e);
973 }
974 void collapse_after_vertex(size_t v1, size_t v2) override;
975
976
981 {
982 std::atomic<long> invariant{0};
983 std::atomic<long> class_region{0};
984 std::atomic<long> class_offset{0};
985 std::atomic<long> order2{0};
986 std::atomic<long> sublink{0};
987 void reset() { invariant = class_region = class_offset = order2 = sublink = 0; }
988 };
989 mutable OffsetCollapseRefusals m_offset_collapse_refusals;
990 // The shared collapse merges the sizing scalar with min(removed, survivor) -- see
991 // TetOptimizerMeshCollapse.cpp -- making every collapse a one-way refinement ratchet. That
992 // is deliberate: a collapse must never discard a finer request the field has already made.
993
1001 bool split_before_cells(const Tuple& edge, const std::vector<Tuple>& parents) override;
1002 bool split_after_cells(size_t v1, size_t v2, size_t v_new, const std::vector<Tuple>& children)
1003 override;
1006 bool split_adjust_position(size_t v_new, const std::vector<Tuple>& children) override;
1007 void split_after_vertex(size_t v_new, bool is_edge_open_boundary) override;
1008
1011 bool is_open_boundary_edge(const Tuple& e) override { return is_order_2_edge(e); }
1012
1013private:
1017 {
1018 bool is_edge_on_region = false;
1019 bool is_edge_on_offset = false;
1020 std::map<simplex::Edge, TetAttributes> tets;
1021 };
1023
1024public:
1025private:
1026public:
1027private:
1032
1033 bool swap_capture_tag(const std::vector<size_t>& tids);
1040
1041public:
1050 void init_from_image(
1051 const MatrixXd& V,
1052 const MatrixXi& T,
1053 const MatrixSi& T_tags,
1054 const MatrixXd& V_env,
1055 const MatrixXi F_env,
1056 const std::vector<std::string>& tag_names);
1057
1058 void init_surfaces_and_boundaries();
1059
1062 bool is_edge_on_region(const Tuple& loc);
1063 bool is_edge_on_offset(const Tuple& loc);
1064
1068 bool ambient_assert();
1069
1074 void label_input_complex();
1075
1079
1084 bool empty_input_complex();
1085
1093
1100 void init_offset_potential();
1101
1106 MatrixXd m_phi_V;
1107 MatrixXi m_phi_E;
1108 MatrixXi m_phi_F;
1109 std::vector<int> m_phi_P;
1110
1111
1115 size_t flood_fill();
1116
1117 std::vector<std::array<size_t, 3>> get_faces_by_condition(
1118 std::function<bool(const FaceAttributes&)> cond) const;
1119
1123 bool split_edge_before(const Tuple& t) override;
1124 bool split_edge_after(const Tuple& t) override;
1125 bool marching_split_edge_before(const Tuple& t);
1126 bool marching_split_edge_after(const Tuple& t);
1127 bool split_face_before(const Tuple& t) override;
1128 bool split_face_after(const Tuple& t) override;
1129 bool split_tet_before(const Tuple& t) override;
1130 bool split_tet_after(const Tuple& t) override;
1131 bool invariants(const std::vector<Tuple>& tets) override;
1135 bool smooth_before(const Tuple& t) override;
1136 bool smooth_after(const Tuple& t) override;
1137
1157
1161 void execute_offset(const std::filesystem::path& output_file);
1162
1166 void optimize_offset(const std::filesystem::path& output_file);
1167
1169
1174 bool is_offset_face(const Tuple& f) const;
1175 bool is_offset_face(const size_t fid) const;
1176
1180 std::vector<Tuple> get_offset_surface_faces_for_vertex(const Tuple& t) const;
1181
1190 {
1191 std::atomic<int> attempted{0};
1192 std::atomic<int> before_bbox{0};
1193 std::atomic<int> before_unrounded{0};
1194 std::atomic<int> before_on_region{0};
1195 std::atomic<int> before_phase_b_not_offset{0};
1197 0};
1198 std::atomic<int> offset_attempted{0};
1199 std::atomic<int> interior_attempted{0};
1200
1201 void reset()
1202 {
1203 for (std::atomic<int>* c :
1204 {&attempted,
1205 &before_bbox,
1212 c->store(0);
1213 }
1214 }
1215 };
1216 mutable SmoothTrace m_smooth_trace;
1217
1218 void log_smooth_trace() const;
1219
1225 mutable std::atomic<int> m_phase_b_constrained{0};
1226
1232 std::unique_ptr<polysolve::nonlinear::Solver>>
1234
1243 enum class VClass { Bbox = 0, Offset = 1, Input = 2, Interior = 3, Count = 4 };
1244
1245 VClass vertex_class(const size_t vid) const
1246 {
1247 if (!m_vertex_attribute[vid].on_bbox_faces.empty()) return VClass::Bbox;
1248 if (m_vertex_extra[vid].m_is_on_offset) return VClass::Offset;
1249 if (m_vertex_extra[vid].m_is_on_region) return VClass::Input;
1250 return VClass::Interior;
1251 }
1252
1253 static const char* vclass_name(VClass c)
1254 {
1255 switch (c) {
1256 case VClass::Bbox: return "bbox";
1257 case VClass::Offset: return "offset";
1258 case VClass::Input: return "input";
1259 default: return "interior";
1260 }
1261 }
1262
1275 {
1276 std::atomic<int> attempted{0};
1277 std::atomic<int> refused_before{0};
1278 std::atomic<int> accepted{0};
1279 std::atomic<double> sum_disp{0.};
1280 std::atomic<double> max_disp{0.};
1281
1282 void add(double d)
1283 {
1284 ++accepted;
1285 for (double cur = sum_disp.load(); !sum_disp.compare_exchange_weak(cur, cur + d);) {
1286 }
1287 for (double cur = max_disp.load();
1288 d > cur && !max_disp.compare_exchange_weak(cur, d);) {
1289 }
1290 }
1291 };
1292 mutable std::array<MoveStats, size_t(VClass::Count)> m_move_stats;
1293
1307 {
1308 std::atomic<int> accepted{0};
1309 std::atomic<int> accepted_zero_tracked{0};
1310 std::atomic<int> added_deviation{0};
1311 std::atomic<int> added_dev_zero_tracked{0};
1312 std::atomic<double> max_single_step{0.};
1313
1314 void note(bool zero_tracked, double dev_before, double dev_after)
1315 {
1316 ++accepted;
1317 if (zero_tracked) ++accepted_zero_tracked;
1318 const double d = dev_after - dev_before;
1319 if (d > 0.) {
1321 if (zero_tracked) ++added_dev_zero_tracked;
1322 for (double cur = max_single_step.load();
1323 d > cur && !max_single_step.compare_exchange_weak(cur, d);) {
1324 }
1325 }
1326 }
1327 };
1328 mutable WallMoveStats m_wall_moves;
1329
1332 double wall_offplane_deviation(const size_t vid) const;
1333
1335 void log_vertex_movement(const char* when) const;
1336
1337
1339
1341
1351
1363 std::pair<double, double> compute_distance_deviation() const;
1364
1373 bool face_is_offset_surface_live(const Tuple& f) const;
1374 void diag_offset_bands(const char* tag) const;
1375
1377 bool cell_is_offset_band(const size_t tid) const { return m_tet_attribute[tid].label == 2; }
1378
1380 bool cell_is_input_complex(const size_t tid) const { return m_tet_attribute[tid].label == 1; }
1381
1391
1394 mutable size_t m_worst_dist_vid = static_cast<size_t>(-1);
1395 void log_worst_dist_vertex() const;
1396
1410 void log_worst_tet(const char* when) const;
1411
1416 std::vector<std::array<double, 8>> optimization_metrics;
1419 std::vector<std::array<int, 3>> churn_counts;
1424 std::vector<std::array<int, 3>> op_counts;
1426 bool m_converged = false;
1427
1431 std::atomic<int> iter_cnt_split_born{0};
1432 std::atomic<int> iter_cnt_recollapsed{0};
1433 std::atomic<int> iter_cnt_recollapsed_same_pass{0};
1434 std::atomic<int> iter_cnt_split{0};
1435 std::atomic<int> iter_cnt_collapse{0};
1436 std::atomic<int> iter_cnt_swap{0};
1437 std::atomic<int> iter_cnt_collapse_offset_removed{0};
1440 std::atomic<int> iter_cnt_swap_offset_reject{0};
1442 std::atomic<int> iter_cnt_split_offset_before{0};
1443 std::atomic<int> iter_cnt_split_offset_endpoints{0};
1444 std::atomic<int> iter_cnt_split_offset_base_reject{0};
1445 std::atomic<int> iter_cnt_split_offset_tried{0};
1446 std::atomic<int> iter_cnt_split_offset{0};
1447 std::atomic<int> iter_cnt_split_offset_reject{0};
1448
1449
1451
1452
1454 // The sizing field's bounds are user-configurable: see Parameters::min_sizing_scalar /
1455 // max_sizing_scalar / sizing_gradation. Refinement stops at min_sizing_scalar; coarsening
1456 // never exceeds max_sizing_scalar, since a sizing scalar above 1 would be coarser than the
1457 // target edge length the user asked for.
1458
1463 static double mean_ratio_metric(const Vector3d& p0, const Vector3d& p1, const Vector3d& p2);
1465
1467
1477 void marching_tets();
1478
1479
1481
1485 bool is_simplicially_embedded() const;
1486
1491 bool tet_is_simp_emb(const Tuple& t) const;
1492
1496 void simplicial_embedding();
1498
1503 void set_offset_tet_tags();
1504
1511 bool offset_is_manifold();
1512
1514 void write_input_complex(const std::string& path); // write components labeled to be offset
1515 void write_vtu(const std::string& path);
1516 // void write_msh(const std::string& file);
1517 void write_msh_groups(const std::string& file);
1519
1520private:
1527 {
1528 size_t v1_id;
1529 size_t v2_id;
1530 // The marching-tets placement and the offset's own data for the new vertex. The
1531 // shared attributes (position, rounding, bbox, order) are written in `after`.
1532 Vector3d new_v_pos;
1533 VertexExtra new_v_extra;
1534
1535 bool is_edge_on_region = false;
1536 bool is_edge_on_offset = false;
1537 bool is_edge_open_boundary = false;
1538
1539 std::vector<std::pair<FaceAttributes, std::array<size_t, 3>>> changed_faces;
1540
1541 // cache edge attributes
1542 EdgeAttributes split_e;
1543 std::map<size_t, EdgeAttributes> internal_e;
1544 std::map<simplex::Edge, EdgeAttributes> external_e; // edge is boundary edge (not link)
1545 std::map<simplex::Edge, EdgeAttributes> link_e; // link edge around splitted edge
1546
1547 // cache face attributes
1548 std::map<size_t, FaceSnapshot> split_f; // splitted faces
1549 std::map<simplex::Edge, FaceSnapshot> internal_f; // new faces created by split
1550 std::map<std::pair<simplex::Edge, size_t>, FaceSnapshot>
1551 external_f; // closed star boundary faces of splitted edge
1552
1553 // cache tet attributes
1554 std::map<simplex::Edge, TetAttributes> tets;
1555 };
1557
1559 {
1560 size_t v1_id;
1561 size_t v2_id;
1562 size_t v3_id;
1563
1564 // cache edge attributes
1565 std::map<simplex::Edge, EdgeAttributes> existing_e;
1566
1567 // cache face attributes
1568 std::map<simplex::Face, FaceSnapshot> existing_f;
1569 int splitf_label;
1570
1571 // cache tet attributes
1572 std::map<size_t, TetAttributes> tets;
1573 };
1575
1577 {
1578 std::array<size_t, 4> v_ids;
1579
1580 // cache retained edge attributes
1581 std::map<simplex::Edge, EdgeAttributes> existing_e;
1582
1583 // cache retained face attributes
1584 std::map<simplex::Face, FaceSnapshot> existing_f;
1585
1586 // cache tet attribute
1587 TetAttributes tet;
1588 };
1590
1591
1592public:
1593 // substructure functions
1594
1595 bool is_order_2_edge(const Tuple& e) const;
1596 bool is_order_2_edge(const std::array<size_t, 2>& e) const;
1597
1598 bool vertex_is_on_surface(const size_t vid) const override;
1599
1600 bool face_is_on_surface(const size_t fid) const override;
1601
1602 size_t get_order_of_vertex(const size_t vid) const override;
1606 void init_vertex_order();
1607
1608private: // helpers
1614 bool any_tag_present(const CellTag& tag1, const CellTag& tag2)
1615 {
1616 if (tag2.empty()) {
1617 return tag1.empty();
1618 }
1619 if (tag1.empty()) { // tag1 is ambient, tag2 is not
1620 return false;
1621 }
1622
1623 for (const int64_t& i : tag1) {
1624 if (tag2.find(i) != tag2.end()) {
1625 return true;
1626 }
1627 }
1628 return false;
1629 }
1630
1634 void sort_edges_by_length(std::vector<simplex::Edge>& edges)
1635 {
1636 std::sort(
1637 edges.begin(),
1638 edges.end(),
1639 [this](const simplex::Edge& e1, const simplex::Edge& e2) {
1640 double len1 = (m_vertex_attribute[e1.vertices()[0]].m_posf -
1641 m_vertex_attribute[e1.vertices()[1]].m_posf)
1642 .norm();
1643 double len2 = (m_vertex_attribute[e2.vertices()[0]].m_posf -
1644 m_vertex_attribute[e2.vertices()[1]].m_posf)
1645 .norm();
1646 return len1 > len2;
1647 });
1648 }
1649
1650public: // helpers
1657
1658 size_t get_partition_id(const Tuple& loc) const
1659 {
1660 return m_vertex_attribute[loc.vid(*this)].partition_id;
1661 }
1662
1666 std::vector<Tuple> get_face_adjacent_tets(const Tuple& t) const
1667 {
1668 std::vector<Tuple> adj_tets;
1669 auto tet_1 = t.switch_tetrahedron(*this);
1670 if (tet_1) {
1671 adj_tets.push_back(tet_1.value());
1672 }
1673 auto tet_2 = t.switch_face(*this).switch_tetrahedron(*this);
1674 if (tet_2) {
1675 adj_tets.push_back(tet_2.value());
1676 }
1677 auto tet_3 = t.switch_edge(*this).switch_face(*this).switch_tetrahedron(*this);
1678 if (tet_3) {
1679 adj_tets.push_back(tet_3.value());
1680 }
1681 auto tet_4 =
1682 t.switch_vertex(*this).switch_edge(*this).switch_face(*this).switch_tetrahedron(*this);
1683 if (tet_4) {
1684 adj_tets.push_back(tet_4.value());
1685 }
1686 return adj_tets;
1687 }
1688
1692 std::vector<size_t> connected_components_helper(const size_t& v_id)
1693 {
1694 auto onering_v_ids = get_one_ring_vids_for_vertex(v_id);
1695 std::vector<size_t> ret_v_ids;
1696 for (const size_t& other_v_id : onering_v_ids) {
1697 size_t e_id = tuple_from_edge({{v_id, other_v_id}}).eid(*this);
1698 if (m_edge_attribute[e_id].label != 0) { // edge labelled 1 or 2
1699 ret_v_ids.push_back(other_v_id);
1700 }
1701 }
1702 return ret_v_ids;
1703 }
1704
1709 {
1710 auto verts = get_vertices();
1711 for (const Tuple& v : verts) {
1712 size_t v_id = v.vid(*this);
1713 m_vertex_extra[v_id].component_id = 0;
1714 }
1715 }
1716};
1717
1718
1719} // namespace wmtk::components::topological_offset
Whether a codimension-1 simplex is tracked surface, and which bbox side it lies on.
Definition SurfaceTagAttributes.h:15
a Tuple refers to a global vid and a global tet id, and a local edge id and local face id
Definition TetMesh.h:50
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:800
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:342
std::vector< Tuple > get_vertices() const
Definition TetMesh.cpp:311
What tetwild and simwild's 3D mesh share.
Definition TetOptimizerMesh.h:45
AttributeContainerGroup m_vertex_attr_group
What p_vertex_attrs points at, so a derived class can register more.
Definition TetOptimizerMesh.h:92
AttributeContainerGroup m_face_attr_group
What p_face_attrs points at, so a derived class can register more.
Definition TetOptimizerMesh.h:107
Per-face data the shared optimizer knows nothing about.
Definition TopoOffsetTetMesh.h:99
Definition TopoOffsetTetMesh.h:106
The offset's tet mesh, on the shared 3D optimizer.
Definition TopoOffsetTetMesh.h:125
std::vector< std::array< int, 3 > > churn_counts
Definition TopoOffsetTetMesh.h:1419
std::atomic< int > m_phase_b_constrained
Definition TopoOffsetTetMesh.h:1225
std::atomic< int > iter_cnt_split_born
Definition TopoOffsetTetMesh.h:1431
std::vector< Tuple > get_face_adjacent_tets(const Tuple &t) const
get tets (as Tuples) that are face-adjacent to the given tet (as Tuple)
Definition TopoOffsetTetMesh.h:1666
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:138
size_t refine_sizing_around_worst(double) override
Phase A's stall-driven sizing refinement – TetWild's, on element quality.
Definition TopoOffsetTetMesh.cpp:1927
OptPhase m_phase
Which phase is running. Read by every hook that differs between them; see OptPhase.
Definition TopoOffsetTetMesh.h:155
std::atomic< int > iter_cnt_collapse_offset_reject
Operations refused because they would have left an offset face over tolerance.
Definition TopoOffsetTetMesh.h:1439
MatrixXd m_phi_V
Definition TopoOffsetTetMesh.h:1106
size_t m_worst_dist_vid
Definition TopoOffsetTetMesh.h:1394
double optimization_stop_metric() const override
Definition TopoOffsetTetMesh.h:710
std::tuple< double, double > optimization_quality_stats() override
(max, avg) Phi residual over the reachable offset surface, in units of its tolerance; the engine's st...
Definition TopoOffsetTetMesh.cpp:1876
wmtk::threading::enumerable_thread_specific< std::unique_ptr< polysolve::nonlinear::Solver > > m_phase_b_solver
Definition TopoOffsetTetMesh.h:1233
bool is_offset_face(const Tuple &f) const
true if face f has exactly one incident tet labelled 2 (offset), i.e. it lies on the boundary between...
Definition Smooth.cpp:357
FaceSamples offset_face_samples(const Tuple &f) const
The Phi residual at offset_residual_samples interior points of offset face f.
Definition TopoOffsetTetMesh.cpp:2322
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 it wraps).
Definition TopoOffsetTetMesh.h:1377
void report_outside_support(const char *when, const DistanceSplit &s) const
Definition TopoOffsetTetMesh.cpp:2406
void sort_edges_by_length(std::vector< simplex::Edge > &edges)
sort edge simplices in place by decreasing edge length
Definition TopoOffsetTetMesh.h:1634
wmtk::threading::enumerable_thread_specific< double > m_collapse_offset_rel_before
Definition TopoOffsetTetMesh.h:1030
VClass
Which of four DISJOINT classes a vertex is accounted to when measuring movement.
Definition TopoOffsetTetMesh.h:1243
wmtk::threading::enumerable_thread_specific< int > m_swap_label
Definition TopoOffsetTetMesh.h:1039
std::shared_ptr< SampleEnvelope > smoothing_energy_envelope(const size_t vid) const override
The offset surface is the one tracked surface with NO envelope, in either role.
Definition TopoOffsetTetMesh.h:604
void log_vertex_movement(const char *when) const
Per-class smoothing movement, plus the direct bounding-box invariant check.
Definition TopoOffsetTetMesh.cpp:2516
uint64_t face_mask(const std::array< size_t, 3 > &vids) const
A face lies on a boundary only if all of it does: the AND of its corners' masks.
Definition TopoOffsetTetMesh.h:546
void for_each_offset_face_sample(const Tuple &f, Visit &&visit) const
The interior lattice a face is sampled on, handed to visit one point at a time.
Definition TopoOffsetTetMesh.h:823
void log_quality_spike(size_t tid, double before, double after, const char *kind) const
Report one tet crossing into absurd quality: its geometry, and why it is degenerate.
Definition TopoOffsetTetMesh.cpp:2658
size_t get_order_of_vertex(const size_t vid) const override
Get the order of a vertex.
Definition TopoOffsetTetMesh.cpp:1058
void reset_connected_components()
reset connected component assignments.
Definition TopoOffsetTetMesh.h:1708
size_t flood_fill()
label connected simplicial complex components (simplices labelled 1 or 2)
Definition TopoOffsetTetMesh.cpp:985
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)
initialize TetMesh from vertex, tet, and tag data
Definition TopoOffsetTetMesh.cpp:36
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:531
std::vector< bool > band_vertex_mask() const
Definition TopoOffsetTetMesh.cpp:2336
void init_offset_sizing_field()
Seed the sizing field from the offset surface's current edge lengths, once, before the first pass....
Definition TopoOffsetTetMesh.cpp:2092
std::shared_ptr< SampleEnvelope > envelope_for_mask(uint64_t mask) const
The envelope a simplex with this boundary mask is contained in, or null.
Definition TopoOffsetTetMesh.cpp:856
void log_worst_tet(const char *when) const
Diagnostic: dump the single worst-quality tet and everything that could be pinning it,...
Definition TopoOffsetTetMesh.cpp:2726
double offset_gradient_tolerance() const
The convergence tolerance: the bound on |grad (Phi - c)^2| at a band vertex.
Definition TopoOffsetTetMesh.h:738
bool cell_in_region(const size_t tid) const
Whether tet tid belongs to the closed offset region, read from its TAGS.
Definition TopoOffsetTetMesh.h:394
bool collapse_edge_before(const Tuple &t) override
Collapse policy that is the offset's own.
Definition Collapse.cpp:12
void marching_tets()
execute simplistic marching tets. All edges with one vertex labelled 0 and the other 1/2 are split,...
Definition TopoOffsetTetMesh.cpp:3403
bool smooth_before(const Tuple &t) override
Definition Smooth.cpp:14
bool split_edge_before(const Tuple &t) override
Definition EdgeSplittingTet.cpp:13
std::shared_ptr< OffsetPotential3D > m_offset_potential
The smooth offset potential Phi of the input complex, as loaded.
Definition TopoOffsetTetMesh.h:274
void init_offset_potential()
Build the smooth offset potential from the extraction init_input_complex_bvh() kept.
Definition TopoOffsetTetMesh.cpp:897
void set_offset_tet_tags()
update 'tags' data for tets in the offset region (tets labelled 2) based on the given offset tag valu...
Definition TopoOffsetTetMesh.cpp:3475
bool split_tet_after(const Tuple &t) override
Compute the attributes for the added simplices.
Definition EdgeSplittingTet.cpp:449
bool face_is_on_surface(const size_t fid) const override
Is a face part of the substructure.
Definition TopoOffsetTetMesh.cpp:1053
bool ambient_assert()
check that the ambient tag does not overlap with any other tags
Definition TopoOffsetTetMesh.cpp:350
std::shared_ptr< SampleEnvelope > m_input_complex_envelope
The exact-kind envelope of the input complex, built only for offset_field "euclidean"....
Definition TopoOffsetTetMesh.h:285
void compute_vertex_partition()
assign each vertex a partition id (by spatial Morton order) for the parallel ExecutePass policies (kP...
Definition TopoOffsetTetMesh.cpp:1077
double amips_rel_at_face(const Tuple &f) const
... and the worst of the (up to two) cells a face separates.
Definition TopoOffsetTetMesh.cpp:2441
double max_band_vertex_distance() const
Definition TopoOffsetTetMesh.cpp:2262
double offset_residual_tolerance() const
Definition TopoOffsetTetMesh.h:720
void mark_input_complex_vertices()
Definition TopoOffsetTetMesh.cpp:587
std::atomic< int > iter_cnt_split_offset_before
Splits of an OFFSET-surface edge: reached split_edge_after, accepted, refused there.
Definition TopoOffsetTetMesh.h:1442
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:1380
std::shared_ptr< SampleEnvelope > smoothing_containment_envelope(const size_t vid) const override
... and it is not CONTAINED by one either.
Definition TopoOffsetTetMesh.h:654
bool marching_split_edge_after(const Tuple &t)
Definition EdgeSplittingTet.cpp:154
bool face_is_offset(const size_t fid) const
Whether face fid is on the offset boundary (as opposed to the input complex).
Definition TopoOffsetTetMesh.h:401
bool split_face_before(const Tuple &t) override
User specified preparations and desideratas for a face split before changing the connectivity.
Definition EdgeSplittingTet.cpp:248
Parameters & m_offset_params
Definition TopoOffsetTetMesh.h:336
bool offset_is_manifold()
verify that the closed offset region (simplices labelled 1 or 2) form a manifold region....
Definition TopoOffsetTetMesh.cpp:3510
uint64_t vertex_boundary_mask(const size_t vid) const
The tag boundaries this vertex lies on – the raw mask, gated on the vertex still being region geometr...
Definition TopoOffsetTetMesh.h:540
void write_optimization_debug_output(const std::string &path) override
Definition TopoOffsetTetMesh.h:674
bool smooth_offset_vertex_backtracking(const Tuple &t)
Phase B's placement of an offset-surface vertex: the offset energy alone, then backtrack into the one...
Definition Smooth.cpp:136
void init_vertex_order()
Compute the vertex order for every vertex.
Definition TopoOffsetTetMesh.cpp:1063
static double mean_ratio_metric(const Vector3d &p0, const Vector3d &p1, const Vector3d &p2)
unsigned mean ratio metric of a triangle: 2*sqrt(3)*area / (sum of squared edge lengths)....
Definition TopoOffsetTetMesh.cpp:1175
void execute_offset(const std::filesystem::path &output_file)
main function from which all others are called
Definition TopoOffsetTetMesh.cpp:1113
bool smooth_interior_vertex_phase_b(const Tuple &t)
Phase B's placement of a background vertex: Newton on the one-ring AMIPS energy, to this vertex's own...
Definition Smooth.cpp:297
bool is_simplicially_embedded() const
check if the input complex (simplices labelled 1) are simplicially embedded w.r.t....
Definition TopoOffsetTetMesh.cpp:3264
bool any_tag_present(const CellTag &tag1, const CellTag &tag2)
determine if any tag from tag1 is also present in tag2.
Definition TopoOffsetTetMesh.h:1614
static double spike_amips(const double q)
Definition TopoOffsetTetMesh.h:473
bool is_edge_on_region(const Tuple &loc)
Definition TopoOffsetTetMesh.cpp:295
bool band_vertex_is_reachable(const size_t vid) const
Definition TopoOffsetTetMesh.h:783
bool m_spike_track
Diagnostic: whether set_cell_quality reports tets crossing into absurd quality.
Definition TopoOffsetTetMesh.h:490
std::shared_ptr< SampleEnvelope > m_offset_envelope
The tube the offset surface may not leave during Phase A. Null in Phase B.
Definition TopoOffsetTetMesh.h:182
double wall_offplane_deviation(const size_t vid) const
Definition TopoOffsetTetMesh.cpp:2504
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:379
std::vector< size_t > connected_components_helper(const size_t &v_id)
get all one-ring vertices through input simplices (labelled 1)
Definition TopoOffsetTetMesh.h:1692
std::map< int64_t, std::shared_ptr< SampleEnvelope > > m_tag_envelopes
One containment envelope per input tag, ambient included. Both phases.
Definition TopoOffsetTetMesh.h:304
bool collapse_before_vertex(size_t v1, size_t v2, double edge_length) override
Definition Collapse.cpp:31
uint64_t tag_bits(const CellTag &tags) const
Definition TopoOffsetTetMesh.h:519
static constexpr int OFFSET_SURFACE_CLASS
SurfaceTagAttributes::m_surface_class for the offset boundary.
Definition TopoOffsetTetMesh.h:133
void simplicial_embedding()
make mesh a simplicial embedding of the input complex (simplices labelled 1)
Definition TopoOffsetTetMesh.cpp:3309
void warn_if_offset_reaches_domain_boundary() const
Warn if conservative growth ran into the bounding box.
Definition TopoOffsetTetMesh.cpp:2471
double band_vertex_residual(const size_t vid) const
Definition TopoOffsetTetMesh.cpp:2315
void init_input_complex_bvh()
Build the input complex's BVH and keep the extraction the potential needs.
Definition TopoOffsetTetMesh.cpp:626
std::shared_ptr< SampleEnvelope > surface_envelope_for_face(const std::array< size_t, 3 > &vids) const override
Envelope the tracked-surface triangle vids must stay inside.
Definition TopoOffsetTetMesh.h:563
bool allow_surface_swap() const override
Definition TopoOffsetTetMesh.h:587
bool is_open_boundary_edge(const Tuple &e) override
Definition TopoOffsetTetMesh.h:1011
bool swap_before_interior(const std::vector< size_t > &tids) override
Which tag the tets a swap creates should carry.
Definition Swap.cpp:52
bool face_is_region(const size_t fid) const
Whether face fid carries input-complex geometry.
Definition TopoOffsetTetMesh.h:407
double m_spike_record
Largest AMIPS seen so far; a new maximum is logged, which bounds the output.
Definition TopoOffsetTetMesh.h:496
void label_input_complex()
label input simplicial complex simplices, as defined in m_offset_params.offset_selection
Definition TopoOffsetTetMesh.cpp:369
std::pair< double, double > compute_distance_deviation() const
How far the offset surface is from where it should be: {max, avg} over its vertices.
Definition TopoOffsetTetMesh.cpp:2286
void split_after_vertex(size_t v_new, bool is_edge_open_boundary) override
Application metadata not represented by the shared vertex attributes.
Definition EdgeSplittingTet.cpp:592
void rebuild_offset_envelope()
Definition TopoOffsetTetMesh.cpp:1247
std::map< uint64_t, std::shared_ptr< SampleEnvelope > > m_isect_cache
Definition TopoOffsetTetMesh.h:312
std::vector< std::array< int, 3 > > op_counts
Definition TopoOffsetTetMesh.h:1424
bool m_converged
Whether the optimization met both convergence criteria before the iteration cap.
Definition TopoOffsetTetMesh.h:1426
void optimize_offset_alternating()
Definition TopoOffsetTetMesh.cpp:1666
GradientSplit gradient_split(bool include_face_samples=true) const
Definition TopoOffsetTetMesh.cpp:1288
bool swap_capture_tag(const std::vector< size_t > &tids)
Definition Swap.cpp:18
bool tet_is_simp_emb(const Tuple &t) const
check if a tet satisfies simpicial embedding criteria w.r.t. input complex (simplices labelled 1)
Definition TopoOffsetTetMesh.cpp:3281
double m_spike_threshold
AMIPS above which a tet counts as absurd rather than merely bad.
Definition TopoOffsetTetMesh.h:492
bool invariants(const std::vector< Tuple > &tets) override
Definition TopoOffsetTetMesh.cpp:3552
PhaseBSub
Phase B's two sub-sweeps, run in this order within every smoothing pass.
Definition TopoOffsetTetMesh.h:170
std::map< int64_t, int > m_tag_bit
Definition TopoOffsetTetMesh.h:308
bool split_tet_before(const Tuple &t) override
User specified preparations and desideratas for a tet split before changing the connectivity.
Definition EdgeSplittingTet.cpp:412
double m_spike_ordinary
AMIPS below which a tet counts as ordinary, so ordinary -> absurd is one operation's doing.
Definition TopoOffsetTetMesh.h:494
size_t phase_b_smooth()
Definition TopoOffsetTetMesh.cpp:1421
void optimize_offset(const std::filesystem::path &output_file)
optimize the offset
Definition TopoOffsetTetMesh.cpp:2970
bool vertex_is_on_region(const size_t vid) const
Is this vertex on a region boundary – a tag boundary, or the domain wall.
Definition TopoOffsetTetMesh.h:512
size_t update_band_sizing_from_tolerance()
Per-vertex band sizing update, run after Phase B. Returns the number changed.
Definition TopoOffsetTetMesh.cpp:1580
bool split_before_cells(const Tuple &edge, const std::vector< Tuple > &parents) override
Split policy that is the offset's own.
Definition EdgeSplittingTet.cpp:515
bool vertex_is_on_surface(const size_t vid) const override
Is a vertex part of the substructure.
Definition TopoOffsetTetMesh.cpp:1041
bool empty_input_complex()
check if the input complex is empty. Only valid after calling init_from_image(...)....
Definition TopoOffsetTetMesh.cpp:613
bool split_face_after(const Tuple &t) override
Compute the attributes for the added simplices.
Definition EdgeSplittingTet.cpp:322
double face_criterion_rel(const Tuple &f) const
Definition TopoOffsetTetMesh.cpp:2450
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 Swap.cpp:93
std::vector< std::array< double, 8 > > optimization_metrics
Definition TopoOffsetTetMesh.h:1416
double cell_quality_rel(const size_t tid) const
Definition TopoOffsetTetMesh.cpp:2427
void check_offset_within_support(const char *when) const
Stop the run if any reachable offset-surface vertex has left the potential's support.
Definition TopoOffsetTetMesh.cpp:2401
bool split_adjust_position(size_t v_new, const std::vector< Tuple > &children) override
Definition EdgeSplittingTet.cpp:578
OptPhase
Which half of the alternating optimization is running.
Definition TopoOffsetTetMesh.h:152
std::vector< Tuple > get_offset_surface_faces_for_vertex(const Tuple &t) const
offset-surface faces (see is_offset_face()) incident to vertex t
Definition Smooth.cpp:367
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 TopoOffsetTetMesh.cpp:2164
wmtk::threading::enumerable_thread_specific< CellTag > m_swap_tag
The tag swap_after_cells writes onto the tets the swap created, chosen in before.
Definition TopoOffsetTetMesh.h:1035
double phase_b_band_gradient_linf()
L-inf over offset vertices of the gradient of each vertex's own placement objective – the energy Phas...
Definition TopoOffsetTetMesh.cpp:1407
double cell_quality(const size_t tid) const override
The quality of cell tid, and how to write it.
Definition TopoOffsetTetMesh.h:444
void write_phi_grid(const std::string &path, int n) const
Definition TopoOffsetTetMesh.cpp:3650
bool smooth_after(const Tuple &t) override
Every vertex goes through the shared smoother. Nothing is dispatched anywhere else.
Definition Smooth.cpp:93
void check_no_vertex_on_both_surfaces(const char *when) const
Definition TopoOffsetTetMesh.cpp:1189
Per-vertex data the shared optimizer knows nothing about.
Definition TopoOffsetTetMesh.h:46
bool m_is_on_input_complex
Definition TopoOffsetTetMesh.h:61
uint32_t m_born_epoch
Definition TopoOffsetTetMesh.h:79
bool m_is_on_region
Definition TopoOffsetTetMesh.h:53
uint64_t m_boundary_mask
Definition TopoOffsetTetMesh.h:71
Definition Simplex.hpp:46
Definition enumerable_thread_specific.hpp:26
bool smooth_quality_veto
Definition OptimizerParameters.h:183
What the offset needs on top of the parameters every wmtk optimizer shares.
Definition Parameters.h:20
bool debug_output_per_pass
Definition Parameters.h:45
The offset surface's residual. Every band vertex and every face sample counts toward the max and the ...
Definition TopoOffsetTetMesh.h:878
A face's shared surface tags together with the offset's own label.
Definition TopoOffsetTetMesh.h:356
The band's placement gradient: |grad (Phi(x) - c)^2| over the offset surface – at every band vertex A...
Definition TopoOffsetTetMesh.h:915
double max_at_vertex
max_reachable, split by where it was measured – see the class comment.
Definition TopoOffsetTetMesh.h:920
size_t n_face_samples
How many of n_reachable came from face interiors rather than vertices.
Definition TopoOffsetTetMesh.h:922
How far each class of vertex actually gets during smoothing.
Definition TopoOffsetTetMesh.h:1275
std::atomic< long > order2
order-2 vertex into lower order
Definition TopoOffsetTetMesh.h:985
std::atomic< long > invariant
would put input-complex and offset on one vertex
Definition TopoOffsetTetMesh.h:982
std::atomic< long > class_region
region vertex may not leave its surface
Definition TopoOffsetTetMesh.h:983
std::atomic< long > sublink
substructure_link_condition refusal
Definition TopoOffsetTetMesh.h:986
std::atomic< long > class_offset
offset vertex may not leave its surface
Definition TopoOffsetTetMesh.h:984
Why smoothing refused a vertex, one counter per site where it can say no.
Definition TopoOffsetTetMesh.h:1190
std::atomic< int > before_bbox
base smooth_before said no: on the bounding box
Definition TopoOffsetTetMesh.h:1192
std::atomic< int > before_unrounded
base smooth_before said no: could not round
Definition TopoOffsetTetMesh.h:1193
std::atomic< int > interior_attempted
reached it as an ordinary interior vertex
Definition TopoOffsetTetMesh.h:1199
std::atomic< int > attempted
smooth_before() entered
Definition TopoOffsetTetMesh.h:1191
std::atomic< int > before_phase_b_not_offset
Phase B: wrong class for this sub-sweep.
Definition TopoOffsetTetMesh.h:1195
std::atomic< int > before_phase_b_enveloped_background
Phase B: envelope-held, stencilled.
Definition TopoOffsetTetMesh.h:1196
std::atomic< int > before_on_region
input complex, frozen
Definition TopoOffsetTetMesh.h:1194
std::atomic< int > offset_attempted
reached the smoother carrying the offset term
Definition TopoOffsetTetMesh.h:1198
Which of two mechanisms lets a wall vertex end up off its wall.
Definition TopoOffsetTetMesh.h:1307
std::atomic< int > added_deviation
... that increased off-plane deviation
Definition TopoOffsetTetMesh.h:1310
std::atomic< int > accepted_zero_tracked
... of those, with NO tracked face at it
Definition TopoOffsetTetMesh.h:1309
std::atomic< int > added_dev_zero_tracked
... and had no tracked face: mechanism (2)
Definition TopoOffsetTetMesh.h:1311
std::atomic< double > max_single_step
largest off-plane deviation one move added
Definition TopoOffsetTetMesh.h:1312
std::atomic< int > accepted
accepted smoothing moves on a vertex with wall flags
Definition TopoOffsetTetMesh.h:1308