414 lines
15 KiB
Python
414 lines
15 KiB
Python
"""
|
|
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 |