Wildmeshing Toolkit
Loading...
Searching...
No Matches
Classes | Public Types | Public Member Functions | Static Public Member Functions | Public Attributes | Static Public Attributes | Private Member Functions | Private Attributes | List of all members
wmtk::components::topological_offset::TopoOffsetTetMesh Class Reference

The offset's tet mesh, on the shared 3D optimizer. More...

#include <TopoOffsetTetMesh.h>

Inheritance diagram for wmtk::components::topological_offset::TopoOffsetTetMesh:
wmtk::TetOptimizerMesh wmtk::TetMesh wmtk::RationalPositions wmtk::components::manifold_extraction::ManExtractTetMesh

Classes

struct  DistanceSplit
 The offset surface's residual. Every band vertex and every face sample counts toward the max and the average, pinned vertices included. The reachable/pinned split (see band_vertex_is_reachable()) is attribution: a max driven by a pinned vertex calls for a different construction, not more optimization. More...
 
struct  EdgeSplitCache
 
struct  FaceSamples
 
struct  FaceSnapshot
 A face's shared surface tags together with the offset's own label. More...
 
struct  FaceSplitCache
 
struct  GradientSplit
 The band's placement gradient: |grad (Phi(x) - c)^2| over the offset surface – at every band vertex AND at interior samples of every band face. More...
 
struct  MoveStats
 How far each class of vertex actually gets during smoothing. More...
 
struct  OffsetCollapseRefusals
 
struct  OptSplitCache
 
struct  SmoothTrace
 Why smoothing refused a vertex, one counter per site where it can say no. More...
 
struct  TetSplitCache
 
struct  WallMoveStats
 Which of two mechanisms lets a wall vertex end up off its wall. More...
 

Public Types

enum class  OptPhase { A , B }
 Which half of the alternating optimization is running. More...
 
enum class  PhaseBSub { Offset , Background }
 Phase B's two sub-sweeps, run in this order within every smoothing pass. More...
 
enum class  EdgeSplitMode { Midpoint = 0 , Optimization = 5 }
 
enum class  VClass {
  Bbox = 0 , Offset = 1 , Input = 2 , Interior = 3 ,
  Count = 4
}
 Which of four DISJOINT classes a vertex is accounted to when measuring movement. 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 SurfaceTopoSignature = wmtk::utils::SurfaceTopoSignature
 
- Public Types inherited from wmtk::TetMesh
template<typename T >
using vector = std::vector< T >
 
using VertexMutex = wmtk::threading::VertexMutex
 

Public Member Functions

void rebuild_offset_envelope ()
 
void check_no_vertex_on_both_surfaces (const char *when) const
 
void optimize_offset_alternating ()
 
size_t phase_b_smooth ()
 
bool smooth_interior_vertex_phase_b (const Tuple &t)
 Phase B's placement of a background vertex: Newton on the one-ring AMIPS energy, to this vertex's own minimum.
 
double phase_b_band_gradient_linf ()
 L-inf over offset vertices of the gradient of each vertex's own placement objective – the energy Phase B's Newton solves minimize. Exactly 0 at the Gauss-Seidel fixed point whatever the residual, so it separates "placement finished" from "placement blocked": a refused move contributes zero displacement but full gradient. Vertices with a float-inverted or unrounded incident tet are skipped, as the smoother skips them.
 
size_t update_band_sizing_from_tolerance ()
 Per-vertex band sizing update, run after Phase B. Returns the number changed.
 
FaceSnapshot face_snapshot (const size_t fid) const
 
void restore_face (const size_t fid, const FaceSnapshot &s)
 
void set_vertex_position (const size_t vid, const Vector3d &p)
 Place a vertex, keeping its exact and rounded coordinates in step.
 
bool cell_in_region (const size_t tid) const
 Whether tet tid belongs to the closed offset region, read from its TAGS.
 
bool face_is_offset (const size_t fid) const
 Whether face fid is on the offset boundary (as opposed to the input complex).
 
bool face_is_region (const size_t fid) const
 Whether face fid carries input-complex geometry.
 
 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
 
void log_quality_spike (size_t tid, double before, double after, const char *kind) const
 Report one tet crossing into absurd quality: its geometry, and why it is degenerate.
 
bool vertex_is_on_region (const size_t vid) const
 Is this vertex on a region boundary – a tag boundary, or the domain wall.
 
uint64_t tag_bits (const CellTag &tags) const
 
uint64_t vertex_boundary_mask (const size_t vid) const
 The tag boundaries this vertex lies on – the raw mask, gated on the vertex still being region geometry at all.
 
uint64_t face_mask (const std::array< size_t, 3 > &vids) const
 A face lies on a boundary only if all of it does: the AND of its corners' masks.
 
std::shared_ptr< SampleEnvelopeenvelope_for_mask (uint64_t mask) const
 The envelope a simplex with this boundary mask is contained in, or null.
 
std::shared_ptr< SampleEnvelopesurface_envelope_for_face (const std::array< size_t, 3 > &vids) const override
 Envelope the tracked-surface triangle vids must stay inside.
 
bool allow_surface_swap () const override
 
bool check_surface_topology () const override
 
std::shared_ptr< SampleEnvelopesmoothing_energy_envelope (const size_t vid) const override
 The offset surface is the one tracked surface with NO envelope, in either role.
 
std::shared_ptr< SampleEnvelopesmoothing_containment_envelope (const size_t vid) const override
 ... and it is not CONTAINED by one either.
 
void write_optimization_debug_output (const std::string &path) override
 
size_t refine_sizing_around_worst (double) override
 Phase A's stall-driven sizing refinement – TetWild's, on element quality.
 
std::tuple< double, double > optimization_quality_stats () override
 (max, avg) Phi residual over the reachable offset surface, in units of its tolerance; the engine's stop metric is therefore 1.0.
 
double optimization_stop_metric () const override
 
double offset_residual_tolerance () const
 
double offset_gradient_tolerance () const
 The convergence tolerance: the bound on |grad (Phi - c)^2| at a band vertex.
 
void check_offset_within_support (const char *when) const
 Stop the run if any reachable offset-surface vertex has left the potential's support.
 
double band_vertex_residual (const size_t vid) const
 
bool band_vertex_is_reachable (const size_t vid) const
 
std::vector< bool > band_vertex_mask () const
 
double max_band_vertex_distance () const
 
void write_phi_grid (const std::string &path, int n) const
 
template<typename Visit >
void for_each_offset_face_sample (const Tuple &f, Visit &&visit) const
 The interior lattice a face is sampled on, handed to visit one point at a time.
 
FaceSamples offset_face_samples (const Tuple &f) const
 The Phi residual at offset_residual_samples interior points of offset face f.
 
double face_criterion_rel (const Tuple &f) const
 
double cell_quality_rel (const size_t tid) const
 
double amips_rel_at_face (const Tuple &f) const
 ... and the worst of the (up to two) cells a face separates.
 
DistanceSplit residual_split () const
 
GradientSplit gradient_split (bool include_face_samples=true) const
 
void report_outside_support (const char *when, const DistanceSplit &s) const
 
bool swap_before_interior (const std::vector< size_t > &tids) override
 Which tag the tets a swap creates should carry.
 
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 collapse_edge_before (const Tuple &t) override
 Collapse policy that is the offset's own.
 
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 split_before_cells (const Tuple &edge, const std::vector< Tuple > &parents) override
 Split policy that is the offset's own.
 
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
 
void init_from_image (const MatrixXd &V, const MatrixXi &T, const MatrixSi &T_tags, const MatrixXd &V_env, const MatrixXi F_env, const std::vector< std::string > &tag_names)
 initialize TetMesh from vertex, tet, and tag data
 
void init_surfaces_and_boundaries ()
 
bool is_edge_on_region (const Tuple &loc)
 
bool is_edge_on_offset (const Tuple &loc)
 
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
 
void mark_input_complex_vertices ()
 
