""" Named Selection Manager for CAE Mesh Generator This module handles automatic creation of named selections for blade geometry, specifically identifying leading edge, trailing edge, and blade root regions. """ import logging from typing import Dict, List, Any, Optional from datetime import datetime logger = logging.getLogger(__name__) class NamedSelectionManager: """ Manager for creating and managing named selections in ANSYS Mechanical This class provides functionality to automatically identify and create named selections for blade geometry features like leading edge, trailing edge, and blade root regions. """ def __init__(self, mechanical_session): """ Initialize named selection manager Args: mechanical_session: Active PyMechanical session """ self.mechanical = mechanical_session self.created_selections = {} self.selection_criteria = { 'leading_edge': { 'description': 'High curvature edges at blade leading edge', 'method': 'curvature_based' }, 'trailing_edge': { 'description': 'Sharp edges at blade trailing edge', 'method': 'angle_based' }, 'blade_root': { 'description': 'Connection surfaces at blade root', 'method': 'location_based' }, 'blade_surfaces': { 'description': 'External blade surfaces', 'method': 'surface_based' } } logger.info("Named Selection Manager initialized") def create_blade_named_selections(self) -> Dict[str, Any]: """ Create all named selections for blade geometry Returns: Dictionary with creation results and selection information """ try: logger.info("Creating blade named selections...") results = { 'success': True, 'selections_created': [], 'selections_failed': [], 'total_selections': 0, 'created_at': datetime.now() } # Create each named selection for selection_name, criteria in self.selection_criteria.items(): try: logger.info(f"Creating named selection: {selection_name}") success = self._create_named_selection( name=selection_name, description=criteria['description'], method=criteria['method'] ) if success: results['selections_created'].append(selection_name) logger.info(f"✓ Created named selection: {selection_name}") else: results['selections_failed'].append(selection_name) logger.warning(f"✗ Failed to create named selection: {selection_name}") except Exception as e: logger.error(f"Error creating named selection {selection_name}: {str(e)}") results['selections_failed'].append(selection_name) results['total_selections'] = len(results['selections_created']) if results['selections_failed']: results['success'] = len(results['selections_created']) > 0 logger.warning(f"Some named selections failed: {results['selections_failed']}") logger.info(f"✓ Named selection creation completed: {results['total_selections']} created") return results except Exception as e: logger.error(f"Named selection creation failed: {str(e)}") return { 'success': False, 'error': str(e), 'selections_created': [], 'selections_failed': list(self.selection_criteria.keys()), 'total_selections': 0, 'created_at': datetime.now() } def _create_named_selection(self, name: str, description: str, method: str) -> bool: """ Create a single named selection using PyMechanical API Args: name: Name of the selection description: Description of the selection method: Method to use for selection (curvature_based, angle_based, etc.) Returns: True if creation successful, False otherwise """ try: # Create named selection script based on method if method == 'curvature_based': script = self._create_curvature_based_selection_script(name, description) elif method == 'angle_based': script = self._create_angle_based_selection_script(name, description) elif method == 'location_based': script = self._create_location_based_selection_script(name, description) elif method == 'surface_based': script = self._create_surface_based_selection_script(name, description) else: logger.error(f"Unknown selection method: {method}") return False # Execute the script result = self.mechanical.run_python_script(script) logger.debug(f"Named selection script result for {name}: {result}") # Store selection info self.created_selections[name] = { 'description': description, 'method': method, 'created_at': datetime.now(), 'script_result': result } return True except Exception as e: logger.error(f"Failed to create named selection {name}: {str(e)}") return False def _create_curvature_based_selection_script(self, name: str, description: str) -> str: """Create script for curvature-based selection (leading edge)""" return f''' # Create curvature-based named selection for {name} try: # Create named selection selection = Model.AddNamedSelection() selection.Name = "{name}" selection.ScopingMethod = GeometryDefineByType.Worksheet # Set up criteria for high curvature edges criteria = selection.GenerationCriteria criterion = Ansys.ACT.Automation.Mechanical.NamedSelectionCriterion() criterion.Active = True criterion.Action = SelectionActionType.Add criterion.EntityType = SelectionType.GeoEdge criterion.Criterion = SelectionCriterionType.Size criterion.Operator = SelectionOperatorType.LessThan criterion.Value = Quantity("5 [mm]") criteria.Add(criterion) selection.Generate() print("Created {name} named selection successfully") except Exception as e: print("Error creating {name} selection: " + str(e)) ''' def _create_angle_based_selection_script(self, name: str, description: str) -> str: """Create script for angle-based selection (trailing edge)""" return f''' # Create angle-based named selection for {name} try: # Create named selection selection = Model.AddNamedSelection() selection.Name = "{name}" selection.ScopingMethod = GeometryDefineByType.Worksheet # Set up criteria for sharp edges criteria = selection.GenerationCriteria criterion = Ansys.ACT.Automation.Mechanical.NamedSelectionCriterion() criterion.Active = True criterion.Action = SelectionActionType.Add criterion.EntityType = SelectionType.GeoEdge criterion.Criterion = SelectionCriterionType.Size criterion.Operator = SelectionOperatorType.LessThan criterion.Value = Quantity("3 [mm]") criteria.Add(criterion) selection.Generate() print("Created {name} named selection successfully") except Exception as e: print("Error creating {name} selection: " + str(e)) ''' def _create_location_based_selection_script(self, name: str, description: str) -> str: """Create script for location-based selection (blade root)""" return f''' # Create location-based named selection for {name} try: # Create named selection selection = Model.AddNamedSelection() selection.Name = "{name}" selection.ScopingMethod = GeometryDefineByType.Worksheet # Set up criteria for surfaces at specific location (bottom/root) criteria = selection.GenerationCriteria criterion = Ansys.ACT.Automation.Mechanical.NamedSelectionCriterion() criterion.Active = True criterion.Action = SelectionActionType.Add criterion.EntityType = SelectionType.GeoFace criterion.Criterion = SelectionCriterionType.LocationZ criterion.Operator = SelectionOperatorType.LessThan criterion.Value = Quantity("-100 [mm]") criteria.Add(criterion) selection.Generate() print("Created {name} named selection successfully") except Exception as e: print("Error creating {name} selection: " + str(e)) ''' def _create_surface_based_selection_script(self, name: str, description: str) -> str: """Create script for surface-based selection (blade surfaces)""" return f''' # Create surface-based named selection for {name} try: # Create named selection selection = Model.AddNamedSelection() selection.Name = "{name}" selection.ScopingMethod = GeometryDefineByType.Worksheet # Set up criteria for all external surfaces criteria = selection.GenerationCriteria criterion = Ansys.ACT.Automation.Mechanical.NamedSelectionCriterion() criterion.Active = True criterion.Action = SelectionActionType.Add criterion.EntityType = SelectionType.GeoFace criterion.Criterion = SelectionCriterionType.Size criterion.Operator = SelectionOperatorType.GreaterThan criterion.Value = Quantity("1 [mm^2]") criteria.Add(criterion) selection.Generate() print("Created {name} named selection successfully") except Exception as e: print("Error creating {name} selection: " + str(e)) ''' def get_named_selections_info(self) -> Dict[str, Any]: """ Get information about created named selections Returns: Dictionary with named selection information """ try: # Get named selections from Mechanical info_script = ''' # Get named selection information selections_info = [] named_selections = Model.GetChildren(Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.NamedSelection, True) for selection in named_selections: try: info = { "name": selection.Name, "entity_count": len(selection.Ids) if hasattr(selection, 'Ids') else 0, "scoping_method": str(selection.ScopingMethod) if hasattr(selection, 'ScopingMethod') else "Unknown" } selections_info.append(info) print("Selection: " + selection.Name + ", Entities: " + str(info["entity_count"])) except Exception as e: print("Error getting info for selection: " + str(e)) print("Total named selections: " + str(len(selections_info))) ''' result = self.mechanical.run_python_script(info_script) logger.info(f"Named selections info: {result}") return { 'success': True, 'created_selections': dict(self.created_selections), 'script_result': result, 'total_count': len(self.created_selections) } except Exception as e: logger.error(f"Failed to get named selections info: {str(e)}") return { 'success': False, 'error': str(e), 'created_selections': dict(self.created_selections), 'total_count': len(self.created_selections) } def validate_named_selections(self) -> Dict[str, Any]: """ Validate that named selections were created successfully Returns: Dictionary with validation results """ try: logger.info("Validating named selections...") validation_script = ''' # Validate named selections validation_results = {} expected_selections = ["leading_edge", "trailing_edge", "blade_root", "blade_surfaces"] named_selections = Model.GetChildren(Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.NamedSelection, True) existing_names = [sel.Name for sel in named_selections] for expected in expected_selections: if expected in existing_names: # Find the selection and get its info selection = next((sel for sel in named_selections if sel.Name == expected), None) if selection: entity_count = len(selection.Ids) if hasattr(selection, 'Ids') else 0 validation_results[expected] = { "exists": True, "entity_count": entity_count, "valid": entity_count > 0 } print("✓ " + expected + ": " + str(entity_count) + " entities") else: validation_results[expected] = {"exists": False, "entity_count": 0, "valid": False} print("✗ " + expected + ": Not found") else: validation_results[expected] = {"exists": False, "entity_count": 0, "valid": False} print("✗ " + expected + ": Not found") total_valid = sum(1 for result in validation_results.values() if result["valid"]) print("Validation complete: " + str(total_valid) + "/" + str(len(expected_selections)) + " selections valid") ''' result = self.mechanical.run_python_script(validation_script) logger.info(f"Validation result: {result}") return { 'success': True, 'validation_result': result, 'validated_at': datetime.now() } except Exception as e: logger.error(f"Named selection validation failed: {str(e)}") return { 'success': False, 'error': str(e), 'validated_at': datetime.now() } def clear_named_selections(self) -> bool: """ Clear all created named selections Returns: True if successful, False otherwise """ try: logger.info("Clearing named selections...") clear_script = ''' # Clear all named selections named_selections = Model.GetChildren(Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.NamedSelection, True) cleared_count = 0 for selection in named_selections: try: selection.Delete() cleared_count += 1 print("Deleted selection: " + selection.Name) except Exception as e: print("Error deleting selection: " + str(e)) print("Cleared " + str(cleared_count) + " named selections") ''' result = self.mechanical.run_python_script(clear_script) logger.info(f"Clear result: {result}") # Clear local tracking self.created_selections.clear() return True except Exception as e: logger.error(f"Failed to clear named selections: {str(e)}") return False