""" ANSYS Error Handler for CAE Mesh Generator This module provides specialized error handling for ANSYS Mechanical operations, including error classification, diagnosis, and solution recommendations. """ import logging import re from typing import Dict, Any, Optional, List, Tuple from datetime import datetime from dataclasses import dataclass from enum import Enum logger = logging.getLogger(__name__) class ErrorSeverity(Enum): """Error severity levels""" CRITICAL = "critical" HIGH = "high" MEDIUM = "medium" LOW = "low" WARNING = "warning" class ErrorCategory(Enum): """ANSYS error categories""" LICENSING = "licensing" GEOMETRY = "geometry" MESH = "mesh" SOLVER = "solver" MEMORY = "memory" FILE_IO = "file_io" CONNECTIVITY = "connectivity" CONFIGURATION = "configuration" UNKNOWN = "unknown" @dataclass class ErrorDiagnosis: """Comprehensive error diagnosis""" error_id: str category: ErrorCategory severity: ErrorSeverity title: str description: str root_cause: str immediate_solutions: List[str] preventive_measures: List[str] related_documentation: List[str] recovery_possible: bool estimated_fix_time: int # minutes confidence_level: float # 0.0 to 1.0 @dataclass class ErrorContext: """Context information for error analysis""" operation_type: str file_path: Optional[str] = None mesh_settings: Optional[Dict[str, Any]] = None system_info: Optional[Dict[str, Any]] = None previous_errors: Optional[List[str]] = None timestamp: datetime = None def __post_init__(self): if self.timestamp is None: self.timestamp = datetime.now() class ANSYSErrorHandler: """ Specialized error handler for ANSYS Mechanical operations This class provides intelligent error analysis, classification, and solution recommendations for ANSYS-specific errors. """ def __init__(self): """Initialize ANSYS error handler""" self.error_patterns = self._initialize_error_patterns() self.error_history = [] self.solution_database = self._initialize_solution_database() logger.info("ANSYS Error Handler initialized") def _initialize_error_patterns(self) -> Dict[str, Dict[str, Any]]: """Initialize known ANSYS error patterns""" return { # Licensing errors 'license_not_available': { 'patterns': [ r'license.*not.*available', r'no.*license.*found', r'license.*server.*not.*responding', r'flexlm.*error' ], 'category': ErrorCategory.LICENSING, 'severity': ErrorSeverity.CRITICAL, 'keywords': ['license', 'flexlm', 'server'] }, # Geometry errors 'geometry_import_failed': { 'patterns': [ r'failed.*to.*import.*geometry', r'geometry.*file.*corrupt', r'invalid.*step.*file', r'cad.*import.*error' ], 'category': ErrorCategory.GEOMETRY, 'severity': ErrorSeverity.HIGH, 'keywords': ['geometry', 'import', 'step', 'cad'] }, 'geometry_invalid': { 'patterns': [ r'invalid.*geometry', r'geometry.*contains.*errors', r'self.*intersecting.*surfaces', r'non.*manifold.*geometry' ], 'category': ErrorCategory.GEOMETRY, 'severity': ErrorSeverity.HIGH, 'keywords': ['geometry', 'invalid', 'intersecting', 'manifold'] }, # Mesh errors 'mesh_generation_failed': { 'patterns': [ r'mesh.*generation.*failed', r'meshing.*error', r'unable.*to.*generate.*mesh', r'mesh.*quality.*too.*poor' ], 'category': ErrorCategory.MESH, 'severity': ErrorSeverity.HIGH, 'keywords': ['mesh', 'generation', 'failed', 'quality'] }, 'mesh_memory_error': { 'patterns': [ r'insufficient.*memory.*for.*meshing', r'out.*of.*memory.*during.*mesh', r'memory.*allocation.*failed.*mesh' ], 'category': ErrorCategory.MEMORY, 'severity': ErrorSeverity.HIGH, 'keywords': ['memory', 'mesh', 'allocation'] }, # Memory errors 'out_of_memory': { 'patterns': [ r'out.*of.*memory', r'insufficient.*memory', r'memory.*allocation.*failed', r'virtual.*memory.*exhausted' ], 'category': ErrorCategory.MEMORY, 'severity': ErrorSeverity.CRITICAL, 'keywords': ['memory', 'allocation', 'virtual'] }, # File I/O errors 'file_not_found': { 'patterns': [ r'file.*not.*found', r'cannot.*open.*file', r'access.*denied.*file', r'file.*path.*invalid' ], 'category': ErrorCategory.FILE_IO, 'severity': ErrorSeverity.MEDIUM, 'keywords': ['file', 'path', 'access', 'open'] }, 'file_permission_error': { 'patterns': [ r'permission.*denied', r'access.*denied', r'file.*is.*read.*only', r'cannot.*write.*to.*file' ], 'category': ErrorCategory.FILE_IO, 'severity': ErrorSeverity.MEDIUM, 'keywords': ['permission', 'access', 'denied', 'read-only'] }, # Connectivity errors 'connection_lost': { 'patterns': [ r'connection.*lost', r'server.*disconnected', r'communication.*error', r'remote.*session.*terminated' ], 'category': ErrorCategory.CONNECTIVITY, 'severity': ErrorSeverity.HIGH, 'keywords': ['connection', 'server', 'communication', 'remote'] }, # Configuration errors 'invalid_settings': { 'patterns': [ r'invalid.*settings', r'configuration.*error', r'parameter.*out.*of.*range', r'incompatible.*options' ], 'category': ErrorCategory.CONFIGURATION, 'severity': ErrorSeverity.MEDIUM, 'keywords': ['settings', 'configuration', 'parameter', 'options'] } } def _initialize_solution_database(self) -> Dict[str, ErrorDiagnosis]: """Initialize solution database for known errors""" return { 'license_not_available': ErrorDiagnosis( error_id='license_not_available', category=ErrorCategory.LICENSING, severity=ErrorSeverity.CRITICAL, title='ANSYS License Not Available', description='ANSYS license server is not responding or no licenses are available.', root_cause='License server connectivity issues or license pool exhaustion.', immediate_solutions=[ 'Check ANSYS license server status', 'Verify network connectivity to license server', 'Wait for license to become available', 'Contact system administrator for license allocation' ], preventive_measures=[ 'Monitor license usage patterns', 'Schedule operations during off-peak hours', 'Implement license queue management' ], related_documentation=[ 'ANSYS Licensing Guide', 'FlexLM Administrator Guide' ], recovery_possible=True, estimated_fix_time=15, confidence_level=0.9 ), 'geometry_import_failed': ErrorDiagnosis( error_id='geometry_import_failed', category=ErrorCategory.GEOMETRY, severity=ErrorSeverity.HIGH, title='Geometry Import Failed', description='Failed to import geometry file into ANSYS Mechanical.', root_cause='Corrupted geometry file, unsupported format, or file access issues.', immediate_solutions=[ 'Verify geometry file format (STEP, IGES, etc.)', 'Check file integrity and size', 'Try importing with different CAD translator settings', 'Repair geometry in original CAD software' ], preventive_measures=[ 'Validate geometry files before import', 'Use supported file formats', 'Maintain file backup copies' ], related_documentation=[ 'ANSYS Geometry Import Guide', 'CAD File Format Compatibility' ], recovery_possible=True, estimated_fix_time=30, confidence_level=0.8 ), 'mesh_generation_failed': ErrorDiagnosis( error_id='mesh_generation_failed', category=ErrorCategory.MESH, severity=ErrorSeverity.HIGH, title='Mesh Generation Failed', description='ANSYS failed to generate mesh for the geometry.', root_cause='Complex geometry, inappropriate mesh settings, or geometry quality issues.', immediate_solutions=[ 'Increase global element size', 'Simplify geometry by removing small features', 'Use different meshing algorithm', 'Apply local mesh controls to problematic areas', 'Check geometry for errors and repair if needed' ], preventive_measures=[ 'Prepare geometry for meshing (defeaturing)', 'Use appropriate mesh sizing for geometry scale', 'Validate geometry quality before meshing' ], related_documentation=[ 'ANSYS Meshing Best Practices', 'Geometry Preparation Guidelines' ], recovery_possible=True, estimated_fix_time=45, confidence_level=0.7 ), 'out_of_memory': ErrorDiagnosis( error_id='out_of_memory', category=ErrorCategory.MEMORY, severity=ErrorSeverity.CRITICAL, title='Insufficient Memory', description='ANSYS ran out of available system memory during operation.', root_cause='Large model size, insufficient RAM, or memory leaks.', immediate_solutions=[ 'Reduce mesh density (increase element size)', 'Close other applications to free memory', 'Use 64-bit ANSYS version if available', 'Enable virtual memory/swap file', 'Simplify geometry to reduce memory requirements' ], preventive_measures=[ 'Monitor memory usage during operations', 'Upgrade system RAM if frequently encountered', 'Use mesh sizing appropriate for available memory' ], related_documentation=[ 'ANSYS Memory Management Guide', 'System Requirements Documentation' ], recovery_possible=True, estimated_fix_time=20, confidence_level=0.9 ), 'connection_lost': ErrorDiagnosis( error_id='connection_lost', category=ErrorCategory.CONNECTIVITY, severity=ErrorSeverity.HIGH, title='Connection Lost', description='Connection to ANSYS server or remote session was lost.', root_cause='Network connectivity issues, server problems, or session timeout.', immediate_solutions=[ 'Check network connectivity', 'Restart ANSYS session', 'Verify server status', 'Check firewall settings', 'Increase session timeout if applicable' ], preventive_measures=[ 'Use stable network connections', 'Monitor network reliability', 'Implement session recovery mechanisms' ], related_documentation=[ 'ANSYS Remote Session Guide', 'Network Configuration Requirements' ], recovery_possible=True, estimated_fix_time=10, confidence_level=0.8 ) } def analyze_error(self, error_message: str, context: ErrorContext = None) -> ErrorDiagnosis: """ Analyze error message and provide comprehensive diagnosis Args: error_message: Error message from ANSYS context: Additional context information Returns: ErrorDiagnosis with analysis and recommendations """ try: logger.info(f"Analyzing ANSYS error: {error_message[:100]}...") # Normalize error message for analysis normalized_error = error_message.lower().strip() # Try to match against known patterns matched_pattern = self._match_error_pattern(normalized_error) if matched_pattern: # Get diagnosis from solution database diagnosis = self.solution_database.get(matched_pattern) if diagnosis: # Enhance diagnosis with context enhanced_diagnosis = self._enhance_diagnosis_with_context(diagnosis, context, error_message) # Record error for learning self._record_error(error_message, enhanced_diagnosis, context) return enhanced_diagnosis # If no pattern matched, create generic diagnosis generic_diagnosis = self._create_generic_diagnosis(error_message, context) self._record_error(error_message, generic_diagnosis, context) return generic_diagnosis except Exception as e: logger.error(f"Error analysis failed: {str(e)}") return self._create_fallback_diagnosis(error_message) def _match_error_pattern(self, error_message: str) -> Optional[str]: """ Match error message against known patterns Args: error_message: Normalized error message Returns: Matched pattern key or None """ try: for pattern_key, pattern_info in self.error_patterns.items(): # Check regex patterns for pattern in pattern_info['patterns']: if re.search(pattern, error_message, re.IGNORECASE): logger.debug(f"Matched pattern: {pattern_key}") return pattern_key # Check keywords keywords = pattern_info.get('keywords', []) if keywords and any(keyword in error_message for keyword in keywords): # Additional confidence check for keyword matches keyword_count = sum(1 for keyword in keywords if keyword in error_message) if keyword_count >= len(keywords) * 0.5: # At least 50% of keywords match logger.debug(f"Matched keywords for pattern: {pattern_key}") return pattern_key return None except Exception as e: logger.warning(f"Pattern matching failed: {str(e)}") return None def _enhance_diagnosis_with_context(self, base_diagnosis: ErrorDiagnosis, context: ErrorContext, error_message: str) -> ErrorDiagnosis: """ Enhance diagnosis with context-specific information Args: base_diagnosis: Base diagnosis from database context: Error context error_message: Original error message Returns: Enhanced ErrorDiagnosis """ try: # Create a copy of the base diagnosis enhanced = ErrorDiagnosis( error_id=base_diagnosis.error_id, category=base_diagnosis.category, severity=base_diagnosis.severity, title=base_diagnosis.title, description=base_diagnosis.description, root_cause=base_diagnosis.root_cause, immediate_solutions=base_diagnosis.immediate_solutions.copy(), preventive_measures=base_diagnosis.preventive_measures.copy(), related_documentation=base_diagnosis.related_documentation.copy(), recovery_possible=base_diagnosis.recovery_possible, estimated_fix_time=base_diagnosis.estimated_fix_time, confidence_level=base_diagnosis.confidence_level ) # Add context-specific enhancements if context: # File-specific recommendations if context.file_path: if base_diagnosis.category == ErrorCategory.GEOMETRY: enhanced.immediate_solutions.insert(0, f"Verify file: {context.file_path}") elif base_diagnosis.category == ErrorCategory.FILE_IO: enhanced.immediate_solutions.insert(0, f"Check file permissions for: {context.file_path}") # Operation-specific recommendations if context.operation_type == 'mesh_generation': if base_diagnosis.category == ErrorCategory.MEMORY: enhanced.immediate_solutions.insert(0, "Consider reducing mesh density for this operation") # System-specific recommendations if context.system_info: available_memory = context.system_info.get('available_memory_gb', 0) if available_memory < 4 and base_diagnosis.category == ErrorCategory.MEMORY: enhanced.immediate_solutions.insert(0, f"System has only {available_memory}GB available - consider upgrading RAM") # Previous error context if context.previous_errors: if len(context.previous_errors) > 2: enhanced.severity = ErrorSeverity.CRITICAL enhanced.immediate_solutions.insert(0, "Multiple consecutive errors detected - consider system restart") # Add original error message for reference enhanced.description += f"\n\nOriginal error: {error_message}" return enhanced except Exception as e: logger.warning(f"Context enhancement failed: {str(e)}") return base_diagnosis def _create_generic_diagnosis(self, error_message: str, context: ErrorContext = None) -> ErrorDiagnosis: """ Create generic diagnosis for unknown errors Args: error_message: Error message context: Error context Returns: Generic ErrorDiagnosis """ try: # Analyze error message for clues category = self._infer_error_category(error_message) severity = self._infer_error_severity(error_message) return ErrorDiagnosis( error_id='unknown_error', category=category, severity=severity, title='Unknown ANSYS Error', description=f'An unrecognized error occurred in ANSYS: {error_message}', root_cause='Unknown - error pattern not recognized in current database.', immediate_solutions=[ 'Check ANSYS log files for additional details', 'Verify system resources (memory, disk space)', 'Restart ANSYS session and retry operation', 'Contact technical support with error details' ], preventive_measures=[ 'Keep ANSYS software updated', 'Monitor system resources during operations', 'Maintain regular backups of work' ], related_documentation=[ 'ANSYS User Manual', 'ANSYS Troubleshooting Guide' ], recovery_possible=True, estimated_fix_time=30, confidence_level=0.3 ) except Exception as e: logger.error(f"Generic diagnosis creation failed: {str(e)}") return self._create_fallback_diagnosis(error_message) def _infer_error_category(self, error_message: str) -> ErrorCategory: """Infer error category from message content""" message_lower = error_message.lower() if any(word in message_lower for word in ['license', 'flexlm']): return ErrorCategory.LICENSING elif any(word in message_lower for word in ['geometry', 'cad', 'step', 'import']): return ErrorCategory.GEOMETRY elif any(word in message_lower for word in ['mesh', 'element', 'node']): return ErrorCategory.MESH elif any(word in message_lower for word in ['memory', 'allocation', 'ram']): return ErrorCategory.MEMORY elif any(word in message_lower for word in ['file', 'path', 'directory']): return ErrorCategory.FILE_IO elif any(word in message_lower for word in ['connection', 'server', 'network']): return ErrorCategory.CONNECTIVITY elif any(word in message_lower for word in ['setting', 'parameter', 'configuration']): return ErrorCategory.CONFIGURATION else: return ErrorCategory.UNKNOWN def _infer_error_severity(self, error_message: str) -> ErrorSeverity: """Infer error severity from message content""" message_lower = error_message.lower() if any(word in message_lower for word in ['critical', 'fatal', 'crash', 'abort']): return ErrorSeverity.CRITICAL elif any(word in message_lower for word in ['error', 'failed', 'cannot', 'unable']): return ErrorSeverity.HIGH elif any(word in message_lower for word in ['warning', 'caution']): return ErrorSeverity.WARNING else: return ErrorSeverity.MEDIUM def _create_fallback_diagnosis(self, error_message: str) -> ErrorDiagnosis: """Create minimal fallback diagnosis when all else fails""" return ErrorDiagnosis( error_id='fallback_error', category=ErrorCategory.UNKNOWN, severity=ErrorSeverity.MEDIUM, title='ANSYS Error', description=f'Error occurred: {error_message}', root_cause='Unable to determine root cause.', immediate_solutions=['Restart ANSYS and retry', 'Check system resources', 'Contact support'], preventive_measures=['Monitor system health', 'Keep software updated'], related_documentation=['ANSYS Documentation'], recovery_possible=True, estimated_fix_time=15, confidence_level=0.1 ) def _record_error(self, error_message: str, diagnosis: ErrorDiagnosis, context: ErrorContext = None): """Record error for learning and analysis""" try: error_record = { 'timestamp': datetime.now(), 'error_message': error_message, 'diagnosis': diagnosis, 'context': context, 'resolved': False } self.error_history.append(error_record) # Keep only recent errors (last 100) if len(self.error_history) > 100: self.error_history = self.error_history[-100:] logger.debug(f"Recorded error: {diagnosis.error_id}") except Exception as e: logger.warning(f"Error recording failed: {str(e)}") def get_error_statistics(self) -> Dict[str, Any]: """ Get error statistics and trends Returns: Dictionary with error statistics """ try: if not self.error_history: return { 'total_errors': 0, 'categories': {}, 'severities': {}, 'most_common': [], 'resolution_rate': 0.0 } # Count by category categories = {} severities = {} error_types = {} for record in self.error_history: diagnosis = record['diagnosis'] # Count categories cat = diagnosis.category.value categories[cat] = categories.get(cat, 0) + 1 # Count severities sev = diagnosis.severity.value severities[sev] = severities.get(sev, 0) + 1 # Count error types error_id = diagnosis.error_id error_types[error_id] = error_types.get(error_id, 0) + 1 # Find most common errors most_common = sorted(error_types.items(), key=lambda x: x[1], reverse=True)[:5] # Calculate resolution rate (simplified) resolved_count = sum(1 for record in self.error_history if record.get('resolved', False)) resolution_rate = resolved_count / len(self.error_history) if self.error_history else 0.0 return { 'total_errors': len(self.error_history), 'categories': categories, 'severities': severities, 'most_common': most_common, 'resolution_rate': resolution_rate, 'recent_errors': len([r for r in self.error_history if (datetime.now() - r['timestamp']).days < 1]) } except Exception as e: logger.error(f"Error statistics calculation failed: {str(e)}") return {'error': str(e)} def get_handler_info(self) -> Dict[str, Any]: """ Get information about the error handler Returns: Dictionary with handler information """ return { 'handler_type': 'ANSYSErrorHandler', 'known_patterns': len(self.error_patterns), 'solution_database_size': len(self.solution_database), 'error_history_size': len(self.error_history), 'supported_categories': [cat.value for cat in ErrorCategory], 'severity_levels': [sev.value for sev in ErrorSeverity], 'capabilities': [ 'error_pattern_matching', 'intelligent_diagnosis', 'solution_recommendations', 'context_aware_analysis', 'error_statistics', 'learning_from_history' ] }