- 화면 글 10 자리(8 파일) — 세월교 힌트 · B08 재료 엑셀 알림 · B09 내역 수기 줄 까닭(거울 둘) · 산근 쓰는 곳 · 단가 힌트 · 총괄 수기 입력 칸 · 로케일 둘 - 저장 키 · 주석 · 손료는 그대로 · 로케일 줄 수 그대로 - 걸린 시험 셋 글 맞춤 · 새 시험 test_171_1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
117 lines
4.1 KiB
Python
117 lines
4.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""횡단배수 › 세월교(표 04) 명세 + 월류 폭 ↔ 높이 연동 — PLAN 52-4.
|
||
|
||
순수 규칙은 Node 헬퍼(`helper_52_4_ford.cjs`)가 돌림 · 화면은 ORCA `ui_checks/52-4_세월교_컨테이너.js`.
|
||
높이 거울 = 이 파일이 Manning(통수단면.md §2 · n 0.017 · S 0.10)을 따로 풀어 같은 값인지 봄.
|
||
"""
|
||
|
||
import json
|
||
import math
|
||
import os
|
||
import subprocess
|
||
|
||
import pytest
|
||
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
ROOT = os.path.dirname(os.path.dirname(HERE))
|
||
N, S = 0.017, 0.10
|
||
|
||
|
||
def _node(*args):
|
||
proc = subprocess.run(
|
||
["node", os.path.join(HERE, "helper_52_4_ford.cjs"), *args],
|
||
capture_output=True,
|
||
text=True,
|
||
encoding="utf-8",
|
||
timeout=60,
|
||
)
|
||
assert proc.returncode == 0, proc.stderr
|
||
return json.loads(proc.stdout)
|
||
|
||
|
||
@pytest.fixture(scope="module")
|
||
def out():
|
||
return _node()
|
||
|
||
|
||
def test_컨테이너_셋_모두_필수(out):
|
||
assert out["containers"] == [
|
||
["ford", "세월교", True],
|
||
["inlet", "유입 구조", True],
|
||
["outlet", "유출 구조", True],
|
||
["soil_add", "흙막이", False],
|
||
["chute_add", "도수로/돌붙임", False],
|
||
]
|
||
# 수량(련) = 관경 바로 뒤(65-4)
|
||
assert out["items"][0] == [
|
||
["diameter", "select", "pipe_diameter_mm"],
|
||
["count", "number", "pipe_count"],
|
||
["width", "number", "ford_width_m"],
|
||
["height", "number", "ford_height_m"],
|
||
]
|
||
assert [c[0] for c in out["choices"][:3]] == [
|
||
["800", "1000", "1200", "1500"],
|
||
["없음", "날개벽", "흙막이"],
|
||
["없음", "날개벽", "흙막이"],
|
||
]
|
||
|
||
|
||
def test_옛_날개벽_칸으로_구조를_세우고_같은_키로_씀(out):
|
||
assert out["legacy"] == {"inlet": "날개벽", "outlet": "없음"}
|
||
assert out["keepSoil"] == "흙막이" # 흙막이는 설치 칸 없음이어도 그대로
|
||
assert out["b06Off"] == "없음" and out["b06On"] == "날개벽" # B06 조정창 설치 칸을 따름
|
||
back = out["back"]
|
||
assert back["pipe_diameter_mm"] == 1000 and back["pipe_count"] == 2
|
||
assert back["ford_width_m"] == 10 and back["ford_height_m"] == 0.3
|
||
assert back["wing_in"] == "있음" and back["wing_out"] == "없음"
|
||
# 표 04 에 없는 옛 칸은 폼이 안 적음(병합이 이어 붙임 = 지난 정의 값)
|
||
assert "pipe_kind" not in back and "wing_in_height_m" not in back
|
||
assert "pipe_kind" in out["retired"]
|
||
|
||
|
||
def test_흙막이_날개벽_고르면_옛_설치_칸도_맞춰_씀(out):
|
||
s = out["soilOut"]
|
||
assert s["inlet_type"] == "흙막이" and s["wing_in"] == "없음"
|
||
assert s["outlet_type"] == "날개벽" and s["wing_out"] == "있음"
|
||
|
||
|
||
def test_잇기_키_컨테이너마다(out):
|
||
assert out["links"] == ["gford", "gfordin", "gfordout", "gsoiladd1", "gchuteadd1"]
|
||
|
||
|
||
def _manning_depth(q, b):
|
||
"""넓은 직사각 수로 Manning 을 h 에 대해 이분법으로 풂 — 헬퍼와 다른 풀이."""
|
||
|
||
def cap(h):
|
||
return b * h * (1 / N) * ((b * h) / (b + 2 * h)) ** (2 / 3) * math.sqrt(S)
|
||
|
||
lo, hi = 0.0, 5.0
|
||
for _ in range(100):
|
||
mid = (lo + hi) / 2
|
||
lo, hi = (mid, hi) if cap(mid) < q else (lo, mid)
|
||
return hi
|
||
|
||
|
||
def test_월류_높이는_폭과_연동_필요_수심_올림(out):
|
||
for q, b, h in out["table"]:
|
||
need = _manning_depth(q, b)
|
||
assert h == pytest.approx(math.ceil(need * 100 - 1e-9) / 100), (q, b)
|
||
# 폭이 넓을수록 높이가 낮아짐
|
||
row = [h for q, b, h in out["table"] if q == 2.54]
|
||
assert row == sorted(row, reverse=True) and len(set(row)) == len(row)
|
||
assert out["none"] == [None, None] # 설계유량 · 폭이 없으면 손 입력
|
||
assert out["hints"] == [
|
||
"필요 수심 0.08 이상",
|
||
"월류 폭을 넣으면 셈",
|
||
"설계유량 없음 — 수기 입력",
|
||
]
|
||
|
||
|
||
def test_정의_파일에_세월교_이름_표가_올라_있음():
|
||
names = _node("names")
|
||
with open(
|
||
os.path.join(ROOT, "resources", "master_template", "구조물정의.json"), encoding="utf-8"
|
||
) as f:
|
||
doc = json.load(f)
|
||
assert doc["종류"]["ford_bridge"]["이름"] == names
|