import gradio as gr # --------------------------- # Small-Hydro 4-Mode Simulator (Gradio) # --------------------------- # 상수 RHO = 1000.0 # kg/m^3 G = 9.80665 # m/s^2 PF_TARGET = 0.98 # 표시용 # 효율 근사 (상대유량 r = Q/Qn) def efficiency_from_relative_flow(r): r = max(0.0, min(1.2, float(r))) curve = [ (0.00, 0.00), (0.10, 0.40), (0.30, 0.60), (0.50, 0.75), (0.70, 0.85), (0.90, 0.90), (1.00, 0.91), (1.20, 0.88), ] for (x1, y1), (x2, y2) in zip(curve[:-1], curve[1:]): if x1 <= r <= x2: t = 0.0 if x2 == x1 else (r - x1) / (x2 - x1) return y1 + t * (y2 - y1) return 0.0 def electrical_power_kw(Q_in, head_m, eta): try: Q_in = float(Q_in); head_m = float(head_m); eta = float(eta) except Exception: return 0.0 if Q_in < 0 or head_m < 0: return 0.0 return (RHO * G * Q_in * head_m * max(0.0, min(1.0, eta))) / 1000.0 def decide_mode(Q_in, q_low, q_high, rain_mm, dp_kpa, rain_thr, dp_limit, emergency): if emergency: return "Emergency" if Q_in < q_low: return "Low-Flow" if Q_in > q_high or dp_kpa > dp_limit or rain_mm >= rain_thr: return "Flood" return "Normal" def run_sim(Q_in, rain_mm, dp_kpa, emergency, Hn, Qn, rain_thr, dp_limit): # 숫자/불리언 형식 보정 try: Q_in = float(Q_in) rain_mm = float(rain_mm) dp_kpa = float(dp_kpa) Hn = float(Hn) Qn = float(Qn) rain_thr = float(rain_thr) dp_limit = float(dp_limit) emergency = bool(emergency) except Exception: return "⚠️ 입력 오류: 숫자/체크 값을 확인하세요." if Qn <= 0 or Hn <= 0: return "⚠️ 설계값(Hₙ, Qₙ)은 0보다 커야 합니다." # 임계치 q_low = 0.4 * Qn q_high = 1.2 * Qn # 모드 판단 mode = decide_mode(Q_in, q_low, q_high, rain_mm, dp_kpa, rain_thr, dp_limit, emergency) # 상대유량, 효율, 기본 출력 r = min(Q_in, Qn) / Qn eta = efficiency_from_relative_flow(r) p_kw = electrical_power_kw(min(Q_in, Qn), Hn, eta) # 세부 설정값(터미널 버전과 동일 노출) guide_vane_pct = round(min(100.0, max(10.0, r * 100.0)), 1) gate_open_pct = guide_vane_pct curtail = False bypass = False dump = False soc_map = { "Normal": (55, 70), "Low-Flow": (45, 65), "Flood": (35, 45), "Emergency": (30, 50), } # 모드별 조정 if mode == "Flood": curtail = True bypass = True dump = True guide_vane_pct = max(20.0, guide_vane_pct - 20.0) gate_open_pct = guide_vane_pct p_kw *= 0.6 # 보수치 elif mode == "Low-Flow": guide_vane_pct = max(15.0, min(55.0, guide_vane_pct)) gate_open_pct = guide_vane_pct p_kw *= 0.85 elif mode == "Emergency": curtail = True bypass = True dump = False guide_vane_pct = 0.0 gate_open_pct = 0.0 p_kw = 0.0 soc_lo, soc_hi = soc_map.get(mode, (55, 70)) # 출력 Markdown lines = [] lines.append(f"## 운전 모드: **{mode}**") lines.append("") lines.append("### 입력 요약") lines.append(f"- 유량 Q: **{Q_in:.2f} m³/s**") lines.append(f"- 강우 예보: **{rain_mm:.1f} mm/24h**") lines.append(f"- 트래시랙 ΔP: **{dp_kpa:.1f} kPa**") lines.append(f"- 설계 낙차 Hₙ: **{Hn:.2f} m**, 설계 유량 Qₙ: **{Qn:.2f} m³/s**") lines.append(f" (저유량 임계 **{q_low:.2f}**, 고유량 임계 **{q_high:.2f}**)") lines.append("") lines.append("### 추정/설정 값") lines.append(f"- 추정효율(eta): {eta:.3f}") lines.append(f"- 추정출력(kW): {p_kw:.1f}") lines.append(f"- 설정_가이드베인(%): {guide_vane_pct:.1f}") lines.append(f"- 설정_취수문개도(%): {gate_open_pct:.1f}") lines.append(f"- 커테일: {curtail}") lines.append(f"- 바이패스개방: {bypass}") lines.append(f"- 덤프로드가동: {dump}") lines.append(f"- ESS_SoC_목표(%): {soc_lo}–{soc_hi}") lines.append(f"- 역률목표: {PF_TARGET}") lines.append("") lines.append("> 참고: 출력은 간이 효율곡선/커테일 반영 보수치입니다.") return "\n".join(lines) # --------------------------- # Gradio UI # --------------------------- with gr.Blocks(theme=gr.themes.Soft(), title="Small-Hydro 4-Mode Simulator") as demo: gr.Markdown("## 🌊 소수력 4-모드 시뮬레이터 (Hugging Face Spaces)") with gr.Row(): Q_in = gr.Number(label="현재 유량 Q (m³/s)", value=3.0) rain = gr.Number(label="강우 예보 (mm/24h)", value=0) dp = gr.Number(label="트래시랙 ΔP (kPa)", value=0) emer = gr.Checkbox(label="비상 알람 (Emergency)", value=False) with gr.Accordion("고급 설정 (설계/임계값)", open=False): with gr.Row(): Hn = gr.Number(label="설계 낙차 Hₙ (m)", value=8.0) Qn = gr.Number(label="설계 유량 Qₙ (m³/s)", value=3.5) with gr.Row(): rain_thr = gr.Number(label="홍수 판단 강우 임계 (mm/24h)", value=40.0) dp_limit = gr.Number(label="트래시랙 ΔP 임계 (kPa)", value=3.0) btn = gr.Button("시뮬레이션 실행", variant="primary") out = gr.Markdown() btn.click( fn=run_sim, inputs=[Q_in, rain, dp, emer, Hn, Qn, rain_thr, dp_limit], outputs=out ) # Hugging Face/로컬 모두에서 안전하게 실행 if __name__ == "__main__": demo.launch()