Files
Aislo/resources/tester/test_530_2_plan_analyze.py
T
eomsangdonandClaude Opus 5.5 83d38681df fix(530-2): D 분석 지점 0.1 mm 반올림 — 노선 끝점 = 지점 · 연도 경계 정확히 맞물림 · 복도 300 m 실측 근거 주석
- 엔진 출력 자리수(4)로 지점을 맞춰 넣음 · 저장본 지점 대조 1 mm
- 실측(검증_sub3 근처 두 구간): 복도 100 · 200 · 300 m 못 지킨 곳 5 · 8 · 1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:47:30 +09:00

112 lines
4.4 KiB
Python

"""D 예정노선 2단계 분석(PLAN 530-2) — 합성 지형 두 구간 · 경계 · 누가 · 저장본 · 복도 인자.
./venv/Scripts/python.exe -m pytest resources/tester/test_530_2_plan_analyze.py -q
"""
import numpy as np
import pytest
from B05_Profile.B05_Profile_Engine_RouteInitial import _POOLS, Terrain
from B05_Profile.B05_Profile_Engine_RouteInitial_Search import CORRIDOR_M, SearchGrid
from D03_PlanAnalysis import D03_PlanAnalysis_Analyze as analyze
CRITERIA = {"max_grade": 0.14, "min_radius_m": 12.0, "design_speed": 20}
SEGMENTS = [
{"year": 2026, "start": (60.0, 200.0), "end": (560.123456789, 230.987654321)},
{"year": 2027, "start": (560.123456789, 230.987654321), "end": (1040.0, 160.0)},
]
@pytest.fixture(scope="module")
def hill() -> Terrain:
x = np.arange(0.0, 1102.0, 2.0)
y = np.arange(0.0, 402.0, 2.0)
xx, yy = np.meshgrid(x, y)
z = 30.0 * np.exp(-((xx - 300.0) ** 2 + (yy - 210.0) ** 2) / 80.0**2) + 0.02 * xx
return Terrain(x, y, z, np.ones(xx.shape, dtype=bool))
@pytest.fixture
def run(tmp_path, hill):
_POOLS.clear()
calls = []
def loader(root, selection, models_dir, cache_dir):
calls.append((models_dir.name, cache_dir.name))
return hill, []
def go(criteria=CRITERIA, corridor_m=150.0):
return analyze.analyze_plan(
tmp_path, SEGMENTS, criteria, loader=loader, corridor_m=corridor_m
)
go.calls = calls
yield go
_POOLS.clear()
def test_boundary_chainage_and_line(run) -> None:
results = run()
# 연도 구간마다 제 DEM · 제 비용면 캐시
assert run.calls == [("models_2026", "cost_2026"), ("models_2027", "cost_2027")]
for seg, result in zip(SEGMENTS, results):
assert set(result["routes"]) == set(analyze.PLAN_MODES)
for route in result["routes"].values():
assert "error" not in route
# 시종점 그대로 — 연도 경계가 정확히 맞물림
assert route["planned"][0] == result["start"] == pytest.approx(seg["start"], abs=1e-4)
assert route["planned"][-1] == result["end"] == pytest.approx(seg["end"], abs=1e-4)
assert results[0]["routes"]["gradeline"]["planned"][-1] == results[1]["start"]
rows = analyze.chain(results, {2026: "earthwork", 2027: None}, 1234.5)
assert [r["mode"] for r in rows] == ["earthwork", "gradeline"]
assert rows[0]["start_chainage_m"] == 1234.5
assert rows[1]["start_chainage_m"] == rows[0]["end_chainage_m"]
for result, row in zip(results, rows):
length = result["routes"][row["mode"]]["metrics"]["length_m"]
assert row["end_chainage_m"] - row["start_chainage_m"] == pytest.approx(length, abs=1e-3)
line = analyze.joined_line(results, rows)
first = results[0]["routes"]["earthwork"]["planned"]
second = results[1]["routes"]["gradeline"]["planned"]
assert len(line) == len(first) + len(second) - 1 # 경계점 한 번만(꺾인 채)
assert line[len(first) - 1] == results[0]["end"] == results[1]["start"]
def test_same_input_reads_store(run, monkeypatch) -> None:
first = run()
assert [r["stored"] for r in first] == [False, False]
def boom(*args, **kwargs):
raise AssertionError("저장본이 있는데 다시 셈")
monkeypatch.setattr(analyze, "generate_initial_route", boom)
again = run()
assert [r["stored"] for r in again] == [True, True]
assert again[1]["routes"] == first[1]["routes"]
# 입력(복도 · 기준)이 바뀌면 다시 셈
with pytest.raises(AssertionError, match="다시 셈"):
run(corridor_m=120.0)
with pytest.raises(AssertionError, match="다시 셈"):
run(criteria={**CRITERIA, "max_grade": 0.12})
def test_corridor_argument_keeps_b_default(hill) -> None:
line = [SEGMENTS[0]["start"], SEGMENTS[0]["end"]]
base = SearchGrid(line, hill, [], stream_offset_m=10.0)
wide = SearchGrid(line, hill, [], stream_offset_m=10.0, corridor_m=180.0)
assert base.corridor_m == CORRIDOR_M == 100.0
assert wide.cells > base.cells
def test_criteria_from_project() -> None:
from B05_Profile.B05_Profile_Engine_Grade import (
legal_grade_limit_pct,
legal_plan_radius_min_m,
)
got = analyze.plan_criteria("work", 99) # 못 고르는 속도 → 종류 기본
assert got["design_speed"] == 20
assert got["max_grade"] == legal_grade_limit_pct("work", "special", False, 20) / 100.0
assert got["min_radius_m"] == legal_plan_radius_min_m(20, "special")