Aoi785 commited on
Commit
5f4e420
ยท
verified ยท
1 Parent(s): b4361ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -0
app.py CHANGED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ # ---------------------------
4
+ # Small-Hydro 4-Mode Simulator (Web, Gradio)
5
+ # ---------------------------
6
+
7
+ RHO = 1000.0 # kg/m^3
8
+ G = 9.80665 # m/s^2
9
+ PF_TARGET = 0.98 # ์—ญ๋ฅ  ๋ชฉํ‘œ ํ‘œ๊ธฐ์šฉ
10
+
11
+ # ํšจ์œจ ๊ทผ์‚ฌ: Kaplan/Crossflow ๋ถ€๋ถ„๋ถ€ํ•˜ ํ•ฉ์„ฑ (์ƒ๋Œ€์œ ๋Ÿ‰ r=Q/Qn)
12
+ def efficiency_from_relative_flow(r: float) -> float:
13
+ r = max(0.0, min(1.2, r))
14
+ curve = [
15
+ (0.00, 0.00),
16
+ (0.10, 0.40),
17
+ (0.30, 0.60),
18
+ (0.50, 0.75),
19
+ (0.70, 0.85),
20
+ (0.90, 0.90),
21
+ (1.00, 0.91),
22
+ (1.20, 0.88),
23
+ ]
24
+ for (x1, y1), (x2, y2) in zip(curve[:-1], curve[1:]):
25
+ if x1 <= r <= x2:
26
+ t = 0.0 if x2 == x1 else (r - x1) / (x2 - x1)
27
+ return y1 + t * (y2 - y1)
28
+ return 0.0
29
+
30
+ def electrical_power_kw(Q_in: float, head_m: float, eta: float) -> float:
31
+ # P [kW] = rho*g*Q*H*eta / 1000
32
+ return (RHO * G * Q_in * head_m * max(0.0, min(1.0, eta))) / 1000.0
33
+
34
+ def decide_mode(Q_in: float, q_low: float, q_high: float, rain_mm: float, dp_kpa: float,
35
+ rain_thr: float, dp_limit: float, emergency: bool) -> str:
36
+ if emergency:
37
+ return "Emergency"
38
+ if Q_in < q_low:
39
+ return "Low-Flow"
40
+ if Q_in > q_high or dp_kpa > dp_limit or rain_mm >= rain_thr:
41
+ return "Flood"
42
+ return "Normal"
43
+
44
+ def mode_policy(mode: str, Q_in: float, Qn: float, Hn: float, rain_mm: float):
45
+ # ์ƒ๋Œ€ ์œ ๋Ÿ‰/ํšจ์œจ/์ถœ๋ ฅ(์ปคํ…Œ์ผ ์ „)
46
+ r = min(Q_in, Qn) / Qn if Qn > 0 else 0.0
47
+ eta = efficiency_from_relative_flow(r)
48
+ p_kw_raw = electrical_power_kw(min(Q_in, Qn), Hn, eta)
49
+
50
+ # ๊ธฐ๋ณธ ์ œ์–ด ๋ณ€์ˆ˜
51
+ gate_open_pct = round(min(100.0, max(10.0, r * 100.0)), 1) # ์ทจ์ˆ˜๋ฌธ ๊ฐœ๋„ ์ถ”์ •
52
+ guide_vane_pct = gate_open_pct # ๊ฐ€์ด๋“œ๋ฒ ์ธ/ํ”ผ์น˜ ์ถ”์ •
53
+ curtail = False
54
+ use_bypass = False
55
+ dump_load = False
56
+ soc_range = (55, 70) # Normal ๊ธฐ๋ณธ
57
+
58
+ actions = []
59
+
60
+ if mode == "Normal":
61
+ soc_range = (55, 70)
62
+ actions += [
63
+ "๊ฐ€์ด๋“œ๋ฒ ์ธ/ํ”ผ์น˜: PID ์ถ”์ข…์œผ๋กœ ์ถœ๋ ฅ ์ตœ์ ํ™”",
64
+ "ESS: SoC 55โ€“70% ์œ ์ง€(์ˆ˜๋ช… ์ตœ์ )",
65
+ "ํŠธ๋ž˜์‹œ๋ž™ ฮ”P ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์ฃผ๊ธฐ ์ฒญ์†Œ",
66
+ ]
67
+
68
+ elif mode == "Flood":
69
+ soc_range = (35, 45)
70
+ curtail = True
71
+ use_bypass = True
72
+ dump_load = True
73
+ gate_open_pct = max(20.0, gate_open_pct - 20.0) # ์ˆ˜๊ฒฉ ๋ฐฉ์ง€์šฉ ๊ฐ๊ฐœ
74
+ guide_vane_pct = gate_open_pct
75
+ p_kw_raw *= 0.6 # ์šฐํšŒ/์ปคํ…Œ์ผ ๋ฐ˜์˜ํ•œ ๋ณด์ˆ˜ ์ถœ๋ ฅ
76
+ actions += [
77
+ "๊ฐ€์ด๋“œ๋ฒ ์ธ ๋‹จ๊ณ„์  ๊ฐ๊ฐœ(์ˆ˜๊ฒฉ ๋ฐฉ์ง€ ๊ณก์„  ์ค€์ˆ˜)",
78
+ "์—ฌ์ˆ˜๋กœ/๋ฐ”์ดํŒจ์Šค ๊ฐœ๋ฐฉ, ๋ฐฐ์‚ฌ๋ฌธ ๊ฐ„ํ— ๊ฐœ๋ฐฉ(ํ† ์‚ฌยทํƒ๋„ ์ €๊ฐ)",
79
+ "ESS: SoC ๋ชฉํ‘œ 35โ€“45% (๊ฐ•์šฐ ์˜ˆ๋ณด ๋ฐ˜์˜)",
80
+ "๋คํ”„๋กœ๋“œ/๊ฐ€๋ณ€๋ถ€ํ•˜ ์šฐ์„  ๊ฐ€๋™์œผ๋กœ ์ดˆ๊ณผ์ „๋ ฅ ํก์ˆ˜",
81
+ "ํŠธ๋ž˜์‹œ๋ž™ ์ž๊ฐ€์ฒญ์†Œ ์—ฐ์† ๊ตฌ๋™(ฮ”P ์ž„๊ณ„ ์ดˆ๊ณผ ์‹œ)",
82
+ ]
83
+
84
+ elif mode == "Low-Flow":
85
+ soc_range = (45, 65)
86
+ guide_vane_pct = max(15.0, min(55.0, guide_vane_pct))
87
+ gate_open_pct = guide_vane_pct
88
+ p_kw_raw *= 0.85 # ๋ถ€๋ถ„๋ถ€ํ•˜ ์†์‹ค
89
+ actions += [
90
+ "์ €๋ถ€ํ•˜ ํšจ์œจ์ (๊ฐ€๋ณ€์†/๊ฐ๋„ ๊ณ ์ •) ์šด์ „",
91
+ "ESS: ํ”ผํฌ ๋ณด์กฐ ๋ฐฉ์ „, SoC 45โ€“65%",
92
+ "์œ ์ž…์ˆ˜์†๋„ ํ™•๋ณด(NPSH ์—ฌ์œ  ์œ ์ง€), ๊ณต๋™ํ˜„์ƒ ๊ฐ์‹œ",
93
+ ]
94
+
95
+ elif mode == "Emergency":
96
+ soc_range = (30, 50)
97
+ curtail = True
98
+ use_bypass = True
99
+ dump_load = False
100
+ guide_vane_pct = 0.0
101
+ gate_open_pct = 0.0
102
+ p_kw_raw = 0.0
103
+ actions += [
104
+ "์ฆ‰์‹œ ๋ฌด๋ถ€ํ•˜ โ†’ ์ •์ง€(์ˆ˜๊ฒฉ ๊ณก์„  ์ค€์ˆ˜)",
105
+ "์ทจ์ˆ˜๋ฌธ ํ์‡„ & ๋ฐ”์ดํŒจ์Šค/์Šคํ•„์›จ์ด ๊ฐœ๋ฐฉ(์น˜์ˆ˜ ์•ˆ์ „)",
106
+ "๊ณ„ํ†ต ๋ถ„๋ฆฌ, UPS/ESS๋กœ ๊ณ„์žฅ ์ „์› ์œ ์ง€",
107
+ "์‚ฌํ›„ ์ ๊ฒ€: ๋ฒ ์–ด๋ง/์ƒคํ”„ํŠธ/๊ด€๋กœ/์„œ์ง€ํƒฑํฌ",
108
+ ]
109
+ else:
110
+ actions.append("์ •์˜๋˜์ง€ ์•Š์€ ๋ชจ๋“œ")
111
+
112
+ estimates = {
113
+ "์ถ”์ •ํšจ์œจ(eta)": f"{eta:.3f}",
114
+ "์ถ”์ •์ถœ๋ ฅ(kW)": f"{p_kw_raw:.1f}",
115
+ "์„ค์ •_๊ฐ€์ด๋“œ๋ฒ ์ธ(%)": f"{guide_vane_pct:.1f}",
116
+ "์„ค์ •_์ทจ์ˆ˜๋ฌธ๊ฐœ๋„(%)": f"{gate_open_pct:.1f}",
117
+ "์ปคํ…Œ์ผ": str(curtail),
118
+ "๋ฐ”์ดํŒจ์Šค๊ฐœ๋ฐฉ": str(use_bypass),
119
+ "๋คํ”„๋กœ๋“œ๊ฐ€๋™": str(dump_load),
120
+ "ESS_SoC_๋ชฉํ‘œ(%)": f"{soc_range[0]}โ€“{soc_range[1]}",
121
+ "์—ญ๋ฅ ๋ชฉํ‘œ": f"{PF_TARGET}",
122
+ }
123
+ return actions, estimates
124
+
125
+ def run_sim(Q_in, rain_mm, dp_kpa, emergency,
126
+ Hn, Qn, rain_thr, dp_limit):
127
+ try:
128
+ Q_in = float(Q_in)
129
+ rain_mm = float(rain_mm)
130
+ dp_kpa = float(dp_kpa)
131
+ Hn = float(Hn)
132
+ Qn = float(Qn)
133
+ rain_thr = float(rain_thr)
134
+ dp_limit = float(dp_limit)
135
+ emergency = bool(emergency)
136
+ except Exception:
137
+ return "โš ๏ธ ์ž…๋ ฅ ์˜ค๋ฅ˜: ์ˆซ์ž/์ฒดํฌ ๊ฐ’์„ ํ™•์ธํ•˜์„ธ์š”."
138
+
139
+ q_low = 0.4 * Qn
140
+ q_high = 1.2 * Qn
141
+
142
+ mode = decide_mode(Q_in, q_low, q_high, rain_mm, dp_kpa, rain_thr, dp_limit, emergency)
143
+ actions, estimates = mode_policy(mode, Q_in, Qn, Hn, rain_mm)
144
+
145
+ # ๊ฒฐ๊ณผ๋ฅผ Markdown์œผ๋กœ ์ •๋ฆฌ
146
+ md = []
147
+ md.append(f"## ์šด์ „ ๋ชจ๋“œ: **{mode}**")
148
+ md.append("")
149
+ md.append("### ์ž…๋ ฅ ์š”์•ฝ")
150
+ md.append(f"- ์œ ๋Ÿ‰ Q: **{Q_in:.2f} mยณ/s**")
151
+ md.append(f"- ๊ฐ•์šฐ ์˜ˆ๋ณด: **{rain_mm:.1f} mm/24h**")
152
+ md.append(f"- ํŠธ๋ž˜์‹œ๋ž™ ฮ”P: **{dp_kpa:.1f} kPa**")
153
+ md.append(f"- ์„ค๊ณ„ ๋‚™์ฐจ Hโ‚™: **{Hn:.2f} m**, ์„ค๊ณ„ ์œ ๋Ÿ‰ Qโ‚™: **{Qn:.2f} mยณ/s** \n"
154
+ f" (์ €์œ ๋Ÿ‰ ์ž„๊ณ„ **{q_low:.2f}**, ๊ณ ์œ ๋Ÿ‰ ์ž„๊ณ„ **{q_high:.2f}**)")
155
+ md.append("")
156
+ md.append("### ๊ถŒ์žฅ ๋™์ž‘")
157
+ for i, a in enumerate(actions, 1):
158
+ md.append(f"{i}. {a}")
159
+ md.append("")
160
+ md.append("### ์ถ”์ •/์„ค์ • ๊ฐ’")
161
+ for k, v in estimates.items():
162
+ md.append(f"- **{k}**: {v}")
163
+ md.append("")
164
+ md.append("> ์ฐธ๊ณ : ์ถœ๋ ฅ ์ถ”์ •์€ ๊ฐ„์ด ํšจ์œจ๊ณก์„ /์ปคํ…Œ์ผ ๋ฐ˜์˜์œผ๋กœ ์‚ฐ์ •๋œ ๋ณด์ˆ˜์น˜์ž…๋‹ˆ๋‹ค.")
165
+ return "\n".join(md)
166
+
167
+ # ---------------------------
168
+ # Gradio UI
169
+ # ---------------------------
170
+ with gr.Blocks(theme=gr.themes.Soft()) as app:
171
+ gr.Markdown("## ๐ŸŒŠ ์†Œ์ˆ˜๋ ฅ 4-๋ชจ๋“œ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ (Hugging Face Spaces)")
172
+ with gr.Row():
173
+ Q_in = gr.Number(label="ํ˜„์žฌ ์œ ๋Ÿ‰ Q (mยณ/s)", value=3.0)
174
+ rain = gr.Number(label="๊ฐ•์šฐ ์˜ˆ๋ณด (mm/24h)", value=0)
175
+ dp = gr.Number(label="ํŠธ๋ž˜์‹œ๋ž™ ์ฐจ์•• ฮ”P (kPa)", value=0)
176
+ emer = gr.Checkbox(label="๋น„์ƒ ์•Œ๋žŒ (Emergency)", value=False)
177
+
178
+ with gr.Accordion("๊ณ ๊ธ‰ ์„ค์ • (์„ค๊ณ„/์ž„๊ณ„๊ฐ’)", open=False):
179
+ with gr.Row():
180
+ Hn = gr.Number(label="์„ค๊ณ„ ๋‚™์ฐจ Hโ‚™ (m)", value=8.0)
181
+ Qn = gr.Number(label="์„ค๊ณ„ ์œ ๋Ÿ‰ Qโ‚™ (mยณ/s)", value=3.5)
182
+ with gr.Row():
183
+ rain_thr = gr.Number(label="ํ™์ˆ˜ ํŒ๋‹จ ๊ฐ•์šฐ ์ž„๊ณ„ (mm/24h)", value=40.0)
184
+ dp_limit = gr.Number(label="ํŠธ๋ž˜์‹œ๋ž™ ฮ”P ์ž„๊ณ„ (kPa)", value=3.0)
185
+
186
+ run_btn = gr.Button("์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์‹คํ–‰", variant="primary")
187
+ out_md = gr.Markdown()
188
+
189
+ run_btn.click(
190
+ fn=run_sim,
191
+ inputs=[Q_in, rain, dp, emer, Hn, Qn, rain_thr, dp_limit],
192
+ outputs=out_md
193
+ )
194
+
195
+ # Spaces์—์„œ๋Š” launch() ํ˜ธ์ถœ ๋ถˆํ•„์š”ํ•˜์ง€๋งŒ, ๋กœ์ปฌ ํ…Œ์ŠคํŠธ์—์„  ์•„๋ž˜ ์ฃผ์„ ํ•ด์ œ
196
+ # if __name__ == "__main__":
197
+ # app.launch()