bool empty_input_complex ()
 check if the input complex is empty. Only valid after calling init_from_image(...). Checks if any vertices (therefore any simplices) are labelled 1, if not returns true
 
void init_input_complex_bvh ()
 Build the input complex's BVH and keep the extraction the potential needs.
 
void init_offset_potential ()
 Build the smooth offset potential from the extraction init_input_complex_bvh() kept.
 
size_t flood_fill ()
 label connected simplicial complex components (simplices labelled 1 or 2)
 
std::vector< std::array< size_t, 3 > > get_faces_by_condition (std::function< bool(const FaceAttributes &)> cond) const
 
bool split_edge_before (const Tuple &t) override
 
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 marching_split_edge_before (const Tuple &t)
 
bool marching_split_edge_after (const Tuple &t)
 
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
 
bool smooth_before (const Tuple &t) override
 
bool smooth_after (const Tuple &t) override
 Every vertex goes through the shared smoother. Nothing is dispatched anywhere else.
 
bool smooth_offset_vertex_backtracking (const Tuple &t)
 Phase B's placement of an offset-surface vertex: the offset energy alone, then backtrack into the one-ring if the solved point inverts something.
 
void execute_offset (const std::filesystem::path &output_file)
 main function from which all others are called
 
void optimize_offset (const std::filesystem::path &output_file)
 optimize the offset
 
bool is_offset_face (const Tuple &f) const
 true if face f has exactly one incident tet labelled 2 (offset), i.e. it lies on the boundary between the offset region and the rest of the mesh
 
bool is_offset_face (const size_t fid) const
 
std::vector< Tupleget_offset_surface_faces_for_vertex (const Tuple &t) const
 offset-surface faces (see is_offset_face()) incident to vertex t
 
void log_smooth_trace () const
 
VClass vertex_class (const size_t vid) const
 
double wall_offplane_deviation (const size_t vid) const
 
void log_vertex_movement (const char *when) const
 Per-class smoothing movement, plus the direct bounding-box invariant check.
 
void init_offset_sizing_field ()
 Seed the sizing field from the offset surface's current edge lengths, once, before the first pass. Paper Sec. 5.3.3 Step 1: "initialized with the current length of each edge.".
 
std::pair< double, double > compute_distance_deviation () const
 How far the offset surface is from where it should be: {max, avg} over its vertices.
 
bool face_is_offset_surface_live (const Tuple &f) const
 The band's outer surface, recomputed live rather than read from the cached class.
 
void diag_offset_bands (const char *tag) const
 
bool cell_is_offset_band (const size_t tid) const
 Whether tet tid is part of the offset BAND (as opposed to the input complex it wraps).
 
bool cell_is_input_complex (const size_t tid) const
 Whether tet tid is part of the INPUT complex the band wraps.
 
void warn_if_offset_reaches_domain_boundary () const
 Warn if conservative growth ran into the bounding box.
 
void log_worst_dist_vertex () const
 
void log_worst_tet (const char *when) const
 Diagnostic: dump the single worst-quality tet and everything that could be pinning it, so a Phase A that will not converge says which element and why rather than only a max.
 
void marching_tets ()
 execute simplistic marching tets. All edges with one vertex labelled 0 and the other 1/2 are split, at the midpoint.
 
bool is_simplicially_embedded () const
 check if the input complex (simplices labelled 1) are simplicially embedded w.r.t. the entire mesh
 
bool tet_is_simp_emb (const Tuple &t) const
 check if a tet satisfies simpicial embedding criteria w.r.t. input complex (simplices labelled 1)
 
void simplicial_embedding ()
 make mesh a simplicial embedding of the input complex (simplices labelled 1)
 
void set_offset_tet_tags ()
 update 'tags' data for tets in the offset region (tets labelled 2) based on the given offset tag values in m_offset_params.offset_tag_value
 
bool offset_is_manifold ()
 verify that the closed offset region (simplices labelled 1 or 2) form a manifold region. This should be true for any offset. This function is for verification.
 
void write_input_complex (const std::string &path)
 
void write_vtu (const std::string &path)
 
void write_msh_groups (const std::string &file)
 
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.
 
void compute_vertex_partition ()
 assign each vertex a partition id (by spatial Morton order) for the parallel ExecutePass policies (kPartition/kColor) used by smooth_all_vertices()/ collapse_all_edges(). A no-op if NUM_THREADS == 0.
 
size_t get_partition_id (const Tuple &loc) const
 
std::vector< Tupleget_face_adjacent_tets (const Tuple &t) const
 get tets (as Tuples) that are face-adjacent to the given tet (as Tuple)
 
std::vector< size_t > connected_components_helper (const size_t &v_id)
 get all one-ring vertices through input simplices (labelled 1)
 
void reset_connected_components ()
 reset connected component assignments.
 
- Public Member Functions inherited from wmtk::TetOptimizerMesh
bool is_force_split_edge (const size_t v1, const size_t v2) const
 
 TetOptimizerMesh (OptimizerParameters &params, std::shared_ptr< SampleEnvelope > env)
 
virtual double quality_rel (const size_t tid) const
 A cell's quality relative to the quality it is required to reach; <= 1 means it meets it.
 
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 ()
 
virtual void update_attributes ()
 Update the attributes of the mesh after an iteration of operations.
 
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
 
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)
 
bool collapse_edge_after (const Tuple &t) override
 User specified modifications and desideratas for after an edge collapse.
 
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_before (const Tuple &t) override
 User specified preparations and desideratas for an 2-3 face swap befroe changing the geometry.
 
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
 
double swap_edge_44_energy (const std::vector< std::array< size_t, 4 > > &tets, const int op_case) override
 User specified energy to decide which of the 4 possible orientations should be chosen.
 
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.
 
virtual std::shared_ptr< polysolve::nonlinear::Problem > smoothing_extra_energy (const size_t) const
 An extra term the application adds to this vertex's smoothing objective, or null.
 
void smooth_all_vertices (const size_t n_iters=1)
 
- 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 > > &center_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.
 
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::vector< Tupleget_edges () const
 
std::vector< Tupleget_faces () const
 
std::vector< Tupleget_vertices () const
 
std::vector< Tupleget_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)
 
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< Tupleswitch_tetrahedron (const Tuple &t) const
 wrapper function from Tuple::switch_tetrahedron
 
std::vector< Tupleget_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< Tupleget_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< Tupleget_incident_tets_for_edge (const Tuple &t) const
 Get the incident tets for edge.
 
std::vector< Tupleget_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< Tupleget_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
 
Tuple tuple_from_cell (size_t cid) const
 
- 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 spike_amips (const double q)
 
static const char * vclass_name (VClass c)
 
static double mean_ratio_metric (const Vector3d &p0, const Vector3d &p1, const Vector3d &p2)
 unsigned mean ratio metric of a triangle: 2*sqrt(3)*area / (sum of squared edge lengths). 1 for equilateral, -> 0 as the triangle degenerates.
 

Public Attributes

OptPhase m_phase = OptPhase::A
 Which phase is running. Read by every hook that differs between them; see OptPhase.
 
PhaseBSub m_phase_b_sub = PhaseBSub::Offset
 
std::shared_ptr< SampleEnvelopem_offset_envelope
 The tube the offset surface may not leave during Phase A. Null in Phase B.
 
int m_vtu_counter = 0
 
std::array< size_t, 4 > m_init_counts = {{0, 0, 0, 0}}
 
size_t m_tags_count
 
SimplicialComplexBVH m_input_complex_bvh
 
std::shared_ptr< OffsetPotential3Dm_offset_potential
 The smooth offset potential Phi of the input complex, as loaded.
 
std::shared_ptr< SampleEnvelopem_input_complex_envelope
 The exact-kind envelope of the input complex, built only for offset_field "euclidean". Null otherwise.
 
std::map< int64_t, std::shared_ptr< SampleEnvelope > > m_tag_envelopes
 One containment envelope per input tag, ambient included. Both phases.
 
std::map< int64_t, int > m_tag_bit
 
