Spaces:
Sleeping
Sleeping
| # -*- coding: utf-8 -*- | |
| """Untitled3.ipynb | |
| Automatically generated by Colab. | |
| Original file is located at | |
| https://colab.research.google.com/drive/11sS74P6WlrbXCzSh9_b8ELPTtcfdsz8s | |
| """ | |
| # -*- coding: utf-8 -*- | |
| # HW3: Deterministic calculator + LLM explanation (Gradio) | |
| # Topic: Simply supported beam with center point load (max bending stress & midspan deflection) | |
| import json | |
| import math | |
| from dataclasses import dataclass, asdict | |
| import gradio as gr | |
| import numpy as np | |
| import pandas as pd | |
| LLM_MODEL_ID = "google/flan-t5-small" | |
| try: | |
| from transformers import pipeline | |
| _llm = pipeline("text2text-generation", model=LLM_MODEL_ID) | |
| except Exception: | |
| _llm = None # no transformers | |
| # ----------------------------- | |
| # ----------------------------- | |
| class BeamInputs: | |
| L_m: float # 跨度(m) | |
| P_kN: float # 中点集中力(kN) | |
| E_GPa: float # 弹性模量(GPa) | |
| I_cm4: float # 截面惯性矩(cm^4) | |
| c_cm: float # 中性轴到受拉/受压边缘距离(cm)- 极纤维距离 | |
| allowable_MPa: float | None = None # 允许应力(MPa,可选) | |
| yield_MPa: float | None = None # 屈服强度(MPa,可选) | |
| safety_factor: float | None = None # 安全系数 n(可选) | |
| def _validate(x, lo, hi, name): | |
| if not (lo <= x <= hi): | |
| raise gr.Error(f"{name} 超出有效范围 [{lo}, {hi}]") | |
| def validate_inputs(inp: BeamInputs): | |
| _validate(inp.L_m, 0.5, 50.0, "跨度 L (m)") | |
| _validate(inp.P_kN, 0.1, 2000.0, "荷载 P (kN)") | |
| _validate(inp.E_GPa, 1.0, 300.0, "弹性模量 E (GPa)") | |
| _validate(inp.I_cm4, 1.0, 1e9, "惯性矩 I (cm^4)") | |
| _validate(inp.c_cm, 0.01, 200.0, "极纤维距离 c (cm)") | |
| if inp.safety_factor is not None: | |
| _validate(inp.safety_factor, 1.0, 5.0, "安全系数 n") | |
| if inp.allowable_MPa is not None: | |
| _validate(inp.allowable_MPa, 1.0, 5000.0, "允许应力 (MPa)") | |
| if inp.yield_MPa is not None: | |
| _validate(inp.yield_MPa, 10.0, 10000.0, "屈服强度 (MPa)") | |
| def beam_calc(inp: BeamInputs): | |
| """ | |
| Simply supported beam with a centered point load P. | |
| M_max = P*L/4 | |
| sigma_max = M_max * c / I | |
| delta_mid = P * L^3 / (48 * E * I) | |
| 单位处理见下。 | |
| """ | |
| validate_inputs(inp) | |
| P_N = inp.P_kN * 1_000 # kN -> N | |
| L = inp.L_m # m | |
| E_Pa = inp.E_GPa * 1e9 # GPa -> Pa | |
| I_m4 = inp.I_cm4 * 1e-8 # cm^4 -> m^4 (1 cm = 0.01 m → (0.01)^4 = 1e-8) | |
| c_m = inp.c_cm * 0.01 # cm -> m | |
| M_max_Nm = P_N * L / 4.0 | |
| sigma_Pa = M_max_Nm * c_m / I_m4 | |
| sigma_MPa = sigma_Pa / 1e6 | |
| delta_m = P_N * (L**3) / (48 * E_Pa * I_m4) | |
| delta_mm = delta_m * 1e3 | |
| allow = None | |
| if inp.allowable_MPa is not None: | |
| allow = float(inp.allowable_MPa) | |
| elif inp.yield_MPa is not None and inp.safety_factor is not None: | |
| allow = float(inp.yield_MPa) / float(inp.safety_factor) | |
| utilization = (sigma_MPa / allow) if (allow and allow > 0) else None | |
| pass_check = (utilization <= 1.0) if utilization is not None else None | |
| record = { | |
| "scope": "Simply supported beam, centered point load; small deflection; linear elastic; prismatic section.", | |
| "inputs": { | |
| "L_m": inp.L_m, | |
| "P_kN": inp.P_kN, | |
| "E_GPa": inp.E_GPa, | |
| "I_cm4": inp.I_cm4, | |
| "c_cm": inp.c_cm, | |
| "allowable_MPa": inp.allowable_MPa, | |
| "yield_MPa": inp.yield_MPa, | |
| "safety_factor": inp.safety_factor, | |
| }, | |
| "units": { | |
| "L_m": "m", "P_kN": "kN", "E_GPa": "GPa", | |
| "I_cm4": "cm^4", "c_cm": "cm", | |
| "sigma_MPa": "MPa", "M_max_kN_m": "kN·m", "delta_mm": "mm", | |
| }, | |
| "formulas": { | |
| "M_max": "P*L/4", | |
| "sigma_max": "M_max * c / I", | |
| "delta_mid": "P * L^3 / (48 * E * I)" | |
| }, | |
| "results": { | |
| "M_max_kN_m": M_max_Nm / 1_000.0, # N·m -> kN·m | |
| "sigma_MPa": sigma_MPa, | |
| "delta_mm": delta_mm, | |
| "allowable_MPa": allow, | |
| "utilization": utilization, | |
| "pass_check": pass_check | |
| }, | |
| "notes": [ | |
| "If allowable stress is not provided, pass/fail is left as None.", | |
| "For non-rectangular/I-beam sections, user provides I and c (or catalogs)." | |
| ] | |
| } | |
| table = pd.DataFrame( | |
| { | |
| "Quantity": ["M_max", "sigma_max", "delta_mid", "allowable", "utilization", "pass"], | |
| "Value": [ | |
| round(record["results"]["M_max_kN_m"], 4), | |
| round(record["results"]["sigma_MPa"], 4), | |
| round(record["results"]["delta_mm"], 4), | |
| (None if allow is None else round(allow, 4)), | |
| (None if utilization is None else round(utilization, 4)), | |
| str(pass_check) | |
| ], | |
| "Unit": ["kN·m", "MPa", "mm", "MPa", "-", "-"] | |
| } | |
| ) | |
| return record, table | |
| # ----------------------------- | |
| # 2) LLM | |
| # ----------------------------- | |
| def explain_record(record: dict) -> str: | |
| compact = json.dumps(record, ensure_ascii=False) | |
| prompt = ( | |
| "You are an engineering assistant. Explain the beam calculation results clearly for a non-expert.\n" | |
| "Use the JSON below. Restate the inputs (with units), show the formulas, plug in numbers, and summarize.\n" | |
| "If allowable stress is present, state pass/fail and utilization. Avoid hallucinations; only use given data.\n" | |
| f"JSON:\n{compact}\n" | |
| "Write 120-200 words. Use bullet points for steps. Keep units." | |
| ) | |
| if _llm is not None: | |
| try: | |
| out = _llm(prompt, max_new_tokens=220, do_sample=False)[0]["generated_text"] | |
| return out | |
| except Exception: | |
| pass | |
| r = record["results"] | |
| i = record["inputs"] | |
| lines = [ | |
| "### Explanation", | |
| f"- Span L = {i['L_m']} m; Center load P = {i['P_kN']} kN.", | |
| f"- E = {i['E_GPa']} GPa; I = {i['I_cm4']} cm^4; c = {i['c_cm']} cm.", | |
| "- Formulas: M=P·L/4; σ=M·c/I; δ=P·L³/(48·E·I).", | |
| f"- Computed: M_max = {r['M_max_kN_m']:.3f} kN·m; σ_max = {r['sigma_MPa']:.3f} MPa; δ_mid = {r['delta_mm']:.3f} mm.", | |
| ] | |
| if r["allowable_MPa"] is not None: | |
| lines.append(f"- Allowable = {r['allowable_MPa']:.3f} MPa; utilization = {r['utilization']:.3f}.") | |
| lines.append(f"- Pass/Fail: {r['pass_check']}.") | |
| else: | |
| lines.append("- No allowable stress provided → pass/fail not evaluated.") | |
| return "\n".join(lines) | |
| # ----------------------------- | |
| # 3) Gradio | |
| # ----------------------------- | |
| def run_calc(L_m, P_kN, E_GPa, I_cm4, c_cm, allowable_MPa, yield_MPa, safety_factor): | |
| allowable = None if (allowable_MPa is None or allowable_MPa == "") else float(allowable_MPa) | |
| fy = None if (yield_MPa is None or yield_MPa == "") else float(yield_MPa) | |
| n = None if (safety_factor is None or safety_factor == "") else float(safety_factor) | |
| inp = BeamInputs( | |
| L_m=float(L_m), P_kN=float(P_kN), E_GPa=float(E_GPa), | |
| I_cm4=float(I_cm4), c_cm=float(c_cm), | |
| allowable_MPa=allowable, yield_MPa=fy, safety_factor=n | |
| ) | |
| rec, table = beam_calc(inp) | |
| expl = explain_record(rec) | |
| return table, expl, rec | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Simply Supported Beam — Center Load\nDeterministic calculator + LLM explanation (first principles).") | |
| with gr.Row(): | |
| with gr.Column(): | |
| L_m = gr.Slider(0.5, 30.0, value=6.0, step=0.1, label="Span L (m)") | |
| P_kN = gr.Slider(0.1, 500.0, value=20.0, step=0.1, label="Center load P (kN)") | |
| E_GPa = gr.Slider(10.0, 300.0, value=200.0, step=1.0, label="Elastic modulus E (GPa)") | |
| I_cm4 = gr.Number(value=8000.0, label="Second moment of area I (cm^4)") # 例如 IPE/I型钢量级 | |
| c_cm = gr.Number(value=15.0, label="Extreme fiber distance c (cm)") | |
| with gr.Accordion("Optional: Allowables", open=False): | |
| allowable_MPa = gr.Number(value=None, label="Allowable stress σ_allow (MPa)") | |
| yield_MPa = gr.Number(value=None, label="Yield strength fy (MPa)") | |
| safety_factor = gr.Number(value=1.5, label="Safety factor n") | |
| run_btn = gr.Button("Compute") | |
| with gr.Column(): | |
| results = gr.Dataframe(headers=["Quantity", "Value", "Unit"], label="Numerical results", wrap=True) | |
| explanation = gr.Markdown(label="Explain the results") | |
| record_json = gr.JSON(label="Structured record (for LLM)") | |
| run_btn.click( | |
| fn=run_calc, | |
| inputs=[L_m, P_kN, E_GPa, I_cm4, c_cm, allowable_MPa, yield_MPa, safety_factor], | |
| outputs=[results, explanation, record_json], | |
| api_name="run_calc", | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |