TetWild

/ (object)

/

Description

Required

/application (string)

/application

Description

Application name must be tetwild.

Options: ['tetwild']

/input (list)

/input

Description

List of triangular input meshes.

/input/* (string)

/input/*

Description

Triangular input mesh.

Optional

/output (string)

/output

Description

Output file name (without extension).

Default: 'out'

/input_names (list)

/input_names

Description

List of names for the input meshes. If no names are assigned, the meshes will be named tag_0, tag_1, etc.

/input_names/* (string)

/input_names/*

Description

Name for one input mesh.

/input_dir (string)

/input_dir

Description

Directory where the input files are located. This is injected by the application and should not be set by the user.

Default: ''

/write_vtu (bool)

/write_vtu

Description

Write the VTU output next to the MSH.

Default: True

/preallocation_factor (float)

/preallocation_factor

Description

Mesh storage (connectivity + attributes) is preallocated to this factor times the live element count at init and consolidation. Operations take fresh slots from that headroom and are retried once it is exhausted. Lower it for pure-decimation runs, raise it for aggressive refinement.

Default: 6.0

Range: [1.0, inf]

/skip_simplify (bool)

/skip_simplify

Description

If true, input simplification will be skipped.

Default: False

/simplify_use_link_condition (bool)

/simplify_use_link_condition

Description

Require the link condition when simplifying the input surface. When true the simplification cannot change the topology of the input, which also means it cannot create non-manifold edges or vertices. When false those collapses are allowed, so the input simplifies further, but the surface handed to the arrangement may differ topologically from the input.

Default: False

/simplify_use_sample_envelope (bool)

/simplify_use_sample_envelope

Description

Use the sampled envelope instead of the exact one for the input simplification only; the tetrahedralisation keeps whatever use_sample_envelope selects. The exact envelope dominates the cost of simplifying a large input. Note the toolkit itself warns that the sampled envelope is unreliable, and on thin lattice geometry it has been observed to over-collapse badly.

Default: True

/simplify_envelope_ratio (float)

/simplify_envelope_ratio

Description

Envelope thickness used for the input simplification, as a fraction of the one used for the tetrahedralisation. Simplifying inside a tighter envelope reserves the remainder as headroom: if the simplification is allowed to place vertices at the full limit, the optimizer starts with a mesh where almost every move already lies outside and most operations are vetoed. 1.0 restores the old behaviour of sharing a single envelope.

Default: 0.5

/optimize_envelope_around_simplified (bool)

/optimize_envelope_around_simplified

Description

EXPERIMENT. Build the optimizer’s envelope around the SIMPLIFIED surface at the remaining tolerance (eps - simplify_eps) instead of around the original input at the full eps. The deviation budget is identical by the triangle inequality – the simplification is already within simplify_eps of the input – but the geometry now starts at the CENTRE of the envelope it is judged against rather than somewhere inside it. The envelope is a hard veto rather than a penalty, so a surface handed over close to the boundary has most of its moves refused; starving that headroom deliberately (simplify_envelope_ratio 0.95) was enough to turn a converging run into a diverging one on Thingi10K 1368052. Also narrows params.eps to match, so l_min and the smoothing energy scale agree with the veto; that makes DEBUG_hausdorff stricter than the input contract, so verify containment against the INPUT separately.

Default: False

/order2_envelope_ratio (float)

/order2_envelope_ratio

Description

Envelope thickness for the order-2 envelope – the open boundaries of the surface and non-manifold edges – as a fraction of the one used for the surface. Kept below 1 deliberately, unlike the surface envelope which uses the full eps. The two are not symmetric: on the surface a wider envelope REMOVES a constraint that was blocking collapses, whereas a boundary curve has no such blockage to relieve, so the extra room is only freedom for the boundary to wander within eps – more geometry to resolve at the same final quality. Measured on Thingi10K 36075 at stop_energy 5: 14 iterations and 31125 tets at 0.5, against 24 iterations and 41463 tets at 1.0, both converging to a max energy of about 4.95. 1.0 gives the boundary curves the full eps.

Default: 0.5

/use_sample_envelope (bool)

/use_sample_envelope

Description

Use sample envelope instead of exact one.

Default: False

/use_legacy_code (bool)

/use_legacy_code

Description

Use the original TetWild code for the mesh improvement. This only works with ‘use_sample_envelope’!

Default: False

/num_threads (int)

/num_threads

Description

Number of threads used by the application

Default: 0

/max_iterations (int)

/max_iterations

Description

Maximum iterations before stopping.

Default: 80

/max_expected_iterations (int)

/max_expected_iterations

Description

Fail the run if mesh_improvement needed more than this many iterations, even if it did reach stop_energy. 0 disables the check. This is a regression guard rather than a quality target: an input that normally converges in a handful of iterations and suddenly needs the whole budget means the sizing-refinement trigger stopped firing when it should, which is a silent slowdown that no energy or envelope assertion catches.

Default: 0

/filter (string)

/filter

Description

Remove the outside region based on different criteria. ‘flood’: flood fill. ‘input’: winding number w.r.t. the input. ‘tracked’: winding number w.r.t. the tracked surface. ‘none’: Do not filter. Flood fill only works if the input is closed. Otherwise, it results in an empty mesh. Filtering w.r.t. the input might cause wrinkles along the surface as some tets might be falsely tagged. Filtering w.r.t. the tracked surface can lead to missing pieces if the input consists of multiple components.

Default: 'none'

Options: ['flood', 'input', 'tracked', 'none']

/eps_rel (float)

/eps_rel

Description

Envelope thickness relative to the bounding box

Default: 0.001

/length_rel (float)

/length_rel

Description

Target edge length relative to the bounding box

Default: 0.05

/stop_energy (float)

/stop_energy

Description

Target energy. If all tets have an energy below this, tetwild will stop.

Default: 100

/split_high_valence_threshold (int)

/split_high_valence_threshold

Description

Incident-tet count above which a vertex accepts only one valence-increasing split per split pass, or 0 to disable. A well-shaped tet mesh has valence around 20-30; a split cascade can drive one vertex into the thousands, at which point every operation touching it is O(valence) and the pass stalls. Splitting an edge leaves its endpoints’ counts unchanged and adds one to each vertex in the edge’s link, so the gate applies to the link.

Default: 0

/w_amips (float)

/w_amips

Description

Relative weight of the AMIPS quality term against the envelope term during smoothing; the envelope weight is 1 - w_amips. The small default makes smoothing primarily about staying on the surface, with quality as a secondary preference.

Default: 0.0001

/smoothing_mode (string)

/smoothing_mode

Description

How smoothing places a surface vertex. ‘projected’: smooth with AMIPS alone as if interior, then walk back toward the start projecting each candidate onto the input; accept the first projected candidate that does not invert and strictly lowers the worst incident element, else do not move. Lands exactly on the input; no weights. ‘exact’: minimize w_amips * AMIPS + (1-w_amips) * (d/eps)^2 with the true region-wise Hessian of the distance to the piecewise-linear input; sliding is free where the input is flat, held at corners, constrained along 3D edges and curve segments. Rests a w_amips-proportional distance off the input.

Default: 'projected'

Options: ['projected', 'exact']

/project_line_search_steps (int)

/project_line_search_steps

Description

Bisections tried by the projected line search before it gives up on a vertex: the step along the Newton direction is halved this many times, each candidate projected onto the input, and the first that does not invert and lowers the worst incident element is taken.

Default: 12

/project_line_search_nested_steps (int)

/project_line_search_nested_steps

Description

After the projected line search gives up, revisit each candidate and bisect between the interpolated point and its projection this many times, taking the longest step toward the input that still does not invert and still lowers the worst element. Lets a vertex whose one-ring cannot tolerate a full projection still travel toward the input, instead of being refused every pass from the same place. 0 disables the pass.

Default: 0

/num_smoothing_passes (int)

/num_smoothing_passes

Description

Number of smoothing passes per optimization iteration. Smoothing is the only phase that improves quality without changing connectivity, so when split, collapse and swap have run out of useful moves it is the only thing that can still lower the energy. Was 10; lowered to 2 once surface vertices stopped being skipped by skip_good_regions, which made each pass do real work – on Thingi10K 240280 a pass costs 41-51s and moves fewer vertices each time (accepted 18606 -> 17077 over five passes, envelope rejections 11316 -> 12237), so the later passes in a run of 10 buy very little for their cost.

Default: 2

/interleaved_smoothing (bool)

/interleaved_smoothing

Description

Interleave smoothing between the topology passes: split + smoothing, collapse + smoothing, swaps + smoothing, instead of split, collapse, swaps followed by num_smoothing_passes smoothing passes. On by default in both applications: each topology pass then sees a relaxed mesh rather than the raw output of the previous one. Measured in 2D on 122839, where per iteration it reached a given energy sooner than the batched schedule (102 vs 535 at iteration 5, 25 vs 69 at iteration 13), at roughly 3x the work per iteration since each topology pass gets its own smoothing.

Default: True

/interleaved_smoothing_passes (int)

/interleaved_smoothing_passes

Description

Smoothing passes to run after each topology pass when interleaved_smoothing is set. One is enough with interleaving on, since there are three of them per iteration.

Default: 1

/coarsen_pass (bool)

/coarsen_pass

Description

Run a final coarsening pass after the optimization has converged. The ordinary collapse judges a candidate on the raw post-collapse geometry – the worst moment in the operation’s life, before smoothing has absorbed any of the damage – so a collapse that would be fine once its neighbourhood relaxes is never taken, and the converged mesh keeps vertices it does not need. This pass takes the collapse optimistically, re-smooths coarsen_smooth_ring around the merged vertex, and keeps it only if the worst element in the region it touched is no worse than before; every element outside that region is untouched, so ‘no worse locally’ is exactly ‘no worse globally’. Alternates with ordinary smoothing passes, as the main optimization does.

Default: False

/coarsen_unbounded (bool)

/coarsen_unbounded

Description

Coarsen as far as the quality guarantee allows, instead of stopping at the target edge length. The pass answers ‘how few elements can hold this max energy’, and that is a more aggressive question than it sounds – the answer ignores how big the elements become. Measured on the registered tetwild models it takes them from 348k to 60k cells (-82.7%) at max energy raised on none of them, against -43.2% with this off, because a converged mesh is sized by length_rel and the adaptive sizing field rather than by what the quality target strictly requires. On by default: the element count is the thing worth having. Turned off, the pass leaves alone any edge already at or past the collapse threshold (0.8 * the target length, the same one the ordinary collapse uses), since collapsing it only makes its neighbours longer still. The sizing FIELD is deliberately not applied either way: that is the optimizer’s own refinement scratch work, and honouring it here would leave the pass unable to undo refinement that turned out to be unnecessary.

Default: True

/coarsen_local_smoothing_passes (int)

/coarsen_local_smoothing_passes

Description

Smoothing sweeps over the ring, inside each candidate collapse, before judging it. 0 by default, which means the pass judges the collapse on its raw post-collapse geometry and never smooths inside the operation. At the level of a single candidate that matters – the composite’s reject rate rises from 25% to 39% without it – but it does not cost coarsening, because the pass runs to a fixed point: a candidate the smoothing would have rescued is replaced by another the pass finds instead, and the relaxation the mesh actually needs comes from the global smoothing between rounds. What it costs is time. The smoothed ball averaged 98 vertices on tetwild’s octocat, so at the old default of 2 sweeps one candidate ran ~200 nonlinear smoothing solves and the pass ran 1.4 million of them to accept 5173 collapses. Over seven tetwild models, dropping this to 0 (with coarsen_max_inner_passes 1) made the pass 9.7x to 31.9x faster for about 1 percentage point of cell reduction.

Default: 0

/coarsen_smooth_ring (int)

/coarsen_smooth_ring

Description

Radius smoothed inside the collapse, and the radius the accept test measures over. The lock claims one more ring than this. With coarsen_local_smoothing_passes at 0 nothing is smoothed, so this only sets how wide a region the accept test compares; a wider region is a more permissive test (it admits untouched cells to both the before and after maxima, which is still sound) so ring 2 coarsens slightly more, but it also makes the pass lock 3 rings per operation where the ordinary collapse locks 2, which is the worst thing for its parallel scaling. 1 by default: on octocat that is 2.3x faster than ring 2 for 1.5 percentage points of cell reduction, and it puts the lock footprint back in line with every other pass.

Default: 1

/coarsen_global_smoothing_passes (int)

/coarsen_global_smoothing_passes

Description

Ordinary whole-mesh smoothing passes run between coarsening rounds. This is what makes a second round worth running at all – see coarsen_max_rounds – so setting it to 0 collapses the rounds to one.

Default: 1

/coarsen_max_rounds (int)

/coarsen_max_rounds

Description

Cap on the coarsen/smooth alternation; the pass also stops as soon as a round accepts no collapse. A round does NOT exist to finish what the previous one started: within a round the collapse pass already runs to a fixed point, so repeating the collapse alone finds nothing. What a round adds is the global smoothing in between, which moves that fixed point – the dirty-epoch retry only re-offers failures next to a SUCCESSFUL collapse, so a whole-mesh smoothing pass is invisible to it, and a rejected composite is rolled back in full so rejections never accumulate progress within a round. Returns decay fast: measured over the 16 challenging triwild models at five rounds, accepted collapses by round were 68.8% / 21.7% / 7.2% / 1.8% / 0.5%. Every round costs a full pre-check sweep over all edges plus a smoothing pass, so the default is two – they carry 90.5% of the coarsening, and the three that would follow are worth 9.5% for 60% of the pass’s budget.

Default: 2

/coarsen_max_inner_passes (int)

/coarsen_max_inner_passes

Description

Cap on the collapse pass’s own dirty-epoch retry loop within one coarsening round; 0 is uncapped. Distinct from coarsen_max_rounds, which counts collapse+global-smoothing alternations. This counts the passes the retry loop makes inside a single collapse pass, re-offering failures whose neighbourhood a successful collapse disturbed. That filter asks only whether the neighbourhood MOVED, not whether it moved helpfully, so a productive first pass re-offers most of the mesh. 1 by default, i.e. one pass per round and no retry within it: on octocat the first pass found 5110 collapses in 135.8s and the three that followed found 27 in 38.4s. Rounds still repeat the pass, and those do pay off, because the global smoothing between them moves the fixed point in a way this filter cannot see.

Default: 1

/preserve_topology (bool)

/preserve_topology

Description

Preserve the topology of the input surface. This forces remove_duplicate_eps to merge only exactly coincident vertices, keeps the link condition enabled during the input simplification, enables the substructure link condition during optimization, and makes the run fail if the output Euler characteristic differs from the input. When unset, all four are relaxed and the topology may change.

Default: False

/remove_duplicate_eps (float)

/remove_duplicate_eps

Description

Remove duplicate vertices in the input meshes that are closer than this fraction of the bounding box diagonal. This is applied to each input mesh separately. 0 merges only exactly coincident vertices, which is still needed to build the surface connectivity; negative skips the pass entirely. Ignored when preserve_topology is set: merging vertices that are merely close welds sheets of the surface together and so removes handles, therefore only exactly coincident vertices are merged in that case.

Default: 0

/allow_surface_swap (bool)

/allow_surface_swap

Description

Allow the edge swaps (3-2, 4-4, 5-6) to operate on surface edges, as a topology-preserving surface diagonal flip. When false, every surface edge is left alone.

Default: True

/check_surface_topology (bool)

/check_surface_topology

Description

Check if the surface topology is preserved after each operation. This can be very slow and should only be used for debugging.

Default: False

/throw_on_fail (bool)

/throw_on_fail

Description

Throw exception if the output does not fulfil the desired criteria. No output will be generated.

Default: False

/log_file (string)

/log_file

Description

Logs are not just printed on the terminal but also saved in this file.

Default: ''

/report (string)

/report

Description

A JSON file that stores information about the result and the method execution, e.g., runtime.

Default: ''

/DEBUG_output (bool)

/DEBUG_output

Description

Write the mesh as debug_{}.vtu after every operation.

Default: False

/DEBUG_sanity_checks (bool)

/DEBUG_sanity_checks

Description

Perform sanity checks after every operation. This can be very slow and should only be used for debugging.

Default: False

/DEBUG_hausdorff (bool)

/DEBUG_hausdorff

Description

Sanity Check: Compute and report the Hausdorff distance of the output to the input. Should be always smaller than eps.

Default: False

/DEBUG_euler (bool)

/DEBUG_euler

Description

Sanity Check: Compute and report the Euler characteristic of the input and output surfaces and warn if they differ. Off by default because on meshes with many components it can take tens of seconds. It is computed regardless when preserve_topology is set (the topology check needs it).

Default: False

/DEBUG_disable_envelope (bool)

/DEBUG_disable_envelope

Description

Diagnostic only. Disables every envelope containment check during the tetrahedralisation, after the input simplification has run. The envelope is the only thing that rejects an operation for geometric rather than combinatorial reasons, so this answers whether a stalled optimization is blocked by the envelope or by the mesh. The output has no containment guarantee and must not be used.

Default: False

/stuck_refine_stall_eps (float)

/stuck_refine_stall_eps

Description

Refine when the last iteration’s improvement was at most this fraction of the distance the max energy still has to cover: (prev_max - max) <= eps * (max - stop_energy). Equivalently, refine unless the mesh is on course to reach the target within about 1/eps more iterations. 0 => only when it does not improve at all.

Default: 0.1

/stuck_refine_cooldown (int)

/stuck_refine_cooldown

Description

After a refinement, skip this many improvement iterations before refining again (let the operations act on the new sizing field). 1 by default: refining every iteration can outrun the operations entirely – on triwild20k 189017 at eps_rel 1e-4 the mesh runs away to 5.8M faces and never converges at 0, and converges at 9.9994 in 35 iterations at 1. Costs ~5% iterations across the challenging models at no wall-time cost.

Default: 1

/stuck_refine_num_worst (int)

/stuck_refine_num_worst

Description

Number of worst tets whose neighborhoods are refined when stuck. 0 => refine all tets with energy above the filter_energy.

Default: 0

Range: [0, inf]

/stuck_refine_rings (int)

/stuck_refine_rings

Description

Graph rings around each worst tet included in the stuck refinement. 0 => only the worst tet itself, 1 => its neighbors, 2 => neighbors of neighbors, etc.

Default: 0

Range: [0, inf]

/stuck_refine_factor (float)

/stuck_refine_factor

Description

Multiplicative reduction of the sizing scalar per stuck refinement (0.5 => halve).

Default: 0.5

/stuck_refine_min_scalar (float)

/stuck_refine_min_scalar

Description

Lower bound on the sizing scalar during stuck refinement.

Default: 0.001

/stuck_refine_gradation (float)

/stuck_refine_gradation

Description

Gradation cap for the monotone sizing smoothing: adjacent sizings may differ by at most this factor (smoothing only lowers values).

Default: 2.0

/stuck_refine_force_split (bool)

/stuck_refine_force_split

Description

When the max energy stalls, split each worst tet’s longest edge once, bypassing the split length gate, to unstick a sliver without changing the sizing field. Adds at most one split per worst tet per stall.

Default: True

/skip_good_regions (bool)

/skip_good_regions

Description

Only smooth vertices incident to a tet whose energy is at least skip_good_regions_margin * stop_energy; skip smoothing already-good regions. OFF by default: smoothing is the only phase that improves quality without changing connectivity, and the premise that a vertex surrounded by good elements has nothing to gain is not reliable – for surface vertices the move is driven by the envelope term rather than by element quality, which is why active_vertices() has to append every surface vertex unconditionally to keep the filter from freezing them outright. With the filter off that special case stops carrying the correctness of the pass. Measured in 2D on 122839 at stop_energy 20, filtering also cost more than it saved: 60 iterations ending at max energy 21.03 with it on, against convergence to 19.9998 in 54 iterations and less wall time (875s vs 947s) with it off. Note the threshold scales with stop_energy, so the filter skips far more at 100 than at 20.

Default: False

/skip_good_regions_margin (float)

/skip_good_regions_margin

Description

A tet is ‘active’ (worth smoothing around) when its energy is at least this fraction of stop_energy.

Default: 0.9

/skip_winding_number (bool)

/skip_winding_number

Description

Skip the finalize-phase winding-number and flood-fill computation. These per-tet quantities are needed to filter the outside region (filter != ‘none’) and are otherwise written only as output annotation fields; the (three) winding-number evaluations dominate the finalize phase on large meshes. When set, they are skipped and the corresponding output fields are left at their defaults. Ignored (with a warning) when filter != ‘none’, which requires them.

Default: False