EricCRX commited on
Commit
cc767c4
·
verified ·
1 Parent(s): 451dbc3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -194
app.py CHANGED
@@ -11,225 +11,130 @@ Original file is located at
11
  # HW3: Deterministic calculator + LLM explanation (Gradio)
12
  # Topic: Simply supported beam with center point load (max bending stress & midspan deflection)
13
 
14
- import json
15
- import math
16
- from dataclasses import dataclass, asdict
17
 
18
  import gradio as gr
19
- import numpy as np
20
- import pandas as pd
21
-
22
-
23
- LLM_MODEL_ID = "google/flan-t5-small"
24
-
25
- try:
26
- from transformers import pipeline
27
- _llm = pipeline("text2text-generation", model=LLM_MODEL_ID)
28
- except Exception:
29
- _llm = None # no transformers
30
-
31
- # -----------------------------
32
-
33
- # -----------------------------
34
-
35
- @dataclass
36
- class BeamInputs:
37
- L_m: float # 跨度(m)
38
- P_kN: float # 中点集中力(kN)
39
- E_GPa: float # 弹性模量(GPa)
40
- I_cm4: float # 截面惯性矩(cm^4)
41
- c_cm: float # 中性轴到受拉/受压边缘距离(cm)- 极纤维距离
42
- allowable_MPa: float | None = None # 允许应力(MPa,可选)
43
- yield_MPa: float | None = None # 屈服强度(MPa,可选)
44
- safety_factor: float | None = None # 安全系数 n(可选)
45
-
46
- def _validate(x, lo, hi, name):
47
- if not (lo <= x <= hi):
48
- raise gr.Error(f"{name} 超出有效范围 [{lo}, {hi}]")
49
-
50
- def validate_inputs(inp: BeamInputs):
51
- _validate(inp.L_m, 0.5, 50.0, "跨度 L (m)")
52
- _validate(inp.P_kN, 0.1, 2000.0, "荷载 P (kN)")
53
- _validate(inp.E_GPa, 1.0, 300.0, "弹性模量 E (GPa)")
54
- _validate(inp.I_cm4, 1.0, 1e9, "惯性矩 I (cm^4)")
55
- _validate(inp.c_cm, 0.01, 200.0, "极纤维距离 c (cm)")
56
- if inp.safety_factor is not None:
57
- _validate(inp.safety_factor, 1.0, 5.0, "安全系数 n")
58
- if inp.allowable_MPa is not None:
59
- _validate(inp.allowable_MPa, 1.0, 5000.0, "允许应力 (MPa)")
60
- if inp.yield_MPa is not None:
61
- _validate(inp.yield_MPa, 10.0, 10000.0, "屈服强度 (MPa)")
62
-
63
- def beam_calc(inp: BeamInputs):
64
  """
65
- Simply supported beam with a centered point load P.
66
- M_max = P*L/4
67
- sigma_max = M_max * c / I
68
- delta_mid = P * L^3 / (48 * E * I)
69
- 单位处理见下。
 
 
 
 
 
 
 
70
  """
71
- validate_inputs(inp)
72
 
 
 
 
 
 
73
 
74
- P_N = inp.P_kN * 1_000 # kN -> N
75
- L = inp.L_m # m
76
- E_Pa = inp.E_GPa * 1e9 # GPa -> Pa
77
- I_m4 = inp.I_cm4 * 1e-8 # cm^4 -> m^4 (1 cm = 0.01 m → (0.01)^4 = 1e-8)
78
- c_m = inp.c_cm * 0.01 # cm -> m
79
 
 
 
 
80
 