std::map< uint64_t, std::shared_ptr< SampleEnvelope > > m_isect_cache
 
std::mutex m_isect_mutex
 
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
 
std::vector< Vector3d > m_V_envelope
 
std::vector< Vector3i > m_F_envelope
 
double m_envelope_eps = -1
 
Parametersm_offset_params
 
VertexExtraCol m_vertex_extra
 
FaceExtraCol m_face_extra
 
EdgeAttCol m_edge_attribute
 
TetAttCol m_tet_attribute
 
std::atomic< int > cnt_split = 0
 
std::atomic< int > cnt_collapse = 0
 
bool m_spike_track = true
 Diagnostic: whether set_cell_quality reports tets crossing into absurd quality.
 
double m_spike_threshold = 1e5
 AMIPS above which a tet counts as absurd rather than merely bad.
 
double m_spike_ordinary = 100.
 AMIPS below which a tet counts as ordinary, so ordinary -> absurd is one operation's doing.
 
double m_spike_record = 0.
 Largest AMIPS seen so far; a new maximum is logged, which bounds the output.
 
std::atomic< int > m_spike_dumps {0}
 
std::atomic< int > m_spike_genesis_dumps {0}
 
std::atomic< int > m_spike_count {0}
 
std::atomic< int > m_spike_genesis {0}
 
int m_spike_dump_budget = 24
 
OffsetCollapseRefusals m_offset_collapse_refusals
 
MatrixXd m_phi_V
 
MatrixXi m_phi_E
 
MatrixXi m_phi_F
 
std::vector< int > m_phi_P
 
SmoothTrace m_smooth_trace
 
std::atomic< int > m_phase_b_constrained {0}
 
wmtk::threading::enumerable_thread_specific< std::unique_ptr< polysolve::nonlinear::Solver > > m_phase_b_solver
 
std::array< MoveStats, size_t(VClass::Count)> m_move_stats
 
WallMoveStats m_wall_moves
 
size_t m_worst_dist_vid = static_cast<size_t>(-1)
 
std::vector< std::array< double, 8 > > optimization_metrics
 
std::vector< std::array< int, 3 > > churn_counts
 
std::vector< std::array< int, 3 > > op_counts
 
bool m_converged = false
 Whether the optimization met both convergence criteria before the iteration cap.
 
std::atomic< int > iter_cnt_split_born {0}
 
std::atomic< int > iter_cnt_recollapsed {0}
 
std::atomic< int > iter_cnt_recollapsed_same_pass {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 face over tolerance.
 
std::atomic< int > iter_cnt_swap_offset_reject {0}
 
std::atomic< int > iter_cnt_split_offset_before {0}
 Splits of an OFFSET-surface edge: reached split_edge_after, accepted, refused there.
 
std::atomic< int > iter_cnt_split_offset_endpoints {0}
 
std::atomic< int > iter_cnt_split_offset_base_reject {0}
 
std::atomic< int > iter_cnt_split_offset_tried {0}
 
std::atomic< int > iter_cnt_split_offset {0}
 
std::atomic< int > iter_cnt_split_offset_reject {0}
 
- Public Attributes inherited from wmtk::TetOptimizerMesh
VertAttCol m_vertex_attribute
 
FaceAttCol m_face_attribute
 
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.
 
OptimizerParametersm_params
 
std::shared_ptr< SampleEnvelopem_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.
 
bool m_collapse_limit_length = true
 
std::set< simplex::Edgem_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
 
int m_iterations_used = 0
 Shared TetWild/SimWild outer optimization schedule.
 
int m_debug_print_counter = 0
 
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
 
- Public Attributes inherited from wmtk::TetMesh
AbstractAttributeContainerp_vertex_attrs = nullptr
 
AbstractAttributeContainerp_edge_attrs = nullptr
 
AbstractAttributeContainerp_face_attrs = nullptr
 
AbstractAttributeContainerp_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 OFFSET_SURFACE_CLASS = 1
 SurfaceTagAttributes::m_surface_class for the offset boundary.
 
- 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 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 any_tag_present (const CellTag &tag1, const CellTag &tag2)
 determine if any tag from tag1 is also present in tag2.
 
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< OptSplitCachem_opt_split_cache
 
wmtk::threading::enumerable_thread_specific< double > m_collapse_offset_rel_before
 
wmtk::threading::enumerable_thread_specific< double > m_swap_offset_rel_before
 
wmtk::threading::enumerable_thread_specific< CellTag > m_swap_tag
 The tag swap_after_cells writes onto the tets the swap created, chosen in before.
 
wmtk::threading::enumerable_thread_specific< int > m_swap_label
 
wmtk::threading::enumerable_thread_specific< EdgeSplitCacheedge_split_cache
 
wmtk::threading::enumerable_thread_specific< FaceSplitCacheface_split_cache
 
wmtk::threading::enumerable_thread_specific< TetSplitCachetet_split_cache
 

Additional Inherited Members

- Protected Member Functions inherited from wmtk::TetOptimizerMesh
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 bool optimization_bare_coarsen_passes () const
 Whether the loop opens with an UNLIMITED-LENGTH collapse pass.
 
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 bool collapse_quality_allowed (size_t v1, double quality, double ring_max) const
 
- Protected Member Functions inherited from wmtk::TetMesh
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)
 
- Protected Attributes inherited from wmtk::TetOptimizerMesh
uint32_t m_op_epoch = 0
 
wmtk::threading::enumerable_thread_specific< SwapInfoCacheswap_cache
 
wmtk::threading::enumerable_thread_specific< SplitInfoCachesplit_cache
 
wmtk::threading::enumerable_thread_specific< CollapseInfoCachecollapse_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.
 

Detailed Description

The offset's tet mesh, on the shared 3D optimizer.

Construction – marching tets, simplicial embedding, growing the offset region – uses TetMesh's splits directly; the optimization after it is wmtk::TetOptimizerMesh's, with the offset supplying only policy through the hooks. Two surfaces are tracked: the input complex (class 0, envelope-held) and the offset boundary (OFFSET_SURFACE_CLASS, free to move, defined by tet labels rather than input geometry). The base's m_is_on_surface is their union, and that union is what an operation must not tear.

Member Enumeration Documentation

◆ OptPhase

Which half of the alternating optimization is running.

The two criteria are optimized in turn, never jointly: driven from one loop they compete, split refining the band and collapse removing on quality grounds what it made.

Phase A is TetWild and nothing else – same operations, gates, sizing field and stall-driven refinement, with no offset energy term, acceptance criterion or stop metric. Its one addition is m_offset_envelope, holding the offset surface within one Phi tolerance of wherever Phase B last placed it, so "do not degrade the offset" is a geometric constraint every operation already honours rather than a per-operation criterion.

Phase B moves the offset surface and nothing else: smoothing against the offset energy to a fixed point, no envelope on the offset (it is what has to travel), no topological operations. Both phases write the shared sizing field – A on element quality, B on the Phi residual of faces smoothing could not place.

◆ PhaseBSub

Phase B's two sub-sweeps, run in this order within every smoothing pass.

Offset: place every offset-surface vertex on the level set, by the 1-D minimization of (Phi - c)^2 along grad Phi, backtracked into the one-ring if the minimum lies outside it. Background: relax the interior by AMIPS, under the surface those placements just defined.

The sweeps must stay separate and in this order, so a pass means "place, then relieve what the placement cost". Interleaving would make each vertex's one-ring a moving target for its neighbours, so the backtracking a placement hits would depend on visit order rather than on geometry. smooth_before() reads this and refuses the other class outright, so the two sweeps share the executor, counters and accept gates and differ only in who they admit.

◆ VClass

Which of four DISJOINT classes a vertex is accounted to when measuring movement.

Bbox first, deliberately: a vertex may be on the bounding box and on a tracked surface at once, and the bbox freeze in smooth_before() overrides everything else, so for "does it actually move" the bbox decides. Offset before input is academic – a vertex on both is a construction defect check_no_vertex_on_both_surfaces() throws on.

