Files
Aislo/B08_Quantity/B08_Quantity_Engine_Hydro.py
T
eomsangdonandClaude Opus 5.5 dc2c27313c feat(B08): 수리집수면적표 박스암거 사각 단면 장 · 세월교 · 물넘이 월류 높이 장(73-11 · 73-12)
- 박스 = 내공 B · H · 수심비(기본 0.8) · n(기본 0.013) · A = B·h · P = B + 2h · Manning · Qd 판정
- 세월교 · 물넘이포장 = 월류 폭(기본 10 m) · 경사(1/10) · n(0.017) → 월류 높이 자동(ford_depth)
- ford_depth = fordHeightM 파이썬 짝(두 파일 머리 · 거울 시험 test_73_11) · 관 몫 안 뺌 · 여유 없음
- [저장]에 새 칸 · 빈 장 없앰 · 물넘이포장은 관 장에서 옮김

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W4uKaV4ZBhsJN4GHG7yRj4
2026-10-07 09:09:25 +09:00

140 lines
6.0 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") or SHEET_DEFAULTS["box"]["depth_ratio"]
if width and height:
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,
}