Spaces:
Sleeping
Sleeping
Upload 2 files
Browse files- app.py +235 -0
- requirements.txt +7 -0
app.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""Untitled3.ipynb
|
| 3 |
+
|
| 4 |
+
Automatically generated by Colab.
|
| 5 |
+
|
| 6 |
+
Original file is located at
|
| 7 |
+
https://colab.research.google.com/drive/11sS74P6WlrbXCzSh9_b8ELPTtcfdsz8s
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
# -*- coding: utf-8 -*-
|
| 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()
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio>=4.24
|
| 2 |
+
pandas
|
| 3 |
+
numpy
|
| 4 |
+
transformers>=4.41
|
| 5 |
+
sentencepiece
|
| 6 |
+
accelerate
|
| 7 |
+
huggingface_hub>=0.23
|