Member Function Documentation

◆ allow_surface_swap()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::allow_surface_swap ( ) const
inlineoverridevirtual

Surface edges may be flipped, as a topology-preserving diagonal flip. Both tracked surfaces need it: the offset boundary is re-triangulated constantly, and refusing its diagonals leaves badly-shaped triangles the sizing field then chases.

Implements wmtk::TetOptimizerMesh.

◆ any_tag_present()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::any_tag_present ( const CellTag &  tag1,
const CellTag &  tag2 
)
inlineprivate

determine if any tag from tag1 is also present in tag2.

Note
if tag2 is empty (ambient), return true if tag1 is empty, otherwise false (tag2 is ambient, so only 'element' is ambient)

◆ band_vertex_is_reachable()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::band_vertex_is_reachable ( const size_t  vid) const
inline

Whether vid is an offset-surface vertex the optimizer could still place on the level set. Only the domain boundary disqualifies one: conservative growth ran out of room there, so the vertex is on the box because the offset could not extend past it, and no placement changes that. m_is_on_region must not disqualify it – the flag is over-broad (splits propagate it, collapses OR it onto survivors), so a vertex carrying it may sit exactly where the offset wants it, and check_no_vertex_on_both_surfaces() already throws on the genuinely contradictory case.

◆ band_vertex_mask()

std::vector< bool > wmtk::components::topological_offset::TopoOffsetTetMesh::band_vertex_mask ( ) const

Which vertices lie on the offset surface. Shared by every measurement so they all agree on what "the band" is.

◆ band_vertex_residual()

double wmtk::components::topological_offset::TopoOffsetTetMesh::band_vertex_residual ( const size_t  vid) const

How far vid is from the level set Phi = c, as a length. This is what the loop converges on and what the sizing field refines by.

◆ cell_in_region()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::cell_in_region ( const size_t  tid) const
inline

Whether tet tid belongs to the closed offset region, read from its TAGS.

The 3D counterpart of TopoOffsetTriMesh::face_in_region. The region is the offset band plus the input complex it wraps, both named by tags, and tags are what the shared operations propagate. Must not be read off the construction label instead: that is derived state, stale the moment a split or swap creates a cell it was never written for.

◆ cell_quality()

double wmtk::components::topological_offset::TopoOffsetTetMesh::cell_quality ( const size_t  tid) const
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.

◆ cell_quality_rel()

double wmtk::components::topological_offset::TopoOffsetTetMesh::cell_quality_rel ( const size_t  tid) const

AMIPS of a cell over stop_energy – the 3D twin of TriOptimizerMesh::quality_rel(), so both dimensions express "how bad is this element" on the same 1.0 scale.

◆ check_no_vertex_on_both_surfaces()

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 and after every phase; see the definition for why a collapse can create it out of two individually fine vertices.

◆ check_offset_within_support()

void wmtk::components::topological_offset::TopoOffsetTetMesh::check_offset_within_support ( const char *  when) const

Stop the run if any reachable offset-surface vertex has left the potential's support.

Beyond dhat, Phi is identically zero with a zero gradient: the vertex is given no direction back, its residual saturates instead of growing, and the sizing field refines around a vertex nothing can move. There is no recovery from that state and no honest report of it, so it is a hard error, not a warning; the answer to it firing is a larger offset_dhat_factor. Called once per optimization iteration and once on the band as built.

◆ check_surface_topology()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::check_surface_topology ( ) const
inlineoverridevirtual

◆ collapse_after_connectivity()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::collapse_after_connectivity ( size_t  v1,
size_t  v2,
const std::vector< std::array< size_t, 2 > > &  boundary_edges 
)
overridevirtual

Reimplemented from wmtk::TetOptimizerMesh.

◆ collapse_after_vertex()

void wmtk::components::topological_offset::TopoOffsetTetMesh::collapse_after_vertex ( size_t  v1,
size_t  v2 
)
overridevirtual

Reimplemented from wmtk::TetOptimizerMesh.

◆ collapse_before_vertex()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::collapse_before_vertex ( size_t  ,
size_t  ,
double   
)
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.

◆ collapse_edge_before()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::collapse_edge_before ( const Tuple t)
overridevirtual

Collapse policy that is the offset's own.

collapse_before_vertex refuses to move a vertex off the surface it belongs to (the base knows only the union of the two), to lower the order of a boundary vertex, or to collapse across a feature of the input complex; it also records how badly aligned the offset surface already was, so its after counterpart can tell a regression from a pre-existing defect.

Reimplemented from wmtk::TetOptimizerMesh.

◆ collapse_is_order_2_edge()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::collapse_is_order_2_edge ( const std::array< size_t, 2 > &  e)
inlineoverridevirtual

Reimplemented from wmtk::TetOptimizerMesh.

◆ compute_distance_deviation()

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 its vertices.

The absolute error |dist(v, input complex) - target_distance|, over the band's outer surface only – a band cell meeting a plain-background cell, not the input complex it wraps, whose interface sits at distance 0 by construction.

A face with no opposite tet is on the domain boundary and must be counted, not skipped: those vertices are pinned on the box and can never be fixed, so dropping them under-reports a bbox-clipped offset by a large factor.

◆ envelope_for_mask()

std::shared_ptr< SampleEnvelope > wmtk::components::topological_offset::TopoOffsetTetMesh::envelope_for_mask ( uint64_t  mask) const

The envelope a simplex with this boundary mask is contained in, or null.

Zero bits: no boundary, no container. One bit: that tag's own envelope. Several bits: a memoized IntersectionEnvelope over the members – inside means inside every tube, which pins junction geometry to the junction. The multi-bit composite is containment-only: it implements just the virtual is_outside queries, so it must never be returned from smoothing_energy_envelope(), whose caller uses the non-virtual nearest_point.

◆ face_criterion_rel()

double wmtk::components::topological_offset::TopoOffsetTetMesh::face_criterion_rel ( const Tuple f) const

The per-face score the collapse and swap acceptance gates gate on (Collapse.cpp, Swap.cpp); >= 1 means the face fails the criterion. Also the per-face form of optimization_quality_stats(). It ranks nothing for refinement – that is update_band_sizing_from_tolerance(), which measures the criterion directly.

◆ face_is_offset_surface_live()

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, so a face a split just created carries whatever cached class its recycled slot happened to hold. Returns true for a band face on the domain boundary; see compute_distance_deviation() for why.

◆ face_is_on_surface()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::face_is_on_surface ( const size_t  fid) const
overridevirtual

Is a face part of the substructure.

Parameters
fidFace ID

Reimplemented from wmtk::TetOptimizerMesh.

◆ for_each_offset_face_sample()

template<typename Visit >
void wmtk::components::topological_offset::TopoOffsetTetMesh::for_each_offset_face_sample ( const Tuple f,
Visit &&  visit 
) const
inline

The interior lattice a face is sampled on, handed to visit one point at a time.

One definition of "inside this face", because two things are measured across a face: the Phi residual (the diagnostic) and the placement gradient (the criterion). They must share a lattice, or a run that fails the criterion in a face is not explicable by the residual.

Interior lattice points of the barycentric subdivision at denominator n = k + 2: every (i, j, l) with i + j + l = n and each >= 1, so nothing lands on an edge or a vertex, which are measured separately and would otherwise be double-counted. n = k + 2 rather than 2D's k + 1 because a triangle has no interior lattice point at denominator 2; k = 1 is then the centroid, where a triangle inscribed in the offset is furthest from it.

◆ get_order_of_vertex()

size_t wmtk::components::topological_offset::TopoOffsetTetMesh::get_order_of_vertex ( const size_t  vid) const
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.

Parameters
vidVertex ID

Reimplemented from wmtk::TetOptimizerMesh.

◆ gradient_split()

TopoOffsetTetMesh::GradientSplit wmtk::components::topological_offset::TopoOffsetTetMesh::gradient_split ( bool  include_face_samples = true) const
Parameters
include_face_samplesfalse measures vertices only – what Phase B's stop test wants, since a face sample is not a variable its sweeps can move. The convergence test always passes true.

