Spaces:
No application file
No application file
| #!/usr/bin/env python3 | |
| """ | |
| Copyright (c) 2025 Joshua Hendricks Cole (DBA: Corporation of Light). All Rights Reserved. PATENT PENDING. | |
| Materials Lab - Main API for all materials science operations | |
| """ | |
| try: # allow package-style imports | |
| from .materials_database import MaterialsDatabase, MaterialProperties # type: ignore | |
| from .material_testing import ( # type: ignore | |
| TensileTest, CompressionTest, FatigueTest, ImpactTest, | |
| HardnessTest, ThermalTest, CorrosionTest, EnvironmentalTest | |
| ) | |
| from .material_designer import ( # type: ignore | |
| AlloyOptimizer, CompositeDesigner, NanostructureEngineer, | |
| SurfaceTreatment, AdditiveManufacturing | |
| ) | |
| from .material_property_predictor import MaterialPropertyPredictor # type: ignore | |
| from .material_profiles import MaterialProfileGenerator # type: ignore | |
| from .calibration import CalibrationManager # type: ignore | |
| from .uncertainty import estimate_property_uncertainty # type: ignore | |
| from .phase_change import run_ice_analysis # type: ignore | |
| except ImportError: # pragma: no cover | |
| from materials_database import MaterialsDatabase, MaterialProperties # type: ignore | |
| from material_testing import ( # type: ignore | |
| TensileTest, CompressionTest, FatigueTest, ImpactTest, | |
| HardnessTest, ThermalTest, CorrosionTest, EnvironmentalTest | |
| ) | |
| from material_designer import ( # type: ignore | |
| AlloyOptimizer, CompositeDesigner, NanostructureEngineer, | |
| SurfaceTreatment, AdditiveManufacturing | |
| ) | |
| from material_property_predictor import MaterialPropertyPredictor # type: ignore | |
| from material_profiles import MaterialProfileGenerator # type: ignore | |
| from calibration import CalibrationManager # type: ignore | |
| from uncertainty import estimate_property_uncertainty # type: ignore | |
| from phase_change import run_ice_analysis # type: ignore | |
| from typing import Dict, List, Optional, Any, Sequence | |
| import time | |
| from validation.results_validator import ResultsValidator, ValidationResult | |
| try: | |
| from .validation_map import MATERIAL_PROPERTY_REFERENCE_MAP | |
| from core.base_lab import BaseLab | |
| except ImportError: # pragma: no cover - allow script-style execution | |
| from validation_map import MATERIAL_PROPERTY_REFERENCE_MAP # type: ignore | |
| # This path might need adjustment depending on execution context | |
| from core.base_lab import BaseLab | |
| class MaterialsLab(BaseLab): | |
| """ | |
| Main Materials Science Laboratory API | |
| Provides unified interface for: | |
| - Material database access (6.6 million+ materials) | |
| - Material testing simulations | |
| - Material design and optimization | |
| - Property prediction with ML | |
| """ | |
| def __init__(self, config: Dict[str, Any] = None): | |
| """Initialize Materials Lab""" | |
| super().__init__(config) | |
| start = time.time() | |
| self.db = MaterialsDatabase(index_on_load=self.config.get("index_on_load", True)) | |
| self.predictor = MaterialPropertyPredictor(self.db) | |
| self.profile_generator = MaterialProfileGenerator(self.db) | |
| self.calibration_manager = CalibrationManager() | |
| self._validator = ResultsValidator() | |
| end = time.time() | |
| print(f"[info] Materials Lab ready in {(end-start)*1000:.1f} ms") | |
| print(f"[info] Database: {self.db.get_count()} local materials (Catalog: 6.6M+ available)") | |
| def run_experiment(self, experiment_spec: Dict[str, Any]) -> Dict[str, Any]: | |
| """ | |
| Run a materials science experiment. | |
| Args: | |
| experiment_spec: Dictionary specifying the experiment. | |
| Required keys: | |
| - 'experiment_type': str (e.g., 'tensile', 'compression', 'hardness') | |
| - 'material_name': str | |
| Other keys are passed as kwargs to the respective test method. | |
| Returns: | |
| A dictionary containing the experiment results. | |
| """ | |
| exp_type = experiment_spec.get("experiment_type") | |
| material_name = experiment_spec.get("material_name") | |
| if not exp_type or not material_name: | |
| raise ValueError("'experiment_type' and 'material_name' are required in the experiment spec.") | |
| # Map experiment type to method | |
| experiment_map = { | |
| "tensile": self.tensile_test, | |
| "compression": self.compression_test, | |
| "fatigue": self.fatigue_test, | |
| "impact": self.impact_test, | |
| "hardness": self.hardness_test, | |
| "thermal": self.thermal_test, | |
| "corrosion": self.corrosion_test, | |
| "environmental": self.environmental_test, | |
| "ice_growth": self.simulate_ice_growth, | |
| } | |
| experiment_method = experiment_map.get(exp_type) | |
| if not experiment_method: | |
| raise ValueError(f"Unknown experiment type: {exp_type}") | |
| # Prepare arguments | |
| kwargs = experiment_spec.copy() | |
| kwargs.pop("experiment_type") | |
| kwargs.pop("material_name") | |
| # Some methods have different signatures | |
| if exp_type == "ice_growth": | |
| # simulate_ice_growth(self, material_name: str, temperature_k: float, relative_humidity: float, duration_hours: float = 1.0) | |
| result = experiment_method(material_name, **kwargs) | |
| return {"status": "completed", "data": result} | |
| else: | |
| result = experiment_method(material_name, **kwargs) | |
| return result.to_dict() | |
| def get_status(self) -> Dict[str, Any]: | |
| """ | |
| Get the current status of the Materials Lab. | |
| For MaterialsLab, this returns the database statistics. | |
| """ | |
| return self.get_statistics() | |
| # ===== VALIDATION ===== | |
| def validate_material_properties( | |
| self, | |
| material_name: str, | |
| properties: Optional[Sequence[str]] = None, | |
| *, | |
| raise_on_missing: bool = True, | |
| ) -> Dict[str, ValidationResult]: | |
| """ | |
| Validate a material's tabulated properties against reference data. | |
| Args: | |
| material_name: Name used in the materials database. | |
| properties: Optional iterable of property names to validate. If | |
| omitted, all mapped properties for the material are checked. | |
| raise_on_missing: When True, raise a ValueError if the material or | |
| requested property does not have a configured reference datum. | |
| Returns: | |
| Mapping of property name to ValidationResult. | |
| """ | |
| material = self.get_material(material_name) | |
| if material is None: | |
| raise ValueError(f"Material not found: {material_name}") | |
| material_map = MATERIAL_PROPERTY_REFERENCE_MAP.get(material_name) | |
| if not material_map: | |
| if raise_on_missing: | |
| raise ValueError(f"No validation mapping configured for '{material_name}'") | |
| return {} | |
| selected = set(properties) if properties else set(material_map) | |
| results: Dict[str, ValidationResult] = {} | |
| for prop_name in selected: | |
| config = material_map.get(prop_name) | |
| if config is None: | |
| if raise_on_missing: | |
| raise ValueError( | |
| f"No validation reference configured for '{material_name}' property '{prop_name}'" | |
| ) | |
| continue | |
| attr = config.get("attribute", prop_name) | |
| if not hasattr(material, attr): | |
| if raise_on_missing: | |
| raise AttributeError( | |
| f"Material '{material_name}' does not expose attribute '{attr}' for validation" | |
| ) | |
| continue | |
| reference_key = config["reference_key"] | |
| simulated_value = getattr(material, attr) | |
| validate_kwargs: Dict[str, Any] = {} | |
| if "tolerance_sigma" in config: | |
| validate_kwargs["tolerance_sigma"] = config["tolerance_sigma"] | |
| if "max_error_percent" in config: | |
| validate_kwargs["max_error_percent"] = config["max_error_percent"] | |
| results[prop_name] = self._validator.validate( | |
| simulated_value, | |
| reference_key, | |
| **validate_kwargs, | |
| ) | |
| return results | |
| def validate_accuracy_suite(self) -> Dict[str, Dict[str, ValidationResult]]: | |
| """ | |
| Run the configured accuracy suite for all mapped materials. | |
| Returns: | |
| Nested mapping of material -> property -> ValidationResult. | |
| """ | |
| report: Dict[str, Dict[str, ValidationResult]] = {} | |
| for material_name in MATERIAL_PROPERTY_REFERENCE_MAP: | |
| report[material_name] = self.validate_material_properties( | |
| material_name, | |
| raise_on_missing=False, | |
| ) | |
| return report | |
| # ===== DATABASE ACCESS ===== | |
| def get_material(self, name: str) -> Optional[MaterialProperties]: | |
| """Get material by name""" | |
| return self.db.get_material(name) | |
| def search_materials(self, **criteria) -> List[MaterialProperties]: | |
| """Search materials by criteria""" | |
| return self.db.search_materials(**criteria) | |
| def list_categories(self) -> List[str]: | |
| """List all material categories""" | |
| return self.db.list_categories() | |
| def list_materials(self, category: Optional[str] = None) -> List[str]: | |
| """List material names, optionally filtered by category.""" | |
| if category: | |
| return sorted( | |
| props.name | |
| for props in self.db.materials.values() | |
| if props.category.lower() == category.lower() | |
| ) | |
| return sorted(self.db.materials.keys()) | |
| def get_statistics(self) -> Dict[str, Any]: | |
| """Get database statistics""" | |
| return self.db.get_statistics() | |
| def get_material_profile(self, material_name: str) -> Dict[str, Any]: | |
| """Generate a detailed property profile with curves and metadata.""" | |
| if not self.db.get_material(material_name): | |
| raise ValueError(f"Material not found: {material_name}") | |
| profile = self.profile_generator.build_profile(material_name) | |
| safety = self.db.get_safety_data(material_name) | |
| if safety: | |
| profile["safety"] = safety | |
| return profile | |
| def get_material_safety(self, material_name: str) -> Optional[Dict[str, object]]: | |
| """Return MSDS-style safety information if available.""" | |
| return self.db.get_safety_data(material_name) | |
| def register_calibration(self, material_name: str, test_type: str, property_name: str, | |
| reference_value: float, measured_value: float) -> Dict[str, float]: | |
| """Register calibration measurement.""" | |
| record = self.calibration_manager.register(material_name, test_type, property_name, | |
| reference_value, measured_value) | |
| return record.to_dict() | |
| def get_calibration_summary(self, material_name: str, test_type: str) -> Dict[str, Dict[str, float]]: | |
| return self.calibration_manager.summary(material_name, test_type) | |
| def simulate_ice_growth(self, | |
| material_name: str, | |
| temperature_k: float, | |
| relative_humidity: float, | |
| duration_hours: float = 1.0) -> Dict[str, float]: | |
| material = self.get_material(material_name) | |
| if material is None: | |
| raise ValueError(f"Material not found: {material_name}") | |
| return run_ice_analysis(material, temperature_k, relative_humidity, duration_hours) | |
| def run_batch_experiments(self, batch_specs: Sequence[Dict[str, Any]]) -> List[Dict[str, Any]]: | |
| """Run multiple experiments and return serialisable summaries.""" | |
| from analysis_tools import run_batch_experiments as _batch_runner # type: ignore | |
| results = _batch_runner(batch_specs, lab=self) | |
| return [ | |
| { | |
| "experiment_id": res.experiment_id, | |
| "material": res.material, | |
| "test_type": res.test_type, | |
| "success": res.success, | |
| "payload": res.payload, | |
| "error": res.error, | |
| } | |
| for res in results | |
| ] | |
| # ===== TESTING ===== | |
| def tensile_test(self, material_name: str, **kwargs): | |
| """Run tensile test""" | |
| mat = self.get_material(material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {material_name}") | |
| test = TensileTest(mat) | |
| result = test.run(**kwargs) | |
| self._attach_uncertainty(mat, "tensile", result.data, | |
| ["youngs_modulus", "yield_strength", "ultimate_strength"]) | |
| self._apply_calibration(mat.name, "tensile", result.data, | |
| ["youngs_modulus", "yield_strength", "ultimate_strength"]) | |
| return result | |
| def compression_test(self, material_name: str, **kwargs): | |
| """Run compression test""" | |
| mat = self.get_material(material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {material_name}") | |
| test = CompressionTest(mat) | |
| result = test.run(**kwargs) | |
| self._attach_uncertainty(mat, "compression", result.data, | |
| ["compressive_modulus", "compressive_strength"]) | |
| self._apply_calibration(mat.name, "compression", result.data, | |
| ["compressive_modulus", "compressive_strength"]) | |
| return result | |
| def fatigue_test(self, material_name: str, **kwargs): | |
| """Run fatigue test""" | |
| mat = self.get_material(material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {material_name}") | |
| test = FatigueTest(mat) | |
| result = test.run(**kwargs) | |
| self._attach_uncertainty(mat, "fatigue", result.data, ["fatigue_limit"]) | |
| self._apply_calibration(mat.name, "fatigue", result.data, ["fatigue_limit"]) | |
| return result | |
| def impact_test(self, material_name: str, **kwargs): | |
| """Run impact test""" | |
| mat = self.get_material(material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {material_name}") | |
| test = ImpactTest(mat) | |
| result = test.run(**kwargs) | |
| self._attach_uncertainty(mat, "impact", result.data, ["impact_energy"]) | |
| return result | |
| def hardness_test(self, material_name: str, **kwargs): | |
| """Run hardness test""" | |
| mat = self.get_material(material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {material_name}") | |
| test = HardnessTest(mat) | |
| result = test.run(**kwargs) | |
| self._attach_uncertainty(mat, "hardness", result.data, ["vickers"]) | |
| return result | |
| def thermal_test(self, material_name: str, test_type: str = "dsc", **kwargs): | |
| """Run thermal test""" | |
| mat = self.get_material(material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {material_name}") | |
| test = ThermalTest(mat) | |
| if test_type == "dsc": | |
| result = test.run_dsc(**kwargs) | |
| elif test_type == "conductivity": | |
| result = test.run_thermal_conductivity(**kwargs) | |
| else: | |
| raise ValueError(f"Unknown thermal test type: {test_type}") | |
| self._attach_uncertainty(mat, "thermal", result.data, list(result.data.keys())) | |
| return result | |
| def corrosion_test(self, material_name: str, test_type: str = "salt_spray", **kwargs): | |
| """Run corrosion test""" | |
| mat = self.get_material(material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {material_name}") | |
| test = CorrosionTest(mat) | |
| if test_type == "salt_spray": | |
| result = test.run_salt_spray(**kwargs) | |
| elif test_type == "electrochemical": | |
| result = test.run_electrochemical(**kwargs) | |
| else: | |
| raise ValueError(f"Unknown corrosion test type: {test_type}") | |
| self._attach_uncertainty(mat, "corrosion", result.data, list(result.data.keys())) | |
| return result | |
| def environmental_test(self, material_name: str, **kwargs): | |
| """Run environmental test""" | |
| mat = self.get_material(material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {material_name}") | |
| test = EnvironmentalTest(mat) | |
| result = test.run_extreme_cold(**kwargs) | |
| self._attach_uncertainty(mat, "environmental_extreme_cold", result.data, | |
| ["heat_loss_rate_W_m2", "thermal_stress_MPa", "adjusted_tensile_strength"]) | |
| return result | |
| # ------------------------------------------------------------ helpers | |
| def _apply_calibration(self, | |
| material_name: str, | |
| test_type: str, | |
| data: Dict[str, Any], | |
| properties: List[str]) -> None: | |
| """Apply stored calibration biases to selected properties.""" | |
| numeric_values = { | |
| prop: data[prop] | |
| for prop in properties | |
| if prop in data and isinstance(data[prop], (int, float)) | |
| } | |
| if not numeric_values: | |
| return | |
| corrections = self.calibration_manager.apply(material_name, test_type, numeric_values) | |
| if corrections: | |
| data.setdefault("calibration", {}).update(corrections) | |
| for prop, value in numeric_values.items(): | |
| data[prop] = value | |
| def _attach_uncertainty(self, | |
| material: MaterialProperties, | |
| test_type: str, | |
| data: Dict[str, Any], | |
| properties: List[str]) -> None: | |
| """Attach one-sigma uncertainty estimates to the data payload.""" | |
| if not properties: | |
| return | |
| uncertainties = data.setdefault("uncertainty", {}) | |
| for prop in properties: | |
| if prop in data and isinstance(data[prop], (int, float)): | |
| uncertainties[prop] = estimate_property_uncertainty(material, prop, data[prop], test_type) | |
| # ===== DESIGN & OPTIMIZATION ===== | |
| def optimize_alloy(self, base_elements: List, target_properties: Dict, **kwargs): | |
| """Optimize alloy composition""" | |
| optimizer = AlloyOptimizer(base_elements, target_properties, **kwargs) | |
| return optimizer.optimize() | |
| def design_composite(self, fiber_name: str, matrix_name: str, **kwargs): | |
| """Design composite material""" | |
| fiber = self.get_material(fiber_name) | |
| matrix = self.get_material(matrix_name) | |
| if not fiber or not matrix: | |
| raise ValueError("Fiber or matrix material not found") | |
| designer = CompositeDesigner(fiber, matrix) | |
| return designer.design_laminate(**kwargs) | |
| def add_nanoparticles(self, base_material_name: str, **kwargs): | |
| """Add nanoparticles to material""" | |
| mat = self.get_material(base_material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {base_material_name}") | |
| engineer = NanostructureEngineer(mat) | |
| return engineer.add_nanoparticles(**kwargs) | |
| def apply_coating(self, base_material_name: str, **kwargs): | |
| """Apply surface coating""" | |
| mat = self.get_material(base_material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {base_material_name}") | |
| treatment = SurfaceTreatment(mat) | |
| return treatment.apply_coating(**kwargs) | |
| def design_lattice(self, base_material_name: str, **kwargs): | |
| """Design lattice structure for AM""" | |
| mat = self.get_material(base_material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {base_material_name}") | |
| am = AdditiveManufacturing(mat) | |
| return am.design_lattice_structure(**kwargs) | |
| # ===== PREDICTION ===== | |
| def predict_from_composition(self, composition: Dict[str, float], properties: List[str]): | |
| """Predict properties from composition""" | |
| return self.predictor.predict_from_composition(composition, properties) | |
| def predict_from_structure(self, crystal_structure: str, bonding_type: str, properties: List[str]): | |
| """Predict properties from structure""" | |
| return self.predictor.predict_from_structure(crystal_structure, bonding_type, properties) | |
| def predict_by_similarity(self, reference_material_name: str, property_name: str): | |
| """Predict property by similarity""" | |
| mat = self.get_material(reference_material_name) | |
| if not mat: | |
| raise ValueError(f"Material not found: {reference_material_name}") | |
| return self.predictor.predict_by_similarity(mat, property_name) | |
| # ===== CONVENIENCE METHODS ===== | |
| def compare_materials(self, material_names: List[str], properties: List[str]) -> Dict: | |
| """Compare multiple materials""" | |
| results = {} | |
| for name in material_names: | |
| mat = self.get_material(name) | |
| if mat: | |
| results[name] = { | |
| prop: getattr(mat, prop, None) | |
| for prop in properties | |
| } | |
| return results | |
| def find_best_material(self, | |
| category: Optional[str] = None, | |
| optimize_for: str = "strength", | |
| constraints: Optional[Dict] = None) -> MaterialProperties: | |
| """ | |
| Find best material for application | |
| Args: | |
| category: Material category to search | |
| optimize_for: Property to maximize | |
| constraints: Dict of property constraints (min/max values) | |
| """ | |
| materials = list(self.db.materials.values()) | |
| # Filter by category | |
| if category: | |
| materials = [m for m in materials if m.category.lower() == category.lower()] | |
| # Apply constraints | |
| if constraints: | |
| for prop, (min_val, max_val) in constraints.items(): | |
| materials = [ | |
| m for m in materials | |
| if min_val <= getattr(m, prop, 0) <= max_val | |
| ] | |
| # Find best | |
| if not materials: | |
| return None | |
| best = max(materials, key=lambda m: getattr(m, optimize_for, 0)) | |
| return best | |
| if __name__ == "__main__": | |
| # Initialize lab | |
| lab = MaterialsLab() | |
| print("\n" + "="*70) | |
| print("MATERIALS LAB - COMPREHENSIVE DEMONSTRATION") | |
| print("="*70) | |
| # Demo 1: Database access | |
| print("\n1. DATABASE ACCESS") | |
| print("-" * 70) | |
| airloy = lab.get_material("Airloy X103") | |
| print(f"Material: {airloy.name}") | |
| print(f" Category: {airloy.category} / {airloy.subcategory}") | |
| print(f" Density: {airloy.density} kg/m³") | |
| print(f" Thermal Conductivity: {airloy.thermal_conductivity*1000:.1f} mW/(m·K)") | |
| print(f" Tensile Strength: {airloy.tensile_strength:.2f} MPa") | |
| # Demo 2: Material testing | |
| print("\n2. TENSILE TEST - Ti-6Al-4V") | |
| print("-" * 70) | |
| result = lab.tensile_test("Ti-6Al-4V", max_strain=0.15) | |
| print(f"Test: {result.test_type}") | |
| print(f" Young's Modulus: {result.data['youngs_modulus']:.0f} MPa") | |
| print(f" Yield Strength: {result.data['yield_strength']:.0f} MPa") | |
| print(f" Ultimate Strength: {result.data['ultimate_strength']:.0f} MPa") | |
| print(f" Elongation: {result.data['elongation_at_break']:.1f}%") | |
| print(f" Toughness: {result.data['toughness']:.1f} MJ/m³") | |
| # Demo 3: Environmental test | |
| print("\n3. EXTREME COLD TEST - Airloy X103 at -200°C with 30 mph wind") | |
| print("-" * 70) | |
| result = lab.environmental_test( | |
| "Airloy X103", | |
| temperature=73, # -200°C | |
| wind_speed=13.4, # 30 mph | |
| duration_hours=24 | |
| ) | |
| print(f"Status: {result.data['status']}") | |
| print(f" Temperature: {result.data['temperature_celsius']:.0f}°C") | |
| print(f" Wind: {result.data['wind_speed_mph']:.0f} mph") | |
| print(f" Strength Retention: {result.data['strength_retention_percent']:.1f}%") | |
| print(f" Heat Loss: {result.data['heat_loss_rate_W_m2']:.1f} W/m²") | |
| print(f" Result: {'✓ PASS' if result.success else '✗ FAIL'}") | |
| # Demo 4: Material design | |
| print("\n4. DESIGN CARBON FIBER COMPOSITE") | |
| print("-" * 70) | |
| result = lab.design_composite( | |
| "Carbon Fiber Epoxy", | |
| "Epoxy Resin", | |
| fiber_volume_fraction=0.65, | |
| layup=[0, 45, 90, -45] | |
| ) | |
| print(f"Composite: {result.optimized_properties.name}") | |
| print(f" Density: {result.optimized_properties.density:.0f} kg/m³") | |
| print(f" Modulus: {result.optimized_properties.youngs_modulus:.1f} GPa") | |
| print(f" Strength: {result.optimized_properties.tensile_strength:.0f} MPa") | |
| print(f" Specific Strength: {result.fitness_score:.1f}") | |
| # Demo 5: Material comparison | |
| print("\n5. COMPARE AEROSPACE MATERIALS") | |
| print("-" * 70) | |
| comparison = lab.compare_materials( | |
| ["Al 7075-T6", "Ti-6Al-4V", "Carbon Fiber Epoxy"], | |
| ["density", "tensile_strength", "youngs_modulus"] | |
| ) | |
| print(f"{'Material':<25} {'Density':<12} {'Strength':<12} {'Modulus':<12}") | |
| print(f"{'':25} {'(kg/m³)':<12} {'(MPa)':<12} {'(GPa)':<12}") | |
| print("-" * 70) | |
| for name, props in comparison.items(): | |
| print(f"{name:<25} {props['density']:<12.0f} {props['tensile_strength']:<12.0f} {props['youngs_modulus']:<12.1f}") | |
| # Demo 6: Find best material | |
| print("\n6. FIND BEST LIGHTWEIGHT HIGH-STRENGTH MATERIAL") | |
| print("-" * 70) | |
| best = lab.find_best_material( | |
| optimize_for="tensile_strength", | |
| constraints={ | |
| "density": (0, 2000), # Low density | |
| "tensile_strength": (100, 1e6) # Reasonable strength | |
| } | |
| ) | |
| if best: | |
| print(f"Best material: {best.name}") | |
| print(f" Density: {best.density:.0f} kg/m³") | |
| print(f" Strength: {best.tensile_strength:.0f} MPa") | |
| print(f" Specific Strength: {best.tensile_strength/best.density*1000:.1f} MPa/(g/cm³)") | |
| # Demo 7: Statistics | |
| print("\n7. DATABASE STATISTICS") | |
| print("-" * 70) | |
| stats = lab.get_statistics() | |
| print(f"Total Materials: {stats['total_materials']}") | |
| print(f"\nBy Category:") | |
| for cat, count in sorted(stats['categories'].items()): | |
| print(f" {cat}: {count}") | |
| print("\n" + "="*70) | |
| print("Materials Lab demonstration complete! ✓") | |
| print("="*70) | |