Files
Aislo/resources/tester/test_567_1_stab_search.py
T

181 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""567-1 E03 2D 탐색 지킴 — 수렴(기본 대 촘촘) · 비원호 다출발 · 탐색 한계 = 모형 끝 · 판정 = 원호 Bishop ·
비원호 Spencer · M-P 중 작은 값 · 「면 확인」(비원호 < 원호 Bishop × 0.85).
./venv/Scripts/python.exe -m pytest resources/tester/test_567_1_stab_search.py -q -s
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(Path(__file__).resolve().parent))
from E03_StabSection import E03_StabSection_Engine_Search as search # noqa: E402
from E03_StabSection import E03_StabSection_View as view # noqa: E402
from E03_StabSection.E03_StabSection_Engine_Model import side_cases # noqa: E402
from test_532_2_stab_lem_benchmarks import ( # noqa: E402
ACADS1C_MATS,
ACADS1C_REGIONS,
ACADS_EXT,
_acads3_model,
_acads4_model,
mats,
)
from test_532_2_stab_section_model import DESIGN, GROUND # noqa: E402
DENSE_CIRCLE = {"grid": (30, 30, 20), "entry_exit_points": 50}
CIRCLE_TOL = 0.005
NONCIRC_TOL = 0.03
def _f(res, method, kind):
return (res["methods"][method].get(kind) or {}).get("F")
def _diff(a, b):
return abs(b / a - 1)
def test_circular_default_vs_dense():
# 원호 = 판정 값 · 격자 4 배 · 진입진출 2 배에도 차 ≤ 0.5 %
cases = {
"1a": {
"materials": mats(("s", 20, 3, 19.6)),
"regions": [{"material_id": "s", "polygon": ACADS_EXT}],
},
"1c": {"materials": ACADS1C_MATS, "regions": ACADS1C_REGIONS},
}
for name, model in cases.items():
base = search.analyze(model, direction=-1, noncircular=False)
dense = search.analyze(model, direction=-1, noncircular=False, **DENSE_CIRCLE)
for m in search.METHODS:
a, b = _f(base, m, "circular"), _f(dense, m, "circular")
print(f"ACADS {name} {m} 원호 기본 {a:.4f} 촘촘 {b:.4f} 차 {(b / a - 1) * 100:+.2f} %")
assert _diff(a, b) <= CIRCLE_TOL
def test_noncircular_default_vs_dense(monkeypatch):
# 비원호 = 출발 셋 기본 대 출발 여섯 · 걸음 두 배 — 차 ≤ 3 %(기록 · 3a +0.2 % · 4 −1.8 %)
for name, make in (("3a", _acads3_model), ("4", _acads4_model)):
base = search.analyze(make(), direction=-1, methods=("janbu", "spencer"))
with monkeypatch.context() as mp:
mp.setattr(search, "NONCIRC_STARTS", 2 * search.NONCIRC_STARTS)
mp.setattr(search, "LS_ITER", 2 * search.LS_ITER)
mp.setattr(search, "LS_ITER_SPENCER", 2 * search.LS_ITER_SPENCER)
dense = search.analyze(make(), direction=-1, methods=("janbu", "spencer"))
for m in ("janbu", "spencer"):
a, b = _f(base, m, "noncircular"), _f(dense, m, "noncircular")
print(
f"ACADS {name} {m} 비원호 기본 {a:.4f} 촘촘 {b:.4f} 차 {(b / a - 1) * 100:+.2f} %"
)
assert _diff(a, b) <= NONCIRC_TOL
def test_multistart_takes_minimum(monkeypatch):
# 출발 셋 = 출발 하나(같은 씨앗)를 안에 품음 → 같거나 낮음
model = _acads4_model()
with monkeypatch.context() as mp:
mp.setattr(search, "NONCIRC_STARTS", 1)
one = search.analyze(model, direction=-1, methods=("janbu", "spencer"))
three = search.analyze(model, direction=-1, methods=("janbu", "spencer"))
assert search.NONCIRC_STARTS == 3
for m in ("janbu", "spencer"):
assert _f(three, m, "noncircular") <= _f(one, m, "noncircular") + 1e-12
def test_limits_reach_model_end():
# 탐색 한계 = 노면 반대쪽 끝 ~ 모형 끝(요구 392 · Slide2 기본)
design = {
"section_mode": "right_cut",
"road_edges": {"left": {"offset_m": 2.0}, "right": {"offset_m": -2.0}},
}
left, right = side_cases(design, GROUND, DESIGN)
assert left["limits"] == (-2.0, GROUND[-1][0])
assert right["limits"] == (GROUND[0][0], 2.0)
# 설정 — 우 깎기 비탈 끝 −6 · H 3.6 → 옛 정책 −6 − max(5, 2H) · 정한 6 m → −12
assert side_cases(design, GROUND, DESIGN, "2h")[1]["limits"] == (-13.2, 2.0)
assert side_cases(design, GROUND, DESIGN, 6.0)[1]["limits"] == (-12.0, 2.0)
def _case(bishop_c, spencer_n=None, mp_n=None, cond="wet", side="right", role="cut", ends=None):
def found(F, kind):
if F is None:
return None
return {"F": F, "kind": kind, "converged": True, "entry": [-30.0, 20.0], "exit": [2.0, 9.0]}
methods = {
"bishop": {"circular": found(bishop_c, "circle"), "min": found(bishop_c, "circle")},
"spencer": {"circular": found(9.0, "circle"), "noncircular": found(spencer_n, "polyline")},
"morgenstern_price": {"noncircular": found(mp_n, "polyline")},
}
result = {"methods": methods, **({"model_x": ends} if ends else {})}
return {"condition": cond, "side": side, "role": role, "result": result}
def _row(*cases):
meta = {"key": "k", "chainage_m": 0.0}
return view.station_row(meta, {"cases": list(cases)}, {"wet": {"cut": 1.2}})
def test_governing_minimum_and_surface_check():
# 비원호 Spencer 1.0 < Bishop 1.3 × 0.85 → 판정 = Spencer 1.0 N.G · 면 확인
c = _row(_case(1.3, 1.0, 1.05))["cases"]["wet"]["right"]
assert (c["method"], c["kind"], c["fs"], c["ok"]) == ("spencer", "polyline", 1.0, False)
assert c["fs_circular"] == 1.3 and c["surface_check"] is True
# 비원호 M-P 1.15 · 15 % 안 → 판정 = M-P N.G · 면 확인 아님
c = _row(_case(1.3, 1.2, 1.15))["cases"]["wet"]["right"]
assert (c["method"], c["fs"], c["ok"], c["surface_check"]) == (
"morgenstern_price",
1.15,
False,
False,
)
# 비원호가 더 높음 → Bishop 원호 그대로(Spencer 원호 9.0 은 후보 아님)
row = _row(_case(1.3, 1.4, None))
c = row["cases"]["wet"]["right"]
assert (c["method"], c["kind"], c["fs"], c["ok"]) == ("bishop", "circle", 1.3, True)
assert row["surface_check"] is False and row["verdict"]["wet"] == "ok"
# 지배 줄 · 요약도 판정 방법 · 면 확인 수를 따름
row = _row(_case(1.3, 1.0))
assert view.governing_row(row)["method"] == "spencer"
assert view.summarize([row], ["wet"])["wet"] == {
"min_fs": 1.0,
"ng": 1,
"surface_check": 1,
"edge": 0,
}
def test_old_result_without_noncircular_keeps_bishop():
# 옛 결과(원호 Bishop min 만) → 판정 그대로
case = {
"condition": "wet",
"side": "right",
"role": "cut",
"result": {"methods": {"bishop": {"min": {"F": 1.25, "kind": "circle"}}}},
}
c = _row(case)["cases"]["wet"]["right"]
assert (c["method"], c["fs"], c["ok"], c["surface_check"]) == ("bishop", 1.25, True, False)
def test_edge_flag():
# 판정 면 진입 −30 · 진출 2 — 모형 끝 −30.5 면 1 m 안 = edge · −40 이면 아님 · 모형 끝 모름 = None
for ends, edge in (([-30.5, 40.0], True), ([-40.0, 40.0], False), (None, None)):
row = _row(_case(1.3, ends=ends))
assert row["cases"]["wet"]["right"]["edge"] is edge
assert view.summarize([_row(_case(1.3, ends=[-30.5, 40.0]))], ["wet"])["wet"]["edge"] == 1
def test_section_view_critical_is_governing_surface():
# 단면 화면 임계면 = 판정 면(방법 이름도) — 후보 고리가 방법 이름을 덮지 않음
case = _case(1.3, 1.0, 1.05)
out = {"cases": [case]}
row = _row(case)
model = {"materials": [], "regions": [], "surface": []}
a = view.section_view(row, out, model, {})["analyses"][0]
assert (a["critical"]["method"], a["critical"]["fs"]) == ("spencer", 1.0)