◆ init_from_image()

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 
)

initialize TetMesh from vertex, tet, and tag data

Parameters
V#V by 3 vertex matrix
T#T by 4 tet matrix
T_tags#T by #tags tag matrix
V_envV_env by 3 EnvelopeSurface vertices
F_envF_env by 3 EnvelopeSurface faces

◆ init_input_complex_bvh()

void wmtk::components::topological_offset::TopoOffsetTetMesh::init_input_complex_bvh ( )

Build the input complex's BVH and keep the extraction the potential needs.

Must be called after init_from_image(...). One extraction feeds both, so the exact distance field and the smooth potential can never describe different geometry.

◆ init_offset_potential()

void wmtk::components::topological_offset::TopoOffsetTetMesh::init_offset_potential ( )

Build the smooth offset potential from the extraction init_input_complex_bvh() kept.

Separate from the BVH init because it needs target_distance and offset_dhat_factor, which a caller that only wants the distance field has no reason to have set.

◆ init_offset_sizing_field()

void wmtk::components::topological_offset::TopoOffsetTetMesh::init_offset_sizing_field ( )

Seed the sizing field from the offset surface's current edge lengths, once, before the first pass. Paper Sec. 5.3.3 Step 1: "initialized with the current length of each edge.".

Required: the field otherwise starts at the base target, far coarser than the offset surface, so every offset edge is a collapse candidate on pass one and the surface is decimated before any metric is computed.

◆ invariants()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::invariants ( const std::vector< Tuple > &  tets)
overridevirtual

TODO: Check this individually in each operation

Reimplemented from wmtk::TetOptimizerMesh.

◆ is_edge_on_region()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::is_edge_on_region ( const Tuple loc)

Whether edge loc lies on the INPUT complex. The base's is_edge_on_region() asks about the union of the two tracked surfaces, and is_edge_on_bbox() is the base's.

◆ is_open_boundary_edge()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::is_open_boundary_edge ( const Tuple e)
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.

◆ log_quality_spike()

void wmtk::components::topological_offset::TopoOffsetTetMesh::log_quality_spike ( size_t  tid,
double  before,
double  after,
const char *  kind 
) const

Report one tet crossing into absurd quality: its geometry, and why it is degenerate.

Prints the four vertices with the two things that separate the plausible causes – their rounding state (an unrounded vertex is exact-rational, and its float image can make a valid tet look degenerate, which is why is_inverted_f and is_inverted are separate predicates) and the float volume against the edge lengths, a genuine sliver against a coordinate blow-up.

◆ log_worst_tet()

void wmtk::components::topological_offset::TopoOffsetTetMesh::log_worst_tet ( const char *  when) const

Diagnostic: dump the single worst-quality tet and everything that could be pinning it, so a Phase A that will not converge says which element and why rather than only a max.

Reports, for the max-cbrt(cell_quality) tet: its volume and inversion state, its region label, and per vertex the flags that decide whether any operation may touch it – on the input complex, on the offset surface, on the bounding box, rounded, order and sizing scalar. Then each of the six edges against the two length gates, so it is visible whether split or collapse would even consider them: split needs len^2 >= splitting_l2 * sbar^2, and the coarsening pass stops at len^2 <= collapsing_l2 unless coarsen_unbounded. The point is to separate "no operation is attempted here" from "operations are attempted and refused", which the aggregate counters cannot distinguish.

◆ marching_split_edge_after()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::marching_split_edge_after ( const Tuple t)

check inversion & rounding

◆ marching_tets()

void wmtk::components::topological_offset::TopoOffsetTetMesh::marching_tets ( )

execute simplistic marching tets. All edges with one vertex labelled 0 and the other 1/2 are split, at the midpoint.

Do not re-add a distance-field placement that root-finds each band-boundary edge onto d(x) = delta at insertion time; measured worse – see git history of this file. The paper places inserted vertices at the midpoint (Sec. 5.2) and leaves the distance entirely to the Step-3 optimization, which has error feedback and the shared accept checks.

◆ mark_input_complex_vertices()

void wmtk::components::topological_offset::TopoOffsetTetMesh::mark_input_complex_vertices ( )

Set m_is_on_region from the input-complex labels. Called at the end of label_input_complex(), which is the earliest point at which the complex is known.

◆ max_band_vertex_distance()

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().

◆ offset_face_samples()

TopoOffsetTetMesh::FaceSamples wmtk::components::topological_offset::TopoOffsetTetMesh::offset_face_samples ( const Tuple f) const

The Phi residual at offset_residual_samples interior points of offset face f.

The criterion cannot be a vertex criterion: a surface can have every vertex exactly on the level set while its triangles cut across it, which reads as converged and is not the offset – the same gap the paper's normal-deviation criterion (Sec. 5.3.3) covered. Sampling the face interior is what the potential makes possible: Phi is defined everywhere, so the offset can be measured anywhere on the surface rather than only where the mesh happens to have put a vertex. The same samples feed face_criterion_rel(), so the sizing field refines a surface too coarse to represent the offset instead of letting it decimate.

Returns nothing for a face with an unreachable corner: a triangle running onto the input complex is legitimately closer than target_distance across part of itself.

◆ offset_gradient_tolerance()

double wmtk::components::topological_offset::TopoOffsetTetMesh::offset_gradient_tolerance ( ) const
inline

The convergence tolerance: the bound on |grad (Phi - c)^2| at a band vertex.

A fraction of target_distance, the right unit: grad E = 2 (Phi - c) grad Phi, and grad Phi is dimensionless for a field whose value is a length, so grad E is a length. Stated on the gradient rather than the residual because it needs no conversion from field value to length: it is the stationarity condition of the objective Phase B minimises, so it is the same test for every potential, which lets a concave input be judged by the same number as a convex one. Where |grad Phi| departs from 1 – a medial axis, a sharp feature, competing primitives – the two bounds are different tests and neither implies the other.

◆ offset_is_manifold()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::offset_is_manifold ( )

verify that the closed offset region (simplices labelled 1 or 2) form a manifold region. This should be true for any offset. This function is for verification.

Note
We first collect the tets labelled 1 or 2, then extract the boundary of this region and check if it is manifold.

◆ offset_residual_tolerance()

double wmtk::components::topological_offset::TopoOffsetTetMesh::offset_residual_tolerance ( ) const
inline

The residual scale, derived from the criterion rather than configured beside it: grad E = 2 (Phi - c) grad Phi, so on a unit-slope field the bound |grad E| <= g is |Phi - c| <= g/2. It must stay derived – it also sizes the Phase A offset envelope (offset_envelope_rel x this) and the min_edge_length floor, and as its own knob it would not follow front_conv_rel, holding the offset surface far tighter than the run is judged by.

◆ optimization_quality_stats()

std::tuple< double, double > wmtk::components::topological_offset::TopoOffsetTetMesh::optimization_quality_stats ( )
overridevirtual

(max, avg) Phi residual over the reachable offset surface, in units of its tolerance; the engine's stop metric is therefore 1.0.

Reimplemented from wmtk::TetOptimizerMesh.

◆ optimization_stop_metric()

double wmtk::components::topological_offset::TopoOffsetTetMesh::optimization_stop_metric ( ) const
inlineoverridevirtual

Phase A is TetWild, so it stops where TetWild stops: absolute AMIPS against stop_energy, the base's default. Phase B's metric is the Phi residual normalized by its own tolerance, so 1.0 means converged. Both must agree with optimization_quality_stats() above: a metric in one unit tested against a bar in another stalls the phase.

Reimplemented from wmtk::TetOptimizerMesh.

◆ optimize_offset()

void wmtk::components::topological_offset::TopoOffsetTetMesh::optimize_offset ( const std::filesystem::path &  output_file)

optimize the offset

All label attributes are ignored from here on out. All relevant information is stored in tags and in the "on_surface" and "on_offset" attributes.

