- 박스 = 내공 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
140 lines
6.0 KiB
Python
140 lines
6.0 KiB
Python
"""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,
|
||
}
|