Files
Aislo/B08_Quantity/B08_Quantity_Engine_Hydro.py
T
eomsangdonandClaude Opus 5.5 14c3f01f11 fix(B08): 임의 · 실무 값 빈칸 — 관경 · 련 미입력 · 1본 프로젝트 값만 · 거창 계수 비율 미정 · 수심비 빈칸(143-2)
- 집계표 — 관경 · 련 빈칸 = 「미입력」 열(or 1000 · or 1 지움) · 관경 빈칸 흙막이 빈 높이 미입력 · 1본 = 프로젝트 층 변수만(시스템 층 폴백 지움)
- 토공집계표 — 성토면다짐 · 초류종자살포 · 지장목제거 = 빈칸 + 「비율 미정」 · 구조물 쪽 늘 빈 줄 8 뺌(나머지 7 유지)
- 수리 박스 — 수심비 처음 값 빈칸 · 빈칸이면 수심 · 유출량 멈춤 · 칸 옆 알림
- 좌측 패널 1본 읽기 전 빈칸 · 시험 test_69 · 98 · 122 · 73_11 고침 + 새 test_143_2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
2026-10-09 14:56:57 +09:00

140 lines
6.1 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.
"""B08 4탭 홍수량 계산서 엔진 — 순수 식 (PLAN 62-2 · 계산 자리 ③ 서버 단독).
배수유역 계산 코드(`common_util_drainage_detail` 등)를 부르지 않음 — 화면 입력만으로 계산.
1-1 합리식 Q = (1/3.6)·f·rt·A[㎢] · Qd = Q × 할증
1-2 도달시간 Kirpich T = 0.0663·L[㎞]^0.77·s^-0.385 [hr] · s = H/L · 최소 도달시간 적용
1-3 강우강도 rt = (R24/24)·(24/T)^0.557 (Mononobe형)
2 단면검토 V = (1/n)·R^(2/3)·S^(1/2) · Q = A·V · 판정 Q ≥ Qd · 유출율 = Q/Qd
장(sheet) pipe = 관 단면 · box = 사각 h = 수심비 × H · A = B·h · P = B + 2h (73-11)
ford = 세월교 · 물넘이 월류 높이 = 폭 · S · n 으로 Qd 를 흘리는 수심(73-12)
짝: `ford_depth` ↔ `A00_Common/design/design_structure_ford_calc.fordHeightM`
(`fordSection` 같은 풀이)
· 거울 시험 test_73_11_b08_hydro_box_ford · 관 몫은 빼지 않음 · 여유(0.9H) 안 넣음.
"""
from __future__ import annotations
import math
from typing import Any
from B08_Quantity.B08_Quantity_Hydro_Defaults import PIPE_KINDS, SHEET_DEFAULTS
MONONOBE_EXPONENT = 0.557
EFFECTIVE_RATIO = 0.7
def section_props(section: str, diameter_m: float) -> dict[str, float]:
"""단면 가정별 단면적 A · 윤변 P · 경심 R."""
d = diameter_m
full_area = math.pi * d * d / 4.0
if section == "arc240":
# 240° 부채꼴 + 현 아래 삼각형(반지름 둘 · 끼인각 120°) — 수심 0.75D
area = full_area * 240.0 / 360.0 + (d / 2.0) ** 2 * math.sin(math.radians(60)) * math.cos(
math.radians(60)
)
perimeter = math.pi * d * 240.0 / 360.0
return {"area": area, "perimeter": perimeter, "radius": area / perimeter}
if section == "eff70":
# 배관 제안 방식 — 유속은 만관 경심(D/4) · 통수단면은 70 %
return {"area": EFFECTIVE_RATIO * full_area, "perimeter": math.pi * d, "radius": d / 4.0}
return {"area": full_area, "perimeter": math.pi * d, "radius": d / 4.0}
def rect_props(width_m: float, depth_m: float) -> dict[str, float]:
"""사각 단면(천장 빼고) — A = B·h · P = B + 2h · R = A/P."""
area, perimeter = width_m * depth_m, width_m + 2.0 * depth_m
return {"area": area, "perimeter": perimeter, "radius": area / perimeter}
def ford_depth(flow_m3s: float, width_m: float, n: float, slope: float) -> float:
"""월류 폭(m)으로 유량을 흘리는 수심(m) · 0.01 m 올림 — Manning 개수로 반복 풀이."""
root = math.sqrt(slope)
def velocity(depth: float) -> float:
return (1 / n) * ((width_m * depth) / (width_m + 2 * depth)) ** (2 / 3) * root
depth = ((flow_m3s * n) / (width_m * root)) ** (3 / 5)
for _ in range(20):
nxt = flow_m3s / (width_m * velocity(depth))
if abs(nxt - depth) < 1e-6:
depth = nxt
break
depth = nxt
return math.ceil(depth * 100 - 1e-9) / 100
def _num(inputs: dict[str, Any], key: str) -> float | None:
value = inputs.get(key)
return None if value is None or value == "" else float(value)
def compute(inputs: dict[str, Any]) -> dict[str, Any]:
"""입력 → 계산서 값 전부(반올림 없음 · 화면이 자릿수를 정함).
빈칸(None)이 있으면 그 값에 기대는 칸만 None — 화면은 「—」 (69-6).
"""
length_m, height_m = _num(inputs, "length_m"), _num(inputs, "height_m")
r24, area_km2 = _num(inputs, "r24_mm"), _num(inputs, "area_km2")
slope_ground = tc_raw_hr = tc_hr = intensity = flow = design_flow = None
if length_m and height_m:
slope_ground = height_m / length_m
tc_raw_hr = 0.0663 * (length_m / 1000.0) ** 0.77 * slope_ground**-0.385
tc_hr = max(tc_raw_hr, float(inputs["tc_min_minutes"]) / 60.0)
if tc_hr and r24:
intensity = (r24 / 24.0) * (24.0 / tc_hr) ** MONONOBE_EXPONENT
if intensity is not None and area_km2:
flow = float(inputs["runoff"]) * intensity * area_km2 / 3.6
design_flow = flow * float(inputs["factor"])
sheet = inputs.get("sheet") or "pipe"
if sheet == "pipe":
n = _num(inputs, "n") or PIPE_KINDS[inputs["pipe_kind"]]
else:
n = _num(inputs, "n") or SHEET_DEFAULTS[sheet]["n"]
slope_den = _num(inputs, "slope_denominator")
slope_bed = 1.0 / slope_den if slope_den else None
props: dict[str, float] = {}
depth = None
if sheet == "pipe":
diameter_mm = _num(inputs, "diameter_mm")
props = section_props(inputs["section"], diameter_mm / 1000.0) if diameter_mm else {}
elif sheet == "box":
width, height = _num(inputs, "box_width_m"), _num(inputs, "box_height_m")
ratio = _num(inputs, "depth_ratio") # 빈칸 = 수심 · 단면 · 유출량 멈춤(143-2)
if width and height and ratio:
depth = ratio * height
props = rect_props(width, depth)
else:
width = _num(inputs, "overflow_width_m")
if width and slope_bed and design_flow:
depth = ford_depth(design_flow, width, n, slope_bed)
props = rect_props(width, depth)
velocity = capacity = None
if props and slope_bed:
velocity = (1.0 / n) * props["radius"] ** (2.0 / 3.0) * math.sqrt(slope_bed)
capacity = props["area"] * velocity
judged = capacity is not None and design_flow is not None
return {
"area_ha": area_km2 * 100.0 if area_km2 else None,
"r24_mm": r24,
"slope_ground": slope_ground,
"tc_raw_hr": tc_raw_hr,
"tc_hr": tc_hr,
"tc_floored": tc_hr is not None and tc_hr > tc_raw_hr,
"intensity": intensity,
"flow": flow,
"design_flow": design_flow,
"n": n,
"depth": depth,
"section_area": props.get("area"),
"perimeter": props.get("perimeter"),
"radius": props.get("radius"),
"slope_bed": slope_bed,
"velocity": velocity,
"capacity": capacity,
"ok": capacity >= design_flow if judged else None,
"ratio_pct": capacity / design_flow * 100.0 if judged and design_flow > 0 else None,
}