◆ optimize_offset_alternating()

void wmtk::components::topological_offset::TopoOffsetTetMesh::optimize_offset_alternating ( )

The A/B driver: Phase A (TetWild + offset envelope), Phase B (smoothing to a fixed point, then refine where Phi is stuck), repeated until both criteria are inside tolerance or max_rounds is reached. Replaces the single mesh_improvement() call.

◆ phase_b_smooth()

size_t wmtk::components::topological_offset::TopoOffsetTetMesh::phase_b_smooth ( )

Phase B's smoothing loop. Each pass sweeps every vertex once: offset vertices are placed on the level set by smooth_offset_vertex_backtracking(), background vertices minimize their one-ring AMIPS by smooth_interior_vertex_phase_b(). Returns the number of passes; the natural exit is a pass in which no offset vertex was backtracked by its one-ring (m_phase_b_constrained == 0), i.e. every local minimum is interior. See the definition for the other exits.

◆ rebuild_offset_envelope()

void wmtk::components::topological_offset::TopoOffsetTetMesh::rebuild_offset_envelope ( )

Build m_offset_envelope from the current offset-surface triangles. Called at the start of each Phase A.

◆ refine_sizing_around_worst()

size_t wmtk::components::topological_offset::TopoOffsetTetMesh::refine_sizing_around_worst ( double  max_metric)
overridevirtual

Phase A's stall-driven sizing refinement – TetWild's, on element quality.

mesh_improvement() fires this when optimization_quality_stats()'s max stalls, and only runs inside Phase A, so this only ever runs there; it ratchets the sizing down around the worst tets by AMIPS, as TetWildMesh does. It does not refine for the offset – that is update_band_sizing_from_tolerance(). Both write the same per-vertex scalar.

Implements wmtk::TetOptimizerMesh.

◆ report_outside_support()

void wmtk::components::topological_offset::TopoOffsetTetMesh::report_outside_support ( const char *  when,
const DistanceSplit s 
) const

Turn a residual_split()'s outside-support tally into the hard error. Separate from check_offset_within_support() so the per-iteration check can reuse a split it already has.

◆ set_cell_quality()

void wmtk::components::topological_offset::TopoOffsetTetMesh::set_cell_quality ( const size_t  tid,
const double  q 
)
inlineoverridevirtual

◆ set_vertex_position()

void wmtk::components::topological_offset::TopoOffsetTetMesh::set_vertex_position ( const size_t  vid,
const Vector3d &  p 
)
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 and its rational position is the exact value of the double it carries. m_pos must still be filled: the shared split falls back to the exact midpoint when the double one would invert a tet, and every quality and orientation test around an unrounded vertex reads its neighbours' m_pos. A stale m_pos would silently feed those the wrong point.

◆ smooth_after()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::smooth_after ( const Tuple t)
overridevirtual

Every vertex goes through the shared smoother. Nothing is dispatched anywhere else.

The offset is a term smooth_offset_vertex_backtracking() drives to zero directly, so the one surface the component exists to place goes through the same line search, exact inversion test and accept checks as every other vertex. See OffsetPotential.

Reimplemented from wmtk::TetOptimizerMesh.

◆ smooth_before()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::smooth_before ( const Tuple t)
overridevirtual

The base rounds and refuses bbox vertices; this drops the bbox refusal (the wall is a region boundary held in an envelope, not frozen) and, in Phase B, refuses every vertex that is not offset-only – that phase places the offset surface and moves nothing else.

Reimplemented from wmtk::TetOptimizerMesh.

◆ smooth_interior_vertex_phase_b()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::smooth_interior_vertex_phase_b ( const Tuple t)

Phase B's placement of a background vertex: Newton on the one-ring AMIPS energy, to this vertex's own minimum.

The counterpart of smooth_offset_vertex_backtracking() for vertices that carry no surface. Solving the interior alongside the surface opens one-rings that would otherwise force the offset root find to backtrack – which is the pass loop's exit criterion.

Reuses the shared smoother (optimization::smooth_vertex_3d) – pure AMIPS, no envelope terms, exact inversion accept, quality veto – but with Phase B's own solver, which stops at vertex_grad_tol_rel of the visit's initial gradient instead of a fixed iteration budget.

◆ smooth_offset_vertex_backtracking()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::smooth_offset_vertex_backtracking ( const Tuple t)

Phase B's placement of an offset-surface vertex: the offset energy alone, then backtrack into the one-ring if the solved point inverts something.

The only smoothing Phase B does – smooth_before() refuses every other vertex there, so no AMIPS term is assembled anywhere in the phase. Do not route it through the shared smoother; measured worse – see git history of this file. That objective is w_amips * AMIPS plus the offset term, so the vertex rests where the two gradients cancel rather than on the level set, and AMIPS being dimensionless its positional gradient scales as 1/h, which makes the resting point worse as the mesh refines.

A 1-D root find along the normal, Phi(x) = c, never a 3-D minimisation of the offset energy: that energy's Hessian is rank one, so the level set's tangent plane is unconstrained and the vertex slides. Element shape is left entirely to split/collapse/swap. No incident tet may invert – a constraint on the segment to the target, not a reason to discard it: bisect for the furthest point along it that keeps every tet valid.

< damping increases per iteration before giving up

< mu/s^2 at entry: effectively undamped Gauss-Newton

◆ smoothing_containment_envelope()

std::shared_ptr< SampleEnvelope > wmtk::components::topological_offset::TopoOffsetTetMesh::smoothing_containment_envelope ( const size_t  vid) const
inlineoverridevirtual

... and it is not CONTAINED by one either.

The base's default answers m_envelope for every vertex the union flag m_is_on_surface is set on, which here covers both tracked surfaces. m_envelope is built from the input complex's boundary, so answering it for an offset-surface vertex would demand the offset stay within eps of the input – a tube of the wrong radius around the wrong surface, which refuses every move the offset term asks for.

The input complex falls through to the base and is contained by m_envelope in every phase. That is the whole constraint on it: it is smoothed like any other TetWild surface vertex and this tube is the tolerance it may drift within.

The offset surface is the exception only in Phase A, where m_offset_envelope contains it like any other tracked surface: Phase A minimises AMIPS alone, so without a container nothing stops it relocating the offset surface for element shape, which Phase B would then have to undo. Phase B answers null, being the pass whose job is to move the surface.

Reimplemented from wmtk::TetOptimizerMesh.

◆ smoothing_energy_envelope()

std::shared_ptr< SampleEnvelope > wmtk::components::topological_offset::TopoOffsetTetMesh::smoothing_energy_envelope ( const size_t  vid) const
inlineoverridevirtual

The offset surface is the one tracked surface with NO envelope, in either role.

It is the surface the optimization exists to move: a tube around wherever conservative growth left it would cap how far it can travel toward the level set. What holds it is the offset term in the objective, not a container.

The pull must be a real envelope, never a composite: this hook's consumers call the non-virtual SampleEnvelope queries (nearest_point, and the ExactDistanceEnergy3D trio), which on a composite bind to the base's null BVH. So a junction vertex is pulled toward its most-violated member tube instead – one real envelope per smoothing attempt, alternating toward the junction across passes – while smoothing_containment_envelope() enforces the full constraint.

Implements wmtk::TetOptimizerMesh.

◆ spike_amips()

static double wmtk::components::topological_offset::TopoOffsetTetMesh::spike_amips ( const double  q)
inlinestatic

cell_quality is AMIPS^3; everything else reports its cube root, so convert once here. A non-finite quality is worse than any finite one, not silently zero.

◆ split_adjust_position()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::split_adjust_position ( size_t  v_new,
const std::vector< Tuple > &  children 
)
overridevirtual

Writes the new vertex's tracked-surface membership before the shared split's containment check reads it; returns the base's positioning verdict unchanged. See the definition.

Reimplemented from wmtk::TetOptimizerMesh.

◆ split_after_cells()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::split_after_cells ( size_t  ,
size_t  ,
size_t  ,
const std::vector< Tuple > &   
)
overridevirtual

