File size: 6,675 Bytes
315fce5
 
 
 
 
 
 
 
 
a744d63
 
 
 
315fce5
 
 
 
 
 
 
25ba998
315fce5
a744d63
25ba998
 
 
315fce5
a744d63
315fce5
 
a744d63
 
315fce5
a744d63
 
315fce5
 
a744d63
315fce5
 
 
25ba998
315fce5
 
 
25ba998
315fce5
a744d63
315fce5
 
 
 
 
a744d63
315fce5
 
 
 
 
a744d63
25ba998
 
 
 
 
 
 
 
 
 
a744d63
25ba998
a744d63
25ba998
 
a744d63
25ba998
a744d63
25ba998
a744d63
 
25ba998
 
a744d63
 
315fce5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a744d63
 
315fce5
 
 
a744d63
 
 
25ba998
315fce5
 
 
 
 
 
a744d63
315fce5
 
 
 
 
a744d63
25ba998
315fce5
 
 
 
 
 
a744d63
 
315fce5
 
 
a744d63
25ba998
a744d63
25ba998
 
315fce5
25ba998
 
315fce5
a744d63
 
 
315fce5
25ba998
315fce5
 
 
 
25ba998
 
315fce5
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import math
import gradio
import pandas as pd

# ---------------- Settings ----------------
TITLE = "Simply-Supported I-Beam"
DESC = "Point load @ midspan or uniform load (UDL). SI units. Euler–Bernoulli."

# ---------------- Deterministic backend ----------------
def _compute(load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m,
             P_kN, w_kNpm, sigma_allow_MPa, defl_ratio):

    # --- Validation ---
    if L_m <= 0: raise ValueError("Span L must be > 0.")
    if E_GPa <= 0: raise ValueError("E must be > 0.")
    if Ix_m4 <= 0: raise ValueError("Ix must be > 0.")
    has_Sx = (mode == "I have Sx")
    if has_Sx and Sx_m3 <= 0: raise ValueError("Sx must be > 0.")
    if (not has_Sx) and depth_m <= 0: raise ValueError("Depth must be > 0 when Sx not provided.")
    if load_type == "Point @ midspan" and P_kN <= 0: raise ValueError("P must be > 0 for point load.")
    if load_type == "Uniform (UDL)" and w_kNpm <= 0: raise ValueError("w must be > 0 for UDL.")

    # --- Units ---
    E = E_GPa * 1e9
    P = P_kN * 1e3
    w = w_kNpm * 1e3

    # --- Section properties ---
    if has_Sx:
        Sx = Sx_m3
        c  = Ix_m4 / Sx
        h  = 2 * c
    else:
        h  = depth_m
        c  = h / 2
        Sx = Ix_m4 / c

    # --- Beam formulas ---
    if load_type == "Point @ midspan":
        Mmax = P * L_m / 4.0
        dmax = P * (L_m**3) / (48.0 * E * Ix_m4)
    else:
        Mmax = w * (L_m**2) / 8.0
        dmax = 5.0 * w * (L_m**4) / (384.0 * E * Ix_m4)

    sigma = Mmax / Sx

    # --- Checks ---
    stress_ok, SF = None, None
    if sigma_allow_MPa and sigma_allow_MPa > 0:
        sigma_MPa = sigma / 1e6
        SF = sigma_allow_MPa / max(sigma_MPa, 1e-12)
        stress_ok = sigma_MPa <= sigma_allow_MPa

    defl_ok, defl_lim = None, None
    if defl_ratio and defl_ratio > 0:
        defl_lim = (L_m * 1e3) / defl_ratio
        defl_ok = (dmax * 1e3) <= defl_lim

    # --- Context text ---
    text = []
    text.append(f"### 🔍 Contextual Explanation")
    text.append(f"**Beam Type:** Simply supported ({load_type.lower()}) with span **L = {L_m:.2f} m**.")
    text.append(f"**Material:** E = {E_GPa:.1f} GPa, **Section:** Ix = {Ix_m4:.2e} m⁴, Sx = {Sx:.2e} m³, depth = {h:.3f} m.")
    text.append(f"\n#### Results Overview")
    text.append(f"- Maximum bending moment **Mmax = {Mmax/1e3:.2f} kN·m**.")
    text.append(f"- Maximum deflection **δmax = {dmax*1e3:.3f} mm**, ratio **δ/L = {dmax/L_m:.5f}**.")
    text.append(f"- Bending stress **σmax = {sigma/1e6:.2f} MPa**.")
    if sigma_allow_MPa > 0:
        if stress_ok:
            text.append(f"✅ Stress **({sigma/1e6:.2f} MPa)** ≤ allowable **({sigma_allow_MPa:.2f} MPa)**.")
        else:
            text.append(f"⚠️ Stress **({sigma/1e6:.2f} MPa)** exceeds allowable **({sigma_allow_MPa:.2f} MPa)**.")
    if defl_ratio > 0:
        if defl_ok:
            text.append(f"✅ Deflection **({dmax*1e3:.3f} mm)** ≤ limit (L/{defl_ratio:.0f} = {defl_lim:.2f} mm).")
        else:
            text.append(f"⚠️ Deflection **({dmax*1e3:.3f} mm)** exceeds limit (L/{defl_ratio:.0f} = {defl_lim:.2f} mm).")
    text.append("\n#### Engineering Meaning")
    text.append("The bending moment represents internal torque; deflection is beam sag. "
                "Safe design ensures both remain below limits to prevent failure or deformation.")
    context_text = "\n".join(text)

    # --- Return two outputs ---
    data = {
        "inputs": {
            "load_type": load_type,
            "L_m": L_m, "E_GPa": E_GPa, "Ix_m4": Ix_m4,
            "Sx_m3": Sx, "depth_m": h,
            "P_kN": P_kN if load_type.startswith("Point") else 0.0,
            "w_kN_per_m": w_kNpm if load_type.startswith("Uniform") else 0.0,
            "sigma_allow_MPa": sigma_allow_MPa or None,
            "deflection_ratio": defl_ratio or None,
        },
        "solution": {
            "M_max_kN_m": Mmax / 1e3,
            "sigma_max_MPa": sigma / 1e6,
            "delta_max_mm": dmax * 1e3,
            "delta_over_L": dmax / L_m,
        },
        "checks": {
            "stress_ok": stress_ok, "SF_stress": SF,
            "deflection_ok": defl_ok,
            "deflection_limit_mm": defl_lim, "deflection_ratio": defl_ratio or None,
        },
    }

    return data, context_text


