Spaces:
Sleeping
Sleeping
File size: 7,321 Bytes
ecade39 caab3f7 ecade39 |
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 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
import math # for infinity
import gradio # UI
import pandas # tables
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline # tiny LLM
def vessel_calc(Do_m: float, t_m: float, L_m: float,
Sy_MPa: float, E_GPa: float, nu: float) -> dict:
"""
Simplified thin-walled closed-end cylinder.
We compute maximum permissible internal pressure p_allow (yield, hoop governs).
- Inputs: outer diameter Do, thickness t, length L, yield Sy, E, nu (all SI; Sy/E in MPa/GPa)
- Early exit if thin-wall model not applicable.
Returns a structured dict with metadata, inputs, validation, results, verdict.
"""
# Precompute geometry checks
Di_m = Do_m - 2.0 * t_m
ratio = t_m / Do_m
issues = []
# Model applicability checks (hard stop)
if Di_m <= 0.0:
issues.append("Invalid geometry: inner diameter (Di) <= 0.")
if ratio < 0.005:
issues.append("Beyond model: wall too thin for this simplified thin-wall model (t/Do < 0.005).")
if ratio > 0.10:
issues.append("Beyond model: wall too thick for thin-wall theory (t/Do > 0.10).")
# Prepare base envelope
base = dict(
metadata={"calc": "Thin-walled cylinder p_allow", "units": "SI", "version": "1.0.0"},
inputs={"Do_m": Do_m, "t_m": t_m, "L_m": L_m, "Sy_MPa": Sy_MPa, "E_GPa": E_GPa, "nu": nu},
validation={"ratio_t_over_Do": ratio, "Di_m": Di_m, "issues": issues},
)
if issues:
return dict(
**base,
results={},
verdict={"overall_ok": False, "message": " ; ".join(issues)}
)
# Allowable pressure (hoop reaches yield at p_allow)
# sigma_theta = p * Di / (2 t) ; set sigma_theta = Sy => p_allow = 2 t Sy / Di
p_allow_MPa = (2.0 * t_m * Sy_MPa) / Di_m
# Stresses at p_allow for transparency (not returned individually)
sigma_theta_MPa = Sy_MPa
sigma_long_MPa = p_allow_MPa * Di_m / (4.0 * t_m)
# von Mises at p_allow (plane stress)
sigma_vm_MPa = math.sqrt(
sigma_theta_MPa**2 + sigma_long_MPa**2 - sigma_theta_MPa * sigma_long_MPa
)
# Optional elastic expansions (reported, but epsilons kept internal)
E_MPa = E_GPa * 1e3 # convert GPa -> MPa
eps_theta = (sigma_theta_MPa - nu * sigma_long_MPa) / E_MPa
eps_long = (sigma_long_MPa - nu * sigma_theta_MPa) / E_MPa
delta_d_mm = eps_theta * Do_m * 1e3
delta_L_mm = eps_long * L_m * 1e3
return dict(
**base,
results={
"p_allow_MPa": p_allow_MPa,
"sigma_vm_MPa_at_p_allow": sigma_vm_MPa,
"delta_d_mm": delta_d_mm,
"delta_L_mm": delta_L_mm
},
verdict={"overall_ok": True, "message": "Model valid; p_allow computed (hoop governs yield)."}
)
def _format_chat(system_prompt: str, user_prompt: str) -> str:
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
]
template = getattr(tokenizer, "chat_template", None)
return tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
def _llm_generate(prompt: str, max_tokens: int) -> str:
out = pipe(
prompt,
max_new_tokens=max_tokens,
do_sample=True,
temperature=0.5,
return_full_text=False,
)
return out[0]["generated_text"]
def llm_explain(d: dict) -> str:
# If invalid, just echo the message
if not d.get("verdict", {}).get("overall_ok", False):
return f"Explanation: {d['verdict']['message']}"
inp = d["inputs"]
res = d["results"]
val = d["validation"]
system_prompt = (
"You explain mechanics like a friendly TA. Be concise, clear, and stick to the provided data. Explain the data."
"Use units and avoid speculation. 6-7 sentences max. Include Maximum internal pressure and some information about the cylinder input data."
)
user_prompt = (
f"Cylinder (thin-wall, closed ends). Outer diameter Do={inp['Do_m']:.4g} m, thickness t={inp['t_m']:.4g} m. \n"
f"length L={inp['L_m']:.4g} m, yield strength Sy={inp['Sy_MPa']:.4g} MPa, E={inp['E_GPa']:.4g} GPa, nu={inp['nu']:.3g}.\n"
f"Results:\n"
f"The maximum permissible internal pressure is {res['p_allow_MPa']:.4g} MPa.\n"
f"von Mises at p_allow={res['sigma_vm_MPa_at_p_allow']:.4g} MPa; "
f"diameter growth Δd={res['delta_d_mm']:.4g} mm; length growth ΔL={res['delta_L_mm']:.4g} mm.\n"
f"State whether the design is within the model assumptions and what p_allow means."
)
formatted = _format_chat(system_prompt, user_prompt)
return _llm_generate(formatted, max_tokens=160)
# ---------------------------
# Glue: one run for the UI
# ---------------------------
def run_once(Do_m, t_m, L_m, Sy_MPa, E_GPa, nu):
d = vessel_calc(
Do_m=float(Do_m),
t_m=float(t_m),
L_m=float(L_m),
Sy_MPa=float(Sy_MPa),
E_GPa=float(E_GPa),
nu=float(nu),
)
if not d["verdict"]["overall_ok"]:
df = pandas.DataFrame([{"Message": d["verdict"]["message"]}])
narrative = d["verdict"]["message"]
return df, narrative
r = d["results"]
df = pandas.DataFrame([{
"Allowed Pressure [MPa]": round(r["p_allow_MPa"], 4),
"σ_vm at Allowed Pressure [MPa]": round(r["sigma_vm_MPa_at_p_allow"], 4),
"Δd [mm]": round(r["delta_d_mm"], 4),
"ΔL [mm]": round(r["delta_L_mm"], 4),
}])
narrative = llm_explain(d).split("\n")[0]
return df, narrative
with gradio.Blocks() as demo:
gradio.Markdown("# Allowable Pressure for Thin-Walled Cylinder (Simplified)")
gradio.Markdown(
"Computes the **maximum permissible internal pressure** for a thin-walled cylinder using hoop-stress yield. "
"Stops early if the thin-wall model is not applicable."
)
with gradio.Row():
Do_m = gradio.Number(value=1.0, label="Outer diameter Do [m]")
t_m = gradio.Number(value=0.01, label="Wall thickness t [m]")
L_m = gradio.Number(value=3.0, label="Length L [m] (for ΔL only)")
with gradio.Row():
Sy_MPa = gradio.Number(value=250.0, label="Yield strength Sy [MPa]")
E_GPa = gradio.Number(value=210.0, label="Elastic modulus E [GPa]")
nu = gradio.Number(value=0.30, label="Poisson's ratio ν [-]")
run_btn = gradio.Button("Compute")
results_df = gradio.Dataframe(label="Numerical results (deterministic)", interactive=False)
explain_md = gradio.Markdown(label="Explanation")
run_btn.click(
fn=run_once,
inputs=[Do_m, t_m, L_m, Sy_MPa, E_GPa, nu],
outputs=[results_df, explain_md]
)
gradio.Examples(
examples=[
# Valid thin-wall
[1.0, 0.01, 3.0, 250.0, 210.0, 0.30],
# Too thin (t/Do < 0.005) -> early stop
[0.8, 0.002, 2.5, 350.0, 200.0, 0.30],
# Too thick (t/Do > 0.10) -> early stop
[0.5, 0.06, 2.0, 300.0, 200.0, 0.30],
],
inputs=[Do_m, t_m, L_m, Sy_MPa, E_GPa, nu],
label="Representative cases",
examples_per_page=3,
cache_examples=False,
)
if __name__ == "__main__":
demo.launch(debug=True) |