AnsysLink/backend/pymechanical/mesh_file_exporter.py
2025-08-11 13:58:59 +08:00

492 lines
17 KiB
Python

"""
Real Mesh File Exporter for CAE Mesh Generator
This module handles exporting mesh data from ANSYS Mechanical to various formats
using PyMechanical API for real ANSYS integration.
"""
import os
import logging
from pathlib import Path
from typing import Dict, List, Any, Optional
from datetime import datetime
from enum import Enum
logger = logging.getLogger(__name__)
class MeshExportFormat(Enum):
"""Supported mesh export formats"""
ANSYS_CDB = "cdb" # ANSYS database format
ANSYS_MSH = "msh" # ANSYS mesh format
NASTRAN_BDF = "bdf" # Nastran bulk data format
ABAQUS_INP = "inp" # Abaqus input format
GENERIC_UNV = "unv" # Universal format
class MeshExportResult:
"""Result container for mesh export operations"""
def __init__(self):
self.success = False
self.exported_files = {} # format -> file_path
self.file_sizes = {} # format -> file_size_bytes
self.export_time = 0.0
self.error_message = None
self.warnings = []
self.mesh_info = {} # element_count, node_count, etc.
self.exported_at = None
class RealMeshFileExporter:
"""
Real mesh file exporter using PyMechanical API
This class provides functionality to export mesh data from ANSYS Mechanical
to various standard formats for use in other CAE software.
"""
def __init__(self, mechanical_session, output_dir: str = "exports"):
"""
Initialize mesh file exporter
Args:
mechanical_session: Active PyMechanical session
output_dir: Directory for exported files
"""
if mechanical_session is None:
raise ValueError("Mechanical session is required for mesh file export")
self.mechanical = mechanical_session
self.output_dir = Path(output_dir)
self.output_dir.mkdir(exist_ok=True)
logger.info(f"Real Mesh File Exporter initialized, output dir: {self.output_dir}")
def export_mesh_files(self,
formats: List[MeshExportFormat] = None,
filename_prefix: str = "mesh") -> MeshExportResult:
"""
Export mesh to multiple formats
Args:
formats: List of formats to export (default: CDB and MSH)
filename_prefix: Prefix for exported filenames
Returns:
MeshExportResult with export results
"""
if formats is None:
formats = [MeshExportFormat.ANSYS_CDB, MeshExportFormat.ANSYS_MSH]
result = MeshExportResult()
result.exported_at = datetime.now()
start_time = datetime.now()
try:
logger.info(f"Starting mesh export to {len(formats)} formats")
# First, verify mesh exists
mesh_info = self._get_mesh_info()
if not mesh_info.get('has_mesh', False):
result.error_message = "No mesh found to export"
return result
result.mesh_info = mesh_info
# Export each requested format
for format_type in formats:
try:
export_result = self._export_single_format(format_type, filename_prefix)
if export_result['success']:
result.exported_files[format_type.value] = export_result['file_path']
result.file_sizes[format_type.value] = export_result['file_size']
logger.info(f"✓ Exported {format_type.value}: {export_result['file_path']}")
else:
result.warnings.append(f"Failed to export {format_type.value}: {export_result['error']}")
logger.warning(f"✗ Export failed for {format_type.value}: {export_result['error']}")
except Exception as format_error:
error_msg = f"Export error for {format_type.value}: {str(format_error)}"
result.warnings.append(error_msg)
logger.error(error_msg)
# Calculate total export time
result.export_time = (datetime.now() - start_time).total_seconds()
# Determine overall success
result.success = len(result.exported_files) > 0
if result.success:
logger.info(f"✓ Mesh export completed: {len(result.exported_files)}/{len(formats)} formats exported")
else:
result.error_message = "No formats were successfully exported"
logger.error("✗ Mesh export failed: no formats exported successfully")
return result
except Exception as e:
logger.error(f"Mesh export failed: {str(e)}")
result.success = False
result.error_message = str(e)
result.export_time = (datetime.now() - start_time).total_seconds()
return result
def _get_mesh_info(self) -> Dict[str, Any]:
"""
Get mesh information from ANSYS
Returns:
Dictionary with mesh information
"""
try:
mesh_info_script = '''
# Get mesh information for export validation
try:
mesh = Model.Mesh
# Check if mesh exists
has_mesh = False
element_count = 0
node_count = 0
try:
# Try to get mesh statistics
if hasattr(mesh, 'Elements') and mesh.Elements is not None:
if hasattr(mesh.Elements, 'Count'):
element_count = mesh.Elements.Count
elif hasattr(mesh.Elements, '__len__'):
element_count = len(mesh.Elements)
if hasattr(mesh, 'Nodes') and mesh.Nodes is not None:
if hasattr(mesh.Nodes, 'Count'):
node_count = mesh.Nodes.Count
elif hasattr(mesh.Nodes, '__len__'):
node_count = len(mesh.Nodes)
has_mesh = element_count > 0 and node_count > 0
print("MESH_INFO_START")
print("HAS_MESH:" + str(has_mesh))
print("ELEMENT_COUNT:" + str(element_count))
print("NODE_COUNT:" + str(node_count))
print("MESH_INFO_END")
except Exception as e:
print("ERROR_GETTING_MESH_INFO:" + str(e))
except Exception as e:
print("SCRIPT_ERROR:" + str(e))
'''
result = self.mechanical.run_python_script(mesh_info_script)
logger.debug(f"Mesh info script result: {result}")
# Parse the result
mesh_info = {
'has_mesh': False,
'element_count': 0,
'node_count': 0,
'error': None
}
if result:
lines = str(result).split('\n')
for line in lines:
if line.startswith('HAS_MESH:'):
mesh_info['has_mesh'] = line.split(':')[1].strip().lower() == 'true'
elif line.startswith('ELEMENT_COUNT:'):
try:
mesh_info['element_count'] = int(line.split(':')[1].strip())
except:
pass
elif line.startswith('NODE_COUNT:'):
try:
mesh_info['node_count'] = int(line.split(':')[1].strip())
except:
pass
elif line.startswith('ERROR_GETTING_MESH_INFO:'):
mesh_info['error'] = line.split(':', 1)[1].strip()
return mesh_info
except Exception as e:
logger.error(f"Failed to get mesh info: {str(e)}")
return {
'has_mesh': False,
'element_count': 0,
'node_count': 0,
'error': str(e)
}
def _export_single_format(self, format_type: MeshExportFormat, filename_prefix: str) -> Dict[str, Any]:
"""
Export mesh to a single format
Args:
format_type: Format to export
filename_prefix: Filename prefix
Returns:
Dictionary with export result
"""
try:
# Generate filename
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{filename_prefix}_{timestamp}.{format_type.value}"
output_path = self.output_dir / filename
logger.info(f"Exporting mesh to {format_type.value} format: {filename}")
# Create format-specific export script
export_script = self._create_export_script(format_type, str(output_path))
# Execute export
result = self.mechanical.run_python_script(export_script)
logger.debug(f"Export script result for {format_type.value}: {result}")
# Verify export success
if output_path.exists():
file_size = output_path.stat().st_size
if file_size > 0:
return {
'success': True,
'file_path': str(output_path),
'file_size': file_size,
'format': format_type.value
}
else:
return {
'success': False,
'error': f"Exported file is empty: {output_path}",
'file_path': str(output_path)
}
else:
return {
'success': False,
'error': f"Export file not created: {output_path}",
'script_result': result
}
except Exception as e:
logger.error(f"Single format export failed for {format_type.value}: {str(e)}")
return {
'success': False,
'error': str(e)
}
def _create_export_script(self, format_type: MeshExportFormat, output_path: str) -> str:
"""
Create PyMechanical script for specific export format
Args:
format_type: Export format
output_path: Output file path
Returns:
PyMechanical script string
"""
# Convert Windows path separators for PyMechanical
safe_path = output_path.replace('\\', '/')
if format_type == MeshExportFormat.ANSYS_CDB:
return f'''
# Export mesh to ANSYS CDB format
try:
mesh = Model.Mesh
# Method 1: Try direct CDB export
try:
# Set export format to ANSYS
mesh.ExportFormat = MeshExportFormat.ANSYS
mesh.ExportSettings.Path = r"{safe_path}"
mesh.Export()
print("CDB_EXPORT_SUCCESS")
except Exception as method1_error:
print("Method 1 failed: " + str(method1_error))
# Method 2: Try alternative CDB export
try:
# Alternative approach using file export
ExtAPI.DataModel.Project.Model.Mesh.ExportFormat = MeshExportFormat.ANSYS
ExtAPI.DataModel.Project.Model.Mesh.ExportSettings.Path = r"{safe_path}"
ExtAPI.DataModel.Project.Model.Mesh.Export()
print("CDB_EXPORT_SUCCESS_ALT")
except Exception as method2_error:
print("Method 2 failed: " + str(method2_error))
print("CDB_EXPORT_FAILED")
except Exception as e:
print("CDB_EXPORT_ERROR: " + str(e))
'''
elif format_type == MeshExportFormat.ANSYS_MSH:
return f'''
# Export mesh to ANSYS MSH format
try:
mesh = Model.Mesh
# Try MSH export
try:
# Set export format to MSH
mesh.ExportFormat = MeshExportFormat.MSH
mesh.ExportSettings.Path = r"{safe_path}"
mesh.Export()
print("MSH_EXPORT_SUCCESS")
except Exception as msh_error:
print("MSH export failed: " + str(msh_error))
# Alternative: try generic mesh export
try:
ExtAPI.DataModel.Project.Model.Mesh.ExportFormat = MeshExportFormat.MSH
ExtAPI.DataModel.Project.Model.Mesh.ExportSettings.Path = r"{safe_path}"
ExtAPI.DataModel.Project.Model.Mesh.Export()
print("MSH_EXPORT_SUCCESS_ALT")
except Exception as alt_error:
print("MSH alternative failed: " + str(alt_error))
print("MSH_EXPORT_FAILED")
except Exception as e:
print("MSH_EXPORT_ERROR: " + str(e))
'''
elif format_type == MeshExportFormat.NASTRAN_BDF:
return f'''
# Export mesh to Nastran BDF format
try:
mesh = Model.Mesh
try:
# Set export format to Nastran
mesh.ExportFormat = MeshExportFormat.Nastran
mesh.ExportSettings.Path = r"{safe_path}"
mesh.Export()
print("BDF_EXPORT_SUCCESS")
except Exception as bdf_error:
print("BDF export failed: " + str(bdf_error))
print("BDF_EXPORT_FAILED")
except Exception as e:
print("BDF_EXPORT_ERROR: " + str(e))
'''
elif format_type == MeshExportFormat.ABAQUS_INP:
return f'''
# Export mesh to Abaqus INP format
try:
mesh = Model.Mesh
try:
# Set export format to Abaqus
mesh.ExportFormat = MeshExportFormat.Abaqus
mesh.ExportSettings.Path = r"{safe_path}"
mesh.Export()
print("INP_EXPORT_SUCCESS")
except Exception as inp_error:
print("INP export failed: " + str(inp_error))
print("INP_EXPORT_FAILED")
except Exception as e:
print("INP_EXPORT_ERROR: " + str(e))
'''
elif format_type == MeshExportFormat.GENERIC_UNV:
return f'''
# Export mesh to Universal UNV format
try:
mesh = Model.Mesh
try:
# Set export format to Universal
mesh.ExportFormat = MeshExportFormat.Universal
mesh.ExportSettings.Path = r"{safe_path}"
mesh.Export()
print("UNV_EXPORT_SUCCESS")
except Exception as unv_error:
print("UNV export failed: " + str(unv_error))
print("UNV_EXPORT_FAILED")
except Exception as e:
print("UNV_EXPORT_ERROR: " + str(e))
'''
else:
return f'''
# Unsupported export format: {format_type.value}
print("UNSUPPORTED_FORMAT: {format_type.value}")
'''
def get_supported_formats(self) -> List[Dict[str, str]]:
"""
Get list of supported export formats
Returns:
List of format information dictionaries
"""
return [
{
'format': MeshExportFormat.ANSYS_CDB.value,
'name': 'ANSYS Database',
'description': 'ANSYS native database format (.cdb)',
'extension': '.cdb'
},
{
'format': MeshExportFormat.ANSYS_MSH.value,
'name': 'ANSYS Mesh',
'description': 'ANSYS mesh format (.msh)',
'extension': '.msh'
},
{
'format': MeshExportFormat.NASTRAN_BDF.value,
'name': 'Nastran Bulk Data',
'description': 'Nastran bulk data format (.bdf)',
'extension': '.bdf'
},
{
'format': MeshExportFormat.ABAQUS_INP.value,
'name': 'Abaqus Input',
'description': 'Abaqus input format (.inp)',
'extension': '.inp'
},
{
'format': MeshExportFormat.GENERIC_UNV.value,
'name': 'Universal Format',
'description': 'Universal mesh format (.unv)',
'extension': '.unv'
}
]
def export_single_format(self, format_type: MeshExportFormat, filename: str = None) -> MeshExportResult:
"""
Export mesh to a single specific format
Args:
format_type: Format to export
filename: Custom filename (optional)
Returns:
MeshExportResult with export results
"""
if filename is None:
filename = f"mesh_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
return self.export_mesh_files([format_type], filename)
def get_export_summary(self) -> Dict[str, Any]:
"""
Get summary of exporter capabilities
Returns:
Dictionary with exporter information
"""
return {
'exporter_type': 'RealMeshFileExporter',
'output_directory': str(self.output_dir),
'supported_formats': self.get_supported_formats(),
'total_formats': len(MeshExportFormat),
'mechanical_session_active': self.mechanical is not None
}