# ---------------- Gradio UI ----------------
with gradio.Blocks() as demo:
    gradio.Markdown(f"# {TITLE}")
    gradio.Markdown(DESC)

    with gradio.Row():
        load_type = gradio.Radio(choices=["Point @ midspan", "Uniform (UDL)"], value="Point @ midspan", label="Load case")
        L_m   = gradio.Number(value=3.0, label="Span L [m]", precision=4)
        E_GPa = gradio.Number(value=200.0, label="Young's modulus E [GPa]", precision=3)
        Ix_m4 = gradio.Number(value=8.0e-6, label="Second moment Ix [m^4]", precision=10)

    with gradio.Row():
        mode = gradio.Radio(choices=["I have Sx", "I have depth (h) only"], value="I have Sx", label="Section input")
        Sx_m3   = gradio.Number(value=4.0e-4, label="Section modulus Sx [m^3]", precision=8, visible=True)
        depth_m = gradio.Number(value=0.30, label="Depth h [m]", precision=4, visible=False)

    def _toggle(v):
        return gradio.update(visible=(v=="I have Sx")), gradio.update(visible=(v!="I have Sx"))
    mode.change(_toggle, inputs=mode, outputs=[Sx_m3, depth_m])

    with gradio.Row():
        P_kN   = gradio.Number(value=20.0, label="Point load P [kN]", precision=3)
        w_kNpm = gradio.Number(value=5.0,  label="UDL w [kN/m]", precision=4)

    with gradio.Row():
        sigma_allow = gradio.Number(value=250.0, label="Allowable stress [MPa] (0=skip)", precision=3)
        defl_ratio  = gradio.Number(value=360,    label="Deflection limit L/ratio (0=skip)", precision=0)

    # --- Two output sections ---
    with gradio.Tab("Numerical Results"):
        out = gradio.JSON(label="Results")

    with gradio.Tab("Understanding the Results"):
        context_out = gradio.Markdown()

    inputs = [load_type, L_m, E_GPa, Ix_m4, mode, Sx_m3, depth_m,
              P_kN, w_kNpm, sigma_allow, defl_ratio]

    for comp in inputs:
        comp.change(fn=_compute, inputs=inputs, outputs=[out, context_out])

    gradio.Examples(
        examples=[
            ["Point @ midspan", 3.0, 200.0, 8.0e-6, "I have Sx", 4.0e-4, 0.30, 20.0, 0.0, 250.0, 360],
            ["Uniform (UDL)", 4.5, 200.0, 1.2e-5, "I have Sx", 6.0e-4, 0.35, 0.0, 8.0, 200.0, 360],
            ["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],
        ],
        inputs=inputs,
        label="Examples",
        cache_examples=False,
    )

demo.launch()