81
- M_max_Nm = P_N * L / 4.0
82
- sigma_Pa = M_max_Nm * c_m / I_m4
83
- sigma_MPa = sigma_Pa / 1e6
84
- delta_m = P_N * (L**3) / (48 * E_Pa * I_m4)
85
- delta_mm = delta_m * 1e3
86
-
87
-
88
- allow = None
89
- if inp.allowable_MPa is not None:
90
- allow = float(inp.allowable_MPa)
91
- elif inp.yield_MPa is not None and inp.safety_factor is not None:
92
- allow = float(inp.yield_MPa) / float(inp.safety_factor)
93
-
94
- utilization = (sigma_MPa / allow) if (allow and allow > 0) else None
95
- pass_check = (utilization <= 1.0) if utilization is not None else None
96
-
97
  record = {
98
- "scope": "Simply supported beam, centered point load; small deflection; linear elastic; prismatic section.",
99
  "inputs": {
100
- "L_m": inp.L_m,
101
- "P_kN": inp.P_kN,
102
- "E_GPa": inp.E_GPa,
103
- "I_cm4": inp.I_cm4,
104
- "c_cm": inp.c_cm,
105
- "allowable_MPa": inp.allowable_MPa,
106
- "yield_MPa": inp.yield_MPa,
107
- "safety_factor": inp.safety_factor,
108
- },
109
- "units": {
110
- "L_m": "m", "P_kN": "kN", "E_GPa": "GPa",
111
- "I_cm4": "cm^4", "c_cm": "cm",
112
- "sigma_MPa": "MPa", "M_max_kN_m": "kN·m", "delta_mm": "mm",
113
- },
114
- "formulas": {
115
- "M_max": "P*L/4",
116
- "sigma_max": "M_max * c / I",
117
- "delta_mid": "P * L^3 / (48 * E * I)"
118
  },
119
  "results": {
120
- "M_max_kN_m": M_max_Nm / 1_000.0, # N·m -> kN·m
121
- "sigma_MPa": sigma_MPa,
122
- "delta_mm": delta_mm,
123
- "allowable_MPa": allow,
124
- "utilization": utilization,
125
- "pass_check": pass_check
126
- },
127
- "notes": [
128
- "If allowable stress is not provided, pass/fail is left as None.",
129
- "For non-rectangular/I-beam sections, user provides I and c (or catalogs)."
130
- ]
131
- }
132
-
133
- table = pd.DataFrame(
134
- {
135
- "Quantity": ["M_max", "sigma_max", "delta_mid", "allowable", "utilization", "pass"],
136
- "Value": [
137
- round(record["results"]["M_max_kN_m"], 4),
138
- round(record["results"]["sigma_MPa"], 4),
139
- round(record["results"]["delta_mm"], 4),
140
- (None if allow is None else round(allow, 4)),
141
- (None if utilization is None else round(utilization, 4)),
142
- str(pass_check)
143
- ],
144
- "Unit": ["kN·m", "MPa", "mm", "MPa", "-", "-"]
145
  }
146
- )
147
- return record, table
148
-
149
- # -----------------------------
150
- # 2) LLM
151
- # -----------------------------
152
-
153
- def explain_record(record: dict) -> str:
154
- compact = json.dumps(record, ensure_ascii=False)
155
- prompt = (
156
- "You are an engineering assistant. Explain the beam calculation results clearly for a non-expert.\n"
157
- "Use the JSON below. Restate the inputs (with units), show the formulas, plug in numbers, and summarize.\n"
158
- "If allowable stress is present, state pass/fail and utilization. Avoid hallucinations; only use given data.\n"
159
- f"JSON:\n{compact}\n"
160
- "Write 120-200 words. Use bullet points for steps. Keep units."
161
- )
162
- if _llm is not None:
163
- try:
164
- out = _llm(prompt, max_new_tokens=220, do_sample=False)[0]["generated_text"]
165
- return out
166
- except Exception:
167
- pass
168
-
169
- r = record["results"]
170
- i = record["inputs"]
171
- lines = [
172
- "### Explanation",
173
- f"- Span L = {i['L_m']} m; Center load P = {i['P_kN']} kN.",
174
- f"- E = {i['E_GPa']} GPa; I = {i['I_cm4']} cm^4; c = {i['c_cm']} cm.",
175
- "- Formulas: M=P·L/4; σ=M·c/I; δ=P·L³/(48·E·I).",
176
- f"- Computed: M_max = {r['M_max_kN_m']:.3f} kN·m; σ_max = {r['sigma_MPa']:.3f} MPa; δ_mid = {r['delta_mm']:.3f} mm.",
177
- ]
178
- if r["allowable_MPa"] is not None:
179
- lines.append(f"- Allowable = {r['allowable_MPa']:.3f} MPa; utilization = {r['utilization']:.3f}.")
180
- lines.append(f"- Pass/Fail: {r['pass_check']}.")
181
- else:
182
- lines.append("- No allowable stress provided → pass/fail not evaluated.")
183
- return "\n".join(lines)
184
-
185
- # -----------------------------
186
- # 3) Gradio
187
- # -----------------------------
188
-
189
- def run_calc(L_m, P_kN, E_GPa, I_cm4, c_cm, allowable_MPa, yield_MPa, safety_factor):
190
-
191
- allowable = None if (allowable_MPa is None or allowable_MPa == "") else float(allowable_MPa)
192
- fy = None if (yield_MPa is None or yield_MPa == "") else float(yield_MPa)
193
- n = None if (safety_factor is None or safety_factor == "") else float(safety_factor)
194
-
195
- inp = BeamInputs(
196
- L_m=float(L_m), P_kN=float(P_kN), E_GPa=float(E_GPa),
197
- I_cm4=float(I_cm4), c_cm=float(c_cm),
198
- allowable_MPa=allowable, yield_MPa=fy, safety_factor=n
199
- )
200
- rec, table = beam_calc(inp)
201
- expl = explain_record(rec)
202
- return table, expl, rec
203
-
204
  with gr.Blocks() as demo:
