apsora commited on
Commit
25ba998
·
verified ·
1 Parent(s): 315fce5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -29
app.py CHANGED
@@ -8,7 +8,6 @@ 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, P_kN, w_kNpm, sigma_allow_MPa, defl_ratio):
11
- # validate
12
  if L_m <= 0: raise ValueError("Span L must be > 0.")
13
  if E_GPa <= 0: raise ValueError("E must be > 0.")
14
  if Ix_m4 <= 0: raise ValueError("Ix must be > 0.")
@@ -16,34 +15,30 @@ def _compute(load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m, P_kN, w_kNpm, s
16
  if has_Sx and Sx_m3 <= 0: raise ValueError("Sx must be > 0.")
17
  if (not has_Sx) and depth_m <= 0: raise ValueError("Depth must be > 0 when Sx not provided.")
18
  if load_type == "Point @ midspan" and P_kN <= 0: raise ValueError("P must be > 0 for point load.")
19
- if load_type == "Uniform (UDL)" and w_kNpm <= 0: raise ValueError("w must be > 0 for UDL.")
20
 
21
- # units
22
- E = E_GPa * 1e9 # Pa
23
- P = P_kN * 1e3 # N
24
- w = w_kNpm * 1e3 # N/m
25
 
26
- # section modulus
27
  if has_Sx:
28
  Sx = Sx_m3
29
- c = Ix_m4 / Sx
30
- h = 2 * c
31
  else:
32
- h = depth_m
33
- c = h / 2
34
  Sx = Ix_m4 / c
35
 
36
- # closed-form results
37
  if load_type == "Point @ midspan":
38
  Mmax = P * L_m / 4.0
39
  dmax = P * (L_m**3) / (48.0 * E * Ix_m4)
40
- else: # UDL
41
  Mmax = w * (L_m**2) / 8.0
42
  dmax = 5.0 * w * (L_m**4) / (384.0 * E * Ix_m4)
43
 
44
- sigma = Mmax / Sx # Pa
45
 
46
- # checks
47
  stress_ok, SF = None, None
48
  if sigma_allow_MPa and sigma_allow_MPa > 0:
49
  sigma_MPa = sigma / 1e6
@@ -54,7 +49,35 @@ def _compute(load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m, P_kN, w_kNpm, s
54
  defl_lim = (L_m * 1e3) / defl_ratio
55
  defl_ok = (dmax * 1e3) <= defl_lim
56
 
57
- # compact dict result (easy to read in the UI)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  return {
59
  "inputs": {
60
  "load_type": load_type,
@@ -73,50 +96,55 @@ def _compute(load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m, P_kN, w_kNpm, s
73
  },
74
  "checks": {
75
  "stress_ok": stress_ok, "SF_stress": SF,
76
- "deflection_ok": defl_ok, "deflection_limit_mm": defl_lim, "deflection_ratio": defl_ratio or None,
 
77
  },
 
78
  }
79
 
80
- # ---------------- Gradio UI (simple, like your example) ----------------
81
  with gradio.Blocks() as demo:
82
  gradio.Markdown(f"# {TITLE}")
83
  gradio.Markdown(DESC)
84
 
85
  with gradio.Row():
86
  load_type = gradio.Radio(choices=["Point @ midspan", "Uniform (UDL)"], value="Point @ midspan", label="Load case")
87
- L_m = gradio.Number(value=3.0, label="Span L [m]", precision=4)
88
  E_GPa = gradio.Number(value=200.0, label="Young's modulus E [GPa]", precision=3)
89
  Ix_m4 = gradio.Number(value=8.0e-6, label="Second moment Ix [m^4]", precision=10)
90
 
91
  with gradio.Row():
92
  mode = gradio.Radio(choices=["I have Sx", "I have depth (h) only"], value="I have Sx", label="Section input")
93
- Sx_m3 = gradio.Number(value=4.0e-4, label="Section modulus Sx [m^3]", precision=8, visible=True)
94
- depth_m = gradio.Number(value=0.30, label="Depth h [m]", precision=4, visible=False)
95
 
96
  def _toggle(v):
97
  return gradio.update(visible=(v=="I have Sx")), gradio.update(visible=(v!="I have Sx"))
98
  mode.change(_toggle, inputs=mode, outputs=[Sx_m3, depth_m])
99
 
100
  with gradio.Row():
101
- P_kN = gradio.Number(value=20.0, label="Point load P [kN]", precision=3)
102
- w_kNpm = gradio.Number(value=5.0, label="UDL w [kN/m]", precision=4)
103
 
104
  with gradio.Row():
105
  sigma_allow = gradio.Number(value=250.0, label="Allowable stress [MPa] (0=skip)", precision=3)
106
- defl_ratio = gradio.Number(value=360, label="Deflection limit L/ratio (0=skip)", precision=0)
 
 
 
107
 
108
- out = gradio.JSON(label="Results")
 
109
 
110
- # Live updates on any input (like your example)
111
  inputs = [load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m, P_kN, w_kNpm, sigma_allow, defl_ratio]
112
  for comp in inputs:
113
- comp.change(fn=_compute, inputs=inputs, outputs=out)
114
 
115
  gradio.Examples(
116
  examples=[
117
  ["Point @ midspan", 3.0, 200.0, 8.0e-6, "I have Sx", 4.0e-4, 0.30, 20.0, 0.0, 250.0, 360],
118
- ["Uniform (UDL)", 4.5, 200.0, 1.2e-5, "I have Sx", 6.0e-4, 0.35, 0.0, 8.0, 200.0, 360],
119
- ["Uniform (UDL)", 2.5, 70.0, 3.5e-6, "I have depth (h) only", 0.0, 0.22, 0.0, 6.0, 160.0, 240],
120
  ],
121
  inputs=inputs,
122
  label="Examples",
 
8
 
9
  # ---------------- Deterministic backend ----------------
10
  def _compute(load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m, P_kN, w_kNpm, sigma_allow_MPa, defl_ratio):
 
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.")
 
15
  if has_Sx and Sx_m3 <= 0: raise ValueError("Sx must be > 0.")
16
  if (not has_Sx) and depth_m <= 0: raise ValueError("Depth must be > 0 when Sx not provided.")
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 = Ix_m4 / Sx
27
+ h = 2 * c
28
  else:
29
+ h = depth_m
30
+ c = h / 2
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)
36
+ else:
37
  Mmax = w * (L_m**2) / 8.0
38
  dmax = 5.0 * w * (L_m**4) / (384.0 * E * Ix_m4)
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
 
49
  defl_lim = (L_m * 1e3) / defl_ratio
50
  defl_ok = (dmax * 1e3) <= defl_lim
51
 
52
+ # Build explanation
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"✅ The beam’s stress **({sigma/1e6:.2f} MPa)** is below the allowable limit **({sigma_allow_MPa:.2f} MPa)**.")
66
+ else:
67
+ text.append(f"⚠️ The stress **({sigma/1e6:.2f} MPa)** exceeds the allowable limit **({sigma_allow_MPa:.2f} MPa)**.")
68
+ if defl_ratio > 0:
69
+ if defl_ok:
70
+ text.append(f"✅ Deflection **({dmax*1e3:.3f} mm)** is within the limit (L/{defl_ratio:.0f} = {defl_lim:.2f} mm).")
71
+ else:
72
+ text.append(f"⚠️ Deflection **({dmax*1e3:.3f} mm)** exceeds the limit (L/{defl_ratio:.0f} = {defl_lim:.2f} mm).")
73
+
74
+ text.append("\n#### Engineering Meaning")
75
+ text.append("The bending moment represents how much internal torque the beam experiences due to the applied load. "
76
+ "The deflection quantifies how much the beam bends under that load. "
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
  return {
82
  "inputs": {
83
  "load_type": load_type,
 
96
  },
97
  "checks": {
98
  "stress_ok": stress_ok, "SF_stress": SF,
99
+ "deflection_ok": defl_ok, "deflection_limit_mm": defl_lim,
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}")
108
  gradio.Markdown(DESC)
109
 
110
  with gradio.Row():
111
  load_type = gradio.Radio(choices=["Point @ midspan", "Uniform (UDL)"], value="Point @ midspan", label="Load case")
112
+ L_m = gradio.Number(value=3.0, label="Span L [m]", precision=4)
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 = gradio.Number(value=4.0e-4, label="Section modulus Sx [m^3]", precision=8, visible=True)
119
+ depth_m = gradio.Number(value=0.30, label="Depth h [m]", precision=4, visible=False)
120
 
121
  def _toggle(v):
122
  return gradio.update(visible=(v=="I have Sx")), gradio.update(visible=(v!="I have Sx"))
123
  mode.change(_toggle, inputs=mode, outputs=[Sx_m3, depth_m])
124
 
125
  with gradio.Row():
126
+ P_kN = gradio.Number(value=20.0, label="Point load P [kN]", precision=3)
127
+ w_kNpm = gradio.Number(value=5.0, label="UDL w [kN/m]", precision=4)
128
 
129
  with gradio.Row():
130
  sigma_allow = gradio.Number(value=250.0, label="Allowable stress [MPa] (0=skip)", precision=3)
131
+ defl_ratio = gradio.Number(value=360, label="Deflection limit L/ratio (0=skip)", precision=0)
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, P_kN, w_kNpm, sigma_allow, defl_ratio]
140
  for comp in inputs:
141
+ comp.change(fn=_compute, inputs=inputs, outputs=[out, context_out])
142
 
143
  gradio.Examples(
144
  examples=[
145
  ["Point @ midspan", 3.0, 200.0, 8.0e-6, "I have Sx", 4.0e-4, 0.30, 20.0, 0.0, 250.0, 360],
146
+ ["Uniform (UDL)", 4.5, 200.0, 1.2e-5, "I have Sx", 6.0e-4, 0.35, 0.0, 8.0, 200.0, 360],
147
+ ["Uniform (UDL)", 2.5, 70.0, 3.5e-6, "I have depth (h) only", 0.0, 0.22, 0.0, 6.0, 160.0, 240],
148
  ],
149
  inputs=inputs,
150
  label="Examples",