|
Wildmeshing Toolkit
|
The offset's tet mesh, on the shared 3D optimizer. More...
#include <TopoOffsetTetMesh.h>
Classes | |
| struct | CoarsenCollapse |
| struct | CollapseVolume |
| struct | DebugTimeline |
| struct | DistanceSplit |
| struct | EdgeSplitCache |
| struct | EnergyCriterion |
| The "energy_gradient" criterion: the front's Newton-step ratios and the refinable faces. The 3D twin of TopoOffsetTriMesh::EnergyCriterion. More... | |
| struct | FaceSamples |
| struct | FaceSnapshot |
| A face's shared surface tags together with the offset's own label. More... | |
| struct | FaceSplitCache |
| struct | FoldCensus |
| struct | FrontSolveRecord |
| DEBUG_output only: every front solve since the last debug frame, so the frames show per vertex what m_newton_front counts per pass (frame fields front_newton_iters and front_newton_status, see write_vtu()). More... | |
| struct | GradientSplit |
| The convergence criterion's own split: ||grad (Phi - c)^2|| at band vertices plus the face-interior chord diagnostic. Same fields as the 2D GradientSplit, face samples in place of edge samples. More... | |
| struct | MeshSnapshot |
| An in-memory image of the mesh, to put it back as it was (for growth_mode "smart": the revert after a failed attempt). Taken on a consolidated mesh: the tets in tid order, each as oriented_tet_vids() gives it – the order the face and edge slots (4 tid + j, 6 tid + j) are keyed on – and copies of every per-element collection over the live slots: the vertex group (m_vertex_attribute, m_vertex_extra), the face group (m_face_attribute, m_face_extra), the edge group (m_edge_attribute) and m_tet_attribute. More... | |
| struct | OptSplitCache |
| struct | SmartGrowthSchedule |
| growth_mode "smart": which target to try next from the last converged distance, and what an attempt's result means. Pure, so it is unit-tested apart from the mesh. More... | |
| struct | SmoothTrace |
| What smoothing did with each class of vertex, per pass. Same fields as 2D. More... | |
| struct | SwapRecord |
| The swap in flight on this thread, as candidate_energy() and the sanity check read it (see tet_energy(), CANDIDATE CELLS). Filled by swap_record_fill(), which swap_before_interior() and swap_before_surface() call once they have accepted the ring – in the engine's swap_edge_before(), swap_edge_44_before() and swap_edge_56_before(), all of which run before the connectivity changes and so before the 4-4 / 5-6 case search, and in swap_face_before() before its gate. Cleared at the top of both hooks, so no record outlives its swap into the next one on the thread. A 3-2 has no case search and no gate and gets no record. More... | |
| struct | SwapSurfaceSides |
| struct | SweptCounters |
| struct | TetSplitCache |
| struct | VtuFrame |
| Everything write_vtu() writes, as arrays: the mesh is read once, by gather_vtu_frame(), and write_vtu_frame() reads this and nothing else – so a debug frame's encoding and writing can run on another thread while the loop goes on (write_debug_frame()). Point arrays are vert_capacity() rows, indexed by vid; see gather_vtu_frame() for what each field is. tags[i] is named tag_names[i]; the last entry of tags is offset_tag. More... | |
Public Types | |
| enum class | EdgeSplitMode { Midpoint = 0 , SphereTrace = 1 , Optimization = 5 } |
| enum class | OpRules { Loop , TetWild } |
| Which rules the operations and the smoother follow – the one switch for them. More... | |
| using | VertexExtraCol = wmtk::AttributeCollection< VertexExtra > |
| using | EdgeAttCol = wmtk::AttributeCollection< EdgeAttributes > |
| using | FaceExtraCol = wmtk::AttributeCollection< FaceExtra > |
| using | TetAttCol = wmtk::AttributeCollection< TetAttributes > |
Public Types inherited from wmtk::TetOptimizerMesh | |
| using | FaceAttributes = wmtk::SurfaceTagAttributes |
| using | VertAttCol = AttributeCollection< VertexAttributes > |
| using | FaceAttCol = AttributeCollection< FaceAttributes > |
| using | EdgeAttCol = AttributeCollection< FeatureEdgeAttributes > |
| using | PositionKey = std::array< std::array< double, 3 >, 3 > |
| A tracked face by its corners' positions, sorted. | |
| using | SurfaceTopoSignature = wmtk::utils::SurfaceTopoSignature |
| using | FeaturePointAttCol = AttributeCollection< FeaturePointAttributes > |
Public Types inherited from wmtk::TetMesh | |
| enum class | OpKind : int { split , collapse , swap_32 , swap_44 , swap_56 , swap_face , COUNT } |
| enum class | OpEvent : int { attempt , before_hook , link_condition , no_return_tet , duplicate_tet , topology , valence , boundary , exists , no_case_allowed , no_better_case , out_of_slots , after_hook , invariants , committed , screened , COUNT } |
| template<typename T > | |
| using | vector = std::vector< T > |
| using | VertexMutex = wmtk::threading::VertexMutex |
Public Member Functions | |
| bool | is_sheet_tag (int64_t tag) const |
Whether tag names an open sheet: the envelope surface group or a face tag. | |
| void | classify_sheet_faces () |
| Mark the mesh faces that lie on the input's envelope surface group (FaceExtra::on_sheet). | |
| 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_input_complex_bvh() captured them), the distance to m_input_complex_envelope everywhere else. | |
| 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. | |
| double | front_vertex_relative_residual (size_t vid) const |
| double | front_vertex_residual_length (size_t vid) const |
| Vector3d | front_vertex_field_gradient (size_t vid) const |
| void | init_region_potentials (double delta, double effective_factor) |
| void | assign_band_regions (bool log=true) |
| void | log_front_profile (size_t vid) |
| Diagnostic: the front objective of one vertex along its normal, offset term vs total. | |
| int | vertex_region (const size_t vid) const |
| int | edge_region (const size_t va, const size_t vb) const |
| const OffsetPotential3D & | potential_for_region (const int region) const |
| const OffsetPotential3D & | potential_for (const size_t vid) const |
| std::shared_ptr< const OffsetPotential3D > | potential_ptr_for (const size_t vid) const |
| const OffsetPotential3D & | potential_for_edge (const size_t va, const size_t vb) const |
| const OffsetPotential3D & | potential_for_face (const Tuple &f) const |
| The field of the band cell a live offset face belongs to. | |
| 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 every HELD face (face_is_held()), built by build_envelopes() before anything moves – after the simplicial embedding, before the repulsion and the march – and swapped for a wider one only for a TetWild pass (build_final_envelopes()). The offset surface is held by nothing in the loop; only a TetWild pass holds it, to m_offset_envelope. | |
| bool | vertex_is_held (size_t vid) const |
| bool | face_is_complex_boundary (const Tuple &f) const |
| void | build_envelopes () |
| void | build_final_envelopes () |
| void | release_final_envelopes () |
| double | tetwild_pass (const char *what, int max_its, bool coarsen=false) |
| void | optimize_construction_pass () |
| optimize_construction: TetWild on the mesh as constructed, before the loop (and before an init_optimize loop). Called by optimize_offset() after the "construction" frame. | |
| MeshSnapshot | take_snapshot () |
| void | restore_snapshot (const MeshSnapshot &s) |
| bool | tetwild_rules () const |
| void | build_offset_envelope () |
| void | check_no_vertex_on_both_surfaces (const char *when) const |
| 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) |
| 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 in one call and then each of its ops[3] smoothing passes in a call of its own, front_pocket_split() right before each: the engine's group runs its operation pass straight into smooth_all_vertices(k), and smoothing_pass_done() runs before a pass's debug frame, so neither offers the point before a smoothing pass. Off: local_operations(ops). where names the group in the [pocket split] lines. | |
| void | front_pocket_split (const std::string &where) |
| EXPERIMENTAL_front_pocket_split. Invariant: no cell is PINNED when a smoothing pass of the loop starts. A pinned cell is a cell outside the input complex, band or not, whose four corners are all front vertices (on the offset surface, VertexExtra::m_is_on_offset). | |
| void | label_cleanup (const std::string &where) |
| EXPERIMENTAL_label_cleanup (see the spec). The one exception to labels being fixed after construction: a cell on the band / non-band interface whose own geometry puts it on the other side of the level set takes the other label, where the flip keeps the topology of both. Invariant: the number of connected components (face adjacency) of the band and of the non-band region is the same before and after; a change throws. | |
| double | label_flip_surface_change (size_t tid) const |
| void | linear_growth () |
| void | smart_growth () |
| double | front_gradient_linf () |
| TopoOffsetTetMesh (Parameters &_m_offset_params, int _num_threads=0) | |
| double | cell_quality (const size_t tid) const override |
The quality of cell tid, and how to write it. | |
| void | set_cell_quality (const size_t tid, const double q) override |
| 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, nothing hypothetical. E = the sum of E(t) over the live cells (global_energy()) is the one energy of the 3D loop. | |
| bool | band_term_cancels (const std::vector< size_t > &tids) const |
| bool | swept_rule () const |
| "exact_swept" (EXACT SWEPT CHANGE). | |
| double | band_swept_change (size_t vid, const Vector3d &x0, const Vector3d &x1, ExactBandIntegral::Stats *st=nullptr) const |
| void | band_swept_faces (size_t vid, const Vector3d &x0, std::vector< std::array< Vector3d, 2 > > &faces) const |
| double | band_swept_sum (const Vector3d &x0, const Vector3d &x1, const std::vector< std::array< Vector3d, 2 > > &faces, ExactBandIntegral::Stats *st=nullptr) const |
| 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 |
| double | swap_swept_change (const std::vector< std::array< size_t, 4 > > &new_band, bool flip) |
| bool | swept_accept (size_t vid, const std::vector< size_t > &ring, const Vector3d &x0, double amips_before, SweptCounters &c) |
| 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_energy() over the one-ring, the rule every other move answers to (under TetWild's rules, m_op_rules, the base's max over the ring). The step takes the first projected position where it is strictly below the sum before the move. | |
| double | vol_amips3 (const std::array< size_t, 4 > &vids, const double amips3) const |
| 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: | |
| 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. | |
| double | band_amips_weight () const |
| 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(). | |
| 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. | |
| double | candidate_energy (const std::array< size_t, 4 > &vids) |
| int | candidate_label (const std::array< size_t, 4 > &vids) |
| double | global_energy () const |
| double | offset_energy () const |
| double | offset_term_weight () const |
| void | set_vertex_position (const size_t vid, const Vector3d &p) |
| Place a vertex, keeping its exact and rounded coordinates in step. | |
| bool | face_is_offset (const size_t fid) const |
| bool | face_is_region (const size_t fid) const |
| FaceSnapshot | face_snapshot (const size_t fid) const |
| void | restore_face (const size_t fid, const FaceSnapshot &s) |
| void | label_offset_boundary () |
| Tag the two tracked surfaces for the optimization phase. | |
| bool | cell_in_region (const size_t tid) const |
| bool | cell_is_input_complex (const size_t tid) const |
Whether tet tid is part of the INPUT complex the band wraps. | |
| 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). | |
| bool | cell_is_plastic (const size_t tid) const |
| bool | plastic_rest (const size_t tid, Eigen::Matrix3d &R) const |
| void | stamp_plastic_rests () |
| void | stamp_rest_cell (size_t tid) |
| 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. | |
| 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. before are the cells it replaces (live, read in its before-hook), after_labels the labels of the cells it will make or change. True when the plastic medium is on, every before cell is plastic (all_plastic()) and every after cell is plastic (a label other than 1 and 2: no band cell, no input-complex cell). The rests are up to date at every rule – restamped after every smoothing pass and, in the operations, on every cell made or changed – so the before cells are at their rests and the after cells are stamped before any rule reads them. | |
| void | optimize_offset (const std::filesystem::path &output_file) |
| The 3D optimization phase: split / collapse / swap / smooth on the shared driver. | |
| std::pair< double, double > | compute_distance_deviation () const |
| How far the offset surface is from where it should be: {max, avg} over vertices. | |
| void | log_worst_dist_vertex () const |
| bool | face_is_offset_surface_live (const Tuple &f) const |
| The band's outer surface, recomputed live rather than read from the cached class. | |
| bool | offset_face_normal_out_of_band (const Tuple &f, Vector3d &n) const |
| size_t | count_real_folds (std::vector< char > *corners=nullptr, std::vector< std::array< size_t, 3 > > *faces=nullptr) const |
| 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. | |
| std::vector< std::array< size_t, 3 > > | offset_surface_faces () const |
| Every live offset-surface face as a sorted vertex triple. | |
| std::vector< Tuple > | offset_surface_faces_live_at (size_t vid) const |
| The live offset-surface faces incident to vid. | |
| bool | vertex_has_live_offset_face (size_t vid) const |
| template<typename F > | |
| bool | for_each_live_offset_face_at (size_t vid, F &&fn) const |
| void | refresh_offset_membership (size_t vid) |
| Re-derive m_is_on_offset for one vertex from the cell labels, exactly. | |
| std::pair< size_t, size_t > | offset_membership_mismatches () const |
| void | check_offset_membership (const char *when) const |
| void | report_offset_face_lookup_misses (const char *when) const |
| 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, oriented out of its band cell (front_face_band_cell(); away from the cell's fourth corner), and the field's unit normal at its centroid, the gradient of the distance d to the input complex (m_input_complex_envelope's nearest primitive). 0 where the face faces along the field, 180 where it faces back toward the input. NaN where it cannot be measured: no band cell, a degenerate face, no input envelope yet, or a zero gradient. The face is a foldover when the angle exceeds foldover_angle (front_face_is_foldover()). Debug frames write both per face on the _off file; the end of optimize_offset() counts the folded faces. The 2D code still labels vertices (TopoOffsetTriMesh). | |
| bool | front_face_out_normal (size_t a, size_t b, size_t c, Vector3d &n) const |
| bool | front_face_out_normal_in (size_t a, size_t b, size_t c, size_t band_tid, Vector3d &n) const |
| 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()). | |
| double | front_face_angle_to_field (size_t a, size_t b, size_t c, const Vector3d &n) const |
| 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. | |
| FoldCensus | fold_census () const |
| bool | fold_reject_active () const |
| void | folded_front_faces_at (size_t vid, std::set< std::array< size_t, 3 > > &out) const |
| bool | face_folded_in (size_t a, size_t b, size_t c, const std::vector< size_t > &cells) const |
| void | wait_debug_frame_write () |
| std::string | flip_funnel_report () const |
| void | flip_funnel_reset () |
| void | op_event (OpKind k, OpEvent e) const override |
| bool | split_edge_is_due (const Tuple &e) const |
| bool | marching_split_edge_before (const Tuple &t) |
| bool | marching_split_edge_after (const Tuple &t) |
| 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 input complex, label != 0) towards p_out (the background endpoint) for the point where d(x) = D, D = m_construction_distance and d(x) the distance to the input complex, input_complex_distance(). From t = 0 the trace evaluates d at the current point and steps forward by D - d, the largest step that cannot cross the level set (d is 1-Lipschitz); it stops when |d - D| <= sphere_trace_target_rel_tol x D and returns true with p_new there. It returns false, p_new untouched, as soon as the current point reaches or passes p_out (t >= L: the level set is not on the edge) or would move behind p_in (d(p_in) already beyond the target); the caller then places the plain midpoint. Every step taken is longer than the tolerance, so the trace ends within L / (tol x D) steps; steps returns how many it took. No snapping away from the endpoints: a point found arbitrarily close to p_out is used as is. | |
| bool | collapse_edge_before (const Tuple &t) override |
| Reject any collapse that violates the substructure link condition, remember the survivor's sizing for sizing_collapse_min = false, and cache the energy rule's before-half. | |
| bool | collapse_edge_after (const Tuple &t) override |
| The coarsening bar, the sizing restore and the rest re-stamp, after the base accepted. | |
| bool | collapse_before_vertex (size_t v1, size_t v2, double edge_length) override |
| bool | collapse_after_connectivity (size_t v1, size_t v2, const std::vector< std::array< size_t, 2 > > &boundary_edges) override |
| bool | collapse_is_order_2_edge (const std::array< size_t, 2 > &e) override |
| void | collapse_after_vertex (size_t v1, size_t v2) override |
| 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 (class match, same boundary). The geometric half is the shared swap's containment check. Both also take the energy rule's before-half (m_swap_energy_before) and fill the record candidate cells take their labels from (SwapRecord). See Optimize3d.cpp. | |
| bool | swap_before_surface (const std::vector< size_t > &tids, size_t a, size_t b, size_t c, size_t d) override |
| 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. | |
| 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, and which of the two tracked surfaces the new vertex joined. See EdgeSplittingTet.cpp. | |
| 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. | |
| bool | split_adjust_position (size_t v_new, const std::vector< Tuple > &children) override |
| void | split_after_vertex (size_t v_new, bool is_edge_open_boundary) override |
| Application metadata not represented by the shared vertex attributes. | |
| bool | is_open_boundary_edge (const Tuple &e) override |
| bool | smooth_before (const Tuple &t) override |
The smoothing hooks. A derived class overriding them – or invariants(), which a smooth also runs – must read every attribute outside the smoothed vertex's star (other vertices, and the edges, faces and tets not incident to the vertex) through const access, e.g. std::as_const(m_vertex_attribute)[u]. | |
| bool | smooth_after (const Tuple &t) override |
| User specified modifications and desideratas for after smoothing a vertex. | |
| bool | vertex_is_on_domain_boundary (const size_t vid) const |
| Identification only – no operation refuses the domain wall through these. | |
| void | init_surfaces_and_boundaries () |
| Classify every region boundary and tag the domain wall – once, from the input mesh, before offset construction runs. A region boundary is a face whose two incident tets carry different tag sets, a sheet-group face, or a face with one incident tet (the domain wall); each becomes a tracked face (m_is_surface_fs). Which of them an envelope holds is face_is_held()'s, and the envelope is build_envelopes()'s. | |
| bool | is_edge_on_region (const Tuple &loc) |
| bool | is_edge_on_offset (const Tuple &loc) |
| void | mark_input_complex_vertices () |
| void | warn_if_offset_reaches_domain_boundary () const |
| Warn if the offset band has grown into the domain boundary. | |
| polysolve::nonlinear::Solver & | smoothing_solver () |
| 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). | |
| bool | is_repulsion_vertex (const size_t vid) const |
| double | repulsion_term_at (const size_t vid) const |
| double | repulsion_cell_term (const std::array< size_t, 4 > &vids) const |
| size_t | repulsion_cells_at (const size_t vid) const |
| std::shared_ptr< OffsetEnergy3D > | repulsion_energy (const size_t vid) const |
| O(v) for vid's smoothing objective, charged once per carrying cell. | |
| bool | repulsion_embedding_kept (const std::vector< size_t > &tids) const |
| bool | simplex_in_input_complex (const std::vector< size_t > &c) const |
| void | log_smoothing_pass_accounting () override |
| void | smoothing_pass_done () override |
| The engine's end-of-pass hook (TetOptimizerMesh::smooth_all_vertices() calls it after EVERY smoothing pass, in every path: a group's passes and pre_smooth): use_rest_pose restamps the plastic rests here – between passes and after the last, never before the first. With the operations stamping the cells they make or change, every rule reads rests that are up to date. A no-op while the plastic medium is off (the final pass, coarsening, the repulsion passes). | |
| 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 the live cells (global_energy()), split into its AMIPS part and the band cells' band_cell_term(). What ~/Developer/wmtk_outputs/plot_energy.py plots per pass. | |
| std::pair< double, double > | energy_parts () const |
| template<typename F > | |
| void | diagnostic_parallel_for (const size_t n, const size_t chunk, F &&fn) const |
| std::vector< double > | front_ring_measures () const |
| The exit test's ring measure of every vertex (energy_criterion()), face for face. | |
| double | ring_measure_at (size_t vid) const |
| One vertex's ring measure, from its own live offset faces (NaN if it has none). | |
| void | crossing_snapshot (const std::string &pass, bool compare, bool match_positions) |
| void | update_attributes () override |
| double | ring_max_quality (size_t vid) const |
| double | tet_amips (const size_t tid) const |
| AMIPS of one tet, the cube root of cell_quality(); the number every log line reports. | |
| double | tet_flatness (size_t tid) const |
| Scale-invariant flatness: 6 * volume / longest_edge^3. | |
| void | needle_forensics () const |
| The full post-mortem on why nothing removes the flat cells; see the 2D twin. | |
| 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}. | |
| void | log_smooth_trace () const |
| void | audit_surface_containment (const std::string &when) const |
| Which held faces are outside m_envelope, and by how much. Diagnostic only. | |
| bool | vertex_is_on_region (const size_t vid) const |
| 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). A wall face: m_envelope, band or not – nothing leaves the domain box. The offset surface: nothing in the loop, m_offset_envelope in a TetWild pass. Any other tracked face: m_envelope when it is held (face_is_held()), else nothing. | |
| bool | allow_surface_swap () const override |
| 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. The loop's case: switches off the engine's own AMIPS^3 swap rule (after is the largest AMIPS^3 over the new cells, before that of the old cells), so the energy decides in its place – on the sum of tet_energy() over the cells the swap changes, in the 4-4 / 5-6 case search (swap_edge_44_energy(), swap_edge_56_energy()), the face swap's gate (swap_face_before()) and swap_after_cells(). The AMIPS^3 bound cannot refuse early: the energy is a sum, and a new cell's AMIPS term can rise while the sum falls. | |
| 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, the base's: the max of AMIPS^3): for op_case 0 (the current cells) the sum of tet_energy() over the ring that swap_before_interior() / swap_before_surface() took, the same cells the engine passes here; for a candidate (op_case >= 1) the sum of candidate_energy() over tets; std::numeric_limits<double>::max() as soon as a cell is inverted, as the base does. | |
| 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. | |
| 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 replaced by the energy – in the loop; under TetWild's rules (m_op_rules) the base itself. | |
| bool | check_surface_topology () const override |
| std::shared_ptr< Envelope > | smoothing_energy_envelope (const size_t vid) const override |
| std::shared_ptr< Envelope > | smoothing_containment_envelope (const size_t vid) const |
| std::shared_ptr< Envelope > | smoothing_envelope (const size_t vid) const |
| std::shared_ptr< Envelope > | smoothing_containment_envelope (const size_t vid, const std::array< size_t, 3 > &, const FaceAttributes &) const override |
| bool | smooth_front_vertex (const Tuple &t) |
| Placement of a front vertex: the shared smoother with the offset's options. See FrontSmooth3d.cpp. | |
| bool | smooth_repulsion_vertex (const Tuple &t) |
| A repulsion vertex in the passes and rounds before the march (repulsion_smoothing()): the shared smoother at the front's options, against the sum of the per-tet energy over its ring – the AMIPS term, and O(v) once for every ring cell that carries it (repulsion_energy(), repulsion_cell_term()); the sum of tet_energy() over its ring may not rise. See FrontSmooth3d.cpp. | |
| bool | smooth_nonfront_vertex (const Tuple &t) |
| Every vertex off the front: the shared smoother with TetWild's options (TetOptimizerMesh::smooth_after()), except that it minimises the vertex's part of the energy (smoothing_extra_energy(); the smoother adds no AMIPS term of its own) and the engine's AMIPS^3 veto is replaced by the energy's: the sum of tet_energy() over the ring may not rise. See Optimize3d.cpp. | |
| double | damping_step (size_t vid) const |
| bool | damping_active () const |
| void | count_damped_move (size_t vid, const Vector3d &x0, double max_step) |
| std::shared_ptr< polysolve::nonlinear::Problem > | amips3_energy (size_t vid) const |
| double | front_vertex_normal_gradient (size_t vid) const |
| Vector3d | front_vertex_move_direction (size_t vid) const |
| double | front_move_alignment (size_t vid) const |
| double | front_vertex_conv_ratio (size_t vid) const |
| 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_vertex_conv_ratio(): finite and within the one bar. Every caller that asks "is the placement of this front vertex done" – energy_criterion()'s vertex count and the placed split of the faces over the bar – goes through here. Unmeasurable (a non-finite ratio) is NOT placed. | |
| 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_frac() – the squared face measure, in units of the tolerance, 1 at the bar. | |
| double | face_offset_term (const OffsetPotential3D &pot, const Vector3d &pa, const Vector3d &pb, const Vector3d &pc) const |
| Vector3d | front_vertex_normal (size_t vid) const |
| The field's unit direction at front vertex vid (zero where grad Phi vanishes). | |
| std::shared_ptr< polysolve::nonlinear::Problem > | front_objective (size_t vid) const |
| bool | vertex_carries_offset_term (const size_t vid) const |
| std::shared_ptr< polysolve::nonlinear::Problem > | smoothing_extra_energy (const size_t vid) const override |
| std::shared_ptr< polysolve::nonlinear::Problem > | front_energy (size_t vid) const |
| std::shared_ptr< polysolve::nonlinear::Problem > | band_volume_energy_at (size_t vid) const |
| int | stencil_order () const |
| Samples per offset face; see offset_face_samples(). | |
| const std::vector< std::array< double, 3 > > & | face_stencil () const |
| const std::vector< double > * | face_stencil_weights () const |
| int | stencil_points_per_face () const |
| void | check_offset_within_support (const char *when) const |
| DistanceSplit | distance_deviation_split () const |
| DistanceSplit | residual_split () const |
| The same split over the quantity the loop converges on: the Phi residual, as a length. | |
| std::vector< bool > | band_vertex_mask () const |
| Which vertices lie on the band's outer surface. Shared by every measurement. | |
| double | max_band_vertex_distance () const |
| double | band_vertex_distance_error (const size_t vid) const |
| |dist(vid, input complex) - target_distance|. Diagnostic: the Euclidean offset. | |
| double | band_vertex_residual (const size_t vid) const |
| How far vid is from the level set Phi = c, as a length. | |
| template<typename Visit > | |
| 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, wa, wb, wc) with the barycentric weights that built it: face_stencil(), as EXPERIMENTAL_stencil_type picks. "subdivision" (the default): order 0 the corners alone, order k the vertices of the face subdivided k-1 times plus the sub-triangles' centroids, so the face measure (face_offset_term()) reads each corner's own placement error. "interior" (2026-10-27, Spencer: the surface integral's stencil, "all stencil points must be internal
to each face"): the interior lattice (i, j, l) / (k + 2), i, j, l >= 1, no point on an edge or a corner. "fem" (2026-10-28): the degree-k Lagrange nodes, corners and edge points included. | |
| template<typename Visit > | |
| 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. | |
| FaceSamples | offset_face_samples (const Tuple &f) const |
| GradientSplit | gradient_split (bool include_face_samples=true) const |
| EnergyCriterion | energy_criterion () |
| double | front_chord_target (size_t va, size_t vb, double len, double sag, double bar) const |
| size_t | refine_front_by_halving (const std::vector< EnergyCriterion::Refinable > &faces) |
| size_t | refine_front_by_halving (const std::vector< size_t > &vertices) |
| bool | settle_refinement_turn (int turn, double &e_prev, bool refine_now) |
| void | grade_sizing (double grade, const std::vector< size_t > &seeds) |
| size_t | grade_sizing_by_distance (const std::vector< size_t > &seeds) |
| void | report_outside_support (const char *when, const DistanceSplit &s) const |
| Turn a residual_split()'s outside-support tally into the hard error. | |
| bool | band_vertex_is_reachable (const size_t vid) const |
| size_t | refine_sizing_around_worst (double max_metric) override |
| TetWild's stall-driven sizing refinement, verbatim; final pass only. See the 2D twin. | |
| void | log_stuck_refine_census (double max_metric, double filter_energy) |
| void | log_refine_block_census (const std::string &when, double filter_energy) const |
| 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. The loop's case: switches off the engine's own AMIPS^3 collapse rule (q the AMIPS^3 of a reshaped cell, ring_max that of v1's ring), so the energy decides in its place – the sum of tet_energy() over the cells the collapse changes, before (collapse_before_vertex()) against after (collapse_after_connectivity()). The AMIPS^3 bound cannot refuse early: the energy is a sum, and a cell's AMIPS term can rise while the sum falls. | |
| void | report_needle (const char *op, size_t tid, double parent_q) const |
| Where the first needles come from – a tripwire, capped at kNeedleReports. | |
| void | needle_scan (const char *when) const |
| Population scan at a named moment. Reports the count and the worst few. | |
| bool | optimization_bare_coarsen_passes () const override |
| bool | coarsen_no_rise () const |
| double | face_criterion_rel (const Tuple &f) const |
| double | cell_quality_rel (const size_t tid) const |
| AMIPS of a cell over stop_energy – the 3D twin of TriOptimizerMesh::quality_rel(). | |
| double | amips_rel_at_face (const Tuple &f) const |
| ... and the worst of the (up to two) cells a face separates. | |
| double | quality_rel (const size_t tid) const override |
| 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()), labelled "r<turn><tag><pass>_<op>" / "r<turn><tag>_end", tag S in the loop and F in the final pass; led by m_frame_prefix (i during init_optimize). | |
| bool | remote_frames () const |
| DEBUG_remote_output: the sparse frame schedule (see the spec doc) instead of DEBUG_output's. | |
| bool | stage_frames () const |
| void | optimization_iteration_done (const int it) override |
| 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. | |
| 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, and one "NNNNN<tab>label" line in <output>_frames.txt. Every debug frame the run writes – the input as loaded, the construction stages, and the optimization's own frames through write_optimization_debug_output() – goes through this sequence, so the numbers are consecutive and the .txt says what each one is. The only debug files outside it are the ones that are not this mesh: <output>_input_complex.vtu and the phi grid. | |
| 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 | |
| bool | ambient_assert () |
| check that the ambient tag does not overlap with any other tags | |
| void | label_input_complex () |
| label input simplicial complex simplices, as defined in m_offset_params.offset_selection | |
| bool | empty_input_complex () |
| check if the input complex is empty. Only valid after calling init_from_image(...). | |
| void | init_input_complex_bvh () |
| Build the input complex's BVH and keep the extraction the potential needs, and number the complex's connected pieces; for offset_field "euclidean", build m_input_complex_envelope from the extraction. Must be called after init_from_image(...) and label_input_complex(). | |
| void | init_offset_potential () |
| Build the smooth offset potential from the extraction init_input_complex_bvh() kept. | |
| void | init_band_exact () |
| size_t | flood_fill () |
| label connected simplicial complex components (simplices labelled 1 or 2) | |
| bool | split_edge_before (const Tuple &t) override |
| User specified preparations and desideratas for an edge split before changing the connectivity. | |
| bool | split_edge_after (const Tuple &t) override |
| This function computes the attributes for the added simplices. User specified modifications and desideratas for after an edge split. | |
| bool | split_face_before (const Tuple &t) override |
| User specified preparations and desideratas for a face split before changing the connectivity. | |
| bool | split_face_after (const Tuple &t) override |
| Compute the attributes for the added simplices. | |
| bool | split_tet_before (const Tuple &t) override |
| User specified preparations and desideratas for a tet split before changing the connectivity. | |
| bool | split_tet_after (const Tuple &t) override |
| Compute the attributes for the added simplices. | |
| bool | invariants (const std::vector< Tuple > &tets) override |
| void | construct_offset (const std::filesystem::path &output_file) |
| void | marching_tets () |
| void | repulsion_smoothing () |
| void | relabel_input_complex () |
| bool | is_simplicially_embedded () const |
| bool | tet_is_simp_emb (const Tuple &t) const |
| void | simplicial_embedding () |
| void | set_offset_tet_tags () |
| update 'tags' data for tets in the offset region (tets labelled 2) | |
| bool | offset_is_manifold () |
| verify that the closed offset region (simplices labelled 1 or 2) form a manifold region. | |
| void | write_phi_grid (const std::string &path, int n) const |
| void | write_input_complex (const std::string &path) |
| void | write_vtu (const std::string &path, MeshMap *output=nullptr) |
| <path>{,_surf,_off,_edge}.vtu, now: write_vtu_frame(gather_vtu_frame(path)). | |
| VtuFrame | gather_vtu_frame (const std::string &path) |
| bool | is_order_2_edge (const Tuple &e) const |
| bool | is_order_2_edge (const std::array< size_t, 2 > &e) const |
| bool | vertex_is_on_surface (const size_t vid) const override |
| Is a vertex part of the substructure. | |
| bool | face_is_on_surface (const size_t fid) const override |
| Is a face part of the substructure. | |
| size_t | get_order_of_vertex (const size_t vid) const override |
| Get the order of a vertex. | |
| void | init_vertex_order () |
| Compute the vertex order for every vertex. | |
| std::vector< size_t > | connected_components_helper (const size_t &v_id) |
| all one-ring vertices through input simplices (labelled 1 or 2) | |
Public Member Functions inherited from wmtk::TetOptimizerMesh | |
| std::vector< std::array< size_t, 3 > > | oriented_tracked_faces () const |
| The tracked surface as an oriented soup: each tracked face emitted |m_orientation| times, in the orientation the input gave it. Faces whose sheets cancel are left out. Vertex ids index m_vertex_attribute. | |
| std::vector< std::pair< std::array< size_t, 2 >, int > > | tracked_surface_boundary () const |
| Mesh edges where the oriented tracked surface has a boundary. | |
| void | throw_if_in_operation_pass (const char *call) const |
| void | add_edge_attributes (AbstractAttributeContainer *c) |
| Register an edge collection with m_edge_attr_group and point p_edge_attrs at the group. | |
| void | enable_feature_edge_tracking () |
| Turn on feature-edge tracking. The collection joins m_edge_attr_group beside whatever the application registered there. | |
| bool | vertex_has_feature_edge (size_t vid) const |
| size_t | substructure_order_of_edge (const std::array< size_t, 2 > &vids) const override |
| The orders substructure_link_condition consults, overridable so an application can widen the 1D substructure beyond what the surface complex derives. | |
| size_t | substructure_order_of_vertex (size_t vid) const override |
| void | substructure_feature_neighbors (size_t vid, std::vector< size_t > &out) const override |
One-ring vertices x of vid where (vid, x) is 1D substructure NOT derivable from the surface complex (feature edges through the interior, for instance). The link condition's order-2 edge collection walks surface faces and would never see them. | |
| bool | feature_edges_at_vertex_inside (size_t vid) const |
| virtual bool | smoothing_position_is_allowed (const size_t vid, const Vector3d &p) const |
| bool | is_force_split_edge (const size_t v1, const size_t v2) const |
| bool | storage_grows_on_demand () const override |
| bool | split_edge_is_due (const Tuple &e) const |
| TetOptimizerMesh (OptimizerParameters ¶ms, std::shared_ptr< Envelope > env) | |
| void | compute_vertex_partition () |
| void | compute_vertex_partition_morton () |
| size_t | get_partition_id (const Tuple &loc) const |
| double | get_length2 (const Tuple &l) const |
| bool | is_inverted (const std::array< size_t, 4 > &vs) const |
| bool | is_inverted (const Tuple &loc) const |
| bool | is_inverted_f (const Tuple &loc) const |
| Inversion check using only the double positions. | |
| double | get_quality (const std::array< size_t, 4 > &vs) const |
| double | get_quality (const Tuple &loc) const |
| std::tuple< double, double > | get_max_avg_energy () |
| void | update_attributes_and_history () |
| update_attributes(), then the change it made to the tracked surface, if recording one. | |
| void | record_surface_history (const std::string &metadata) |
| std::optional< history::SurfaceHistory > | finish_surface_history (const history::SurfaceHistory *input=nullptr) |
| Bring the recorded surface up to date with the tracked faces, stop recording, log what the history holds and return it, for the application to save (ComponentIO:: save_history). None if not recording. | |
| void | record_volume_history () |
| std::optional< std::vector< uint8_t > > | finish_volume_history (const std::map< int64_t, std::string > &tag_names={}) |
| bool | is_tracked_face (size_t tid, int j) const override |
| The faces with m_is_surface_fs, for a surface history (TetMesh::start_surface_history). | |
| std::array< size_t, 3 > | orient_tracked_face (const std::array< size_t, 3 > &f, const std::vector< size_t > &tets) const override |
| std::vector< std::array< size_t, 2 > > | tracked_feature_edges () const override |
| The tagged feature edges (m_feature_edge_attribute) that are edges of tracked faces. | |
| bool | checks_tracked_surface () const override |
| m_params.check_surface_folds; see there. | |
| void | tracked_surface_before (const std::vector< size_t > &vs, bool all) override |
| bool | tracked_surface_after (const std::vector< size_t > &vs, bool all) override |
| Eigen::Vector3d | tracked_area_normal (const std::vector< std::array< size_t, 3 > > &faces) const |
| size_t | tracked_not_facing (const std::vector< std::array< size_t, 3 > > &faces, const Eigen::Vector3d &n) const |
| size_t | tracked_star_folds (const std::vector< std::array< size_t, 3 > > &faces) const |
| void | mesh_improvement (int max_its=80) |
| std::tuple< double, double > | local_operations (const std::array< int, 4 > &ops, bool collapse_limit_length=true) |
| bool | round (const Tuple &v) |
| Round a vertex position to floating point, if that inverts no incident tet. | |
| bool | is_edge_on_surface (const Tuple &loc) |
| bool | is_edge_on_bbox (const Tuple &loc) |
| int | edge_incident_surface_face_count (const Tuple &e) |
How many of the faces incident to edge e are on the tracked surface. | |
| bool | surface_triangle_is_outside (const size_t a, const size_t b, const size_t c, const FaceAttributes &attr) const |
| std::vector< std::array< size_t, 3 > > | get_faces_by_condition (std::function< bool(const FaceAttributes &)> cond) const |
| void | output_faces (std::string file, std::function< bool(const FaceAttributes &)> cond) |
| void | gradation_smooth_sizing (double grade, const std::vector< size_t > &seeds) |
| Grade the refined sizing region into its surroundings (monotone, only lowers). | |
| double | active_quality_threshold () const |
| Cell-quality threshold above which a tet is "active" (worth operating on) for the skip-good-regions filter. | |
| virtual std::vector< size_t > | active_vertices () const |
| void | split_all_edges () |
| void | collapse_all_edges (bool is_limit_length=true) |
| size_t | coarsen_mesh () |
| Coarsen the mesh without letting the max energy rise. | |
| bool | coarsen_collapse_edge (const Tuple &e, std::vector< Tuple > &new_tets) |
| One collapse under the coarsening rules, outside a coarsening pass. | |
| size_t | swap_all_edges_32 () |
| bool | swap_edge_before (const Tuple &t) override |
| User specified preparations and desideratas for an 3-2 edge swap before changing the conenctivity. | |
| bool | swap_edge_after (const Tuple &t) override |
| User specified modifications and desideratas for after a 3-2 edge swap. | |
| size_t | swap_all_edges_44 () |
| bool | swap_edge_44_before (const Tuple &t) override |
| User specified preparations and desideratas for an 4-4 edge swap before changing the connectivity. | |
| bool | swap_edge_44_accept_case (const std::array< size_t, 2 > &new_edge) override |
| Filter which of the 4-4 orientations may be chosen. | |
| bool | swap_edge_44_after (const Tuple &t) override |
| User specified modifications and desideratas for after a 4-4 edge swap. | |
| size_t | swap_all_edges_56 () |
| bool | swap_edge_56_before (const Tuple &t) override |
| User specified preparations and desideratas for a 5-6 edge swap before changing the connectivity. | |
| bool | swap_edge_56_accept_case (const std::array< size_t, 3 > &new_face) override |
| Filter which of the 5-6 orientations may be chosen. | |
| bool | swap_edge_56_after (const Tuple &t) override |
| User specified modifications and desideratas for after a 5-6 edge swap. | |
| size_t | swap_all_faces () |
| bool | swap_face_after (const Tuple &t) override |
| User specified modifications and desideratas for after a 2-3 face swap. | |
| size_t | swap_all_edges_all () |
| bool | prepare_surface_flip (const Tuple &t, const std::vector< size_t > &incident_tets) |
| SurfaceTopoSignature | surface_topology_signature () const |
| void | warn_if_surface_topology_changed (const SurfaceTopoSignature &before, const char *where) const |
| virtual bool | boundary_edges_at_vertex_inside (size_t) const |
| virtual double | smoothing_cell_energy (const size_t, const double quality) const |
| The number the shared smoother compares for one cell, given the cell's quality (AMIPS^3) at the position being tested. Its quality veto compares the max of this over the ring before and after the move, and the default smoothing_ring_energy() is the same max. | |
| void | smooth_all_vertices (const size_t n_iters=1) |
| bool | use_colored_smoothing () const |
| Whether this pass smooths by color class (see OptimizerParameters::colored_smoothing). | |
| void | smooth_vertices_colored (const std::vector< std::pair< std::string, Tuple > > &ops) |
One smoothing sweep over ops (all "vertex_smooth"), one color class at a time. | |
| virtual std::array< size_t, 3 > | orient_tracked_face (const std::array< size_t, 3 > &f, const std::vector< size_t > &tets) const |
| std::array< size_t, 3 > | orient_tracked_face (const std::array< size_t, 3 > &f) const |
| void | enable_feature_point_tracking (const bool preserve) |
Turn on feature-point tracking, and the policy when preserve. | |
| bool | collapse_breaks_feature_point (const size_t v1_id, const size_t v2_id) const |
| bool | feature_point_ball_contains (const size_t vid, const Vector3d &p) const |
| std::pair< size_t, size_t > | feature_retention (double *worst_ratio=nullptr) const |
Public Member Functions inherited from wmtk::TetMesh | |
| size_t | vert_capacity () const |
| get the current largest global vid | |
| size_t | tet_capacity () const |
| get the current largest global tid | |
| size_t | vertex_size () const |
| get the number of unremoved verticies | |
| size_t | tet_size () const |
| get the number of unremoved tets | |
| void | init (size_t n_vertices, const std::vector< std::array< size_t, 4 > > &tets) |
| void | init_with_isolated_vertices (size_t n_vertices, const std::vector< std::array< size_t, 4 > > &tets) |
| void | init (const MatrixXi &T) |
| Generate the connectivity of the mesh from an IGL-style T matrix. | |
| bool | split_edge (const Tuple &t, std::vector< Tuple > &new_tets) |
| virtual bool | collapse_edge (const Tuple &t, std::vector< Tuple > &new_tets) |
| bool | link_condition (const Tuple &t) |
| bool | collapse_edge_conn (const Tuple &loc0, size_t &v1_id, Tuple &new_loc, std::map< size_t, wmtk::TetMesh::VertexConnectivity > &rollback_vert_conn, std::vector< size_t > &n1_t_ids_copy, std::vector< size_t > &new_tet_id, std::vector< TetrahedronConnectivity > &old_tets) |
| bool | collapse_edge_check_topology (const std::vector< size_t > &new_tet_id) |
| Check topology after collapse connectivity change. This is a sanity check and should not be necessary. | |
| void | collapse_edge_rollback (size_t &v1_id, std::map< size_t, wmtk::TetMesh::VertexConnectivity > &rollback_vert_conn, std::vector< size_t > &n1_t_ids, std::vector< size_t > &new_tet_id, std::vector< TetrahedronConnectivity > &old_tets) |
| bool | swap_edge_56 (const Tuple &t, std::vector< Tuple > &new_tets) |
| bool | swap_edge_44 (const Tuple &t, std::vector< Tuple > &new_tets) |
| bool | swap_edge (const Tuple &t, std::vector< Tuple > &new_tets) |
| 3-2 edge swap | |
| bool | swap_face (const Tuple &t, std::vector< Tuple > &new_tets) |
| 2-3 face swap | |
| bool | smooth_vertex (const Tuple &t) |
| bool | split_tet (const Tuple &t, std::vector< Tuple > &new_tets) |
| Split a tet in 4 tets. | |
| bool | split_face (const Tuple &t, std::vector< Tuple > &new_tets) |
| Split a face in 3 faces. | |
| void | triangle_insertion (const std::vector< Tuple > &intersected_tets, const std::vector< Tuple > &intersected_edges, std::vector< size_t > &new_edge_vids, std::vector< size_t > &new_center_vids, std::vector< std::array< size_t, 4 > > ¢er_split_tets) |
| Insert a triangle into a tetmesh, with known intersection information. | |
| bool | insert_point (const Tuple &t, std::vector< Tuple > &new_tets) |
| Insert a point into a tetmesh inside a tet. In general position, this split a tet into 4. In face position, split two tets. In edge position, In point position, do nothing. It has no surface history hooks, and throws while a surface history is recording. | |
| virtual bool | insert_point_before (const Tuple &t) |
| virtual bool | insert_point_after (std::vector< Tuple > &new_tets) |
| void | consolidate_mesh () |
| cleans up the deleted vertices or tetrahedra, fixes the corresponding indices, and reset the version number. WARNING: it invalidates all tuples! | |
| std::shared_ptr< history::TrackedSurfaceRecorder > | start_surface_history (std::function< Eigen::Vector3d(size_t)> position) |
| Record every change to the tracked surface from now on (see wmtk/history/TrackedRecorder.hpp). | |
| void | stop_surface_history () |
| Stop recording. The recorder keeps what it has. | |
| size_t | resync_surface_history () |
| std::shared_ptr< history::VolumeRecorder > | start_volume_history (std::function< Eigen::Vector3d(size_t)> position) |
Record, alongside the surface history, the tets around every operation that changes the tracked surface (wmtk/history/VolumeHistory.hpp): what a map of the volume needs. The surface history must be recording; a volume history is its own choice, since it takes far more room. position(v) is called as start_surface_history()'s is; a tet's region is its tet_tags(). | |
| void | stop_volume_history () |
| Stop recording the volume. The recorder keeps what it has. | |
| virtual std::vector< int64_t > | tet_tags (size_t tid) const |
| std::vector< std::array< size_t, 3 > > | tracked_faces () const |
| Every tracked face, oriented by orient_tracked_face(). | |
| std::array< size_t, 3 > | orient_tracked_face (const std::array< size_t, 3 > &f) const |
| orient_tracked_face, finding the face's tets. | |
| std::array< size_t, 3 > | face_out_of (size_t tid, const std::array< size_t, 3 > &face) const |
The corners of face, a face of tet tid, ordered so that its normal points out of the tet – for a tet the right way round as TetOptimizerMesh has them, with its fourth vertex on the side of the first three's normal (orient3d negative). | |
| std::vector< size_t > | tets_of_face (const std::array< size_t, 3 > &f) const |
The tets with the face f, sorted: one on the boundary, two inside. | |
| std::vector< Tuple > | get_edges () const |
| std::vector< Tuple > | get_faces () const |
| std::vector< Tuple > | get_vertices () const |
| std::vector< Tuple > | get_tets () const |
| virtual void | for_each_face (const std::function< void(const TetMesh::Tuple &)> &) |
| looping through all the unique edges and perform the given function | |
| void | set_preallocation_factor (double factor) |
| Preallocation factor: init/consolidate reserve capacity = max(floor, ceil(factor * live_count)) so operations can grab fresh slots without resizing the storage. When a pass exhausts the reserved capacity the affected operations fail (and are retried later after a consolidate). Tune per application (e.g. from JSON); values < 1 are clamped to 1. | |
| double | preallocation_factor () const |
| bool | slots_exhausted () const |
| void | clear_slots_exhausted () |
| size_t | request_tet_slots (size_t n) |
| size_t | request_vert_slots (size_t n) |
| void | ensure_free_tet_capacity (size_t extra) |
| void | ensure_free_vert_capacity (size_t extra) |
| bool | op_refused (const OpKind k, const OpEvent e) const |
op_event() for a refusal, returning false so a site reads return op_refused(...). | |
| bool | op_screened (const OpKind k) const |
| The end of a dry run that would have gone on to change the mesh: true, and counted. | |
| 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). | |
| Tuple | tuple_from_edge (const std::array< size_t, 2 > &vids) const |
| get a Tuple from global vids of the 2 end of an edge | |
| Tuple | tuple_from_face (size_t tid, int local_fid) const |
| get a Tuple from global tetra index and local face index (from 0-3). | |
| std::tuple< Tuple, size_t > | tuple_from_face (const std::array< size_t, 3 > &vids) const |
| get a Tuple and the global face index from global vertex index of the face. | |
| 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. | |
| size_t | lowest_common_tet (size_t v0_id, size_t v1_id, size_t v2_id) const |
| Lowest tet id incident to all three vertices, or size_t(-1) if there is none. | |
| size_t | vertex_valence (const size_t vid) const |
| Number of tets incident to a vertex, in O(1). | |
| std::tuple< Tuple, size_t > | tuple_from_face (const simplex::Face &f) const |
| Tuple | tuple_from_vertex (size_t vid) const |
| get a Tuple from global vertex index | |
| Tuple | tuple_from_tet (size_t tid) const |
| get a Tuple from global tetra index | |
| Tuple | tuple_from_vids (size_t vid0, size_t vid1, size_t vid2, size_t vid3) const |
| Get a Tuple from global vertex IDs. | |
| simplex::Tet | simplex_from_tet (const Tuple &t) const |
| simplex::Tet | simplex_from_tet (const size_t tid) const |
| simplex::Face | simplex_from_face (const Tuple &t) const |
| simplex::Edge | simplex_from_edge (const Tuple &t) const |
| Tuple | switch_vertex (const Tuple &t) const |
| wrapper function from Tuple::switch_vertex | |
| Tuple | switch_edge (const Tuple &t) const |
| wrapper function from Tuple::switch_edge | |
| Tuple | switch_face (const Tuple &t) const |
| wrapper function from Tuple::switch_face | |
| std::optional< Tuple > | switch_tetrahedron (const Tuple &t) const |
| wrapper function from Tuple::switch_tetrahedron | |
| std::vector< Tuple > | get_one_ring_tets_for_vertex (const Tuple &t) const |
| Get the one ring tets for a vertex. | |
| const std::vector< size_t > & | get_one_ring_tids_for_vertex (const Tuple &t) const |
| Get the one ring tids for vertex. | |
| const std::vector< size_t > & | get_one_ring_tids_for_vertex (const size_t vid) const |
| std::vector< Tuple > | get_one_ring_vertices_for_vertex (const Tuple &t) const |
| Get the one ring vertices for a vertex. | |
| 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. | |
| std::vector< size_t > | get_one_ring_vids_for_vertex (size_t vid) const |
| Get the one ring vids for vertex. | |
| std::vector< size_t > | get_one_ring_vids_for_vertex_adj (size_t vid) const |
| Duplicate of the function TetMesh::get_one_ring_vids_for_vertex. | |
| std::vector< size_t > | get_one_ring_vids_for_vertex_adj (size_t vid, std::vector< size_t > &cache) |
| Duplicate of the function TetMesh::get_one_ring_vids_for_vertex. | |
| std::vector< Tuple > | get_incident_tets_for_edge (const Tuple &t) const |
| Get the incident tets for edge. | |
| std::vector< Tuple > | get_incident_tets_for_edge (const size_t vid0, const size_t vid1) const |
| std::vector< size_t > | get_incident_tids_for_edge (const Tuple &t) const |
| std::vector< size_t > | get_incident_tids_for_edge (const size_t vid0, const size_t vid1) const |
| std::vector< Tuple > | get_one_ring_tets_for_edge (const Tuple &t) const |
| Get the one ring tets for edge. | |
| std::vector< std::array< size_t, 3 > > | vertex_adjacent_boundary_faces (const Tuple &t) const |
| std::array< Tuple, 4 > | oriented_tet_vertices (const Tuple &t) const |
| std::array< size_t, 4 > | oriented_tet_vids (const Tuple &t) const |
| std::array< size_t, 4 > | oriented_tet_vids (const size_t tid) const |
| std::array< Tuple, 3 > | get_face_vertices (const Tuple &t) const |
| Get the 3 vertices of a face represented by Tuple. | |
| std::array< size_t, 3 > | get_face_vids (const Tuple &t) const |
| std::array< Tuple, 6 > | tet_edges (const Tuple &t) const |
| get the 6 edges of a tet represented by Tuples | |
| void | check_tuple_validity (const Tuple &t) const |
| bool | check_mesh_connectivity_validity () const |
| checks the validity of the connectivity of the mesh. Including the validity of each Tuple | |
| void | remove_tets_by_ids (const std::vector< size_t > &tids) |
| remove the tetrahedrons in the mesh that have given tet ids | |
| void | start_protect_attributes () |
| void | release_protect_attributes () |
| void | rollback_protected_attributes () |
| int | release_vertex_mutex_in_stack () |
| int | release_vertex_mutex_to (size_t mark) |
Release the mutexes taken since the release stack held mark entries. | |
| bool | try_set_vertex_mutex_n_ring (const Tuple &v, int threadid, int n) |
Lock every vertex within graph distance n of v, the seed included. | |
| bool | try_set_vertex_mutex_n_ring (size_t vid, int threadid, int n) |
| bool | try_set_edge_mutex_n_ring (const Tuple &e, int threadid, int n) |
| try_set_vertex_mutex_n_ring seeded from both ends of an edge. | |
| bool | try_set_face_mutex_n_ring (size_t v1, size_t v2, size_t v3, int threadid, int n) |
| try_set_vertex_mutex_n_ring seeded from the three vertices of a face. | |
| void | for_each_edge (const std::function< void(const TetMesh::Tuple &)> &) |
| perform the given function for each edge | |
| void | for_each_vertex (const std::function< void(const TetMesh::Tuple &)> &) |
| perform the given function for each vertex | |
| void | for_each_tetra (const std::function< void(const TetMesh::Tuple &)> &) |
| perform the given function for each tet | |
| simplex::SimplexCollection | get_surface_faces_for_vertex (const size_t vid) const |
| Get all faces on the surface that are incident to vid. | |
| simplex::SimplexCollection | get_surface_faces_for_edge (const std::array< size_t, 2 > &vids) const |
| Get all faces on the surface that are incident to the edge. | |
| size_t | get_num_surface_faces_for_edge (const std::array< size_t, 2 > &vids) const |
| Get the number of surface faces incident to the edge. | |
| size_t | compute_vertex_order (const size_t vid) const |
| Compute the vertex order for a single vertex. | |
| size_t | get_order_of_edge (const std::array< size_t, 2 > &vids) const |
| Compute the order of an edge. | |
| bool | substructure_link_condition (const Tuple &e_tuple) const |
| Link condition that also considers substructures. | |
| size_t | tet_storage_capacity () const |
| size_t | vert_storage_capacity () const |
| bool | try_set_vertex_mutex_two_ring (const Tuple &v, int threadid) |
| Lock v's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_vertex_mutex_two_ring_vid (const Tuple &v, int threadid) |
| try_set_vertex_mutex_two_ring reached through vids rather than Tuples. | |
| bool | try_set_vertex_mutex_two_ring_vid (size_t v, int threadid) |
| try_set_vertex_mutex_two_ring reached through vids rather than Tuples. | |
| bool | try_set_edge_mutex_two_ring (const Tuple &e, int threadid=0) |
| Lock the edge's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_face_mutex_two_ring (const Tuple &f, int threadid=0) |
| Lock the face's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_face_mutex_two_ring (const Tuple &v1, const Tuple &v2, const Tuple &v3, int threadid=0) |
| Lock the face's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_face_mutex_two_ring (size_t v1, size_t v2, size_t v3, int threadid=0) |
| Lock the face's one-ring and, partially, its two-ring. See the note above. | |
| bool | try_set_vertex_mutex_one_ring (const Tuple &v, int threadid=0) |
| Lock v and its one-ring. Complete, unlike the two-ring family. | |
| size_t | cell_capacity () const |
| size_t | cell_storage_capacity () const |
| Tuple | tuple_from_cell (size_t cid) const |
| size_t | cell_slot_bound (const Tuple &t) const |
| void | reserve_free_slots (size_t cells, size_t verts) |
Public Member Functions inherited from wmtk::RationalPositions | |
| size_t | round_all_vertices () |
| Try to round every un-rounded vertex; returns the number reclaimed. | |
| bool | round_and_check_all_rounded () |
| Run the sweep, then report whether the mesh is now fully rounded. | |
Static Public Member Functions | |
| static double | growth_stage_target (double d0, double delta, int k, int n) |
| static double | band_side_dihedral (const Vector3d &n1, const Vector3d &n2, const Vector3d &t) |
| static bool | dihedral_is_foldover (const double dihedral, const double angle) |
| static void | write_debug_pvd (const std::string &out, const DebugTimeline &timeline) |
| static bool & | split_off_longest () |
| static void | write_vtu_frame (const VtuFrame &frame, MeshMap *output=nullptr) |
The files' part: paraviewo encodes and writes frame, nothing else is read. | |
Static Public Member Functions inherited from wmtk::TetOptimizerMesh | |
| static PositionKey | position_key (const Vector3d &a, const Vector3d &b, const Vector3d &c) |
Static Public Member Functions inherited from wmtk::TetMesh | |
| static bool | dry_run () |
| Whether operations on this thread are dry runs (see wmtk/OperationDryRun.hpp). | |
| static SlotUsage | slot_usage_of_this_thread () |
Public Attributes | |
| std::array< size_t, 4 > | m_init_counts = {{0, 0, 0, 0}} |
| size_t | m_tags_count |
| int64_t | m_sheet_tag = -1 |
| std::vector< std::string > | m_face_tag_names |
| std::map< int64_t, int > | m_face_tag_bit |
| MatrixXd | m_sheet_V |
| The surface group as loaded, kept because the classification below is redone on demand. | |
| MatrixXi | m_sheet_F |
| std::shared_ptr< const wmtk::utils::WindingNumberHierarchy > | m_input_solid_winding |
| std::shared_ptr< OffsetPotential3D > | m_offset_potential |
| The smooth offset potential, and with it the definition of the offset itself. | |
| 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, or of m_phi_E and m_phi_P): built once, by init_input_complex_bvh(), never rebuilt. Every 3D distance to the input reads it: the euclidean field (m_offset_potential), the repulsion pass, the band term's searches (OffsetPotential.hpp), the exact integral's box queries (m_band_exact), the march and the diagnostics (with input_complex_distance()'s inside test where a distance must be 0 inside the solid). | |
| double | m_input_complex_pad = 0. |
| int | m_n_regions = 0 |
| One field per connected piece of the input complex, and which one each band vertex is placed on. See TopoOffsetTriMesh::m_region_potentials for the full argument. | |
| std::vector< std::shared_ptr< OffsetPotential3D > > | m_region_potentials |
| std::vector< std::shared_ptr< Envelope > > | m_region_envelopes |
| std::vector< int64_t > | m_phi_vert_region |
| per m_phi_V row: region index | |
| std::vector< int64_t > | m_phi_seg_region |
| per m_phi_E row: region index, -1 unknown | |
| std::vector< int64_t > | m_phi_face_region |
| per m_phi_F row: region index, -1 unknown | |
| std::vector< int64_t > | m_phi_point_region |
| per m_phi_P entry: region index, -1 unknown | |
| std::vector< int > | m_cell_region |
| per tet: band's region, -1 none, -2 reached from two | |
| std::vector< int > | m_vertex_region |
| per vertex: region of its band cells, -1 / -2 as above | |
| double | m_construction_envelope_eps = 0. |
| std::vector< Eigen::Vector3d > | m_held_verts |
| The held triangle soup as build_envelopes() captured it, for build_final_envelopes(). | |
| std::vector< Eigen::Vector3i > | m_held_tris |
| std::shared_ptr< Envelope > | m_loop_envelope |
| The loop's m_envelope while the final pass holds its own. | |
| bool | m_tetwild_pass = false |
| OpRules | m_op_rules = OpRules::Loop |
| std::shared_ptr< Envelope > | m_offset_envelope |
| The envelope the offset surface may not leave during a TetWild pass (tetwild_pass()), of eps envelope_size (optimize_construction's pass: half the traced distance), built by build_offset_envelope() from the surface as it is just before that pass. Null until then: the loop holds the offset surface to no envelope. The surface is a closed manifold by then (offset_is_manifold() is the driver's check), so it is one triangle set with no junctions: a face is either on it or not, and surface_envelope_for_face() answers an offset face with this envelope alone. | |
| bool | m_pocket_splitting = false |
| Set while front_pocket_split() splits: split_edge_before() skips the longest-edge order. | |
| std::vector< std::array< size_t, 2 > > | m_settle_split_edges |
| bool | m_init_optimize = false |
| std::string | m_frame_prefix |
| double | m_front_gradient_reference = 0. |
| Its value on the band as constructed, measured once before turn 1. | |
| EdgeSplitMode | m_edge_split_mode = EdgeSplitMode::Midpoint |
| std::map< std::string, int64_t > | m_tag_name_to_id |
| std::map< int64_t, std::string > | m_tag_id_to_name |
| CellTag | m_offset_output_tag_ids |
| bool | m_singlebody = false |
| int64_t | m_single_tag |
| bool | m_has_envelope = false |
| MatrixXd | m_V_envelope |
| MatrixXi | m_F_envelope |
| double | m_envelope_eps = -1 |
| Parameters & | m_offset_params |
| The base holds only wmtk::OptimizerParameters; this is the same object, typed. | |
| VertexExtraCol | m_vertex_extra |
| FaceExtraCol | m_face_extra |
| EdgeAttCol | m_edge_attribute |
| TetAttCol | m_tet_attribute |
| SweptCounters | m_swept_front |
| SweptCounters | m_swept_nonfront |
| BandVolumeEnergy3D::SweptDerivCounts | m_swept_derivs |
| wmtk::threading::enumerable_thread_specific< std::pair< size_t, Vector3d > > | m_swept_stored |
| std::shared_ptr< const ExactBandIntegral > | m_band_exact |
| std::atomic< int > | m_volume_reject_collapse {0} |
| std::atomic< int > | m_volume_reject_swap {0} |
| wmtk::threading::enumerable_thread_specific< double > | m_split_energy_before |
| std::atomic< int > | m_volume_reject_split {0} |
| bool | m_plastic_active = false |
| size_t | m_worst_dist_vid = static_cast<size_t>(-1) |
| std::atomic< long long > | m_offset_face_lookup_misses {0} |
| Faces that offset_surface_faces_live_at() asked for and the connectivity did not have. | |
| std::atomic< long long > | m_offset_face_invalid_tuple {0} |
| wmtk::threading::enumerable_thread_specific< int > | m_collapse_folds_before {-1} |
| wmtk::threading::enumerable_thread_specific< int > | m_swap_folds_before {-1} |
| std::atomic< int > | m_fold_reject_collapse {0} |
| std::atomic< int > | m_fold_reject_swap {0} |
| std::atomic< int > | m_fold_reject_smooth {0} |
| std::vector< std::array< double, 8 > > | optimization_metrics |
| int | m_round = 0 |
| size_t | m_debug_seq = 0 |
| Monotonic frame counter for the debug timeline. | |
| DebugTimeline | m_debug_timeline |
| std::future< DebugTimeline > | m_debug_frame_write |
| int | m_debug_pass = 0 |
| int | m_debug_last_round = -1 |
| char | m_debug_last_tag = '?' |
| std::string | m_debug_last_prefix = "?" |
| bool | m_converged = false |
| bool | m_quality_converged = true |
| double | m_quality_max_amips = 0. |
| std::atomic< int > | iter_cnt_split = 0 |
| std::atomic< int > | iter_cnt_collapse = 0 |
| std::atomic< int > | iter_cnt_swap = 0 |
| std::atomic< int > | iter_cnt_collapse_offset_removed {0} |
| std::atomic< int > | iter_cnt_collapse_offset_reject {0} |
| Operations refused because they would have left an offset-surface face over tolerance. | |
| std::atomic< int > | iter_cnt_swap_offset_reject {0} |
| std::atomic< long long > | m_swap_scoring_checked {0} |
| std::atomic< long long > | m_swap_scoring_mismatch {0} |
| std::atomic< long long > | funnel_offered {0} |
| [flip funnel]: of the flips of the offset surface, how many survive each stage. Reset per turn and reported next to [swap reject]. | |
| std::array< std::atomic< long long >, 3 > | funnel_kind {} |
| offered, split by swap kind: [0] = 3-2, [1] = 4-4, [2] = 5-6. | |
| std::atomic< long long > | funnel_cases {0} |
| std::atomic< long long > | funnel_case_inverted {0} |
| std::atomic< long long > | funnel_case_not_better {0} |
| std::atomic< long long > | funnel_case_better {0} |
| std::atomic< long long > | funnel_after_cells {0} |
| std::atomic< long long > | funnel_energy {0} |
| std::atomic< long long > | funnel_committed {0} |
| std::atomic< int > | iter_cnt_split_offset_before {0} |
| std::atomic< int > | iter_cnt_split_offset {0} |
| std::atomic< long > | m_split_order_waits {0} |
| std::atomic< long > | m_split_off_longest {0} |
| wmtk::threading::enumerable_thread_specific< OptSplitCache > | m_opt_split_cache |
| size_t | m_marching_root_splits = 0 |
| size_t | m_marching_midpoint_splits = 0 |
| size_t | m_marching_trace_steps = 0 |
| size_t | m_marching_trace_steps_max = 0 |
| double | m_construction_distance = 0. |
| double | m_growth_start = 0. |
| SmoothTrace | m_smooth_trace |
| optimization::NewtonCounters | m_newton_front |
| std::vector< FrontSolveRecord > | m_front_solve_log |
| std::mutex | m_front_solve_log_mutex |
| std::atomic< size_t > | m_front_veto_asked {0} |
| std::atomic< size_t > | m_front_veto_fired {0} |
| std::atomic< size_t > | m_damping_moves {0} |
| std::atomic< size_t > | m_damping_bounded {0} |
| std::shared_ptr< const OffsetPotential3D > | m_repulsion_potential |
| optimization::NewtonCounters | m_newton_repulsion |
| std::atomic< size_t > | m_repulsion_veto_asked {0} |
| std::atomic< size_t > | m_repulsion_veto_fired {0} |
| std::shared_ptr< OffsetEnergy3D > | m_repulsion_term |
| std::atomic< size_t > | m_repulsion_embed_refused {0} |
| std::array< std::atomic< size_t >, kGradBins > | m_front_grad_abs {} |
| std::array< std::atomic< size_t >, kGradBins > | m_front_grad_rel {} |
| int | m_energy_step_turn = 0 |
| const char * | m_energy_step_group = "" |
| The running group's name, and whether its first smoothing pass is still to come. | |
| bool | m_energy_step_first = false |
| std::vector< double > | m_cross_ring |
| std::vector< Vector3d > | m_cross_pos |
| bool | m_cross_valid = false |
| std::atomic< size_t > | m_cross_own {0} |
| std::atomic< size_t > | m_cross_neighbour {0} |
| 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 / fixed / stationary. See TopoOffsetTriMesh::m_needle_pre. | |
| std::atomic< size_t > | m_needle_smooth_offered {0} |
| std::atomic< size_t > | m_needle_smooth_reached {0} |
| std::atomic< size_t > | m_needle_smooth_fixed {0} |
| std::atomic< size_t > | m_needle_smooth_stationary {0} |
| std::atomic< size_t > | m_needle_smooth_reports {0} |
| std::atomic< size_t > | m_flat_created_split {0} |
| std::atomic< size_t > | m_flat_created_collapse {0} |
| std::atomic< size_t > | m_flat_worsened_split {0} |
| std::atomic< size_t > | m_flat_genesis_reports {0} |
| wmtk::threading::enumerable_thread_specific< double > | m_collapse_parent_flatness |
| The flattest tet in the collapse's ring before it ran, for record_flatness(). | |
| wmtk::threading::enumerable_thread_specific< double > | m_collapse_survivor_sizing |
| wmtk::threading::enumerable_thread_specific< CollapseVolume > | m_collapse_volume |
| wmtk::threading::enumerable_thread_specific< std::vector< size_t > > | m_collapse_edge_link |
| The link of the collapsed edge, captured in collapse_before_vertex(). | |
| int | m_smooth_group = 0 |
| std::optional< EnergyCriterion > | m_energy_verdict |
| std::atomic< size_t > | m_deg_split_created {0} |
| size_t | m_deg_prev_split_created = 0 |
| std::atomic< size_t > | m_needle_reports {0} |
| std::set< std::tuple< long, long, long > > | m_stuck_prev_cells |
| size_t | m_stuck_calls = 0 |
| std::atomic< size_t > | m_coarsen_reject_amips {0} |
| std::atomic< size_t > | m_coarsen_reject_offset {0} |
| wmtk::threading::enumerable_thread_specific< CoarsenCollapse > | m_coarsen_collapse |
| MatrixXd | m_phi_V |
| MatrixXi | m_phi_E |
| MatrixXi | m_phi_F |
| std::vector< int8_t > | m_phi_F_inner |
| std::vector< int > | m_phi_P |
Public Attributes inherited from wmtk::TetOptimizerMesh | |
| VertAttCol | m_vertex_attribute |
| FaceAttCol | m_face_attribute |
| bool | m_tracks_orientation = false |
| The application set FaceAttributes::m_orientation at insertion, so the tracked surface carries the input's orientation and the operations keep it. | |
| std::atomic< size_t > | m_inconsistent_orientation_flips = 0 |
| Surface flips that joined two faces not oriented alike (a fold of the tracked surface, or unequal multiplicities). | |
| AttributeContainerGroup | m_vertex_attr_group |
| What p_vertex_attrs points at, so a derived class can register more. | |
| AttributeContainerGroup | m_face_attr_group |
| What p_face_attrs points at, so a derived class can register more. | |
| AttributeContainerGroup | m_edge_attr_group |
| What p_edge_attrs points at, once a collection is registered with it. | |
| bool | m_in_operation_pass = false |
| True while a pass of the shared operations runs. | |
| EdgeAttCol | m_feature_edge_attribute |
| Sized, protected and rolled back only once enable_feature_edge_tracking() ran. | |
| bool | m_track_feature_edges = false |
| Whether the shared operations maintain m_feature_edge_attribute. | |
| std::shared_ptr< Envelope > | m_feature_envelope |
| Envelope around the input feature curves: the tube a tagged edge must stay in. | |
| OptimizerParameters & | m_params |
| std::shared_ptr< Envelope > | m_envelope |
| Surface envelope: what a surface vertex is pulled toward and checked against. | |
| double | m_s_amips = 1. |
| double | m_s_envelope = -1. |
| double | time_env = 0.0 |
| igl::Timer | isout_timer |
| wmtk::threading::enumerable_thread_specific< std::unique_ptr< polysolve::nonlinear::Solver > > | m_solver |
| Per-thread Newton solver for smoothing; created on first use. | |
| optimization::SmoothRejectCounters | m_smooth_rejects |
| Why smoothing attempts were refused, reported once per pass. | |
| optimization::NewtonCounters | m_newton |
| bool | m_collapse_limit_length = true |
| std::set< simplex::Edge > | m_force_split_edges |
| size_t | m_force_split_count = 0 |
| Force-splits taken in the current split pass. Diagnostic only. | |
| std::unique_ptr< std::atomic< int >[]> | m_high_valence_claim |
| Per-pass claims for the shared high-valence split gate. | |
| size_t | m_high_valence_claim_size = 0 |
| std::atomic< size_t > | m_high_valence_rejects = 0 |
| size_t | m_split_order_waits = 0 |
| The current split pass's ExecutePass::waits() and wait_defects(). | |
| size_t | m_split_order_wait_defects = 0 |
| std::vector< std::pair< PositionKey, std::vector< uint32_t > > > | m_insertion_sources |
| std::shared_ptr< history::VolumeRecorder > | m_recorded_volume |
| uint64_t | m_surface_edit_offset = 0 |
| std::optional< history::SurfaceKey > | m_finished_surface |
| std::atomic< size_t > | m_surface_fold_refusals = 0 |
| The operations check_surface_folds refused. | |
| int | m_iterations_used = 0 |
| Shared TetWild/SimWild outer optimization schedule. | |
| std::shared_ptr< history::TrackedSurfaceRecorder > | m_recorded_surface |
| wmtk::threading::enumerable_thread_specific< SurfaceFolds > | m_surface_folds_before |
| std::vector< std::vector< uint64_t > > | m_recorded_sources |
| The input triangles each face of the recorded surface's first state lies on. | |
| int | m_debug_print_counter = 0 |
| std::string | m_debug_pass_name |
| CoarsenStats | m_coarsen_stats |
| std::atomic< int > | cnt_swap = 0 |
| std::atomic< int > | cnt_surface_swap = 0 |
| std::atomic< int > | cnt_surface_swap_32 = 0 |
| std::atomic< int > | cnt_surface_swap_44 = 0 |
| std::atomic< int > | cnt_surface_swap_56 = 0 |
| FeaturePointAttCol | m_feature_point_attribute |
| Registered with m_vertex_attr_group only once enable_feature_point_tracking() ran. | |
| std::vector< Vector3d > | m_feature_points |
| double | m_feature_eps = -1 |
| bool | m_track_feature_points = false |
| Whether the shared operations maintain the ids (enable_feature_point_tracking()). | |
| bool | m_preserve_feature_points = false |
Whether the collapse and smoothing policy is on (its preserve argument). | |
Public Attributes inherited from wmtk::TetMesh | |
| bool | m_check_smoothing_stays_in_star = false |
| While true, smooth_vertex() checks that the smooth recorded nothing outside the vertex's star – the vertex itself and the edges, faces and cells incident to it – and throws if it did. | |
| AbstractAttributeContainer * | p_vertex_attrs = nullptr |
| AbstractAttributeContainer * | p_edge_attrs = nullptr |
| AbstractAttributeContainer * | p_face_attrs = nullptr |
| AbstractAttributeContainer * | p_tet_attrs = nullptr |
| bool | m_collapse_check_link_condition = true |
| bool | m_collapse_check_topology = false |
| bool | m_collapse_check_manifold = true |
| wmtk::threading::enumerable_thread_specific< std::vector< size_t > > | mutex_release_stack |
| wmtk::threading::enumerable_thread_specific< std::vector< size_t > > | get_one_ring_cache |
| int | NUM_THREADS = 0 |
Static Public Attributes | |
| static constexpr int | kEnergyDecreasePasses = 100 |
| EXPERIMENTAL_label_cleanup "energy_decrease": the most passes one clean-up runs. | |
| static constexpr int | kPocketMaxRounds = 10 |
| static constexpr int | INPUT_SURFACE_CLASS = 0 |
| SurfaceTagAttributes::m_surface_class: which of the two tracked surfaces a face belongs to. Same scheme as 2D. | |
| static constexpr int | OFFSET_SURFACE_CLASS = 1 |
| static constexpr int | kSweptHalvings = 4 |
| static constexpr double | kCos150 = -0.86602540378443864676 |
| static constexpr double | kSmoothRelGradNormTol = 1e-6 |
| The Newton stopping rule every smoother of this component runs with, set on the thread's shared solver (the engine's m_solver slot) by smoothing_solver() at the start of every visit – front and interior alike, so the settings do not depend on which path first ran on a thread. | |
| static constexpr int | kGradBins = 22 |
| static constexpr size_t | kDiagnosticChunk = 64 |
| static constexpr double | kFlatThreshold = 1e-3 |
| static constexpr size_t | kNeedleReports = 12 |
| static constexpr double | kNeedleQuality = 1e6 |
| What counts as a needle for the tripwire, in AMIPS – deliberately far below MAX_ENERGY. | |
Static Public Attributes inherited from wmtk::TetOptimizerMesh | |
| static constexpr double | MAX_ENERGY = 1e50 |
| The sentinel get_quality returns for an element AMIPS cannot score. | |
| static constexpr size_t | NO_FEATURE_POINT = std::numeric_limits<size_t>::max() |
| A vertex that stands for no feature point. See FeaturePointAttributes. | |
Static Public Attributes inherited from wmtk::TetMesh | |
| static constexpr int | EDGES_PER_CELL = 6 |
| static constexpr int | FACES_PER_CELL = 4 |
Private Member Functions | |
| bool | swap_capture_tag (const std::vector< size_t > &tids) |
| bool | swap_capture_surface_sides (const std::vector< size_t > &tids, size_t a, size_t b, size_t c, size_t d) |
| void | swap_record_fill (bool flip) |
| void | swap_scoring_check (const std::vector< size_t > &tids, double after) |
| void | sort_edges_by_length (std::vector< simplex::Edge > &edges) |
| sort edge simplices in place by decreasing edge length | |
Private Attributes | |
| wmtk::threading::enumerable_thread_specific< EdgeSplitCache > | edge_split_cache |
| wmtk::threading::enumerable_thread_specific< FaceSplitCache > | face_split_cache |
| wmtk::threading::enumerable_thread_specific< TetSplitCache > | tet_split_cache |
| 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. | |
| wmtk::threading::enumerable_thread_specific< int > | m_swap_label |
| The construction label shared by every cell of an interior swap's ring, captured alongside. | |
| wmtk::threading::enumerable_thread_specific< SwapSurfaceSides > | m_swap_sides |
| 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 replaces. swap_before_interior() and swap_before_surface() take it, swap_after_cells(), the case search (swap_edge_44_energy()) and the face swap's gate (swap_face_before()) compare against it. The cells outside the ring keep their corners and labels, so their energy, and are in neither sum. | |
| std::atomic< int > | m_plastic_tie_swap {0} |
| std::atomic< int > | m_plastic_tie_collapse {0} |
| wmtk::threading::enumerable_thread_specific< SwapRecord > | m_swap_record |
m_face_extra through the optimization's operations | |
Carried exactly as the base carries m_face_attribute (TetOptimizerMeshSplit.cpp, TetOptimizerMeshCollapse.cpp, TetOptimizerMeshSwaps.cpp), at the canonical face id, which is where every reader looks:
The edge label is carried the same way (edge_label_capture() / edge_label_assign()): an edge's slot is its lowest incident tet's, so an operation that changes the tets around an edge moves it to a slot holding another edge's value or a dead one's. After that, every operation closes the labels over the cells it made (close_labels()). | |
| using | FaceExtraMap = std::map< std::array< size_t, 3 >, FaceExtra > |
| using | EdgeLabelMap = std::map< std::array< size_t, 2 >, int > |
| wmtk::threading::enumerable_thread_specific< FaceExtraMap > | m_face_extra_split |
| The split's and the swap's capture, per thread. | |
| wmtk::threading::enumerable_thread_specific< FaceExtraMap > | m_face_extra_swap |
| 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). | |
| wmtk::threading::enumerable_thread_specific< EdgeLabelMap > | m_edge_label_split |
| The split's, the swap's and the collapse's edge capture, per thread. | |
| wmtk::threading::enumerable_thread_specific< EdgeLabelMap > | m_edge_label_swap |
| wmtk::threading::enumerable_thread_specific< EdgeLabelMap > | m_edge_label_collapse |
| 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()). | |
| 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. | |
| 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 |
| 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. | |
| void | close_labels (const std::vector< size_t > &tids) |
EXPERIMENTAL_band_volume_rule "surface" (2026-10-27, Spencer's HackMD "Global surface | |
integral", revised the same day; offset weight 1 since 2026-10-28): E = (w / s^3) sum_t V_t A(t)^3 + L / front_conv^2 sum over the front's faces of O(f), L the bounding box diagonal, O(f) = (A_f / |S|) sum over the stencil points x_i (face_stencil(): stencil_order, EXPERIMENTAL_stencil_type) of (d(x_i) - delta)^2. No cell carries an offset term (cell_energy()); every rule adds the faces it changes beside its cells' sums: a collapse the offset faces through its two ends before against the survivor's after, a flip of the offset surface its two faces out and two in, a split the faces on the edge against the new vertex's, a smoothing move the faces through the vertex. A face outside those is unchanged by the operation and left out of both sides. | |
| wmtk::threading::enumerable_thread_specific< size_t > | m_smoothing_vid {size_t(-1)} |
| bool | surface_energy () const |
| double | surface_term_weight () const |
| double | surface_face_term (size_t a, size_t b, size_t c) const |
| double | surface_term_at (size_t vid) const |
| surface_face_term() summed over the live offset faces through vid. | |
| double | surface_term_total () const |
| ... over every live offset face. | |
| double | stencil_max_sq_error () const |
| double | face_stencil_max_sq (const std::array< size_t, 3 > &f) const |
| double | surface_integral_raw () const |
| double | settle_metric () const |
| 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()). | |
Additional Inherited Members | |
Protected Types inherited from wmtk::TetOptimizerMesh | |
| enum class | SwapReject : int { base_before , base_after , valence , bbox , surface_not_allowed , feature_edge , prepare_flip , interior_hook , flip_open_boundary , flip_not_two_surf , flip_nonmanifold_edge , flip_cd_nonmanifold , flip_new_face_surface , flip_app_refused , flip_wrong_case , app_capture_label , app_capture_tag , app_fid_missing , app_class_mismatch , app_mask_mismatch , app_sag_raised , after_inverted , after_quality , after_cells , app_after_side_conflict , app_after_no_side , after_envelope , face_tracked_surface , face_tracked_bbox , face_inverted , face_not_better , COUNT } |
| enum class | SwapStage : int { attempt , surface_attempt , before_pass , after_enter , accepted , screened , COUNT } |
| enum class | CollapseReject : int { coarsen_length , bbox , surface_leaves_envelope , inverted , quality , substructure_link , feature_leaves_tube , feature_point , app_both_surfaces , app_front_unlimited , app_leaves_input , app_leaves_offset , app_leaves_region , app_order2 , app_substructure_link , app_ops_guard , after_base , after_connectivity , after_envelope , after_face_attribute , after_order2_chord , after_feature_edge , after_coarsen_region , COUNT } |
| enum class | SwapRejectRole { before_leaf , after_leaf , wrapper , per_case } |
Protected Member Functions inherited from wmtk::TetOptimizerMesh | |
| void | on_slot_storage_grown () override |
| std::vector< size_t > | all_vertex_ids () const override |
| Every live vertex, in the mesh's own iteration order. | |
| bool | vertex_is_rounded (const size_t vid) const override |
| Whether this vertex's double position is currently trusted. | |
| bool | round_vertex (const size_t vid) override |
| virtual std::tuple< double, double > | optimization_quality_stats () |
| virtual double | optimization_stop_metric () const |
| virtual void | optimization_sanity_checks_extra () |
| virtual bool | optimization_stop_at_float () const |
| virtual bool | optimization_stalled (double prev, double cur) |
Whether an iteration that moved the metric from prev to cur is stalled, and the sizing refinement should therefore fire. | |
| virtual void | collapse_after_features (size_t, size_t) |
| bool | swap_reject (SwapReject r) const |
| bool | swap_reject_kind_only (SwapReject r) const |
| void | swap_stage (SwapStage s) const |
| void | swap_counters_reset () |
| std::string | swap_reject_report () const |
| One line per nonzero counter, for a per-turn log. Does not reset. | |
| bool | collapse_reject (CollapseReject r) const |
| std::string | op_accounting_report (OpKind k) const |
| bool | op_accounting_unexplained (OpKind k) const |
True when the report for k has a nonzero remainder: a refusal no counter named. | |
| void | op_accounting_reset () |
| OpHookReasons | op_hook_reasons (OpKind k) const |
| long | op_event_count (const OpKind k, const OpEvent e) const |
| long | op_unaccounted (OpKind k) const |
| Attempts minus every outcome: 0 unless an exit of the TetMesh operation reports nothing. | |
| void | feature_edges_cache (const std::vector< size_t > &tids, std::map< std::array< size_t, 2 >, bool > &cache) |
Cache the feature tag of every edge of every tet in tids. | |
| void | feature_edges_restore_region (const std::vector< Tuple > &tets, const std::map< std::array< size_t, 2 >, bool > &cache) |
Write back tags over every edge of every tet in tets (created regions). | |
| void | feature_edges_restore_region (const std::vector< size_t > &tids, const std::map< std::array< size_t, 2 >, bool > &cache) |
| bool | feature_edges_restore_remap (const std::map< std::array< size_t, 2 >, bool > &cache, size_t v_old, size_t v_new) |
Write back tags for every cached edge, with v_old renamed to v_new (collapse). | |
| virtual void | split_after_vertex_feature (size_t, bool) |
Protected Member Functions inherited from wmtk::TetMesh | |
| std::vector< std::array< size_t, 3 > > | tracked_faces_around (const std::vector< size_t > &vs, bool all) const |
| std::vector< VertexFace > | faces_at_vertex (size_t v) const |
| The faces with v as a corner, found from v's tets alone. | |
| size_t | face_id_in (const std::array< size_t, 3 > &f, size_t tid) const |
| The face id (Tuple::fid) of f, a face of tet tid, its lowest; from tid alone. | |
| virtual bool | triangle_insertion_before (const std::vector< Tuple > &faces) |
| virtual bool | triangle_insertion_after (const std::vector< std::vector< Tuple > > &) |
| void | resize_vertex_mutex (size_t v) |
Static Protected Member Functions inherited from wmtk::TetOptimizerMesh | |
| static const char * | swap_reject_name (SwapReject r) |
| static const char * | swap_stage_name (SwapStage s) |
| static const char * | collapse_reject_name (CollapseReject r) |
| static OpKind & | current_op_kind () |
| static const char * | op_kind_name (OpKind k) |
| static const char * | op_event_name (OpEvent e) |
| static SwapRejectRole | swap_reject_role (SwapReject r) |
Protected Attributes inherited from wmtk::TetOptimizerMesh | |
| uint32_t | m_op_epoch = 0 |
| std::array< std::atomic< long >, size_t(SwapReject::COUNT)> | m_swap_reject {} |
| std::array< std::atomic< long >, size_t(SwapStage::COUNT)> | m_swap_stage {} |
| std::array< std::atomic< long >, size_t(CollapseReject::COUNT)> | m_collapse_reject {} |
| std::array< std::array< std::atomic< long >, size_t(OpEvent::COUNT)>, size_t(OpKind::COUNT)> | m_op_events {} |
| std::array< std::array< std::atomic< long >, size_t(SwapReject::COUNT)>, size_t(OpKind::COUNT)+1 > | m_swap_reject_by_kind {} |
| wmtk::threading::enumerable_thread_specific< SwapInfoCache > | swap_cache |
| wmtk::threading::enumerable_thread_specific< SplitInfoCache > | split_cache |
| wmtk::threading::enumerable_thread_specific< CollapseInfoCache > | collapse_cache |
| bool | m_coarsen_mode = false |
| Set for the duration of coarsen_mesh(); read-only while a pass is running. | |
Protected Attributes inherited from wmtk::RationalPositions | |
| std::atomic< bool > | m_all_rounded = false |
| True when every vertex is known to be rounded. | |
Static Protected Attributes inherited from wmtk::TetMesh | |
| static constexpr std::array< std::array< int, 2 >, 6 > | m_local_edges |
| local edges within a tet | |
The offset's tet mesh, on the shared 3D optimizer.
Mirrors TopoOffsetTriMesh one dimension up: the construction phase (simplicial embedding, marching tets, growing the band) is entirely its own, and the optimization phase that follows is wmtk::TetOptimizerMesh's, with the offset supplying only policy through the hooks.
Two surfaces are tracked. Every tag-region boundary (input complex and domain wall included) keeps the primary class 0; the offset surface is OFFSET_SURFACE_CLASS, the faces across which the incident tet labels differ. Which class-0 faces an envelope holds, and when, is face_is_held()'s; only the offset surface is driven toward target_distance.
|
strong |
Which rules the operations and the smoother follow – the one switch for them.
Loop (the default; the loop and the repulsion passes): this component's energy E. Every rule compares the SUM of tet_energy() over the cells an operation changes, before against after: collapses (collapse_before_vertex() / collapse_after_connectivity()), swaps (swap_after_cells(); the 4-4 / 5-6 case search, swap_edge_44_energy(), and the face swap's gate, swap_face_before(), on candidate_energy()), the smoothing veto and the projected step (smoothing_ring_energy()), the split gate (EXPERIMENTAL_split_energy_gate). The engine's AMIPS^3 rules admit everything (collapse_quality_allowed(), swap_quality_allowed()), and the smoother minimises the vertex's part of E (smoothing_extra_energy()) with this component's solver criterion (smoothing_solver()).
TetWild (every TetWild pass, tetwild_pass(): optimize_construction's and the final pass): TetWild's own. The engine's rules on the MAX of AMIPS^3 over those cells: collapse_quality_allowed() and swap_quality_allowed() as the base has them, the base's scores in the 4-4 / 5-6 search and the base's face swap gate; no sum rule and no split gate. The smoother is TetOptimizerMesh::smooth_after(): the raw sum of AMIPS over the one-ring (AMIPSEnergy3D; no extra energy), its quality veto and projected step on the ring's max (smoothing_ring_energy() the base's), the engine's solver criterion (rel_grad_norm_tol 0). TetWild's bare coarsening passes run too (optimization_bare_coarsen_passes()).
|
inlineoverridevirtual |
Surface edges may be flipped, as a topology-preserving diagonal flip. Both tracked surfaces need it: the offset surface is re-triangulated constantly.
Implements wmtk::TetOptimizerMesh.
| std::shared_ptr< polysolve::nonlinear::Problem > wmtk::components::topological_offset::TopoOffsetTetMesh::amips3_energy | ( | size_t | vid | ) | const |
The energy's AMIPS term over vid's one-ring, (w / s^3) sum vol AMIPS^3 (band_amips_weight(), vol_amips3()), in the smoother's form (CubedAMIPSEnergy3D).
| void wmtk::components::topological_offset::TopoOffsetTetMesh::assign_band_regions | ( | bool | log = true | ) |
Rebuild m_*_region from the mesh. log false suppresses the per-turn "[regions]" line: write_vtu() re-derives the map for its frame diagnostics and puts the old one back.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::audit_surface_containment | ( | const std::string & | when | ) | const |
Which held faces are outside m_envelope, and by how much. Diagnostic only.
< outside: the worst sample distance; inside: that over eps
|
inline |
The AMIPS term's weight, w / s^3 (w = offset_amips_weight, s = stop_energy): the term is w vol (AMIPS / s)^3, about w vol for a cell at the stop energy.
|
inline |
b(t), one band cell's part of int_B (d - delta) / delta dV, as an upper bound:
vol * min over ALL input triangles P of (1/4) sum over the 4 corners q of
(d_P(q) - delta) / delta,
d_P the distance to input triangle P alone, delta = target_distance, vol the cell's unsigned volume (the cell is valid wherever this is read). An upper bound on the exact vol * mean of (d - delta)/delta for every P: d <= d_P, and the convex d_P lies below its linear interpolant. A function of the four corners alone, and no split raises it: each child's min includes the parent's minimiser, whose finer interpolant lies lower. Exact where one triangle's distance is affine on the cell. Signed: about -vol for a cell at the input, positive beyond the level set. A volume, as vol AMIPS^3 is. One input region only (the triangles are the whole input's): refused otherwise.
Why the corner bound rather than a quadrature of d over the cell: corners on the input read (d - delta)/delta = -1 whatever the cell's interior is, so the mean over the four corners and the centroid read a band tet with all four corners on the input as -1.57 against a dense +4.83, and the whole band as -41.0 against a dense +13.1 (one-slot model, stage 1, cells about 10x target_distance). Why the min over ALL triangles rather than over each corner's nearest plus a triangle stored on the cell at the last split pass: over 2000 band cells with a front vertex on the last frames of the cube (24 triangles) and 100026 (1736), the corners' nearest missed the min in 0.1% and 4.5% of the cells, by up to 23 and 70 bars – corners lie on input edges and vertices, where triangles tie – and the term depended on how the cell was made. No triangle outside the corners' nearest (ties included) had a smaller mean; the exact search is min_mean_distance(), on m_input_complex_envelope.
That is EXPERIMENTAL_band_volume_rule "corner_bound". Under "exact", the default, b(t) is J_t / delta, J_t = int_t (d - delta) dV in closed form (ExactBandIntegral::value()): exact up to rounding, and an upper bound only on the pieces of t where two features' regions meet on a curved border (rule (a)). Under "front_flux" b(t) is the same J_t / delta: the front form changes how the smoother evaluates the band term, not the term. A cell's part of the front form is oint_dt G.n dA + D_t (ExactBandIntegral.hpp), which IS int_t (d - delta) dV by the divergence theorem, so the operations, the veto and the global energy read it as value() computes it. The two can differ only on rule-(a) pieces, where value() takes an upper bound and the front form labels the piece exactly, or throws where a curved bisector crosses it. See the spec doc.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::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 |
EXACT SWEPT CHANGE: dE* / delta for the band cells old_band replaced by new_band over the same region, at the current positions (a swap moves no vertex).
|
inlinestatic |
The dihedral angle in degrees, in [0, 360), across an offset-surface edge between two faces with unit normals n1, n2 out of the band, measured on the band side: 180 flat, below 180 where the front bends away from the band (convex seen from the offset side), toward 0 or toward 360 as the two faces fold flat onto each other one way or the other. t is the edge's unit direction as the first face runs it, its corners counterclockwise seen from the side n1 points to.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::band_swept_change | ( | size_t | vid, |
| const Vector3d & | x0, | ||
| const Vector3d & | x1, | ||
| ExactBandIntegral::Stats * | st = nullptr |
||
| ) | const |
EXACT SWEPT CHANGE: dE* / delta for vid moving from x0 to x1, the boundary faces of B through vid read from the mesh (labels, connectivity, the other corners' positions). st collects value()'s statistics (rule-(a) pieces) when given.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::band_swept_faces | ( | size_t | vid, |
| const Vector3d & | x0, | ||
| std::vector< std::array< Vector3d, 2 > > & | faces | ||
| ) | const |
band_swept_change() in two halves: the boundary faces of B through vid as (a_f, b_f), ordered out of B at x0 (no value() call), and the sum over them for a move x0 -> x1.
|
inline |
Whether an operation whose old cells are tids leaves the band term unchanged exactly, so its sums leave it out; see tet_energy(), BAND TERM THAT CANCELS.
|
inline |
Whether vid is a band vertex the optimizer could still place at target_distance. An envelope-held offset vertex is pinned, as is one on the domain boundary. Same rule as 2D.
| std::shared_ptr< polysolve::nonlinear::Problem > wmtk::components::topological_offset::TopoOffsetTetMesh::band_volume_energy_at | ( | size_t | vid | ) | const |
The band term (band_cell_term()) of vid's band cells with vid moving (BandVolumeEnergy3D), null when vid has none. Defined in FrontSmooth3d.cpp.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::build_envelopes | ( | ) |
Build m_envelope from the held faces. Called once, from construct_offset(), after the simplicial embedding: the complex has to be labelled for face_is_held(), and it has to precede the march, whose band replaces the tags of the cells it covers.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::build_final_envelopes | ( | ) |
The final pass's envelopes: the offset surface's envelope (build_offset_envelope()), and m_envelope swapped for one that also holds every other region boundary, at its position as the loop left it, beside the surfaces build_envelopes() captured (held where they were then). release_final_envelopes() puts the loop's envelope back.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::candidate_energy | ( | const std::array< size_t, 4 > & | vids | ) |
cell_energy() of a candidate cell of the swap in flight, which does not exist yet, with the label swap_after_cells() will write. See tet_energy(), CANDIDATE CELLS.
| int wmtk::components::topological_offset::TopoOffsetTetMesh::candidate_label | ( | const std::array< size_t, 4 > & | vids | ) |
The label swap_after_cells() will write on the candidate cell vids (candidate_energy(), and "exact_swept"'s case search).
|
inline |
Whether tet tid belongs to the closed offset region, read from its label: the band (label 2) plus the input complex it wraps (label 1). Every operation carries the label onto the cells it creates, so this is exact; tags cannot express the distinction.
|
inline |
Every cell outside the offset band and the input complex (cell_in_region() false), while the plastic medium is active: its AMIPS term in tet_energy() and in the smoother is against its stamped rest shape (TetAttributes::rest_pos) instead of the regular tet. The band and the complex are elastic.
|
inlineoverridevirtual |
The quality of cell tid, and how to write it.
The cell attribute types differ between the applications, so the base reaches the one field it shares through these. Both are AMIPS^3; see MAX_ENERGY. Topological operations and smoothing use the accessors so the shared algorithms remain independent of each application's cell-attribute type.
Implements wmtk::TetOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::check_no_vertex_on_both_surfaces | ( | const char * | when | ) | const |
Hard error if any vertex is on both the input complex and the offset surface – a state no placement satisfies. Called at construction.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::check_offset_membership | ( | const char * | when | ) | const |
Log offset_membership_mismatches() and throw when it is not {0, 0}. perform_sanity_checks only.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::check_offset_within_support | ( | const char * | when | ) | const |
Stop the run if any reachable band vertex has left the potential's support. Called once per turn and once on the band as constructed.
|
inlineoverridevirtual |
Implements wmtk::TetOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::classify_sheet_faces | ( | ) |
Mark the mesh faces that lie on the input's envelope surface group (FaceExtra::on_sheet).
Geometric, against the sheet's own envelope (the same eps the tag envelopes use), because the .msh carries the sheet with its own vertices and there is no index to match on. Called whenever the complex is labelled, not once at load: the flag is a property of a face and nothing propagates it through split and collapse.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::close_labels | ( | const std::vector< size_t > & | tids | ) |
Restore the closure over tids, the cells an operation made: each cell's label onto its faces, each face's onto its edges, each edge's onto its ends (closed_label()), and then VertexExtra::m_is_on_input of every corner from its label, as mark_input_complex_vertices() defines it. Only raises: a carried label stays.
|
inline |
EXPERIMENTAL_coarsen_guard "no_rise" (2026-10-28): coarsening judged by collapse_after_connectivity()'s no-rise guard, and run only for the final target (see the spec). "bar" (the default): face_criterion_rel() <= 1 on the survivor's offset faces.
|
overridevirtual |
The collapse's energy rule (see tet_energy() and the definition), and in coarsening the absolute offset bar.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Non-const: an override may cache what it measured before the collapse so its after counterpart can tell a regression from a defect that was already there.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
The coarsening bar, the sizing restore and the rest re-stamp, after the base accepted.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Reject any collapse that violates the substructure link condition, remember the survivor's sizing for sizing_collapse_min = false, and cache the energy rule's before-half.
The base applies the link condition only when both endpoints already sit on a tracked surface or the bbox; the offset region is a thin shell, so a collapse with one endpoint in the interior can still pinch its two sides together. The offset asks unconditionally.
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
In the loop (m_op_rules Loop) admits every collapse; under TetWild's rules, the base's. The loop's case: switches off the engine's own AMIPS^3 collapse rule (q the AMIPS^3 of a reshaped cell, ring_max that of v1's ring), so the energy decides in its place – the sum of tet_energy() over the cells the collapse changes, before (collapse_before_vertex()) against after (collapse_after_connectivity()). The AMIPS^3 bound cannot refuse early: the energy is a sum, and a cell's AMIPS term can rise while the sum falls.
Reimplemented from wmtk::TetOptimizerMesh.
| std::pair< double, double > wmtk::components::topological_offset::TopoOffsetTetMesh::compute_distance_deviation | ( | ) | const |
How far the offset surface is from where it should be: {max, avg} over vertices.
The absolute error |dist(v, input complex) - target_distance| over the offset-surface vertices only, pinned ones included. Mirrors TopoOffsetTriMesh::compute_distance_deviation().
| void wmtk::components::topological_offset::TopoOffsetTetMesh::construct_offset | ( | const std::filesystem::path & | output_file | ) |
Construction, start to finish, on the input mesh as given: the simplicial embedding, marching_tets(), the re-embedding and the offset tagging. The optimization is optimize_offset(), which the driver calls afterwards.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::count_damped_move | ( | size_t | vid, |
| const Vector3d & | x0, | ||
| double | max_step | ||
| ) |
EXPERIMENTAL_damping: counts an accepted move from x0 in m_damping_moves, and in m_damping_bounded when it ended on the bound (within a relative 1e-9 of max_step).
| size_t wmtk::components::topological_offset::TopoOffsetTetMesh::count_real_folds | ( | std::vector< char > * | corners = nullptr, |
| std::vector< std::array< size_t, 3 > > * | faces = nullptr |
||
| ) | const |
The real folds now: offset-surface edges with exactly two offset faces whose normals out of the band are within 30 degrees of opposite (the edge of a zero-volume sheet, Lemma 5 of the case table, 2026-10-24); corners, when given, marks the corners of every fold's two faces, and faces, when given, receives those faces as sorted vertex triples.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::crossing_snapshot | ( | const std::string & | pass, |
| bool | compare, | ||
| bool | match_positions | ||
| ) |
Take the snapshot; with compare, log the crossings against the previous one first. match_positions: an id counts as the same vertex only at the same position (operation passes, which move no vertex; a storage retry inside one renumbers).
|
inline |
EXPERIMENTAL_damping acts in the loop's smoothing passes only: never in a TetWild pass (m_tetwild_pass: optimize_construction's, the final pass) nor in the repulsion passes before the march (m_repulsion_potential).
| double wmtk::components::topological_offset::TopoOffsetTetMesh::damping_step | ( | size_t | vid | ) | const |
EXPERIMENTAL_damping: the longest move one smoothing solve may make at vid, EXPERIMENTAL_damping_factor (default 0.5) times the shortest edge from vid to a vertex of its one-ring, at the positions before the solve; the three smoothers above pass it as SmoothVertexOptions::max_step. Infinity when damping is off or not active (damping_active()).
|
inline |
Run fn(begin, end) over [0, n) in chunks of chunk items handed out on demand (wmtk::threading::dynamic_parallel_for) on the mesh's NUM_THREADS threads; a serial run (NUM_THREADS <= 1) runs fn(0, n) on the calling thread. For the read-only diagnostics (energy_parts(), gather_vtu_frame()), called between passes: every item must read the mesh only and write its own slots, so what they compute does not depend on the thread count.
|
inlinestatic |
foldover_angle's dihedral test: the two faces close to within angle degrees of each other, either way – a band-side dihedral (band_side_dihedral()) below angle or above 360 - angle. The debug frames' offset_foldover_dihedral.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::edge_label_capture | ( | const std::vector< size_t > & | tids, |
| EdgeLabelMap & | out, | ||
| size_t | from = size_t(-1), |
||
| size_t | to = size_t(-1) |
||
| ) | const |
The edges of tids by sorted vertices; from, when given, renamed to first (a collapse's v1 -> v2). Two edges meeting at one key combine by closed_label().
| std::pair< double, double > wmtk::components::topological_offset::TopoOffsetTetMesh::energy_parts | ( | ) | const |
log_energy_step()'s two sums over the live cells: {the AMIPS term, the band term}; E is their sum. Each cell is evaluated on the mesh's threads (diagnostic_parallel_for()), the sums are taken in get_tets() order on the calling thread: the same additions in the same order whatever the thread count.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::face_criterion_rel | ( | const Tuple & | f | ) | const |
Max of the two normalized criteria on this face: AMIPS over stop_energy for the cells it separates, and on a live offset face the root of face_offset_term() (the face's RMS relative error over its stencil in units of the bar; +inf if unmeasurable). > 1 means it fails at least one. The coarsen-mode collapse accept reads it (EXPERIMENTAL_coarsen_guard "bar").
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::face_folded_in | ( | size_t | a, |
| size_t | b, | ||
| size_t | c, | ||
| const std::vector< size_t > & | cells | ||
| ) | const |
Whether face (a, b, c) is folded, its band cell looked for among cells (a band cell holding it); false when none is.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::face_is_complex_boundary | ( | const Tuple & | f | ) | const |
Whether this face lies on the boundary of the input complex: exactly one incident tet carries label 1, or the face itself does while neither tet does (a sheet or face piece).
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::face_is_held | ( | const Tuple & | f | ) | const |
THE ENVELOPES. One, m_envelope (the base's pointer): an Envelope of half-width envelope_size around every HELD face (face_is_held()), built by build_envelopes() before anything moves – after the simplicial embedding, before the repulsion and the march – and swapped for a wider one only for a TetWild pass (build_final_envelopes()). The offset surface is held by nothing in the loop; only a TetWild pass holds it, to m_offset_envelope.
Which faces are held, asked live of the mesh (the operations carry the cell tags and labels it reads), never stored:
|
inline |
Whether face fid is on the offset surface / bounds a region – any tracked face that is not the offset surface. The input complex is included, and deliberately: both are held by the same per-tag envelopes and neither is what the optimization moves.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::face_is_offset_surface_live | ( | const Tuple & | f | ) | const |
The band's outer surface, recomputed live rather than read from the cached class.
A band cell meeting a cell that is neither band nor input complex. Must be live: the operations that ask run between one labelling pass and the next. Returns true for a band face on the domain boundary, whose vertices are pinned and must be measured, not hidden. The 2D twin is edge_is_offset_surface_live().
|
overridevirtual |
Is a face part of the substructure.
| fid | Face ID |
Reimplemented from wmtk::TetOptimizerMesh.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::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_frac() – the squared face measure, in units of the tolerance, 1 at the bar.
Every reader of the face measure goes through it: the logs print its root, energy_criterion()'s exit, refinement and ring measures, and the debug frames' front_err_ratio and front_ring_ratio. It is not part of the energy (tet_energy()). -1 when unmeasurable (a sample whose relative_residual() is not finite, which the euclidean field never has), +inf when the bar is not positive.
The (a, b, c) form reads the field of the face's edge (a, b) (potential_for_edge()), as the face measure always has.
|
inline |
THE face stencil at the configured stencil_order, as EXPERIMENTAL_stencil_type picks: subdivision_face_stencil() under "subdivision" (the default), interior_face_stencil() under "interior", fem_face_stencil() under "fem". Every 3D reader of the face stencil goes through it.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::face_stencil_max_sq | ( | const std::array< size_t, 3 > & | f | ) | const |
The max over face f's stencil points (face_stencil()) of (d - delta)^2, +inf when a point is unmeasurable, 0 without a potential: one face's share of O* (stencil_max_sq_error() is its max over the front), and EXPERIMENTAL_settle_mark "stencil"'s test.
|
inline |
The points' area weights beside face_stencil(), in its order, as fractions of the face's area: fem_face_stencil_weights() (lumped, the composite trapezoid rule) under "fem"; null under "subdivision" and "interior", whose readers take the plain mean (the arithmetic they always had).
|
inline |
growth_mode "individual_op_reject": a collapse, a flip of the offset surface or a smoothing move that raises the number of folded offset faces (front_face_is_foldover()'s test) among the faces it changes is refused. In the loops only: not in a TetWild pass (the optimize_construction pass, the final pass), nor before the march.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::folded_front_faces_at | ( | size_t | vid, |
| std::set< std::array< size_t, 3 > > & | out | ||
| ) | const |
The folded live offset faces through vid (front_face_field_angle() > foldover_angle), as sorted vertex triples, added to out. Reads vid's own tet list and its cells only, as vertex_has_live_offset_face() does (for_each_live_offset_face_at()), so the operation hooks may ask it: the passes' locks always claim vid's list.
|
inline |
The stencil_order stencil a triangle is sampled on, handed to visit one point at a time as (point, wa, wb, wc) with the barycentric weights that built it: face_stencil(), as EXPERIMENTAL_stencil_type picks. "subdivision" (the default): order 0 the corners alone, order k the vertices of the face subdivided k-1 times plus the sub-triangles' centroids, so the face measure (face_offset_term()) reads each corner's own placement error. "interior" (2026-10-27, Spencer: the surface integral's stencil, "all stencil points must be internal
to each face"): the interior lattice (i, j, l) / (k + 2), i, j, l >= 1, no point on an edge or a corner. "fem" (2026-10-28): the degree-k Lagrange nodes, corners and edge points included.
Takes positions rather than a Tuple, so a face can be read at any positions. The 2D twin is for_each_offset_edge_sample(), which keeps its own lattice.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::for_each_live_offset_face_at | ( | size_t | vid, |
| F && | fn | ||
| ) | const |
Every live offset face through vid, from vid's own tet list alone (see vertex_has_live_offset_face()): fn(sorted vertex triple, its band cell) for each, until fn returns true; returns whether one did. A band face on the domain boundary counts.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::front_chord_target | ( | size_t | va, |
| size_t | vb, | ||
| double | len, | ||
| double | sag, | ||
| double | bar | ||
| ) | const |
The edge length that would bring a front chord's sag under the bar (front_conv): 3/4 L (bar / sag)^(1/p) capped at L/2, with the exponent p measured from how the level set turns across the chord. Same formula as 2D.
Reached from ONE place now: the refinable / at-floor test in energy_criterion(), which asks whether there is any target left below what the face's corners already carry.
| std::shared_ptr< polysolve::nonlinear::Problem > wmtk::components::topological_offset::TopoOffsetTetMesh::front_energy | ( | size_t | vid | ) | const |
The offset term for a front vertex: the band term of its band cells (band_volume_energy_at()), in an EnergySum, empty when it has none. Defined in FrontSmooth3d.cpp.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::front_face_angle_to_field | ( | size_t | a, |
| size_t | b, | ||
| size_t | c, | ||
| const Vector3d & | n | ||
| ) | const |
The angle front_face_field_angle() reports, for face (a, b, c) with normal n out of the band: against grad d at the centroid; NaN without an input envelope or a gradient.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::front_face_out_normal | ( | size_t | a, |
| size_t | b, | ||
| size_t | c, | ||
| Vector3d & | n | ||
| ) | const |
Front face (a, b, c)'s area normal (b - a) x (c - a) in n, turned out of its band cell (away from front_face_band_cell()'s fourth corner). False, n unusable, where it cannot be measured: a degenerate face, or no band cell. front_face_field_angle()'s normal.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::front_face_out_normal_in | ( | size_t | a, |
| size_t | b, | ||
| size_t | c, | ||
| size_t | band_tid, | ||
| Vector3d & | n | ||
| ) | const |
front_face_out_normal() with the face's band cell given: band_tid, a band cell holding the face (no lookup, so it reads that cell alone).
| double wmtk::components::topological_offset::TopoOffsetTetMesh::front_gradient_linf | ( | ) |
Max over the front vertices of ||grad F . n||, F the vertex's front objective (front_objective()). Logged as the loop's gradient reference.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::front_move_alignment | ( | size_t | vid | ) | const |
|cos| between front_vertex_move_direction() and the field normal: 1 means the convergence test's 1-D step is the step toward the level set, 0 means it measures a direction that cannot reduce the distance. Debug-frame diagnostic; see write_vtu().
| std::shared_ptr< polysolve::nonlinear::Problem > wmtk::components::topological_offset::TopoOffsetTetMesh::front_objective | ( | size_t | vid | ) | const |
The objective of front vertex vid, as the smoother assembles it for a front vertex it places against the offset term (smoothing_extra_energy()): the AMIPS term over the one-ring (amips3_energy()) and front_energy(). What front_vertex_normal_gradient() differentiates.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::front_pocket_split | ( | const std::string & | where | ) |
EXPERIMENTAL_front_pocket_split. Invariant: no cell is PINNED when a smoothing pass of the loop starts. A pinned cell is a cell outside the input complex, band or not, whose four corners are all front vertices (on the offset surface, VertexExtra::m_is_on_offset).
Why. (1) Only smoothing moves vertices, and no vertex joins or leaves the offset surface during a smoothing pass, so a cell can only be flattened during smoothing, and only from a cell that is pinned when the pass starts. (2) The band term depends only on the band as a region (under the exact rule, the one measured, it is the integral of (d - target_distance) / target_distance over the band), so moving a vertex that is not on the offset surface does not change it (a vertex on the input complex carries no band term, smoothing_extra_energy()): the AMIPS term alone moves such a vertex, and it keeps the vertex's cells shaped. A pinned cell has no such corner. The band term pulls its four corners onto the level set d = target_distance, so the level set fixes the cell's shape – a sliver – and where the level set curves the wrong way the energy flattens the cell to zero volume, a fold of the offset surface. (3) Hence the invariant.
How. Each pinned cell has its longest edge that is on no live offset-surface face split by the loop's own split (split_edge()), the next longest when that is refused. The midpoint is off the offset surface and a corner of every cell the split makes, so such a split makes no pinned cell and removes at least one. A pinned cell with all six edges on the offset surface has instead the longest edge of one of its faces that is not an offset face split: its two children are pinned, each with an edge off the offset surface, which the next round splits. A pinned cell with all four faces on the offset surface has no edge to split and is counted only. Rescanned until no pinned cell has an edge to split, at most kPocketMaxRounds rounds. One [pocket split] line, where naming the pass. A no-op with the key off and in a TetWild pass. The measurements: the spec doc.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::front_vertex_conv_ratio | ( | size_t | vid | ) | const |
The vertex measure over the one bar: |relative_residual(x)| / front_conv_frac(), the face term's order-0 stencil at one corner. Infinite when unmeasurable. As in 2D.
| Vector3d wmtk::components::topological_offset::TopoOffsetTetMesh::front_vertex_move_direction | ( | size_t | vid | ) | const |
The line a front vertex is placed along: the field normal, or that normal projected into the boundary surface (onto its crease) where an input envelope holds it.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::front_vertex_normal_gradient | ( | size_t | vid | ) | const |
||grad F|| at front vertex vid along its move direction, F the objective smooth_front_vertex() minimises. +inf if unmeasurable.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::front_vertex_relative_residual | ( | size_t | vid | ) | const |
The field at front vertex vid, potential_for(vid): relative_residual(), residual_length() and gradient() at the vertex's position.
| TopoOffsetTetMesh::VtuFrame wmtk::components::topological_offset::TopoOffsetTetMesh::gather_vtu_frame | ( | const std::string & | path | ) |
The mesh's part of write_vtu(): every array of the four files. Logs each file's "Write" line once its arrays are gathered – for a debug frame, before the writer thread writes it. Does not change the mesh (the band-region map it re-derives is put back).
|
overridevirtual |
Get the order of a vertex.
The order of a vertex in a TetMesh is as follows: 0: vertex is not on the surface 1: vertex is on the surface 2: vertex is on the surface boundary or a non-manifold edge 3: vertex is at the boundary of a non-manifold edge or a non-manifold vertex
Computing the vertex order is expensive. It is recommended to store the vertex order as a vertex attribute and override this method.
| vid | Vertex ID |
Reimplemented from wmtk::TetOptimizerMesh.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::global_energy | ( | ) | const |
E, the sum of tet_energy() over every live tet: the [band volume energy] lines'. Under EXPERIMENTAL_band_volume_rule "surface" plus surface_term_total().
| void wmtk::components::topological_offset::TopoOffsetTetMesh::grade_sizing | ( | double | grade, |
| const std::vector< size_t > & | seeds | ||
| ) |
Spread the refinement just made at seeds to the vertices around them, the way sizing_gradation_mode says: "ring" is the base gradation_smooth_sizing(grade, seeds), "distance" is grade_sizing_by_distance(seeds) and ignores grade. Every place the offset lowers the field goes through here.
| size_t wmtk::components::topological_offset::TopoOffsetTetMesh::grade_sizing_by_distance | ( | const std::vector< size_t > & | seeds | ) |
TetWild's gradation, ported from tetwild::TetWild::adjust_sizing_field: a breadth-first walk out of the seeds over mesh neighbours; every vertex reached within R = 1.8 l of its nearest seed has its scalar multiplied by 0.5 + 0.5 dist / R, the walk stops at vertices farther than R, and the result is floored at the sizing floor. The seeds themselves keep the scalar the caller gave them. Returns the number of vertices lowered.
| TopoOffsetTetMesh::GradientSplit wmtk::components::topological_offset::TopoOffsetTetMesh::gradient_split | ( | bool | include_face_samples = true | ) | const |
| include_face_samples | false skips the face-interior half (the expensive one). |
|
static |
Stage k of n's target: d0 + k (delta - d0) / n, so d0 at k = 0, and delta itself at k = n (not the formula, which rounding can leave a hair off it).
| void wmtk::components::topological_offset::TopoOffsetTetMesh::init_band_exact | ( | ) |
The exact band integral, m_band_exact, on the input triangles (m_phi_V, m_phi_F, m_phi_F_inner, m_input_complex_envelope), built only where something reads it: the band rules "exact", "front_flux" and "exact_swept" (with its value table and the bisector search), "surface" with a label clean-up, settle_mark "max", DEBUG_output. init_offset_potential() calls it.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::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
| V | V by 3 vertex matrix |
| T | #T by 4 tet matrix |
| T_tags | #T by #tags tag matrix |
| V_env | V_env by 3 EnvelopeSurface vertices |
| F_env | F_env by 3 EnvelopeSurface faces |
| double wmtk::components::topological_offset::TopoOffsetTetMesh::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_input_complex_bvh() captured them), the distance to m_input_complex_envelope everywhere else.
Inside means a winding number above 1/2 (wmtk::utils::winding_number_inside()) with respect to the solid's boundary faces, oriented out of it (m_input_solid_winding); a point on that boundary is not inside and is at distance 0 from the envelope's triangles anyway. A complex with no cells has no inside. This is the distance SimplicialComplexBVH::squared_dist() answered (inside any of its tets, else the distance to the tets' faces, the isolated triangles, edges and points) until 2026-10-15, when the 3D offset's distance queries moved onto the envelope: outside the solid the nearest point of the tets' faces is on its boundary, which is what the envelope holds.
|
overridevirtual |
Reimplemented from wmtk::TetOptimizerMesh.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::is_edge_on_region | ( | const Tuple & | loc | ) |
Whether edge loc lies on a region boundary / on the offset surface, by the cached face classes.
|
inlineoverridevirtual |
The offset's surface can end on a non-manifold or boundary edge of the input complex, which the base must not flip or split across.
Reimplemented from wmtk::TetOptimizerMesh.
|
inline |
While the repulsion runs: an outer end of a marched edge – off the complex, with a neighbour on it – that no envelope holds. Asked of the mesh every time, so it follows the repulsion rounds' operations.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::label_cleanup | ( | const std::string & | where | ) |
EXPERIMENTAL_label_cleanup (see the spec). The one exception to labels being fixed after construction: a cell on the band / non-band interface whose own geometry puts it on the other side of the level set takes the other label, where the flip keeps the topology of both. Invariant: the number of connected components (face adjacency) of the band and of the non-band region is the same before and after; a change throws.
Serial passes in tid order over the cells with a front face, reading the current labels, until a pass flips nothing; positions do not move, and a cell's side test reads its own corners alone, so a cell flips at most once and the passes end. After each flip every value derived from labels at the cell's closure is re-derived (the closed labels, the front's cached face class, the front flags of its corners); after the passes the corners' vertex order and the band-region map. One [label cleanup] line. Called by loop_group() after the operation passes; a no-op with the key "off".
| double wmtk::components::topological_offset::TopoOffsetTetMesh::label_flip_surface_change | ( | size_t | tid | ) | const |
EXPERIMENTAL_band_volume_rule "surface": how much the face term changes if cell tid's label flips (band <-> background): + its faces shared with a cell of its own label (they become front faces), - its faces shared with a cell of the other label (they stop being front faces); a face on an input-complex cell or on the domain boundary counts neither way. What label_cleanup()'s surface flips compare against 0.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::label_offset_boundary | ( | ) |
Tag the two tracked surfaces for the optimization phase.
The offset surface is the faces across which the incident tet labels differ, so it falls out of the labelling and is recomputed here once. The 2D twin is TopoOffsetTriMesh::label_offset_boundary().
| void wmtk::components::topological_offset::TopoOffsetTetMesh::linear_growth | ( | ) |
growth_mode "linear": stages k = 0..N (N = linear_growth_steps), stage k at growth_stage_target(m_growth_start, target_distance, k, N). Before each stage target_distance is set to its target and the field rebuilt on it (init_offset_potential()); stages before the last are optimize_offset_loop(false, true) to convergence, their frames led by g<k>; stage N is the ordinary loop. A stage before the last that does not converge stops the run there, target_distance and the field set back for the verdict.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::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 the live cells (global_energy()), split into its AMIPS part and the band cells' band_cell_term(). What ~/Developer/wmtk_outputs/plot_energy.py plots per pass.
Steps: "start" (a group's E before its operation pass: a baseline, not a step), then one per smoothing pass. There is no engine hook after an operation pass, so the group's FIRST smoothing line carries the group's name ("split", "collapse", "swap") and its change from "start" is the operation pass and that smoothing pass together; every later one is "smooth".
| void wmtk::components::topological_offset::TopoOffsetTetMesh::log_refine_block_census | ( | const std::string & | when, |
| double | filter_energy | ||
| ) | const |
For every element above filter_energy, why its edges cannot be split: short / valence / contain / free. The 3D twin of log_refine_block_census().
|
overridevirtual |
Called by smooth_all_vertices() after each pass's own accounting lines: an application that solves some vertices on a path of its own logs, and then resets, its counters here.
Reimplemented from wmtk::TetOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::log_stuck_refine_census | ( | double | max_metric, |
| double | filter_energy | ||
| ) |
Why the final pass is stuck: a census of the tets stuck-refine is about to chase. The 3D twin of log_stuck_refine_census().
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::marching_split_edge_after | ( | const Tuple & | t | ) |
check inversion & rounding
| void wmtk::components::topological_offset::TopoOffsetTetMesh::marching_tets | ( | ) |
Marching tets: every edge with one endpoint in the input complex (label 1/2) and the other in the background (label 0) is split – where d(x) = target_distance along the edge, or under construction_mode's fallback at half the maximum marchable distance or at the midpoint (see edge_split_sphere_trace()) – and afterwards every background tet still touching a complex frontier vertex (the split-off halves) becomes the band (label 2).
| void wmtk::components::topological_offset::TopoOffsetTetMesh::mark_input_complex_vertices | ( | ) |
Set VertexExtra::m_is_on_input from the construction labels, once label_input_complex() has evaluated the selection. The 2D twin has the same name.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::max_band_vertex_distance | ( | ) | const |
The furthest any offset-surface vertex sits from the input complex, by BVH. 0 when no offset exists yet. Sizes dhat in init_offset_potential().
| double wmtk::components::topological_offset::TopoOffsetTetMesh::offset_energy | ( | ) | const |
The offset energy: band_cell_term() summed over the band cells, E without its AMIPS term; under "surface" surface_term_total(). What the settle test compares (settle_refinement_turn()).
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::offset_face_normal_out_of_band | ( | const Tuple & | f, |
| Vector3d & | n | ||
| ) | const |
The normal of live offset face f, unit, pointing out of the band; false when f has no band cell or no area.
| TopoOffsetTetMesh::FaceSamples wmtk::components::topological_offset::TopoOffsetTetMesh::offset_face_samples | ( | const Tuple & | f | ) | const |
The Phi residual at the stencil_order stencil's points of offset face f. Returns nothing for a face with an unreachable corner. The 2D twin is offset_edge_samples().
| std::pair< size_t, size_t > wmtk::components::topological_offset::TopoOffsetTetMesh::offset_membership_mismatches | ( | ) | const |
perform_sanity_checks: how many vertices carry m_is_on_offset without a live offset face, and how many are the other way round. Whole-mesh, O(V x ring); zero is the invariant.
|
inline |
1 / front_conv_frac()^2: the factor that turns a squared relative error into a squared error in units of the tolerance. The scale of face_offset_term() and of the repulsion term.
|
overridevirtual |
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
TetWild's bare coarsening passes (collapse + swap with no length gate, before the first iteration of mesh_improvement() and after the last): on under TetWild's rules (m_op_rules), as in TetWild – mesh_improvement() runs only in tetwild_pass(), so in every TetWild pass (since 2026-10-20). Off otherwise: in the loop, with no length gate, the quality test alone demolished the band, and the sizing field cannot refuse a collapse.
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
DEBUG_remote_output: a frame after every iteration of a TetWild pass (mesh_improvement() runs only in tetwild_pass()), labelled "<prefix>r<turn>F_it<k>".
Reimplemented from wmtk::TetOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::optimize_construction_pass | ( | ) |
optimize_construction: TetWild on the mesh as constructed, before the loop (and before an init_optimize loop). Called by optimize_offset() after the "construction" frame.
Every vertex's sizing scalar is set to 1 first, so the pass starts from the field at 1 everywhere. Then tetwild_pass() with the offset surface held in an envelope of eps m_construction_distance / 2 – half the distance the march traced it to, d_0 when growth_mode applies (m_construction_envelope_eps, this pass only; the final pass keeps envelope_size) – and every other tracked surface in m_envelope, under TetWild's own rules (m_op_rules: no band term); then the loop's setup redone on its result (vertex order, plastic rests). The loop starts from the sizing field the pass leaves.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::optimize_offset_loop | ( | bool | final_stage = true, |
| bool | refine = true, |
||
| const std::string & | label = std::string(), |
||
| int | max_turns = 0, |
||
| bool | fold_free = false |
||
| ) |
TetWild's loop, the front placed inside its smoothing passes. final_stage false is the init_optimize loop: it returns on convergence without the final TetWild pass and without deciding the verdict. refine false skips the halving. label names the loop in its log. max_turns > 0 caps the turns in place of max_rounds (growth_mode "smart"'s attempts). fold_free: a converged turn with any folded offset face (fold_census()) ends the loop as not converged, before the verdict and the final TetWild pass. Returns whether the loop converged (fold-free when asked) within its turns.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::plastic_rest | ( | const size_t | tid, |
| Eigen::Matrix3d & | R | ||
| ) | const |
The rest edge matrix of a plastic cell with a valid rest, columns rest_pos[k] - rest_pos[0] in the oriented order; false when the cell is not plastic, has no rest, or the rest holds no shape (det <= 0) – the cell is then measured against the regular tet.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::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. before are the cells it replaces (live, read in its before-hook), after_labels the labels of the cells it will make or change. True when the plastic medium is on, every before cell is plastic (all_plastic()) and every after cell is plastic (a label other than 1 and 2: no band cell, no input-complex cell). The rests are up to date at every rule – restamped after every smoothing pass and, in the operations, on every cell made or changed – so the before cells are at their rests and the after cells are stamped before any rule reads them.
Then every cell on both sides has A = 3 and an AMIPS term of (w / s^3) 27 vol, no band term, and the operation retriangulates one region of one total volume: the two sums of tet_energy() are EQUAL in exact arithmetic. Floating point does not reproduce it – measured on 2000 perturbed all-plastic collapses with VolAMIPSEnergy3D::value_of()'s arithmetic, the relative difference had median 3.5e-16 and max 3.1e-15, after > before in 876, after < before in 858, an exact tie in 266 – so the rules take the equality instead of comparing: a swap is refused outright (its rule is strict), a collapse is not gated by the energy (a tie passes) and answers only to the length gate, the inversion test and the other refusals.
|
inline |
The same selection as potential_for(), as the pointer the energies take a share of. Null only when m_offset_potential is, which the front placement paths test for.
|
inlineoverridevirtual |
The engine's coarsening region test (the region's max quality_rel() may not rise). EXPERIMENTAL_coarsen_guard "no_rise": on a clamped scale, AMIPS over stop_energy but never below 1, so every cell under stop_energy reads the same 1. collapse_after_connectivity()'s coarsening guard (the max AMIPS over the cells a collapse changes may not rise) decides; with no smoothing inside a collapse (coarsen_local_smoothing_passes 0, the default) the region's max cannot rise after a collapse the guard keeps, so this test never refuses one. "bar": the engine's own. In 3D only the coarsening reads quality_rel().
Reimplemented from wmtk::TetOptimizerMesh.
| size_t wmtk::components::topological_offset::TopoOffsetTetMesh::refine_front_by_halving | ( | const std::vector< EnergyCriterion::Refinable > & | faces | ) |
THE refinement: halve the sizing scalar at the corners of every refinable face, once per vertex per call, floored at max(min_sizing_scalar, min_edge_length / l), then graded outward. Returns the number of vertices lowered.
| size_t wmtk::components::topological_offset::TopoOffsetTetMesh::refine_front_by_halving | ( | const std::vector< size_t > & | vertices | ) |
The same halving at the listed vertices themselves: each lowered once per call, floored, then graded. The face form above is this on its faces' corners, in the order given; front_measure "vertex_ring" calls it directly with the vertices whose ring measure is over the bar.
|
overridevirtual |
TetWild's stall-driven sizing refinement, verbatim; final pass only. See the 2D twin.
Implements wmtk::TetOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::refresh_offset_membership | ( | size_t | vid | ) |
Re-derive m_is_on_offset for one vertex from the cell labels, exactly.
THE definition of the flag, and the only thing that writes it after label_offset_boundary(): a vertex is on the offset surface iff some incident face has the band on one side and a non-complex cell on the other. Called from the three hooks where an operation can change the answer – see the note above m_collapse_edge_link.
Reads labels, never the flag it is writing and never the cached face class, so a wrong value cannot propagate and any vertex an operation touches is corrected whatever it carried before.
It deliberately does NOT touch m_vertex_attribute[vid].m_is_on_surface, which is the base's union over every tracked surface (input, region, offset); clearing that from here would unhold a vertex that is still on a region boundary. See the CLAUDE.md note.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::relabel_input_complex | ( | ) |
Every construction label (vertex, edge, face, tet) back to 0, then label_input_complex(): the input complex from the region tags, which the operations carry.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::report_offset_face_lookup_misses | ( | const char * | when | ) | const |
Log the two counters above, run totals, and only when either is non-zero – a clean run prints nothing. Not gated on perform_sanity_checks: these are free unless they fire.
|
inline |
THE REPULSION PART OF THE PER-TET ENERGY, before the march (tet_energy(), candidate_energy()): a cell carries O(v) of each of its corners v that is an outer end within it – off the complex, held by no envelope, in a cell that has a complex vertex, i.e. joined to the complex by a marched edge of this cell. These cells are the band the march will make, so this is the counterpart of a band cell carrying its front face's term. Reads the cell's own four vertices only. 0 while the repulsion does not run.
|
inline |
How many cells of vid's ring carry its term (repulsion_cell_term()): the smoother's objective is the ring's sum of the per-tet energy, so it charges O(v) that many times.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::repulsion_embedding_kept | ( | const std::vector< size_t > & | tids | ) | const |
repulsion_rounds: whether the complex is still simplicially embedded around the cells tids an operation just made – no cell off the complex has complex vertices spanning a simplex off the complex (tet_is_simp_emb()'s test, decided from the cells' labels, which the operations carry, instead of the edge and face labels, which they do not). A collapse or a swap that breaks it is refused. Defined in TopoOffsetTetMesh.cpp.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::repulsion_smoothing | ( | ) |
repulsion_smoothing_passes and repulsion_rounds (see the spec): between the simplicial embedding and marching_tets(), smoothing passes and then rounds of the loop's operations that push the outer ends of the marched edges out to 2 x target_distance, stopping once every outer end is beyond target_distance + front_conv.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::restore_snapshot | ( | const MeshSnapshot & | s | ) |
Put the mesh back as s holds it: the connectivity rebuilt (init_with_isolated_vertices(), which clears the old one and resizes every collection), the collections' live slots copied back, the band regions re-derived. Not restored: run counters, the debug-frame sequence. Between passes only.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::ring_max_quality | ( | size_t | vid | ) | const |
Max AMIPS (the cube root of the stored cell quality) over the tets incident to vid. -1 if it has none.
|
inlineoverridevirtual |
Implements wmtk::TetOptimizerMesh.
|
inline |
Place a vertex, keeping its exact and rounded coordinates in step.
The offset works in doubles throughout, so every vertex it places is rounded: its exact position is its double one (set_pos_to_posf). The shared split's exact-midpoint fallback and the exact tests around an unrounded neighbour read it through pos().
|
inline |
What the settle test compares turn to turn: offset_energy() under the volume rules; under "surface" stencil_max_sq_error() (EXPERIMENTAL_settle_test "stall") or surface_integral_raw() ("bar").
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::settle_refinement_turn | ( | int | turn, |
| double & | e_prev, | ||
| bool | refine_now | ||
| ) |
EXPERIMENTAL_settle_refinement, one turn, after energy_criterion(): settled when the offset energy (offset_energy(), no AMIPS) fell by less than the bar energy b = front_conv^2 A / (2 target_distance) since e_prev (the offset energy at the end of the previous turn, turn 1: before its first group; set to the offset energy now). Only a settled turn tests the front faces (min_triangle_distance() and, settle_mark "max", ExactBandIntegral::max_distance()) and halves the corners of those more than front_conv from the level set, when refine_now. Logs the turn's [settle] line. Returns settled and no face marked: the loop's exit. See the spec doc.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::simplex_in_input_complex | ( | const std::vector< size_t > & | c | ) | const |
Whether the simplex on the vertices c (2 or 3 of them) is in the input complex, from the cells' labels: in a cell of the complex, or, for offset_in, on a domain-boundary face of a body cell. Single-body offset_in or offset_out only, which repulsion_smoothing() checks.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::smart_growth | ( | ) |
growth_mode "smart" (see the spec doc): the loop at d_0 up to max_rounds turns (throws when it does not converge), then attempts of smart_growth_attempts turns each, as SmartGrowthSchedule picks their targets, each judged converged and fold-free (optimize_offset_loop(..., fold_free true)). The mesh is stored (take_snapshot()) after level 0 and after every success, and put back (restore_snapshot()) after every failure; SmartGrowthSchedule::Next::GiveUp throws. The attempt at target_distance is the final stage: on success it runs the final TetWild pass and decides the verdict.
|
overridevirtual |
User specified modifications and desideratas for after smoothing a vertex.
| t | Tuple refering to a vertex |
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
The smoothing hooks. A derived class overriding them – or invariants(), which a smooth also runs – must read every attribute outside the smoothed vertex's star (other vertices, and the edges, faces and tets not incident to the vertex) through const access, e.g. std::as_const(m_vertex_attribute)[u].
Parallel smoothing runs non-adjacent vertices concurrently without locks (see OptimizerParameters::colored_smoothing). Two of them can share a neighbour, and a non-const access inside the smooth records the entry, which a rejected smooth writes back: two threads would write the same neighbour at once. Every smooth of that pass checks this (TetMesh/TriMeshm_check_smoothing_stays_in_star) and throws on the first offence.
Reimplemented from wmtk::TetOptimizerMesh.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::smooth_front_vertex | ( | const Tuple & | t | ) |
Placement of a front vertex: the shared smoother with the offset's options. See FrontSmooth3d.cpp.
Front placement: an offset-surface vertex is placed by the shared 3-D smoother with the offset's energy (see smooth_front_vertex()). Everything else is smoothed by Optimize3d.cpp. The 2D twin is FrontSmooth2d.cpp.
|
inlineoverridevirtual |
The smoother holds every tracked face at a held vertex to that one envelope, whatever the face: the per-vertex answer above, not the base's per-face one.
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
A held vertex is pulled to and contained in m_envelope – a real Envelope, as the pull requires. In a TetWild pass a front vertex no other envelope holds is pulled to and contained in the front's own envelope, m_offset_envelope (one on another held surface too keeps m_envelope, which its front faces are not in, so it stays where it is). Any other vertex, and the front in the loop, is held by nothing.
Implements wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
THE smoothing objective at vid, for every vertex the shared smoother places: vid's part of the energy E, the sum of tet_energy() over vid's ring up to the terms that do not move with vid. The AMIPS term over the ring (amips3_energy()) for every vertex – the smoother adds no AMIPS term of its own, every caller passing opts.w_amips 0 – plus the band term of the ring's band cells (band_volume_energy_at(); through front_energy() for a front vertex the loop places) unless an input envelope holds vid or, under "exact", the term cancels over the ring (band_term_cancels(): it does not move with vid), or, in the passes before the march, a repulsion vertex's term once per ring cell that carries it (repulsion_energy(), repulsion_cell_term()). Under TetWild's rules (m_op_rules) none: null, so the engine's smoother minimises the raw sum of AMIPS over the ring.
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
The engine's end-of-pass hook (TetOptimizerMesh::smooth_all_vertices() calls it after EVERY smoothing pass, in every path: a group's passes and pre_smooth): use_rest_pose restamps the plastic rests here – between passes and after the last, never before the first. With the operations stamping the cells they make or change, every rule reads rests that are up to date. A no-op while the plastic medium is off (the final pass, coarsening, the repulsion passes).
Inside a turn's operation group it also logs the pass's [energy step] line (log_energy_step()), before the restamp, and a "stamp" line after it when the plastic medium is on: a restamp changes the plastic cells' energy without moving anything.
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
What the shared smoother's projected step compares for a vertex held on a surface: the sum of tet_energy() over the one-ring, the rule every other move answers to (under TetWild's rules, m_op_rules, the base's max over the ring). The step takes the first projected position where it is strictly below the sum before the move.
Read on the mesh as it is in both calls: the engine asks for the value before the move (stored) before its solve moves the vertex, and a candidate's after the candidate is placed. Until this hook the step took the first position where the ring's MAX of offset_amips_weight AMIPS^3 dropped, and the veto after it refused a move that raised the sum – measured on the cube (target 1e-2, bar 1e-4, serial, 7 turns): 5,270 such refusals of 21,687 held moves, 2,964 of them moves whose AMIPS part fell and whose band part rose. Now the step looks on along its shorter steps for a position E accepts.
Reimplemented from wmtk::TetOptimizerMesh.
| polysolve::nonlinear::Solver & wmtk::components::topological_offset::TopoOffsetTetMesh::smoothing_solver | ( | ) |
The thread's shared solver, created with the engine's parameters if needed, with kSmoothRelGradNormTol applied. Every smoothing path of this component takes it from here.
|
overridevirtual |
Optional annotation-only adjustment of the midpoint. Ordinary optimization returns true without changing the shared TetWild position.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Restore application cell data on the children made by a split.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Application metadata not represented by the shared vertex attributes.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Split policy that is the offset's own: which region tag the two child tets inherit, and which of the two tracked surfaces the new vertex joined. See EdgeSplittingTet.cpp.
The shared split places the new vertex, keeps quality and shared attributes, and checks envelope containment. These three hooks add what only the offset knows: which region tag each child tet inherits, and which tracked surfaces the new vertex joined.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
This function computes the attributes for the added simplices. User specified modifications and desideratas for after an edge split.
| the | edge Tuple to be split |
check inversion & rounding
update quality
containment: the new surface triangles must stay inside the envelope
update vertex attribute
update face attribute
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
User specified preparations and desideratas for an edge split before changing the connectivity.
| the | edge Tuple to be split |
save face track info
Reimplemented from wmtk::TetOptimizerMesh.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::split_edge_is_due | ( | const Tuple & | e | ) | const |
The shared split pass's gate: TetOptimizerMesh::split_all_edges's is_weight_up_to_date without its staleness test.
|
overridevirtual |
Compute the attributes for the added simplices.
User specified modifications and desideratas for after a face split
| t | The face tuple to be split. |
Reimplemented from wmtk::TetMesh.
|
overridevirtual |
User specified preparations and desideratas for a face split before changing the connectivity.
| t | The face tuple to be split. |
Reimplemented from wmtk::TetMesh.
|
inlinestatic |
Whether the split running on this thread is off the longest edge of an incident tet. Set by split_edge_before(), counted by op_event() when that split commits.
|
overridevirtual |
Compute the attributes for the added simplices.
User specified modifications and desideratas for after a tet split
| t | The tet tuple to be split. |
Reimplemented from wmtk::TetMesh.
|
overridevirtual |
User specified preparations and desideratas for a tet split before changing the connectivity.
| t | The tet tuple to be split. |
Reimplemented from wmtk::TetMesh.
|
inline |
The construction stages' frames (input, simplicial embedding, march, tagging, repulsion): DEBUG_output's, none under DEBUG_remote_output.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::stamp_plastic_rests | ( | ) |
Stamp rest := current for every plastic cell; called when the loop starts and after every smoothing pass (smoothing_pass_done()). A no-op while the plastic medium is off.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::stamp_rest_cell | ( | size_t | tid | ) |
Stamp one cell's rest := its current corners (oriented order). For the cells a split or a swap creates and a collapse changes, before the operation's energy rule; a no-op unless the cell is plastic.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::stencil_max_sq_error | ( | ) | const |
O*, the settle test's metric under "surface" (HackMD "settlement option 2"): the max over every live offset face's stencil points (face_stencil(), stencil_order) of (d - delta)^2, a squared length; +inf when a point is unmeasurable, 0 without a front or a potential.
|
inline |
How many points for_each_face_sample() visits at the configured order k: under "subdivision" 3 at order 0, else (n + 1) (n + 2) / 2 + n^2 with n = 2^(k-1) (4, 10, 31, 109 for k = 1..4); k (k + 1) / 2 under "interior", the interior lattice of interior_face_stencil() (1, 3, 6, 10, 15 for k = 1..5); (k + 1) (k + 2) / 2 under "fem" (3, 6, 10, 15, 21); 0 below order 1 under those two. Kept in step with for_each_face_sample() by the unit tests stencil-order-point-counts and fem-face-stencil.
|
inlineoverridevirtual |
The containment of a tracked face an operation has just made (the base asks only after the fact). A wall face: m_envelope, band or not – nothing leaves the domain box. The offset surface: nothing in the loop, m_offset_envelope in a TetWild pass. Any other tracked face: m_envelope when it is held (face_is_held()), else nothing.
Reimplemented from wmtk::TetOptimizerMesh.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::surface_face_term | ( | size_t | a, |
| size_t | b, | ||
| size_t | c | ||
| ) | const |
W A_f m_f of triangle (a, b, c), its corners read in ascending vertex order so every reader gets the same number (SurfaceOffsetError3D::face_value()); +inf where a stencil point is unmeasurable; 0 without a potential.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::surface_integral_raw | ( | ) | const |
O_g = int over the front of (d - delta)^2 dS by the faces' stencils, sum_f (A_f / |S|) sum_k (d(x_k) - delta)^2: the face term without its weight, a squared length times an area; EXPERIMENTAL_settle_test "bar"'s metric. +inf where a point is unmeasurable.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::surface_term_weight | ( | ) | const |
W = L / front_conv_frac()^2, L the bounding box diagonal (diag_l): W A_f mean r^2, r = (d - delta) / delta, is L (A_f / |S|) sum (d - delta)^2 / front_conv^2 – a volume, as the AMIPS term (w / s^3) V A^3 is, both integrands 1 at convergence (HackMD, 2026-10-27 revision).
|
overridevirtual |
Propagate application data to the cells made by a successful topological swap.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Which tag the tets a swap creates should carry, and the topology half of the surface-flip refusal (class match, same boundary). The geometric half is the shared swap's containment check. Both also take the energy rule's before-half (m_swap_energy_before) and fill the record candidate cells take their labels from (SwapRecord). See Optimize3d.cpp.
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
Reimplemented from wmtk::TetOptimizerMesh.
|
private |
INTERIOR swaps only: the ring must be homogeneous in tag and in construction label, and the single value of each is captured for swap_after_cells() to stamp on the new cells. A ring that is not homogeneous has a region boundary or the offset surface running through it, and an interior swap would move that boundary.
DO NOT call this on the surface path. A face is on the offset surface exactly when one incident cell is band and the other is not (cell_is_offset_band: label == 2), so the ring around a surface-flip edge ALWAYS spans two labels and this always refuses – which is what made every offset-surface flip impossible until 2026-09-17. The surface path uses swap_capture_surface_sides() instead.
|
overridevirtual |
The 4-4 / 5-6 case search's score, on the energy (under TetWild's rules, m_op_rules, the base's: the max of AMIPS^3): for op_case 0 (the current cells) the sum of tet_energy() over the ring that swap_before_interior() / swap_before_surface() took, the same cells the engine passes here; for a candidate (op_case >= 1) the sum of candidate_energy() over tets; std::numeric_limits<double>::max() as soon as a cell is inverted, as the base does.
TetMesh::swap_edge_44() and swap_edge_56() pick their retetrahedralization by seeding min_energy with the score of DOING NOTHING (op_case 0) and taking a case only when it scores strictly lower, before any after-hook runs. With this score that test IS the swap rule of swap_after_cells() for these swaps, applied to cells that do not exist yet (see tet_energy(), CANDIDATE CELLS); the after-hook applies it again on the real cells. Until 2026-09-28 the search scored AMIPS^3 (the base's max over the cells), so a case that lowers the energy while raising AMIPS never reached the rule.
Also counts the scored cases of a flip of the offset surface for [flip funnel] (SwapSurfaceSides::case0_energy: inverted, not better, better), and records the lowest candidate score for the sanity check (SwapRecord::scored_energy).
Until 2026-09-25 these overrides reported stop_energy for op_case 0, which turned the base's test into "the new cells are under stop_energy" for flips of the offset surface (the absolute bar; see swap_before_surface() for why it existed and why it went).
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
User specified energy to decide which of the 5 possible orientations should be chosen.
Accepts the last shown orientation if not overridden.
| tets | New tets after performing a 5-6 swap. |
| op_case | The operation case, where 0 are the tets before swap. |
Reimplemented from wmtk::TetOptimizerMesh.
|
overridevirtual |
The face (2-3) swap's before-hook: TetOptimizerMesh::swap_face_before() with its AMIPS^3 gate replaced by the energy – in the loop; under TetWild's rules (m_op_rules) the base itself.
The base gates the three new cells, each by AMIPS^3 against the max of the two cells' stored AMIPS^3, and only then calls swap_before_interior(). Here the sum of candidate_energy() over the three new cells must be strictly below the sum of tet_energy() over the two old ones, which swap_before_interior() takes – so it runs BEFORE the gate. The rest of the base's steps are duplicated in order, reject kinds included (the engine has no energy hook for this gate; one is a planned follow-up, after which this override goes).
Reimplemented from wmtk::TetOptimizerMesh.
|
inlineoverridevirtual |
In the loop (m_op_rules Loop) admits every swap; under TetWild's rules, the base's. The loop's case: switches off the engine's own AMIPS^3 swap rule (after is the largest AMIPS^3 over the new cells, before that of the old cells), so the energy decides in its place – on the sum of tet_energy() over the cells the swap changes, in the 4-4 / 5-6 case search (swap_edge_44_energy(), swap_edge_56_energy()), the face swap's gate (swap_face_before()) and swap_after_cells(). The AMIPS^3 bound cannot refuse early: the energy is a sum, and a new cell's AMIPS term can rise while the sum falls.
Reimplemented from wmtk::TetOptimizerMesh.
|
private |
A fresh record for the swap in flight; flip for a surface flip, whose sides swap_capture_surface_sides() has captured first. See SwapRecord.
|
private |
perform_sanity_checks, from swap_after_cells() once the new cells are labelled: the record's scored_energy against tet_energy_sum(tids), counted in m_swap_scoring_*.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::swap_swept_change | ( | const std::vector< std::array< size_t, 4 > > & | new_band, |
| bool | flip | ||
| ) |
"exact_swept": the swap in flight's dE* / delta, new_band the band cells among its new cells (by candidate_label() in the case search, by the labels written in the rule): 0 for an interior swap, band_chain_change() from SwapRecord::old_band for a flip.
|
inline |
"exact_swept" (EXACT SWEPT CHANGE).
EXACT SWEPT CHANGE (EXPERIMENTAL_band_volume_rule "exact_swept", 2026-10-22). The energy is "exact"'s – b(t) = J_t / delta, J_t = ExactBandIntegral::value() – but an operation's rule does not integrate its cells before and after: it compares the AMIPS terms of the cells it changes (band_term_cancels() is true for every one) plus the CHANGE of the band term E* / delta, E* = int_B (d - delta) dV, B the union of the band cells, which lives only where B changes, and is integrated exactly there by value() on small tets:
| TopoOffsetTetMesh::MeshSnapshot wmtk::components::topological_offset::TopoOffsetTetMesh::take_snapshot | ( | ) |
Consolidate, record the image, and rebuild the mesh from it at once (restore_snapshot()): the mesh then IS what a restore gives, so every attempt from the image – the first, and each one after a revert – starts from identical storage (the rebuild orders each vertex's tet fan by tid; consolidation keeps the old order). Refused with feature-edge or feature-point tracking on: their collections are not in the image. Between passes only.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::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, nothing hypothetical. E = the sum of E(t) over the live cells (global_energy()) is the one energy of the 3D loop.
tet_energy(t) = cell_energy(oriented_tet_vids(t), get_quality(t),
cell_is_offset_band(t) && !m_tetwild_pass)
cell_energy(vids, A^3, band) = MAX_ENERGY if A^3 >= MAX_ENERGY
= (w / s^3) vol A^3 + [band] b(vids) otherwise
w = offset_amips_weight, s = stop_energy (optimization_stop_metric()), so a cell at the stop energy costs about w vol. A^3 is the base's TetOptimizerMesh::get_quality(), the AMIPS^3 the engine stores as the cell quality; its MAX_ENERGY (unscoreable) is the cell's energy. vol A^3 is vol_amips3(), from the exact AMIPS^3 and the edge lengths, never from a determinant. b is band_cell_term(): the band cell's part of int_B (d - delta) / delta dV, as an upper bound that no split raises – vol times the min over all input triangles P of the corner mean of (d_P - delta) / delta. Both terms are volumes, so E converges under refinement. Before the march, while the repulsion runs (m_repulsion_potential set), a cell also carries repulsion_cell_term(); there is no band then. A cell's energy depends on its own four corners and its label alone: an operation changes the energy of the cells it changes and of no other – except under use_rest_pose, where a plastic cell's (cell_is_plastic(), outside the band and the complex) AMIPS term is vol A^3 against its stamped rest (TetAttributes::rest_pos, VolAMIPSEnergy3D::value_of()), so a restamp changes it too: every plastic cell after each smoothing pass, and the cells an operation makes or changes at that operation. All-plastic operations are decided as exact ties (plastic_tie()), not by comparing the sums.
WHERE IT IS COMPARED: always as the SUM of tet_energy() over the cells an operation changes, before against after, the cells summed in the same order each time – in the loop. In a TetWild pass none of the rules below reads it: TetWild's own decide (m_op_rules).
BAND TERM THAT CANCELS (band_term_cancels()): under EXPERIMENTAL_band_volume_rule "exact" or "front_flux", a collapse or swap whose old cells are all band cells makes new cells that are all band cells too – a swap gives every new cell the ring's one label (interior) or a ring vertex's side, all of them band (flip); a collapse's new cells are v1's old cells in their own slots with their own labels – and the new cells cover the region the old ones did. So the band region B is unchanged, and the band term, the integral of (d - delta) / delta over B, changes by exactly 0 – where value() is exact. On a rule-(a) piece (ExactBandIntegral) value() is an upper bound that depends on the cell, and its change is not 0. So the ring must also be clear of the input's bisectors: no old, candidate or new cell flagged by ExactBandIntegral::front_flux_flag() or beyond its reach (distance_bound()), the test BandVolumeEnergy3D's front form makes for the same reason. Every such cell is made of the ring's vertices, and both tests flag a subset of points only if they flag the points, so the ring's vertices are tested once, before the operation. Such an operation compares its sums without the band term (band_term = false in tet_energy(), tet_energy_sum(), candidate_energy()): before, candidates and after alike. Not under "corner_bound": its per-cell bound changes with the cells. MEASURED on the cube (target 1e-2, bar 1e-4, front_flux, 15 turns, serial, 516 s): these operations spent 97.4 s (19% of the run) integrating band cells, their band change was |dJ| <= 1.2e-13 delta V (rounding), and reading it as 0 flipped 0 of their decisions.
CANDIDATE CELLS: the 4-4 and 5-6 case searches (TetMesh::swap_edge_44() / swap_edge_56(), scored through swap_edge_44_energy() / swap_edge_56_energy()) and the face swap's gate (swap_face_before()) score cells that do not exist yet, by the same sum: the current cells by the before-sum (op_case 0), a candidate configuration by the sum of candidate_energy() over its cells – cell_energy() at the candidate's four positions (a swap moves no vertex) with the label swap_after_cells() will write (the ring's one label for an interior swap, the side of a ring vertex the cell contains for a flip). A candidate is taken only when its sum is strictly below the current cells'. swap_after_cells() then applies the rule again on the real cells and stays THE rule; under perform_sanity_checks it compares the committed configuration's score with the sum it reads (swap_scoring_check()). Until 2026-09-28 the search and the gate scored AMIPS^3 alone, so a swap that lowers the energy while raising AMIPS was discarded before the rule saw it: at the cube 1e-3, 93.5% of the valence-4 surface edges whose flip would cut the front's error raised max AMIPS.
THE SMOOTHER minimises the vertex's part of E (smoothing_extra_energy()): the AMIPS term over its ring (amips3_energy()) and, unless an input envelope holds the vertex, the band term of its band cells (band_volume_energy_at()) – the held-vertex exception is measured there; under "exact" not where that term cancels (above), being a constant. Every move is then judged by the ring's sum of this energy: a vertex held on a surface through the engine's projected step, which takes the first projected position where that sum drops (smoothing_ring_energy()); every other vertex through the veto after its solve (smooth_front_vertex(), smooth_nonfront_vertex()).
The front's face measure (face_offset_term()) is not part of E: the exit, the refinement and the frames read it.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::tet_flatness | ( | size_t | tid | ) | const |
Scale-invariant flatness: 6 * volume / longest_edge^3.
~0.118 for a regular tet, -> 0 as the four vertices become coplanar, and independent of size. AMIPS saturates at the MAX_ENERGY sentinel while this keeps resolving. The 2D twin is face_flatness().
| double wmtk::components::topological_offset::TopoOffsetTetMesh::tetwild_pass | ( | const char * | what, |
| int | max_its, | ||
| bool | coarsen = false |
||
| ) |
A TetWild pass: every tracked surface held (the final envelopes; the front in its envelope, smoothed like any held vertex), TetWild's own rules for the operations and the smoother (m_tetwild_pass, m_op_rules), mesh_improvement(max_its), everything put back and the band regions re-derived. Returns the max AMIPS after it. The final pass and optimize_construction_pass() both run it; what names it in the log. coarsen (the final pass's): mesh_improvement()'s closing coarsening pass (coarsen_mesh()) runs, if coarsen_pass allows it, its collapses kept only where the max AMIPS over the changed cells does not rise and the changed faces pass the offset half (collapse_after_connectivity()); one [coarsening] line logs it. max_its 0: no iterations and no bare sweeps, the coarsening alone – the "final coarsening" the loop runs when it converges at the final target with max AMIPS already under stop_energy (no final pass).
|
overridevirtual |
The engine calls this at the start of local_operations() and after each operation pass that ran; DEBUG_crossings hooks the operation passes here. Log-only.
Reimplemented from wmtk::TetOptimizerMesh.
|
inline |
Whether the smoother places vid against the offset term: a front vertex, outside a TetWild pass, that no envelope also holds (vertex_is_held()).
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::vertex_has_live_offset_face | ( | size_t | vid | ) | const |
Whether ANY live offset-surface face is incident to vid. The same question offset_surface_faces_live_at() answers, without building the list: this one runs in the operation hooks, where the list would be allocated and thrown away.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::vertex_is_held | ( | size_t | vid | ) | const |
A vertex is held when one of its tracked faces is (face_is_held()): the smoother then pulls it to and contains it in m_envelope. Asked of the mesh, like face_is_held().
|
inline |
Identification only – no operation refuses the domain wall through these.
The wall is a tracked region boundary like every other one: init_surfaces_and_boundaries() tags its faces m_is_surface_fs and face_is_held() always holds them, so refinement, coarsening, flips and smoothing are governed by the same containment, merge rules and link conditions that govern the input complex.
|
inline |
Is this vertex on a region boundary – a tag boundary, or the domain wall. Derived, not stored, exactly as in 2D.
|
overridevirtual |
Is a vertex part of the substructure.
| vid | Vertex ID |
Reimplemented from wmtk::TetOptimizerMesh.
|
inline |
vol AMIPS^3 of a valid cell from its AMIPS^3 and edge lengths, never from a determinant: AMIPS = tr / det(J)^(2/3) gives vol = (sqrt2/12) (tr/AMIPS)^(3/2), so vol AMIPS^3 = (sqrt2/12) tr^(3/2) sqrt(AMIPS^3), tr = tr(J^T J) = (1/2) sum over the six edges of |e|^2 (no cancellation). Measured: a nearly flat tet (exact 6V = 1.5e-21, edges ~1.5) whose double determinant came out 0 was priced at 0 against an exact 1.7e21, and a collapse that made it passed the rule (one-slot model, stage 2: max AMIPS 1.9e14).
| void wmtk::components::topological_offset::TopoOffsetTetMesh::wait_debug_frame_write | ( | ) |
DEBUG_output: wait for the frame in flight, if any, and take m_debug_timeline back; rethrows what the writer threw. Called before the next frame is handed over, and once the run writes no more frames (topological_offset.cpp), so every frame is complete on disk before anything reads it.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::warn_if_offset_reaches_domain_boundary | ( | ) | const |
Warn if the offset band has grown into the domain boundary.
When target_distance exceeds the clearance between the input complex and the bounding box, construction runs out of room and the band's outer surface becomes the box itself; those vertices are pinned and the target distance is unreachable there.
|
static |
DEBUG_output: rewrite <out>{_main,_off,_surf,_edge,_front,_stencil}.pvd, a ParaView time series over the debug frames. Needed because ParaView only groups a file series when the index is immediately before the extension, which is false for every companion (<output>_NNNNN_off.vtu). Called after every frame, so a killed run still opens. Static: it runs on the frame writer's thread and reads only what it is given.
|
inlineoverridevirtual |
Put the optimization's frames on the run's single debug timeline (see write_debug_frame()), labelled "r<turn><tag><pass>_<op>" / "r<turn><tag>_end", tag S in the loop and F in the final pass; led by m_frame_prefix (i during init_optimize).
Implements wmtk::TetOptimizerMesh.
| void wmtk::components::topological_offset::TopoOffsetTetMesh::write_phi_grid | ( | const std::string & | path, |
| int | n | ||
| ) | const |
output stuff Sample the potential on the plane through the box centre normal to its shortest extent and write it as <path>_phi.vtu, n x n samples. See phi_grid_resolution; 0 disables.
|
mutable |
The scored CASES of those flips, by what the case search's energy (the per-tet energy) said about them against the current cells (SwapSurfaceSides::case0_energy). An inverted case is one whose retetrahedralization inverts a cell – swap_edge_*_energy returns double::max() for that – which is a geometric refusal, not a quality one. Only a better case passes the base's energy < min_energy test, so case_better is what can still become a swap.
|
mutable |
of the 4-4 and 5-6 ones, those for which at least one case survived accept_case and was scored. offered(4-4 + 5-6) - this is exactly what flip_wrong_case threw away.
|
mutable |
[flip funnel]: of the flips of the offset surface, how many survive each stage. Reset per turn and reported next to [swap reject].
[swap reject] counts every refusal of every surface flip, most of which SHOULD be refused. This follows only the flips of the offset surface – both replaced faces offset surface – through the base's case search and then swap_after_cells(), which captures the new cells' sides and then applies the energy rule (see tet_energy()).
Reading it. offset-surface flips is counted in swap_before_surface(), which is the app's first sight of a candidate; anything the base turned down earlier (valence, bbox, connectivity) never reaches it and is in [swap reject] instead. For a 4-4 or 5-6, what does not reach swap_after_cells() is lost in the base's case search, which scores on the per-tet energy (swap_edge_44_energy()): no retetrahedralization that makes the (c,d) diagonal was found, or none scored strictly below the current cells (the cases split). A 3-2 reaches swap_after_cells() unless a new cell inverts: swap_quality_allowed() admits every swap. after_cells - energy is swap_after_cells() refusing on the side/label capture, energy - committed the energy rule refusing. What the envelope check then refuses is past this hook and shows as after_envelope in [swap reject].
The funnel counts real attempts only. In a screened pass (a parallel pass, see ExecutePass::screen_before_commit) every candidate is first dry-run, and that dry run counts nothing here; [swap reject] does count the refusals of the dry runs, so there offered - cases is what flip_wrong_case threw away in the real attempts.
|
staticconstexpr |
SurfaceTagAttributes::m_surface_class: which of the two tracked surfaces a face belongs to. Same scheme as 2D.
OFFSET is the surface the optimization places at target_distance. Everything else – the input complex, another body's boundary, the domain wall – keeps the primary class 0 and is envelope-checked by the shared operations exactly as in tetwild and simwild, when it is held (face_is_held()).
| std::atomic<int> wmtk::components::topological_offset::TopoOffsetTetMesh::iter_cnt_split_offset_before {0} |
Splits of an offset-surface edge: offered, accepted. Real attempts only: a screened pass's dry runs are not counted.
|
staticconstexpr |
cos 150 degrees: a real fold's two offset faces have normals out of the band closer to opposite than this (the [foldover] dihedral reading, analyze.py's).
|
staticconstexpr |
diagnostic_parallel_for()'s chunk for per-cell, per-vertex and per-face items, whose cost varies a lot (a band cell's exact integral against an AMIPS term alone).
|
staticconstexpr |
Diagnostic (2026-09-28, Uday): where the front solves stop. Per pass, histograms of the final gradient norm of each front solve (log10 bins, -14..+7) and of its ratio to the solve's first gradient norm (log10 bins, -14..+1), read from polysolve's Criteria after the solve. Asked because 96% of front solves hit the 10-iteration cap under the tolerance-unit objective while the interior solves stop on the same absolute tolerance.
|
staticconstexpr |
The Newton stopping rule every smoother of this component runs with, set on the thread's shared solver (the engine's m_solver slot) by smoothing_solver() at the start of every visit – front and interior alike, so the settings do not depend on which path first ran on a thread.
Relative gradient tolerance 1e-6, beside the engine's absolute 1e-10 (which stays). The front objective is in tolerance units, 1e4 times the old front objective, and its gradient's round-off floor sits at 1e-11..1e-9: measured 2026-09-28 on the cube (target 1e-2, tolerance 1e-4), 65% of front solves ran to the 10-iteration cap converged to machine precision (|grad|/|grad_0| at 1e-14..1e-11), unable to meet the absolute 1e-10. With the relative rule: mean 2.8 iterations (exact Hessian) instead of 9.7, 98% stopped on it. Interior solves, which met the absolute rule in 3.8 iterations, stop a little earlier.
|
staticconstexpr |
"exact_swept": the smoother's acceptance after a solve moved vid from x0 to where it is. The move stands iff the ring's AMIPS change (tet_energy_sum(ring, false) - amips_before) plus band_swept_change() is <= 0 (a tie passes, a NaN refuses); if not, the step is halved toward x0 and tested again, up to kSweptHalvings times, then refused. The halved positions are on the segment between two valid positions, and the positions at which vid's ring has no inverted cell are an intersection of half-spaces, so they are valid too (tested anyway, exactly). Counts into c.
| std::shared_ptr<const ExactBandIntegral> wmtk::components::topological_offset::TopoOffsetTetMesh::m_band_exact |
EXPERIMENTAL_band_volume_rule "exact" or "front_flux", or EXPERIMENTAL_settle_refinement with settle_mark "max" (its exact max over a front face): the input's triangles for the exact integral, built once by init_offset_potential() on m_input_complex_envelope.
| std::atomic<size_t> wmtk::components::topological_offset::TopoOffsetTetMesh::m_coarsen_reject_amips {0} |
The coarsening guard's refusals in the running TetWild pass (collapse_after_connectivity()): the max AMIPS over the changed cells rose; the offset face term over the changed faces rose (under EXPERIMENTAL_settle_mark "stencil": a stencil point of the new faces at or beyond front_conv, their max above the removed faces').
|
mutable |
The link of the collapsed edge, captured in collapse_before_vertex().
Which vertices a collapse can move off the offset surface, exactly: the faces that DIE are the ones carrying both endpoints, (v1, v2, w) for w in the link, so only v2 and those w can lose their last surface face. A face (v1, a, b) with neither corner on the edge does not die – it is relabelled onto v2 – so a and b keep it and are unaffected. Nothing but v2 can gain, since faces only ever move from v1 to v2.
Captured before the collapse because the edge is gone by collapse_after_vertex(), which is where the refresh runs.
|
mutable |
growth_mode "individual_op_reject": the folded faces the collapse / offset-surface flip in flight saw before it (folded_front_faces_at(), face_folded_in()), per thread; -1 for no rule.
|
mutable |
The collapse survivor's own sizing scalar, recorded in collapse_edge_before() and put back in collapse_edge_after() when sizing_collapse_min is false; see that key.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::m_construction_distance = 0. |
The distance the marching's sphere trace aims for: target_distance, or half the maximum marchable distance under construction_mode "max_marchable_fallback" (see the marching).
| double wmtk::components::topological_offset::TopoOffsetTetMesh::m_construction_envelope_eps = 0. |
The offset surface's envelope eps while optimize_construction_pass() runs: half the distance the march traced the front to, m_construction_distance / 2. 0 at every other time, when build_offset_envelope() takes envelope_size (the final pass included).
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::m_converged = false |
The run's verdict: the front resolved (EnergyCriterion::converged(): every offset face's measure within the bar, nothing unmeasurable) AND the final quality under stop_energy. Read by the report and by throw_on_nonconvergence.
| std::atomic<size_t> wmtk::components::topological_offset::TopoOffsetTetMesh::m_cross_own {0} |
Per smoothing pass: accepted front moves that took the moved vertex itself (own), or one of its front neighbours (neighbour), from a ring measure <= 1 to > 1.
| std::vector<double> wmtk::components::topological_offset::TopoOffsetTetMesh::m_cross_ring |
DEBUG_crossings (log-only). The ring measure of every front vertex as the last pass left it (NaN where a vertex has none), the positions it was taken at, and whether it is current (consolidation renumbers vertices, so the loop clears it). A pass's line counts the front vertices whose ring measure went from <= 1 to > 1 (up), back (down), new vertices already over (a split's, or an old id at a new position after a storage retry), and vertices over the bar that the pass removed.
| std::atomic<size_t> wmtk::components::topological_offset::TopoOffsetTetMesh::m_damping_moves {0} |
EXPERIMENTAL_damping, per pass: smoothing moves accepted, and how many of them ended on the bound (count_damped_move()). Reported and reset beside the Newton counters.
| std::future<DebugTimeline> wmtk::components::topological_offset::TopoOffsetTetMesh::m_debug_frame_write |
DEBUG_output: the frame whose files a background thread is encoding and writing, if any (write_debug_frame()). Moved into it, m_debug_timeline belongs to that thread until wait_debug_frame_write() takes it back, so the two threads never share it; the frame's arrays are its own snapshot. A std::async future, so destroying it waits for the write: a run that throws still finishes the frame in flight.
|
mutable |
Pass index within the current turn, and the (turn, loop-or-final-pass tag) it belongs to – when those change the index restarts. All three exist only to name frames.
| int wmtk::components::topological_offset::TopoOffsetTetMesh::m_energy_step_turn = 0 |
The turn (1-based) whose operation group is running, 0 outside one: smoothing_pass_done() logs only inside a group, so pre_smooth, the final pass and coarsening log nothing.
| std::optional<EnergyCriterion> wmtk::components::topological_offset::TopoOffsetTetMesh::m_energy_verdict |
The energy criterion as measured when the loop converged; the final pass runs after it and the verdict must not be re-measured on that mesh.
| std::vector<std::string> wmtk::components::topological_offset::TopoOffsetTetMesh::m_face_tag_names |
Face tags, read from the input's tagged faces: bit j of a face's input set is m_face_tag_names[j]. Each name is also a tag id (m_face_tag_bit: id -> bit) so that offset_selection can name it, but not a tet tag: the ids lie past m_tags_count.
|
mutable |
Operations refused for raising the folded faces (fold_reject_active()), per operation group; logged as [op reject].
| std::string wmtk::components::topological_offset::TopoOffsetTetMesh::m_frame_prefix |
Leads every debug frame label: i during the init_optimize loop, g<k> during growth_mode "linear"'s stage k < N, s0 / s<L>a during growth_mode "smart"'s level 0 / level L's attempt A, empty otherwise.
| std::atomic<size_t> wmtk::components::topological_offset::TopoOffsetTetMesh::m_front_veto_asked {0} |
The front veto (smooth_front_vertex(), solve_3d): moves whose Newton solve succeeded and reached the veto, and how many it refused for raising the ring's sum of tet_energy(). Reported and reset per pass beside the Newton counters. Kept apart from m_smooth_rejects.quality, which the non-front veto also counts.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::m_growth_start = 0. |
growth_mode "linear" / "smart": d_0, the distance the march traced the front to when that is below target_distance (m_construction_distance, half the maximum marchable distance). Set by marching_tets() with growth on and optimize_offset true; 0 otherwise, and optimize_offset() then runs the loop once, at target_distance.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::m_init_optimize = false |
init_optimize: optimize_offset() opens with a stencil_order loop without refinement. Set by marching_tets(), only when the target is beyond the maximum marchable distance.
| std::shared_ptr<Envelope> wmtk::components::topological_offset::TopoOffsetTetMesh::m_input_complex_envelope |
The input complex as loaded, as an envelope of its primitives (the rows of m_phi_F, or of m_phi_E and m_phi_P): built once, by init_input_complex_bvh(), never rebuilt. Every 3D distance to the input reads it: the euclidean field (m_offset_potential), the repulsion pass, the band term's searches (OffsetPotential.hpp), the exact integral's box queries (m_band_exact), the march and the diagnostics (with input_complex_distance()'s inside test where a distance must be 0 inside the solid).
init_input_complex_bvh() has one call site, before construct_offset() runs, so this holds the original geometry however the elements representing the complex are later remeshed. Rebuilding from the live mesh would redefine the offset distance in terms of a surface the optimizer had just moved. Containment is not its job – m_envelope holds the complex's boundary in place.
A query engine, never asked a containment question: no operation tests against it. It answers nearest_point_feature() – the foot point plus the feature kind the exact distance derivatives case on – and the per-primitive queries, which read only the envelope's BVH and its copies of the input, filled by every init() (src/wmtk/envelope/Envelope.cpp). Under offset_field "euclidean" it is built exact at target_distance (euclidean_query_envelope()), and its exact structure is unused; under any other field (refused by the 3D app; unit tests build meshes with none) it is built with eps 0, the query engine alone, whose containment queries throw.
| double wmtk::components::topological_offset::TopoOffsetTetMesh::m_input_complex_pad = 0. |
input_pad() of m_phi_V, computed once beside m_input_complex_envelope: the rounding allowance of the band term's box searches on it (OffsetPotential.hpp).
| std::shared_ptr<const wmtk::utils::WindingNumberHierarchy> wmtk::components::topological_offset::TopoOffsetTetMesh::m_input_solid_winding |
input_complex_distance()'s inside test: the winding-number hierarchy of the rows of m_phi_F that bound the solid (m_phi_F_inner != 0), each oriented out of the label-1 cell it bounds. Built once by init_input_complex_bvh(), only when the complex has cells; null otherwise.
| size_t wmtk::components::topological_offset::TopoOffsetTetMesh::m_marching_root_splits = 0 |
marching_tets() tallies for the construction log: edges placed on the level set / at the midpoint because the trace left the edge, and the trace steps (total, max). Reset at the start of marching_tets().
| int wmtk::components::topological_offset::TopoOffsetTetMesh::m_n_regions = 0 |
One field per connected piece of the input complex, and which one each band vertex is placed on. See TopoOffsetTriMesh::m_region_potentials for the full argument.
m_offset_potential is built over the whole selected complex; where two pieces are close the union field has no level set across the gap. A band grown from one piece is placed on that piece's field alone. Pieces are the connected components of the captured complex under vertex connectivity, numbered once in init_input_complex_bvh(); assign_band_regions() maps the band's cells and vertices to them by a flood fill seeded from complex vertices. connected pieces of the input complex; one field each
| optimization::NewtonCounters wmtk::components::topological_offset::TopoOffsetTetMesh::m_newton_front |
How the solves this class makes itself ended, per smoothing pass, beside the base's m_newton (the background, through TetOptimizerMesh::smooth_after()). Front: every front placement – the 1-D solve along the field normal and the 3-D solve it falls back to. Logged and reset by log_smoothing_pass_accounting().
| optimization::NewtonCounters wmtk::components::topological_offset::TopoOffsetTetMesh::m_newton_repulsion |
Per repulsion pass or round, as m_newton_front and the front veto counters are per loop pass.
|
mutable |
face_is_offset_surface_live() calls handed an invalid Tuple (m_global_tid == size_t(-1)). Defence in depth behind the two walks above: with both of them checking their lookups this should stay 0, and a future caller that forgets gets false instead of a wild read.
|
mutable |
Faces that offset_surface_faces_live_at() asked for and the connectivity did not have.
It walks a vertex's one-ring of tets and then steps across each face to the tet on the other side, so it reads two and three hops out from the seed. (vertex_has_live_offset_face() walked the same way until 2026-09-30; it now pairs the faces within the vertex's own tets and reads nothing the lock does not hold.) The collapse and swap passes guarantee only {v1, v2} u N(v1) u N(v2) – see "Ring lockers -- NOT balls" in TetMesh.h – so at num_threads > 0 a neighbouring thread can be shrinking a vertex fan these walks are reading, and the face lookup misses. A miss is taken as "not a live offset face", which is what TetMesh.h's try_tuple_from_face doc calls the legitimate answer.
Counted because a miss means the m_is_on_offset just written was derived from a stale read and may be wrong. Zero is the expected value. Non-zero says the walks are racing the pass, and the remedy is to widen the collapse pass's lock (collapse_all_edges_impl's exact_ball_lock), which is shared code and not an offsets-side change.
History: before 2026-09-18 neither walk checked for a miss. The asserting tuple_from_face returns a default Tuple under NDEBUG, whose m_global_tid is size_t(-1), and switch_tetrahedron() indexed m_tet_connectivity with it – one element below the vector's base. That was the SIGSEGV on the cube at target_distance_rel 1e-3.
| std::shared_ptr<OffsetPotential3D> wmtk::components::topological_offset::TopoOffsetTetMesh::m_offset_potential |
The smooth offset potential, and with it the definition of the offset itself.
The offset surface is the level set Phi = c. Built from the same extraction as m_input_complex_envelope, so the two describe the same geometry and the same never-rebuilt rule applies. See OffsetPotential for what Phi is. shared_ptr because OffsetEnergy3D holds one per smoothing call.
| std::vector<int8_t> wmtk::components::topological_offset::TopoOffsetTetMesh::m_phi_F_inner |
Per row of m_phi_F: the sign of orient3d(a, b, c, x) for x in the input solid it bounds (+1: the row's normal (b - a) x (c - a) points out of the label-1 region), 0 for an isolated triangle. The exact rule's offset side (ExactBandIntegral) reads it.
| MatrixXd wmtk::components::topological_offset::TopoOffsetTetMesh::m_phi_V |
The input complex as Phi's primitives: the BOUNDARY triangles of the label-1 tet region plus the complex's isolated triangles, every edge of those triangles plus the complex's wires, and its isolated points. Filled by init_input_complex_bvh().
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::m_plastic_active = false |
Set while the loop runs under use_rest_pose (not in the final pass, which is TetWild's regular-tet AMIPS).
|
mutableprivate |
use_rest_pose: operations decided by plastic_tie() – swaps refused outright, collapses passed by the energy rule. Per group, for the [band volume energy] line.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::m_quality_converged = true |
The finishing-pass half of the verdict: max AMIPS < stop_energy once the front is resolved, after the final pass when one ran. True when no pass was needed; false when the pass ended still over. m_quality_max_amips is the value it was judged on.
| std::vector<std::shared_ptr<Envelope> > wmtk::components::topological_offset::TopoOffsetTetMesh::m_region_envelopes |
One envelope per piece, over that piece's primitives alone (euclidean_query_envelope(), as m_input_complex_envelope): what assign_band_regions() reads a seed vertex's piece off (nearest piece), and the euclidean per-piece field's engine. 2D answers the same question through its BVH's feature ids.
| std::vector<std::shared_ptr<OffsetPotential3D> > wmtk::components::topological_offset::TopoOffsetTetMesh::m_region_potentials |
one per piece
| std::shared_ptr<const OffsetPotential3D> wmtk::components::topological_offset::TopoOffsetTetMesh::m_repulsion_potential |
The repulsion passes and rounds (repulsion_smoothing()), set only while they run: the Euclidean field at level 2 x target_distance. Null otherwise, so every other path is untouched.
| std::shared_ptr<OffsetEnergy3D> wmtk::components::topological_offset::TopoOffsetTetMesh::m_repulsion_term |
O(v) = (max(0, 2 delta - d(v)) / front_conv)^2: the Euclidean residual against level 2 delta is (d - 2 delta) / (2 delta), so the weight (2 delta / front_conv)^2 = 4 offset_term_weight() puts it in units of the tolerance, as the front's terms are. One object for the whole repulsion (value() changes nothing in it, so threads share it); set with m_repulsion_potential.
| int wmtk::components::topological_offset::TopoOffsetTetMesh::m_round = 0 |
The turn the run is in, 1-based; 0 before the loop starts. Read only by write_optimization_debug_output(), to tag each frame with the turn it belongs to.
| std::vector<std::array<size_t, 2> > wmtk::components::topological_offset::TopoOffsetTetMesh::m_settle_split_edges |
EXPERIMENTAL_settle_mark "vertex_split": the front edges the last settled turn chose to split (settle_refinement_turn()); front_pocket_split() splits them and clears the list.
| int64_t wmtk::components::topological_offset::TopoOffsetTetMesh::m_sheet_tag = -1 |
Tag id of the input's envelope surface group (the .msh triangle elements), or -1. An open sheet has no tet set whose boundary it is, so it is selectable only through this tag: offset_selection naming it makes the sheet the complex and the band grows on both of its sides. The 2D twin is m_curve_tag.
| int wmtk::components::topological_offset::TopoOffsetTetMesh::m_smooth_group = 0 |
Counts the loop's operation groups (each split, collapse or swap pass with the smoothing passes that follow it); the debug frames show a vertex's VertexExtra::m_front_change only when it was recorded in the latest group.
|
mutable |
The vertex the shared smoother is placing on this thread, for smoothing_ring_energy() (which is handed the ring alone); size_t(-1) outside smooth_nonfront_vertex().
|
mutable |
EXPERIMENTAL_split_energy_gate: the split in flight's parents' sum of tet_energy() (taken in split_before_cells()), and the splits refused for not lowering it, per operation group.
| std::atomic<long> wmtk::components::topological_offset::TopoOffsetTetMesh::m_split_order_waits {0} |
Longest-edge order in the optimization split (see split_edge_before()), counted per turn: splits that waited for a strictly longer edge of an incident tet that was over the split gate, and committed splits whose edge was not the longest edge of every incident tet.
| std::set<std::tuple<long, long, long> > wmtk::components::topological_offset::TopoOffsetTetMesh::m_stuck_prev_cells |
Quantised centroids of the MAX_ENERGY tets at the previous stuck-refine, for the overlap line. Diagnostic only.
| std::atomic<long long> wmtk::components::topological_offset::TopoOffsetTetMesh::m_swap_scoring_checked {0} |
perform_sanity_checks: swaps whose cells the case search or the face gate scored before they existed (SwapRecord::scored_energy), compared in swap_after_cells() against the same cells read on the mesh, and those that read differently (a warning each, the first 8 per turn). Reported and reset once a turn as [swap scoring].
|
mutable |
"exact_swept": the smoother's search model's derivative evaluations, and of them those taken from the ring's band cells (BandVolumeEnergy3D::set_swept()), per pass.
|
mutable |
"exact_swept", the projected step: the vertex smooth_nonfront_vertex() smooths and its position at the stored call of smoothing_ring_energy(), which every candidate's band change is taken from.
| bool wmtk::components::topological_offset::TopoOffsetTetMesh::m_tetwild_pass = false |
Inside tetwild_pass(): every tracked face is held, the offset surface to m_offset_envelope (surface_envelope_for_face(), face_is_held()); a front vertex is smoothed like every other held vertex, pulled to and contained in its envelope (smoothing_energy_envelope()); every cell's energy, where it is still read (tet_energy(): the logs and frames), is its equilateral AMIPS term alone, no band term; the plastic medium is off; and the operations and the smoother follow TetWild's own rules (m_op_rules, set there too).
|
mutable |
Collapses and swaps refused for raising E over the cells they change, per operation group ([band volume energy]).
|
mutable |
The vertex compute_distance_deviation() last found the max at, and a dump of everything that could be stopping it from moving. Diagnostic only.
| std::vector<std::array<double, 8> > wmtk::components::topological_offset::TopoOffsetTetMesh::optimization_metrics |
{max_dist_err, avg_dist_err, max_phi_residual, avg_phi_residual, max_grad, avg_grad, max_grad_at_vertex, max_grad_in_face}. One entry for the whole run, as in 2D.