feat(num): N3 selectable scale-floor clamp mode (HardJava | SmoothBarrier)
The hyper-ideal vertex scale b is floored to keep the geometry valid. The original clamp `b<0 → 0.01` mirrors the Java oracle but is only C⁰ (in fact value-discontinuous at b=0): a Newton step crossing the feasibility boundary hits a kink that can stall convergence (numerical-stability audit N3). Rather than replace the Java-faithful behaviour (which would break the golden parity tests), make the floor a selectable mode so BOTH the Java standpoint and the clean mathematics are available: - HyperIdealScaleClamp::HardJava (DEFAULT) — the original snap, bit-for-bit faithful to HyperIdealFunctional.java → all parity tests unchanged. - HyperIdealScaleClamp::SmoothBarrier — C¹ softplus floor b ↦ floor + softplus_β(b−floor), β = HYPER_IDEAL_SCALE_SHARPNESS (=100); ≈ identity away from the floor, smooth across b=0. Opt-in. clamp_hyper_ideal_scale centralises the logic (also folds in the N4 nachzügler: compute_face_angles used a bare 0.01). The mode threads with a defaulted trailing parameter through compute_face_angles, face_angles_from_local_dofs, evaluate_hyper_ideal, the four hyper_ideal_hessian* variants and newton_hyper_ideal — so every existing call site keeps HardJava behaviour. Tests (+4): clamp-function C¹/floor/identity contract, mode-equivalence away from the boundary, and end-to-end SmoothBarrier convergence to the same Java golden vector (LawsonHyperIdeal). 296/296 CGAL tests pass. Documented in doc/math/geometry-modes.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -194,6 +194,54 @@ TEST(LawsonHyperIdeal, ConvergenceGoldenVector_JavaXVal)
|
||||
}
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// N3 — SmoothBarrier clamp mode converges to the same golden solution
|
||||
//
|
||||
// The C¹ smooth-barrier scale floor is an opt-in alternative to the Java hard
|
||||
// clamp. On this well-posed problem the scales stay well above the floor
|
||||
// throughout the solve, so the barrier is ≈ identity and the converged vector
|
||||
// must match the same golden classes as the HardJava run above — demonstrating
|
||||
// the smooth mode is a drop-in that does not move the solution when the clamp
|
||||
// region is never entered.
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
TEST(LawsonHyperIdeal, SmoothBarrierConvergesToGoldenVector)
|
||||
{
|
||||
std::set<Edge_index> original;
|
||||
ConformalMesh m = make_lawson_square_tiled(&original);
|
||||
|
||||
HyperIdealMaps maps = setup_hyper_ideal_maps(m);
|
||||
const int n = assign_all_dof_indices(m, maps);
|
||||
ASSERT_EQ(n, 4 + 18);
|
||||
|
||||
for (auto e : m.edges())
|
||||
if (original.count(e)) maps.theta_e[e] = PI / 2.0;
|
||||
|
||||
std::vector<double> x0(static_cast<std::size_t>(n), 1.0);
|
||||
auto res = newton_hyper_ideal(m, x0, maps, /*tol=*/1e-10, /*max_iter=*/200,
|
||||
/*hess_eps=*/1e-5,
|
||||
HyperIdealScaleClamp::SmoothBarrier);
|
||||
ASSERT_TRUE(res.converged)
|
||||
<< "SmoothBarrier HyperIdeal Newton did not converge; ||G||="
|
||||
<< res.grad_inf_norm;
|
||||
|
||||
constexpr double b_gold = 1.1462158341786262;
|
||||
constexpr double a_orig_gold = 1.7627471737467797;
|
||||
constexpr double a_aux_gold = 2.633915794495759;
|
||||
const double tol = 1e-5;
|
||||
|
||||
for (auto v : m.vertices()) {
|
||||
const int iv = maps.v_idx[v];
|
||||
ASSERT_GE(iv, 0);
|
||||
EXPECT_NEAR(res.x[static_cast<std::size_t>(iv)], b_gold, tol);
|
||||
}
|
||||
for (auto e : m.edges()) {
|
||||
const int ie = maps.e_idx[e];
|
||||
ASSERT_GE(ie, 0);
|
||||
const double gold = original.count(e) ? a_orig_gold : a_aux_gold;
|
||||
EXPECT_NEAR(res.x[static_cast<std::size_t>(ie)], gold, tol);
|
||||
}
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════════════
|
||||
// Branch-points variant — Java HyperIdealConvergenceTest...WithBranchPoints
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user