Calculator / app.py
yusenthebot's picture
Upload 3 files
bc0c243 verified
import math
import gradio as gr
import pandas as pd
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
# Initialize the LLM model
MODEL_ID = "HuggingFaceTB/SmolLM2-135M-Instruct"
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
pipe = pipeline(
task="text-generation",
model=AutoModelForCausalLM.from_pretrained(MODEL_ID),
tokenizer=tokenizer
)
def cantilever_beam_calc(L_m: float, F_kN: float, w_mm: float, h_mm: float,
E_GPa: float, Sy_MPa: float, rho_kg_m3: float) -> dict:
"""
Calculate stress, deflection, and safety factors for a cantilever beam with end load.
Scope: Rectangular cross-section cantilever beam with concentrated end load
Assumptions:
- Linear elastic material behavior
- Small deflection theory applies
- Homogeneous, isotropic material
- No buckling consideration
- Neglecting self-weight for simplicity
Args:
L_m: Beam length [meters]
F_kN: Applied force at free end [kilonewtons]
w_mm: Beam width [millimeters]
h_mm: Beam height [millimeters]
E_GPa: Young's modulus [gigapascals]
Sy_MPa: Yield strength [megapascals]
rho_kg_m3: Material density [kg/m³]
Returns:
Dictionary with calculation results and verdicts
"""
# Convert units to SI base units
F_N = F_kN * 1000 # kN to N
w_m = w_mm / 1000 # mm to m
h_m = h_mm / 1000 # mm to m
E_Pa = E_GPa * 1e9 # GPa to Pa
Sy_Pa = Sy_MPa * 1e6 # MPa to Pa
# Calculate beam properties
A_m2 = w_m * h_m # Cross-sectional area
I_m4 = (w_m * h_m**3) / 12 # Second moment of area (rectangular section)
c_m = h_m / 2 # Distance to neutral axis
Z_m3 = I_m4 / c_m # Section modulus
# Calculate mass
volume_m3 = A_m2 * L_m
mass_kg = volume_m3 * rho_kg_m3
# Maximum bending moment (at fixed end)
M_max_Nm = F_N * L_m
# Maximum bending stress (at fixed end, outer fiber)
sigma_max_Pa = M_max_Nm * c_m / I_m4
sigma_max_MPa = sigma_max_Pa / 1e6
# Maximum deflection (at free end)
delta_max_m = (F_N * L_m**3) / (3 * E_Pa * I_m4)
delta_max_mm = delta_max_m * 1000
# Allowable deflection (L/200 for cantilever beams - common serviceability limit)
delta_allow_m = L_m / 200
delta_allow_mm = delta_allow_m * 1000
# Calculate safety factors
FoS_yield = Sy_Pa / sigma_max_Pa if sigma_max_Pa > 0 else float('inf')
FoS_deflection = delta_allow_m / delta_max_m if delta_max_m > 0 else float('inf')
# Check pass/fail criteria
passes_yield = sigma_max_Pa <= Sy_Pa
passes_deflection = delta_max_m <= delta_allow_m
overall_safe = passes_yield and passes_deflection
# Natural frequency (first mode)
if mass_kg > 0:
k_eff = 3 * E_Pa * I_m4 / L_m**3 # Effective stiffness
m_eff = 0.25 * mass_kg # Effective mass for cantilever
omega_rad_s = math.sqrt(k_eff / m_eff)
freq_Hz = omega_rad_s / (2 * math.pi)
else:
freq_Hz = float('inf')
return {
"results": {
"sigma_max_MPa": sigma_max_MPa,
"FoS_yield": FoS_yield,
"delta_max_mm": delta_max_mm,
"delta_allow_mm": delta_allow_mm,
"FoS_deflection": FoS_deflection,
"mass_kg": mass_kg,
"freq_Hz": freq_Hz,
"M_max_Nm": M_max_Nm,
"I_m4": I_m4,
"Z_m3": Z_m3
},
"verdict": {
"passes_yield": bool(passes_yield),
"passes_deflection": bool(passes_deflection),
"overall_safe": bool(overall_safe),
"yield_message": "✅ Safe: Stress below yield strength" if passes_yield
else "❌ Unsafe: Stress exceeds yield strength",
"deflection_message": "✅ Acceptable: Deflection within limits" if passes_deflection
else "⚠️ Warning: Excessive deflection"
}
}
def format_chat(system_prompt: str, user_prompt: str) -> str:
"""Format messages for the chat model"""
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
return tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
def llm_generate(prompt: str, max_tokens: int = 150) -> str:
"""Generate LLM response"""
try:
output = pipe(
prompt,
max_new_tokens=max_tokens,
do_sample=True,
temperature=0.7,
return_full_text=False,
)
return output[0]["generated_text"]
except Exception as e:
return f"LLM generation error: {str(e)}"
def generate_explanation(results: dict, inputs: dict) -> str:
"""Generate natural language explanation of results"""
r = results["results"]
v = results["verdict"]
system_prompt = """You are an engineering educator explaining structural analysis results.
Use clear analogies and simple language to help non-engineers understand.
Focus on safety implications and practical meaning of the numbers.
Keep explanations concise but informative - 2-3 sentences maximum."""
user_prompt = f"""
A cantilever beam analysis shows:
- Maximum stress: {r['sigma_max_MPa']:.1f} MPa (Safety Factor: {r['FoS_yield']:.2f})
- Maximum deflection: {r['delta_max_mm']:.2f} mm (Limit: {r['delta_allow_mm']:.2f} mm)
- Natural frequency: {r['freq_Hz']:.1f} Hz
- Beam dimensions: {inputs['L_m']}m long, {inputs['w_mm']}x{inputs['h_mm']}mm cross-section
- Load: {inputs['F_kN']} kN at the free end
Strength verdict: {v['yield_message']}
Deflection verdict: {v['deflection_message']}
Explain what these results mean for the beam's safety and performance in simple terms.
"""
formatted = format_chat(system_prompt, user_prompt)
return llm_generate(formatted)
def validate_inputs(L_m, F_kN, w_mm, h_mm, E_GPa, Sy_MPa, rho_kg_m3):
"""Validate input ranges"""
errors = []
# Valid ranges based on typical engineering applications
if not (0.1 <= L_m <= 10):
errors.append("Length must be between 0.1 and 10 meters")
if not (0.001 <= F_kN <= 1000):
errors.append("Force must be between 0.001 and 1000 kN")
if not (5 <= w_mm <= 1000):
errors.append("Width must be between 5 and 1000 mm")
if not (5 <= h_mm <= 1000):
errors.append("Height must be between 5 and 1000 mm")
if not (1 <= E_GPa <= 500):
errors.append("Young's modulus must be between 1 and 500 GPa")
if not (10 <= Sy_MPa <= 2000):
errors.append("Yield strength must be between 10 and 2000 MPa")
if not (100 <= rho_kg_m3 <= 20000):
errors.append("Density must be between 100 and 20000 kg/m³")
return errors
def run_analysis(L_m, F_kN, w_mm, h_mm, E_GPa, Sy_MPa, rho_kg_m3):
"""Main analysis function for Gradio interface"""
# Validate inputs
errors = validate_inputs(L_m, F_kN, w_mm, h_mm, E_GPa, Sy_MPa, rho_kg_m3)
if errors:
error_msg = "❌ Input validation errors:\n" + "\n".join(f"• {e}" for e in errors)
return pd.DataFrame(), error_msg, ""
# Run calculations
inputs = {
"L_m": L_m, "F_kN": F_kN, "w_mm": w_mm, "h_mm": h_mm,
"E_GPa": E_GPa, "Sy_MPa": Sy_MPa, "rho_kg_m3": rho_kg_m3
}
results = cantilever_beam_calc(L_m, F_kN, w_mm, h_mm, E_GPa, Sy_MPa, rho_kg_m3)
# Create results dataframe
df = pd.DataFrame([{
"Parameter": "Maximum Stress",
"Value": f"{results['results']['sigma_max_MPa']:.2f}",
"Unit": "MPa",
"Status": "✅" if results['verdict']['passes_yield'] else "❌"
}, {
"Parameter": "Factor of Safety (Yield)",
"Value": f"{results['results']['FoS_yield']:.2f}",
"Unit": "-",
"Status": "✅" if results['results']['FoS_yield'] >= 1.5 else "⚠️"
}, {
"Parameter": "Maximum Deflection",
"Value": f"{results['results']['delta_max_mm']:.3f}",
"Unit": "mm",
"Status": "✅" if results['verdict']['passes_deflection'] else "⚠️"
}, {
"Parameter": "Allowable Deflection",
"Value": f"{results['results']['delta_allow_mm']:.3f}",
"Unit": "mm",
"Status": "—"
}, {
"Parameter": "Natural Frequency",
"Value": f"{results['results']['freq_Hz']:.2f}",
"Unit": "Hz",
"Status": "ℹ️"
}, {
"Parameter": "Beam Mass",
"Value": f"{results['results']['mass_kg']:.3f}",
"Unit": "kg",
"Status": "ℹ️"
}])
# Generate status summary
if results['verdict']['overall_safe']:
status = "## ✅ Beam Design is SAFE\n"
status += "All structural requirements are satisfied."
else:
status = "## ⚠️ Beam Design Needs Review\n"
if not results['verdict']['passes_yield']:
status += "• **Critical**: Stress exceeds material yield strength\n"
if not results['verdict']['passes_deflection']:
status += "• **Warning**: Deflection exceeds serviceability limits\n"
# Generate natural language explanation
explanation = "### AI Explanation\n"
explanation += generate_explanation(results, inputs)
return df, status, explanation
# Create Gradio interface
with gr.Blocks(title="Cantilever Beam Calculator with AI Explanations") as demo:
gr.Markdown("""
# 🏗️ Cantilever Beam Stress & Deflection Calculator
## About This Tool
This calculator analyzes a **rectangular cantilever beam** with a concentrated load at the free end.
It computes structural safety metrics and provides AI-generated explanations of the results.
### Scope & Assumptions
- **Beam Type**: Cantilever with fixed support at one end
- **Loading**: Single concentrated force at free end
- **Cross-section**: Rectangular (uniform along length)
- **Analysis**: Linear elastic, small deflection theory
- **Safety Limits**: Yield stress and L/200 deflection limit
""")
gr.Markdown("---")
# Input section
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 📏 Geometry")
L_m = gr.Number(value=2.0, label="Beam Length [m]",
info="Distance from fixed support to free end (0.1-10 m)")
w_mm = gr.Number(value=100, label="Beam Width [mm]",
info="Width of rectangular section (5-1000 mm)")
h_mm = gr.Number(value=200, label="Beam Height [mm]",
info="Height of rectangular section (5-1000 mm)")
with gr.Column(scale=1):
gr.Markdown("### ⚡ Loading")
F_kN = gr.Number(value=10.0, label="Applied Force [kN]",
info="Concentrated load at free end (0.001-1000 kN)")
gr.Markdown("### 🔧 Material Properties")
E_GPa = gr.Number(value=200.0, label="Young's Modulus [GPa]",
info="Steel: 200, Aluminum: 70, Wood: 10-15")
Sy_MPa = gr.Number(value=250.0, label="Yield Strength [MPa]",
info="Steel: 250-500, Aluminum: 100-400")
rho_kg_m3 = gr.Number(value=7850.0, label="Density [kg/m³]",
info="Steel: 7850, Aluminum: 2700, Wood: 500-800")
# Calculate button
calc_btn = gr.Button("🔍 Analyze Beam", variant="primary", size="lg")
gr.Markdown("---")
# Output section
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 📊 Numerical Results")
results_df = gr.Dataframe(
label="Calculated Values",
interactive=False,
wrap=True
)
with gr.Column(scale=1):
gr.Markdown("### 📋 Safety Assessment")
status_md = gr.Markdown(label="Overall Status")
explanation_md = gr.Markdown(label="AI Explanation")
# Connect calculation function
calc_btn.click(
fn=run_analysis,
inputs=[L_m, F_kN, w_mm, h_mm, E_GPa, Sy_MPa, rho_kg_m3],
outputs=[results_df, status_md, explanation_md]
)
# Examples section
gr.Markdown("---")
gr.Examples(
examples=[
[2.0, 10.0, 100, 200, 200.0, 250.0, 7850], # Steel beam - standard
[1.5, 5.0, 50, 150, 70.0, 200.0, 2700], # Aluminum beam - light
[3.0, 2.0, 150, 300, 200.0, 250.0, 7850], # Long steel beam - low load
[1.0, 50.0, 200, 400, 200.0, 350.0, 7850], # Short strong beam - high load
[2.5, 1.0, 80, 120, 12.0, 40.0, 600], # Wood beam
],
inputs=[L_m, F_kN, w_mm, h_mm, E_GPa, Sy_MPa, rho_kg_m3],
label="Example Cases (Click to Load)",
examples_per_page=5,
cache_examples=False
)
# Additional information
gr.Markdown("""
---
### 📖 Technical Documentation
**Calculations Performed:**
- Maximum bending stress: σ = Mc/I at fixed support
- Maximum deflection: δ = FL³/(3EI) at free end
- Factor of Safety (Yield): FoS = Sy/σ_max
- Factor of Safety (Deflection): FoS = δ_allow/δ_max
- First natural frequency: f = ω/(2π) where ω = √(k_eff/m_eff)
**Material Reference Values:**
- **Structural Steel**: E = 200 GPa, Sy = 250-500 MPa, ρ = 7850 kg/m³
- **Aluminum Alloys**: E = 70 GPa, Sy = 100-400 MPa, ρ = 2700 kg/m³
- **Wood (Pine)**: E = 10-15 GPa, Sy = 30-50 MPa, ρ = 500-600 kg/m³
- **Concrete**: E = 20-40 GPa, Sy = 20-40 MPa, ρ = 2400 kg/m³
**Safety Guidelines:**
- FoS (Yield) > 1.5 recommended for static loads
- Deflection < L/200 for cantilever beams (serviceability)
- Consider dynamic effects if natural frequency < 10 Hz
---
*Created for CMU 24-679 Course | Engineering Calculations with AI Explanations*
""")
if __name__ == "__main__":
demo.launch()