Iteration archival: solve on master model, archive outputs to studies/iterations/iterNNN/

- Each iteration gets: params.json, results.json, OP2, F06, mass files
- Model directory stays clean (no solver output buildup)
- Study folder is self-contained with full trial history
This commit is contained in:
2026-02-11 14:39:10 +00:00
parent 60dbf5b172
commit 4243a332a3

View File

@@ -153,15 +153,18 @@ class AtomizerNXSolver:
if not model_dir.exists():
raise FileNotFoundError(f"Model directory not found: {model_dir}")
self.model_dir = model_dir
self.model_dir = model_dir.resolve()
self.nx_version = nx_version
self.timeout = timeout
self.use_iteration_folders = use_iteration_folders
self._iteration = 0
# Set up iteration base directory
self.iterations_dir = model_dir.parent / "2_iterations"
# Iteration outputs go inside the study folder, not models/
# study_dir = the directory where run_doe.py lives
self.study_dir = Path(__file__).parent.resolve()
self.iterations_dir = self.study_dir / "iterations"
self.iterations_dir.mkdir(parents=True, exist_ok=True)
logger.info("Iterations dir: %s", self.iterations_dir)
# Find the .sim file
sim_files = list(model_dir.glob("*.sim"))
@@ -277,25 +280,34 @@ class AtomizerNXSolver:
trial.hole_count,
)
try:
# Step 1: Create iteration folder with fresh model copies + .exp file
if self.use_iteration_folders:
iter_dir = self._nx_solver.create_iteration_folder(
iterations_base_dir=self.iterations_dir,
iteration_number=self._iteration,
expression_updates=expressions,
)
sim_file = iter_dir / self.sim_file.name
prt_file = iter_dir / self.prt_file.name
else:
iter_dir = self.model_dir
sim_file = self.sim_file
prt_file = self.prt_file
# Create iteration output folder inside the study
iter_dir = self.iterations_dir / f"iter{self._iteration:03d}"
iter_dir.mkdir(parents=True, exist_ok=True)
# Step 2: Run NX journal (update expressions + solve)
try:
# Step 1: Solve directly on master model (no file copying)
# NX file references stay intact — expressions updated in-place by journal
sim_file = self.sim_file
prt_file = self.prt_file
# Save trial params to iteration folder
import json
params_file = iter_dir / "params.json"
params_file.write_text(json.dumps({
"iteration": self._iteration,
"expressions": expressions,
"trial_input": {
"beam_half_core_thickness": trial.beam_half_core_thickness,
"beam_face_thickness": trial.beam_face_thickness,
"holes_diameter": trial.holes_diameter,
"hole_count": trial.hole_count,
},
}, indent=2))
# Step 2: Run NX journal (update expressions + solve) on master model
solve_result = self._nx_solver.run_simulation(
sim_file=sim_file,
working_dir=iter_dir,
working_dir=self.model_dir,
expression_updates=expressions,
)
@@ -348,6 +360,9 @@ class AtomizerNXSolver:
logger.warning("Stress extraction failed: %s", e)
max_vm_stress = float("nan")
# Step 6: Copy solver outputs to iteration folder for archival
self._archive_iteration(iter_dir, op2_path, mass_kg, tip_displacement, max_vm_stress)
elapsed = time.time() - start_time
logger.info(
"Trial %d complete: mass=%.2f kg, disp=%.3f mm, stress=%.1f MPa (%.1fs)",
@@ -373,6 +388,52 @@ class AtomizerNXSolver:
iteration_dir=str(iter_dir) if 'iter_dir' in locals() else None,
)
def _archive_iteration(
self,
iter_dir: Path,
op2_path: Path,
mass: float,
displacement: float,
stress: float,
) -> None:
"""Copy solver outputs to iteration folder for archival.
Keeps the models/ directory clean — solver outputs go to the study's
iterations/ folder. Each iteration gets: OP2, F06, mass file, and
a results summary JSON.
"""
import json
import shutil
# Copy OP2 and F06 files
for suffix in [".op2", ".f06", ".log"]:
src = op2_path.with_suffix(suffix)
if src.exists():
try:
shutil.copy2(src, iter_dir / src.name)
except Exception as e:
logger.warning("Could not copy %s: %s", src.name, e)
# Copy mass temp file if it exists
for fname in ["_temp_mass.txt", "_temp_part_properties.json"]:
src = self.model_dir / fname
if src.exists():
try:
shutil.copy2(src, iter_dir / fname)
except Exception as e:
logger.warning("Could not copy %s: %s", fname, e)
# Write results summary
results_file = iter_dir / "results.json"
results_file.write_text(json.dumps({
"mass_kg": mass,
"tip_displacement_mm": displacement,
"max_von_mises_mpa": stress,
"op2_file": op2_path.name,
}, indent=2))
logger.info("Archived iteration %d to %s", self._iteration, iter_dir.name)
def close(self) -> None:
"""Clean up NX solver resources."""
logger.info("AtomizerNXSolver closed. %d iterations completed.", self._iteration)