AnsysLink/backend/utils/mesh_processor.py

321 lines
14 KiB
Python

"""
Main mesh processing workflow for CAE Mesh Generator
This module implements the complete mesh generation workflow including
geometry import, named selections, mesh controls, generation, and quality check.
"""
import logging
from typing import Dict, Any, Optional, Callable
from datetime import datetime
from enum import Enum
from backend.pymechanical.session_manager import ANSYSSessionManager
from backend.utils.state_manager import state_manager
logger = logging.getLogger(__name__)
class ProcessingStep(Enum):
"""Processing step enumeration"""
INITIALIZING = "initializing"
STARTING_SESSION = "starting_session"
IMPORTING_GEOMETRY = "importing_geometry"
VALIDATING_GEOMETRY = "validating_geometry"
CREATING_NAMED_SELECTIONS = "creating_named_selections"
APPLYING_MESH_CONTROLS = "applying_mesh_controls"
GENERATING_MESH = "generating_mesh"
CHECKING_QUALITY = "checking_quality"
FINALIZING = "finalizing"
COMPLETED = "completed"
FAILED = "failed"
class MeshProcessingResult:
"""Result container for mesh processing workflow"""
def __init__(self):
self.success = False
self.current_step = ProcessingStep.INITIALIZING
self.progress_percentage = 0.0
self.started_at = None
self.completed_at = None
self.total_time = 0.0
self.error_message = None
self.warnings = []
# Step results
self.geometry_result = None
self.named_selections_result = None
self.mesh_controls_result = None
self.mesh_generation_result = None
self.quality_check_result = None
# Final mesh info
self.element_count = 0
self.node_count = 0
self.quality_score = 0.0
self.quality_status = "UNKNOWN"
def process_blade_mesh(
file_path: str,
progress_callback: Optional[Callable[[float, str, ProcessingStep], None]] = None,
simulation_mode: bool = False
) -> MeshProcessingResult:
"""
Main mesh processing function for blade geometry
This function implements the complete workflow from geometry import to
quality checking, integrating all mesh generation components.
Args:
file_path: Path to the STEP file
progress_callback: Optional callback for progress updates (progress%, message, step)
simulation_mode: Whether to use simulation mode (for development/testing)
Returns:
MeshProcessingResult with complete processing results
"""
result = MeshProcessingResult()
result.started_at = datetime.now()
session_manager = None
def update_progress(percentage: float, message: str, step: ProcessingStep = None):
"""Update progress and call callback if provided"""
result.progress_percentage = percentage
if step:
result.current_step = step
if progress_callback:
try:
progress_callback(percentage, message, result.current_step)
except Exception as e:
logger.warning(f"Progress callback error: {str(e)}")
logger.info(f"Progress: {percentage:.1f}% - {message} (Step: {result.current_step.value})")
try:
# Step 1: Initialize session
update_progress(5.0, "Initializing ANSYS session...", ProcessingStep.STARTING_SESSION)
session_manager = ANSYSSessionManager(simulation_mode=simulation_mode)
if not session_manager.start_session():
raise Exception("Failed to start ANSYS session")
update_progress(10.0, "ANSYS session started successfully")
# Step 2: Import geometry
update_progress(15.0, "Importing geometry from STEP file...", ProcessingStep.IMPORTING_GEOMETRY)
if not session_manager.import_geometry(file_path):
raise Exception("Failed to import geometry")
update_progress(25.0, "Geometry imported successfully")
# Step 3: Validate geometry
update_progress(30.0, "Validating imported geometry...", ProcessingStep.VALIDATING_GEOMETRY)
geometry_validation = session_manager.validate_geometry()
result.geometry_result = geometry_validation
if not geometry_validation.get('valid', False):
raise Exception(f"Geometry validation failed: {geometry_validation.get('error', 'Unknown error')}")
update_progress(35.0, f"Geometry validated: {geometry_validation.get('surface_count', 0)} surfaces")
# Step 4: Create named selections
update_progress(40.0, "Creating named selections for blade features...", ProcessingStep.CREATING_NAMED_SELECTIONS)
named_selections_result = session_manager.create_named_selections()
result.named_selections_result = named_selections_result
if not named_selections_result.get('success', False):
result.warnings.append(f"Named selections creation had issues: {named_selections_result.get('error', 'Unknown error')}")
logger.warning("Named selections creation failed, continuing without them")
else:
selections_count = named_selections_result.get('total_selections', 0)
update_progress(50.0, f"Named selections created: {selections_count} selections")
# Step 5: Apply mesh controls
update_progress(55.0, "Applying mesh controls and sizing...", ProcessingStep.APPLYING_MESH_CONTROLS)
# Get named selections for mesh controls
named_selections = []
if named_selections_result.get('success'):
named_selections = named_selections_result.get('selections_created', [])
mesh_controls_result = session_manager.apply_mesh_controls(named_selections)
result.mesh_controls_result = mesh_controls_result
if not mesh_controls_result.get('success', False):
result.warnings.append(f"Mesh controls application had issues: {mesh_controls_result.get('error', 'Unknown error')}")
logger.warning("Mesh controls application failed, using defaults")
else:
controls_count = len(mesh_controls_result.get('controls_applied', []))
update_progress(65.0, f"Mesh controls applied: {controls_count} controls")
# Step 6: Generate mesh
update_progress(70.0, "Generating mesh...", ProcessingStep.GENERATING_MESH)
# Set up progress callback for mesh generation
def mesh_progress_callback(mesh_progress: float, mesh_message: str):
# Map mesh generation progress (0-100) to overall progress (70-85)
overall_progress = 70.0 + (mesh_progress / 100.0) * 15.0
update_progress(overall_progress, f"Mesh generation: {mesh_message}")
mesh_generation_result = session_manager.generate_mesh(progress_callback=mesh_progress_callback)
result.mesh_generation_result = mesh_generation_result
if not mesh_generation_result.get('success', False):
raise Exception(f"Mesh generation failed: {mesh_generation_result.get('error_message', 'Unknown error')}")
result.element_count = mesh_generation_result.get('element_count', 0)
result.node_count = mesh_generation_result.get('node_count', 0)
update_progress(85.0, f"Mesh generated: {result.element_count} elements, {result.node_count} nodes")
# Step 7: Check mesh quality
update_progress(90.0, "Checking mesh quality...", ProcessingStep.CHECKING_QUALITY)
quality_check_result = session_manager.check_mesh_quality()
result.quality_check_result = quality_check_result
if quality_check_result.get('success', False):
result.quality_score = quality_check_result.get('quality_score', 0.0)
result.quality_status = quality_check_result.get('overall_status', 'UNKNOWN')
quality_issues = len(quality_check_result.get('critical_issues', []))
if quality_issues > 0:
result.warnings.append(f"Mesh quality check found {quality_issues} critical issues")
update_progress(95.0, f"Quality check completed: {result.quality_status} (Score: {result.quality_score:.1f})")
else:
result.warnings.append(f"Quality check failed: {quality_check_result.get('error', 'Unknown error')}")
result.quality_status = "CHECK_FAILED"
update_progress(95.0, "Quality check failed, but mesh generation completed")
# Step 8: Finalize
update_progress(98.0, "Finalizing results...", ProcessingStep.FINALIZING)
# Calculate total processing time
result.completed_at = datetime.now()
result.total_time = (result.completed_at - result.started_at).total_seconds()
# Mark as successful
result.success = True
result.current_step = ProcessingStep.COMPLETED
update_progress(100.0, f"Mesh processing completed successfully in {result.total_time:.1f} seconds", ProcessingStep.COMPLETED)
logger.info(f"✓ Blade mesh processing completed successfully:")
logger.info(f" - Elements: {result.element_count}")
logger.info(f" - Nodes: {result.node_count}")
logger.info(f" - Quality Score: {result.quality_score:.1f}")
logger.info(f" - Quality Status: {result.quality_status}")
logger.info(f" - Processing Time: {result.total_time:.1f} seconds")
return result
except Exception as e:
# Handle processing failure
logger.error(f"Mesh processing failed: {str(e)}")
result.success = False
result.current_step = ProcessingStep.FAILED
result.error_message = str(e)
result.completed_at = datetime.now()
if result.started_at:
result.total_time = (result.completed_at - result.started_at).total_seconds()
update_progress(0.0, f"Processing failed: {str(e)}", ProcessingStep.FAILED)
return result
finally:
# Clean up session
if session_manager:
try:
session_manager.close_session()
session_manager.cleanup_temp_files()
logger.info("✓ Session cleanup completed")
except Exception as cleanup_error:
logger.warning(f"Session cleanup error: {str(cleanup_error)}")
def process_blade_mesh_with_state_updates(
file_path: str,
simulation_mode: bool = False
) -> MeshProcessingResult:
"""
Process blade mesh with automatic state manager updates
This function wraps the main processing function and automatically
updates the global state manager with progress information.
Args:
file_path: Path to the STEP file
simulation_mode: Whether to use simulation mode
Returns:
MeshProcessingResult with complete processing results
"""
def progress_callback(percentage: float, message: str, step: ProcessingStep):
"""Update state manager with progress"""
try:
# Update processing status in state manager
processing_status = state_manager.get_processing_status()
processing_status.status = step.value
processing_status.progress_percentage = percentage
processing_status.current_operation = message
processing_status.last_updated = datetime.now()
# Update state manager
state_manager.update_processing_status(processing_status)
except Exception as e:
logger.warning(f"State update error: {str(e)}")
logger.info(f"Starting blade mesh processing with state updates: {file_path}")
# Perform mesh processing with state updates
result = process_blade_mesh(
file_path=file_path,
progress_callback=progress_callback,
simulation_mode=simulation_mode
)
# Update final state
try:
if result.success:
# Create mesh result for state manager
from backend.models.data_models import MeshResult
mesh_result = MeshResult(
element_count=result.element_count,
node_count=result.node_count,
generation_time=result.total_time,
quality_score=result.quality_score,
quality_status=result.quality_status,
mesh_file_path=None, # Could be added later for file output
created_at=result.completed_at
)
state_manager.set_mesh_result(mesh_result)
# Update processing status to completed
processing_status = state_manager.get_processing_status()
processing_status.status = "completed"
processing_status.progress_percentage = 100.0
processing_status.current_operation = "Mesh processing completed successfully"
processing_status.completed_at = result.completed_at
state_manager.update_processing_status(processing_status)
else:
# Update processing status to failed
processing_status = state_manager.get_processing_status()
processing_status.status = "failed"
processing_status.error_message = result.error_message
processing_status.completed_at = result.completed_at
state_manager.update_processing_status(processing_status)
except Exception as state_error:
logger.error(f"State manager update failed: {str(state_error)}")
return result