Restore application cell data on the children made by a split.

Reimplemented from wmtk::TetOptimizerMesh.

◆ split_after_vertex()

void wmtk::components::topological_offset::TopoOffsetTetMesh::split_after_vertex ( size_t  ,
bool   
)
overridevirtual

Application metadata not represented by the shared vertex attributes.

Reimplemented from wmtk::TetOptimizerMesh.

◆ split_before_cells()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::split_before_cells ( const Tuple edge,
const std::vector< Tuple > &  parents 
)
overridevirtual

Split policy that is the offset's own.

The shared split places the vertex, keeps the quality and the shared attributes and checks containment; what it cannot know is which region tag the two child tets inherit, and which of the two tracked surfaces the new vertex joined.

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.

◆ split_edge_after()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::split_edge_after ( const Tuple t)
overridevirtual

This function computes the attributes for the added simplices. User specified modifications and desideratas for after an edge split.

Parameters
theedge Tuple to be split
Returns
true if the modification succeed

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.

◆ split_edge_before()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::split_edge_before ( const Tuple t)
overridevirtual

overriden splits/invariants Dispatch: the optimization phase runs the shared split, everything else the marching-tets one below.

Reimplemented from wmtk::TetOptimizerMesh.

◆ split_face_after()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::split_face_after ( const Tuple t)
overridevirtual

Compute the attributes for the added simplices.

User specified modifications and desideratas for after a face split

Parameters
tThe face tuple to be split.
Returns
true if the modification succeed

Reimplemented from wmtk::TetMesh.

◆ split_face_before()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::split_face_before ( const Tuple t)
overridevirtual

User specified preparations and desideratas for a face split before changing the connectivity.

Parameters
tThe face tuple to be split.
Returns
true if the preparation succeed.

Reimplemented from wmtk::TetMesh.

◆ split_tet_after()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::split_tet_after ( const Tuple t)
overridevirtual

Compute the attributes for the added simplices.

User specified modifications and desideratas for after a tet split

Parameters
tThe tet tuple to be split.
Returns
true if the modification succeed

Reimplemented from wmtk::TetMesh.

◆ split_tet_before()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::split_tet_before ( const Tuple t)
overridevirtual

User specified preparations and desideratas for a tet split before changing the connectivity.

Parameters
tThe tet tuple to be split.
Returns
true if the preparation succeed.

Reimplemented from wmtk::TetMesh.

◆ surface_envelope_for_face()

std::shared_ptr< SampleEnvelope > wmtk::components::topological_offset::TopoOffsetTetMesh::surface_envelope_for_face ( const std::array< size_t, 3 > &  vids) const
inlineoverridevirtual

Envelope the tracked-surface triangle vids must stay inside.

Null means this triangle carries no containment requirement, and every containment check on it is skipped. Both applications that track a single surface keep one envelope for it, so the default answers with that; an application tracking more than one surface (see SurfaceTagAttributes::m_surface_class) overrides this to answer per class – the offset boundary, for instance, is free to move and is checked against nothing.

Keyed on the vertex ids rather than the fid because every caller is a topological operation asking about the triangles it is ABOUT to create or has just created, where only the vids are known for certain.

Reimplemented from wmtk::TetOptimizerMesh.

◆ swap_after_cells()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::swap_after_cells ( const std::vector< size_t > &  ,
bool   
)
overridevirtual

Propagate application data to the cells made by a successful topological swap.

Reimplemented from wmtk::TetOptimizerMesh.

◆ swap_before_interior()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::swap_before_interior ( const std::vector< size_t > &  tids)
overridevirtual

Which tag the tets a swap creates should carry.

A swap must not move the boundary between differently tagged regions, since that boundary is the offset. Around an interior edge every incident tet already shares a tag, so this is a cheap safety net there; on a surface flip the ring genuinely spans two and the majority tag wins. Refuses the swap when three or more tags meet: there is then no single answer and any choice would relabel a tet.

Reimplemented from wmtk::TetOptimizerMesh.

◆ swap_before_surface()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::swap_before_surface ( const std::vector< size_t > &  tids,
size_t  a,
size_t  b,
size_t  c,
size_t  d 
)
overridevirtual

Reimplemented from wmtk::TetOptimizerMesh.

◆ swap_capture_tag()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::swap_capture_tag ( const std::vector< size_t > &  tids)
private

The swap operations themselves are wmtk::TetOptimizerMesh's. What is left here is the offset's own: keeping the region tags and construction labels consistent across a swap.

◆ tag_bits()

uint64_t wmtk::components::topological_offset::TopoOffsetTetMesh::tag_bits ( const CellTag &  tags) const
inline

The three helpers of the per-tag envelope dispatch. tag_bits() and face_mask() are trivial; envelope_for_mask() is out of line (it builds IntersectionEnvelopes lazily).

◆ update_band_sizing_from_tolerance()

size_t wmtk::components::topological_offset::TopoOffsetTetMesh::update_band_sizing_from_tolerance ( )

Per-vertex band sizing update, run after Phase B. Returns the number changed.

Refine-only, and only on pure chord error. "In tolerance" is the convergence criterion itself – |grad (Phi - c)^2| <= offset_gradient_tolerance() – measured at band vertices and at face-interior samples on the lattice for_each_offset_face_sample() defines, so this responds to the quantity that decides the run rather than to a proxy for it.

  • Halve when the vertex and every surface one-ring neighbour are in tolerance but some sample inside an incident surface face is not: pure resolution error, the one thing a finer sizing field can fix.
  • Otherwise leave it alone. A vertex out of tolerance is misplaced, not under-resolved, and a vertex in tolerance with an out-of-tolerance neighbour is that neighbour's problem.

This is the only thing that refines the sizing field for the offset. Phase A keeps TetWild's quality-driven stall response (refine_sizing_around_worst), which ranks elements by AMIPS; the two write the same field and accumulate.

Clamped below by max(min_sizing_scalar, min_edge_length / l) – the band's floor, not stuck_refine_min_scalar – and the changed vertices seed gradation_smooth_sizing(), so a re-sized patch does not sit against an untouched one.

◆ vertex_boundary_mask()

uint64_t wmtk::components::topological_offset::TopoOffsetTetMesh::vertex_boundary_mask ( const size_t  vid) const
inline

The tag boundaries this vertex lies on – the raw mask, gated on the vertex still being region geometry at all.

The gate is required, not redundant. m_boundary_mask propagates by a bare AND of a split's endpoints, so an edge whose two ends share a bit hands that bit to its midpoint even when the edge is a chord through the interior – and the front is built by splitting precisely such edges. vertex_is_on_region() does not over-claim that way, so the mask says which boundaries and the gate says whether the vertex is on one at all; the mask only ever narrows an answer the flags already allow.

◆ vertex_is_on_region()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::vertex_is_on_region ( const size_t  vid) const
inline

Is this vertex on a region boundary – a tag boundary, or the domain wall.

Derived, never stored. Both halves are already maintained exactly: m_is_on_region by is_edge_on_region() at each split and an OR at each collapse, on_bbox_faces by the intersection of the split endpoints and by the collapse rule that a wall vertex may only merge into one at least as constrained. A stored flag would be a fourth thing to keep in step with those, carrying nothing they do not.

◆ vertex_is_on_surface()

bool wmtk::components::topological_offset::TopoOffsetTetMesh::vertex_is_on_surface ( const size_t  vid) const
overridevirtual

Is a vertex part of the substructure.

Parameters
vidVertex ID

Reimplemented from wmtk::TetOptimizerMesh.

◆ wall_offplane_deviation()

double wmtk::components::topological_offset::TopoOffsetTetMesh::wall_offplane_deviation ( const size_t  vid) const

How far vid sits from every bounding-box plane it claims membership of. Zero when it is where its on_bbox_faces say it is; empty membership gives zero.

◆ warn_if_offset_reaches_domain_boundary()

