fix(E03): 엄밀해 해 없음 허용 2 % — 찾은 최소가 Bishop +2 % 안이면 값 살림(converged_far · 요구 351)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 04:08:53 +09:00
co-authored by Claude Opus 5.5
parent d5b828dc26
commit 3ead4efa6c
2 changed files with 28 additions and 3 deletions
@@ -40,6 +40,8 @@ SPENCER_SEED_POOL = 300
CROSS_TOP = 10
#: Spencer 원호 씨앗 — Bishop 임계 둘레 구름 크기
CLOUD_SIZE = 200
#: 엄밀해 「해 없음」 허용 — Bishop 임계 원에서 해 없어도 찾은 최소가 Bishop × (1 + 이 값) 안이면 값을 살림
FAR_TOL = 0.02
# ---- 원호 ---------------------------------------------------------------------
@@ -487,8 +489,9 @@ def analyze(
if g is not None:
best_circles[m] = g
results[m]["circular"] = _describe(prep, "circle", g, m, n_slices, min_depth)
# 엄밀해(Spencer · M-P)가 Bishop 임계 원에서 해 없음 · 찾은 최소가 Bishop 보다 높음 = 임계 둘레에서
# 풀리는 원이 없어 먼 원을 고른 것 → 그 방법 원호 = 「해 없음」(Bishop 임계 원 · F None · 까닭 · F–λ 곡선)
# 엄밀해(Spencer · M-P)가 Bishop 임계 원에서 해 없음 · 찾은 최소가 Bishop + FAR_TOL 넘게 높음 = 임계
# 둘레에서 풀리는 원이 없어 먼 원을 고른 것 → 그 방법 원호 = 「해 없음」(Bishop 임계 원 · F None · 까닭 ·
# F–λ 곡선) · 허용 안이면 찾은 값을 살림(status converged_far)
ref = best_circles.get("bishop") if "bishop" in results else None
fb = (results.get("bishop", {}).get("circular") or {}).get("F")
for m in [k for k in ("spencer", "morgenstern_price") if k in methods and ref and fb]:
@@ -496,7 +499,14 @@ def analyze(
if cur and cur["F"] is not None and cur["F"] <= fb:
continue
at = _describe(prep, "circle", ref, m, n_slices, min_depth)
if at["F"] is None:
if at["F"] is not None:
continue
if cur and cur["F"] is not None and cur["F"] <= fb * (1 + FAR_TOL):
cur["status"] = "converged_far"
cur["reason"] = (
f"Bishop 임계 원에서 해 없음 · 다른 원 최소가 Bishop 의 +{FAR_TOL:.0%} 안이라 살림"
)
else:
results[m]["circular"] = at
if noncircular:
mv = polyline_vertices
@@ -4,6 +4,7 @@
- 해 없는 견본 = 검증_sub4 STA.0+340 우기 깎기 비탈을 줄인 꼴(풍화토 위 · 풍화암 · 지표 수위 · 노면 하중):
Bishop 임계 원에서 F_m · F_f 가 λ −2 ~ 2(M-P ±π)에서 안 만남 → 옛 엔진은 먼 원(F 1.82 · Bishop 1.34 의
136 %)을 Spencer 값으로 냈음 → 이제 Spencer · M-P 원호 = F None · status no_solution · 까닭 · 넓은 곡선.
- 허용 2 % — 찾은 최소가 Bishop + 2 % 안이면 값을 살림(status converged_far).
- 해 있는 면 = 상태 converged · 잔차 < 1e-4 · λ 좁은 범위 안 · 곡선 21 점(옛 꼴 그대로).
./venv/Scripts/python.exe -m pytest resources/tester/test_532_2_stab_nosolution.py -q
@@ -25,6 +26,7 @@ from test_532_2_stab_lem_benchmarks import ACADS_EXT, mats # noqa: E402
from E03_StabSection import E03_StabSection_Engine_GLE as gle # noqa: E402
from E03_StabSection import E03_StabSection_Engine_LEM as lem # noqa: E402
from E03_StabSection import E03_StabSection_Engine_Search as search # noqa: E402
from E03_StabSection.E03_StabSection_Engine_Search import ( # noqa: E402
analyze,
evaluate_specified,
@@ -118,5 +120,18 @@ def test_solved_surface_status_and_shape_unchanged():
assert len(out["lambda_curve"]["lambda"]) == gle.CURVE_POINTS
def test_far_tolerance_keeps_value_within_limit(monkeypatch):
"""허용(FAR_TOL) 안이면 찾은 최소를 살림(converged_far) — 견본은 136 % 라 2 % 기본에선 해 없음 그대로."""
monkeypatch.setattr(search, "FAR_TOL", 0.5)
r = analyze(
NO_SOLUTION_MODEL, direction=1, limits=[-9.561, 2.0], min_depth=0.3, noncircular=False,
methods=("bishop", "spencer"),
) # fmt: skip
fb = r["methods"]["bishop"]["circular"]["F"]
c = r["methods"]["spencer"]["circular"]
assert c["status"] == "converged_far" and fb < c["F"] <= 1.5 * fb
assert "살림" in c["reason"] and r["methods"]["spencer"]["min"]["F"] == c["F"]
if __name__ == "__main__":
sys.exit(pytest.main([__file__, "-q"]))