george2cool36 commited on
Commit
0b51f7e
·
verified ·
1 Parent(s): 3245ffd

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +303 -0
app.py ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =========================
2
+ # Cantilever Rectangular Beam — Point Load (Free End or at Position a)
3
+ # =========================
4
+
5
+ import math
6
+ import json
7
+ import gradio as gr
8
+ import pandas as pd
9
+
10
+ # ---- Optional LLM with safe fallback ----
11
+ _USE_LLM = True
12
+ try:
13
+ from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
14
+ MODEL_ID = "HuggingFaceTB/SmolLM2-135M-Instruct"
15
+ _tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
16
+ _pipe = pipeline(
17
+ task="text-generation",
18
+ model=AutoModelForCausalLM.from_pretrained(MODEL_ID),
19
+ tokenizer=_tokenizer,
20
+ )
21
+ except Exception:
22
+ _USE_LLM = False
23
+ _tokenizer = None
24
+ _pipe = None
25
+
26
+ SCOPE_MD = """
27
+ ### Scope & Assumptions
28
+ - Problem: **Cantilever rectangular beam**, **point load** either at the **free end** or at **position a** from the fixed end.
29
+ - Outputs: Maximum bending stress (σ_max), yield FoS, free-end deflection (δ), deflection FoS vs. **L/180** (typical cantilever service limit).
30
+ - Method: Euler–Bernoulli beam theory (linear-elastic, small deflection). Shear deformation and local buckling not included.
31
+ - Section: Rectangle with **width b** (in-plane) and **height h** (bends about the strong axis).
32
+ - Units: SI (m, N, GPa, MPa). Results in MPa and mm.
33
+
34
+ ### Valid ranges (hard checks)
35
+ - 0.05 < L ≤ 10 m
36
+ - 0 < P ≤ 1*10^6 N
37
+ - 1 ≤ E ≤ 400 GPa
38
+ - 10 ≤ Sy ≤ 3000 MPa
39
+ - 0.005 < b ≤ 2 m
40
+ - 0.005 < h ≤ 2 m
41
+ - For load at position a: 0 < a ≤ L
42
+
43
+ **Notes:** Service limit uses **L/180** for cantilevers (typical).
44
+ """
45
+
46
+ # ---------- Validation & core ----------
47
+ def _validate_inputs(L_m, P_N, E_GPa, Sy_MPa, b_m, h_m):
48
+ errs = []
49
+ def in_range(name, val, lo, hi):
50
+ if not (lo < val <= hi):
51
+ errs.append(f"{name} must be in ({lo}, {hi}] (got {val}).")
52
+ in_range("Beam length L [m]", L_m, 0.05, 10.0)
53
+ in_range("Point load P [N]", P_N, 0.0, 1_000_000.0)
54
+ in_range("Elastic modulus E [GPa]", E_GPa, 1.0, 400.0)
55
+ in_range("Yield strength Sy [MPa]", Sy_MPa, 10.0, 3000.0)
56
+ in_range("Section width b [m]", b_m, 0.005, 2.0)
57
+ in_range("Section height h [m]", h_m, 0.005, 2.0)
58
+ if errs:
59
+ raise ValueError("\n".join(errs))
60
+
61
+ def rect_I(b, h):
62
+ # Strong-axis bending: I = b*h^3/12
63
+ return b * (h**3) / 12.0
64
+
65
+ def calc_cantilever_rect(L_m, P_N, E_GPa, Sy_MPa, b_m, h_m, mode, a_in):
66
+ """
67
+ Cantilever rectangular beam with point load at:
68
+ - Free end -> a = L
69
+ - Position a -> 0 < a <= L
70
+ """
71
+ _validate_inputs(L_m, P_N, E_GPa, Sy_MPa, b_m, h_m)
72
+
73
+ if mode == "Free end (a = L)":
74
+ a = L_m
75
+ mode_note = "Free end load (a = L)"
76
+ else:
77
+ a = float(a_in)
78
+ if not (0.0 < a <= L_m):
79
+ raise ValueError(f"a must satisfy 0 < a ≤ L (got a={a}, L={L_m}).")
80
+ mode_note = f"Load at position a (a = {a:g} m)"
81
+
82
+ E_Pa = E_GPa * 1e9
83
+ Sy_Pa = Sy_MPa * 1e6
84
+
85
+ I = rect_I(b_m, h_m)
86
+ c = h_m / 2.0
87
+
88
+ # Max moment at fixed end
89
+ # M_max = P * a
90
+ M = P_N * a
91
+
92
+ # Max bending stress (outer fiber at fixed end)
93
+ # sigma_max = M*c / I
94
+ sigma_max_Pa = (M * c) / I
95
+ sigma_max_MPa = sigma_max_Pa / 1e6
96
+
97
+ # Free-end deflection for cantilever with a point load at position a:
98
+ # δ(L) = P * a^2 * (3L - a) / (6 E I)
99
+ delta_m = (P_N * (a**2) * (3.0 * L_m - a)) / (6.0 * E_Pa * I)
100
+ delta_mm = delta_m * 1e3
101
+
102
+ # Serviceability (typical cantilever): δ_allow = L / 180
103
+ delta_allow_m = L_m / 180.0
104
+ fos_deflection = (delta_allow_m / delta_m) if delta_m > 0 else math.inf
105
+ deflection_ok = delta_m <= delta_allow_m
106
+
107
+ utilization = sigma_max_Pa / Sy_Pa
108
+ fos_yield = (1.0 / utilization) if utilization > 0 else math.inf
109
+ passes_yield = sigma_max_Pa <= Sy_Pa
110
+
111
+ overall_ok = bool(passes_yield and deflection_ok)
112
+
113
+ structured = {
114
+ "problem": "Cantilever rectangular beam with point load (free end or at position a)",
115
+ "assumptions": [
116
+ "Linear-elastic, small deflection (Euler–Bernoulli)",
117
+ "No shear deformation or local buckling",
118
+ "Rectangular section, strong-axis bending"
119
+ ],
120
+ "mode": mode_note,
121
+ "inputs_SI": {
122
+ "L_m": L_m, "P_N": P_N, "E_GPa": E_GPa, "Sy_MPa": Sy_MPa,
123
+ "section": {"b_m": b_m, "h_m": h_m},
124
+ "a_m": a
125
+ },
126
+ "section_properties": {"I_m4": I, "c_m": c},
127
+ "formulas": {
128
+ "I_rect": "I = b*h^3/12",
129
+ "M_max": "M = P*a",
130
+ "sigma_max": "sigma_max = M*c / I",
131
+ "delta_tip": "delta(L) = P*a^2*(3L - a)/(6*E*I)",
132
+ "service_limit": "delta_allow = L/180 (cantilever typical)"
133
+ },
134
+ "results": {
135
+ "sigma_max_MPa": sigma_max_MPa,
136
+ "FoS_yield": fos_yield,
137
+ "delta_mm": delta_mm,
138
+ "FoS_deflection": fos_deflection
139
+ },
140
+ "verdicts": {
141
+ "strength_ok": passes_yield,
142
+ "service_ok": deflection_ok,
143
+ "overall_ok": overall_ok
144
+ }
145
+ }
146
+
147
+ # ---- Show the math (explicit '*' multiplications) ----
148
+ def _fmt(x, d=6):
149
+ try:
150
+ return f"{x:.{d}g}"
151
+ except Exception:
152
+ return str(x)
153
+
154
+ steps_md = "\n".join([
155
+ "## Show the math",
156
+ f"Mode: {mode_note}",
157
+ f"L = {_fmt(L_m)} m, P = {_fmt(P_N)} N, E = {_fmt(E_GPa)} GPa, Sy = {_fmt(Sy_MPa)} MPa",
158
+ f"b = {_fmt(b_m)} m, h = {_fmt(h_m)} m, a = {_fmt(a)} m",
159
+ "",
160
+ "I = b*h^3/12",
161
+ f" = {b_m} * {h_m}^3 / 12",
162
+ f" = {I:.6e} m^4",
163
+ f"c = h/2 = {h_m} / 2 = {h_m/2:.4f} m",
164
+ "",
165
+ "M_max = P * a",
166
+ f" = {P_N} * {a} = {P_N*a:.6e} N·m",
167
+ "sigma_max = M * c / I",
168
+ f" = ({P_N*a:.6e}) * ({h_m}/2) / ({I:.6e})",
169
+ f" = {sigma_max_MPa:.3f} MPa",
170
+ "",
171
+ "delta(L) = P * a^2 * (3*L - a) / (6*E*I)",
172
+ f" = {P_N} * {a}^2 * (3*{L_m} - {a}) / (6 * ({E_GPa} * 10^9) * {I:.6e})",
173
+ f" = {delta_mm:.3f} mm",
174
+ f"delta_allow = L / 180 = {L_m} / 180 = {L_m/180.0:.6f} m = {L_m/180.0*1e3:.3f} mm",
175
+ "",
176
+ "FoS_yield = Sy / sigma_max",
177
+ f" = {Sy_MPa} / {sigma_max_MPa:.3f} = {fos_yield:.3f}",
178
+ "FoS_deflection = delta_allow / delta",
179
+ f" = {L_m/180.0*1e3:.3f} / {delta_mm:.3f} = {fos_deflection:.3f}",
180
+ ])
181
+
182
+ return {
183
+ "results": {
184
+ "sigma_max_MPa": sigma_max_MPa,
185
+ "safety_factor_yield": fos_yield,
186
+ "delta_m": delta_m,
187
+ "delta_mm": delta_mm,
188
+ "fos_deflection": fos_deflection,
189
+ },
190
+ "verdict": {
191
+ "passes_yield": bool(passes_yield),
192
+ "passes_serviceability": bool(deflection_ok),
193
+ "overall_ok": bool(overall_ok),
194
+ "strength_message": "OK: stress < yield" if passes_yield else "Not OK: stress ≥ yield",
195
+ "service_message": "OK: deflection < L/180" if deflection_ok else "Not OK: deflection ≥ L/180",
196
+ },
197
+ "structured_message": json.dumps(structured, indent=2),
198
+ "steps_markdown": steps_md
199
+ }
200
+
201
+ # ---------- LLM helper (safe) ----------
202
+ def _format_chat(system_prompt: str, user_prompt: str) -> str:
203
+ if _tokenizer is None:
204
+ return system_prompt + "\n\n" + user_prompt
205
+ messages = [{"role":"system","content":system_prompt},{"role":"user","content":user_prompt}]
206
+ return _tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
207
+
208
+ def llm_explain(structured_message: str) -> str:
209
+ # Graceful fallback
210
+ if (not _USE_LLM) or (_tokenizer is None) or (_pipe is None):
211
+ try:
212
+ d = json.loads(structured_message)
213
+ so = d["verdicts"]["strength_ok"]
214
+ sv = d["verdicts"]["service_ok"]
215
+ s_msg = "OK" if so else "NOT OK"
216
+ v_msg = "OK" if sv else "NOT OK"
217
+ return f"Like a sturdy spatula holding a pancake: strength is {s_msg} and deflection (L/180) is {v_msg}."
218
+ except Exception:
219
+ return "Quick take: strength and deflection (L/180) checks computed; see the table and math."
220
+ system_prompt = (
221
+ "You explain engineering to a smart 5-year-old using a quick food analogy. "
222
+ "You ALWAYS respond in exactly ONE friendly sentence."
223
+ )
224
+ user_prompt = (
225
+ "Here are the inputs, formulas, and results for a cantilever beam calculation.\n"
226
+ "Summarize whether the beam is OK in strength and deflection.\n\n"
227
+ + structured_message
228
+ )
229
+ formatted = _format_chat(system_prompt, user_prompt)
230
+ out = _pipe(formatted, max_new_tokens=120, do_sample=True, temperature=0.4, return_full_text=False)
231
+ return out[0]["generated_text"].split("\n")[0]
232
+
233
+ # ---------- Gradio runner ----------
234
+ def run_once(L_m, P_kN, E_GPa, Sy_MPa, b_m, h_m, mode, a_m):
235
+ try:
236
+ P_N = float(P_kN) * 1e3
237
+ d = calc_cantilever_rect(
238
+ float(L_m), P_N, float(E_GPa), float(Sy_MPa), float(b_m), float(h_m),
239
+ mode, float(a_m) if a_m is not None else float(L_m)
240
+ )
241
+ df = pd.DataFrame([{
242
+ "σ_max [MPa]": round(d["results"]["sigma_max_MPa"], 3),
243
+ "FoS (yield) [-]": round(d["results"]["safety_factor_yield"], 3),
244
+ "δ [mm]": round(d["results"]["delta_mm"], 3),
245
+ "FoS (deflection) [-]": round(d["results"]["fos_deflection"], 3),
246
+ "Strength Verdict": d["verdict"]["strength_message"],
247
+ "Deflection Verdict": d["verdict"]["service_message"],
248
+ }])
249
+ narrative = llm_explain(d["structured_message"])
250
+ return df, narrative, d["steps_markdown"], ""
251
+ except Exception as e:
252
+ return pd.DataFrame(), "", "", f"Input error:\n{e}"
253
+
254
+ with gr.Blocks(title="Cantilever Rectangular Beam — Point Load") as demo:
255
+ gr.Markdown("# Cantilever Rectangular Beam — Point Load (Free End or at Position a)")
256
+ gr.Markdown(SCOPE_MD)
257
+
258
+ with gr.Row():
259
+ with gr.Column():
260
+ gr.Markdown("### Load & Material")
261
+ L_m = gr.Number(value=2.0, label="Beam length L [m]")
262
+ P_kN = gr.Number(value=5.0, label="Point load P [kN]")
263
+ E_GPa = gr.Number(value=200., label="Elastic modulus E [GPa]")
264
+ Sy_MPa= gr.Number(value=250., label="Yield strength Sy [MPa]")
265
+ with gr.Column():
266
+ gr.Markdown("### Rectangular Section")
267
+ b_m = gr.Number(value=0.05, label="Width b [m]")
268
+ h_m = gr.Number(value=0.10, label="Height h [m]")
269
+
270
+ # Load position controls
271
+ mode = gr.Radio(
272
+ ["Free end (a = L)", "At position a"],
273
+ value="Free end (a = L)",
274
+ label="Load position mode"
275
+ )
276
+ a_m = gr.Number(value=2.0, label="a [m] (distance from fixed end)", visible=False)
277
+
278
+ def _toggle_a(selected):
279
+ return gr.update(visible=(selected == "At position a"))
280
+
281
+ mode.change(_toggle_a, inputs=[mode], outputs=[a_m])
282
+
283
+ run_btn = gr.Button("Compute")
284
+
285
+ gr.Markdown("### Results")
286
+ results_df = gr.Dataframe(label="Numerical results", interactive=False)
287
+
288
+ gr.Markdown("### Explain the results")
289
+ explain_md = gr.Markdown()
290
+
291
+ gr.Markdown("### Show the math")
292
+ steps_md = gr.Markdown()
293
+
294
+ err_box = gr.Textbox(label="Errors", interactive=False)
295
+
296
+ run_btn.click(
297
+ fn=run_once,
298
+ inputs=[L_m, P_kN, E_GPa, Sy_MPa, b_m, h_m, mode, a_m],
299
+ outputs=[results_df, explain_md, steps_md, err_box]
300
+ )
301
+
302
+ if __name__ == "__main__":
303
+ demo.launch(debug=False)