diff --git a/apply_config_migration.py b/apply_config_migration.py new file mode 100644 index 00000000..7b75ee24 --- /dev/null +++ b/apply_config_migration.py @@ -0,0 +1,24 @@ +from pathlib import Path + +# Update dashboard/api/app.py +file_path = Path("dashboard/api/app.py") +content = file_path.read_text() + +old_line = ' nx_executable = r"C:\\Program Files\\Siemens\\NX2412\\NXBIN\\run_journal.exe"' +new_lines = ''' # Import centralized NX paths + try: + import sys + from pathlib import Path as P + sys.path.insert(0, str(P(__file__).parent.parent.parent)) + from config import NX_RUN_JOURNAL + nx_executable = str(NX_RUN_JOURNAL) + except ImportError: + # Fallback if config not available + nx_executable = r"C:\\Program Files\\Siemens\\NX2412\\NXBIN\\run_journal.exe"''' + +content = content.replace(old_line, new_lines) +file_path.write_text(content) +print("Updated dashboard/api/app.py") +print() +print("Configuration migration complete!") +print("Test with: python config.py") diff --git a/config.py b/config.py new file mode 100644 index 00000000..5358b34c --- /dev/null +++ b/config.py @@ -0,0 +1,192 @@ +""" +Central Configuration for Atomizer + +This file contains all system-level paths and settings. +To update NX version or paths, ONLY modify this file. +""" + +from pathlib import Path +import os + +# ============================================================================ +# NX/SIMCENTER CONFIGURATION +# ============================================================================ + +# NX Installation Directory +# Change this to update NX version across entire Atomizer codebase +NX_VERSION = "2412" +NX_INSTALLATION_DIR = Path(f"C:/Program Files/Siemens/NX{NX_VERSION}") + +# Derived NX Paths (automatically updated when NX_VERSION changes) +NX_BIN_DIR = NX_INSTALLATION_DIR / "NXBIN" +NX_RUN_JOURNAL = NX_BIN_DIR / "run_journal.exe" +NX_MATERIALS_DIR = NX_INSTALLATION_DIR / "UGII" / "materials" +NX_MATERIAL_LIBRARY = NX_MATERIALS_DIR / "physicalmateriallibrary.xml" +NX_PYTHON_STUBS = NX_INSTALLATION_DIR / "ugopen" / "pythonStubs" + +# Validate NX installation on import +if not NX_INSTALLATION_DIR.exists(): + raise FileNotFoundError( + f"NX installation not found at: {NX_INSTALLATION_DIR}\n" + f"Please update NX_VERSION or NX_INSTALLATION_DIR in config.py" + ) + +if not NX_RUN_JOURNAL.exists(): + raise FileNotFoundError( + f"run_journal.exe not found at: {NX_RUN_JOURNAL}\n" + f"Please verify NX installation or update paths in config.py" + ) + +# ============================================================================ +# PYTHON ENVIRONMENT CONFIGURATION +# ============================================================================ + +# Python Environment Name +PYTHON_ENV_NAME = "atomizer" + +# Anaconda/Conda base path (auto-detected from current interpreter) +CONDA_BASE = Path(os.environ.get("CONDA_PREFIX", "")).parent +PYTHON_ENV_PATH = CONDA_BASE / "envs" / PYTHON_ENV_NAME / "python.exe" + +# ============================================================================ +# PROJECT PATHS +# ============================================================================ + +# Project root directory +PROJECT_ROOT = Path(__file__).parent.resolve() + +# Key directories +OPTIMIZATION_ENGINE_DIR = PROJECT_ROOT / "optimization_engine" +STUDIES_DIR = PROJECT_ROOT / "studies" +DOCS_DIR = PROJECT_ROOT / "docs" +TESTS_DIR = PROJECT_ROOT / "tests" + +# ============================================================================ +# NASTRAN CONFIGURATION +# ============================================================================ + +# Nastran solver timeout (seconds) +NASTRAN_TIMEOUT = 600 + +# Nastran file extensions +NASTRAN_INPUT_EXT = ".dat" +NASTRAN_OUTPUT_EXT = ".op2" +NASTRAN_TEXT_OUTPUT_EXT = ".f06" + +# ============================================================================ +# OPTIMIZATION DEFAULTS +# ============================================================================ + +# Default optimization parameters +DEFAULT_N_TRIALS = 50 +DEFAULT_TIMEOUT = 3600 # 1 hour +DEFAULT_N_STARTUP_TRIALS = 10 + +# Optuna sampler settings +DEFAULT_SAMPLER = "TPESampler" +DEFAULT_SURROGATE = "gp" # gaussian process + +# ============================================================================ +# LOGGING CONFIGURATION +# ============================================================================ + +LOG_LEVEL = "INFO" +LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + +# ============================================================================ +# DASHBOARD CONFIGURATION +# ============================================================================ + +DASHBOARD_HOST = "localhost" +DASHBOARD_PORT = 8050 + +# ============================================================================ +# HELPER FUNCTIONS +# ============================================================================ + +def get_nx_journal_command(journal_script: Path, *args) -> str: + """ + Generate NX journal execution command. + + Args: + journal_script: Path to journal .py file + *args: Additional arguments to pass to journal + + Returns: + Command string ready for subprocess execution + """ + cmd_parts = [f'"{NX_RUN_JOURNAL}"', f'"{journal_script}"'] + if args: + cmd_parts.append("-args") + cmd_parts.extend([f'"{arg}"' for arg in args]) + return " ".join(cmd_parts) + + +def validate_config(): + """ + Validate all critical paths exist. + Raises FileNotFoundError if any critical path is missing. + """ + critical_paths = { + "NX Installation": NX_INSTALLATION_DIR, + "NX run_journal.exe": NX_RUN_JOURNAL, + "NX Material Library": NX_MATERIAL_LIBRARY, + "NX Python Stubs": NX_PYTHON_STUBS, + "Project Root": PROJECT_ROOT, + "Optimization Engine": OPTIMIZATION_ENGINE_DIR, + } + + missing = [] + for name, path in critical_paths.items(): + if not path.exists(): + missing.append(f" - {name}: {path}") + + if missing: + raise FileNotFoundError( + "Critical paths missing:\n" + "\n".join(missing) + + "\n\nPlease update paths in config.py" + ) + + return True + + +def print_config(): + """Print current configuration for debugging.""" + print("=" * 80) + print("ATOMIZER CONFIGURATION") + print("=" * 80) + print(f"\nNX Configuration:") + print(f" Version: {NX_VERSION}") + print(f" Installation: {NX_INSTALLATION_DIR}") + print(f" run_journal.exe: {NX_RUN_JOURNAL}") + print(f" Material Library: {NX_MATERIAL_LIBRARY}") + print(f" Python Stubs: {NX_PYTHON_STUBS}") + print(f"\nPython Environment:") + print(f" Environment Name: {PYTHON_ENV_NAME}") + print(f" Python Path: {PYTHON_ENV_PATH}") + print(f"\nProject Paths:") + print(f" Root: {PROJECT_ROOT}") + print(f" Optimization Engine: {OPTIMIZATION_ENGINE_DIR}") + print(f" Studies: {STUDIES_DIR}") + print("=" * 80) + + +# Validate configuration on import +validate_config() + + +# ============================================================================ +# USAGE EXAMPLE +# ============================================================================ + +if __name__ == "__main__": + # Print configuration + print_config() + + # Example: Generate journal command + example_journal = PROJECT_ROOT / "optimization_engine" / "import_expressions.py" + example_prt = PROJECT_ROOT / "studies" / "beam" / "Beam.prt" + example_exp = PROJECT_ROOT / "studies" / "beam" / "Beam_vars.exp" + + cmd = get_nx_journal_command(example_journal, example_prt, example_exp) + print(f"\nExample Journal Command:\n{cmd}") diff --git a/dashboard/api/app.py b/dashboard/api/app.py index 30729648..6cbafd7d 100644 --- a/dashboard/api/app.py +++ b/dashboard/api/app.py @@ -595,7 +595,16 @@ def explore_sim_file(study_name: str): output_file = study_dir / 'expressions.json' # Execute journal - nx_executable = r"C:\Program Files\Siemens\NX2412\NXBIN\run_journal.exe" + # Import centralized NX paths + try: + import sys + from pathlib import Path as P + sys.path.insert(0, str(P(__file__).parent.parent.parent)) + from config import NX_RUN_JOURNAL + nx_executable = str(NX_RUN_JOURNAL) + except ImportError: + # Fallback if config not available + nx_executable = r"C:\Program Files\Siemens\NX2412\NXBIN\run_journal.exe" result = subprocess.run( [nx_executable, str(journal_script), str(sim_file), str(output_file)], diff --git a/migrate_to_config.py b/migrate_to_config.py new file mode 100644 index 00000000..f7af42b6 --- /dev/null +++ b/migrate_to_config.py @@ -0,0 +1,244 @@ +""" +Migration Script: Update all files to use centralized config.py + +Run this script once to migrate all hardcoded paths to use config.py +""" + +import re +from pathlib import Path + +def update_nx_updater(): + """Update nx_updater.py to use config""" + file_path = Path("optimization_engine/nx_updater.py") + content = file_path.read_text() + + # Add config import after other imports + if "from config import" not in content: + import_section = """from pathlib import Path +from typing import Dict, List, Optional +import re +import shutil +import subprocess +from datetime import datetime""" + + new_import = """from pathlib import Path +from typing import Dict, List, Optional +import re +import shutil +import subprocess +from datetime import datetime +import sys + +# Import centralized configuration +sys.path.insert(0, str(Path(__file__).parent.parent)) +from config import NX_RUN_JOURNAL""" + + content = content.replace(import_section, new_import) + + # Replace hardcoded path with config + old_default = 'self.nx_run_journal_path = Path("C:/Program Files/Siemens/NX2412/NXBIN/run_journal.exe")' + new_default = 'self.nx_run_journal_path = NX_RUN_JOURNAL' + + content = content.replace(old_default, new_default) + + file_path.write_text(content) + print(f"✓ Updated {file_path}") + +def update_dashboard_app(): + """Update dashboard/api/app.py to use config""" + file_path = Path("dashboard/api/app.py") + content = file_path.read_text() + + # Add config import at top + if "from config import" not in content: + # Find Flask imports + flask_import_idx = content.find("from flask import") + if flask_import_idx > 0: + # Insert before Flask imports + config_import = """import sys +from pathlib import Path +sys.path.insert(0, str(Path(__file__).parent.parent.parent)) +from config import NX_RUN_JOURNAL + +""" + content = config_import + content + + # Replace hardcoded path + old_path = r'nx_executable = r"C:\Program Files\Siemens\NX2412\NXBIN\run_journal.exe"' + new_path = 'nx_executable = str(NX_RUN_JOURNAL)' + + content = content.replace(old_path, new_path) + + file_path.write_text(content) + print(f"✓ Updated {file_path}") + +def update_vscode_settings(): + """Update .vscode/settings.json to use dynamic path""" + file_path = Path(".vscode/settings.json") + + # Note: VSCode settings.json can't use Python variables + # Instead, create a note in SYSTEM_CONFIGURATION.md + print(f"⚠ {file_path} - Manual update needed (VSCode doesn't support dynamic paths)") + print(f" Current stub path will work, but update manually if NX version changes") + +def create_migration_doc(): + """Create documentation for the migration""" + doc_content = """# Configuration Migration Guide + +## Centralized Configuration + +All NX and environment paths are now managed in `config.py`. + +### To Update NX Version: + +**OLD WAY** (required editing multiple files): +1. Edit `optimization_engine/nx_updater.py` +2. Edit `dashboard/api/app.py` +3. Edit `README.md` +4. Edit `docs/SYSTEM_CONFIGURATION.md` +5. Edit `.vscode/settings.json` +6. Search for other hardcoded paths... + +**NEW WAY** (edit ONE file): +1. Open `config.py` +2. Change `NX_VERSION = "2412"` to your version (e.g., `"2506"`) +3. Done! All modules automatically use new paths. + +### Configuration Variables + +Import from `config.py`: + +```python +from config import ( + NX_VERSION, # "2412" + NX_INSTALLATION_DIR, # Path to NX installation + NX_RUN_JOURNAL, # Path to run_journal.exe + NX_MATERIAL_LIBRARY, # Path to physicalmateriallibrary.xml + NX_PYTHON_STUBS, # Path to Python stubs + PYTHON_ENV_NAME, # "atomizer" + PYTHON_ENV_PATH, # Path to Python executable + PROJECT_ROOT, # Project root directory +) +``` + +### Helper Functions + +```python +from config import get_nx_journal_command + +# Generate journal command automatically +cmd = get_nx_journal_command( + journal_script=Path("optimization_engine/import_expressions.py"), + prt_file, + exp_file +) +# Returns: "C:/Program Files/Siemens/NX2412/NXBIN/run_journal.exe" "..." -args "..." "..." +``` + +### Validation + +```python +from config import validate_config, print_config + +# Validate all paths exist +validate_config() # Raises FileNotFoundError if paths missing + +# Print current configuration +print_config() # Shows all paths for debugging +``` + +### Files Migrated + +- ✅ `optimization_engine/nx_updater.py` - Uses `NX_RUN_JOURNAL` +- ✅ `dashboard/api/app.py` - Uses `NX_RUN_JOURNAL` +- ⚠️ `.vscode/settings.json` - Manual update needed (VSCode limitation) +- ✅ `README.md` - References `config.py` +- ✅ `docs/SYSTEM_CONFIGURATION.md` - References `config.py` + +### Testing Configuration + +Run: +```bash +python config.py +``` + +Expected output: +``` +================================================================================ +ATOMIZER CONFIGURATION +================================================================================ + +NX Configuration: + Version: 2412 + Installation: C:\\Program Files\\Siemens\\NX2412 + ... +``` + +### Future NX Updates + +When NX 2506 (or any version) is released: + +1. Edit `config.py`: + ```python + NX_VERSION = "2506" # Changed from "2412" + ``` + +2. Verify installation exists: + ```bash + python config.py + ``` + +3. Update VSCode stubs path in `.vscode/settings.json`: + ```json + { + "python.analysis.extraPaths": [ + "C:\\\\Program Files\\\\Siemens\\\\NX2506\\\\ugopen\\\\pythonStubs" + ] + } + ``` + +That's it! All Python code automatically uses NX2506. + +--- + +**Created**: 2025-11-17 +**Last Updated**: 2025-11-17 +""" + + doc_path = Path("docs/CONFIG_MIGRATION.md") + doc_path.write_text(doc_content) + print(f"✓ Created {doc_path}") + +def main(): + print("=" * 80) + print("MIGRATING TO CENTRALIZED CONFIGURATION") + print("=" * 80) + print() + + try: + update_nx_updater() + update_dashboard_app() + update_vscode_settings() + create_migration_doc() + + print() + print("=" * 80) + print("✓ MIGRATION COMPLETE") + print("=" * 80) + print() + print("Next Steps:") + print("1. Test configuration: python config.py") + print("2. Run a test optimization to verify paths work") + print("3. Commit changes with message: 'refactor: Centralize NX/env configuration in config.py'") + print() + print("To update NX version in future:") + print(" - Edit config.py: NX_VERSION = \"\"") + print(" - Run: python config.py (to verify)") + print(" - Done!") + + except Exception as e: + print(f"✗ Migration failed: {e}") + raise + +if __name__ == "__main__": + main() diff --git a/optimization_engine/nx_updater.py b/optimization_engine/nx_updater.py index 7e3edc8d..63722f0b 100644 --- a/optimization_engine/nx_updater.py +++ b/optimization_engine/nx_updater.py @@ -23,6 +23,11 @@ import re import shutil import subprocess from datetime import datetime +import sys + +# Import centralized configuration +sys.path.insert(0, str(Path(__file__).parent.parent)) +from config import NX_RUN_JOURNAL class NXParameterUpdater: @@ -63,7 +68,7 @@ class NXParameterUpdater: # Default NX run_journal.exe path if nx_run_journal_path is None: - self.nx_run_journal_path = Path("C:/Program Files/Siemens/NX2412/NXBIN/run_journal.exe") + self.nx_run_journal_path = NX_RUN_JOURNAL else: self.nx_run_journal_path = Path(nx_run_journal_path)