205
- gr.Markdown("# Simply Supported Beam — Center Load\nDeterministic calculator + LLM explanation (first principles).")
 
206
 
207
  with gr.Row():
208
  with gr.Column():
209
- L_m = gr.Slider(0.5, 30.0, value=6.0, step=0.1, label="Span L (m)")
210
- P_kN = gr.Slider(0.1, 500.0, value=20.0, step=0.1, label="Center load P (kN)")
211
- E_GPa = gr.Slider(10.0, 300.0, value=200.0, step=1.0, label="Elastic modulus E (GPa)")
212
- I_cm4 = gr.Number(value=8000.0, label="Second moment of area I (cm^4)") # 例如 IPE/I型钢量级
213
- c_cm = gr.Number(value=15.0, label="Extreme fiber distance c (cm)")
214
 
215
  with gr.Accordion("Optional: Allowables", open=False):
216
- allowable_MPa = gr.Number(value=None, label="Allowable stress σ_allow (MPa)")
217
- yield_MPa = gr.Number(value=None, label="Yield strength fy (MPa)")
218
- safety_factor = gr.Number(value=1.5, label="Safety factor n")
219
 
220
  run_btn = gr.Button("Compute")
221
 
222
  with gr.Column():
223
- results = gr.Dataframe(headers=["Quantity", "Value", "Unit"], label="Numerical results", wrap=True)
224
- explanation = gr.Markdown(label="Explain the results")
225
- record_json = gr.JSON(label="Structured record (for LLM)")
226
 
227
  run_btn.click(
228
- fn=run_calc,
229
- inputs=[L_m, P_kN, E_GPa, I_cm4, c_cm, allowable_MPa, yield_MPa, safety_factor],
230
- outputs=[results, explanation, record_json],
231
- api_name="run_calc",
232
  )
233
 
234
  if __name__ == "__main__":
235
- demo.launch()
 
11
  # HW3: Deterministic calculator + LLM explanation (Gradio)
12
  # Topic: Simply supported beam with center point load (max bending stress & midspan deflection)
13
 
14
+ # -*- coding: utf-8 -*-
15
+ # HW3: Deterministic calculator + Gradio + LLM explanation
16
+ # Simply Supported Beam with Center Load
17
 
18
  import gradio as gr
19
+ import math
20
+ import json
21
+ from transformers import pipeline
22
+
23
+ # -------------------------------
24
+ # 1. Deterministic backend function
25
+ # -------------------------------
26
+ def beam_calculator(L, P, E, I, c, sigma_allow=250.0, fy=350.0, n=1.5):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  """
28
+ Deterministic first-principles calculation for a simply supported beam
29
+ under a central point load.
30
+
31
+ Inputs:
32
+ L (m): span length
33
+ P (kN): central load
34
+ E (GPa): elastic modulus
35
+ I (cm^4): second moment of area
36
+ c (cm): extreme fiber distance
37
+ sigma_allow (MPa): allowable stress
38
+ fy (MPa): yield strength
39
+ n (–): safety factor
40
  """
 
41
 
42
+ # --- Unit conversions ---
43
+ P_N = P * 1000 # kN -> N
44
+ E_Pa = E * 1e9 # GPa -> Pa
45
+ I_m4 = I * 1e-8 # cm^4 -> m^4
46
+ c_m = c / 100.0 # cm -> m
47
 
