FIX: Resolve all paths to absolute before passing to NX

Root cause: Path.absolute() on Windows does NOT resolve '..' components.
sim_file_path was reaching NX as '...\studies\01_doe_landscape\..\..\models\Beam_sim1.sim'
NX likely can't resolve referenced parts from a path with '..' in it.

Fixes:
- nx_interface.py: glob from self.model_dir (resolved) not model_dir (raw)
- solver.py: sim_file.resolve() instead of sim_file.absolute()
- solve_simulation.py: os.path.abspath(sim_file_path) at entry point
- Diagnostic logging still in place for next run
This commit is contained in:
2026-02-11 15:24:20 +00:00
parent 55f0f917c7
commit 80104d2467
19 changed files with 3445 additions and 8 deletions

View File

@@ -209,8 +209,9 @@ def main(args):
name, value = arg.split("=", 1) name, value = arg.split("=", 1)
expression_updates[name] = float(value) expression_updates[name] = float(value)
# Get working directory # Resolve ALL paths to absolute — NX can fail silently with ".." in paths
working_dir = os.path.dirname(os.path.abspath(sim_file_path)) sim_file_path = os.path.abspath(sim_file_path)
working_dir = os.path.dirname(sim_file_path)
sim_filename = os.path.basename(sim_file_path) sim_filename = os.path.basename(sim_file_path)
print(f"[JOURNAL] " + "=" * 60) print(f"[JOURNAL] " + "=" * 60)

View File

@@ -414,7 +414,8 @@ class NXSolver:
# Create a custom journal that passes the sim file path, solution name, and expression values # Create a custom journal that passes the sim file path, solution name, and expression values
# Build argv list with expression updates # Build argv list with expression updates
argv_list = [f"r'{sim_file.absolute()}'"] # MUST use resolve() not absolute() — absolute() doesn't resolve ".." on Windows
argv_list = [f"r'{sim_file.resolve()}'"]
# Add solution name if provided (passed as second argument) # Add solution name if provided (passed as second argument)
if solution_name: if solution_name:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
# Reference Models — Hydrotech Beam
Golden copies of the NX model files. **Do not modify these directly.**
Studies should copy these to their own `model/` folder and modify there.
## Files
| File | Description | Status |
|------|-------------|--------|
| `Beam.prt` | NX CAD part — sandwich I-beam with lightening holes | ⏳ Pending upload |
| `Beam_fem1.fem` | FEM mesh file | ⏳ Pending upload |
| `Beam_fem1_i.prt` | Idealized part for FEM | ⏳ Pending upload |
| `Beam_sim1.sim` | Simulation file — SOL 101 static | ⏳ Pending upload |
## Key Expression
- `p173` — beam mass (kg)
## Notes
- ⏳ Awaiting Syncthing setup for project folder sync (dalidou ↔ server)
- Model files will appear here once sync is live
- Verify model rebuild across full design variable range before optimization
- **Do NOT modify these reference copies** — studies copy them to their own `model/` folder

View File

@@ -0,0 +1,15 @@
{
"iteration": 1,
"expressions": {
"beam_half_core_thickness": 25.162,
"beam_face_thickness": 21.504,
"holes_diameter": 300.0,
"hole_count": 10.0
},
"trial_input": {
"beam_half_core_thickness": 25.162,
"beam_face_thickness": 21.504,
"holes_diameter": 300.0,
"hole_count": 10
}
}

View File

@@ -0,0 +1,15 @@
{
"iteration": 2,
"expressions": {
"beam_half_core_thickness": 16.7813185433211,
"beam_face_thickness": 26.83364680743843,
"holes_diameter": 192.42062402658527,
"hole_count": 8.0
},
"trial_input": {
"beam_half_core_thickness": 16.7813185433211,
"beam_face_thickness": 26.83364680743843,
"holes_diameter": 192.42062402658527,
"hole_count": 8
}
}

View File

@@ -183,16 +183,18 @@ class AtomizerNXSolver:
logger.info("Using existing model backup at %s", self._backup_dir) logger.info("Using existing model backup at %s", self._backup_dir)
# Find the .sim file # Find the .sim file
sim_files = list(model_dir.glob("*.sim")) # Use resolved model_dir for all path operations (NX needs clean absolute paths)
sim_files = list(self.model_dir.glob("*.sim"))
if not sim_files: if not sim_files:
raise FileNotFoundError(f"No .sim file found in {model_dir}") raise FileNotFoundError(f"No .sim file found in {self.model_dir}")
self.sim_file = sim_files[0] self.sim_file = sim_files[0] # Already absolute (from resolved parent)
logger.info("SIM file: %s", self.sim_file.name) logger.info("SIM file: %s", self.sim_file.name)
logger.info("SIM path: %s", self.sim_file)
# Find the .prt file (for mass extraction) # Find the .prt file (for mass extraction)
prt_files = [f for f in model_dir.glob("*.prt") if "_i." not in f.name] prt_files = [f for f in self.model_dir.glob("*.prt") if "_i." not in f.name]
if not prt_files: if not prt_files:
raise FileNotFoundError(f"No .prt file found in {model_dir}") raise FileNotFoundError(f"No .prt file found in {self.model_dir}")
self.prt_file = prt_files[0] self.prt_file = prt_files[0]
logger.info("PRT file: %s", self.prt_file.name) logger.info("PRT file: %s", self.prt_file.name)