smallhydro / app.py
Aoi785's picture
Update app.py
a71fd6a verified
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()