Merge remote-tracking branch 'origin/sub_laptop_4' into main_laptop_1

This commit is contained in:
2026-10-18 13:27:07 +09:00
6 changed files with 127 additions and 13 deletions
@@ -150,8 +150,8 @@ def _route(name: str, earth: float, station: int = 0):
def test_choose_profiles_only_shortlist() -> None:
"""종단 반영 켬 토공 = 노선 횡단 토공(줄 세움) 위 `SHORTLIST` 개만 균형 종단 · 그 사이에서 고름 ·
줄 세움 = `earth`(켬 = 상한 누른 계획선 · 끔 = 지반고) · 없으면 띠 가늠."""
"""종단 반영 켬 토공 = 노선 횡단 토공(줄 세움) 위 `SHORTLIST` 개만 균형 종단 · 그 사이에서
고름 · 줄 세움 = `earth`(켬 = 상한 누른 계획선 · 끔 = 지반고) · 없으면 띠 가늠."""
routes = [_route(f"r{i}", 100.0 + 10 * i) for i in range(12)] + [_route("off", 1.0, 1)]
last = f"r{SHORTLIST - 1}"
profiled = []
@@ -212,3 +212,67 @@ def test_hill_branches_rules(hill_runs: dict, mode: str) -> None:
assert over["total"] == over["radius"] + over["stream"] + over.get("station", 0)
assert "unsnapped" not in result
assert result["ends_over_limit"] is False and result["ends_grade_pct"] < 14.0
def _hill_expected():
x = np.arange(0.0, 802.0, 2.0)
y = np.arange(0.0, 402.0, 2.0)
xx, yy = np.meshgrid(x, y)
hill = Terrain(x, y, 40.0 * np.exp(-((xx - 400.0) ** 2 + (yy - 200.0) ** 2) / 90.0**2), xx > -1)
expected = [(float(v), 200.0 + 40.0 * math.sin(v / 60.0)) for v in np.arange(50.0, 752.0, 4.0)]
return hill, expected
def test_419_2_pick_independent_of_cache_order() -> None:
"""같은 입력 — 앞서 다른 갈래를 보였든(캐시 후보에 정밀 종단) 처음이든 같은 선(419-2)."""
from B05_Profile.B05_Profile_Engine_RouteInitial import _POOLS
hill, expected = _hill_expected()
def run(mode):
return generate_initial_route(
expected, hill, [], mode=mode, weights=None, max_grade=0.14, min_radius_m=RADIUS
)
_POOLS.clear()
fresh = {mode: run(mode)["planned"] for mode in ("earthwork", "cost")}
_POOLS.clear()
for mode in ("gentle", "gradeline", "weighted", "cost", "earthwork", "gentle"):
run(mode)
again = {mode: run(mode)["planned"] for mode in ("earthwork", "cost")}
# 캐시 후보에 정밀 종단이 안 남음 — 고르기는 늘 빠른 판끼리(정밀은 낼 사본에만)
pool = next(iter(_POOLS.values()))
routes = [*pool.candidates, *pool.searched.values()]
assert not any((m.get("profile") or {}).get("exact") for _, m in routes)
_POOLS.clear()
assert again == fresh
def test_419_3_applied_profile_holds_grade() -> None:
"""[적용] 종단 — 종단곡선을 넣으려 꼭짓점을 밀어도 직선 · 곡선 어디서나 균형 종단 상한 안."""
from B05_Profile.B05_Profile_Engine_Grade_Alignment import (
AlignmentPolicy,
build_curves,
evaluate,
)
from B05_Profile.B05_Profile_Engine_Grade_Profile import pass_through
from B05_Profile.B05_Profile_Engine_RouteInitial_Profile import held_pass_through
policy = AlignmentPolicy.from_config(
station_interval_m=20.0, max_grade_pct=14.0, curve_skip_delta_pct=5.0, paved=False
)
s = np.arange(0.0, 401.0, 20.0)
grades = [0.14, 0.14, -0.02, 0.14, 0.14, 0.03, -0.14, -0.14, 0.0, 0.14]
grades += [0.10, 0.14, -0.05, -0.14, -0.14, 0.02, 0.14, 0.14, 0.0, -0.14]
plan = np.concatenate([[100.0], 100.0 + np.cumsum(np.asarray(grades) * 20.0)])
fine = np.linspace(0.0, 400.0, 4001)
def steepest(z):
curves, _ = build_curves(s, z, policy)
return float(np.abs(np.diff(evaluate(s, z, curves, fine)) / 0.1).max())
assert steepest(pass_through(s, plan, policy)) > 0.15 # 옛 맞춤 = 상한 넘음(재현)
held = held_pass_through(s, list(s), list(plan), policy)
assert steepest(held) <= 0.14 + 1e-5
assert steepest(np.round(held, 4)) <= 0.14 # 정본 0.1 mm 반올림 뒤도 상한 안
assert held[0] == pytest.approx(plan[0]) and held[-1] == pytest.approx(plan[-1])
@@ -6,6 +6,8 @@
import json
from pathlib import Path
import pytest
from B05_Profile.B05_Profile_Router_Replan import RouteReplanRequest
from B05_Profile.B05_Profile_Router_RouteInitial import _set_base_pvi
from B06_Section.B06_Section_Router_Design import CARRY_INPUT_KEYS
@@ -47,7 +49,8 @@ def test_변화점_계획고를_균형_종단으로(tmp_path: Path) -> None:
p["elevation_m"]
for p in json.loads(path.read_text("utf-8"))["profile_alignment"]["base_pvi"]
]
assert got == [10.0, 12.0, 13.0, 14.0] # 구조물 측점(30 m)은 직선 보간
# 구조물 측점(30 m)은 직선 보간 · 상한 누를 때 반올림 여유 0.1 mm 몫(419-3)
assert got == pytest.approx([10.0, 12.0, 13.0, 14.0], abs=5e-4)
def test_확인_요청_기본은_끔() -> None:
-4
View File
@@ -209,10 +209,6 @@ def _check_weight_monotone(run) -> None:
assert tops[0] <= min(tops) + 1e-6, tops # 끝 0/0/100 = 기울기 순한
@pytest.mark.xfail(
reason="엔진 pick 이 정밀 · 빠른 판 종단을 섞어 견줌 → 캐시 순서 의존 · PLAN 419 에서 풂",
strict=False,
)
def test_weight_ends_match_branches(hill: Terrain) -> None:
_check_weight_ends(lambda mode, weights=None: _run(hill, mode, weights=weights))