diff --git a/optimization_engine/nx/solve_simulation.py b/optimization_engine/nx/solve_simulation.py index 6ebd09e2..aa1706a5 100644 --- a/optimization_engine/nx/solve_simulation.py +++ b/optimization_engine/nx/solve_simulation.py @@ -1013,6 +1013,15 @@ def solve_simple_workflow( theSession.DeleteUndoMark(markId_update, "NX update") print(f"[JOURNAL] Geometry rebuilt ({nErrs} errors)") + # Extract mass NOW while geometry part is work part and freshly rebuilt + # This is the most reliable timing — solid bodies reflect updated parameters + print(f"[JOURNAL] Extracting mass from {workPart.Name}...") + try: + mass_kg = extract_part_mass(theSession, workPart, working_dir) + print(f"[JOURNAL] Mass extracted: {mass_kg:.6f} kg") + except Exception as mass_err: + print(f"[JOURNAL] WARNING: Mass extraction after rebuild failed: {mass_err}") + # Save geometry part print(f"[JOURNAL] Saving geometry part...") partSaveStatus_geom = workPart.Save( @@ -1171,25 +1180,22 @@ def solve_simple_workflow( ) # Extract mass and write to _temp_mass.txt - # Strategy: try expression lookup first, fall back to MeasureManager + # Strategy: Use MeasureManager on geometry part (most reliable) + # Note: Mass may have already been extracted during geometry rebuild phase above. + # This post-solve extraction ensures we have mass even if no expression updates were done. try: mass_value = None - # Attempt 1: Read mass from expression p173 on geometry part - try: - for part in theSession.Parts: - part_type = type(part).__name__ - if "fem" not in part_type.lower() and "sim" not in part_type.lower(): - for expr in part.Expressions: - if expr.Name == "p173": - mass_value = expr.Value - print(f"[JOURNAL] Mass expression p173 = {mass_value}") - break - break - except Exception as expr_err: - print(f"[JOURNAL] Expression lookup failed: {expr_err}") + # Check if mass was already written during geometry rebuild + mass_file = os.path.join(working_dir, "_temp_mass.txt") + if os.path.exists(mass_file) and expression_updates: + print(f"[JOURNAL] Mass already extracted during geometry rebuild phase") + with open(mass_file, "r") as f: + content = f.read().strip() + print(f"[JOURNAL] Existing mass file: {content}") + mass_value = -1 # sentinel to skip re-extraction - # Attempt 2: Use MeasureManager (more reliable for derived/measurement expressions) + # Use MeasureManager if mass not yet extracted if mass_value is None: print(f"[JOURNAL] Expression p173 not found, using MeasureManager fallback...") geom_part = None @@ -1206,12 +1212,12 @@ def solve_simple_workflow( print(f"[JOURNAL] MeasureManager failed: {mm_err}") # Write mass file (extract_part_mass may have already written it, but ensure consistency) - if mass_value is not None: + if mass_value is not None and mass_value != -1: mass_file = os.path.join(working_dir, "_temp_mass.txt") with open(mass_file, "w") as f: f.write(f"p173={mass_value}\n") print(f"[JOURNAL] Wrote mass to {mass_file}") - else: + elif mass_value is None: print(f"[JOURNAL] WARNING: Could not extract mass via expression or MeasureManager") except Exception as e: print(f"[JOURNAL] WARNING: Mass extraction failed: {e}") diff --git a/projects/hydrotech-beam/CONTEXT.md b/projects/hydrotech-beam/CONTEXT.md index 31a9778a..9d43df34 100644 --- a/projects/hydrotech-beam/CONTEXT.md +++ b/projects/hydrotech-beam/CONTEXT.md @@ -20,7 +20,7 @@ Minimize beam mass while meeting displacement and stress constraints. Single-obj | hole_span | `p6` (→ `Pattern_p9`) | 4,000 | TBD | mm | Potential (G15) | Total span for hole distribution | ## Constraints -- Max tip displacement: ≤ 10 mm +- Max tip displacement: ≤ 20 mm (relaxed from 10mm — CEO approved 2026-02-13, dummy case) - Max von Mises stress: ≤ ~130 MPa (steel, conservative — Gap G9) - Mass tracked via NX expression **`p173`** (`body_property147.mass` [kg]) diff --git a/projects/hydrotech-beam/DECISIONS.md b/projects/hydrotech-beam/DECISIONS.md index 86b73b47..e14357b7 100644 --- a/projects/hydrotech-beam/DECISIONS.md +++ b/projects/hydrotech-beam/DECISIONS.md @@ -79,6 +79,13 @@ Numbered decision log. Check here before proposing anything that contradicts a p - **Rationale:** Optuna DB is study-scoped and can be cleaned/reset. History DB provides permanent record across studies, enabling cross-study learning, debugging, and auditability. Append-only means no data loss. - **Status:** Approved — implemented and tested +## DEC-HB-012: Relax displacement constraint to 20mm +- **Date:** 2026-02-13 +- **By:** CEO +- **Decision:** Relax max tip displacement constraint from 10 mm to 20 mm. Stress constraint unchanged (≤ 130 MPa). +- **Rationale:** Hydrotech Beam is a dummy/proving case — goal is to validate the full optimization pipeline end-to-end, not achieve a real engineering target. 10mm was unreachable in the current design space (baseline 19.6mm, 0/51 DOE trials feasible). 20mm makes feasibility achievable. +- **Status:** Approved + ## DEC-HB-011: Git-only development workflow (Syncthing paused) - **Date:** 2026-02-11 - **By:** CEO