492 lines
17 KiB
Python
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
|
|
} |