import streamlit as st import plotly.graph_objects as go import numpy as np import pandas as pd # ============================================================================= # 1. CONFIGURATION & DATA # ============================================================================= st.set_page_config( page_title="Biomaterials Stress-Strain Explorer", page_icon="🦷", layout="wide", initial_sidebar_state="expanded" ) # Material Database: Dental & Biomaterials MATERIALS = { "🦷 Dental Amalgam": { "E": 80000, "yield": 200, "uts": 260, "strain": 0.006, "type": "brittle", "desc": "Traditional restorative alloy. High stiffness, low ductility.", "color": "#4A90D9" }, "🦷 Composite Resin": { "E": 12000, "yield": 90, "uts": 120, "strain": 0.035, "type": "semi-ductile", "desc": "Tooth-colored restorative. Moderate stiffness and ductility.", "color": "#E8A838" }, "🦷 Zirconia (Y-TZP)": { "E": 200000, "yield": 900, "uts": 1100, "strain": 0.003, "type": "brittle", "desc": "High-strength ceramic. Extremely stiff, very brittle failure.", "color": "#9B59B6" }, "🦷 Gold Alloy (Type IV)": { "E": 85000, "yield": 350, "uts": 500, "strain": 0.12, "type": "ductile", "desc": "Cast noble metal. Excellent ductility and fatigue resistance.", "color": "#F39C12" }, "🦷 Titanium (Grade 5)": { "E": 110000, "yield": 850, "uts": 950, "strain": 0.10, "type": "ductile", "desc": "Ti-6Al-4V alloy. Standard for implants. High strength-to-weight.", "color": "#3498DB" }, "🦷 Glass Ionomer": { "E": 8000, "yield": 40, "uts": 55, "strain": 0.008, "type": "brittle", "desc": "Adhesive cement. Low strength, fluoride release.", "color": "#1ABC9C" }, "🦴 Cortical Bone": { "E": 17000, "yield": 120, "uts": 150, "strain": 0.018, "type": "semi-ductile", "desc": "Dense outer bone. Anisotropic, moderate stiffness.", "color": "#ECF0F1" }, "🧬 PEEK": { "E": 3600, "yield": 95, "uts": 100, "strain": 0.06, "type": "ductile", "desc": "Polyetheretherketone. Used in spinal implants. Low modulus matches bone.", "color": "#E74C3C" }, "🧬 UHMWPE": { "E": 800, "yield": 20, "uts": 30, "strain": 0.35, "type": "ductile", "desc": "Ultra-high molecular weight polyethylene. Joint bearing surface.", "color": "#F1C40F" }, "⚙️ Stainless Steel 316L": { "E": 193000, "yield": 205, "uts": 505, "strain": 0.40, "type": "ductile", "desc": "Austenitic steel. Surgical instruments and temporary implants.", "color": "#7F8C8D" } } # ============================================================================= # 2. PHYSICS ENGINE (Curve Generation) # ============================================================================= def generate_stress_strain(E, sigma_y, sigma_uts, epsilon_break, material_type): """ Generates a realistic stress-strain curve using a piecewise model. 1. Elastic: Linear (Hooke's Law) 2. Plastic: Cosine transition to UTS 3. Necking: Power-law decay to fracture """ # Calculate yield strain epsilon_y = sigma_y / E # Determine strain at UTS based on material type if material_type == "brittle": epsilon_uts = epsilon_y * 1.1 elif material_type == "semi-ductile": epsilon_uts = epsilon_y + (epsilon_break - epsilon_y) * 0.5 else: # ductile epsilon_uts = epsilon_y + (epsilon_break - epsilon_y) * 0.4 # Generate strain points n_points = 1000 epsilon = np.linspace(0, epsilon_break, n_points) sigma = np.zeros_like(epsilon) for i, eps in enumerate(epsilon): if eps <= epsilon_y: # Elastic Region sigma[i] = E * eps elif eps <= epsilon_uts: # Strain Hardening Region (Smooth transition) # Normalize position between yield and UTS t = (eps - epsilon_y) / (epsilon_uts - epsilon_y) # Use cosine for smooth hardening sigma[i] = sigma_y + (sigma_uts - sigma_y) * (0.5 * (1 - np.cos(np.pi * t))) else: # Necking / Softening Region t = (eps - epsilon_uts) / (epsilon_break - epsilon_uts) # Stress drops from UTS to near 0 sigma[i] = sigma_uts * (1 - 0.95 * (t ** 0.7)) return epsilon, np.maximum(sigma, 0) # ============================================================================= # 3. ACCESSIBILITY HELPERS # ============================================================================= def inject_accessibility_css(): """Injects CSS for high contrast and large text modes.""" st.markdown(""" """, unsafe_allow_html=True) # ============================================================================= # 4. MAIN APP LOGIC # ============================================================================= def main(): inject_accessibility_css() # --- Sidebar: Controls & Accessibility --- with st.sidebar: st.header("⚙️ Controls") # Material Selection selected_material = st.selectbox( "Select Material Preset", options=list(MATERIALS.keys()), index=0 ) st.markdown("---") st.header("📊 Parameters") # Get current preset values preset = MATERIALS[selected_material] # Widgets col1, col2 = st.columns(2) with col1: E = st.slider("Young's Modulus (MPa)", 100, 300000, int(preset["E"]), 100) sigma_y = st.slider("Yield Strength (MPa)", 10, 1500, int(preset["yield"]), 10) with col2: sigma_uts = st.slider("UTS (MPa)", 10, 1500, int(preset["uts"]), 10) epsilon_break = st.slider("Strain at Break", 0.001, 0.50, float(preset["strain"]), 0.001) st.markdown("---") st.header("♿ Accessibility") # Accessibility Toggles high_contrast = st.toggle("High Contrast Mode", value=False) large_text = st.toggle("Large Text Mode", value=False) screen_reader_mode = st.toggle("Verbose Descriptions", value=False) # Apply CSS classes based on toggles css_classes = [] if high_contrast: css_classes.append("high-contrast") if large_text: css_classes.append("large-text") if css_classes: st.markdown(f'
Chart displays stress-strain curve for {selected_material}. Young's Modulus is {E} MPa. Yield Strength is {sigma_y} MPa. Ultimate Tensile Strength is {sigma_uts} MPa. Fracture occurs at strain {epsilon_break}.
""", unsafe_allow_html=True) # --- Calculation --- strain, stress = generate_stress_strain(E, sigma_y, sigma_uts, epsilon_break, preset["type"]) # --- Plotly Chart --- fig = go.Figure() # 1. The Stress-Strain Curve fig.add_trace(go.Scatter( x=strain, y=stress, mode='lines', name='Stress-Strain', line=dict(color=preset["color"], width=4), hovertemplate="Strain: %{x:.3f}