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

This commit is contained in:
2026-09-14 22:56:28 +09:00
3 changed files with 305 additions and 2 deletions
@@ -0,0 +1,83 @@
"""㉳ (가) 미교차 측점 지반 샘플을 지표면 끝까지 넓힘 — 2026-09-14 브레인 판정.
반폭 20m 끝에서 성토 비탈이 원지반과 안 만나면 면적이 잘려 성토가 작게 섰음(936be972 12곳 중 11곳).
⇒ 열린 쪽만 +5m 씩 확정 지표면에서 다시 떠 계산 · 닫히면 멈춤 · 지표면 밖(무효 샘플)이면 멈추고 미교차 그대로.
상한 숫자 없음(지표면이 실제로 있는 끝) · 716 「미교차만 +5m 한 번」 갈음.
"""
from __future__ import annotations
import sys
from pathlib import Path
import numpy as np
import pytest
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from B06_Section.B06_Section_Engine_Design import compute_cross_design # noqa: E402
from B06_Section.B06_Section_Engine_SampleExtend import extend_unclosed # noqa: E402
from common_util.common_util_surface_sampler import CallableSurfaceSampler # noqa: E402
FRAME = {"origin": {"x": 0.0, "y": 0.0}, "left_xy": [1.0, 0.0]}
def _ground(x: np.ndarray) -> np.ndarray:
"""좌(+)는 30m 까지 1:1 로 떨어지다 완만 · 우(−)는 오르막(절토가 금방 닫힘)."""
x = np.asarray(x, dtype=float)
left = np.where(x < 30.0, 100.0 - x, 70.0 - 0.1 * (x - 30.0))
return np.where(x >= 0, left, 100.0 - 0.8 * x)
def _samples(half: float = 20.0) -> list[dict]:
offsets = np.arange(-half, half + 0.25, 0.5)
return [
{"offset_m": float(o), "elevation_m": float(z), "valid": True}
for o, z in zip(offsets, _ground(offsets))
]
def _compute(samples: list[dict]) -> dict:
return compute_cross_design(samples, 100.0, ground_type="soil", section_mode="right_cut")
def _sampler(limit: float | None = None) -> CallableSurfaceSampler:
def function(xy: np.ndarray) -> np.ndarray:
values = _ground(xy[:, 0])
if limit is not None:
values = np.where(xy[:, 0] > limit, np.nan, values)
return values
return CallableSurfaceSampler(function)
def test_열린_쪽만_5m_씩_넓혀_닫히면_멈춤() -> None:
before = _compute(_samples())
assert before["slope_unclosed"], "기준 단면이 20m 에서 안 닫혀야 시험이 뜻이 있음"
samples, info = extend_unclosed(_samples(), FRAME, _compute, _sampler())
after = _compute(samples)
assert not after["slope_unclosed"]
assert info["left_m"] > 0 and info["left_m"] % 5 == 0 and info["right_m"] == 0
assert info["surface_end"] is False
assert max(s["offset_m"] for s in samples) == pytest.approx(20.0 + info["left_m"])
assert min(s["offset_m"] for s in samples) == pytest.approx(-20.0) # 닫힌 쪽은 안 넓힘
assert after["fill_area_m2"] > before["fill_area_m2"] # 잘린 성토가 제 크기로
def test_지표면_끝이면_멈추고_미교차_그대로() -> None:
samples, info = extend_unclosed(_samples(), FRAME, _compute, _sampler(limit=32.0))
assert info["surface_end"] is True
assert max(s["offset_m"] for s in samples) == pytest.approx(30.0) # 무효가 섞인 걸음은 안 붙임
assert _compute(samples)["slope_unclosed"]
def test_닫힌_측점은_그대로() -> None:
closed = _samples(60.0)
assert extend_unclosed(closed, FRAME, _compute, _sampler()) is None
def test_서버_재계산이_부름() -> None:
source = (ROOT / "B06_Section" / "B06_Section_Server_Calc_Prebuild.py").read_text("utf-8")
assert "extend_unclosed_sections(" in source