Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
|
| 18 |
import gradio as gr
|
| 19 |
-
import
|
| 20 |
-
import
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 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 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
"""
|
| 71 |
-
validate_inputs(inp)
|
| 72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
c_m = inp.c_cm * 0.01 # cm -> m
|
| 79 |
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
-
|
| 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":
|
| 101 |
-
"
|
| 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 |
-
"
|
| 121 |
-
"
|
| 122 |
-
"
|
| 123 |
-
"
|
| 124 |
-
"
|
| 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
|
| 148 |
-
|
| 149 |
-
#
|
| 150 |
-
# 2
|
| 151 |
-
#
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 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
|
|
|
|
| 206 |
|
| 207 |
with gr.Row():
|
| 208 |
with gr.Column():
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
|
| 215 |
with gr.Accordion("Optional: Allowables", open=False):
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
|
| 220 |
run_btn = gr.Button("Compute")
|
| 221 |
|
| 222 |
with gr.Column():
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
record_json = gr.JSON(label="Structured record (for LLM)")
|
| 226 |
|
| 227 |
run_btn.click(
|
| 228 |
-
fn=
|
| 229 |
-
inputs=[
|
| 230 |
-
outputs=[
|
| 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()
|