- B06 정본 키 둘(pipe_slope_pct · box_ground_slope_pct) — 관 길이와 같은 길(STRUCTURE_ROW_KEYS ↔ _AREA_KEYS ↔ CrossSectionPatch) · 주인 측점에만 - B08 수리 표 linked 에 slope_denominator = 100 / 경사 · 0 % 이하 · 짝 없음은 빈칸 · 세월교 · 물넘이는 장 기본값 그대로 - 수리 표 원천 표 「B06」 · 96 「하상경사 안 이음」 주석 고침 · 시험 test_110 새로 · 짝 시험 키 더함 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BVPp2o6Mh2FZJr72yW3ofZ
168 lines
7.2 KiB
Python
168 lines
7.2 KiB
Python
"""B08 4탭 홍수량 계산서 — 기본값 · 선택지 · B05 연결값 한 곳 (PLAN 62-3 · 73-11 · 73-12 · 96).
|
||
|
||
표준값(유출계수 · 할증 · 재현기간 · 최소 도달시간)은 B05 배수 설정(config DRAINAGE_*)과 같은 값.
|
||
측점 고유 값은 B05 배수유역 저장본의 연결값(linked · 96-1) · 하상경사는 B06 횡단 경사(110) ·
|
||
관 종류 · n 은 안 이음.
|
||
근거 후보: docs/raw/조사/2026-10-06_62-1_홍수량_방법.md (별표2 2.0배 · KDS 표 2.7-1 · 실무 견본).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
from common_util.common_util_drainage_pipes import carry_indices
|
||
from config.config_system import FORD_MANNING_N, FORD_SLOPE
|
||
from config.config_system_design import (
|
||
DRAINAGE_DESIGN_FLOW_FACTOR,
|
||
DRAINAGE_DESIGN_RETURN_PERIOD_YR,
|
||
DRAINAGE_RUNOFF_COEFFICIENT,
|
||
DRAINAGE_TC_MIN_MINUTES,
|
||
)
|
||
from M02_MasterTemplete.M02_Table_Fill import _nearest
|
||
|
||
#: 할증(Qd = Q × 할증) — 2.0 별표2 법정 · 1.5 견본(울진 2공구) · 1.2 구기준
|
||
FACTORS = (2.0, 1.5, 1.2)
|
||
|
||
#: 단면 가정 — 만관 · 240° 유효(수심 0.75D) · 유효 70 %(배관 제안 방식)
|
||
SECTIONS = {"full": "만관", "arc240": "240° 유효단면", "eff70": "유효 70 %"}
|
||
|
||
#: 관 종류별 조도계수 n — 파형강관은 KDS 표에 없음(지금 배관 제안 0.024)
|
||
PIPE_KINDS = {"파형강관": 0.024, "흄관": 0.013, "콘크리트관": 0.015}
|
||
|
||
#: 처음 값 = 표준 값만(69-6) · 측점 고유 값(유역면적 · L · H · D · S)은 None(빈칸)
|
||
#: R24 · 지역은 공사지 자료(B08_Quantity_Hydro_Site) — 자료가 없을 때만 화면 입력
|
||
DEFAULTS: dict[str, Any] = {
|
||
"factor": DRAINAGE_DESIGN_FLOW_FACTOR,
|
||
"runoff": DRAINAGE_RUNOFF_COEFFICIENT, # 유출계수 f — KDS 가파른 산지
|
||
"area_km2": None,
|
||
"length_m": None, # 산정에서 하천시점까지 거리
|
||
"height_m": None, # 산정에서 하천시점까지 표고차
|
||
"tc_min_minutes": DRAINAGE_TC_MIN_MINUTES, # 최소 도달시간(KDS 2.4)
|
||
"r24_mm": None,
|
||
"region": "",
|
||
"return_period": DRAINAGE_DESIGN_RETURN_PERIOD_YR,
|
||
"pipe_kind": "파형강관",
|
||
"n": None, # None = 관 종류 값
|
||
"diameter_mm": None,
|
||
"slope_denominator": None, # 하상경사 S = 1 / 이 값
|
||
"section": "full",
|
||
}
|
||
|
||
|
||
#: 시설 → 계산서 장(73-11 · 73-12) — 박스 = 사각 단면 · 세월교 · 물넘이 = 월류 높이 · 그 밖 = 관
|
||
SHEETS = {
|
||
"box_culvert": ("box", "박스암거"),
|
||
"ford_bridge": ("ford", "세월교"),
|
||
"ford_pavement": ("ford", "물넘이포장"),
|
||
}
|
||
|
||
#: 장별 처음 값 — 박스 수심비 0.8H · n 0.013(울진 산출서 · 조사 69-7) · 내공 · 경사는 빈칸
|
||
#: 세월교 · 물넘이 월류 폭 10 m(사용자 10-7) · n · 경사 = B05 세월교 높이 계산과 같은 값
|
||
SHEET_DEFAULTS: dict[str, dict[str, Any]] = {
|
||
"box": {"n": 0.013, "depth_ratio": 0.8, "box_width_m": None, "box_height_m": None},
|
||
"ford": {
|
||
"n": FORD_MANNING_N,
|
||
"overflow_width_m": 10.0,
|
||
"slope_denominator": round(1 / FORD_SLOPE, 6),
|
||
},
|
||
}
|
||
|
||
|
||
#: 장 → B06 횡단 정본 경사 키(110) — 관 = 관 바닥 두 끝 경사 · 박스 = 박스 두 끝 원지반 경사
|
||
#: (본체는 늘 수평) · 세월교 · 물넘이는 장 기본값(B05 세월교 높이 계산과 같은 값)
|
||
SLOPE_KEYS = {"pipe": "pipe_slope_pct", "box": "box_ground_slope_pct"}
|
||
|
||
|
||
def _positive(value: Any) -> float | None:
|
||
"""양수만 — 글 '1000' 도 숫자로 · 0 · 빈 값 · 글자는 None(빈칸)."""
|
||
try:
|
||
number = float(value)
|
||
except (TypeError, ValueError):
|
||
return None
|
||
return number if number > 0 else None
|
||
|
||
|
||
def bed_slopes(designs: list[dict[str, Any]] | None) -> dict[str, dict[float, float]]:
|
||
"""B06 횡단 설계 `[{chainage_m, design}]` → 장별 `{측점: 경사 %}` — 0 % 이하는 안 담음(빈칸)."""
|
||
found: dict[str, dict[float, float]] = {sheet: {} for sheet in SLOPE_KEYS}
|
||
for row in designs or []:
|
||
design = row.get("design") or {}
|
||
for sheet, key in SLOPE_KEYS.items():
|
||
pct = _positive(design.get(key))
|
||
if pct:
|
||
found[sheet][round(float(row.get("chainage_m") or 0.0), 3)] = pct
|
||
return found
|
||
|
||
|
||
def _linked(
|
||
sheet: str,
|
||
options: dict[str, Any],
|
||
basin: dict[str, Any] | None,
|
||
slope_pct: float | None = None,
|
||
) -> dict[str, Any]:
|
||
"""연결값 → 계산서 칸(96-1) — 값이 있는 칸만 · A · L · H 는 유역 · 치수는 시설 옵션 ·
|
||
하상경사 S = 1 / 분모 는 B06 경사(110 · 0 % 이하면 빈칸 · 손 입력)."""
|
||
values: dict[str, Any] = {}
|
||
if slope_pct:
|
||
values["slope_denominator"] = round(100 / slope_pct, 3)
|
||
if basin is not None:
|
||
area_m2 = _positive(basin.get("area_m2"))
|
||
values["area_km2"] = area_m2 / 1e6 if area_m2 else None
|
||
values["length_m"] = _positive(basin.get("flow_length_m"))
|
||
values["height_m"] = _positive(basin.get("relief_m"))
|
||
if sheet == "pipe":
|
||
values["diameter_mm"] = _positive(options.get("pipe_diameter_mm"))
|
||
elif sheet == "box":
|
||
values["box_width_m"] = _positive(options.get("body_width_m"))
|
||
values["box_height_m"] = _positive(options.get("body_height_m"))
|
||
else:
|
||
values["overflow_width_m"] = _positive(options.get("ford_width_m"))
|
||
return {k: v for k, v in values.items() if v is not None}
|
||
|
||
|
||
def station_rows(
|
||
points: list[Any],
|
||
basins: list[dict[str, Any]] | None = None,
|
||
designs: list[dict[str, Any]] | None = None,
|
||
) -> list[dict[str, Any]]:
|
||
"""관 지점 → 보조 탭 줄(측점 차례) · 장 종류(pipe · box · ford).
|
||
|
||
basins(B05 세부유역 속성) 를 주면 측점마다 연결값 `linked` · 비교용 `b05`(Qd · T) — 측점 짝은
|
||
`carry_indices`(±0.05 m) · 안 주면 둘 다 None. designs(B06 횡단 설계) 경사는 관 지점과
|
||
가장 가까운 횡단 측점(±0.5 m · 관 연장과 같은 짝)에서.
|
||
"""
|
||
slopes = bed_slopes(designs)
|
||
points = sorted(points, key=lambda p: p.chainage_m)
|
||
picks = (
|
||
carry_indices([p.chainage_m for p in points], [float(b["chainage_m"]) for b in basins])
|
||
if basins is not None
|
||
else [None] * len(points)
|
||
)
|
||
rows = []
|
||
for p, pick in zip(points, picks):
|
||
sheet, kind = SHEETS.get(p.facility, ("pipe", ""))
|
||
row = {"chainage_m": p.chainage_m, "facility": p.facility, "kind": kind, "sheet": sheet}
|
||
row["linked"] = row["b05"] = None
|
||
if basins is not None:
|
||
basin = basins[pick] if pick is not None else None
|
||
slope = _nearest(slopes.get(sheet, {}), p.chainage_m)
|
||
row["linked"] = _linked(sheet, getattr(p, "options", None) or {}, basin, slope)
|
||
if basin is not None:
|
||
row["b05"] = {
|
||
"design_flow_m3s": basin.get("design_flow_m3s"),
|
||
"tc_minutes": basin.get("tc_minutes"),
|
||
}
|
||
rows.append(row)
|
||
return rows
|
||
|
||
|
||
def options() -> dict[str, Any]:
|
||
"""화면 선택지 — 기본값 · 할증 · 단면 가정 · 관 종류 n."""
|
||
return {
|
||
"defaults": DEFAULTS,
|
||
"factors": list(FACTORS),
|
||
"sections": SECTIONS,
|
||
"pipe_kinds": PIPE_KINDS,
|
||
"sheet_defaults": SHEET_DEFAULTS,
|
||
}
|