"""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, }