From 3ead4efa6c1c5e9789401ddfd7247ea4de7e20f0 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 26 Oct 2026 04:08:53 +0900 Subject: [PATCH] =?UTF-8?q?fix(E03):=20=EC=97=84=EB=B0=80=ED=95=B4=20?= =?UTF-8?q?=ED=95=B4=20=EC=97=86=EC=9D=8C=20=ED=97=88=EC=9A=A9=202=20%=20?= =?UTF-8?q?=E2=80=94=20=EC=B0=BE=EC=9D=80=20=EC=B5=9C=EC=86=8C=EA=B0=80=20?= =?UTF-8?q?Bishop=20+2=20%=20=EC=95=88=EC=9D=B4=EB=A9=B4=20=EA=B0=92=20?= =?UTF-8?q?=EC=82=B4=EB=A6=BC(converged=5Ffar=20=C2=B7=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=20351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- E03_StabSection/E03_StabSection_Engine_Search.py | 16 +++++++++++++--- resources/tester/test_532_2_stab_nosolution.py | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/E03_StabSection/E03_StabSection_Engine_Search.py b/E03_StabSection/E03_StabSection_Engine_Search.py index e47aa1e9d..145d4e529 100644 --- a/E03_StabSection/E03_StabSection_Engine_Search.py +++ b/E03_StabSection/E03_StabSection_Engine_Search.py @@ -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 diff --git a/resources/tester/test_532_2_stab_nosolution.py b/resources/tester/test_532_2_stab_nosolution.py index cc9502b28..7a034e63d 100644 --- a/resources/tester/test_532_2_stab_nosolution.py +++ b/resources/tester/test_532_2_stab_nosolution.py @@ -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"]))