Spaces:
Sleeping
Sleeping
| 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() |