Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -7,7 +7,10 @@ TITLE = "Simply-Supported I-Beam"
|
|
| 7 |
DESC = "Point load @ midspan or uniform load (UDL). SI units. Euler–Bernoulli."
|
| 8 |
|
| 9 |
# ---------------- Deterministic backend ----------------
|
| 10 |
-
def _compute(load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m,
|
|
|
|
|
|
|
|
|
|
| 11 |
if L_m <= 0: raise ValueError("Span L must be > 0.")
|
| 12 |
if E_GPa <= 0: raise ValueError("E must be > 0.")
|
| 13 |
if Ix_m4 <= 0: raise ValueError("Ix must be > 0.")
|
|
@@ -17,19 +20,22 @@ def _compute(load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m, P_kN, w_kNpm, s
|
|
| 17 |
if load_type == "Point @ midspan" and P_kN <= 0: raise ValueError("P must be > 0 for point load.")
|
| 18 |
if load_type == "Uniform (UDL)" and w_kNpm <= 0: raise ValueError("w must be > 0 for UDL.")
|
| 19 |
|
|
|
|
| 20 |
E = E_GPa * 1e9
|
| 21 |
P = P_kN * 1e3
|
| 22 |
w = w_kNpm * 1e3
|
| 23 |
|
|
|
|
| 24 |
if has_Sx:
|
| 25 |
Sx = Sx_m3
|
| 26 |
-
c
|
| 27 |
-
h
|
| 28 |
else:
|
| 29 |
-
h
|
| 30 |
-
c
|
| 31 |
Sx = Ix_m4 / c
|
| 32 |
|
|
|
|
| 33 |
if load_type == "Point @ midspan":
|
| 34 |
Mmax = P * L_m / 4.0
|
| 35 |
dmax = P * (L_m**3) / (48.0 * E * Ix_m4)
|
|
@@ -39,46 +45,44 @@ def _compute(load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m, P_kN, w_kNpm, s
|
|
| 39 |
|
| 40 |
sigma = Mmax / Sx
|
| 41 |
|
|
|
|
| 42 |
stress_ok, SF = None, None
|
| 43 |
if sigma_allow_MPa and sigma_allow_MPa > 0:
|
| 44 |
sigma_MPa = sigma / 1e6
|
| 45 |
SF = sigma_allow_MPa / max(sigma_MPa, 1e-12)
|
| 46 |
stress_ok = sigma_MPa <= sigma_allow_MPa
|
|
|
|
| 47 |
defl_ok, defl_lim = None, None
|
| 48 |
if defl_ratio and defl_ratio > 0:
|
| 49 |
defl_lim = (L_m * 1e3) / defl_ratio
|
| 50 |
defl_ok = (dmax * 1e3) <= defl_lim
|
| 51 |
|
| 52 |
-
#
|
| 53 |
text = []
|
| 54 |
text.append(f"### 🔍 Contextual Explanation")
|
| 55 |
text.append(f"**Beam Type:** Simply supported ({load_type.lower()}) with span **L = {L_m:.2f} m**.")
|
| 56 |
text.append(f"**Material:** E = {E_GPa:.1f} GPa, **Section:** Ix = {Ix_m4:.2e} m⁴, Sx = {Sx:.2e} m³, depth = {h:.3f} m.")
|
| 57 |
-
|
| 58 |
text.append(f"\n#### Results Overview")
|
| 59 |
text.append(f"- Maximum bending moment **Mmax = {Mmax/1e3:.2f} kN·m**.")
|
| 60 |
text.append(f"- Maximum deflection **δmax = {dmax*1e3:.3f} mm**, ratio **δ/L = {dmax/L_m:.5f}**.")
|
| 61 |
text.append(f"- Bending stress **σmax = {sigma/1e6:.2f} MPa**.")
|
| 62 |
-
|
| 63 |
if sigma_allow_MPa > 0:
|
| 64 |
if stress_ok:
|
| 65 |
-
text.append(f"✅
|
| 66 |
else:
|
| 67 |
-
text.append(f"⚠️
|
| 68 |
if defl_ratio > 0:
|
| 69 |
if defl_ok:
|
| 70 |
-
text.append(f"✅ Deflection **({dmax*1e3:.3f} mm)**
|
| 71 |
else:
|
| 72 |
-
text.append(f"⚠️ Deflection **({dmax*1e3:.3f} mm)** exceeds
|
| 73 |
-
|
| 74 |
text.append("\n#### Engineering Meaning")
|
| 75 |
-
text.append("The bending moment represents
|
| 76 |
-
"
|
| 77 |
-
"A safe design keeps both the stress and deflection below their respective limits to avoid failure or excessive sagging.")
|
| 78 |
-
|
| 79 |
context_text = "\n".join(text)
|
| 80 |
|
| 81 |
-
|
|
|
|
| 82 |
"inputs": {
|
| 83 |
"load_type": load_type,
|
| 84 |
"L_m": L_m, "E_GPa": E_GPa, "Ix_m4": Ix_m4,
|
|
@@ -96,12 +100,14 @@ def _compute(load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m, P_kN, w_kNpm, s
|
|
| 96 |
},
|
| 97 |
"checks": {
|
| 98 |
"stress_ok": stress_ok, "SF_stress": SF,
|
| 99 |
-
"deflection_ok": defl_ok,
|
| 100 |
-
"deflection_ratio": defl_ratio or None,
|
| 101 |
},
|
| 102 |
-
"context": context_text
|
| 103 |
}
|
| 104 |
|
|
|
|
|
|
|
|
|
|
| 105 |
# ---------------- Gradio UI ----------------
|
| 106 |
with gradio.Blocks() as demo:
|
| 107 |
gradio.Markdown(f"# {TITLE}")
|
|
@@ -109,13 +115,13 @@ with gradio.Blocks() as demo:
|
|
| 109 |
|
| 110 |
with gradio.Row():
|
| 111 |
load_type = gradio.Radio(choices=["Point @ midspan", "Uniform (UDL)"], value="Point @ midspan", label="Load case")
|
| 112 |
-
L_m
|
| 113 |
E_GPa = gradio.Number(value=200.0, label="Young's modulus E [GPa]", precision=3)
|
| 114 |
Ix_m4 = gradio.Number(value=8.0e-6, label="Second moment Ix [m^4]", precision=10)
|
| 115 |
|
| 116 |
with gradio.Row():
|
| 117 |
mode = gradio.Radio(choices=["I have Sx", "I have depth (h) only"], value="I have Sx", label="Section input")
|
| 118 |
-
Sx_m3
|
| 119 |
depth_m = gradio.Number(value=0.30, label="Depth h [m]", precision=4, visible=False)
|
| 120 |
|
| 121 |
def _toggle(v):
|
|
@@ -123,20 +129,23 @@ with gradio.Blocks() as demo:
|
|
| 123 |
mode.change(_toggle, inputs=mode, outputs=[Sx_m3, depth_m])
|
| 124 |
|
| 125 |
with gradio.Row():
|
| 126 |
-
P_kN
|
| 127 |
-
w_kNpm = gradio.Number(value=5.0,
|
| 128 |
|
| 129 |
with gradio.Row():
|
| 130 |
sigma_allow = gradio.Number(value=250.0, label="Allowable stress [MPa] (0=skip)", precision=3)
|
| 131 |
-
defl_ratio
|
| 132 |
|
|
|
|
| 133 |
with gradio.Tab("Numerical Results"):
|
| 134 |
out = gradio.JSON(label="Results")
|
| 135 |
|
| 136 |
with gradio.Tab("Understanding the Results"):
|
| 137 |
context_out = gradio.Markdown()
|
| 138 |
|
| 139 |
-
inputs = [load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m,
|
|
|
|
|
|
|
| 140 |
for comp in inputs:
|
| 141 |
comp.change(fn=_compute, inputs=inputs, outputs=[out, context_out])
|
| 142 |
|
|
|
|
| 7 |
DESC = "Point load @ midspan or uniform load (UDL). SI units. Euler–Bernoulli."
|
| 8 |
|
| 9 |
# ---------------- Deterministic backend ----------------
|
| 10 |
+
def _compute(load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m,
|
| 11 |
+
P_kN, w_kNpm, sigma_allow_MPa, defl_ratio):
|
| 12 |
+
|
| 13 |
+
# --- Validation ---
|
| 14 |
if L_m <= 0: raise ValueError("Span L must be > 0.")
|
| 15 |
if E_GPa <= 0: raise ValueError("E must be > 0.")
|
| 16 |
if Ix_m4 <= 0: raise ValueError("Ix must be > 0.")
|
|
|
|
| 20 |
if load_type == "Point @ midspan" and P_kN <= 0: raise ValueError("P must be > 0 for point load.")
|
| 21 |
if load_type == "Uniform (UDL)" and w_kNpm <= 0: raise ValueError("w must be > 0 for UDL.")
|
| 22 |
|
| 23 |
+
# --- Units ---
|
| 24 |
E = E_GPa * 1e9
|
| 25 |
P = P_kN * 1e3
|
| 26 |
w = w_kNpm * 1e3
|
| 27 |
|
| 28 |
+
# --- Section properties ---
|
| 29 |
if has_Sx:
|
| 30 |
Sx = Sx_m3
|
| 31 |
+
c = Ix_m4 / Sx
|
| 32 |
+
h = 2 * c
|
| 33 |
else:
|
| 34 |
+
h = depth_m
|
| 35 |
+
c = h / 2
|
| 36 |
Sx = Ix_m4 / c
|
| 37 |
|
| 38 |
+
# --- Beam formulas ---
|
| 39 |
if load_type == "Point @ midspan":
|
| 40 |
Mmax = P * L_m / 4.0
|
| 41 |
dmax = P * (L_m**3) / (48.0 * E * Ix_m4)
|
|
|
|
| 45 |
|
| 46 |
sigma = Mmax / Sx
|
| 47 |
|
| 48 |
+
# --- Checks ---
|
| 49 |
stress_ok, SF = None, None
|
| 50 |
if sigma_allow_MPa and sigma_allow_MPa > 0:
|
| 51 |
sigma_MPa = sigma / 1e6
|
| 52 |
SF = sigma_allow_MPa / max(sigma_MPa, 1e-12)
|
| 53 |
stress_ok = sigma_MPa <= sigma_allow_MPa
|
| 54 |
+
|
| 55 |
defl_ok, defl_lim = None, None
|
| 56 |
if defl_ratio and defl_ratio > 0:
|
| 57 |
defl_lim = (L_m * 1e3) / defl_ratio
|
| 58 |
defl_ok = (dmax * 1e3) <= defl_lim
|
| 59 |
|
| 60 |
+
# --- Context text ---
|
| 61 |
text = []
|
| 62 |
text.append(f"### 🔍 Contextual Explanation")
|
| 63 |
text.append(f"**Beam Type:** Simply supported ({load_type.lower()}) with span **L = {L_m:.2f} m**.")
|
| 64 |
text.append(f"**Material:** E = {E_GPa:.1f} GPa, **Section:** Ix = {Ix_m4:.2e} m⁴, Sx = {Sx:.2e} m³, depth = {h:.3f} m.")
|
|
|
|
| 65 |
text.append(f"\n#### Results Overview")
|
| 66 |
text.append(f"- Maximum bending moment **Mmax = {Mmax/1e3:.2f} kN·m**.")
|
| 67 |
text.append(f"- Maximum deflection **δmax = {dmax*1e3:.3f} mm**, ratio **δ/L = {dmax/L_m:.5f}**.")
|
| 68 |
text.append(f"- Bending stress **σmax = {sigma/1e6:.2f} MPa**.")
|
|
|
|
| 69 |
if sigma_allow_MPa > 0:
|
| 70 |
if stress_ok:
|
| 71 |
+
text.append(f"✅ Stress **({sigma/1e6:.2f} MPa)** ≤ allowable **({sigma_allow_MPa:.2f} MPa)**.")
|
| 72 |
else:
|
| 73 |
+
text.append(f"⚠️ Stress **({sigma/1e6:.2f} MPa)** exceeds allowable **({sigma_allow_MPa:.2f} MPa)**.")
|
| 74 |
if defl_ratio > 0:
|
| 75 |
if defl_ok:
|
| 76 |
+
text.append(f"✅ Deflection **({dmax*1e3:.3f} mm)** ≤ limit (L/{defl_ratio:.0f} = {defl_lim:.2f} mm).")
|
| 77 |
else:
|
| 78 |
+
text.append(f"⚠️ Deflection **({dmax*1e3:.3f} mm)** exceeds limit (L/{defl_ratio:.0f} = {defl_lim:.2f} mm).")
|
|
|
|
| 79 |
text.append("\n#### Engineering Meaning")
|
| 80 |
+
text.append("The bending moment represents internal torque; deflection is beam sag. "
|
| 81 |
+
"Safe design ensures both remain below limits to prevent failure or deformation.")
|
|
|
|
|
|
|
| 82 |
context_text = "\n".join(text)
|
| 83 |
|
| 84 |
+
# --- Return two outputs ---
|
| 85 |
+
data = {
|
| 86 |
"inputs": {
|
| 87 |
"load_type": load_type,
|
| 88 |
"L_m": L_m, "E_GPa": E_GPa, "Ix_m4": Ix_m4,
|
|
|
|
| 100 |
},
|
| 101 |
"checks": {
|
| 102 |
"stress_ok": stress_ok, "SF_stress": SF,
|
| 103 |
+
"deflection_ok": defl_ok,
|
| 104 |
+
"deflection_limit_mm": defl_lim, "deflection_ratio": defl_ratio or None,
|
| 105 |
},
|
|
|
|
| 106 |
}
|
| 107 |
|
| 108 |
+
return data, context_text
|
| 109 |
+
|
| 110 |
+
|
| 111 |
# ---------------- Gradio UI ----------------
|
| 112 |
with gradio.Blocks() as demo:
|
| 113 |
gradio.Markdown(f"# {TITLE}")
|
|
|
|
| 115 |
|
| 116 |
with gradio.Row():
|
| 117 |
load_type = gradio.Radio(choices=["Point @ midspan", "Uniform (UDL)"], value="Point @ midspan", label="Load case")
|
| 118 |
+
L_m = gradio.Number(value=3.0, label="Span L [m]", precision=4)
|
| 119 |
E_GPa = gradio.Number(value=200.0, label="Young's modulus E [GPa]", precision=3)
|
| 120 |
Ix_m4 = gradio.Number(value=8.0e-6, label="Second moment Ix [m^4]", precision=10)
|
| 121 |
|
| 122 |
with gradio.Row():
|
| 123 |
mode = gradio.Radio(choices=["I have Sx", "I have depth (h) only"], value="I have Sx", label="Section input")
|
| 124 |
+
Sx_m3 = gradio.Number(value=4.0e-4, label="Section modulus Sx [m^3]", precision=8, visible=True)
|
| 125 |
depth_m = gradio.Number(value=0.30, label="Depth h [m]", precision=4, visible=False)
|
| 126 |
|
| 127 |
def _toggle(v):
|
|
|
|
| 129 |
mode.change(_toggle, inputs=mode, outputs=[Sx_m3, depth_m])
|
| 130 |
|
| 131 |
with gradio.Row():
|
| 132 |
+
P_kN = gradio.Number(value=20.0, label="Point load P [kN]", precision=3)
|
| 133 |
+
w_kNpm = gradio.Number(value=5.0, label="UDL w [kN/m]", precision=4)
|
| 134 |
|
| 135 |
with gradio.Row():
|
| 136 |
sigma_allow = gradio.Number(value=250.0, label="Allowable stress [MPa] (0=skip)", precision=3)
|
| 137 |
+
defl_ratio = gradio.Number(value=360, label="Deflection limit L/ratio (0=skip)", precision=0)
|
| 138 |
|
| 139 |
+
# --- Two output sections ---
|
| 140 |
with gradio.Tab("Numerical Results"):
|
| 141 |
out = gradio.JSON(label="Results")
|
| 142 |
|
| 143 |
with gradio.Tab("Understanding the Results"):
|
| 144 |
context_out = gradio.Markdown()
|
| 145 |
|
| 146 |
+
inputs = [load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m,
|
| 147 |
+
P_kN, w_kNpm, sigma_allow, defl_ratio]
|
| 148 |
+
|
| 149 |
for comp in inputs:
|
| 150 |
comp.change(fn=_compute, inputs=inputs, outputs=[out, context_out])
|
| 151 |
|