import os, shutil for cache_dir in ["/root/.cache/huggingface", "/root/.cache/torch", "/root/.cache/autogluon"]: if os.path.exists(cache_dir): shutil.rmtree(cache_dir, ignore_errors=True) import math import gradio import pandas as pd from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline # Model for explanations (small, CPU-friendly) MODEL_ID = "HuggingFaceTB/SmolLM2-135M-Instruct" # Load model and tokenizer once tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) model = AutoModelForCausalLM.from_pretrained(MODEL_ID) pipe = pipeline(task="text-generation", model=model, tokenizer=tokenizer) def beam_calculation_engine(L_m: float, a_m: float, P_N: float, E_GPa: float, Sy_MPa: float) -> dict: if any(val <= 0 for val in [L_m, a_m, P_N, E_GPa, Sy_MPa]): raise ValueError("All input parameters must be positive values") E_Pa = E_GPa * 1e9 Sy_Pa = Sy_MPa * 1e6 I = a_m**4 / 12.0 c = a_m / 2.0 M_max = P_N * L_m / 4.0 sigma_max_Pa = M_max * c / I sigma_max_MPa = sigma_max_Pa / 1e6 delta_max_m = (P_N * L_m**3) / (48.0 * E_Pa * I) delta_max_mm = delta_max_m * 1000 delta_allowable_m = L_m / 360.0 delta_allowable_mm = delta_allowable_m * 1000 stress_ratio = sigma_max_Pa / Sy_Pa fos_yield = 1.0 / stress_ratio if stress_ratio > 0 else float("inf") deflection_ratio = delta_max_m / delta_allowable_m fos_deflection = 1.0 / deflection_ratio if deflection_ratio > 0 else float("inf") passes_strength = sigma_max_Pa <= Sy_Pa passes_serviceability = delta_max_m <= delta_allowable_m overall_safe = passes_strength and passes_serviceability calculation_summary = { "beam_properties": { "length_m": L_m, "cross_section_side_m": a_m, "load_N": P_N, "elastic_modulus_GPa": E_GPa, "yield_strength_MPa": Sy_MPa }, "calculated_values": { "maximum_stress_MPa": round(sigma_max_MPa, 3), "maximum_deflection_mm": round(delta_max_mm, 3), "allowable_deflection_mm": round(delta_allowable_mm, 3), "factor_of_safety_yield": round(fos_yield, 3), "factor_of_safety_deflection": round(fos_deflection, 3) }, "engineering_verdict": { "strength_check": "PASS" if passes_strength else "FAIL", "serviceability_check": "PASS" if passes_serviceability else "FAIL", "overall_safety": "SAFE" if overall_safe else "UNSAFE", "strength_message": f"Stress ({sigma_max_MPa:.1f} MPa) {'≤' if passes_strength else '>'} Yield ({Sy_MPa} MPa)", "deflection_message": f"Deflection ({delta_max_mm:.1f} mm) {'≤' if passes_serviceability else '>'} Allowable ({delta_allowable_mm:.1f} mm)" }, "human_readable_summary": ( f"Beam Analysis Results:\n" f"- Maximum bending stress: {sigma_max_MPa:.1f} MPa\n" f"- Maximum deflection: {delta_max_mm:.1f} mm\n" f"- Allowable deflection: {delta_allowable_mm:.1f} mm\n" f"- Factor of safety (yield): {fos_yield:.2f}\n" f"- Factor of safety (deflection): {fos_deflection:.2f}\n" f"- Strength check: {'PASS' if passes_strength else 'FAIL'}\n" f"- Serviceability check: {'PASS' if passes_serviceability else 'FAIL'}\n" f"- Overall verdict: {'SAFE' if overall_safe else 'UNSAFE'}" ) } return calculation_summary def format_llm_prompt(system_prompt: str, user_prompt: str) -> str: messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ] if hasattr(tokenizer, "chat_template") and tokenizer.chat_template: return tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) return f"System: {system_prompt}\n\nUser: {user_prompt}\n\nAssistant:" def generate_explanation(structured_results: dict) -> str: props = structured_results["beam_properties"] calc = structured_results["calculated_values"] verdict = structured_results["engineering_verdict"] system_prompt = ( "You are an engineering educator explaining structural analysis results. " "Be accurate, concise, and ground the explanation only in provided numbers. " "No new assumptions or invented figures." ) user_prompt = ( f"Explain these beam results in simple terms:\n" f"Beam length: {props['length_m']} m, square side: {props['cross_section_side_m']*1000:.1f} mm, " f"load: {props['load_N']/1000:.1f} kN. Material: E={props['elastic_modulus_GPa']} GPa, Sy={props['yield_strength_MPa']} MPa.\n" f"Stress={calc['maximum_stress_MPa']} MPa, deflection={calc['maximum_deflection_mm']} mm, " f"allowable deflection={calc['allowable_deflection_mm']} mm. " f"FoS(yield)={calc['factor_of_safety_yield']}, FoS(deflection)={calc['factor_of_safety_deflection']}.\n" f"Verdict: Strength={verdict['strength_check']}, Serviceability={verdict['serviceability_check']}, Overall={verdict['overall_safety']}." ) prompt = format_llm_prompt(system_prompt, user_prompt) try: # Deterministic generation: greedy decoding (no sampling) out = pipe(prompt, max_new_tokens=150, do_sample=False, return_full_text=False) explanation = out[0]["generated_text"].strip() except Exception as e: explanation = f"Explanation unavailable: {e}. Raw results:\n{structured_results['human_readable_summary']}" return explanation def run_beam_analysis(L_m, a_m, P_kN, E_GPa, Sy_MPa): try: L_m = float(L_m) a_m = float(a_m) P_kN = float(P_kN) E_GPa = float(E_GPa) Sy_MPa = float(Sy_MPa) if not (0.5 <= L_m <= 10.0): return None, None, "Error: Beam length must be between 0.5 and 10.0 meters" if not (0.01 <= a_m <= 0.5): return None, None, "Error: Cross-section side must be between 0.01 and 0.5 meters" if not (0.1 <= P_kN <= 100.0): return None, None, "Error: Load must be between 0.1 and 100.0 kN" if not (50.0 <= E_GPa <= 300.0): return None, None, "Error: Elastic modulus must be between 50 and 300 GPa" if not (100.0 <= Sy_MPa <= 1000.0): return None, None, "Error: Yield strength must be between 100 and 1000 MPa" P_N = P_kN * 1000.0 results = beam_calculation_engine(L_m, a_m, P_N, E_GPa, Sy_MPa) results_df = pd.DataFrame([ {"Parameter": "Maximum Stress", "Value": f"{results['calculated_values']['maximum_stress_MPa']} MPa", "Limit": f"{Sy_MPa} MPa", "Status": results['engineering_verdict']['strength_check']}, {"Parameter": "Factor of Safety (Yield)", "Value": f"{results['calculated_values']['factor_of_safety_yield']}", "Limit": "> 1.0", "Status": "PASS" if results['calculated_values']['factor_of_safety_yield'] > 1.0 else "FAIL"}, {"Parameter": "Maximum Deflection", "Value": f"{results['calculated_values']['maximum_deflection_mm']} mm", "Limit": f"{results['calculated_values']['allowable_deflection_mm']} mm", "Status": results['engineering_verdict']['serviceability_check']}, {"Parameter": "Factor of Safety (Deflection)", "Value": f"{results['calculated_values']['factor_of_safety_deflection']}", "Limit": "> 1.0", "Status": "PASS" if results['calculated_values']['factor_of_safety_deflection'] > 1.0 else "FAIL"}, ]) explanation = generate_explanation(results) return results_df, results['human_readable_summary'], explanation except Exception as e: return None, None, f"Calculation error: {e}" with gradio.Blocks(title="Beam Analysis Calculator") as demo: gradio.Markdown("# Beam Analysis Calculator with Natural Language Explanations") gradio.Markdown("Deterministic simply-supported beam analysis with a central point load. See the numbers and the plain-language explanation.") with gradio.Row(): with gradio.Column(scale=1): gradio.Markdown("## Inputs") with gradio.Row(): L_m = gradio.Number(value=2.0, label="Beam Length (m)", minimum=0.5, maximum=10.0, info="Support-to-support length") a_m = gradio.Number(value=0.05, label="Square Side (m)", minimum=0.01, maximum=0.5, info="Side length of square cross-section") with gradio.Row(): P_kN = gradio.Number(value=5.0, label="Central Point Load (kN)", minimum=0.1, maximum=100.0, info="Load applied at midspan") with gradio.Row(): E_GPa = gradio.Number(value=200.0, label="Elastic Modulus (GPa)", minimum=50.0, maximum=300.0, info="Young's modulus of material") Sy_MPa = gradio.Number(value=250.0, label="Yield Strength (MPa)", minimum=100.0, maximum=1000.0, info="Material yield strength") calculate_btn = gradio.Button("Run Analysis") gradio.Markdown("### Example Cases") gradio.Examples( examples=[ [2.0, 0.05, 5.0, 200.0, 250.0], [3.0, 0.08, 10.0, 70.0, 200.0], [1.5, 0.03, 2.0, 210.0, 350.0], ], inputs=[L_m, a_m, P_kN, E_GPa, Sy_MPa], label="Click to load parameters" ) with gradio.Column(scale=1): gradio.Markdown("## Results") results_table = gradio.Dataframe(headers=["Parameter", "Value", "Limit", "Status"], interactive=False, label="Calculation Results") detailed_output = gradio.Textbox(label="Detailed Calculation Summary", interactive=False, lines=6) explanation_output = gradio.Textbox(label="Natural Language Explanation", interactive=False, lines=6) calculate_btn.click(fn=run_beam_analysis, inputs=[L_m, a_m, P_kN, E_GPa, Sy_MPa], outputs=[results_table, detailed_output, explanation_output]) if __name__ == "__main__": demo.launch()