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()