george2cool36 commited on
Commit
8560cc0
·
verified ·
1 Parent(s): d58c0c2

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +386 -0
app.py ADDED
@@ -0,0 +1,386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =========================
2
+ # Shear & Moment Diagram Generator (Simply Supported Beam)
3
+ # + Add (max 2) & Delete controls for loads
4
+ # =========================
5
+
6
+ import math
7
+ import numpy as np
8
+ import matplotlib.pyplot as plt
9
+ import gradio as gr
10
+ import pandas as pd
11
+
12
+ plt.rcParams.update({"figure.dpi": 120})
13
+
14
+ # ---------- Core statics ----------
15
+ def reactions_simply_supported(L, point_loads, udls):
16
+ Wp = sum(P for (_x, P) in point_loads)
17
+ Wu = sum(w * (x2 - x1) for (x1, x2, w) in udls)
18
+ Wtot = Wp + Wu
19
+
20
+ Mp = sum(P * x for (x, P) in point_loads)
21
+ Mu = sum((w * (x2 - x1)) * ((x1 + x2) / 2.0) for (x1, x2, w) in udls)
22
+
23
+ RB = (Mp + Mu) / L
24
+ RA = Wtot - RB
25
+ return RA, RB
26
+
27
+ def vm_diagrams(L, point_loads, udls, n=1001):
28
+ RA, RB = reactions_simply_supported(L, point_loads, udls)
29
+ x = np.linspace(0.0, L, n)
30
+ V = np.zeros_like(x, dtype=float)
31
+ M = np.zeros_like(x, dtype=float)
32
+
33
+ def udl_active_length(xi, x1, x2):
34
+ if xi <= x1:
35
+ return 0.0
36
+ elif xi >= x2:
37
+ return (x2 - x1)
38
+ else:
39
+ return (xi - x1)
40
+
41
+ for i, xi in enumerate(x):
42
+ v = RA
43
+ for (xp, P) in point_loads:
44
+ if xp <= xi + 1e-12:
45
+ v -= P
46
+ for (x1, x2, w) in udls:
47
+ a = udl_active_length(xi, x1, x2)
48
+ v -= w * max(0.0, a)
49
+ V[i] = v
50
+
51
+ m = RA * xi
52
+ for (xp, P) in point_loads:
53
+ a = max(0.0, xi - xp)
54
+ m -= P * a
55
+ for (x1, x2, w) in udls:
56
+ a = udl_active_length(xi, x1, x2)
57
+ if a > 0:
58
+ centroid = x1 + a / 2.0
59
+ m -= w * a * (xi - centroid)
60
+ M[i] = m
61
+
62
+ return x, V, M, (RA, RB)
63
+
64
+ # ---------- Plot helpers ----------
65
+ def draw_beam_sketch(L, point_loads, udls, RA, RB):
66
+ fig, ax = plt.subplots(figsize=(7, 1.8))
67
+ ax.set_xlim(-0.02*L, 1.02*L)
68
+ ax.set_ylim(-1.2, 1.2)
69
+
70
+ ax.plot([0, L], [0, 0], lw=4, color="black")
71
+ triA = plt.Polygon([[0,0],[ -0.05*L, -0.5],[ 0.05*L, -0.5]], color="gray")
72
+ triB = plt.Polygon([[L,0],[ L-0.05*L, -0.5],[ L+0.05*L, -0.5]], color="gray")
73
+ ax.add_patch(triA); ax.add_patch(triB)
74
+
75
+ ax.annotate("", xy=(0,0.6), xytext=(0,0.05),
76
+ arrowprops=dict(arrowstyle="->", lw=2))
77
+ ax.text(0, 0.7, f"R_A={RA:.1f} N", ha="center", va="bottom", fontsize=9)
78
+ ax.annotate("", xy=(L,0.6), xytext=(L,0.05),
79
+ arrowprops=dict(arrowstyle="->", lw=2))
80
+ ax.text(L, 0.7, f"R_B={RB:.1f} N", ha="center", va="bottom", fontsize=9)
81
+
82
+ for (xp, P) in point_loads:
83
+ ax.annotate("", xy=(xp,-0.7), xytext=(xp,0.05),
84
+ arrowprops=dict(arrowstyle="->", lw=2))
85
+ ax.text(xp, -0.8, f"P={P:.0f} N", ha="center", va="top", fontsize=9)
86
+
87
+ for (x1, x2, w) in udls:
88
+ ax.plot([x1, x2], [0.25, 0.25], lw=6, color="tab:blue")
89
+ n_ar = max(2, int((x2-x1)/(L/10)) )
90
+ xs = np.linspace(x1, x2, n_ar)
91
+ for xk in xs:
92
+ ax.annotate("", xy=(xk,0.05), xytext=(xk,0.4),
93
+ arrowprops=dict(arrowstyle="->", lw=1.5))
94
+ ax.text((x1+x2)/2, 0.45, f"w={w:.0f} N/m", ha="center", va="bottom", fontsize=9, color="tab:blue")
95
+
96
+ ax.axis("off")
97
+ ax.set_title("Beam & Loads (simply supported)")
98
+ fig.tight_layout()
99
+ return fig
100
+
101
+ def plot_line(x, y, title, ylbl):
102
+ fig, ax = plt.subplots(figsize=(7, 2.5))
103
+ ax.axhline(0, color="k", lw=1)
104
+ ax.plot(x, y, lw=2)
105
+ ax.set_xlabel("x [m]")
106
+ ax.set_ylabel(ylbl)
107
+ ax.set_title(title)
108
+ ax.grid(True, alpha=0.3)
109
+ fig.tight_layout()
110
+ return fig
111
+
112
+ # ---------- Defaults & Help ----------
113
+ EX_POINT = pd.DataFrame(
114
+ [[2.0, 8000],
115
+ [5.0, 6000]],
116
+ columns=["x (m)", "P (N, down +)"]
117
+ )
118
+ EX_UDL = pd.DataFrame(
119
+ [[1.0, 3.0, 1500],
120
+ [6.0, 8.5, 1000]],
121
+ columns=["x1 (m)", "x2 (m)", "w (N/m, down +)"]
122
+ )
123
+
124
+ HELP_MD = """
125
+ **How to use**
126
+
127
+ 1) Set **span L** and enter loads:
128
+ - **Point loads:** `(x, P)`; `P>0` is downward.
129
+ - **UDLs:** `(x1, x2, w)`; `w>0` is downward, active on `[x1,x2]`.
130
+
131
+ 2) Use **Add** (limited to 2 rows per type), **Delete rows**, **Clear**, or **Reset examples**.
132
+
133
+ 3) Click **Compute** to see reactions, the beam sketch, **V(x)** and **M(x)**, and equilibrium checks.
134
+
135
+ **Sign convention**
136
+ - Upward reactions positive. Downward loads positive inputs.
137
+ - Shear: positive upward on left face; Moment: sagging positive.
138
+ """
139
+
140
+ # ---------- Small helpers for add/delete ----------
141
+ def _parse_row_list(s, n_rows):
142
+ if not s:
143
+ return []
144
+ idxs = []
145
+ for tok in str(s).replace(" ", "").split(","):
146
+ if not tok:
147
+ continue
148
+ try:
149
+ k = int(tok)
150
+ if 1 <= k <= n_rows:
151
+ idxs.append(k-1)
152
+ except ValueError:
153
+ pass
154
+ return sorted(set(idxs), reverse=True)
155
+
156
+ def delete_point_rows(df_points, rows_to_delete, confirm):
157
+ if not confirm:
158
+ return df_points, "Deletion NOT performed (check 'Confirm delete')."
159
+ if df_points is None or len(df_points) == 0:
160
+ return pd.DataFrame(columns=["x (m)","P (N, down +)"]), "No point rows to delete."
161
+ idxs = _parse_row_list(rows_to_delete, len(df_points))
162
+ if not idxs:
163
+ return df_points, "No valid point row indices provided."
164
+ df = df_points.copy()
165
+ for i in idxs:
166
+ df = df.drop(df.index[i])
167
+ df = df.reset_index(drop=True)
168
+ return df, f"Deleted point rows: {', '.join(str(i+1) for i in idxs)}."
169
+
170
+ def delete_udl_rows(df_udl, rows_to_delete, confirm):
171
+ if not confirm:
172
+ return df_udl, "Deletion NOT performed (check 'Confirm delete')."
173
+ if df_udl is None or len(df_udl) == 0:
174
+ return pd.DataFrame(columns=["x1 (m)","x2 (m)","w (N/m, down +)"]), "No UDL rows to delete."
175
+ idxs = _parse_row_list(rows_to_delete, len(df_udl))
176
+ if not idxs:
177
+ return df_udl, "No valid UDL row indices provided."
178
+ df = df_udl.copy()
179
+ for i in idxs:
180
+ df = df.drop(df.index[i])
181
+ df = df.reset_index(drop=True)
182
+ return df, f"Deleted UDL rows: {', '.join(str(i+1) for i in idxs)}."
183
+
184
+ def clear_points():
185
+ return pd.DataFrame(columns=["x (m)","P (N, down +)"]), "Cleared all point loads."
186
+
187
+ def clear_udls():
188
+ return pd.DataFrame(columns=["x1 (m)","x2 (m)","w (N/m, down +)"]), "Cleared all UDLs."
189
+
190
+ def reset_examples_points():
191
+ return EX_POINT.copy(), "Restored example point loads."
192
+
193
+ def reset_examples_udls():
194
+ return EX_UDL.copy(), "Restored example UDLs."
195
+
196
+ # --- ADD with limit = 2 ---
197
+ MAX_POINTS = 2
198
+ MAX_UDLS = 2
199
+
200
+ def add_point_row(df_points, x, P):
201
+ df = df_points.copy() if df_points is not None else pd.DataFrame(columns=["x (m)","P (N, down +)"])
202
+ if len(df) >= MAX_POINTS:
203
+ return df, f"Limit reached: max {MAX_POINTS} point loads."
204
+ try:
205
+ x = float(x); P = float(P)
206
+ except Exception:
207
+ return df, "Provide numeric x and P."
208
+ if P < 0:
209
+ return df, "Use P ≥ 0 (downward +)."
210
+ df = pd.concat([df, pd.DataFrame([[x, P]], columns=df.columns)], ignore_index=True)
211
+ return df, f"Added point load #{len(df)}."
212
+
213
+ def add_udl_row(df_udl, x1, x2, w):
214
+ df = df_udl.copy() if df_udl is not None else pd.DataFrame(columns=["x1 (m)","x2 (m)","w (N/m, down +)"])
215
+ if len(df) >= MAX_UDLS:
216
+ return df, f"Limit reached: max {MAX_UDLS} UDLs."
217
+ try:
218
+ x1 = float(x1); x2 = float(x2); w = float(w)
219
+ except Exception:
220
+ return df, "Provide numeric x1, x2, w."
221
+ if not (x1 < x2):
222
+ return df, "Require x1 < x2."
223
+ if w < 0:
224
+ return df, "Use w ≥ 0 (downward +)."
225
+ df = pd.concat([df, pd.DataFrame([[x1, x2, w]], columns=df.columns)], ignore_index=True)
226
+ return df, f"Added UDL #{len(df)}."
227
+
228
+ # ---------- Core compute ----------
229
+ def run_once(L, df_points, df_udl, npts):
230
+ try:
231
+ L = float(L)
232
+ n = int(npts)
233
+ if L <= 0:
234
+ raise ValueError("Beam length L must be > 0.")
235
+ if n < 201:
236
+ n = 201
237
+
238
+ point_loads = []
239
+ if df_points is not None and len(df_points) > 0:
240
+ for _, row in df_points.iterrows():
241
+ x = float(row[0]); P = float(row[1])
242
+ if 0 <= x <= L and P >= 0:
243
+ point_loads.append((x, P))
244
+
245
+ udls = []
246
+ if df_udl is not None and len(df_udl) > 0:
247
+ for _, row in df_udl.iterrows():
248
+ x1 = float(row[0]); x2 = float(row[1]); w = float(row[2])
249
+ if 0 <= x1 < x2 <= L and w >= 0:
250
+ udls.append((x1, x2, w))
251
+
252
+ x, V, M, (RA, RB) = vm_diagrams(L, point_loads, udls, n=n)
253
+
254
+ fig_beam = draw_beam_sketch(L, point_loads, udls, RA, RB)
255
+ fig_V = plot_line(x, V, "Shear Force Diagram V(x)", "V [N]")
256
+ fig_M = plot_line(x, M, "Bending Moment Diagram M(x)", "M [N·m]")
257
+
258
+ total_down = sum(P for _, P in point_loads) + sum(w*(x2-x1) for (x1,x2,w) in udls)
259
+ eq_sumF = RA + RB - total_down
260
+ Mp = sum(P * x for (x, P) in point_loads)
261
+ Mu = sum(w*(x2-x1)*( (x1+x2)/2 ) for (x1,x2,w) in udls)
262
+ eq_MA = RB*L - (Mp + Mu)
263
+
264
+ df = pd.DataFrame([{
265
+ "RA [N]": round(RA, 3),
266
+ "RB [N]": round(RB, 3),
267
+ "ΣF (should ≈ 0) [N]": round(eq_sumF, 6),
268
+ "ΣM_A (should ≈ 0) [N·m]": round(eq_MA, 6),
269
+ "max|V| [N]": round(float(np.max(np.abs(V))), 3),
270
+ "max|M| [N·m]": round(float(np.max(np.abs(M))), 3),
271
+ }])
272
+
273
+ return df, fig_beam, fig_V, fig_M, ""
274
+ except Exception as e:
275
+ return pd.DataFrame(), None, None, None, f"Input error:\n{e}"
276
+
277
+ # ---------- UI ----------
278
+ with gr.Blocks(title="Shear & Moment Diagrams — Simply Supported Beam") as demo:
279
+ gr.Markdown("# Shear & Bending Moment Diagram Generator")
280
+ gr.Markdown(HELP_MD)
281
+
282
+ with gr.Row():
283
+ with gr.Column():
284
+ L = gr.Number(value=10.0, label="Span L [m]")
285
+ npts = gr.Slider(minimum=201, maximum=5001, step=100, value=1201, label="Resolution (points)")
286
+
287
+ with gr.Column():
288
+ gr.Markdown("### Point loads (downward +) — max 2")
289
+ df_points = gr.Dataframe(
290
+ value=EX_POINT, headers=["x (m)","P (N, down +)"],
291
+ datatype=["number","number"], row_count=(1, "dynamic")
292
+ )
293
+ with gr.Row():
294
+ x_new = gr.Number(value=1.0, label="x (m)")
295
+ P_new = gr.Number(value=5000.0, label="P (N, down +)")
296
+ btn_add_pt = gr.Button("Add point load", variant="primary")
297
+ with gr.Row():
298
+ del_pts_rows = gr.Textbox(label="Delete point rows (e.g., 1,2)")
299
+ confirm_pts = gr.Checkbox(value=False, label="Confirm delete")
300
+ with gr.Row():
301
+ btn_del_pts = gr.Button("Delete selected point rows", variant="secondary")
302
+ btn_clr_pts = gr.Button("Clear ALL point loads", variant="stop")
303
+ btn_rst_pts = gr.Button("Reset example points", variant="primary")
304
+
305
+ with gr.Column():
306
+ gr.Markdown("### Uniform distributed loads (downward +) — max 2")
307
+ df_udl = gr.Dataframe(
308
+ value=EX_UDL, headers=["x1 (m)","x2 (m)","w (N/m, down +)"],
309
+ datatype=["number","number","number"], row_count=(1, "dynamic")
310
+ )
311
+ with gr.Row():
312
+ x1_new = gr.Number(value=2.0, label="x1 (m)")
313
+ x2_new = gr.Number(value=4.0, label="x2 (m)")
314
+ w_new = gr.Number(value=1000.0, label="w (N/m)")
315
+ btn_add_udl = gr.Button("Add UDL", variant="primary")
316
+ with gr.Row():
317
+ del_udl_rows = gr.Textbox(label="Delete UDL rows (e.g., 1)")
318
+ confirm_udl = gr.Checkbox(value=False, label="Confirm delete")
319
+ with gr.Row():
320
+ btn_del_udl = gr.Button("Delete selected UDL rows", variant="secondary")
321
+ btn_clr_udl = gr.Button("Clear ALL UDLs", variant="stop")
322
+ btn_rst_udl = gr.Button("Reset example UDLs", variant="primary")
323
+
324
+ go_btn = gr.Button("Compute", variant="primary")
325
+
326
+ gr.Markdown("### Reactions & Checks")
327
+ out_tbl = gr.Dataframe(interactive=False)
328
+
329
+ gr.Markdown("### Beam sketch")
330
+ out_beam = gr.Plot()
331
+
332
+ with gr.Row():
333
+ out_V = gr.Plot()
334
+ out_M = gr.Plot()
335
+
336
+ status = gr.Textbox(label="Status / Errors", interactive=False)
337
+
338
+ # --- Wire add actions (limit enforced) ---
339
+ def _add_point(df, x, P):
340
+ new_df, msg = add_point_row(df, x, P)
341
+ return new_df, msg
342
+ btn_add_pt.click(_add_point, inputs=[df_points, x_new, P_new], outputs=[df_points, status])
343
+
344
+ def _add_udl(df, x1, x2, w):
345
+ new_df, msg = add_udl_row(df, x1, x2, w)
346
+ return new_df, msg
347
+ btn_add_udl.click(_add_udl, inputs=[df_udl, x1_new, x2_new, w_new], outputs=[df_udl, status])
348
+
349
+ # --- Wire delete/clear/reset actions ---
350
+ def _delete_pts(df, rows_txt, confirm):
351
+ new_df, msg = delete_point_rows(df, rows_txt, confirm)
352
+ return new_df, msg
353
+ btn_del_pts.click(_delete_pts, inputs=[df_points, del_pts_rows, confirm_pts],
354
+ outputs=[df_points, status])
355
+
356
+ def _clear_pts():
357
+ return clear_points()
358
+ btn_clr_pts.click(_clear_pts, inputs=None, outputs=[df_points, status])
359
+
360
+ def _reset_pts():
361
+ return reset_examples_points()
362
+ btn_rst_pts.click(_reset_pts, inputs=None, outputs=[df_points, status])
363
+
364
+ def _delete_udl(df, rows_txt, confirm):
365
+ new_df, msg = delete_udl_rows(df, rows_txt, confirm)
366
+ return new_df, msg
367
+ btn_del_udl.click(_delete_udl, inputs=[df_udl, del_udl_rows, confirm_udl],
368
+ outputs=[df_udl, status])
369
+
370
+ def _clear_udl():
371
+ return clear_udls()
372
+ btn_clr_udl.click(_clear_udl, inputs=None, outputs=[df_udl, status])
373
+
374
+ def _reset_udl():
375
+ return reset_examples_udls()
376
+ btn_rst_udl.click(_reset_udl, inputs=None, outputs=[df_udl, status])
377
+
378
+ # --- Compute ---
379
+ go_btn.click(
380
+ fn=run_once,
381
+ inputs=[L, df_points, df_udl, npts],
382
+ outputs=[out_tbl, out_beam, out_V, out_M, status]
383
+ )
384
+
385
+ if __name__ == "__main__":
386
+ demo.launch(debug=False)