Files
Aislo/resources/tester/test_b06_curve_widening.py
T
eomsangdonandClaude Opus 5 0ef32b5279 chore(tester): 시험을 resources/tester/ 로 옮김 — 창끼리 건너가게
⚠ **뿌리** — `tmp/` 는 창 사이에 안 건너감(실측 확인: 상대 창이 놓은 `tmp/_sync_probe.txt`
가 시간을 두고 두 번 봐도 안 보임). 그래서 **정본(등록부 스키마)만 건너가고 그것을 읽는
시험은 안 건너가** 오늘 두 번, 같은 시험이 **연 창은 통과·받은 창은 실패**가 됐음.

- `tmp/tests/*` 를 `resources/tester/` 로 **복사**(127 파일). 내용은 **한 줄도 안 고침**
- `tmp/tests` 는 **남겨 둠** — 되돌릴 자리(사용자 지시)
- 실행: `./venv/Scripts/python.exe -m pytest resources/tester/ -q`
  옮기기 전과 **같은 수**: 617 통과 / 22 건너뜀 / 실패 0

⇒ 이제 시험·예외·까닭이 **정본과 함께** 움직임. 오늘 세운 「예외는 정본 스키마에」와 짝임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 17:12:30 +09:00

151 lines
5.7 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.
"""곡선부 확폭 — 표·경계·편측 적용·상한을 확인한다(2026-09-06).
파이썬과 브라우저가 한 세트이므로 같은 기대값을 `test_b06_curve_widening.mjs` 도 쓴다.
값이 갈리면 두 화면이 다른 단면을 그린다.
"""
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
import numpy as np # noqa: E402
from B05_Profile.B05_Profile_Engine_Sections_Core import _plan_radii # noqa: E402
from B06_Section.B06_Section_Engine_Design import compute_cross_design # noqa: E402
from config.config_system_design import curve_widening_m # noqa: E402
# (반경 m, 기대 확폭 m) — 별표2 .2.나.(4). 경계는 "이상 ~ 미만".
TABLE_CASES = [
(9.9, 0.0),
(10.0, 2.25),
(12.999, 2.25),
(13.0, 2.0),
(14.0, 1.75),
(15.0, 1.5),
(18.0, 1.25),
(20.0, 1.0),
(25.0, 0.75),
(30.0, 0.5),
(40.0, 0.25),
(45.0, 0.0),
(200.0, 0.0),
]
def _flat_samples() -> list[dict]:
return [{"offset_m": float(o), "elevation_m": 100.0, "valid": True} for o in range(-25, 26)]
def test_widening_table_boundaries() -> None:
for radius, expected in TABLE_CASES:
assert curve_widening_m(radius) == expected, radius
assert curve_widening_m(None) == 0.0
def test_widening_applies_to_outer_side_only() -> None:
samples = _flat_samples()
base = compute_cross_design(samples, 100.0, ground_type="soil", section_mode="both_fill")
right = compute_cross_design(
samples,
100.0,
ground_type="soil",
section_mode="both_fill",
plan_radius_m=16.0,
curve_outer_side="right",
)
left = compute_cross_design(
samples,
100.0,
ground_type="soil",
section_mode="both_fill",
plan_radius_m=16.0,
curve_outer_side="left",
)
assert base["widening_left_m"] == 0.0 and base["widening_right_m"] == 0.0
assert right["widening_right_m"] == 1.5 and right["widening_left_m"] == 0.0
assert left["widening_left_m"] == 1.5 and left["widening_right_m"] == 0.0
# 확폭은 붙은 쪽 차도 끝만 밖으로 민다.
assert right["carriageway_edges"]["left"] == base["carriageway_edges"]["left"]
assert right["carriageway_edges"]["right"]["offset_m"] < base["carriageway_edges"]["right"]["offset_m"]
assert right["carriageway_width_m"] == base["carriageway_width_m"] + 1.5
def test_widening_capped_by_legal_max_width() -> None:
"""확폭을 더한 유효너비는 5m 를 넘지 않는다(별표2 — 최대 5미터까지)."""
samples = _flat_samples()
design = compute_cross_design(
samples,
100.0,
ground_type="soil",
section_mode="both_fill",
plan_radius_m=11.0, # 표값 2.25m
curve_outer_side="right",
)
assert design["carriageway_width_m"] <= 5.0 + 1e-9
# 규격 3.0m 이면 2.0m 까지만 붙는다.
assert design["widening_right_m"] == 2.0
def test_plan_radius_and_outer_side_from_polyline() -> None:
"""반지름 50m 원호에서 반경 50m 가 나오고, 좌회전이면 바깥은 우측이다."""
angles = np.linspace(0.0, np.pi / 2, 400)
radius = 50.0
left_turn = np.column_stack([radius * np.cos(angles), radius * np.sin(angles)])
chainage = np.r_[
0.0, np.cumsum(np.hypot(np.diff(left_turn[:, 0]), np.diff(left_turn[:, 1])))
]
radii, sides = _plan_radii(left_turn, chainage, np.array([40.0]), float(chainage[-1]))
assert abs(radii[0] - radius) < 0.1
assert sides[0] == "right"
right_turn = np.column_stack([radius * np.cos(-angles), radius * np.sin(-angles)])
chainage2 = np.r_[
0.0, np.cumsum(np.hypot(np.diff(right_turn[:, 0]), np.diff(right_turn[:, 1])))
]
_, sides2 = _plan_radii(right_turn, chainage2, np.array([40.0]), float(chainage2[-1]))
assert sides2[0] == "left"
def test_straight_route_has_no_radius() -> None:
line = np.array([[0.0, 0.0], [300.0, 0.0]])
radii, sides = _plan_radii(line, np.array([0.0, 300.0]), np.array([50.0, 150.0]), 300.0)
assert radii == [None, None]
assert sides == [None, None]
def test_widening_taper_runs_before_and_after_curve() -> None:
"""곡선 앞뒤 10m 안 측점은 확폭이 0 → W 로 이어진다(2026-09-06)."""
from B05_Profile.B05_Profile_Engine_Sections_Core import _curve_widenings
# 5m 간격 측점: 20~30m 구간만 곡선(R=16 → 1.5m), 나머지는 직선.
chainage = np.arange(0.0, 55.0, 5.0)
radii: list[float | None] = [None] * len(chainage)
sides: list[str | None] = [None] * len(chainage)
for index, value in enumerate(chainage):
if 20.0 <= value <= 30.0:
radii[index] = 16.0
sides[index] = "right"
widenings, out_sides = _curve_widenings(chainage, radii, sides)
by_chainage = dict(zip(chainage.tolist(), widenings, strict=True))
# 곡선 안은 표값 그대로.
assert by_chainage[20.0] == 1.5 and by_chainage[25.0] == 1.5 and by_chainage[30.0] == 1.5
# 앞뒤 10m 는 선형으로 줄어든다 — 5m 지점에서 절반.
assert by_chainage[15.0] == 0.75 and by_chainage[35.0] == 0.75
assert by_chainage[10.0] == 0.0 and by_chainage[40.0] == 0.0
# 테이퍼 측점도 확폭이 붙는 쪽을 물려받는다.
assert out_sides[chainage.tolist().index(15.0)] == "right"
# 테이퍼 밖은 방향이 없다.
assert out_sides[chainage.tolist().index(5.0)] is None
def test_widening_without_curve_stays_zero() -> None:
from B05_Profile.B05_Profile_Engine_Sections_Core import _curve_widenings
chainage = np.arange(0.0, 60.0, 20.0)
widenings, sides = _curve_widenings(chainage, [None] * 3, [None] * 3)
assert widenings == [0.0, 0.0, 0.0]
assert sides == [None, None, None]