Full cycle now automated:
1. Delete old extrude (if exists)
2. Delete old sketch (try ReplaceFeatureBuilder first, fallback to delete)
3. Create new sketch with rib geometry
4. Extrude new sketch by rib thickness along face normal
5. Name both features for identification on next iteration
Rib thickness read from profile JSON (parameters_used.thickness)
with fallback to geometry JSON or default 10mm.
No more manual extrude step needed between iterations.
Replaces naive ReplaceEntity approach with NX's proper
ReplaceFeatureBuilder API. This mirrors the interactive
right-click → Replace Feature workflow:
- SelectFeature: old sketch to replace
- ReplacementFeature: new sketch taking over
- DoAutomaticGeomMatch: auto-maps curves
- DeleteOriginalFeature: removes old after remap
- Fallback: manual delete if builder fails
All downstream features (extrude, trim) automatically
re-reference the new sketch.
Complete rewrite of triangulation engine:
- Regular hexagonal-packed vertex grid (equilateral triangles)
- Density-adaptive refinement: denser near holes, coarser in open areas
- Boundary-conforming vertices along frame edge and hole keepouts
- Delaunay on point set + clip to valid region (inside frame, outside keepouts)
- Result: proper isogrid layout, 87 pockets from 234 triangles
- 553 NX entities, min fillet 4.89mm, mass 2770g
- No more dependency on Shewchuk Triangle library (scipy.spatial.Delaunay)
- Switched to uniform triangulation (no density-adaptive refinement for
initial pass — saves that for stress-informed iterations)
- s_min=45mm, s_max=55mm (was 12/35) — larger triangles fit 6mm fillets
- Boss keepout circles: 12 segments (was 32) — less boundary clutter
- Fillet must be >= 80% of r_f at every corner or pocket is skipped
- Result: 75 pockets, 481 NX entities, min fillet 4.85mm, mass 4066g
- adaptive_density=True param enables density refinement for future stress iterations
- Default r_f raised from 1.5mm to 6mm (machining constraint)
- Default min_pocket_radius raised to 6mm
- Pockets that can't fit r_f at any corner (within 80% tolerance) are
skipped entirely — left solid for more stiffness in tight areas
- Result: 26 pockets (was 432), 187 NX entities (was 13,061)
- Min fillet radius: 4.88mm, all >= 4.8mm (80% of 6mm)
- Mass: 4,601g (was 3,480g — more solid = heavier but manufacturable)
Model curves (part.Curves.CreateLine) are SmartObjects that can't be added
to a sketch via AddGeometry. Switch to SketchLineBuilder which creates
native sketch geometry directly (SetStartPoint/SetEndPoint/Commit).
- Extract mass RIGHT AFTER geometry rebuild while part is work part
- Replace unreliable p173 expression lookup with MeasureManager
- Skip re-extraction if mass already captured during rebuild
- Relax displacement constraint to 20mm (DEC-HB-012, CEO approved)
Root cause: journal hardcoded M1_Blank for bracket, failed silently on Beam.prt
Fix by: NX Expert + Manager diagnosis
Bug 1 — Journal (solve_simulation.py simple workflow):
Expression lookup for p173 fails silently for derived/measurement
expressions, so _temp_mass.txt was never written. Added MeasureManager
fallback via extract_part_mass() (already used in assembly workflow).
Bug 2 — Extractor (extract_mass_from_expression.py):
Journal writes 'p173=<value>' format but extractor tried float() on
the whole content including 'p173='. Added key=value parsing.
Defense in depth — nx_interface.py:
Added stdout parsing fallback: if _temp_mass.txt still missing, parse
mass from journal output captured via solver.py stdout passthrough.
Files changed:
- optimization_engine/nx/solve_simulation.py — MeasureManager fallback
- optimization_engine/extractors/extract_mass_from_expression.py — key=value parse
- optimization_engine/nx/solver.py — include stdout in result dict
- projects/hydrotech-beam/studies/01_doe_landscape/nx_interface.py — stdout fallback
Tags: hydrotech-beam, mass-extraction