48
+ # --- Mechanics formulas ---
49
+ M_max = P_N * L / 4.0 # Max bending moment (N·m)
50
+ delta_max = (P_N * L**3) / (48 * E_Pa * I_m4) # Max deflection (m)
51
+ sigma_max = M_max * c_m / I_m4 / 1e6 # Max stress (MPa)
 
52
 
53
+ # --- Safety checks ---
54
+ utilization_stress = sigma_max / sigma_allow
55
+ utilization_yield = sigma_max / (fy / n)
56
 
57
+ # Structured record
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  record = {
 
59
  "inputs": {
60
+ "L_m": L, "P_kN": P, "E_GPa": E, "I_cm4": I, "c_cm": c,
61
+ "sigma_allow_MPa": sigma_allow, "fy_MPa": fy, "safety_factor": n
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  },
63
  "results": {
64
+ "M_max_Nm": M_max,
65
+ "delta_max_m": delta_max,
66
+ "sigma_max_MPa": sigma_max,
67
+ "utilization_vs_allow": utilization_stress,
68
+ "utilization_vs_yield": utilization_yield
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  }
70
+ }
71
+ return record
72
+
73
+ # -------------------------------
74
+ # 2. LLM Explanation
75
+ # -------------------------------
76
+ explainer = pipeline("text-generation", model="distilgpt2")
77
+
78
+ def explain_results(record):
79
+ """Use LLM to produce a clear explanation grounded in numbers."""
80
+ text = json.dumps(record, indent=2)
81
+ prompt = f"""
82
+ You are an engineering assistant. A beam calculation was performed.
83
+ Here are the structured results:
84
+
85
+ {text}
86
+
87
+ Explain the results to a civil engineering student in plain English:
88
+ - Report the maximum bending moment, stress, and deflection.
89
+ - Compare stress with allowable stress and yield strength.
90
+ - State clearly if the beam is safe or unsafe.
91
+ Avoid speculation. Base explanation only on the provided numbers.
92
+ """
93
+ explanation = explainer(prompt, max_new_tokens=200)[0]["generated_text"]
94
+ return explanation
95
+
96
+ # -------------------------------
97
+ # 3. Wrapper for Gradio
98
+ # -------------------------------
99
+ def run_calculation(L, P, E, I, c, sigma_allow, fy, n):
100
+ try:
101
+ rec = beam_calculator(L, P, E, I, c, sigma_allow, fy, n)
102
+ explanation = explain_results(rec)
103
+ return json.dumps(rec["results"], indent=2), explanation
104
+ except Exception as e:
105
+ return {"error": str(e)}, "Error during calculation."
106
+
107
+ # -------------------------------
108
+ # 4. Gradio UI
109
+ # -------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  with gr.Blocks() as demo:
111
+ gr.Markdown("# Simply Supported Beam — Center Load")
112
+ gr.Markdown("Deterministic calculator + LLM explanation (first principles).")
113
 
114
  with gr.Row():
115
  with gr.Column():
116
+ L = gr.Slider(0.5, 30.0, value=2.0, label="Span L (m)")
117
+ P = gr.Slider(0.1, 500.0, value=10.0, label="Center load P (kN)")
118
+ E = gr.Slider(10, 300, value=200, label="Elastic modulus E (GPa)")
119
+ I = gr.Number(value=8000, label="Second moment of area I (cm^4)")
120
+ c = gr.Number(value=15, label="Extreme fiber distance c (cm)")
121
 
122
  with gr.Accordion("Optional: Allowables", open=False):
123
+ sigma_allow = gr.Number(value=250, label="Allowable stress σ_allow (MPa)")
124
+ fy = gr.Number(value=350, label="Yield strength fy (MPa)")
125
+ n = gr.Number(value=1.5, label="Safety factor n")
126
 
127
  run_btn = gr.Button("Compute")
128
 
129
  with gr.Column():
130
+ results_out = gr.Textbox(label="Numerical Results")
131
+ explain_out = gr.Textbox(label="LLM Explanation")
 
132
 
133
  run_btn.click(
134
+ fn=run_calculation,
135
+ inputs=[L, P, E, I, c, sigma_allow, fy, n],
136
+ outputs=[results_out, explain_out],
 
137
  )
138
 
139
  if __name__ == "__main__":
140
+ demo.launch()