""" Simulation Validator - Validates design parameters before running FEA simulations. This module helps prevent simulation failures by: 1. Checking if geometry will be valid 2. Validating parameter combinations 3. Providing actionable error messages 4. Detecting likely failure modes Usage: validator = SimulationValidator(model_type='circular_plate') is_valid, warnings = validator.validate(design_variables) if not is_valid: print(f"Invalid parameters: {warnings}") """ from typing import Dict, Tuple, List from pathlib import Path class SimulationValidator: """Validates design parameters before running simulations.""" def __init__(self, model_type: str = 'generic', verbose: bool = True): """ Initialize validator for specific model type. Args: model_type: Type of FEA model ('circular_plate', 'beam', etc.) verbose: Print validation warnings """ self.model_type = model_type self.verbose = verbose # Model-specific validation rules self.validation_rules = self._get_validation_rules(model_type) def _get_validation_rules(self, model_type: str) -> Dict: """Get validation rules for specific model type.""" if model_type == 'circular_plate': # NOTE: Only use parameter bounds for validation # No arbitrary aspect ratio checks - let Optuna explore the full parameter space # Modal analysis is robust and doesn't need strict aspect ratio limits return {} # Generic rules for unknown models return {} def validate( self, design_variables: Dict[str, float], strict: bool = False ) -> Tuple[bool, List[str]]: """ Validate design variables before simulation. Args: design_variables: Dict of parameter names to values strict: If True, reject on soft limit violations (warnings) Returns: (is_valid, warnings_list) - is_valid: True if parameters are acceptable - warnings_list: List of warning/error messages """ warnings = [] is_valid = True # Check each parameter for param_name, value in design_variables.items(): if param_name not in self.validation_rules: continue # No rules for this parameter rules = self.validation_rules[param_name] # Hard limits (always reject) if value < rules.get('min', float('-inf')): is_valid = False warnings.append( f"INVALID: {param_name}={value:.2f} < min={rules['min']:.2f}. " f"{rules.get('reason', '')}" ) if value > rules.get('max', float('inf')): is_valid = False warnings.append( f"INVALID: {param_name}={value:.2f} > max={rules['max']:.2f}. " f"{rules.get('reason', '')}" ) # Soft limits (warnings, may cause issues) if 'soft_min' in rules and value < rules['soft_min']: msg = ( f"WARNING: {param_name}={value:.2f} < recommended={rules['soft_min']:.2f}. " f"{rules.get('reason', 'May cause simulation issues')}" ) warnings.append(msg) if strict: is_valid = False if 'soft_max' in rules and value > rules['soft_max']: msg = ( f"WARNING: {param_name}={value:.2f} > recommended={rules['soft_max']:.2f}. " f"{rules.get('reason', 'May cause simulation issues')}" ) warnings.append(msg) if strict: is_valid = False # Model-specific combined checks can be added here if needed # For now, rely only on parameter bounds (no arbitrary physics checks) # Print warnings if verbose if self.verbose and warnings: print(f"\n[VALIDATOR] Validation results:") for warning in warnings: print(f" {warning}") return is_valid, warnings def _validate_circular_plate_aspect_ratio( self, design_variables: Dict[str, float] ) -> tuple[bool, List[str]]: """Check circular plate aspect ratio (diameter/thickness). Returns: (is_valid, warnings): Tuple of validation status and warning messages """ warnings = [] is_valid = True diameter = design_variables.get('inner_diameter') thickness = design_variables.get('plate_thickness') if diameter and thickness: aspect_ratio = diameter / thickness rules = self.validation_rules.get('aspect_ratio', {}) min_aspect = rules.get('min', 0) max_aspect = rules.get('max', float('inf')) if aspect_ratio > max_aspect: is_valid = False # FIX: Make this a hard rejection warnings.append( f"INVALID: Aspect ratio {aspect_ratio:.1f} > {max_aspect:.1f}. " f"Very thin plate will cause numerical instability." ) elif aspect_ratio < min_aspect: is_valid = False # FIX: Make this a hard rejection warnings.append( f"INVALID: Aspect ratio {aspect_ratio:.1f} < {min_aspect:.1f}. " f"Very thick plate will have poor mesh quality." ) return is_valid, warnings def suggest_corrections( self, design_variables: Dict[str, float] ) -> Dict[str, float]: """ Suggest corrected parameters that are more likely to succeed. Args: design_variables: Original parameters Returns: Corrected parameters (clamped to safe ranges) """ corrected = design_variables.copy() for param_name, value in design_variables.items(): if param_name not in self.validation_rules: continue rules = self.validation_rules[param_name] # Clamp to soft limits (safer range) soft_min = rules.get('soft_min', rules.get('min', float('-inf'))) soft_max = rules.get('soft_max', rules.get('max', float('inf'))) if value < soft_min: corrected[param_name] = soft_min if self.verbose: print(f"[VALIDATOR] Corrected {param_name}: {value:.2f} -> {soft_min:.2f}") if value > soft_max: corrected[param_name] = soft_max if self.verbose: print(f"[VALIDATOR] Corrected {param_name}: {value:.2f} -> {soft_max:.2f}") return corrected def validate_before_simulation( design_variables: Dict[str, float], model_type: str = 'circular_plate', strict: bool = False ) -> Tuple[bool, List[str]]: """ Convenience function for quick validation. Args: design_variables: Parameters to validate model_type: Type of FEA model strict: Reject on warnings (not just errors) Returns: (is_valid, warnings) """ validator = SimulationValidator(model_type=model_type, verbose=False) return validator.validate(design_variables, strict=strict)