""" 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 }