void wmtk::components::topological_offset::TopoOffsetTetMesh::warn_if_offset_reaches_domain_boundary ( ) const

Warn if conservative growth ran into the bounding box.

A band face on the domain boundary means target_distance exceeded the clearance between the input complex and the box, so the offset is clipped there. No operation can move those vertices, making the target distance unreachable by construction – worth saying out loud rather than leaving as a mysterious plateau in max_dist_err.

◆ write_optimization_debug_output()

void wmtk::components::topological_offset::TopoOffsetTetMesh::write_optimization_debug_output ( const std::string &  path)
inlineoverridevirtual

The driver hands a bare debug_N; put the frames beside the run's own output rather than in whatever directory it was launched from, which is what lets the viewer find them as a timeline – it globs *debug_*.vtu in the output directory and orders them by counter.

Implements wmtk::TetOptimizerMesh.

◆ write_phi_grid()

void wmtk::components::topological_offset::TopoOffsetTetMesh::write_phi_grid ( const std::string &  path,
int  n 
) const

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.

Member Data Documentation

◆ churn_counts

std::vector<std::array<int, 3> > wmtk::components::topological_offset::TopoOffsetTetMesh::churn_counts

{split-born vertices, recollapsed, recollapsed in the immediately following collapse pass} per A/B round, in step with op_counts. See VertexExtra::m_born_epoch.

◆ iter_cnt_split_born

std::atomic<int> wmtk::components::topological_offset::TopoOffsetTetMesh::iter_cnt_split_born {0}

Per-iteration operation counters, reset before each iteration's operation passes. Churn: split-born vertices a collapse later removed, and the subset removed in the same pass-pair that created them. The rest survived at least into a later iteration.

◆ m_collapse_offset_rel_before

wmtk::threading::enumerable_thread_specific<double> wmtk::components::topological_offset::TopoOffsetTetMesh::m_collapse_offset_rel_before
private

The worst offset-face criterion around the operation, captured before it runs – the bar its after hook compares against, mirroring how the AMIPS gates use cache.max_energy.

◆ m_input_complex_envelope

std::shared_ptr<SampleEnvelope> wmtk::components::topological_offset::TopoOffsetTetMesh::m_input_complex_envelope

The exact-kind envelope of the input complex, built only for offset_field "euclidean". Null otherwise.

Not a containment envelope and never used as one: no operation tests against it. It exists because nearest_point_feature() – the foot point plus the feature kind the exact distance derivatives case on – is only answered by the exact path. Built from the same extraction as m_input_complex_bvh, so it describes the same geometry.

◆ m_isect_cache

std::map<uint64_t, std::shared_ptr<SampleEnvelope> > wmtk::components::topological_offset::TopoOffsetTetMesh::m_isect_cache
mutable

Memoized IntersectionEnvelope per multi-bit mask. Lazily built under the mutex because the queries that need them run concurrently under kPartition.

◆ m_offset_envelope

std::shared_ptr<SampleEnvelope> wmtk::components::topological_offset::TopoOffsetTetMesh::m_offset_envelope

The tube the offset surface may not leave during Phase A. Null in Phase B.

Rebuilt at the start of every Phase A from the offset surface as Phase B just left it, with eps = offset_envelope_rel x the Phi tolerance. Rebuilding is what lets the surface travel across rounds: each Phase A pins it near its current position, each Phase B is free to move it. m_envelope, the input complex's, is built once and never rebuilt – that geometry is what the offset distance is measured against and may not drift at all.

◆ m_offset_params

Parameters& wmtk::components::topological_offset::TopoOffsetTetMesh::m_offset_params

The base holds only wmtk::OptimizerParameters; this is the same object, typed, for the offset-only fields.

◆ m_offset_potential

std::shared_ptr<OffsetPotential3D> wmtk::components::topological_offset::TopoOffsetTetMesh::m_offset_potential

The smooth offset potential Phi of the input complex, as loaded.

The offset surface is the level set Phi = c. Built from the same extraction as m_input_complex_bvh, in the same call, so the two describe the same geometry, and like the BVH built once and never rebuilt: every offset quantity is measured against the input as given, not against whatever the mesh has drifted to.

◆ m_phase_b_constrained

std::atomic<int> wmtk::components::topological_offset::TopoOffsetTetMesh::m_phase_b_constrained {0}
mutable

Offset placements this Phase B pass that could not reach their unconstrained minimum: the root find entered its one-ring bisection, was refused outright by inversion, or found the ring already float-inverted on entry. Reset by phase_b_smooth() before each pass; a pass ending with this at zero is the loop's natural exit, since the potential is a frozen field and placements couple only through the ring constraint.

◆ m_phase_b_solver

wmtk::threading::enumerable_thread_specific< std::unique_ptr<polysolve::nonlinear::Solver> > wmtk::components::topological_offset::TopoOffsetTetMesh::m_phase_b_solver
mutable

Per-thread Newton solver for Phase B's interior AMIPS solves. Separate from the base's m_solver because it carries a different stopping rule: polysolve's rel_grad_norm_tol set to vertex_grad_tol_rel with a deep iteration budget, against the base's fixed shallow budget with no tolerance. Created on first use.

◆ m_phi_V

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(), consumed by init_offset_potential().

◆ m_swap_label

wmtk::threading::enumerable_thread_specific<int> wmtk::components::topological_offset::TopoOffsetTetMesh::m_swap_label
private

The construction label shared by every cell of the swap's ring, captured alongside the tag. Required: without it a swap leaves its new cells holding whatever label occupied the recycled tet slot.

◆ m_tag_bit

std::map<int64_t, int> wmtk::components::topological_offset::TopoOffsetTetMesh::m_tag_bit

Input tag id -> bit position in VertexExtra::m_boundary_mask. Assigned in init_from_image() once the tag maps are complete; at most 64 input tags.

◆ m_tag_envelopes

std::map<int64_t, std::shared_ptr<SampleEnvelope> > wmtk::components::topological_offset::TopoOffsetTetMesh::m_tag_envelopes

One containment envelope per input tag, ambient included. Both phases.

E_t is a tube of half-width m_envelope_eps around the boundary faces of region t as the input mesh carried them, built in init_surfaces_and_boundaries() before offset construction, so the band's later tag rewriting never enters them. A simplex on several boundaries is constrained by the intersection of its tags' tubes (envelope_for_mask()), which pins junction curves and points to the junction itself. Every simplex of the input complex lies on tag boundaries (see label_input_complex()), so these tubes hold all of it.

m_envelope (the base's pointer) is a UnionEnvelope over these members, so the shared engine's one direct use – the collapse_edge_before point check – keeps union semantics.

Interior vertices of the complex are not held by these: a face interior to a region has identical tag sets on both sides and lands in no bucket, so a tet-filled complex's interior is free to optimise.

◆ m_worst_dist_vid

size_t wmtk::components::topological_offset::TopoOffsetTetMesh::m_worst_dist_vid = static_cast<size_t>(-1)
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.

◆ OFFSET_SURFACE_CLASS

constexpr int wmtk::components::topological_offset::TopoOffsetTetMesh::OFFSET_SURFACE_CLASS = 1
staticconstexpr

SurfaceTagAttributes::m_surface_class for the offset boundary.

The input complex keeps the primary class 0, so a face carrying input geometry is envelope-checked by the shared operations exactly as in tetwild and simwild.

◆ op_counts

std::vector<std::array<int, 3> > wmtk::components::topological_offset::TopoOffsetTetMesh::op_counts

{splits, collapses, swaps} per A/B round – one entry per round the driver runs, the converging one included, as deltas rather than running totals. Phase B does no topological work, so a round's entry is exactly what its Phase A did. Does not mirror optimization_metrics, which is a single whole-run summary.

◆ optimization_metrics

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} per optimization iteration. Only the last pair is the convergence criterion; the rest are diagnostics answering different questions – the Euclidean error says how far the smoothed offset ended up from the exact one, and the Phi residual still ranks the sizing field.


The documentation for this class was generated from the following files: