Wildmeshing Toolkit
Loading...
Searching...
No Matches
OptimizerParameters.h
1#pragma once
2
3#include <limits>
4#include <string>
5
6namespace wmtk {
7
28{
29 double epsr = 1e-3; // relative error bound (wrt diagonal)
30 double eps = -1.; // absolute error bound
31 double lr = 5e-2; // target edge length (relative)
32 double l = -1.;
33 double l_min = -1;
34 double diag_l = -1.;
35
36 bool preserve_topology = false;
37
64
71
72 // ---- Stuck-element sizing refinement --------------------------------
73 // Trigger threshold: fire when the last iteration's improvement is small compared
74 // with the distance the max energy still has to cover, i.e. refine when
75 // (prev_max - max) <= stall_eps * (max - target).
76 // Equivalently: refine unless the mesh is on course to reach the target within
77 // about 1/stall_eps more iterations. 0 => only when it does not improve at all.
78 //
79 // The denominator is the remaining distance, not prev_max, and that is the whole
80 // point. Measured against prev_max, a mesh grinding down at a steady 1.3% per
81 // iteration toward a target far below clears a 1% bar every single iteration and so
82 // never looks stalled -- even though at that rate it needs on the order of a hundred
83 // more iterations and the operations have in fact deadlocked. The escape hatch then
84 // never fires, which is exactly the regime it exists for.
85 double stuck_refine_stall_eps = 0.1;
86 // Cooldown: after a refinement, skip this many improvement iterations before
87 // refining again, so the operations get full passes to act on the new sizing
88 // field before more refinement is added. 0 => may refine every iteration.
89 //
90 // 0 by default: measured over 468 triwild20k models, a cooldown of 1 costs ~13% wall
91 // time for exactly the same mesh sizes (identical median and p90 vertex counts). The
92 // idea that the operations need an idle iteration to act on the new field does not
93 // survive contact with the data -- the trigger already declines to fire while the mesh
94 // is converging, so a separate cooldown only delays the next escape.
95 int stuck_refine_cooldown = 0;
96 // Number of worst cells (by energy) whose neighborhoods are refined.
97 int stuck_refine_num_worst = 0;
98 // Graph rings around each worst cell's vertices included in the refinement.
99 int stuck_refine_rings = 0;
100 // Multiplicative reduction of m_sizing_scalar per refinement (0.5 => /2).
101 double stuck_refine_factor = 0.5;
102 // Lower bound on m_sizing_scalar. Much smaller than the old l_min/l floor;
103 // still far above the position-rounding scale so it stays numerically safe.
104 double stuck_refine_min_scalar = 1e-3;
105 // Gradation cap for the monotone sizing smoothing: neighboring sizings may
106 // differ by at most this factor. The smoothing only ever *lowers* sizings
107 // (spreads refinement outward), never raises the refined values, avoiding
108 // sharp resolution jumps that make operations ill-conditioned.
109 double stuck_refine_gradation = 2.0;
110 // Force-split: when the max energy stalls, split each worst cell's longest edge
111 // once, bypassing the split length gate. This unsticks a sliver whose edges are
112 // too short to be split-eligible, WITHOUT touching the sizing field (which the
113 // *factor ratchet above still drives). Adds at most one split per worst cell per
114 // stall, so it does not bloat the element count.
115 //
116 // This deliberately applies to EVERY worst cell, including one already at its target
117 // size. There used to be a stuck_refine_force_split_oversized_only gate that skipped
118 // those, on the argument that AMIPS is scale invariant so subdividing a badly shaped
119 // cell yields two badly shaped cells. The argument is sound about the cell itself and
120 // wrong about the outcome: force-splitting it also refines its NEIGHBOURHOOD, and that
121 // is what breaks a deadlocked configuration open. On Thingi10K 46024 -- the one model of
122 // 10,000 in the sweep that finished above stop_energy -- the gate refused ~2000 tets on
123 // every stall, the max energy froze at iteration 3 and stayed identical to 15 significant
124 // figures for the remaining 77 iterations (4.6 h). Without the gate the same model
125 // converges to 9.98 in 14 iterations and 11 minutes.
126 bool stuck_refine_force_split = true;
127
128
129 // ---- Skip good regions ----------------------------------------------
130 // Only smooth vertices incident to a cell whose energy is >=
131 // skip_good_regions_margin * stop_energy. Smoothing a vertex surrounded by
132 // good cells does nothing, so skipping it is free (14-16x faster smooth
133 // passes). Only smoothing is gated: gating the topology/sizing ops
134 // (split/collapse/swap) starves the optimizer and blows up the element
135 // count, so those always run over the whole mesh.
136 bool skip_good_regions = false;
137 // Safety margin on the "active" threshold: a cell is active when its energy is >= this
138 // fraction of stop_energy, so vertices near cells sitting just below the target are
139 // still smoothed.
140 double skip_good_regions_margin = 0.9;
141
142 double splitting_l2 = -1.; // the lower bound length (squared) for edge split
143 double collapsing_l2 =
144 std::numeric_limits<double>::max(); // the upper bound length (squared) for edge collapse
145
146 double stop_energy = 100;
147
154 double w_amips = 1e-4;
156 std::string smoothing_mode = "projected";
161 double w_envelope = 1. - 1e-4; // derived; not read from json
162
165 bool interleaved_smoothing = true;
166 int interleaved_smoothing_passes = 1;
167
168 // ---- Coarsening pass -------------------------------------------------
190 bool coarsen_pass = false;
214 bool coarsen_unbounded = true;
298
299 bool debug_output = false;
300 bool perform_sanity_checks = false;
301
309 void init_lengths_from_diagonal(const double diag)
310 {
311 diag_l = diag;
312 if (l > 0) {
313 lr = l / diag_l;
314 } else {
315 l = lr * diag_l;
316 }
317 splitting_l2 = l * l * (16 / 9.);
318 collapsing_l2 = l * l * (16 / 25.);
319
320 if (eps > 0) {
321 epsr = eps / diag_l;
322 } else {
323 eps = epsr * diag_l;
324 }
325 }
326};
327
328} // namespace wmtk
The parameters tetwild, triwild and simwild all share.
Definition OptimizerParameters.h:28
int project_line_search_nested_steps
Partial-projection bisections tried after it gives up; 0 disables that pass.
Definition OptimizerParameters.h:160
double w_amips
Definition OptimizerParameters.h:154
int coarsen_max_inner_passes
Definition OptimizerParameters.h:297
std::string smoothing_mode
"projected" or "exact"; see SmoothVertexOptions::SmoothingMode.
Definition OptimizerParameters.h:156
bool coarsen_pass
Definition OptimizerParameters.h:190
bool optimize_envelope_around_simplified
Definition OptimizerParameters.h:63
int coarsen_global_smoothing_passes
Definition OptimizerParameters.h:255
int coarsen_max_rounds
Definition OptimizerParameters.h:279
void init_lengths_from_diagonal(const double diag)
Derive the edge-length and envelope quantities from the bounding-box diagonal.
Definition OptimizerParameters.h:309
bool coarsen_unbounded
Definition OptimizerParameters.h:214
int split_high_valence_threshold
Definition OptimizerParameters.h:70
int coarsen_local_smoothing_passes
Definition OptimizerParameters.h:232
int project_line_search_steps
Bisections tried before the projected search gives up. See SmoothVertexOptions.
Definition OptimizerParameters.h:158
int coarsen_smooth_ring
Definition OptimizerParameters.h:248
int num_smoothing_passes
Number and placement of smoothing passes in the shared Wild optimization driver.
Definition OptimizerParameters.h:164