Merge remote-tracking branches 'origin/sub_laptop_7' and 'origin/sub_laptop_8' into main_laptop_1
This commit is contained in:
@@ -175,9 +175,10 @@ def test_acads_3b_weak_seam_specified_polyline():
|
||||
|
||||
def test_acads_3a_weak_seam_noncircular_search():
|
||||
# S2VM #7 (ACADS 3a) — 블록 탐색 Janbu 보정 1.275 · Spencer 1.258(심판값 1.24~1.27)
|
||||
# 비원호 다출발(567-1)이 약층 바닥을 따르는 더 낮은 면을 찾음(Spencer 1.217 · −3.3 %) → ±5 %
|
||||
r = analyze(_acads3_model(), direction=-1, methods=("janbu", "spencer"))
|
||||
near(nonc(r, "janbu"), 1.275)
|
||||
near(nonc(r, "spencer"), 1.258)
|
||||
near(nonc(r, "spencer"), 1.258, 0.05)
|
||||
|
||||
|
||||
def _acads4_model():
|
||||
@@ -213,8 +214,8 @@ def _acads4_model():
|
||||
def test_acads_4_inclined_seam_water_surcharge():
|
||||
# S2VM #9 — 최적화 비원호 Spencer 0.707 · Janbu 보정 0.699 (약층 좌표 = 저해상 그림 판독 — 출처 ±5 % 권함)
|
||||
r = analyze(_acads4_model(), direction=-1, methods=("janbu", "spencer"))
|
||||
near(nonc(r, "spencer"), 0.707)
|
||||
near(nonc(r, "janbu"), 0.699)
|
||||
near(nonc(r, "spencer"), 0.707, 0.05)
|
||||
near(nonc(r, "janbu"), 0.699, 0.05)
|
||||
|
||||
|
||||
FK_EXT = [[0, 0], [180, 0], [180, 20], [140, 20], [60, 60], [0, 60]]
|
||||
|
||||
@@ -81,9 +81,9 @@ def test_mp_search_slide2():
|
||||
|
||||
|
||||
def test_mp_acads4_noncircular():
|
||||
# #9 블록 + 최적화 GLE 0.683 (약층 좌표 = 그림 판독 → ±2 %)
|
||||
# #9 블록 + 최적화 GLE 0.683 (약층 좌표 = 그림 판독 · 비원호 다출발이 더 낮은 면을 찾음 → ±5 %)
|
||||
r = analyze(_acads4_model(), direction=-1, methods=("janbu", "spencer", MP))
|
||||
near(nonc(r, MP), 0.683, 0.02)
|
||||
near(nonc(r, MP), 0.683, 0.05)
|
||||
|
||||
|
||||
def _closure(rows, lam, f_last=1.0):
|
||||
|
||||
@@ -446,7 +446,7 @@ process.stdout.write(JSON.stringify({
|
||||
zAt: [m.zAt([[0, 0], [10, 10]], 2.5), m.zAt([[10, 10], [0, 0]], -5)],
|
||||
stride: [m.labelStride(100, 64), m.labelStride(40, 64), m.labelStride(10, 64), m.labelStride(0.1, 64)],
|
||||
labels: [1, 2, 5].map((k) => m.planLabelKeys(input.rows, 20, k, null)),
|
||||
labelsActive: m.planLabelKeys(input.rows, 20, 5, "e"),
|
||||
labelsActive: [m.planLabelKeys(input.rows, 20, 5, "e"), m.planLabelKeys(input.rows, 20, 5, "b")],
|
||||
}));
|
||||
"""
|
||||
|
||||
@@ -580,7 +580,7 @@ def test_logic_TS():
|
||||
# 평면 띠 측점 글 솎음 — 간격 px × 걸음 ≥ 64 · 본 측점 중 걸음 배수 · 고른 측점은 늘
|
||||
assert out["stride"] == [1, 2, 10, 100]
|
||||
assert out["labels"] == [["a", "b", "c", "d", "e"], ["a", "c", "e"], ["a"]]
|
||||
assert out["labelsActive"] == ["a", "e"]
|
||||
assert out["labelsActive"] == [["a", "e"], ["b"]] # 고른 측점 반 걸음(50 m) 안 「a」 뺌
|
||||
|
||||
|
||||
def test_엔진_지문은_코드_내용():
|
||||
|
||||
@@ -217,3 +217,38 @@ def test_ground_order_and_label_map(tmp_path):
|
||||
assert ids == ["fill", "weathered_soil", "weathered_rock", "soft_rock"]
|
||||
mapped = grid_engine.strata_from_grid(grid, label_map={"soil": "colluvium", "fill": "fill"})
|
||||
assert [m["id"] for m in mapped.materials][:2] == ["fill", "colluvium"]
|
||||
|
||||
|
||||
def _embankment(run_m, gamma, phi, c, half=20.0):
|
||||
"""Scoops3D 검증 제방(H 10 m) — 탐색 상자 없이 격자 전체 · y ±20 m."""
|
||||
x = np.arange(-25, run_m + 35 + 1e-9, 1.0)
|
||||
y = np.arange(-half, half + 1e-9, 1.0)
|
||||
xx, _ = np.meshgrid(x, y)
|
||||
z = np.clip(xx / run_m * 10.0, 0, 10.0)
|
||||
mat = {"id": "m", "name": "m", "gamma_t": gamma, "gamma_sat": gamma, "c": c, "phi": phi}
|
||||
return Strata(x, y, z, np.zeros((0,) + z.shape), [mat])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("run_m", "gamma", "phi", "c", "expected"),
|
||||
[
|
||||
(10 / math.tan(math.radians(30)), 19, 20, 8.64, 1.32), # 표 6.8 제방 30°
|
||||
(20.0, 20, 19.6, 3.0, 1.04), # Donald & Giam 1a
|
||||
(10 / math.tan(math.radians(60)), 19, 20, 8.64, 0.75), # 표 6.11 제방 60°
|
||||
],
|
||||
)
|
||||
def test_default_settings_reproduce_scoops3d(run_m, gamma, phi, c, expected):
|
||||
"""567-2 — 실사용 기본 설정 그대로(노드 2 m · 거친 4 배 · 높이 2~30 m · dr 1 m · 부피 10~10,000 m³ ·
|
||||
구 + 타원체) · 좁힌 상자 없이 → 문헌값 ±2 %(실측 +0.0 · −0.1 · −0.2 %)."""
|
||||
best = search(_embankment(run_m, gamma, phi, c), SearchParams(workers=8)).best
|
||||
assert best["governing"] == pytest.approx(expected, rel=0.02)
|
||||
assert best["columns"] == len(best["cells"]) >= 100 and best["few_columns"] is False
|
||||
|
||||
|
||||
def test_few_columns_warning():
|
||||
"""567-2 — 임계 활동체 기둥 < 100(Scoops3D limcol) → few_columns 경고."""
|
||||
st = _embankment(10 / math.tan(math.radians(60)), 19, 20, 8.64, half=8.0)
|
||||
p = SearchParams(box=(-9, -3, -1, 1), h_min=4, h_max=10, v_min=10, v_max=60, ratios=(1.0,))
|
||||
r = search(st, p)
|
||||
assert r.best["columns"] < 100 and r.best["few_columns"] is True
|
||||
assert r.stats["columns"] == r.best["columns"] and r.stats["few_columns"] is True
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""536-2 — E07 광역 자연사면 위험 `E07_StabArea_Engine` · `_Router`.
|
||||
|
||||
무한사면 해석해(건조 · 포화 · 점착) · SINMAP 안정 지수(닫힌 꼴 = 표본 · 등급 경계) ·
|
||||
D8 집수면적 견본(기운 평면 · V 골짜기) · 습윤지수 · SI 하한 / 상한 해석해 · 시나리오 둘(표준 · 최악) ·
|
||||
D∞ 집수면적 견본(기운 평면 · V 골짜기 · 면 몫 · 돌린 평면) · 습윤지수 · SI 하한 / 상한 해석해 · 시나리오 둘(표준 · 최악) ·
|
||||
가짜 복사본으로 띠 · 측점표 · 저장본 · 지문.
|
||||
"""
|
||||
|
||||
@@ -84,6 +84,22 @@ def test_D8_집수면적_기운평면_V골짜기():
|
||||
assert np.all(a[4, :-1] < a[4, 1:]) # 골 따라 늘어남
|
||||
|
||||
|
||||
def test_Dinf_면_몫_돌린평면():
|
||||
"""567-2 D∞(Tarboton 1997 · SINMAP 원본) — 면 안 22.5° 흐름 = 곁 · 대각 반씩 · 30° 돌린 평면
|
||||
집수면적 = 흐름길 길이(D8 줄무늬 표준편차 0.16 → D∞ 0.05)."""
|
||||
cell = 2.0
|
||||
ii, jj = np.indices((5, 5))
|
||||
r1, r2, f1 = engine.dinf_receivers(-(jj - math.tan(math.pi / 8) * ii) * cell, cell)
|
||||
assert (r1[2, 2], r2[2, 2]) == (2 * 5 + 3, 1 * 5 + 3) and f1[2, 2] == pytest.approx(0.5)
|
||||
n, th = 60, math.radians(30)
|
||||
i, j = np.indices((n, n))
|
||||
z = 500 - (j * math.cos(th) + i * math.sin(th)) * cell * 0.5
|
||||
length = np.minimum((j + 0.5) / math.cos(th), (i + 0.5) / math.sin(th)) * cell
|
||||
inner = (i > 5) & (j > 5) & (i < n - 5) & (j < n - 5)
|
||||
ratio = engine.catchment_area(z, cell)[inner] / length[inner]
|
||||
assert np.std(ratio) < 0.08 and np.median(np.abs(ratio - 1)) < 0.2
|
||||
|
||||
|
||||
def test_습윤지수():
|
||||
beta = np.radians(np.array([30.0, 30.0, 0.0]))
|
||||
a = np.array([100.0, 1e6, 50.0])
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
# -*- 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
|
||||
Reference in New Issue
Block a user