diff --git a/LICENSE b/LICENSE index f2879c11..2100d39a 100644 --- a/LICENSE +++ b/LICENSE @@ -9,4 +9,4 @@ property of Atomaste. Unauthorized copying, modification, distribution, or use o Software, via any medium, is strictly prohibited without prior written permission from Atomaste. -For licensing inquiries, please contact: contact@atomaste.com +For licensing inquiries, please contact: antoine@atomaste.ca diff --git a/examples/bracket/optimization_config.json b/examples/bracket/optimization_config.json new file mode 100644 index 00000000..5d8bcc58 --- /dev/null +++ b/examples/bracket/optimization_config.json @@ -0,0 +1,180 @@ +{ + "design_variables": [ + { + "name": "tip_thickness", + "type": "continuous", + "bounds": [ + 15.0, + 25.0 + ], + "units": "mm", + "initial_value": 20.0 + }, + { + "name": "support_angle", + "type": "continuous", + "bounds": [ + 20.0, + 40.0 + ], + "units": "degrees", + "initial_value": 30.0 + } + ], + "objectives": [ + { + "name": "minimize_mass", + "description": "Minimize total mass (weight reduction)", + "extractor": "mass_extractor", + "metric": "total_mass", + "direction": "minimize", + "weight": 5.0 + }, + { + "name": "minimize_max_stress", + "description": "Minimize maximum von Mises stress", + "extractor": "stress_extractor", + "metric": "max_von_mises", + "direction": "minimize", + "weight": 10.0 + } + ], + "constraints": [ + { + "name": "max_displacement_limit", + "description": "Maximum allowable displacement", + "extractor": "displacement_extractor", + "metric": "max_displacement", + "type": "upper_bound", + "limit": 1.0, + "units": "mm" + }, + { + "name": "max_stress_limit", + "description": "Maximum allowable von Mises stress", + "extractor": "stress_extractor", + "metric": "max_von_mises", + "type": "upper_bound", + "limit": 200.0, + "units": "MPa" + } + ], + "optimization_settings": { + "n_trials": 150, + "sampler": "TPE", + "n_startup_trials": 20 + }, + "model_info": { + "sim_file": "C:/Users/antoi/Documents/Atomaste/Atomizer/examples/bracket/Bracket_sim1.sim", + "solutions": [ + { + "name": "Direct Frequency Response", + "type": "Direct Frequency Response", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Disable in Thermal Solution 2D", + "type": "Disable in Thermal Solution 2D", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Nonlinear Statics", + "type": "Nonlinear Statics", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Linear Statics", + "type": "Linear Statics", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "*Thermal-Flow Coupled Solution Parameters", + "type": "*Thermal-Flow Coupled Solution Parameters", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Thermal Solution Parameters", + "type": "Thermal Solution Parameters", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Disable in Thermal Solution 3D", + "type": "Disable in Thermal Solution 3D", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Modal Frequency Response", + "type": "Modal Frequency Response", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Direct Transient Response", + "type": "Direct Transient Response", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "-Flow-Structural Coupled Solution Parameters", + "type": "-Flow-Structural Coupled Solution Parameters", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Normal Modes", + "type": "Normal Modes", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Modal Transient Response", + "type": "Modal Transient Response", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "\"ObjectDisableInThermalSolution3D", + "type": "\"ObjectDisableInThermalSolution3D", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "1Pass Structural Contact Solution to Flow Solver", + "type": "1Pass Structural Contact Solution to Flow Solver", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "0Thermal-Structural Coupled Solution Parameters", + "type": "0Thermal-Structural Coupled Solution Parameters", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Design Optimization", + "type": "Design Optimization", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "DisableInThermalSolution", + "type": "DisableInThermalSolution", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "\"ObjectDisableInThermalSolution2D", + "type": "\"ObjectDisableInThermalSolution2D", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + } + ] + } +} \ No newline at end of file diff --git a/examples/test_bracket.sim b/examples/test_bracket.sim new file mode 100644 index 00000000..def626ce --- /dev/null +++ b/examples/test_bracket.sim @@ -0,0 +1,80 @@ + + + + + + test_bracket + Simple bracket structural analysis + NX 2412 + 2025-11-15 + + + + + + Linear static analysis under load + + 101 + Direct + + + + + + + + 5.0 + Dimension + + + 10.0 + Dimension + + + 40.0 + Dimension + + + 2.7 + Material Property + + + + + + + + + + + + + + + + + + + + + + + + Top Face + 0 -1 0 + + + + + + Bottom Holes + + + + + + + test_bracket.prt + test_bracket.fem + + diff --git a/mcp_server/tools/README.md b/mcp_server/tools/README.md new file mode 100644 index 00000000..18967d5e --- /dev/null +++ b/mcp_server/tools/README.md @@ -0,0 +1,281 @@ +# MCP Tools Documentation + +This directory contains the MCP (Model Context Protocol) tools that enable LLM-driven optimization configuration for Atomizer. + +## Available Tools + +### 1. Model Discovery (`model_discovery.py`) ✅ IMPLEMENTED + +**Purpose**: Parse Siemens NX .sim files to extract FEA model information. + +**Function**: `discover_fea_model(sim_file_path: str) -> Dict[str, Any]` + +**What it extracts**: +- **Solutions**: Analysis types (static, thermal, modal, etc.) +- **Expressions**: Parametric variables that can be optimized +- **FEM Info**: Mesh, materials, loads, constraints +- **Linked Files**: Associated .prt files and result files + +**Usage Example**: +```python +from mcp_server.tools import discover_fea_model, format_discovery_result_for_llm + +# Discover model +result = discover_fea_model("C:/Projects/Bracket/analysis.sim") + +# Format for LLM +if result['status'] == 'success': + markdown_output = format_discovery_result_for_llm(result) + print(markdown_output) + +# Access structured data +for expr in result['expressions']: + print(f"{expr['name']}: {expr['value']} {expr['units']}") +``` + +**Command Line Usage**: +```bash +python mcp_server/tools/model_discovery.py examples/test_bracket.sim +``` + +**Output Format**: +- **JSON**: Complete structured data for programmatic use +- **Markdown**: Human-readable format for LLM consumption + +**Supported .sim File Versions**: +- NX 2412 (tested) +- Should work with NX 12.0+ (XML-based .sim files) + +**Limitations**: +- Expression values are best-effort extracted from .sim XML +- For accurate values, the associated .prt file is parsed (binary parsing) +- Binary .prt parsing is heuristic-based and may miss some expressions + +--- + +### 2. Build Optimization Config (`optimization_config.py`) ✅ IMPLEMENTED + +**Purpose**: Generate `optimization_config.json` from user selections of objectives, constraints, and design variables. + +**Functions**: +- `build_optimization_config(...)` - Create complete optimization configuration +- `list_optimization_options(sim_file_path)` - List all available options for a model +- `format_optimization_options_for_llm(options)` - Format options as Markdown + +**What it does**: +- Discovers available design variables from the FEA model +- Lists available objectives (minimize mass, stress, displacement, volume) +- Lists available constraints (max stress, max displacement, mass limits) +- Builds a complete `optimization_config.json` based on user selections +- Validates that all selections are valid for the model + +**Usage Example**: +```python +from mcp_server.tools import build_optimization_config, list_optimization_options + +# Step 1: List available options +options = list_optimization_options("examples/bracket/Bracket_sim1.sim") +print(f"Available design variables: {len(options['available_design_variables'])}") + +# Step 2: Build configuration +result = build_optimization_config( + sim_file_path="examples/bracket/Bracket_sim1.sim", + design_variables=[ + {'name': 'tip_thickness', 'lower_bound': 15.0, 'upper_bound': 25.0}, + {'name': 'support_angle', 'lower_bound': 20.0, 'upper_bound': 40.0} + ], + objectives=[ + {'objective_key': 'minimize_mass', 'weight': 5.0}, + {'objective_key': 'minimize_max_stress', 'weight': 10.0} + ], + constraints=[ + {'constraint_key': 'max_displacement_limit', 'limit_value': 1.0}, + {'constraint_key': 'max_stress_limit', 'limit_value': 200.0} + ], + optimization_settings={ + 'n_trials': 150, + 'sampler': 'TPE' + } +) + +if result['status'] == 'success': + print(f"Config saved to: {result['config_file']}") +``` + +**Command Line Usage**: +```bash +python mcp_server/tools/optimization_config.py examples/bracket/Bracket_sim1.sim +``` + +**Available Objectives**: +- `minimize_mass`: Minimize total mass (weight reduction) +- `minimize_max_stress`: Minimize maximum von Mises stress +- `minimize_max_displacement`: Minimize maximum displacement (increase stiffness) +- `minimize_volume`: Minimize total volume (material usage) + +**Available Constraints**: +- `max_stress_limit`: Maximum allowable von Mises stress +- `max_displacement_limit`: Maximum allowable displacement +- `min_mass_limit`: Minimum required mass (structural integrity) +- `max_mass_limit`: Maximum allowable mass (weight budget) + +**Output**: Creates `optimization_config.json` with: +- Design variable definitions with bounds +- Multi-objective configuration with weights +- Constraint definitions with limits +- Optimization algorithm settings (trials, sampler) + +--- + +### 3. Start Optimization (PLANNED) + +**Purpose**: Launch optimization run with given configuration. + +**Function**: `start_optimization(config_path: str, resume: bool = False) -> Dict[str, Any]` + +--- + +### 4. Query Optimization Status (PLANNED) + +**Purpose**: Get current status of running optimization. + +**Function**: `query_optimization_status(session_id: str) -> Dict[str, Any]` + +--- + +### 5. Extract Results (PLANNED) + +**Purpose**: Parse FEA result files (OP2, F06, XDB) for optimization metrics. + +**Function**: `extract_results(result_files: List[str], extractors: List[str]) -> Dict[str, Any]` + +--- + +### 6. Run NX Journal (PLANNED) + +**Purpose**: Execute NXOpen scripts via file-based communication. + +**Function**: `run_nx_journal(journal_script: str, parameters: Dict) -> Dict[str, Any]` + +--- + +## Testing + +### Unit Tests + +```bash +# Install pytest (if not already installed) +pip install pytest + +# Run all MCP tool tests +pytest tests/mcp_server/tools/ -v + +# Run specific test +pytest tests/mcp_server/tools/test_model_discovery.py -v +``` + +### Example Files + +Example .sim files for testing are located in `examples/`: +- `test_bracket.sim`: Simple structural analysis with 4 expressions + +--- + +## Development Guidelines + +### Adding a New Tool + +1. **Create module**: `mcp_server/tools/your_tool.py` + +2. **Implement function**: +```python +def your_tool_name(param: str) -> Dict[str, Any]: + """ + Brief description. + + Args: + param: Description + + Returns: + Structured result dictionary + """ + try: + # Implementation + return { + 'status': 'success', + 'data': result + } + except Exception as e: + return { + 'status': 'error', + 'error_type': 'error_category', + 'message': str(e), + 'suggestion': 'How to fix' + } +``` + +3. **Add to `__init__.py`**: +```python +from .your_tool import your_tool_name + +__all__ = [ + # ... existing tools + "your_tool_name", +] +``` + +4. **Create tests**: `tests/mcp_server/tools/test_your_tool.py` + +5. **Update documentation**: Add section to this README + +--- + +## Error Handling + +All MCP tools follow a consistent error handling pattern: + +**Success Response**: +```json +{ + "status": "success", + "data": { ... } +} +``` + +**Error Response**: +```json +{ + "status": "error", + "error_type": "file_not_found | invalid_file | unexpected_error", + "message": "Detailed error message", + "suggestion": "Actionable suggestion for user" +} +``` + +--- + +## Integration with MCP Server + +These tools are designed to be called by the MCP server and consumed by LLMs. The workflow is: + +1. **LLM Request**: "Analyze my FEA model at C:/Projects/model.sim" +2. **MCP Server**: Calls `discover_fea_model()` +3. **Tool Returns**: Structured JSON result +4. **MCP Server**: Formats with `format_discovery_result_for_llm()` +5. **LLM Response**: Uses formatted data to answer user + +--- + +## Future Enhancements + +- [ ] Support for binary .sim file formats (older NX versions) +- [ ] Direct NXOpen integration for accurate expression extraction +- [ ] Support for additional analysis types (thermal, modal, etc.) +- [ ] Caching of parsed results for performance +- [ ] Validation of .sim file integrity +- [ ] Extraction of solver convergence settings + +--- + +**Last Updated**: 2025-11-15 +**Status**: Phase 1 (Model Discovery) ✅ COMPLETE | Phase 2 (Optimization Config Builder) ✅ COMPLETE diff --git a/mcp_server/tools/__init__.py b/mcp_server/tools/__init__.py index 80d0267f..6a8b5792 100644 --- a/mcp_server/tools/__init__.py +++ b/mcp_server/tools/__init__.py @@ -12,10 +12,19 @@ Available tools: """ from typing import Dict, Any +from .model_discovery import discover_fea_model, format_discovery_result_for_llm +from .optimization_config import ( + build_optimization_config, + list_optimization_options, + format_optimization_options_for_llm +) __all__ = [ "discover_fea_model", + "format_discovery_result_for_llm", "build_optimization_config", + "list_optimization_options", + "format_optimization_options_for_llm", "start_optimization", "query_optimization_status", "extract_results", diff --git a/mcp_server/tools/model_discovery.py b/mcp_server/tools/model_discovery.py new file mode 100644 index 00000000..9425c779 --- /dev/null +++ b/mcp_server/tools/model_discovery.py @@ -0,0 +1,621 @@ +""" +MCP Tool: FEA Model Discovery + +Parses Siemens NX .sim files to extract: +- Simulation solutions (structural, thermal, modal, etc.) +- Parametric expressions (design variables) +- FEM information (mesh, elements, materials) +- Linked part files + +This tool enables LLM-driven optimization configuration by providing +structured information about what can be optimized in a given FEA model. +""" + +import xml.etree.ElementTree as ET +from pathlib import Path +from typing import Dict, Any, List, Optional +import json +import re + + +class SimFileParser: + """ + Parser for Siemens NX .sim (simulation) files. + + IMPORTANT: Real NX .sim files are BINARY (not XML) in NX 12+. + The parser uses two approaches: + 1. XML parsing for test/legacy files + 2. Binary string extraction for real NX files + + .sim files contain references to: + - Parent .prt file (geometry and expressions) + - Solution definitions (structural, thermal, etc.) + - FEM (mesh, materials, loads, constraints) + - Solver settings + """ + + def __init__(self, sim_path: Path): + """ + Initialize parser with path to .sim file. + + Args: + sim_path: Absolute path to .sim file + + Raises: + FileNotFoundError: If sim file doesn't exist + ValueError: If file is not a valid .sim file + """ + self.sim_path = Path(sim_path) + + if not self.sim_path.exists(): + raise FileNotFoundError(f"Sim file not found: {sim_path}") + + if self.sim_path.suffix.lower() != '.sim': + raise ValueError(f"Not a .sim file: {sim_path}") + + self.tree = None + self.root = None + self.is_binary = False + self.sim_strings = [] # Extracted strings from binary file + self._parse_file() + + def _parse_file(self): + """ + Parse the .sim file - handles both XML (test files) and binary (real NX files). + """ + # First, try XML parsing + try: + self.tree = ET.parse(self.sim_path) + self.root = self.tree.getroot() + self.is_binary = False + return + except ET.ParseError: + # Not XML, must be binary - this is normal for real NX files + pass + + # Binary file - extract readable strings + try: + with open(self.sim_path, 'rb') as f: + content = f.read() + + # Extract strings (sequences of printable ASCII characters) + # Minimum length of 4 to avoid noise + text_content = content.decode('latin-1', errors='ignore') + self.sim_strings = re.findall(r'[\x20-\x7E]{4,}', text_content) + self.is_binary = True + + except Exception as e: + raise ValueError(f"Failed to parse .sim file (tried both XML and binary): {e}") + + def extract_solutions(self) -> List[Dict[str, Any]]: + """ + Extract solution definitions from .sim file. + + Returns: + List of solution dictionaries with type, name, solver info + """ + solutions = [] + + if not self.is_binary and self.root is not None: + # XML parsing + for solution_tag in ['Solution', 'AnalysisSolution', 'SimSolution']: + for elem in self.root.iter(solution_tag): + solution_info = { + 'name': elem.get('name', 'Unknown'), + 'type': elem.get('type', 'Unknown'), + 'solver': elem.get('solver', 'NX Nastran'), + 'description': elem.get('description', ''), + } + solutions.append(solution_info) + else: + # Binary parsing - look for solution type indicators + solution_types = { + 'SOL 101': 'Linear Statics', + 'SOL 103': 'Normal Modes', + 'SOL 106': 'Nonlinear Statics', + 'SOL 108': 'Direct Frequency Response', + 'SOL 109': 'Direct Transient Response', + 'SOL 111': 'Modal Frequency Response', + 'SOL 112': 'Modal Transient Response', + 'SOL 200': 'Design Optimization', + } + + found_solutions = set() + for s in self.sim_strings: + for sol_id, sol_type in solution_types.items(): + if sol_id in s: + found_solutions.add(sol_type) + + # Also check for solution names in strings + for s in self.sim_strings: + if 'Solution' in s and len(s) < 50: + # Potential solution name + if any(word in s for word in ['Structural', 'Thermal', 'Modal', 'Static']): + found_solutions.add(s.strip()) + + for sol_name in found_solutions: + solutions.append({ + 'name': sol_name, + 'type': sol_name, + 'solver': 'NX Nastran', + 'description': 'Extracted from binary .sim file' + }) + + # Default if nothing found + if not solutions: + solutions.append({ + 'name': 'Default Solution', + 'type': 'Static Structural', + 'solver': 'NX Nastran', + 'description': 'Solution info could not be fully extracted from .sim file' + }) + + return solutions + + def extract_expressions(self) -> List[Dict[str, Any]]: + """ + Extract expression references from .sim file. + + Note: Actual expression values are stored in the .prt file. + This method extracts references and attempts to read from .prt if available. + + Returns: + List of expression dictionaries with name, value, units + """ + expressions = [] + + # XML parsing - look for expression elements + if not self.is_binary and self.root is not None: + for expr_elem in self.root.iter('Expression'): + expr_info = { + 'name': expr_elem.get('name', ''), + 'value': expr_elem.get('value', None), + 'units': expr_elem.get('units', ''), + 'formula': expr_elem.text if expr_elem.text else None + } + if expr_info['name']: + expressions.append(expr_info) + + # Try to read from associated .prt file (works for both XML and binary .sim) + # Try multiple naming patterns: + # 1. Same name as .sim: Bracket_sim1.prt + # 2. Base name: Bracket.prt + # 3. With _i suffix: Bracket_fem1_i.prt + prt_paths = [ + self.sim_path.with_suffix('.prt'), # Bracket_sim1.prt + self.sim_path.parent / f"{self.sim_path.stem.split('_')[0]}.prt", # Bracket.prt + self.sim_path.parent / f"{self.sim_path.stem}_i.prt", # Bracket_sim1_i.prt + ] + + for prt_path in prt_paths: + if prt_path.exists(): + prt_expressions = self._extract_prt_expressions(prt_path) + # Merge with existing, prioritizing .prt values + expr_dict = {e['name']: e for e in expressions} + for prt_expr in prt_expressions: + expr_dict[prt_expr['name']] = prt_expr + expressions = list(expr_dict.values()) + break # Use first .prt file found + + return expressions + + def _extract_prt_expressions(self, prt_path: Path) -> List[Dict[str, Any]]: + """ + Extract expressions from associated .prt file. + + .prt files are binary, but expression data is stored in readable sections. + NX expression format: #(Type [units]) name: value; + + Args: + prt_path: Path to .prt file + + Returns: + List of expression dictionaries + """ + expressions = [] + + try: + # Read as binary and search for text patterns + with open(prt_path, 'rb') as f: + content = f.read() + + # Try to decode as latin-1 (preserves all byte values) + text_content = content.decode('latin-1', errors='ignore') + + # Pattern 1: NX native format with variations: + # #(Number [mm]) tip_thickness: 20; + # (Number [mm]) p3: 10; + # *(Number [mm]) support_blend_radius: 10; + # ((Number [degrees]) support_angle: 30; + # Prefix can be: #(, *(, (, (( + nx_pattern = r'[#*\(]*\((\w+)\s*\[([^\]]*)\]\)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)' + + # Use set to avoid duplicates + expr_names_seen = set() + + for match in re.finditer(nx_pattern, text_content): + expr_type, units, name, value = match.groups() + if name not in expr_names_seen: + expr_names_seen.add(name) + expressions.append({ + 'name': name, + 'value': float(value), + 'units': units, + 'type': expr_type, + 'source': 'prt_file_nx_format' + }) + + # Pattern 2: Find expression names from Root: references + # Format: Root:expression_name: + root_pattern = r'Root:([a-zA-Z_][a-zA-Z0-9_]{2,}):' + potential_expr_names = set() + + for match in re.finditer(root_pattern, text_content): + name = match.group(1) + # Filter out common NX internal names + if name not in ['index', '%%Name', '%%ug_objects_for_', 'WorldModifier']: + if not name.startswith('%%'): + potential_expr_names.add(name) + + # For names found in Root: but not in value patterns, + # mark as "found but value unknown" + for name in potential_expr_names: + if name not in expr_names_seen: + expressions.append({ + 'name': name, + 'value': None, + 'units': '', + 'type': 'Unknown', + 'source': 'prt_file_reference_only' + }) + + # Pattern 3: Fallback - simple name=value pattern + # Only use if no NX-format expressions found + if not expressions: + simple_pattern = r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)' + + for match in re.finditer(simple_pattern, text_content): + name, value = match.groups() + # Filter out common false positives (short names, underscore-prefixed) + if len(name) > 3 and not name.startswith('_'): + # Additional filter: avoid Nastran keywords + if name.upper() not in ['PRINT', 'PUNCH', 'PLOT', 'BOTH', 'GRID', 'GAUSS']: + expressions.append({ + 'name': name, + 'value': float(value), + 'units': '', + 'source': 'prt_file_simple_pattern' + }) + + except Exception as e: + # .prt parsing is best-effort, don't fail if it doesn't work + print(f"Warning: Could not extract expressions from .prt file: {e}") + + return expressions + + def extract_fem_info(self) -> Dict[str, Any]: + """ + Extract FEM (finite element model) information. + + Returns: + Dictionary with mesh, material, and element info + """ + fem_info = { + 'mesh': {}, + 'materials': [], + 'element_types': [], + 'loads': [], + 'constraints': [] + } + + if not self.is_binary and self.root is not None: + # XML parsing + for mesh_elem in self.root.iter('Mesh'): + fem_info['mesh'] = { + 'name': mesh_elem.get('name', 'Default Mesh'), + 'element_size': mesh_elem.get('element_size', 'Unknown'), + 'node_count': mesh_elem.get('node_count', 'Unknown'), + 'element_count': mesh_elem.get('element_count', 'Unknown') + } + + for mat_elem in self.root.iter('Material'): + material = { + 'name': mat_elem.get('name', 'Unknown'), + 'type': mat_elem.get('type', 'Isotropic'), + 'properties': {} + } + for prop in ['youngs_modulus', 'poissons_ratio', 'density', 'yield_strength']: + if mat_elem.get(prop): + material['properties'][prop] = mat_elem.get(prop) + fem_info['materials'].append(material) + + for elem_type in self.root.iter('ElementType'): + fem_info['element_types'].append(elem_type.get('type', 'Unknown')) + + for load_elem in self.root.iter('Load'): + load = { + 'name': load_elem.get('name', 'Unknown'), + 'type': load_elem.get('type', 'Force'), + 'magnitude': load_elem.get('magnitude', 'Unknown') + } + fem_info['loads'].append(load) + + for constraint_elem in self.root.iter('Constraint'): + constraint = { + 'name': constraint_elem.get('name', 'Unknown'), + 'type': constraint_elem.get('type', 'Fixed'), + } + fem_info['constraints'].append(constraint) + + else: + # Binary parsing - extract from .fem file if available + fem_path = self.sim_path.with_name(self.sim_path.stem.replace('_sim', '_fem') + '.fem') + if not fem_path.exists(): + # Try alternative naming patterns + fem_path = self.sim_path.parent / f"{self.sim_path.stem.split('_')[0]}_fem1.fem" + + if fem_path.exists(): + fem_info = self._extract_fem_from_fem_file(fem_path) + else: + # Extract what we can from .sim strings + fem_info['note'] = 'Limited FEM info available from binary .sim file' + + return fem_info + + def _extract_fem_from_fem_file(self, fem_path: Path) -> Dict[str, Any]: + """ + Extract FEM information from .fem file. + + Args: + fem_path: Path to .fem file + + Returns: + Dictionary with FEM information + """ + fem_info = { + 'mesh': {}, + 'materials': [], + 'element_types': set(), + 'loads': [], + 'constraints': [] + } + + try: + with open(fem_path, 'rb') as f: + content = f.read() + text_content = content.decode('latin-1', errors='ignore') + + # Look for mesh metadata + mesh_match = re.search(r'Mesh\s+(\d+)', text_content) + if mesh_match: + fem_info['mesh']['name'] = f"Mesh {mesh_match.group(1)}" + + # Look for material names + for material_match in re.finditer(r'MAT\d+\s+([A-Za-z0-9_\-\s]+)', text_content): + mat_name = material_match.group(1).strip() + if mat_name and len(mat_name) > 2: + fem_info['materials'].append({ + 'name': mat_name, + 'type': 'Unknown', + 'properties': {} + }) + + # Look for element types (Nastran format: CQUAD4, CTRIA3, CTETRA, etc.) + element_pattern = r'\b(C[A-Z]{3,6}\d?)\b' + for elem_match in re.finditer(element_pattern, text_content): + elem_type = elem_match.group(1) + if elem_type.startswith('C') and len(elem_type) <= 8: + fem_info['element_types'].add(elem_type) + + fem_info['element_types'] = list(fem_info['element_types']) + + except Exception as e: + fem_info['note'] = f'Could not fully parse .fem file: {e}' + + return fem_info + + def get_linked_files(self) -> Dict[str, str]: + """ + Get paths to linked files (.prt, result files, etc.) + + Returns: + Dictionary mapping file type to path + """ + linked_files = {} + + # .prt file (geometry and expressions) + prt_path = self.sim_path.with_suffix('.prt') + if prt_path.exists(): + linked_files['part_file'] = str(prt_path) + + # Common result file locations + result_dir = self.sim_path.parent + sim_name = self.sim_path.stem + + # Nastran result files + for ext in ['.op2', '.f06', '.f04', '.bdf']: + result_file = result_dir / f"{sim_name}{ext}" + if result_file.exists(): + linked_files[f'result{ext}'] = str(result_file) + + return linked_files + + +def discover_fea_model(sim_file_path: str) -> Dict[str, Any]: + """ + MCP Tool: Discover FEA Model + + Analyzes a Siemens NX .sim file and extracts: + - Solutions (analysis types) + - Expressions (potential design variables) + - FEM information (mesh, materials, loads) + - Linked files + + This is the primary tool for LLM-driven optimization setup. + + Args: + sim_file_path: Absolute path to .sim file (Windows or Unix format) + + Returns: + Structured dictionary with model information + + Example: + >>> result = discover_fea_model("C:/Projects/Bracket/analysis.sim") + >>> print(result['expressions']) + [{'name': 'wall_thickness', 'value': 5.0, 'units': 'mm'}, ...] + """ + try: + # Normalize path (handle both Windows and Unix) + sim_path = Path(sim_file_path).resolve() + + # Parse the .sim file + parser = SimFileParser(sim_path) + + # Extract all components + result = { + 'status': 'success', + 'sim_file': str(sim_path), + 'file_exists': sim_path.exists(), + 'solutions': parser.extract_solutions(), + 'expressions': parser.extract_expressions(), + 'fem_info': parser.extract_fem_info(), + 'linked_files': parser.get_linked_files(), + 'metadata': { + 'parser_version': '0.1.0', + 'nx_version': 'NX 2412', # Can be extracted from .sim file in future + } + } + + # Add summary statistics + result['summary'] = { + 'solution_count': len(result['solutions']), + 'expression_count': len(result['expressions']), + 'material_count': len(result['fem_info']['materials']), + 'load_count': len(result['fem_info']['loads']), + 'constraint_count': len(result['fem_info']['constraints']), + } + + return result + + except FileNotFoundError as e: + return { + 'status': 'error', + 'error_type': 'file_not_found', + 'message': str(e), + 'suggestion': 'Check that the file path is absolute and the .sim file exists' + } + + except ValueError as e: + return { + 'status': 'error', + 'error_type': 'invalid_file', + 'message': str(e), + 'suggestion': 'Ensure the file is a valid NX .sim file (not corrupted or encrypted)' + } + + except Exception as e: + return { + 'status': 'error', + 'error_type': 'unexpected_error', + 'message': str(e), + 'suggestion': 'This may be an unsupported .sim file format. Please report this issue.' + } + + +def format_discovery_result_for_llm(result: Dict[str, Any]) -> str: + """ + Format discovery result for LLM consumption (Markdown). + + This is used by the MCP server to present results to the LLM + in a clear, structured format. + + Args: + result: Output from discover_fea_model() + + Returns: + Markdown-formatted string + """ + if result['status'] != 'success': + return f"❌ **Error**: {result['message']}\n\n💡 {result['suggestion']}" + + md = [] + md.append(f"# FEA Model Analysis\n") + md.append(f"**File**: `{result['sim_file']}`\n") + + # Solutions + md.append(f"## Solutions ({result['summary']['solution_count']})\n") + for sol in result['solutions']: + md.append(f"- **{sol['name']}** ({sol['type']}) - Solver: {sol['solver']}") + if sol['description']: + md.append(f" - {sol['description']}") + md.append("") + + # Expressions (Design Variables) + md.append(f"## Expressions ({result['summary']['expression_count']})\n") + if result['expressions']: + md.append("| Name | Value | Units |") + md.append("|------|-------|-------|") + for expr in result['expressions']: + value = expr.get('value', 'N/A') + units = expr.get('units', '') + md.append(f"| `{expr['name']}` | {value} | {units} |") + else: + md.append("⚠️ No expressions found. Model may not be parametric.") + md.append("") + + # FEM Information + fem = result['fem_info'] + md.append(f"## FEM Information\n") + + if fem['mesh']: + md.append(f"**Mesh**: {fem['mesh'].get('name', 'Unknown')}") + md.append(f"- Nodes: {fem['mesh'].get('node_count', 'Unknown')}") + md.append(f"- Elements: {fem['mesh'].get('element_count', 'Unknown')}") + md.append("") + + if fem['materials']: + md.append(f"**Materials** ({len(fem['materials'])})") + for mat in fem['materials']: + md.append(f"- {mat['name']} ({mat['type']})") + md.append("") + + if fem['loads']: + md.append(f"**Loads** ({len(fem['loads'])})") + for load in fem['loads']: + md.append(f"- {load['name']} ({load['type']})") + md.append("") + + if fem['constraints']: + md.append(f"**Constraints** ({len(fem['constraints'])})") + for constraint in fem['constraints']: + md.append(f"- {constraint['name']} ({constraint['type']})") + md.append("") + + # Linked Files + if result['linked_files']: + md.append(f"## Linked Files\n") + for file_type, file_path in result['linked_files'].items(): + md.append(f"- **{file_type}**: `{file_path}`") + md.append("") + + return "\n".join(md) + + +# For testing/debugging +if __name__ == "__main__": + import sys + + if len(sys.argv) < 2: + print("Usage: python model_discovery.py ") + sys.exit(1) + + sim_path = sys.argv[1] + result = discover_fea_model(sim_path) + + if result['status'] == 'success': + print(format_discovery_result_for_llm(result)) + print("\n" + "="*60) + print("JSON Output:") + print(json.dumps(result, indent=2)) + else: + print(f"Error: {result['message']}") diff --git a/mcp_server/tools/optimization_config.py b/mcp_server/tools/optimization_config.py new file mode 100644 index 00000000..3a70adce --- /dev/null +++ b/mcp_server/tools/optimization_config.py @@ -0,0 +1,368 @@ +""" +MCP Tool: Build Optimization Configuration + +Wraps the OptimizationConfigBuilder to create an MCP-compatible tool +that helps LLMs guide users through building optimization configurations. + +This tool: +1. Discovers the FEA model (design variables) +2. Lists available objectives and constraints +3. Builds a complete optimization_config.json based on user selections +""" + +from pathlib import Path +from typing import Dict, Any, List, Optional +import json +import sys + +# Add project root to path for imports +project_root = Path(__file__).parent.parent.parent +sys.path.insert(0, str(project_root)) + +from optimization_engine.optimization_config_builder import OptimizationConfigBuilder +from mcp_server.tools.model_discovery import discover_fea_model + + +def build_optimization_config( + sim_file_path: str, + design_variables: List[Dict[str, Any]], + objectives: List[Dict[str, Any]], + constraints: Optional[List[Dict[str, Any]]] = None, + optimization_settings: Optional[Dict[str, Any]] = None, + output_path: Optional[str] = None +) -> Dict[str, Any]: + """ + MCP Tool: Build Optimization Configuration + + Creates a complete optimization configuration file from user selections. + + Args: + sim_file_path: Absolute path to .sim file + design_variables: List of design variable definitions + [ + { + 'name': 'tip_thickness', + 'lower_bound': 15.0, + 'upper_bound': 25.0 + }, + ... + ] + objectives: List of objective definitions + [ + { + 'objective_key': 'minimize_mass', + 'weight': 5.0, # optional + 'target': None # optional, for goal programming + }, + ... + ] + constraints: Optional list of constraint definitions + [ + { + 'constraint_key': 'max_stress_limit', + 'limit_value': 200.0 + }, + ... + ] + optimization_settings: Optional dict with algorithm settings + { + 'n_trials': 100, + 'sampler': 'TPE' + } + output_path: Optional path to save config JSON. + Defaults to 'optimization_config.json' in sim file directory + + Returns: + Dictionary with status and configuration details + + Example: + >>> result = build_optimization_config( + ... sim_file_path="C:/Projects/Bracket/analysis.sim", + ... design_variables=[ + ... {'name': 'tip_thickness', 'lower_bound': 15.0, 'upper_bound': 25.0} + ... ], + ... objectives=[ + ... {'objective_key': 'minimize_mass', 'weight': 5.0} + ... ], + ... constraints=[ + ... {'constraint_key': 'max_stress_limit', 'limit_value': 200.0} + ... ] + ... ) + """ + try: + # Step 1: Discover model + model_result = discover_fea_model(sim_file_path) + + if model_result['status'] != 'success': + return { + 'status': 'error', + 'error_type': 'model_discovery_failed', + 'message': model_result.get('message', 'Failed to discover FEA model'), + 'suggestion': model_result.get('suggestion', 'Check that the .sim file is valid') + } + + # Step 2: Create builder + builder = OptimizationConfigBuilder(model_result) + + # Step 3: Validate and add design variables + available_vars = {dv['name']: dv for dv in builder.list_available_design_variables()} + + for dv in design_variables: + name = dv['name'] + if name not in available_vars: + return { + 'status': 'error', + 'error_type': 'invalid_design_variable', + 'message': f"Design variable '{name}' not found in model", + 'available_variables': list(available_vars.keys()), + 'suggestion': f"Choose from: {', '.join(available_vars.keys())}" + } + + builder.add_design_variable( + name=name, + lower_bound=dv['lower_bound'], + upper_bound=dv['upper_bound'] + ) + + # Step 4: Add objectives + available_objectives = builder.list_available_objectives() + + for obj in objectives: + obj_key = obj['objective_key'] + if obj_key not in available_objectives: + return { + 'status': 'error', + 'error_type': 'invalid_objective', + 'message': f"Objective '{obj_key}' not recognized", + 'available_objectives': list(available_objectives.keys()), + 'suggestion': f"Choose from: {', '.join(available_objectives.keys())}" + } + + builder.add_objective( + objective_key=obj_key, + weight=obj.get('weight'), + target=obj.get('target') + ) + + # Step 5: Add constraints (optional) + if constraints: + available_constraints = builder.list_available_constraints() + + for const in constraints: + const_key = const['constraint_key'] + if const_key not in available_constraints: + return { + 'status': 'error', + 'error_type': 'invalid_constraint', + 'message': f"Constraint '{const_key}' not recognized", + 'available_constraints': list(available_constraints.keys()), + 'suggestion': f"Choose from: {', '.join(available_constraints.keys())}" + } + + builder.add_constraint( + constraint_key=const_key, + limit_value=const['limit_value'] + ) + + # Step 6: Set optimization settings (optional) + if optimization_settings: + builder.set_optimization_settings( + n_trials=optimization_settings.get('n_trials'), + sampler=optimization_settings.get('sampler') + ) + + # Step 7: Build and validate configuration + config = builder.build() + + # Step 8: Save to file + if output_path is None: + sim_path = Path(sim_file_path) + output_path = sim_path.parent / 'optimization_config.json' + else: + output_path = Path(output_path) + + with open(output_path, 'w') as f: + json.dump(config, f, indent=2) + + # Step 9: Return success with summary + return { + 'status': 'success', + 'message': 'Optimization configuration created successfully', + 'config_file': str(output_path), + 'summary': { + 'design_variables': len(config['design_variables']), + 'objectives': len(config['objectives']), + 'constraints': len(config['constraints']), + 'n_trials': config['optimization_settings']['n_trials'], + 'sampler': config['optimization_settings']['sampler'] + }, + 'config': config + } + + except ValueError as e: + return { + 'status': 'error', + 'error_type': 'validation_error', + 'message': str(e), + 'suggestion': 'Check that all required fields are provided correctly' + } + + except Exception as e: + return { + 'status': 'error', + 'error_type': 'unexpected_error', + 'message': str(e), + 'suggestion': 'This may be a bug. Please report this issue.' + } + + +def list_optimization_options(sim_file_path: str) -> Dict[str, Any]: + """ + Helper tool: List all available optimization options for a model. + + This is useful for LLMs to show users what they can choose from. + + Args: + sim_file_path: Absolute path to .sim file + + Returns: + Dictionary with all available options + """ + try: + # Discover model + model_result = discover_fea_model(sim_file_path) + + if model_result['status'] != 'success': + return model_result + + # Create builder to get options + builder = OptimizationConfigBuilder(model_result) + + # Get all available options + design_vars = builder.list_available_design_variables() + objectives = builder.list_available_objectives() + constraints = builder.list_available_constraints() + + return { + 'status': 'success', + 'sim_file': sim_file_path, + 'available_design_variables': design_vars, + 'available_objectives': objectives, + 'available_constraints': constraints, + 'model_info': { + 'solutions': model_result.get('solutions', []), + 'expression_count': len(model_result.get('expressions', [])) + } + } + + except Exception as e: + return { + 'status': 'error', + 'error_type': 'unexpected_error', + 'message': str(e) + } + + +def format_optimization_options_for_llm(options: Dict[str, Any]) -> str: + """ + Format optimization options for LLM consumption (Markdown). + + Args: + options: Output from list_optimization_options() + + Returns: + Markdown-formatted string + """ + if options['status'] != 'success': + return f"❌ **Error**: {options['message']}\n\n💡 {options.get('suggestion', '')}" + + md = [] + md.append(f"# Optimization Configuration Options\n") + md.append(f"**Model**: `{options['sim_file']}`\n") + + # Design Variables + md.append(f"## Available Design Variables ({len(options['available_design_variables'])})\n") + if options['available_design_variables']: + md.append("| Name | Current Value | Units | Suggested Bounds |") + md.append("|------|---------------|-------|------------------|") + for dv in options['available_design_variables']: + bounds = dv['suggested_bounds'] + md.append(f"| `{dv['name']}` | {dv['current_value']} | {dv['units']} | [{bounds[0]:.2f}, {bounds[1]:.2f}] |") + else: + md.append("⚠️ No design variables found. Model may not be parametric.") + md.append("") + + # Objectives + md.append(f"## Available Objectives\n") + for key, obj in options['available_objectives'].items(): + md.append(f"### `{key}`") + md.append(f"- **Description**: {obj['description']}") + md.append(f"- **Metric**: {obj['metric']} ({obj['units']})") + md.append(f"- **Default Weight**: {obj['typical_weight']}") + md.append(f"- **Extractor**: `{obj['extractor']}`") + md.append("") + + # Constraints + md.append(f"## Available Constraints\n") + for key, const in options['available_constraints'].items(): + md.append(f"### `{key}`") + md.append(f"- **Description**: {const['description']}") + md.append(f"- **Metric**: {const['metric']} ({const['units']})") + md.append(f"- **Typical Value**: {const['typical_value']}") + md.append(f"- **Type**: {const['constraint_type']}") + md.append(f"- **Extractor**: `{const['extractor']}`") + md.append("") + + return "\n".join(md) + + +# For testing +if __name__ == "__main__": + import sys + + if len(sys.argv) < 2: + print("Usage: python optimization_config.py ") + sys.exit(1) + + sim_path = sys.argv[1] + + # Test 1: List options + print("=" * 60) + print("TEST 1: List Available Options") + print("=" * 60) + options = list_optimization_options(sim_path) + print(format_optimization_options_for_llm(options)) + + # Test 2: Build configuration + print("\n" + "=" * 60) + print("TEST 2: Build Optimization Configuration") + print("=" * 60) + + result = build_optimization_config( + sim_file_path=sim_path, + design_variables=[ + {'name': 'tip_thickness', 'lower_bound': 15.0, 'upper_bound': 25.0}, + {'name': 'support_angle', 'lower_bound': 20.0, 'upper_bound': 40.0}, + ], + objectives=[ + {'objective_key': 'minimize_mass', 'weight': 5.0}, + {'objective_key': 'minimize_max_stress', 'weight': 10.0} + ], + constraints=[ + {'constraint_key': 'max_displacement_limit', 'limit_value': 1.0}, + {'constraint_key': 'max_stress_limit', 'limit_value': 200.0} + ], + optimization_settings={ + 'n_trials': 150, + 'sampler': 'TPE' + } + ) + + if result['status'] == 'success': + print(f"SUCCESS: Configuration saved to: {result['config_file']}") + print(f"\nSummary:") + for key, value in result['summary'].items(): + print(f" - {key}: {value}") + else: + print(f"ERROR: {result['message']}") + print(f"Suggestion: {result.get('suggestion', '')}") diff --git a/optimization_config.json b/optimization_config.json new file mode 100644 index 00000000..e3a5489e --- /dev/null +++ b/optimization_config.json @@ -0,0 +1,190 @@ +{ + "design_variables": [ + { + "name": "tip_thickness", + "type": "continuous", + "bounds": [ + 15.0, + 25.0 + ], + "units": "mm", + "initial_value": 20.0 + }, + { + "name": "support_angle", + "type": "continuous", + "bounds": [ + 20.0, + 40.0 + ], + "units": "degrees", + "initial_value": 30.0 + }, + { + "name": "support_blend_radius", + "type": "continuous", + "bounds": [ + 5.0, + 15.0 + ], + "units": "mm", + "initial_value": 10.0 + } + ], + "objectives": [ + { + "name": "minimize_mass", + "description": "Minimize total mass (weight reduction)", + "extractor": "mass_extractor", + "metric": "total_mass", + "direction": "minimize", + "weight": 5.0 + }, + { + "name": "minimize_max_stress", + "description": "Minimize maximum von Mises stress", + "extractor": "stress_extractor", + "metric": "max_von_mises", + "direction": "minimize", + "weight": 10.0 + } + ], + "constraints": [ + { + "name": "max_displacement_limit", + "description": "Maximum allowable displacement", + "extractor": "displacement_extractor", + "metric": "max_displacement", + "type": "upper_bound", + "limit": 1.0, + "units": "mm" + }, + { + "name": "max_stress_limit", + "description": "Maximum allowable von Mises stress", + "extractor": "stress_extractor", + "metric": "max_von_mises", + "type": "upper_bound", + "limit": 200.0, + "units": "MPa" + } + ], + "optimization_settings": { + "n_trials": 150, + "sampler": "TPE", + "n_startup_trials": 20 + }, + "model_info": { + "sim_file": "/home/user/Atomizer/tests/Bracket_sim1.sim", + "solutions": [ + { + "name": "DisableInThermalSolution", + "type": "DisableInThermalSolution", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Disable in Thermal Solution 3D", + "type": "Disable in Thermal Solution 3D", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "-Flow-Structural Coupled Solution Parameters", + "type": "-Flow-Structural Coupled Solution Parameters", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Direct Frequency Response", + "type": "Direct Frequency Response", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "*Thermal-Flow Coupled Solution Parameters", + "type": "*Thermal-Flow Coupled Solution Parameters", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "0Thermal-Structural Coupled Solution Parameters", + "type": "0Thermal-Structural Coupled Solution Parameters", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Linear Statics", + "type": "Linear Statics", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Disable in Thermal Solution 2D", + "type": "Disable in Thermal Solution 2D", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Thermal Solution Parameters", + "type": "Thermal Solution Parameters", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Nonlinear Statics", + "type": "Nonlinear Statics", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Modal Frequency Response", + "type": "Modal Frequency Response", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "1Pass Structural Contact Solution to Flow Solver", + "type": "1Pass Structural Contact Solution to Flow Solver", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "\"ObjectDisableInThermalSolution3D", + "type": "\"ObjectDisableInThermalSolution3D", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Design Optimization", + "type": "Design Optimization", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "\"ObjectDisableInThermalSolution2D", + "type": "\"ObjectDisableInThermalSolution2D", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Normal Modes", + "type": "Normal Modes", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Direct Transient Response", + "type": "Direct Transient Response", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + }, + { + "name": "Modal Transient Response", + "type": "Modal Transient Response", + "solver": "NX Nastran", + "description": "Extracted from binary .sim file" + } + ] + } +} \ No newline at end of file diff --git a/optimization_engine/optimization_config_builder.py b/optimization_engine/optimization_config_builder.py new file mode 100644 index 00000000..6eed6227 --- /dev/null +++ b/optimization_engine/optimization_config_builder.py @@ -0,0 +1,403 @@ +""" +Optimization Configuration Builder + +Helps users build multi-objective optimization configurations by: +1. Discovering available design variables from FEA model +2. Listing available objectives and constraints +3. Creating structured optimization_config.json + +Supports: +- Multi-objective optimization (minimize weight + stress simultaneously) +- Constraints (max displacement, stress limits, mass limits) +- User selection of which objectives/constraints to apply +""" + +from pathlib import Path +from typing import Dict, Any, List +import json + + +class OptimizationConfigBuilder: + """ + Interactive builder for optimization configurations. + + Workflow: + 1. Discover model capabilities (design variables, analysis type) + 2. Present available objectives/constraints to user + 3. Build configuration based on user selections + """ + + # Available objectives that can be extracted from OP2 files + AVAILABLE_OBJECTIVES = { + 'minimize_mass': { + 'description': 'Minimize total mass (weight reduction)', + 'extractor': 'mass_extractor', + 'metric': 'total_mass', + 'units': 'kg', + 'direction': 'minimize', + 'typical_weight': 5.0 # Higher priority in multi-objective + }, + 'minimize_max_stress': { + 'description': 'Minimize maximum von Mises stress', + 'extractor': 'stress_extractor', + 'metric': 'max_von_mises', + 'units': 'MPa', + 'direction': 'minimize', + 'typical_weight': 10.0 # Very important - failure prevention + }, + 'minimize_max_displacement': { + 'description': 'Minimize maximum displacement (increase stiffness)', + 'extractor': 'displacement_extractor', + 'metric': 'max_displacement', + 'units': 'mm', + 'direction': 'minimize', + 'typical_weight': 3.0 + }, + 'minimize_volume': { + 'description': 'Minimize total volume (material usage)', + 'extractor': 'volume_extractor', + 'metric': 'total_volume', + 'units': 'mm^3', + 'direction': 'minimize', + 'typical_weight': 4.0 + } + } + + # Available constraints + AVAILABLE_CONSTRAINTS = { + 'max_stress_limit': { + 'description': 'Maximum allowable von Mises stress', + 'extractor': 'stress_extractor', + 'metric': 'max_von_mises', + 'units': 'MPa', + 'typical_value': 200.0, # Below yield strength with safety factor + 'constraint_type': 'upper_bound' + }, + 'max_displacement_limit': { + 'description': 'Maximum allowable displacement', + 'extractor': 'displacement_extractor', + 'metric': 'max_displacement', + 'units': 'mm', + 'typical_value': 1.0, # Stiffness requirement + 'constraint_type': 'upper_bound' + }, + 'min_mass_limit': { + 'description': 'Minimum required mass (structural integrity)', + 'extractor': 'mass_extractor', + 'metric': 'total_mass', + 'units': 'kg', + 'typical_value': 0.3, + 'constraint_type': 'lower_bound' + }, + 'max_mass_limit': { + 'description': 'Maximum allowable mass (weight budget)', + 'extractor': 'mass_extractor', + 'metric': 'total_mass', + 'units': 'kg', + 'typical_value': 0.5, + 'constraint_type': 'upper_bound' + } + } + + def __init__(self, model_discovery_result: Dict[str, Any]): + """ + Initialize with model discovery results. + + Args: + model_discovery_result: Output from discover_fea_model() + """ + self.model_info = model_discovery_result + self.config = { + 'design_variables': [], + 'objectives': [], + 'constraints': [], + 'optimization_settings': { + 'n_trials': 100, + 'sampler': 'TPE', + 'n_startup_trials': 20 + } + } + + def list_available_design_variables(self) -> List[Dict[str, Any]]: + """ + List all available design variables from model. + + Returns: + List of design variable options + """ + if 'expressions' not in self.model_info: + return [] + + design_vars = [] + for expr in self.model_info['expressions']: + if expr['value'] is not None: # Only variables with known values + design_vars.append({ + 'name': expr['name'], + 'current_value': expr['value'], + 'units': expr['units'], + 'type': expr.get('type', 'Unknown'), + 'suggested_bounds': self._suggest_bounds(expr) + }) + + return design_vars + + def _suggest_bounds(self, expr: Dict[str, Any]) -> tuple: + """ + Suggest reasonable optimization bounds for a design variable. + + Args: + expr: Expression dictionary + + Returns: + (lower_bound, upper_bound) + """ + value = expr['value'] + expr_type = expr.get('type', '').lower() + + if 'angle' in expr_type or 'degrees' in expr.get('units', '').lower(): + # Angles: ±15 degrees + return (max(0, value - 15), min(180, value + 15)) + elif 'thickness' in expr['name'].lower() or 'dimension' in expr_type: + # Dimensions: ±30% + return (value * 0.7, value * 1.3) + elif 'radius' in expr['name'].lower() or 'diameter' in expr['name'].lower(): + # Radii/diameters: ±25% + return (value * 0.75, value * 1.25) + else: + # Default: ±20% + return (value * 0.8, value * 1.2) + + def list_available_objectives(self) -> Dict[str, Dict[str, Any]]: + """ + List all available optimization objectives. + + Returns: + Dictionary of objective options + """ + return self.AVAILABLE_OBJECTIVES.copy() + + def list_available_constraints(self) -> Dict[str, Dict[str, Any]]: + """ + List all available constraints. + + Returns: + Dictionary of constraint options + """ + return self.AVAILABLE_CONSTRAINTS.copy() + + def add_design_variable(self, name: str, lower_bound: float, upper_bound: float): + """ + Add a design variable to the configuration. + + Args: + name: Expression name from model + lower_bound: Minimum value + upper_bound: Maximum value + """ + # Verify variable exists in model + expr = next((e for e in self.model_info['expressions'] if e['name'] == name), None) + if not expr: + raise ValueError(f"Design variable '{name}' not found in model") + + self.config['design_variables'].append({ + 'name': name, + 'type': 'continuous', + 'bounds': [lower_bound, upper_bound], + 'units': expr.get('units', ''), + 'initial_value': expr['value'] + }) + + def add_objective(self, objective_key: str, weight: float = None, target: float = None): + """ + Add an objective to the configuration. + + Args: + objective_key: Key from AVAILABLE_OBJECTIVES + weight: Importance weight (for multi-objective) + target: Target value (optional, for goal programming) + """ + if objective_key not in self.AVAILABLE_OBJECTIVES: + raise ValueError(f"Unknown objective: {objective_key}") + + obj_info = self.AVAILABLE_OBJECTIVES[objective_key] + + objective = { + 'name': objective_key, + 'description': obj_info['description'], + 'extractor': obj_info['extractor'], + 'metric': obj_info['metric'], + 'direction': obj_info['direction'], + 'weight': weight or obj_info['typical_weight'] + } + + if target is not None: + objective['target'] = target + + self.config['objectives'].append(objective) + + def add_constraint(self, constraint_key: str, limit_value: float): + """ + Add a constraint to the configuration. + + Args: + constraint_key: Key from AVAILABLE_CONSTRAINTS + limit_value: Constraint limit value + """ + if constraint_key not in self.AVAILABLE_CONSTRAINTS: + raise ValueError(f"Unknown constraint: {constraint_key}") + + const_info = self.AVAILABLE_CONSTRAINTS[constraint_key] + + constraint = { + 'name': constraint_key, + 'description': const_info['description'], + 'extractor': const_info['extractor'], + 'metric': const_info['metric'], + 'type': const_info['constraint_type'], + 'limit': limit_value, + 'units': const_info['units'] + } + + self.config['constraints'].append(constraint) + + def set_optimization_settings(self, n_trials: int = None, sampler: str = None): + """ + Configure optimization algorithm settings. + + Args: + n_trials: Number of optimization iterations + sampler: 'TPE', 'CMAES', 'GP', etc. + """ + if n_trials: + self.config['optimization_settings']['n_trials'] = n_trials + if sampler: + self.config['optimization_settings']['sampler'] = sampler + + def build(self) -> Dict[str, Any]: + """ + Build and validate the configuration. + + Returns: + Complete optimization configuration + """ + # Validation + if not self.config['design_variables']: + raise ValueError("At least one design variable is required") + + if not self.config['objectives']: + raise ValueError("At least one objective is required") + + # Add metadata + self.config['model_info'] = { + 'sim_file': self.model_info.get('sim_file', ''), + 'solutions': self.model_info.get('solutions', []) + } + + return self.config + + def save(self, output_path: Path): + """ + Save configuration to JSON file. + + Args: + output_path: Path to save configuration + """ + config = self.build() + + with open(output_path, 'w') as f: + json.dump(config, f, indent=2) + + print(f"Configuration saved to: {output_path}") + + def print_summary(self): + """Print a human-readable summary of the configuration.""" + print("\n" + "="*60) + print("OPTIMIZATION CONFIGURATION SUMMARY") + print("="*60) + + print(f"\nModel: {self.model_info.get('sim_file', 'Unknown')}") + + print(f"\nDesign Variables ({len(self.config['design_variables'])}):") + for dv in self.config['design_variables']: + print(f" • {dv['name']}: [{dv['bounds'][0]:.2f}, {dv['bounds'][1]:.2f}] {dv['units']}") + + print(f"\nObjectives ({len(self.config['objectives'])}):") + for obj in self.config['objectives']: + print(f" • {obj['description']} (weight: {obj['weight']:.1f})") + + print(f"\nConstraints ({len(self.config['constraints'])}):") + for const in self.config['constraints']: + operator = '<=' if const['type'] == 'upper_bound' else '>=' + print(f" • {const['description']}: {const['metric']} {operator} {const['limit']} {const['units']}") + + print(f"\nOptimization Settings:") + print(f" • Trials: {self.config['optimization_settings']['n_trials']}") + print(f" • Sampler: {self.config['optimization_settings']['sampler']}") + + print("="*60 + "\n") + + +# Example usage +if __name__ == "__main__": + from mcp_server.tools.model_discovery import discover_fea_model + + # Step 1: Discover model + print("Step 1: Discovering FEA model...") + model_result = discover_fea_model("tests/Bracket_sim1.sim") + + # Step 2: Create builder + builder = OptimizationConfigBuilder(model_result) + + # Step 3: Show available options + print("\n" + "="*60) + print("AVAILABLE DESIGN VARIABLES:") + print("="*60) + for dv in builder.list_available_design_variables(): + print(f"\n• {dv['name']}") + print(f" Current value: {dv['current_value']} {dv['units']}") + print(f" Suggested bounds: {dv['suggested_bounds']}") + + print("\n" + "="*60) + print("AVAILABLE OBJECTIVES:") + print("="*60) + for key, obj in builder.list_available_objectives().items(): + print(f"\n• {key}") + print(f" Description: {obj['description']}") + print(f" Default weight: {obj['typical_weight']}") + + print("\n" + "="*60) + print("AVAILABLE CONSTRAINTS:") + print("="*60) + for key, const in builder.list_available_constraints().items(): + print(f"\n• {key}") + print(f" Description: {const['description']}") + print(f" Typical value: {const['typical_value']} {const['units']}") + + # Step 4: Build a multi-objective configuration + print("\n" + "="*60) + print("BUILDING CONFIGURATION:") + print("="*60) + + # Add design variables + builder.add_design_variable('tip_thickness', 15.0, 25.0) + builder.add_design_variable('support_angle', 20.0, 40.0) + builder.add_design_variable('support_blend_radius', 5.0, 15.0) + + # Add objectives: minimize weight AND minimize stress + builder.add_objective('minimize_mass', weight=5.0) + builder.add_objective('minimize_max_stress', weight=10.0) + + # Add constraints: max displacement < 1.0 mm, max stress < 200 MPa + builder.add_constraint('max_displacement_limit', limit_value=1.0) + builder.add_constraint('max_stress_limit', limit_value=200.0) + + # Set optimization settings + builder.set_optimization_settings(n_trials=150, sampler='TPE') + + # Print summary + builder.print_summary() + + # Save configuration + builder.save(Path('optimization_config.json')) + + print("\nConfiguration ready for optimization!") diff --git a/optimization_engine/result_extractors/op2_extractor_example.py b/optimization_engine/result_extractors/op2_extractor_example.py new file mode 100644 index 00000000..f76df09e --- /dev/null +++ b/optimization_engine/result_extractors/op2_extractor_example.py @@ -0,0 +1,236 @@ +""" +Example: Result Extraction from OP2 files using pyNastran + +This shows how to extract optimization metrics from Nastran OP2 files. +Common metrics: +- Max displacement (for stiffness constraints) +- Max von Mises stress (for strength constraints) +- Mass (for minimization objectives) +""" + +from pathlib import Path +from typing import Dict, Any +import numpy as np + + +def extract_max_displacement(op2_path: Path) -> Dict[str, Any]: + """ + Extract maximum displacement magnitude from OP2 file. + + Args: + op2_path: Path to .op2 file + + Returns: + Dictionary with max displacement, node ID, and components + """ + from pyNastran.op2.op2 import OP2 + + op2 = OP2() + op2.read_op2(str(op2_path)) + + # Get first subcase (usually the only one in static analysis) + subcase_id = list(op2.displacements.keys())[0] + displacements = op2.displacements[subcase_id] + + # Extract node IDs and displacement data + node_ids = displacements.node_gridtype[:, 0].astype(int) + disp_data = displacements.data[0] # First (and usually only) timestep + + # Calculate magnitude: sqrt(dx^2 + dy^2 + dz^2) + dx = disp_data[:, 0] + dy = disp_data[:, 1] + dz = disp_data[:, 2] + magnitudes = np.sqrt(dx**2 + dy**2 + dz**2) + + # Find max + max_idx = np.argmax(magnitudes) + max_displacement = magnitudes[max_idx] + max_node_id = node_ids[max_idx] + + return { + 'max_displacement': float(max_displacement), + 'max_node_id': int(max_node_id), + 'dx': float(dx[max_idx]), + 'dy': float(dy[max_idx]), + 'dz': float(dz[max_idx]), + 'units': 'mm', # NX typically uses mm + 'subcase': subcase_id + } + + +def extract_max_stress(op2_path: Path, stress_type: str = 'von_mises') -> Dict[str, Any]: + """ + Extract maximum stress from OP2 file. + + Args: + op2_path: Path to .op2 file + stress_type: 'von_mises' or 'max_principal' + + Returns: + Dictionary with max stress, element ID, and location + """ + from pyNastran.op2.op2 import OP2 + + op2 = OP2() + op2.read_op2(str(op2_path)) + + # Stress can be in different tables depending on element type + # Common: cquad4_stress, ctria3_stress, ctetra_stress, etc. + stress_tables = [ + 'cquad4_stress', + 'ctria3_stress', + 'ctetra_stress', + 'chexa_stress', + 'cbar_stress', + 'cbeam_stress' + ] + + max_stress_overall = 0.0 + max_element_id = None + max_element_type = None + + for table_name in stress_tables: + if hasattr(op2, table_name): + stress_table = getattr(op2, table_name) + if stress_table: + subcase_id = list(stress_table.keys())[0] + stress_data = stress_table[subcase_id] + + # Extract von Mises stress + # Note: Structure varies by element type + element_ids = stress_data.element_node[:, 0].astype(int) + + if stress_type == 'von_mises': + # von Mises is usually last column + stresses = stress_data.data[0, :, -1] # timestep 0, all elements, last column + else: + # Max principal stress (second-to-last column typically) + stresses = stress_data.data[0, :, -2] + + max_stress_in_table = np.max(stresses) + if max_stress_in_table > max_stress_overall: + max_stress_overall = max_stress_in_table + max_idx = np.argmax(stresses) + max_element_id = element_ids[max_idx] + max_element_type = table_name.replace('_stress', '') + + return { + 'max_stress': float(max_stress_overall), + 'stress_type': stress_type, + 'element_id': int(max_element_id) if max_element_id else None, + 'element_type': max_element_type, + 'units': 'MPa', # NX typically uses MPa + } + + +def extract_mass(op2_path: Path) -> Dict[str, Any]: + """ + Extract total mass from OP2 file. + + Args: + op2_path: Path to .op2 file + + Returns: + Dictionary with mass and center of gravity + """ + from pyNastran.op2.op2 import OP2 + + op2 = OP2() + op2.read_op2(str(op2_path)) + + # Mass is in grid_point_weight table + if hasattr(op2, 'grid_point_weight') and op2.grid_point_weight: + mass_data = op2.grid_point_weight + + # Total mass + total_mass = mass_data.mass.sum() + + # Center of gravity + cg = mass_data.cg + + return { + 'total_mass': float(total_mass), + 'cg_x': float(cg[0]), + 'cg_y': float(cg[1]), + 'cg_z': float(cg[2]), + 'units': 'kg' + } + else: + # Fallback: Mass not directly available + return { + 'total_mass': None, + 'note': 'Mass data not found in OP2 file. Ensure PARAM,GRDPNT,0 is in Nastran deck' + } + + +# Combined extraction function for optimization +def extract_all_results(op2_path: Path) -> Dict[str, Any]: + """ + Extract all common optimization metrics from OP2 file. + + Args: + op2_path: Path to .op2 file + + Returns: + Dictionary with all results + """ + results = { + 'op2_file': str(op2_path), + 'status': 'success' + } + + try: + results['displacement'] = extract_max_displacement(op2_path) + except Exception as e: + results['displacement'] = {'error': str(e)} + + try: + results['stress'] = extract_max_stress(op2_path) + except Exception as e: + results['stress'] = {'error': str(e)} + + try: + results['mass'] = extract_mass(op2_path) + except Exception as e: + results['mass'] = {'error': str(e)} + + return results + + +# Example usage +if __name__ == "__main__": + import sys + import json + + if len(sys.argv) < 2: + print("Usage: python op2_extractor_example.py ") + sys.exit(1) + + op2_path = Path(sys.argv[1]) + + if not op2_path.exists(): + print(f"Error: File not found: {op2_path}") + sys.exit(1) + + print(f"Extracting results from: {op2_path}") + print("=" * 60) + + results = extract_all_results(op2_path) + + print("\nResults:") + print(json.dumps(results, indent=2)) + + # Summary + print("\n" + "=" * 60) + print("SUMMARY:") + if 'displacement' in results and 'max_displacement' in results['displacement']: + disp = results['displacement'] + print(f" Max Displacement: {disp['max_displacement']:.6f} {disp['units']} at node {disp['max_node_id']}") + + if 'stress' in results and 'max_stress' in results['stress']: + stress = results['stress'] + print(f" Max {stress['stress_type']}: {stress['max_stress']:.2f} {stress['units']} in element {stress['element_id']}") + + if 'mass' in results and 'total_mass' in results['mass'] and results['mass']['total_mass']: + mass = results['mass'] + print(f" Total Mass: {mass['total_mass']:.6f} {mass['units']}") diff --git a/tests/Bracket.prt b/tests/Bracket.prt new file mode 100644 index 00000000..82091dc6 Binary files /dev/null and b/tests/Bracket.prt differ diff --git a/tests/Bracket_fem1.fem b/tests/Bracket_fem1.fem new file mode 100644 index 00000000..388e4b34 Binary files /dev/null and b/tests/Bracket_fem1.fem differ diff --git a/tests/Bracket_sim1.sim b/tests/Bracket_sim1.sim new file mode 100644 index 00000000..2635545f Binary files /dev/null and b/tests/Bracket_sim1.sim differ diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/bracket_sim1-solution_1.dat b/tests/bracket_sim1-solution_1.dat new file mode 100644 index 00000000..292aa9fc --- /dev/null +++ b/tests/bracket_sim1-solution_1.dat @@ -0,0 +1,1970 @@ +$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$* +$* Simcenter v2412.0.0.7002 Translator +$* for Simcenter Nastran version 2412.0 +$* +$* FEM FILE: C:\Users\antoi\Documents\Atomaste\Atomizer\tests\Bracket_fem1.fem +$* SIM FILE: C:\Users\antoi\Documents\Atomaste\Atomizer\tests\Bracket_sim1.sim +$* ANALYSIS TYPE: Structural +$* SOLUTION NAME: Solution 1 +$* SOLUTION TYPE: SOL 101 Linear Statics +$* +$* SOLVER INPUT FILE: bracket_sim1-solution_1.dat +$* CREATION DATE: 15-Nov-2025 +$* CREATION TIME: 08:33:00 +$* HOSTNAME: AntoineThinkpad +$* NASTRAN LICENSE: Desktop Bundle +$* +$* UNITS: mm (milli-newton) +$* ... LENGTH : mm +$* ... TIME : sec +$* ... MASS : kilogram (kg) +$* ... TEMPERATURE : deg Celsius +$* ... FORCE : milli-newton +$* ... THERMAL ENERGY : mN-mm (micro-joule) +$* +$* IMPORTANT NOTE: +$* This banner was generated by Simcenter and altering this +$* information may compromise the pre and post processing of results +$* +$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$* +$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$* +$* FILE MANAGEMENT +$* +$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$* +$* +$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$* +$* EXECUTIVE CONTROL +$* +$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$* +ID,NASTRAN,bracket_sim1-solution_1 +SOL 101 +CEND +$* +$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$* +$* CASE CONTROL +$* +$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$* +ECHO = NONE +OUTPUT +DISPLACEMENT(PLOT,REAL) = ALL +SPCFORCES(PLOT,REAL) = ALL +STRESS(PLOT,REAL,VONMISES,CENTER) = ALL +$* Step: Subcase - Statics 1 +SUBCASE 1 + LABEL = Subcase - Statics 1 + LOAD = 1 + SPC = 2 +$* +$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$* +$* BULK DATA +$* +$*$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$* +BEGIN BULK +$* +$* PARAM CARDS +$* +PARAM K6ROT100.0000 +PARAM OIBULK YES +PARAM OMACHPR YES +PARAM POST -2 +PARAM POSTEXT YES +PARAM UNITSYS MN-MM +$* +$* GRID CARDS +$* +GRID* 1 0-5.000000000E+003.0809149719E-14+ +* 6.0969398499E+01 0 +GRID* 2 0-5.000000000E+000.0000000000E+00+ +* 0.0000000000E+00 0 +GRID* 3 0-5.000000000E+001.0000000000E+01+ +* 0.0000000000E+00 0 +GRID* 4 0-5.000000000E+003.0766941071E+01+ +* 3.5969398499E+01 0 +GRID* 5 0-5.000000000E+003.9427196503E+01+ +* 4.0969398499E+01 0 +GRID* 6 0-5.000000000E+001.0000000000E+02+ +* 6.0969398499E+01 0 +GRID* 7 0-5.000000000E+001.0000000000E+02+ +* 4.0969398499E+01 0 +GRID* 8 05.0000000000E+000.0000000000E+00+ +* 0.0000000000E+00 0 +GRID* 9 05.0000000000E+003.0809149719E-14+ +* 6.0969398499E+01 0 +GRID* 10 05.0000000000E+001.0000000000E+01+ +* 0.0000000000E+00 0 +GRID* 11 05.0000000000E+003.0766941071E+01+ +* 3.5969398499E+01 0 +GRID* 12 05.0000000000E+003.9427196503E+01+ +* 4.0969398499E+01 0 +GRID* 13 05.0000000000E+001.0000000000E+02+ +* 4.0969398499E+01 0 +GRID* 14 05.0000000000E+001.0000000000E+02+ +* 6.0969398499E+01 0 +GRID* 15 0-5.000000000E+005.6599138936E-15+ +* 1.1200618933E+01 0 +GRID* 16 0-5.000000000E+007.4463678094E-15+ +* 1.4735900552E+01 0 +GRID* 17 0-5.000000000E+001.2998593569E-14+ +* 2.5723411339E+01 0 +GRID* 18 0-5.000000000E+001.1146461935E-14+ +* 2.2058157584E+01 0 +GRID* 19 0-5.000000000E+001.4878673103E-14+ +* 2.9443972255E+01 0 +GRID* 20 0-5.000000000E+001.6776288650E-14+ +* 3.3199235856E+01 0 +GRID* 21 0-5.000000000E+009.2818773938E-15+ +* 1.8368260299E+01 0 +GRID* 22 0-5.000000000E+002.4509094810E-14+ +* 4.8501980156E+01 0 +GRID* 23 0-5.000000000E+002.2516325714E-14+ +* 4.4558413578E+01 0 +GRID* 24 0-5.000000000E+001.8657565464E-14+ +* 3.6922166114E+01 0 +GRID* 25 0-5.000000000E+002.6552877281E-14+ +* 5.2546499042E+01 0 +GRID* 26 0-5.000000000E+002.8654609257E-14+ +* 5.6705696408E+01 0 +GRID* 27 0-5.000000000E+002.0564375719E-14+ +* 4.0695625473E+01 0 +GRID* 28 0-5.000000000E+003.8249043630E-15+ +* 7.5692487605E+00 0 +GRID* 29 0-5.000000000E+001.9481352367E-15+ +* 3.8552389357E+00 0 +GRID* 30 0-5.000000000E+003.5024129445E+00+ +* 0.0000000000E+00 0 +GRID* 31 0-5.000000000E+007.0841633353E+00+ +* 0.0000000000E+00 0 +GRID* 32 0-5.000000000E+002.9304451981E+01+ +* 3.3436292988E+01 0 +GRID* 33 0-5.000000000E+002.7390009892E+01+ +* 3.0120381888E+01 0 +GRID* 34 0-5.000000000E+002.5408015081E+01+ +* 2.6687466038E+01 0 +GRID* 35 0-5.000000000E+002.1555376243E+01+ +* 2.0014499559E+01 0 +GRID* 36 0-5.000000000E+002.3466452750E+01+ +* 2.3324581298E+01 0 +GRID* 37 0-5.000000000E+001.9699822681E+01+ +* 1.6800586383E+01 0 +GRID* 38 0-5.000000000E+001.6069470407E+01+ +* 1.0512631543E+01 0 +GRID* 39 0-5.000000000E+001.7856460381E+01+ +* 1.3607789095E+01 0 +GRID* 40 0-5.000000000E+001.4376422902E+01+ +* 7.5801871258E+00 0 +GRID* 41 0-5.000000000E+001.2730156969E+01+ +* 4.7287707737E+00 0 +GRID* 42 0-5.000000000E+001.1243918277E+01+ +* 2.1545297429E+00 0 +GRID* 43 0-5.000000000E+003.6136957970E+01+ +* 4.0357355675E+01 0 +GRID* 44 0-5.000000000E+003.3533547646E+01+ +* 3.9045344197E+01 0 +GRID* 45 0-5.000000000E+003.0288176981E+01+ +* 6.0969398499E+01 0 +GRID* 46 0-5.000000000E+002.6219783709E+01+ +* 6.0969398499E+01 0 +GRID* 47 0-5.000000000E+003.4395022243E+01+ +* 6.0969398499E+01 0 +GRID* 48 0-5.000000000E+004.2802664765E+01+ +* 6.0969398499E+01 0 +GRID* 49 0-5.000000000E+004.6988428548E+01+ +* 6.0969398499E+01 0 +GRID* 50 0-5.000000000E+003.8587374217E+01+ +* 6.0969398499E+01 0 +GRID* 51 0-5.000000000E+006.7510060672E+01+ +* 6.0969398499E+01 0 +GRID* 52 0-5.000000000E+006.3455753671E+01+ +* 6.0969398499E+01 0 +GRID* 53 0-5.000000000E+005.9370054951E+01+ +* 6.0969398499E+01 0 +GRID* 54 0-5.000000000E+005.1137500769E+01+ +* 6.0969398499E+01 0 +GRID* 55 0-5.000000000E+005.5262296559E+01+ +* 6.0969398499E+01 0 +GRID* 56 0-5.000000000E+007.1572616289E+01+ +* 6.0969398499E+01 0 +GRID* 57 0-5.000000000E+007.5636537064E+01+ +* 6.0969398499E+01 0 +GRID* 58 0-5.000000000E+007.9714010810E+01+ +* 6.0969398499E+01 0 +GRID* 59 0-5.000000000E+008.3793617603E+01+ +* 6.0969398499E+01 0 +GRID* 60 0-5.000000000E+009.1932211807E+01+ +* 6.0969398499E+01 0 +GRID* 61 0-5.000000000E+009.5968857727E+01+ +* 6.0969398499E+01 0 +GRID* 62 0-5.000000000E+008.8577358654E+00+ +* 6.0969398499E+01 0 +GRID* 63 0-5.000000000E+004.4264576473E+00+ +* 6.0969398499E+01 0 +GRID* 64 0-5.000000000E+008.7869704397E+01+ +* 6.0969398499E+01 0 +GRID* 65 0-5.000000000E+001.3292095420E+01+ +* 6.0969398499E+01 0 +GRID* 66 0-5.000000000E+001.7702276246E+01+ +* 6.0969398499E+01 0 +GRID* 67 0-5.000000000E+002.2030172574E+01+ +* 6.0969398499E+01 0 +GRID* 68 0-5.000000000E+005.9392142471E+01+ +* 4.0969398499E+01 0 +GRID* 69 0-5.000000000E+006.3467673597E+01+ +* 4.0969398499E+01 0 +GRID* 70 0-5.000000000E+005.1226024617E+01+ +* 4.0969398499E+01 0 +GRID* 71 0-5.000000000E+005.5307029501E+01+ +* 4.0969398499E+01 0 +GRID* 72 0-5.000000000E+004.7164089390E+01+ +* 4.0969398499E+01 0 +GRID* 73 0-5.000000000E+004.3181930500E+01+ +* 4.0969398499E+01 0 +GRID* 74 0-5.000000000E+006.7516125932E+01+ +* 4.0969398499E+01 0 +GRID* 75 0-5.000000000E+009.5968337974E+01+ +* 4.0969398499E+01 0 +GRID* 76 0-5.000000000E+009.1932157327E+01+ +* 4.0969398499E+01 0 +GRID* 77 0-5.000000000E+008.7869946355E+01+ +* 4.0969398499E+01 0 +GRID* 78 0-5.000000000E+007.9714807161E+01+ +* 4.0969398499E+01 0 +GRID* 79 0-5.000000000E+007.5638061444E+01+ +* 4.0969398499E+01 0 +GRID* 80 0-5.000000000E+007.1575575698E+01+ +* 4.0969398499E+01 0 +GRID* 81 0-5.000000000E+008.3793963486E+01+ +* 4.0969398499E+01 0 +GRID* 82 0-5.000000000E+001.0000000000E+02+ +* 4.6014875615E+01 0 +GRID* 83 0-5.000000000E+001.0000000000E+02+ +* 5.0977922286E+01 0 +GRID* 84 0-5.000000000E+001.0000000000E+02+ +* 5.5931393500E+01 0 +GRID* 85 05.0149807778E-030.0000000000E+00+ +* 0.0000000000E+00 0 +GRID* 86 05.2099017236E-033.0809149719E-14+ +* 6.0969398499E+01 0 +GRID* 87 05.1094438118E-031.0000000000E+01+ +* 0.0000000000E+00 0 +GRID* 88 05.4978155655E-033.0766941071E+01+ +* 3.5969398499E+01 0 +GRID* 89 05.6290425668E-033.9427196503E+01+ +* 4.0969398499E+01 0 +GRID* 90 05.3952402233E-031.0000000000E+02+ +* 4.0969398499E+01 0 +GRID* 91 05.2102686847E-031.0000000000E+02+ +* 6.0969398499E+01 0 +GRID* 92 05.0000000000E+003.8251026909E-15+ +* 7.5696412391E+00 0 +GRID* 93 05.0000000000E+001.9485099981E-15+ +* 3.8559805653E+00 0 +GRID* 94 05.0000000000E+001.1146499179E-14+ +* 2.2058231288E+01 0 +GRID* 95 05.0000000000E+001.2998622030E-14+ +* 2.5723467662E+01 0 +GRID* 96 05.0000000000E+001.4878702140E-14+ +* 2.9444029718E+01 0 +GRID* 97 05.0000000000E+001.6776311170E-14+ +* 3.3199280421E+01 0 +GRID* 98 05.0000000000E+009.2819237749E-15+ +* 1.8368352084E+01 0 +GRID* 99 05.0000000000E+007.4464265072E-15+ +* 1.4736016711E+01 0 +GRID* 100 05.0000000000E+002.8654612998E-14+ +* 5.6705703813E+01 0 +GRID* 101 05.0000000000E+002.6552884026E-14+ +* 5.2546512391E+01 0 +GRID* 102 05.0000000000E+002.4509105680E-14+ +* 4.8502001668E+01 0 +GRID* 103 05.0000000000E+002.2516338299E-14+ +* 4.4558438484E+01 0 +GRID* 104 05.0000000000E+002.0564394189E-14+ +* 4.0695662023E+01 0 +GRID* 105 05.0000000000E+001.8657587566E-14+ +* 3.6922209852E+01 0 +GRID* 106 05.0000000000E+005.6600020874E-15+ +* 1.1200793463E+01 0 +GRID* 107 05.0000000000E+007.0829159424E+00+ +* 0.0000000000E+00 0 +GRID* 108 05.0000000000E+003.5020416032E+00+ +* 0.0000000000E+00 0 +GRID* 109 05.0000000000E+001.1243279170E+01+ +* 2.1534227763E+00 0 +GRID* 110 05.0000000000E+002.9304095846E+01+ +* 3.3435676144E+01 0 +GRID* 111 05.0000000000E+001.6069447535E+01+ +* 1.0512591928E+01 0 +GRID* 112 05.0000000000E+001.7856248355E+01+ +* 1.3607421854E+01 0 +GRID* 113 05.0000000000E+002.7389943247E+01+ +* 3.0120266456E+01 0 +GRID* 114 05.0000000000E+002.5407952489E+01+ +* 2.6687357624E+01 0 +GRID* 115 05.0000000000E+001.9699724948E+01+ +* 1.6800417106E+01 0 +GRID* 116 05.0000000000E+002.1555389278E+01+ +* 2.0014522135E+01 0 +GRID* 117 05.0000000000E+002.3466535396E+01+ +* 2.3324724446E+01 0 +GRID* 118 05.0000000000E+001.4376418155E+01+ +* 7.5801789052E+00 0 +GRID* 119 05.0000000000E+001.2730044924E+01+ +* 4.7285767050E+00 0 +GRID* 120 05.0000000000E+003.3533540603E+01+ +* 3.9045337856E+01 0 +GRID* 121 05.0000000000E+003.6137163716E+01+ +* 4.0357422526E+01 0 +GRID* 122 05.0000000000E+003.0288181507E+01+ +* 6.0969398499E+01 0 +GRID* 123 05.0000000000E+003.4395017111E+01+ +* 6.0969398499E+01 0 +GRID* 124 05.0000000000E+002.6219812464E+01+ +* 6.0969398499E+01 0 +GRID* 125 05.0000000000E+003.8587367319E+01+ +* 6.0969398499E+01 0 +GRID* 126 05.0000000000E+004.2802652841E+01+ +* 6.0969398499E+01 0 +GRID* 127 05.0000000000E+006.3455757541E+01+ +* 6.0969398499E+01 0 +GRID* 128 05.0000000000E+006.7510065076E+01+ +* 6.0969398499E+01 0 +GRID* 129 05.0000000000E+005.9370055353E+01+ +* 6.0969398499E+01 0 +GRID* 130 05.0000000000E+005.5262293984E+01+ +* 6.0969398499E+01 0 +GRID* 131 05.0000000000E+005.1137495423E+01+ +* 6.0969398499E+01 0 +GRID* 132 05.0000000000E+004.6988427828E+01+ +* 6.0969398499E+01 0 +GRID* 133 05.0000000000E+007.5636537417E+01+ +* 6.0969398499E+01 0 +GRID* 134 05.0000000000E+007.1572619625E+01+ +* 6.0969398499E+01 0 +GRID* 135 05.0000000000E+007.9714010552E+01+ +* 6.0969398499E+01 0 +GRID* 136 05.0000000000E+008.3793616445E+01+ +* 6.0969398499E+01 0 +GRID* 137 05.0000000000E+008.7869701402E+01+ +* 6.0969398499E+01 0 +GRID* 138 05.0000000000E+009.1932210970E+01+ +* 6.0969398499E+01 0 +GRID* 139 05.0000000000E+009.5968853910E+01+ +* 6.0969398499E+01 0 +GRID* 140 05.0000000000E+004.4264627718E+00+ +* 6.0969398499E+01 0 +GRID* 141 05.0000000000E+008.8577525524E+00+ +* 6.0969398499E+01 0 +GRID* 142 05.0000000000E+001.7702295276E+01+ +* 6.0969398499E+01 0 +GRID* 143 05.0000000000E+002.2030196659E+01+ +* 6.0969398499E+01 0 +GRID* 144 05.0000000000E+001.3292113057E+01+ +* 6.0969398499E+01 0 +GRID* 145 05.0000000000E+005.9392147692E+01+ +* 4.0969398499E+01 0 +GRID* 146 05.0000000000E+006.3467674794E+01+ +* 4.0969398499E+01 0 +GRID* 147 05.0000000000E+005.5307033645E+01+ +* 4.0969398499E+01 0 +GRID* 148 05.0000000000E+006.7516124144E+01+ +* 4.0969398499E+01 0 +GRID* 149 05.0000000000E+005.1226027554E+01+ +* 4.0969398499E+01 0 +GRID* 150 05.0000000000E+004.7164117708E+01+ +* 4.0969398499E+01 0 +GRID* 151 05.0000000000E+004.3181977028E+01+ +* 4.0969398499E+01 0 +GRID* 152 05.0000000000E+009.5968333205E+01+ +* 4.0969398499E+01 0 +GRID* 153 05.0000000000E+009.1932158349E+01+ +* 4.0969398499E+01 0 +GRID* 154 05.0000000000E+008.3793964011E+01+ +* 4.0969398499E+01 0 +GRID* 155 05.0000000000E+008.7869944682E+01+ +* 4.0969398499E+01 0 +GRID* 156 05.0000000000E+007.9714807966E+01+ +* 4.0969398499E+01 0 +GRID* 157 05.0000000000E+007.5638067018E+01+ +* 4.0969398499E+01 0 +GRID* 158 05.0000000000E+007.1575585475E+01+ +* 4.0969398499E+01 0 +GRID* 159 05.0000000000E+001.0000000000E+02+ +* 4.6014844030E+01 0 +GRID* 160 05.0000000000E+001.0000000000E+02+ +* 5.5931419254E+01 0 +GRID* 161 05.0000000000E+001.0000000000E+02+ +* 5.0977923066E+01 0 +GRID* 162 0-5.000000000E+003.8365121664E+00+ +* 7.4284499561E+00 0 +GRID* 163 0-5.000000000E+008.3019736448E+00+ +* 6.6599196629E+00 0 +GRID* 164 0-5.000000000E+003.5858243885E+00+ +* 3.7864582063E+00 0 +GRID* 165 0-5.000000000E+007.4111186332E+00+ +* 3.3924111896E+00 0 +GRID* 166 0-5.000000000E+003.0261966325E+01+ +* 5.5797624131E+01 0 +GRID* 167 0-5.000000000E+002.6202825136E+01+ +* 5.5900797863E+01 0 +GRID* 168 0-5.000000000E+002.6046207614E+01+ +* 5.0799626429E+01 0 +GRID* 169 0-5.000000000E+003.0206371270E+01+ +* 5.0587846507E+01 0 +GRID* 170 0-5.000000000E+003.4425731238E+01+ +* 5.0638502354E+01 0 +GRID* 171 0-5.000000000E+003.4382597204E+01+ +* 5.5796642686E+01 0 +GRID* 172 0-5.000000000E+003.8738469389E+01+ +* 5.0814645657E+01 0 +GRID* 173 0-5.000000000E+003.8606803936E+01+ +* 5.5841311807E+01 0 +GRID* 174 0-5.000000000E+004.2935806391E+01+ +* 5.0930227602E+01 0 +GRID* 175 0-5.000000000E+004.7064561723E+01+ +* 5.0980110334E+01 0 +GRID* 176 0-5.000000000E+004.7008968620E+01+ +* 5.5922357733E+01 0 +GRID* 177 0-5.000000000E+004.2835144637E+01+ +* 5.5890902902E+01 0 +GRID* 178 0-5.000000000E+003.9717010439E+00+ +* 1.1028442634E+01 0 +GRID* 179 0-5.000000000E+003.9729639623E+00+ +* 1.4572077720E+01 0 +GRID* 180 0-5.000000000E+005.9388994694E+01+ +* 4.6035564386E+01 0 +GRID* 181 0-5.000000000E+006.3465851338E+01+ +* 4.6031046831E+01 0 +GRID* 182 0-5.000000000E+005.1211153059E+01+ +* 4.6041675710E+01 0 +GRID* 183 0-5.000000000E+005.5299839154E+01+ +* 4.6039357388E+01 0 +GRID* 184 0-5.000000000E+006.7513021829E+01+ +* 5.0989248017E+01 0 +GRID* 185 0-5.000000000E+006.7515157582E+01+ +* 4.6026854479E+01 0 +GRID* 186 0-5.000000000E+006.3461604090E+01+ +* 5.0993508791E+01 0 +GRID* 187 0-5.000000000E+006.3457447083E+01+ +* 5.5940079475E+01 0 +GRID* 188 0-5.000000000E+006.7510905387E+01+ +* 5.5937681938E+01 0 +GRID* 189 0-5.000000000E+005.9373067231E+01+ +* 5.5942003156E+01 0 +GRID* 190 0-5.000000000E+004.5944108102E+00+ +* 2.5533999190E+01 0 +GRID* 191 0-5.000000000E+009.2507377041E+00+ +* 2.4835122196E+01 0 +GRID* 192 0-5.000000000E+009.8438147377E+00+ +* 2.8549810729E+01 0 +GRID* 193 0-5.000000000E+004.8011338595E+00+ +* 2.9262711243E+01 0 +GRID* 194 0-5.000000000E+005.9380921484E+01+ +* 5.0997772036E+01 0 +GRID* 195 0-5.000000000E+005.1178301095E+01+ +* 5.0996946556E+01 0 +GRID* 196 0-5.000000000E+005.5283703372E+01+ +* 5.1000045689E+01 0 +GRID* 197 0-5.000000000E+005.1149252786E+01+ +* 5.5937387372E+01 0 +GRID* 198 0-5.000000000E+005.5268523157E+01+ +* 5.5942200447E+01 0 +GRID* 199 0-5.000000000E+004.3097432551E+01+ +* 4.6000435376E+01 0 +GRID* 200 0-5.000000000E+004.7134712784E+01+ +* 4.6037037515E+01 0 +GRID* 201 0-5.000000000E+003.0056084717E+01+ +* 4.4465066164E+01 0 +GRID* 202 0-5.000000000E+003.4795603625E+01+ +* 4.5350245837E+01 0 +GRID* 203 0-5.000000000E+003.9065137652E+01+ +* 4.5841725194E+01 0 +GRID* 204 0-5.000000000E+002.7445280290E+01+ +* 3.9447357212E+01 0 +GRID* 205 0-5.000000000E+002.5130745611E+01+ +* 3.5814551797E+01 0 +GRID* 206 0-5.000000000E+004.3067709387E+00+ +* 2.1899179071E+01 0 +GRID* 207 0-5.000000000E+004.5854119095E+00+ +* 3.3052149312E+01 0 +GRID* 208 0-5.000000000E+001.0408676214E+01+ +* 3.2314503766E+01 0 +GRID* 209 0-5.000000000E+004.0281942222E+00+ +* 1.8212479069E+01 0 +GRID* 210 0-5.000000000E+002.5539380545E+01+ +* 4.5550113508E+01 0 +GRID* 211 0-5.000000000E+002.1377788207E+01+ +* 4.6561264295E+01 0 +GRID* 212 0-5.000000000E+002.1815807140E+01+ +* 5.1233535845E+01 0 +GRID* 213 0-5.000000000E+008.5491014793E+00+ +* 2.1210936626E+01 0 +GRID* 214 0-5.000000000E+007.7954383411E+00+ +* 1.7612877969E+01 0 +GRID* 215 0-5.000000000E+001.0381218799E+01+ +* 1.3190337870E+01 0 +GRID* 216 0-5.000000000E+001.1479821165E+01+ +* 1.6586273715E+01 0 +GRID* 217 0-5.000000000E+006.9404112616E+00+ +* 1.4094311450E+01 0 +GRID* 218 0-5.000000000E+004.1909105512E+00+ +* 4.4483623783E+01 0 +GRID* 219 0-5.000000000E+004.2970293940E+00+ +* 4.8453263108E+01 0 +GRID* 220 0-5.000000000E+001.3751268708E+01+ +* 3.1552983633E+01 0 +GRID* 221 0-5.000000000E+001.3958841105E+01+ +* 2.3572553427E+01 0 +GRID* 222 0-5.000000000E+001.5486324956E+01+ +* 2.7041435400E+01 0 +GRID* 223 0-5.000000000E+004.0579883040E+00+ +* 3.6836491960E+01 0 +GRID* 224 0-5.000000000E+009.1932166209E+01+ +* 4.6015631759E+01 0 +GRID* 225 0-5.000000000E+009.5968305069E+01+ +* 4.6014980640E+01 0 +GRID* 226 0-5.000000000E+002.3276271916E+01+ +* 3.2305606592E+01 0 +GRID* 227 0-5.000000000E+008.7869905730E+01+ +* 4.6016563319E+01 0 +GRID* 228 0-5.000000000E+002.1414972922E+01+ +* 2.8827733176E+01 0 +GRID* 229 0-5.000000000E+007.1574102419E+01+ +* 5.0987859150E+01 0 +GRID* 230 0-5.000000000E+007.1573010223E+01+ +* 5.5936801339E+01 0 +GRID* 231 0-5.000000000E+007.5636781770E+01+ +* 5.5934522831E+01 0 +GRID* 232 0-5.000000000E+007.5637327480E+01+ +* 5.0984524027E+01 0 +GRID* 233 0-5.000000000E+007.9714096504E+01+ +* 5.5932797395E+01 0 +GRID* 234 0-5.000000000E+007.9714391488E+01+ +* 5.0982003384E+01 0 +GRID* 235 0-5.000000000E+001.9680545589E+01+ +* 2.5364118146E+01 0 +GRID* 236 0-5.000000000E+001.7987636184E+01+ +* 2.1934866221E+01 0 +GRID* 237 0-5.000000000E+008.3793933627E+01+ +* 4.6017729484E+01 0 +GRID* 238 0-5.000000000E+008.7869831344E+01+ +* 5.0978719974E+01 0 +GRID* 239 0-5.000000000E+008.3793801201E+01+ +* 5.0980100253E+01 0 +GRID* 240 0-5.000000000E+008.7869740953E+01+ +* 5.5930957551E+01 0 +GRID* 241 0-5.000000000E+008.3793682491E+01+ +* 5.5931582326E+01 0 +GRID* 242 0-5.000000000E+009.5968347748E+01+ +* 5.0977911013E+01 0 +GRID* 243 0-5.000000000E+009.5968551832E+01+ +* 5.5931268958E+01 0 +GRID* 244 0-5.000000000E+009.1932201614E+01+ +* 5.5930890555E+01 0 +GRID* 245 0-5.000000000E+009.1932183077E+01+ +* 5.0978024842E+01 0 +GRID* 246 0-5.000000000E+001.7194804782E+01+ +* 3.0539057589E+01 0 +GRID* 247 0-5.000000000E+001.8603797858E+01+ +* 3.4176464665E+01 0 +GRID* 248 0-5.000000000E+001.2697834455E+01+ +* 2.0062966605E+01 0 +GRID* 249 0-5.000000000E+001.6317123075E+01+ +* 1.8592003704E+01 0 +GRID* 250 0-5.000000000E+007.9714675653E+01+ +* 4.6019460461E+01 0 +GRID* 251 0-5.000000000E+007.5637874859E+01+ +* 4.6021807753E+01 0 +GRID* 252 0-5.000000000E+007.1575198131E+01+ +* 4.6025363409E+01 0 +GRID* 253 0-5.000000000E+004.3646207187E+00+ +* 5.2493734364E+01 0 +GRID* 254 0-5.000000000E+008.5311567074E+00+ +* 4.8241101060E+01 0 +GRID* 255 0-5.000000000E+008.7261968294E+00+ +* 5.2343566168E+01 0 +GRID* 256 0-5.000000000E+008.8357886711E+00+ +* 5.6606495042E+01 0 +GRID* 257 0-5.000000000E+004.4201932880E+00+ +* 5.6687422057E+01 0 +GRID* 258 0-5.000000000E+002.4240937228E+01+ +* 4.0958902174E+01 0 +GRID* 259 0-5.000000000E+001.9750114465E+01+ +* 3.8006838560E+01 0 +GRID* 260 0-5.000000000E+001.4792199709E+01+ +* 3.5287813104E+01 0 +GRID* 261 0-5.000000000E+002.0639224613E+01+ +* 4.2145096266E+01 0 +GRID* 262 0-5.000000000E+001.5686003112E+01+ +* 3.9101196029E+01 0 +GRID* 263 0-5.000000000E+001.6470867985E+01+ +* 4.3102758818E+01 0 +GRID* 264 0-5.000000000E+001.7068280210E+01+ +* 4.7295961041E+01 0 +GRID* 265 0-5.000000000E+008.2502923495E+00+ +* 4.4242193434E+01 0 +GRID* 266 0-5.000000000E+007.3856354839E+00+ +* 3.6567038836E+01 0 +GRID* 267 0-5.000000000E+001.1080322586E+01+ +* 3.6048222966E+01 0 +GRID* 268 0-5.000000000E+001.4672953953E+01+ +* 1.5287289605E+01 0 +GRID* 269 0-5.000000000E+007.8817572481E+00+ +* 4.0348421218E+01 0 +GRID* 270 0-5.000000000E+001.1754800011E+01+ +* 3.9859681028E+01 0 +GRID* 271 0-5.000000000E+004.1125081512E+00+ +* 4.0620057734E+01 0 +GRID* 272 0-5.000000000E+001.2310648162E+01+ +* 4.3795021939E+01 0 +GRID* 273 0-5.000000000E+009.3781249413E+00+ +* 9.8441626418E+00 0 +GRID* 274 0-5.000000000E+002.2011752999E+01+ +* 5.6083194573E+01 0 +GRID* 275 0-5.000000000E+001.2770197843E+01+ +* 4.7829696614E+01 0 +GRID* 276 0-5.000000000E+001.3083325242E+01+ +* 5.2070351351E+01 0 +GRID* 277 0-5.000000000E+001.7469402021E+01+ +* 5.1689557303E+01 0 +GRID* 278 0-5.000000000E+001.7672726100E+01+ +* 5.6289588292E+01 0 +GRID* 279 0-5.000000000E+001.3256496106E+01+ +* 5.6471574922E+01 0 +GRID* 280 05.2254857519E-035.6597138560E-15+ +* 1.1200223071E+01 0 +GRID* 281 05.2658229352E-037.4461618604E-15+ +* 1.4735492991E+01 0 +GRID* 282 05.1394950470E-033.8247632052E-15+ +* 7.5689694180E+00 0 +GRID* 283 05.0430063071E-031.9479040733E-15+ +* 3.8547814776E+00 0 +GRID* 284 05.3587535366E-031.1146313285E-14+ +* 2.2057863415E+01 0 +GRID* 285 05.3747609166E-031.2998457965E-14+ +* 2.5723142986E+01 0 +GRID* 286 05.4028904636E-031.4878550597E-14+ +* 2.9443729825E+01 0 +GRID* 287 05.4008342812E-031.6776189467E-14+ +* 3.3199039577E+01 0 +GRID* 288 05.3252820565E-039.2817242372E-15+ +* 1.8367957212E+01 0 +GRID* 289 05.4027604829E-032.2516257635E-14+ +* 4.4558278853E+01 0 +GRID* 290 05.3569058093E-032.4509059034E-14+ +* 4.8501909358E+01 0 +GRID* 291 05.4084787353E-031.8657459583E-14+ +* 3.6921956582E+01 0 +GRID* 292 05.2564979044E-032.8654596369E-14+ +* 5.6705670904E+01 0 +GRID* 293 05.3114795296E-032.6552862920E-14+ +* 5.2546470624E+01 0 +GRID* 294 05.4188574359E-032.0564314441E-14+ +* 4.0695504207E+01 0 +GRID* 295 05.0245207723E-033.5024088424E+00+ +* 0.0000000000E+00 0 +GRID* 296 05.0823261880E-037.0877025377E+00+ +* 0.0000000000E+00 0 +GRID* 297 05.1678079559E-031.1242187412E+01+ +* 2.1515317975E+00 0 +GRID* 298 05.4966796687E-032.9306433243E+01+ +* 3.3439724634E+01 0 +GRID* 299 05.3875079762E-031.6069794700E+01+ +* 1.0513193235E+01 0 +GRID* 300 05.4197991684E-031.7857382416E+01+ +* 1.3609386107E+01 0 +GRID* 301 05.5026041388E-032.7377194902E+01+ +* 3.0098185674E+01 0 +GRID* 302 05.4898124908E-032.5408367861E+01+ +* 2.6688077070E+01 0 +GRID* 303 05.4959988386E-032.1572718486E+01+ +* 2.0044537206E+01 0 +GRID* 304 05.4148212568E-031.9699858840E+01+ +* 1.6800649014E+01 0 +GRID* 305 05.5066523055E-032.3469294530E+01+ +* 2.3329503407E+01 0 +GRID* 306 05.3170301457E-031.4377184979E+01+ +* 7.5815070818E+00 0 +GRID* 307 05.2390163530E-031.2731479052E+01+ +* 4.7310606873E+00 0 +GRID* 308 05.5998550378E-033.6137654296E+01+ +* 4.0357581925E+01 0 +GRID* 309 05.5177899784E-033.3533438340E+01+ +* 3.9045245778E+01 0 +GRID* 310 05.4395328609E-033.0288232762E+01+ +* 6.0969398499E+01 0 +GRID* 311 05.4951315654E-032.6219931544E+01+ +* 6.0969398499E+01 0 +GRID* 312 05.4971397712E-033.4394852291E+01+ +* 6.0969398499E+01 0 +GRID* 313 05.5007013505E-033.8587334368E+01+ +* 6.0969398499E+01 0 +GRID* 314 05.4554501964E-034.2802622182E+01+ +* 6.0969398499E+01 0 +GRID* 315 05.4617495710E-034.6988435748E+01+ +* 6.0969398499E+01 0 +GRID* 316 05.5066690895E-036.3455751437E+01+ +* 6.0969398499E+01 0 +GRID* 317 05.5045119972E-036.7510070172E+01+ +* 6.0969398499E+01 0 +GRID* 318 05.5128549463E-035.9370045419E+01+ +* 6.0969398499E+01 0 +GRID* 319 05.4624905952E-035.5262296406E+01+ +* 6.0969398499E+01 0 +GRID* 320 05.4676802500E-035.1137468923E+01+ +* 6.0969398499E+01 0 +GRID* 321 05.4539606750E-037.1572616995E+01+ +* 6.0969398499E+01 0 +GRID* 322 05.4485721924E-037.5636544474E+01+ +* 6.0969398499E+01 0 +GRID* 323 05.4316444717E-037.9714001137E+01+ +* 6.0969398499E+01 0 +GRID* 324 05.4049192368E-038.3793634761E+01+ +* 6.0969398499E+01 0 +GRID* 325 05.3930559039E-038.7869675396E+01+ +* 6.0969398499E+01 0 +GRID* 326 05.2428199584E-039.5968774637E+01+ +* 6.0969398499E+01 0 +GRID* 327 05.3155463860E-039.1932247737E+01+ +* 6.0969398499E+01 0 +GRID* 328 05.2381958383E-034.4264876379E+00+ +* 6.0969398499E+01 0 +GRID* 329 05.3123510316E-038.8578145490E+00+ +* 6.0969398499E+01 0 +GRID* 330 05.4845379400E-032.2030434585E+01+ +* 6.0969398499E+01 0 +GRID* 331 05.4714560238E-031.7702566532E+01+ +* 6.0969398499E+01 0 +GRID* 332 05.3789696702E-031.3292188735E+01+ +* 6.0969398499E+01 0 +GRID* 333 05.7628527603E-035.9392148152E+01+ +* 4.0969398499E+01 0 +GRID* 334 05.7602670734E-036.3467680495E+01+ +* 4.0969398499E+01 0 +GRID* 335 05.7549397954E-035.5307046863E+01+ +* 4.0969398499E+01 0 +GRID* 336 05.7620110452E-036.7516126073E+01+ +* 4.0969398499E+01 0 +GRID* 337 05.7328846864E-035.1226065890E+01+ +* 4.0969398499E+01 0 +GRID* 338 05.7089741275E-034.7164402549E+01+ +* 4.0969398499E+01 0 +GRID* 339 05.6390662531E-034.3182037549E+01+ +* 4.0969398499E+01 0 +GRID* 340 05.5322811782E-039.1932185183E+01+ +* 4.0969398499E+01 0 +GRID* 341 05.4160570910E-039.5968227119E+01+ +* 4.0969398499E+01 0 +GRID* 342 05.6959347775E-038.3793979843E+01+ +* 4.0969398499E+01 0 +GRID* 343 05.6239673368E-038.7869920872E+01+ +* 4.0969398499E+01 0 +GRID* 344 05.7299466726E-037.9714798164E+01+ +* 4.0969398499E+01 0 +GRID* 345 05.7463512599E-037.5638083432E+01+ +* 4.0969398499E+01 0 +GRID* 346 05.7700215971E-037.1575579651E+01+ +* 4.0969398499E+01 0 +GRID* 347 05.3550421035E-031.0000000000E+02+ +* 4.6014888910E+01 0 +GRID* 348 05.3259000957E-031.0000000000E+02+ +* 5.0977906001E+01 0 +GRID* 349 05.2651214887E-031.0000000000E+02+ +* 5.5931422411E+01 0 +GRID* 350 05.0000000000E+007.4113330594E+00+ +* 3.3940645029E+00 0 +GRID* 351 05.0000000000E+004.2835126991E+01+ +* 5.5890944626E+01 0 +GRID* 352 05.0000000000E+004.2935764189E+01+ +* 5.0930242467E+01 0 +GRID* 353 05.0000000000E+004.7008955624E+01+ +* 5.5922400053E+01 0 +GRID* 354 05.0000000000E+004.7064558562E+01+ +* 5.0980119969E+01 0 +GRID* 355 05.0000000000E+003.8366602877E+00+ +* 7.4287152518E+00 0 +GRID* 356 05.0000000000E+003.5856974341E+00+ +* 3.7873457890E+00 0 +GRID* 357 05.0000000000E+003.0261964422E+01+ +* 5.5797650755E+01 0 +GRID* 358 05.0000000000E+003.4382581526E+01+ +* 5.5796679046E+01 0 +GRID* 359 05.0000000000E+003.9718266944E+00+ +* 1.1028509044E+01 0 +GRID* 360 05.0000000000E+009.3785909726E+00+ +* 9.8439289701E+00 0 +GRID* 361 05.0000000000E+003.9729721919E+00+ +* 1.4572141030E+01 0 +GRID* 362 05.0000000000E+006.9403566321E+00+ +* 1.4094366971E+01 0 +GRID* 363 05.0000000000E+003.8606774847E+01+ +* 5.5841348853E+01 0 +GRID* 364 05.0000000000E+003.8738418782E+01+ +* 5.0814657207E+01 0 +GRID* 365 05.0000000000E+002.6202817093E+01+ +* 5.5900823207E+01 0 +GRID* 366 05.0000000000E+005.9388987152E+01+ +* 4.6035527194E+01 0 +GRID* 367 05.0000000000E+006.3465847580E+01+ +* 4.6031004930E+01 0 +GRID* 368 05.0000000000E+005.5299836544E+01+ +* 4.6039327646E+01 0 +GRID* 369 05.0000000000E+006.7515155196E+01+ +* 4.6026813591E+01 0 +GRID* 370 05.0000000000E+005.1178299379E+01+ +* 5.0996950800E+01 0 +GRID* 371 05.0000000000E+005.1149247397E+01+ +* 5.5937427010E+01 0 +GRID* 372 05.0000000000E+005.5268525166E+01+ +* 5.5942238655E+01 0 +GRID* 373 05.0000000000E+005.5283704844E+01+ +* 5.1000047157E+01 0 +GRID* 374 05.0000000000E+005.1211159137E+01+ +* 4.6041648472E+01 0 +GRID* 375 05.0000000000E+006.3457453995E+01+ +* 5.5940119114E+01 0 +GRID* 376 05.0000000000E+006.7510910272E+01+ +* 5.5937721338E+01 0 +GRID* 377 05.0000000000E+005.9373068539E+01+ +* 5.5942041900E+01 0 +GRID* 378 05.0000000000E+005.9380922316E+01+ +* 5.0997772071E+01 0 +GRID* 379 05.0000000000E+006.3461605101E+01+ +* 5.0993509944E+01 0 +GRID* 380 05.0000000000E+006.7513020038E+01+ +* 5.0989247899E+01 0 +GRID* 381 05.0000000000E+004.7134721663E+01+ +* 4.6037028277E+01 0 +GRID* 382 05.0000000000E+004.3097398515E+01+ +* 4.6000476265E+01 0 +GRID* 383 05.0000000000E+003.9065052534E+01+ +* 4.5841801191E+01 0 +GRID* 384 05.0000000000E+002.7445107621E+01+ +* 3.9446867269E+01 0 +GRID* 385 05.0000000000E+002.5130714920E+01+ +* 3.5814269748E+01 0 +GRID* 386 05.0000000000E+003.0056053841E+01+ +* 4.4465002628E+01 0 +GRID* 387 05.0000000000E+003.4795410630E+01+ +* 4.5350228025E+01 0 +GRID* 388 05.0000000000E+003.4425667459E+01+ +* 5.0638496245E+01 0 +GRID* 389 05.0000000000E+003.0203964212E+01+ +* 5.0566731657E+01 0 +GRID* 390 05.0000000000E+004.8011681810E+00+ +* 2.9262710076E+01 0 +GRID* 391 05.0000000000E+004.5944280103E+00+ +* 2.5533990260E+01 0 +GRID* 392 05.0000000000E+009.2507564628E+00+ +* 2.4835127113E+01 0 +GRID* 393 05.0000000000E+009.8438230211E+00+ +* 2.8549804947E+01 0 +GRID* 394 05.0000000000E+004.5854585543E+00+ +* 3.3052142140E+01 0 +GRID* 395 05.0000000000E+001.0408679431E+01+ +* 3.2314499236E+01 0 +GRID* 396 05.0000000000E+008.5491415315E+00+ +* 2.1211000581E+01 0 +GRID* 397 05.0000000000E+004.3067665318E+00+ +* 2.1899179201E+01 0 +GRID* 398 05.0000000000E+002.6046185981E+01+ +* 5.0799615324E+01 0 +GRID* 399 05.0000000000E+004.0281999484E+00+ +* 1.8212520922E+01 0 +GRID* 400 05.0000000000E+007.7954339287E+00+ +* 1.7612910514E+01 0 +GRID* 401 05.0000000000E+001.1479883680E+01+ +* 1.6586408787E+01 0 +GRID* 402 05.0000000000E+001.0381261056E+01+ +* 1.3190266404E+01 0 +GRID* 403 05.0000000000E+001.2697865566E+01+ +* 2.0062992563E+01 0 +GRID* 404 05.0000000000E+001.3958899880E+01+ +* 2.3572551647E+01 0 +GRID* 405 05.0000000000E+001.6317193205E+01+ +* 1.8592045733E+01 0 +GRID* 406 05.0000000000E+001.4672741051E+01+ +* 1.5287659399E+01 0 +GRID* 407 05.0000000000E+001.1080322048E+01+ +* 3.6048228510E+01 0 +GRID* 408 05.0000000000E+007.3856214512E+00+ +* 3.6567036284E+01 0 +GRID* 409 05.0000000000E+002.3276326697E+01+ +* 3.2305584427E+01 0 +GRID* 410 05.0000000000E+009.1932167638E+01+ +* 4.6015597608E+01 0 +GRID* 411 05.0000000000E+009.5968301975E+01+ +* 4.6014949588E+01 0 +GRID* 412 05.0000000000E+002.1414903547E+01+ +* 2.8827775205E+01 0 +GRID* 413 05.0000000000E+008.3793931702E+01+ +* 4.6017694379E+01 0 +GRID* 414 05.0000000000E+008.7869906920E+01+ +* 4.6016526963E+01 0 +GRID* 415 05.0000000000E+007.1573007002E+01+ +* 5.5936838117E+01 0 +GRID* 416 05.0000000000E+007.1574107304E+01+ +* 5.0987860683E+01 0 +GRID* 417 05.0000000000E+001.7987682711E+01+ +* 2.1934803882E+01 0 +GRID* 418 05.0000000000E+007.5636775211E+01+ +* 5.5934563485E+01 0 +GRID* 419 05.0000000000E+007.5637325362E+01+ +* 5.0984525461E+01 0 +GRID* 420 05.0000000000E+008.3793800311E+01+ +* 5.0980098064E+01 0 +GRID* 421 05.0000000000E+008.7869829166E+01+ +* 5.0978720162E+01 0 +GRID* 422 05.0000000000E+007.9714392142E+01+ +* 5.0982001912E+01 0 +GRID* 423 05.0000000000E+008.3793684812E+01+ +* 5.5931617703E+01 0 +GRID* 424 05.0000000000E+007.9714098187E+01+ +* 5.5932837157E+01 0 +GRID* 425 05.0000000000E+008.7869735646E+01+ +* 5.5930992392E+01 0 +GRID* 426 05.0000000000E+009.1932183314E+01+ +* 5.0978026380E+01 0 +GRID* 427 05.0000000000E+001.9680506791E+01+ +* 2.5364068443E+01 0 +GRID* 428 05.0000000000E+001.5486344273E+01+ +* 2.7041424366E+01 0 +GRID* 429 05.0000000000E+001.7194800368E+01+ +* 3.0539074755E+01 0 +GRID* 430 05.0000000000E+001.3751252504E+01+ +* 3.1553005538E+01 0 +GRID* 431 05.0000000000E+009.5968347869E+01+ +* 5.0977912665E+01 0 +GRID* 432 05.0000000000E+001.8603824805E+01+ +* 3.4176488932E+01 0 +GRID* 433 05.0000000000E+009.1932200421E+01+ +* 5.5930923222E+01 0 +GRID* 434 05.0000000000E+009.5968551480E+01+ +* 5.5931293994E+01 0 +GRID* 435 05.0000000000E+004.4201902522E+00+ +* 5.6687431366E+01 0 +GRID* 436 05.0000000000E+007.9714672836E+01+ +* 4.6019422200E+01 0 +GRID* 437 05.0000000000E+007.5637880937E+01+ +* 4.6021767105E+01 0 +GRID* 438 05.0000000000E+007.1575208023E+01+ +* 4.6025326634E+01 0 +GRID* 439 05.0000000000E+008.8357899269E+00+ +* 5.6606501491E+01 0 +GRID* 440 05.0000000000E+004.3780153764E+00+ +* 5.2506440997E+01 0 +GRID* 441 05.0000000000E+004.2970264175E+00+ +* 4.8453256920E+01 0 +GRID* 442 05.0000000000E+008.7261976681E+00+ +* 5.2343566537E+01 0 +GRID* 443 05.0000000000E+008.5311506319E+00+ +* 4.8241092428E+01 0 +GRID* 444 05.0000000000E+001.4792197561E+01+ +* 3.5287825562E+01 0 +GRID* 445 05.0000000000E+002.5539378040E+01+ +* 4.5550076076E+01 0 +GRID* 446 05.0000000000E+002.4240883522E+01+ +* 4.0958697372E+01 0 +GRID* 447 05.0000000000E+001.9750106774E+01+ +* 3.8006793537E+01 0 +GRID* 448 05.0000000000E+002.0639219604E+01+ +* 4.2145053216E+01 0 +GRID* 449 05.0000000000E+001.5686001412E+01+ +* 3.9101186611E+01 0 +GRID* 450 05.0000000000E+001.6470874003E+01+ +* 4.3102751904E+01 0 +GRID* 451 05.0000000000E+002.1377789165E+01+ +* 4.6561255261E+01 0 +GRID* 452 05.0000000000E+001.7068287963E+01+ +* 4.7295958071E+01 0 +GRID* 453 05.0000000000E+001.2310657286E+01+ +* 4.3795009194E+01 0 +GRID* 454 05.0000000000E+001.1754792922E+01+ +* 3.9859669505E+01 0 +GRID* 455 05.0000000000E+004.1909059657E+00+ +* 4.4483620900E+01 0 +GRID* 456 05.0000000000E+008.2502863932E+00+ +* 4.4242189988E+01 0 +GRID* 457 05.0000000000E+007.8817557025E+00+ +* 4.0348414643E+01 0 +GRID* 458 05.0000000000E+004.1125026121E+00+ +* 4.0620056315E+01 0 +GRID* 459 05.0000000000E+004.0579676845E+00+ +* 3.6836474685E+01 0 +GRID* 460 05.0000000000E+001.2770196775E+01+ +* 4.7829692929E+01 0 +GRID* 461 05.0000000000E+001.7469402323E+01+ +* 5.1689557076E+01 0 +GRID* 462 05.0000000000E+001.3083320656E+01+ +* 5.2070353747E+01 0 +GRID* 463 05.0000000000E+008.3025201051E+00+ +* 6.6601957597E+00 0 +GRID* 464 05.0000000000E+001.3256491402E+01+ +* 5.6471585662E+01 0 +GRID* 465 05.0000000000E+001.7672719846E+01+ +* 5.6289606483E+01 0 +GRID* 466 05.0000000000E+002.1815804283E+01+ +* 5.1233531505E+01 0 +GRID* 467 05.0000000000E+002.2011741917E+01+ +* 5.6083219559E+01 0 +GRID* 468 05.1550769091E-033.8367229898E+00+ +* 7.4285256472E+00 0 +GRID* 469 05.2625962377E-038.3017535866E+00+ +* 6.6582454350E+00 0 +GRID* 470 05.1107794821E-037.4104022887E+00+ +* 3.3885932763E+00 0 +GRID* 471 05.0520875335E-033.5858216484E+00+ +* 3.7861739595E+00 0 +GRID* 472 05.6047753274E-032.6202790686E+01+ +* 5.5900804280E+01 0 +GRID* 473 05.6003981113E-033.0282448972E+01+ +* 5.5810725539E+01 0 +GRID* 474 05.6890935481E-032.6046262869E+01+ +* 5.0799635796E+01 0 +GRID* 475 05.6733094931E-033.0203979353E+01+ +* 5.0566750852E+01 0 +GRID* 476 05.5937223911E-033.4382564122E+01+ +* 5.5796651690E+01 0 +GRID* 477 05.6678742945E-033.4425678129E+01+ +* 5.0638459889E+01 0 +GRID* 478 05.5945754826E-033.8606812756E+01+ +* 5.5841323434E+01 0 +GRID* 479 05.6713574409E-033.8738472308E+01+ +* 5.0814618064E+01 0 +GRID* 480 05.6960151374E-034.7064579842E+01+ +* 5.0980092069E+01 0 +GRID* 481 05.6860090077E-034.2935754355E+01+ +* 5.0930214716E+01 0 +GRID* 482 05.6017950952E-034.7008975175E+01+ +* 5.5922390102E+01 0 +GRID* 483 05.6002267480E-034.2835020121E+01+ +* 5.5890940605E+01 0 +GRID* 484 05.2504033625E-033.9720045527E+00+ +* 1.1028654001E+01 0 +GRID* 485 05.3485088825E-033.9729750507E+00+ +* 1.4571956492E+01 0 +GRID* 486 05.3212621093E-039.3775208876E+00+ +* 9.8436756492E+00 0 +GRID* 487 05.3981260240E-036.9403065053E+00+ +* 1.4094205952E+01 0 +GRID* 488 05.7547070861E-035.9388978332E+01+ +* 4.6035567370E+01 0 +GRID* 489 05.7512537420E-036.3465841025E+01+ +* 4.6031064716E+01 0 +GRID* 490 05.7486721158E-035.5299829646E+01+ +* 4.6039406207E+01 0 +GRID* 491 05.7523042738E-036.7515164495E+01+ +* 4.6026917246E+01 0 +GRID* 492 05.6059040904E-035.1149259789E+01+ +* 5.5937407522E+01 0 +GRID* 493 05.7097689092E-035.1178301451E+01+ +* 5.0996935362E+01 0 +GRID* 494 05.6095586002E-035.5268546833E+01+ +* 5.5942233174E+01 0 +GRID* 495 05.7143957198E-035.5283698826E+01+ +* 5.1000035187E+01 0 +GRID* 496 05.7358161390E-035.1211147664E+01+ +* 4.6041751889E+01 0 +GRID* 497 05.7136022329E-036.7513025642E+01+ +* 5.0989224409E+01 0 +GRID* 498 05.7156250656E-036.3461599380E+01+ +* 5.0993496010E+01 0 +GRID* 499 05.6048908114E-036.3457451612E+01+ +* 5.5940110533E+01 0 +GRID* 500 05.6086272776E-036.7525611267E+01+ +* 5.5939623802E+01 0 +GRID* 501 05.6106091321E-035.9373059749E+01+ +* 5.5942052572E+01 0 +GRID* 502 05.7184041321E-035.9380913406E+01+ +* 5.0997757984E+01 0 +GRID* 503 05.4620967090E-034.5943829768E+00+ +* 2.5534042829E+01 0 +GRID* 504 05.5667475641E-039.2507494301E+00+ +* 2.4835116849E+01 0 +GRID* 505 05.6060307503E-039.8438320518E+00+ +* 2.8549810851E+01 0 +GRID* 506 05.4992155015E-034.8012002163E+00+ +* 2.9262746962E+01 0 +GRID* 507 05.6825072348E-034.3097345825E+01+ +* 4.6000696626E+01 0 +GRID* 508 05.7115794003E-034.7134705869E+01+ +* 4.6037199584E+01 0 +GRID* 509 05.6627445698E-033.9064875032E+01+ +* 4.5841813532E+01 0 +GRID* 510 05.5936106324E-032.5130822265E+01+ +* 3.5815507623E+01 0 +GRID* 511 05.6385525346E-032.7446073303E+01+ +* 3.9448950868E+01 0 +GRID* 512 05.6876555860E-033.0056020642E+01+ +* 4.4465046060E+01 0 +GRID* 513 05.6495495915E-033.4795717711E+01+ +* 4.5350212645E+01 0 +GRID* 514 05.6277678192E-031.0408682293E+01+ +* 3.2314491252E+01 0 +GRID* 515 05.5026576698E-034.5854339653E+00+ +* 3.3052170033E+01 0 +GRID* 516 05.3826846957E-034.3068103989E+00+ +* 2.1899351933E+01 0 +GRID* 517 05.5249349058E-038.5490462244E+00+ +* 2.1210898541E+01 0 +GRID* 518 05.4125279963E-034.0282607430E+00+ +* 1.8212498382E+01 0 +GRID* 519 05.7408378303E-032.1377781295E+01+ +* 4.6561273483E+01 0 +GRID* 520 05.7255194366E-032.5539463931E+01+ +* 4.5550085616E+01 0 +GRID* 521 05.6986824453E-032.1815765300E+01+ +* 5.1233534334E+01 0 +GRID* 522 05.4652892828E-037.7954555657E+00+ +* 1.7612890251E+01 0 +GRID* 523 05.4329723895E-031.0381167179E+01+ +* 1.3190188504E+01 0 +GRID* 524 05.4877751350E-031.1479518941E+01+ +* 1.6585772328E+01 0 +GRID* 525 05.3839326680E-034.2970341642E+00+ +* 4.8453286958E+01 0 +GRID* 526 05.4978445947E-034.1909062021E+00+ +* 4.4483635303E+01 0 +GRID* 527 05.6774482906E-031.3751273762E+01+ +* 3.1552957723E+01 0 +GRID* 528 05.5721194327E-031.3958770759E+01+ +* 2.3572477975E+01 0 +GRID* 529 05.5380032241E-031.2697763285E+01+ +* 2.0062946938E+01 0 +GRID* 530 05.4934897304E-031.6316793136E+01+ +* 1.8592018612E+01 0 +GRID* 531 05.4656022072E-031.4673107735E+01+ +* 1.5284522406E+01 0 +GRID* 532 05.6769006729E-031.1080298207E+01+ +* 3.6048223923E+01 0 +GRID* 533 05.5730991840E-037.3856433836E+00+ +* 3.6567068647E+01 0 +GRID* 534 05.6366340101E-031.5486243300E+01+ +* 2.7041252242E+01 0 +GRID* 535 05.6121514022E-032.3275959291E+01+ +* 3.2305515523E+01 0 +GRID* 536 05.4453068256E-034.0580437382E+00+ +* 3.6836618568E+01 0 +GRID* 537 05.3816825926E-039.5968102416E+01+ +* 4.6015005139E+01 0 +GRID* 538 05.5084653974E-039.1932197382E+01+ +* 4.6015674022E+01 0 +GRID* 539 05.5960432470E-032.1414864205E+01+ +* 2.8827618653E+01 0 +GRID* 540 05.6861058652E-038.3793950606E+01+ +* 4.6017789212E+01 0 +GRID* 541 05.6139842451E-038.7869880934E+01+ +* 4.6016609875E+01 0 +GRID* 542 05.7083048701E-037.1574091868E+01+ +* 5.0987837342E+01 0 +GRID* 543 05.6048982620E-037.1573005931E+01+ +* 5.5936834602E+01 0 +GRID* 544 05.5945792079E-037.5636784540E+01+ +* 5.5934552221E+01 0 +GRID* 545 05.7001613855E-037.5637340741E+01+ +* 5.0984511574E+01 0 +GRID* 546 05.5797339261E-037.9714086297E+01+ +* 5.5932827442E+01 0 +GRID* 547 05.6873761892E-037.9714380192E+01+ +* 5.0981983451E+01 0 +GRID* 548 05.5485904992E-031.7987787940E+01+ +* 2.1934500434E+01 0 +GRID* 549 05.5411101162E-031.9679883443E+01+ +* 2.5362807864E+01 0 +GRID* 550 05.6508459926E-038.3793818067E+01+ +* 5.0980072168E+01 0 +GRID* 551 05.5818126380E-038.7869801510E+01+ +* 5.0978703527E+01 0 +GRID* 552 05.5443660200E-038.3793699245E+01+ +* 5.5931622325E+01 0 +GRID* 553 05.4789387465E-038.7869714130E+01+ +* 5.5930988638E+01 0 +GRID* 554 05.3527594388E-039.5968141040E+01+ +* 5.0977896651E+01 0 +GRID* 555 05.3887829959E-039.1932231118E+01+ +* 5.5930916964E+01 0 +GRID* 556 05.2853614867E-039.5968340715E+01+ +* 5.5931287377E+01 0 +GRID* 557 05.4791883409E-039.1932210972E+01+ +* 5.0978007173E+01 0 +GRID* 558 05.6860872388E-031.7194844596E+01+ +* 3.0538994173E+01 0 +GRID* 559 05.6899354637E-031.8603767641E+01+ +* 3.4176450188E+01 0 +GRID* 560 05.3585112870E-034.4201743954E+00+ +* 5.6687429695E+01 0 +GRID* 561 05.7232842624E-037.9714659202E+01+ +* 4.6019494501E+01 0 +GRID* 562 05.7379209280E-037.5637893664E+01+ +* 4.6021849063E+01 0 +GRID* 563 05.7458893239E-037.1575189487E+01+ +* 4.6025395361E+01 0 +GRID* 564 05.4649800837E-038.8357642949E+00+ +* 5.6606492905E+01 0 +GRID* 565 05.3361073911E-034.3709826668E+00+ +* 5.2513611985E+01 0 +GRID* 566 05.5569724023E-038.7262190640E+00+ +* 5.2343596337E+01 0 +GRID* 567 05.5204384804E-038.5311558750E+00+ +* 4.8241130335E+01 0 +GRID* 568 05.7195924997E-031.4741868965E+01+ +* 3.5286438267E+01 0 +GRID* 569 05.7086103439E-032.4241030508E+01+ +* 4.0959262785E+01 0 +GRID* 570 05.7153158665E-031.9750082754E+01+ +* 3.8006872128E+01 0 +GRID* 571 05.7509035647E-032.0639190577E+01+ +* 4.2145132940E+01 0 +GRID* 572 05.7377085865E-031.5692107044E+01+ +* 3.9120924975E+01 0 +GRID* 573 05.7505869150E-031.6470870129E+01+ +* 4.3102789637E+01 0 +GRID* 574 05.7305299520E-031.7068269364E+01+ +* 4.7295971957E+01 0 +GRID* 575 05.5695154548E-038.2502649334E+00+ +* 4.4242205274E+01 0 +GRID* 576 05.6951285183E-031.1754797033E+01+ +* 3.9859646525E+01 0 +GRID* 577 05.6887582719E-031.2310636780E+01+ +* 4.3795009431E+01 0 +GRID* 578 05.5850797176E-037.8817822245E+00+ +* 4.0348488342E+01 0 +GRID* 579 05.5169218063E-034.1124886625E+00+ +* 4.0620039742E+01 0 +GRID* 580 05.7049036801E-031.2770199157E+01+ +* 4.7829676953E+01 0 +GRID* 581 05.6951545954E-031.7469400235E+01+ +* 5.1689567385E+01 0 +GRID* 582 05.6487300277E-031.3083334423E+01+ +* 5.2070340870E+01 0 +GRID* 583 05.5414155900E-031.3256467796E+01+ +* 5.6471588282E+01 0 +GRID* 584 05.5843532860E-031.7672695287E+01+ +* 5.6289603798E+01 0 +GRID* 585 05.6011506200E-032.2011729457E+01+ +* 5.6083202450E+01 0 +$* +$* ELEMENT CARDS +$* +$* Mesh Collector: Solid(1) +$* Mesh: 3d_mesh(1) +CHEXA 1 1 469 163 165 470 468 162+ ++ 164 471 +CHEXA 2 1 166 167 168 169 473 472+ ++ 474 475 +CHEXA 3 1 164 471 295 30 165 470+ ++ 296 31 +CHEXA 4 1 170 477 479 172 171 476+ ++ 478 173 +CHEXA 5 1 166 169 170 171 473 475+ ++ 477 476 +CHEXA 6 1 350 470 297 109 107 296+ ++ 87 10 +CHEXA 7 1 174 175 176 177 481 480+ ++ 482 483 +CHEXA 8 1 178 484 485 179 15 280+ ++ 281 16 +CHEXA 9 1 483 177 174 481 478 173+ ++ 172 479 +CHEXA 10 1 351 483 482 353 352 481+ ++ 480 354 +CHEXA 11 1 45 310 311 46 166 473+ ++ 472 167 +CHEXA 12 1 92 355 356 93 282 468+ ++ 471 283 +CHEXA 13 1 171 166 473 476 47 45+ ++ 310 312 +CHEXA 14 1 473 357 358 476 310 122+ ++ 123 312 +CHEXA 15 1 360 359 361 362 486 484+ ++ 485 487 +CHEXA 16 1 478 363 364 479 483 351+ ++ 352 481 +CHEXA 17 1 472 311 124 365 473 310+ ++ 122 357 +CHEXA 18 1 478 363 351 483 313 125+ ++ 126 314 +CHEXA 19 1 482 483 314 315 176 177+ ++ 48 49 +CHEXA 20 1 366 488 489 367 145 333+ ++ 334 146 +CHEXA 21 1 490 368 147 335 488 366+ ++ 145 333 +CHEXA 22 1 333 68 180 488 334 69+ ++ 181 489 +CHEXA 23 1 336 148 369 491 334 146+ ++ 367 489 +CHEXA 24 1 371 492 494 372 370 493+ ++ 495 373 +CHEXA 25 1 374 496 490 368 149 337+ ++ 335 147 +CHEXA 26 1 493 370 374 496 495 373+ ++ 368 490 +CHEXA 27 1 492 482 480 493 371 353+ ++ 354 370 +CHEXA 28 1 71 335 490 183 70 337+ ++ 496 182 +CHEXA 29 1 490 183 180 488 335 71+ ++ 68 333 +CHEXA 30 1 491 185 181 489 497 184+ ++ 186 498 +CHEXA 31 1 314 313 50 48 483 478+ ++ 173 177 +CHEXA 32 1 375 127 128 376 499 316+ ++ 317 500 +CHEXA 33 1 123 125 363 358 312 313+ ++ 478 476 +CHEXA 34 1 187 188 51 52 499 500+ ++ 317 316 +CHEXA 35 1 499 187 189 501 316 52+ ++ 53 318 +CHEXA 36 1 499 316 318 501 375 127+ ++ 129 377 +CHEXA 37 1 377 501 499 375 378 502+ ++ 498 379 +CHEXA 38 1 190 191 192 193 503 504+ ++ 505 506 +CHEXA 39 1 499 187 188 500 498 186+ ++ 184 497 +CHEXA 40 1 367 369 491 489 379 380+ ++ 497 498 +CHEXA 41 1 489 367 379 498 488 366+ ++ 378 502 +CHEXA 42 1 186 498 499 187 194 502+ ++ 501 189 +CHEXA 43 1 502 378 377 501 495 373+ ++ 372 494 +CHEXA 44 1 319 318 129 130 494 501+ ++ 377 372 +CHEXA 45 1 315 132 353 482 320 131+ ++ 371 492 +CHEXA 46 1 131 320 319 130 371 492+ ++ 494 372 +CHEXA 47 1 195 493 496 182 196 495+ ++ 490 183 +CHEXA 48 1 492 493 480 482 197 195+ ++ 175 176 +CHEXA 49 1 198 494 495 196 189 501+ ++ 502 194 +CHEXA 50 1 197 54 320 492 198 55+ ++ 319 494 +CHEXA 51 1 199 200 175 174 507 508+ ++ 480 481 +CHEXA 52 1 374 149 150 381 496 337+ ++ 338 508 +CHEXA 53 1 509 383 12 89 507 382+ ++ 151 339 +CHEXA 54 1 507 382 151 339 508 381+ ++ 150 338 +CHEXA 55 1 197 492 493 195 198 494+ ++ 495 196 +CHEXA 56 1 509 507 481 479 383 382+ ++ 352 364 +CHEXA 57 1 338 337 70 72 508 496+ ++ 182 200 +CHEXA 58 1 180 488 502 194 183 490+ ++ 495 196 +CHEXA 59 1 370 354 480 493 374 381+ ++ 508 496 +CHEXA 60 1 189 501 318 53 198 494+ ++ 319 55 +CHEXA 61 1 384 511 88 11 385 510+ ++ 298 110 +CHEXA 62 1 49 315 482 176 54 320+ ++ 492 197 +CHEXA 63 1 201 512 513 202 169 475+ ++ 477 170 +CHEXA 64 1 339 507 508 338 73 199+ ++ 200 72 +CHEXA 65 1 481 352 382 507 480 354+ ++ 381 508 +CHEXA 66 1 493 496 508 480 195 182+ ++ 200 175 +CHEXA 67 1 481 174 199 507 479 172+ ++ 203 509 +CHEXA 68 1 73 199 203 5 339 507+ ++ 509 89 +CHEXA 69 1 489 181 180 488 498 186+ ++ 194 502 +CHEXA 70 1 497 498 379 380 500 499+ ++ 375 376 +CHEXA 71 1 32 205 510 298 4 204+ ++ 511 88 +CHEXA 72 1 509 203 5 89 513 202+ ++ 43 308 +CHEXA 73 1 309 512 201 44 88 511+ ++ 204 4 +CHEXA 74 1 512 386 384 511 309 120+ ++ 11 88 +CHEXA 75 1 509 513 477 479 203 202+ ++ 170 172 +CHEXA 76 1 389 386 512 475 388 387+ ++ 513 477 +CHEXA 77 1 334 69 181 489 336 74+ ++ 185 491 +CHEXA 78 1 509 513 308 89 383 387+ ++ 121 12 +CHEXA 79 1 44 43 202 201 309 308+ ++ 513 512 +CHEXA 80 1 171 47 312 476 173 50+ ++ 313 478 +CHEXA 81 1 126 314 315 132 351 483+ ++ 482 353 +CHEXA 82 1 509 479 477 513 383 364+ ++ 388 387 +CHEXA 83 1 390 506 505 393 391 503+ ++ 504 392 +CHEXA 84 1 390 393 505 506 394 395+ ++ 514 515 +CHEXA 85 1 512 386 120 309 513 387+ ++ 121 308 +CHEXA 86 1 516 503 391 397 517 504+ ++ 392 396 +CHEXA 87 1 95 285 503 391 94 284+ ++ 516 397 +CHEXA 88 1 391 95 96 390 503 285+ ++ 286 506 +CHEXA 89 1 516 503 190 206 284 285+ ++ 17 18 +CHEXA 90 1 389 475 474 398 357 473+ ++ 472 365 +CHEXA 91 1 515 287 286 506 394 97+ ++ 96 390 +CHEXA 92 1 192 193 506 505 208 207+ ++ 515 514 +CHEXA 93 1 193 506 515 207 19 286+ ++ 287 20 +CHEXA 94 1 286 19 193 506 285 17+ ++ 190 503 +CHEXA 95 1 281 485 179 16 288 518+ ++ 209 21 +CHEXA 96 1 206 516 284 18 209 518+ ++ 288 21 +CHEXA 97 1 210 520 474 168 211 519+ ++ 521 212 +CHEXA 98 1 206 516 518 209 213 517+ ++ 522 214 +CHEXA 99 1 487 523 215 217 522 524+ ++ 216 214 +CHEXA 100 1 179 485 487 217 209 518+ ++ 522 214 +CHEXA 101 1 190 503 516 206 191 504+ ++ 517 213 +CHEXA 102 1 516 397 399 518 517 396+ ++ 400 522 +CHEXA 103 1 288 284 94 98 518 516+ ++ 397 399 +CHEXA 104 1 362 400 522 487 361 399+ ++ 518 485 +CHEXA 105 1 288 98 99 281 518 399+ ++ 361 485 +CHEXA 106 1 522 400 362 487 524 401+ ++ 402 523 +CHEXA 107 1 290 22 219 525 289 23+ ++ 218 526 +CPENTA 108 1 192 220 208 505 527 514 +CHEXA 109 1 528 529 403 404 504 517+ ++ 396 392 +CHEXA 110 1 401 403 529 524 400 396+ ++ 517 522 +CHEXA 111 1 405 530 531 406 403 529+ ++ 524 401 +CHEXA 112 1 515 514 532 533 394 395+ ++ 407 408 +CPENTA 113 1 111 406 112 299 531 300 +CHEXA 114 1 221 528 534 222 191 504+ ++ 505 192 +CHEXA 115 1 385 510 298 110 409 535+ ++ 301 113 +CHEXA 116 1 373 495 502 378 368 490+ ++ 488 366 +CHEXA 117 1 111 402 401 406 299 523+ ++ 524 531 +CHEXA 118 1 536 515 287 291 223 207+ ++ 20 24 +CHEXA 119 1 538 410 153 340 537 411+ ++ 152 341 +CHEXA 120 1 537 411 152 341 347 159+ ++ 13 90 +CHEXA 121 1 75 76 340 341 225 224+ ++ 538 537 +CHEXA 122 1 301 113 409 535 302 114+ ++ 412 539 +CHEXA 123 1 510 205 32 298 535 226+ ++ 33 301 +CHEXA 124 1 90 341 537 347 7 75+ ++ 225 82 +CHEXA 125 1 413 540 541 414 154 342+ ++ 343 155 +CHEXA 126 1 76 77 343 340 224 227+ ++ 541 538 +CHEXA 127 1 535 539 228 226 301 302+ ++ 34 33 +CHEXA 128 1 416 542 497 380 415 543+ ++ 500 376 +CHEXA 129 1 188 184 229 230 500 497+ ++ 542 543 +CHEXA 130 1 56 321 543 230 57 322+ ++ 544 231 +CHEXA 131 1 545 544 231 232 542 543+ ++ 230 229 +CHEXA 132 1 546 233 231 544 323 58+ ++ 57 322 +CHEXA 133 1 547 546 233 234 545 544+ ++ 231 232 +CHEXA 134 1 115 116 303 304 405 417+ ++ 548 530 +CHEXA 135 1 418 544 543 415 133 322+ ++ 321 134 +CHEXA 136 1 406 405 115 112 531 530+ ++ 304 300 +CHEXA 137 1 35 36 235 236 303 305+ ++ 549 548 +CHEXA 138 1 545 419 418 544 542 416+ ++ 415 543 +CHEXA 139 1 128 317 321 134 376 500+ ++ 543 415 +CHEXA 140 1 500 543 230 188 317 321+ ++ 56 51 +CHEXA 141 1 550 540 541 551 420 413+ ++ 414 421 +CHEXA 142 1 550 420 423 552 547 422+ ++ 424 546 +CHEXA 143 1 421 551 550 420 425 553+ ++ 552 423 +CHEXA 144 1 227 541 551 238 237 540+ ++ 550 239 +CHEXA 145 1 550 551 238 239 552 553+ ++ 240 241 +CHEXA 146 1 546 552 241 233 323 324+ ++ 59 58 +CHEXA 147 1 324 323 546 552 136 135+ ++ 424 423 +CHEXA 148 1 137 325 553 425 136 324+ ++ 552 423 +CHEXA 149 1 554 242 83 348 537 225+ ++ 82 347 +CHEXA 150 1 327 555 244 60 326 556+ ++ 243 61 +CHEXA 151 1 227 224 245 238 541 538+ ++ 557 551 +CHEXA 152 1 557 555 244 245 551 553+ ++ 240 238 +CHEXA 153 1 421 414 541 551 426 410+ ++ 538 557 +CHEXA 154 1 557 245 242 554 538 224+ ++ 225 537 +CHEXA 155 1 192 222 246 220 505 534+ ++ 558 527 +CHEXA 156 1 549 534 222 235 548 528+ ++ 221 236 +CHEXA 157 1 427 549 305 117 417 548+ ++ 303 116 +CHEXA 158 1 505 534 558 527 393 428+ ++ 429 430 +CHEXA 159 1 554 242 243 556 348 83+ ++ 84 349 +CPENTA 160 1 505 527 514 393 430 395 +CHEXA 161 1 537 411 431 554 538 410+ ++ 426 557 +CHEXA 162 1 34 302 539 228 36 305+ ++ 549 235 +CHEXA 163 1 549 235 222 534 539 228+ ++ 246 558 +CHEXA 164 1 228 226 247 246 539 535+ ++ 559 558 +CHEXA 165 1 244 555 557 245 243 556+ ++ 554 242 +CHEXA 166 1 429 428 534 558 412 427+ ++ 549 539 +CHEXA 167 1 412 409 535 539 429 432+ ++ 559 558 +CHEXA 168 1 327 555 433 138 325 553+ ++ 425 137 +CHEXA 169 1 425 553 555 433 421 551+ ++ 557 426 +CHEXA 170 1 434 556 554 431 433 555+ ++ 557 426 +CHEXA 171 1 114 302 305 117 412 539+ ++ 549 427 +CHEXA 172 1 160 434 139 14 349 556+ ++ 326 91 +CHEXA 173 1 528 221 236 548 529 248+ ++ 249 530 +CHEXA 174 1 560 435 140 328 292 100+ ++ 9 86 +CHEXA 175 1 556 243 61 326 349 84+ ++ 6 91 +CHEXA 176 1 554 431 161 348 556 434+ ++ 160 349 +CHEXA 177 1 392 393 428 404 504 505+ ++ 534 528 +CHEXA 178 1 427 549 548 417 428 534+ ++ 528 404 +CHEXA 179 1 405 403 404 417 530 529+ ++ 528 548 +CHEXA 180 1 154 342 344 156 413 540+ ++ 561 436 +CHEXA 181 1 437 562 561 436 157 345+ ++ 344 156 +CHEXA 182 1 547 234 239 550 561 250+ ++ 237 540 +CHEXA 183 1 545 232 234 547 562 251+ ++ 250 561 +CHEXA 184 1 345 344 78 79 562 561+ ++ 250 251 +CHEXA 185 1 229 542 563 252 232 545+ ++ 562 251 +CHEXA 186 1 185 252 229 184 491 563+ ++ 542 497 +CHEXA 187 1 416 542 545 419 438 563+ ++ 562 437 +CHEXA 188 1 563 438 158 346 562 437+ ++ 157 345 +CHEXA 189 1 563 252 251 562 346 80+ ++ 79 345 +CHEXA 190 1 80 74 336 346 252 185+ ++ 491 563 +CHEXA 191 1 497 380 369 491 542 416+ ++ 438 563 +CHEXA 192 1 369 491 563 438 148 336+ ++ 346 158 +CHEXA 193 1 439 564 560 435 141 329+ ++ 328 140 +CHEXA 194 1 25 293 290 22 253 565+ ++ 525 219 +CHEXA 195 1 565 253 255 566 525 219+ ++ 254 567 +CHEXA 196 1 440 565 525 441 101 293+ ++ 290 102 +CHEXA 197 1 566 442 439 564 565 440+ ++ 435 560 +CHEXA 198 1 440 565 566 442 441 525+ ++ 567 443 +CHEXA 199 1 256 255 566 564 257 253+ ++ 565 560 +CHEXA 200 1 256 564 329 62 257 560+ ++ 328 63 +CHEXA 201 1 26 1 86 292 257 63+ ++ 328 560 +CHEXA 202 1 293 25 26 292 565 253+ ++ 257 560 +CHEXA 203 1 560 435 100 292 565 440+ ++ 101 293 +CHEXA 204 1 432 559 558 429 444 568+ ++ 527 430 +CHEXA 205 1 512 386 445 520 511 384+ ++ 446 569 +CHEXA 206 1 511 204 258 569 512 201+ ++ 210 520 +CHEXA 207 1 385 510 535 409 447 570+ ++ 559 432 +CPENTA 208 1 385 446 384 510 569 511 +CHEXA 209 1 385 447 448 446 510 570+ ++ 571 569 +CHEXA 210 1 510 205 226 535 570 259+ ++ 247 559 +CHEXA 211 1 247 559 568 260 246 558+ ++ 527 220 +CPENTA 212 1 205 204 258 510 511 569 +CHEXA 213 1 259 205 258 261 570 510+ ++ 569 571 +CHEXA 214 1 444 449 447 432 568 572+ ++ 570 559 +CHEXA 215 1 259 570 572 262 247 559+ ++ 568 260 +CHEXA 216 1 572 262 263 573 570 259+ ++ 261 571 +CHEXA 217 1 571 573 572 570 448 450+ ++ 449 447 +CHEXA 218 1 451 519 520 445 448 571+ ++ 569 446 +CHEXA 219 1 519 211 210 520 571 261+ ++ 258 569 +CHEXA 220 1 263 261 211 264 573 571+ ++ 519 574 +CHEXA 221 1 448 571 573 450 451 519+ ++ 574 452 +CHEXA 222 1 265 254 219 218 575 567+ ++ 525 526 +CHEXA 223 1 576 577 453 454 572 573+ ++ 450 449 +CHEXA 224 1 455 456 457 458 526 575+ ++ 578 579 +CHEXA 225 1 575 456 457 578 577 453+ ++ 454 576 +CHEXA 226 1 525 441 455 526 567 443+ ++ 456 575 +CHEXA 227 1 290 102 103 289 525 441+ ++ 455 526 +CHEXA 228 1 526 455 103 289 579 458+ ++ 104 294 +CHEXA 229 1 104 458 459 105 294 579+ ++ 536 291 +CHEXA 230 1 459 408 533 536 458 457+ ++ 578 579 +CHEXA 231 1 514 515 533 532 208 207+ ++ 266 267 +CPENTA 232 1 515 536 533 207 223 266 +CHEXA 233 1 536 515 394 459 291 287+ ++ 97 105 +CHEXA 234 1 531 530 249 268 524 529+ ++ 248 216 +CHEXA 235 1 216 248 213 214 524 529+ ++ 517 522 +CHEXA 236 1 191 504 517 213 221 528+ ++ 529 248 +CPENTA 237 1 515 533 536 394 408 459 +CHEXA 238 1 527 514 532 568 220 208+ ++ 267 260 +CHEXA 239 1 407 408 457 454 532 533+ ++ 578 576 +CHEXA 240 1 530 304 37 249 548 303+ ++ 35 236 +CHEXA 241 1 444 568 527 430 407 532+ ++ 514 395 +CHEXA 242 1 444 449 572 568 407 454+ ++ 576 532 +CHEXA 243 1 267 266 533 532 270 269+ ++ 578 576 +CHEXA 244 1 291 294 579 536 24 27+ ++ 271 223 +CHEXA 245 1 578 269 266 533 579 271+ ++ 223 536 +CHEXA 246 1 572 568 532 576 262 260+ ++ 267 270 +CHEXA 247 1 576 577 573 572 270 272+ ++ 263 262 +CHEXA 248 1 215 38 268 216 523 299+ ++ 531 524 +CHEXA 249 1 304 37 39 300 530 249+ ++ 268 531 +CHEXA 250 1 272 577 575 265 270 576+ ++ 578 269 +CHEXA 251 1 27 23 289 294 271 218+ ++ 526 579 +CHEXA 252 1 218 526 579 271 265 575+ ++ 578 269 +CHEXA 253 1 460 580 574 452 453 577+ ++ 573 450 +CPENTA 254 1 38 39 268 299 300 531 +CHEXA 255 1 283 471 356 93 85 295+ ++ 108 8 +CPENTA 256 1 360 362 402 486 487 523 +CHEXA 257 1 360 486 523 402 118 306+ ++ 299 111 +CHEXA 258 1 422 547 550 420 436 561+ ++ 540 413 +CHEXA 259 1 453 456 443 460 577 575+ ++ 567 580 +CHEXA 260 1 344 561 540 342 78 250+ ++ 237 81 +CHEXA 261 1 545 419 437 562 547 422+ ++ 436 561 +CHEXA 262 1 581 461 462 582 574 452+ ++ 460 580 +CHEXA 263 1 118 306 307 119 360 486+ ++ 469 463 +CHEXA 264 1 280 484 359 106 282 468+ ++ 355 92 +CHEXA 265 1 566 442 443 567 582 462+ ++ 460 580 +CHEXA 266 1 564 439 442 566 583 464+ ++ 462 582 +CHEXA 267 1 359 484 486 360 355 468+ ++ 469 463 +CHEXA 268 1 433 555 327 138 434 556+ ++ 326 139 +CHEXA 269 1 523 215 38 299 486 273+ ++ 40 306 +CHEXA 270 1 461 581 582 462 465 584+ ++ 583 464 +CHEXA 271 1 554 537 347 348 431 411+ ++ 159 161 +CHEXA 272 1 324 59 64 325 552 241+ ++ 240 553 +CHEXA 273 1 64 325 553 240 60 327+ ++ 555 244 +CHEXA 274 1 178 484 468 162 273 486+ ++ 469 163 +CHEXA 275 1 359 106 99 361 484 280+ ++ 281 485 +CHEXA 276 1 28 15 280 282 162 178+ ++ 484 468 +CHEXA 277 1 201 512 475 169 210 520+ ++ 474 168 +CHEXA 278 1 389 386 445 398 475 512+ ++ 520 474 +CPENTA 279 1 486 487 523 273 217 215 +CHEXA 280 1 461 452 574 581 466 451+ ++ 519 521 +CHEXA 281 1 445 520 519 451 398 474+ ++ 521 466 +CHEXA 282 1 398 474 521 466 365 472+ ++ 585 467 +CHEXA 283 1 486 484 485 487 273 178+ ++ 179 217 +CHEXA 284 1 233 241 552 546 234 239+ ++ 550 547 +CHEXA 285 1 474 168 212 521 472 167+ ++ 274 585 +CHEXA 286 1 585 467 143 330 584 465+ ++ 142 331 +CHEXA 287 1 521 466 467 585 581 461+ ++ 465 584 +CHEXA 288 1 365 472 585 467 124 311+ ++ 330 143 +CHEXA 289 1 567 254 275 580 575 265+ ++ 272 577 +CHEXA 290 1 573 263 272 577 574 264+ ++ 275 580 +CHEXA 291 1 307 306 40 41 469 486+ ++ 273 163 +CHEXA 292 1 471 356 108 295 470 350+ ++ 107 296 +CHEXA 293 1 470 471 356 350 469 468+ ++ 355 463 +CHEXA 294 1 163 469 470 165 41 307+ ++ 297 42 +CHEXA 295 1 133 322 323 135 418 544+ ++ 546 424 +CHEXA 296 1 422 547 545 419 424 546+ ++ 544 418 +CHEXA 297 1 350 463 119 109 470 469+ ++ 307 297 +CHEXA 298 1 275 580 582 276 254 567+ ++ 566 255 +CHEXA 299 1 519 574 581 521 211 264+ ++ 277 212 +CHEXA 300 1 42 165 31 3 297 470+ ++ 296 87 +CHEXA 301 1 277 581 521 212 278 584+ ++ 585 274 +CHEXA 302 1 581 277 264 574 582 276+ ++ 275 580 +CHEXA 303 1 279 65 332 583 278 66+ ++ 331 584 +CHEXA 304 1 282 28 162 468 283 29+ ++ 164 471 +CHEXA 305 1 540 237 227 541 342 81+ ++ 77 343 +CHEXA 306 1 276 279 256 255 582 583+ ++ 564 566 +CHEXA 307 1 541 414 155 343 538 410+ ++ 153 340 +CHEXA 308 1 277 581 584 278 276 582+ ++ 583 279 +CHEXA 309 1 564 329 332 583 256 62+ ++ 65 279 +CHEXA 310 1 332 144 141 329 583 464+ ++ 439 564 +CHEXA 311 1 583 464 465 584 332 144+ ++ 142 331 +CHEXA 312 1 46 167 472 311 67 274+ ++ 585 330 +CHEXA 313 1 585 274 278 584 330 67+ ++ 66 331 +CHEXA 314 1 471 164 30 295 283 29+ ++ 2 85 +CHEXA 315 1 477 388 364 479 476 358+ ++ 363 478 +CHEXA 316 1 477 476 473 475 388 358+ ++ 357 389 +$* +$* PROPERTY CARDS +$* +$* Property: PSOLID1::Bracket_fem1::[1] +PSOLID 1 1 0 SMECH +$* +$* MATERIAL CARDS +$* +$* Material: Aluminum_6061::Bracket_fem1::[1] +MAT1 16.8980+7 0.3300002.7110-62.2380-5 +MATT1 1 1 2 3 +TABLEM1 1 1 + ++ 20.00006.8980+7 21.11006.8980+7 23.89006.8980+7 37.78006.8290+7+ ++ 51.67006.8290+7 65.56006.7600+7 79.44006.6910+7 93.33006.6221+7+ ++ 107.22006.5531+7121.11006.4841+7135.00006.4151+7148.89006.3461+7+ ++ 162.78006.2772+7176.67006.1392+7190.56006.0702+7204.44005.9323+7+ ++ 218.33005.8633+7232.22005.7253+7246.11005.5874+7260.00005.4494+7+ ++ 273.89005.3114+7287.78005.1735+7301.67005.0355+7315.56004.8976+7+ ++ 329.44004.7596+7343.33004.6216+7357.22004.4147+7371.11004.2767+7+ ++ 385.00004.1388+7398.89003.9318+7412.78003.7939+7426.67003.5869+7+ ++ ENDT +TABLEM1 2 1 + ++ 20.00000.330000 21.11000.330000 ENDT +TABLEM1 3 1 + ++ 20.00002.2380-5 93.33002.3184-5107.22002.3346-5121.11002.3508-5+ ++ 135.00002.3670-5148.89002.3832-5162.78002.3994-5176.67002.4138-5+ ++ 190.56002.4300-5204.44002.4444-5218.33002.4588-5232.22002.4732-5+ ++ 246.11002.4876-5260.00002.5002-5273.89002.5146-5287.78002.5272-5+ ++ 301.67002.5398-5315.56002.5524-5 ENDT +$* +$* LOAD AND CONSTRAINT CARDS +$* +$* Load: Force(1) +FORCE 1 6 02.5026+5 0.0000 0.0000-1.00000 +FORCE 1 14 02.4974+5 0.0000 0.0000-1.00000 +FORCE 1 91 05.0000+5 0.0000 0.0000-1.00000 +$* Constraint: Fixed(1) +SPC 2 1 123456 0.0000 +SPC 2 2 123456 0.0000 +SPC 2 8 123456 0.0000 +SPC 2 9 123456 0.0000 +SPC 2 15 123456 0.0000 +SPC 2 16 123456 0.0000 +SPC 2 17 123456 0.0000 +SPC 2 18 123456 0.0000 +SPC 2 19 123456 0.0000 +SPC 2 20 123456 0.0000 +SPC 2 21 123456 0.0000 +SPC 2 22 123456 0.0000 +SPC 2 23 123456 0.0000 +SPC 2 24 123456 0.0000 +SPC 2 25 123456 0.0000 +SPC 2 26 123456 0.0000 +SPC 2 27 123456 0.0000 +SPC 2 28 123456 0.0000 +SPC 2 29 123456 0.0000 +SPC 2 85 123456 0.0000 +SPC 2 86 123456 0.0000 +SPC 2 92 123456 0.0000 +SPC 2 93 123456 0.0000 +SPC 2 94 123456 0.0000 +SPC 2 95 123456 0.0000 +SPC 2 96 123456 0.0000 +SPC 2 97 123456 0.0000 +SPC 2 98 123456 0.0000 +SPC 2 99 123456 0.0000 +SPC 2 100 123456 0.0000 +SPC 2 101 123456 0.0000 +SPC 2 102 123456 0.0000 +SPC 2 103 123456 0.0000 +SPC 2 104 123456 0.0000 +SPC 2 105 123456 0.0000 +SPC 2 106 123456 0.0000 +SPC 2 280 123456 0.0000 +SPC 2 281 123456 0.0000 +SPC 2 282 123456 0.0000 +SPC 2 283 123456 0.0000 +SPC 2 284 123456 0.0000 +SPC 2 285 123456 0.0000 +SPC 2 286 123456 0.0000 +SPC 2 287 123456 0.0000 +SPC 2 288 123456 0.0000 +SPC 2 289 123456 0.0000 +SPC 2 290 123456 0.0000 +SPC 2 291 123456 0.0000 +SPC 2 292 123456 0.0000 +SPC 2 293 123456 0.0000 +SPC 2 294 123456 0.0000 +ENDDATA 4ea11cef diff --git a/tests/mcp_server/__init__.py b/tests/mcp_server/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/mcp_server/tools/__init__.py b/tests/mcp_server/tools/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/mcp_server/tools/test_model_discovery.py b/tests/mcp_server/tools/test_model_discovery.py new file mode 100644 index 00000000..8c22cdf6 --- /dev/null +++ b/tests/mcp_server/tools/test_model_discovery.py @@ -0,0 +1,211 @@ +""" +Unit tests for MCP Model Discovery Tool + +Tests the .sim file parser and FEA model discovery functionality. +""" + +import pytest +from pathlib import Path +import sys + +# Add project root to path +project_root = Path(__file__).parent.parent.parent.parent +sys.path.insert(0, str(project_root)) + +from mcp_server.tools.model_discovery import ( + discover_fea_model, + format_discovery_result_for_llm, + SimFileParser +) + + +class TestSimFileParser: + """Test the SimFileParser class""" + + @pytest.fixture + def example_sim_path(self): + """Path to example .sim file""" + return project_root / "examples" / "test_bracket.sim" + + def test_parser_initialization(self, example_sim_path): + """Test that parser initializes correctly""" + parser = SimFileParser(example_sim_path) + assert parser.sim_path.exists() + assert parser.tree is not None + assert parser.root is not None + + def test_parser_file_not_found(self): + """Test error handling for missing file""" + with pytest.raises(FileNotFoundError): + SimFileParser("/nonexistent/path/file.sim") + + def test_parser_invalid_extension(self): + """Test error handling for non-.sim file""" + with pytest.raises(ValueError): + SimFileParser(project_root / "README.md") + + def test_extract_solutions(self, example_sim_path): + """Test solution extraction""" + parser = SimFileParser(example_sim_path) + solutions = parser.extract_solutions() + + assert len(solutions) > 0 + assert solutions[0]['name'] == 'Structural Analysis 1' + assert solutions[0]['type'] == 'Static Structural' + assert solutions[0]['solver'] == 'NX Nastran' + + def test_extract_expressions(self, example_sim_path): + """Test expression extraction""" + parser = SimFileParser(example_sim_path) + expressions = parser.extract_expressions() + + assert len(expressions) > 0 + + # Check for expected expressions + expr_names = [e['name'] for e in expressions] + assert 'wall_thickness' in expr_names + assert 'hole_diameter' in expr_names + assert 'rib_spacing' in expr_names + + # Check expression values + wall_thickness = next(e for e in expressions if e['name'] == 'wall_thickness') + assert wall_thickness['value'] == '5.0' + assert wall_thickness['units'] == 'mm' + + def test_extract_fem_info(self, example_sim_path): + """Test FEM information extraction""" + parser = SimFileParser(example_sim_path) + fem_info = parser.extract_fem_info() + + # Check mesh info + assert 'mesh' in fem_info + assert fem_info['mesh']['name'] == 'Bracket Mesh' + assert fem_info['mesh']['node_count'] == '8234' + assert fem_info['mesh']['element_count'] == '4521' + + # Check materials + assert len(fem_info['materials']) > 0 + assert fem_info['materials'][0]['name'] == 'Aluminum 6061-T6' + + # Check loads + assert len(fem_info['loads']) > 0 + assert fem_info['loads'][0]['name'] == 'Applied Force' + + # Check constraints + assert len(fem_info['constraints']) > 0 + assert fem_info['constraints'][0]['name'] == 'Fixed Support' + + +class TestDiscoverFEAModel: + """Test the main discover_fea_model function""" + + @pytest.fixture + def example_sim_path(self): + """Path to example .sim file""" + return str(project_root / "examples" / "test_bracket.sim") + + def test_successful_discovery(self, example_sim_path): + """Test successful model discovery""" + result = discover_fea_model(example_sim_path) + + assert result['status'] == 'success' + assert result['file_exists'] is True + assert 'solutions' in result + assert 'expressions' in result + assert 'fem_info' in result + assert 'summary' in result + + # Check summary statistics + assert result['summary']['solution_count'] >= 1 + assert result['summary']['expression_count'] >= 3 + + def test_file_not_found_error(self): + """Test error handling for missing file""" + result = discover_fea_model("/nonexistent/file.sim") + + assert result['status'] == 'error' + assert result['error_type'] == 'file_not_found' + assert 'message' in result + assert 'suggestion' in result + + def test_result_structure(self, example_sim_path): + """Test that result has expected structure""" + result = discover_fea_model(example_sim_path) + + # Check top-level keys + expected_keys = ['status', 'sim_file', 'file_exists', 'solutions', + 'expressions', 'fem_info', 'linked_files', 'metadata', 'summary'] + + for key in expected_keys: + assert key in result, f"Missing key: {key}" + + # Check summary keys + expected_summary_keys = ['solution_count', 'expression_count', + 'material_count', 'load_count', 'constraint_count'] + + for key in expected_summary_keys: + assert key in result['summary'], f"Missing summary key: {key}" + + +class TestFormatDiscoveryResult: + """Test the Markdown formatting function""" + + @pytest.fixture + def example_sim_path(self): + """Path to example .sim file""" + return str(project_root / "examples" / "test_bracket.sim") + + def test_format_success_result(self, example_sim_path): + """Test formatting of successful discovery""" + result = discover_fea_model(example_sim_path) + formatted = format_discovery_result_for_llm(result) + + assert isinstance(formatted, str) + assert '# FEA Model Analysis' in formatted + assert 'Solutions' in formatted + assert 'Expressions' in formatted + assert 'wall_thickness' in formatted + + def test_format_error_result(self): + """Test formatting of error result""" + result = discover_fea_model("/nonexistent/file.sim") + formatted = format_discovery_result_for_llm(result) + + assert isinstance(formatted, str) + assert '❌' in formatted or 'Error' in formatted + assert result['message'] in formatted + + +# Integration test +def test_end_to_end_workflow(): + """ + Test the complete workflow: + 1. Discover model + 2. Format for LLM + 3. Verify output is useful + """ + example_sim = str(project_root / "examples" / "test_bracket.sim") + + # Step 1: Discover + result = discover_fea_model(example_sim) + assert result['status'] == 'success' + + # Step 2: Format + formatted = format_discovery_result_for_llm(result) + assert len(formatted) > 100 # Should be substantial output + + # Step 3: Verify key information is present + assert 'wall_thickness' in formatted + assert 'Aluminum' in formatted + assert 'Static Structural' in formatted + + print("\n" + "="*60) + print("INTEGRATION TEST OUTPUT:") + print("="*60) + print(formatted) + print("="*60) + + +if __name__ == "__main__": + # Run tests with pytest + pytest.main([__file__, "-v", "-s"])