From d6a1d6eee116b242a73abc16146dbd4beee13130 Mon Sep 17 00:00:00 2001 From: Antoine Date: Sun, 15 Feb 2026 08:00:21 +0000 Subject: [PATCH] auto: daily sync --- docs/guides/DOCUMENTATION_BOUNDARIES.md | 37 + docs/guides/PKM_DASHBOARD_STANDARD.md | 48 + docs/plans/MODEL_INTROSPECTION_MASTER_PLAN.md | 874 ++++++++++ projects/hydrotech-beam/CONTEXT.md | 342 ++-- ...T.sync-conflict-20260214-191800-VBCUD7Z.md | 171 ++ projects/hydrotech-beam/README.md | 4 + projects/hydrotech-beam/USER_GUIDE.md | 65 + .../dashboard/EXECUTIVE_DASHBOARD.md | 33 + .../hydrotech-beam/dashboard/MASTER_PLAN.md | 194 +++ .../dashboard/OPERATIONS_DASHBOARD.md | 43 + .../dashboard/TECHNICAL_DASHBOARD.md | 43 + projects/hydrotech-beam/kb/_history.md | 96 +- ...y.sync-conflict-20260209-202842-VBCUD7Z.md | 44 +- ...y.sync-conflict-20260214-191758-VBCUD7Z.md | 48 + projects/hydrotech-beam/kb/_index.md | 182 +- ...x.sync-conflict-20260209-202842-VBCUD7Z.md | 94 +- ...x.sync-conflict-20260214-191751-VBCUD7Z.md | 91 + .../kb/components/sandwich-beam.md | 152 +- ...m.sync-conflict-20260209-202842-VBCUD7Z.md | 104 +- ...m.sync-conflict-20260214-191802-VBCUD7Z.md | 76 + ...1.sync-conflict-20260209-202842-VBCUD7Z.md | 116 +- projects/hydrotech-beam/kb/dev/gen-002.md | 412 ++--- ...2.sync-conflict-20260214-191754-VBCUD7Z.md | 206 +++ .../kb/fea/models/sol101-static.md | 296 ++-- ...c.sync-conflict-20260209-202842-VBCUD7Z.md | 92 +- ...c.sync-conflict-20260214-191758-VBCUD7Z.md | 148 ++ .../hydrotech-beam/kb/materials/steel-aisi.md | 118 +- ...i.sync-conflict-20260209-202842-VBCUD7Z.md | 52 +- ...i.sync-conflict-20260214-191804-VBCUD7Z.md | 59 + projects/hydrotech-beam/models/Beam.prt | Bin 293258 -> 293259 bytes projects/hydrotech-beam/models/Beam_fem1.fem | Bin 3682623 -> 3682627 bytes .../hydrotech-beam/models/Beam_fem1_i.prt | Bin 223898 -> 223886 bytes projects/hydrotech-beam/models/Beam_sim1.sim | Bin 6573453 -> 6573451 bytes projects/hydrotech-beam/models/README.md | 50 +- ...E.sync-conflict-20260214-191801-VBCUD7Z.md | 25 + ...E.sync-conflict-20260214-191802-VBCUD7Z.md | 25 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + projects/hydrotech-beam/models/_temp_mass.txt | 2 +- .../models/_temp_part_properties.json | 15 + projects/hydrotech-beam/playbooks/DOE.md | 39 + .../hydrotech-beam/playbooks/NX_REAL_RUN.md | 38 + .../playbooks/SYNCTHING_RECOVERY.md | 38 + .../01_doe_landscape/OPTIMIZATION_STRATEGY.md | 1028 ++++++------ ...Y.sync-conflict-20260214-191756-VBCUD7Z.md | 514 ++++++ .../studies/01_doe_landscape/README.md | 470 +++--- ...E.sync-conflict-20260214-191752-VBCUD7Z.md | 235 +++ .../01_doe_landscape/_model_backup/README.md | 50 +- ...E.sync-conflict-20260214-191802-VBCUD7Z.md | 25 + .../01_doe_landscape/atomizer_spec_draft.json | 604 +++---- ...sync-conflict-20260214-191806-VBCUD7Z.json | 302 ++++ .../01_doe_landscape/geometric_checks.py | 410 ++--- ...s.sync-conflict-20260214-191802-VBCUD7Z.py | 205 +++ .../studies/01_doe_landscape/history.py | 470 +++--- ...y.sync-conflict-20260214-191750-VBCUD7Z.py | 235 +++ .../01_doe_landscape/iteration_manager.py | 498 +++--- ...r.sync-conflict-20260214-191804-VBCUD7Z.py | 249 +++ ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter001/_temp_mass.txt | 2 +- .../iter001/_temp_part_properties.json | 15 + .../iterations/iter001/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter002/_temp_mass.txt | 2 +- .../iter002/_temp_part_properties.json | 15 + ....sync-conflict-20260214-191807-VBCUD7Z.exp | 4 + .../iterations/iter002/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter003/_temp_mass.txt | 2 +- .../iter003/_temp_part_properties.json | 15 + .../iterations/iter003/params.json | 28 +- ...sync-conflict-20260214-141755-RBNC225.json | 15 + .../iterations/iter003/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter004/_temp_mass.txt | 2 +- .../iter004/_temp_part_properties.json | 15 + .../iterations/iter004/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter004/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter005/_temp_mass.txt | 2 +- .../iter005/_temp_part_properties.json | 15 + .../iterations/iter005/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter005/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter006/_temp_mass.txt | 2 +- .../iter006/_temp_part_properties.json | 15 + .../iterations/iter006/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter006/results.json | 14 +- ...sync-conflict-20260214-141755-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter007/_temp_mass.txt | 2 +- .../iter007/_temp_part_properties.json | 15 + .../iterations/iter007/params.json | 28 +- ...sync-conflict-20260214-141755-RBNC225.json | 15 + .../iterations/iter007/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter008/_temp_mass.txt | 2 +- .../iter008/_temp_part_properties.json | 15 + .../iterations/iter008/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter008/results.json | 14 +- ...sync-conflict-20260214-141755-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter009/_temp_mass.txt | 2 +- .../iter009/_temp_part_properties.json | 15 + .../iterations/iter009/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter009/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter010/_temp_mass.txt | 2 +- .../iter010/_temp_part_properties.json | 15 + .../iterations/iter010/params.json | 28 +- ...sync-conflict-20260214-141755-RBNC225.json | 15 + .../iterations/iter010/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter011/_temp_mass.txt | 2 +- .../iter011/_temp_part_properties.json | 15 + .../iterations/iter011/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter011/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter012/_temp_mass.txt | 2 +- .../iter012/_temp_part_properties.json | 15 + .../iterations/iter012/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter012/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter013/_temp_mass.txt | 2 +- .../iter013/_temp_part_properties.json | 15 + .../iterations/iter013/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter013/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter014/_temp_mass.txt | 2 +- .../iter014/_temp_part_properties.json | 15 + .../iterations/iter014/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter014/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter015/_temp_mass.txt | 2 +- .../iter015/_temp_part_properties.json | 15 + .../iterations/iter015/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter015/results.json | 14 +- ...sync-conflict-20260214-141755-RBNC225.json | 8 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter016/_temp_mass.txt | 2 +- .../iter016/_temp_part_properties.json | 15 + .../iterations/iter016/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter016/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter017/_temp_mass.txt | 2 +- .../iter017/_temp_part_properties.json | 15 + .../iterations/iter017/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter017/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter018/_temp_mass.txt | 2 +- .../iter018/_temp_part_properties.json | 15 + .../iterations/iter018/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter018/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter019/_temp_mass.txt | 2 +- .../iter019/_temp_part_properties.json | 15 + .../iterations/iter019/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter019/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter020/_temp_mass.txt | 2 +- .../iter020/_temp_part_properties.json | 15 + .../iterations/iter020/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter020/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter021/_temp_mass.txt | 2 +- .../iter021/_temp_part_properties.json | 15 + .../iterations/iter021/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter021/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter022/_temp_mass.txt | 2 +- .../iter022/_temp_part_properties.json | 15 + .../iterations/iter022/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter022/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter023/_temp_mass.txt | 2 +- .../iter023/_temp_part_properties.json | 15 + .../iterations/iter023/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter023/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter024/_temp_mass.txt | 2 +- .../iter024/_temp_part_properties.json | 15 + .../iterations/iter024/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter024/results.json | 14 +- ...sync-conflict-20260214-141755-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter025/_temp_mass.txt | 2 +- .../iter025/_temp_part_properties.json | 15 + .../iterations/iter025/params.json | 28 +- ...sync-conflict-20260214-141755-RBNC225.json | 15 + .../iterations/iter025/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter026/_temp_mass.txt | 2 +- .../iter026/_temp_part_properties.json | 15 + .../iterations/iter026/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter026/results.json | 14 +- ...sync-conflict-20260214-141755-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter027/_temp_mass.txt | 2 +- .../iter027/_temp_part_properties.json | 15 + .../iterations/iter027/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter027/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter028/_temp_mass.txt | 2 +- .../iter028/_temp_part_properties.json | 15 + .../iterations/iter028/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter028/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter029/_temp_mass.txt | 2 +- .../iter029/_temp_part_properties.json | 15 + .../iterations/iter029/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter029/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter030/_temp_mass.txt | 2 +- .../iter030/_temp_part_properties.json | 15 + .../iterations/iter030/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter030/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter031/_temp_mass.txt | 2 +- .../iter031/_temp_part_properties.json | 15 + .../iterations/iter031/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter031/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter032/_temp_mass.txt | 2 +- .../iter032/_temp_part_properties.json | 15 + .../iterations/iter032/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter032/results.json | 14 +- ...sync-conflict-20260214-141755-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter033/_temp_mass.txt | 2 +- .../iter033/_temp_part_properties.json | 15 + .../iterations/iter033/params.json | 28 +- ...sync-conflict-20260214-141755-RBNC225.json | 15 + .../iterations/iter033/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter034/_temp_mass.txt | 2 +- .../iter034/_temp_part_properties.json | 15 + .../iterations/iter034/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter034/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter035/_temp_mass.txt | 2 +- .../iter035/_temp_part_properties.json | 15 + .../iterations/iter035/params.json | 28 +- ...sync-conflict-20260214-141755-RBNC225.json | 15 + .../iterations/iter035/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter036/_temp_mass.txt | 2 +- .../iter036/_temp_part_properties.json | 15 + .../iterations/iter036/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter036/results.json | 14 +- ...sync-conflict-20260214-141755-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter037/_temp_mass.txt | 2 +- .../iter037/_temp_part_properties.json | 15 + .../iterations/iter037/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter037/results.json | 14 +- ...sync-conflict-20260214-141755-RBNC225.json | 8 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter038/_temp_mass.txt | 2 +- .../iter038/_temp_part_properties.json | 15 + .../iterations/iter038/params.json | 28 +- ...sync-conflict-20260214-141755-RBNC225.json | 15 + .../iterations/iter038/results.json | 14 +- ...sync-conflict-20260214-141754-RBNC225.json | 8 + ....sync-conflict-20260214-141754-RBNC225.txt | 1 + ....sync-conflict-20260214-141755-RBNC225.txt | 1 + .../iterations/iter039/_temp_mass.txt | 2 +- .../iter039/_temp_part_properties.json | 15 + .../iterations/iter039/params.json | 28 +- ...sync-conflict-20260214-141754-RBNC225.json | 15 + .../iterations/iter039/results.json | 14 +- ...sync-conflict-20260214-141755-RBNC225.json | 8 + .../studies/01_doe_landscape/nx_interface.py | 1146 ++++++------- ...e.sync-conflict-20260214-191757-VBCUD7Z.py | 573 +++++++ ....sync-conflict-20260214-191754-VBCUD7Z.txt | 7 + .../studies/01_doe_landscape/requirements.txt | 14 +- .../doe_results.csv | 103 ++ .../doe_summary.json | 64 + .../history.db | Bin 0 -> 45056 bytes ....sync-conflict-20260214-191750-VBCUD7Z.csv | 52 + ...sync-conflict-20260214-191802-VBCUD7Z.json | 64 + ...y.sync-conflict-20260214-191804-VBCUD7Z.db | Bin 0 -> 32768 bytes ...y.sync-conflict-20260214-191758-VBCUD7Z.db | Bin 0 -> 114688 bytes .../01_doe_landscape/results/doe_results.csv | 104 +- .../01_doe_landscape/results/doe_summary.json | 126 +- .../01_doe_landscape/results/history.db | Bin 32768 -> 45056 bytes .../studies/01_doe_landscape/run_doe.py | 1474 ++++++++--------- ...e.sync-conflict-20260214-191753-VBCUD7Z.py | 737 +++++++++ .../studies/01_doe_landscape/sampling.py | 548 +++--- ...g.sync-conflict-20260214-191800-VBCUD7Z.py | 274 +++ 376 files changed, 12864 insertions(+), 5377 deletions(-) create mode 100644 docs/guides/DOCUMENTATION_BOUNDARIES.md create mode 100644 docs/guides/PKM_DASHBOARD_STANDARD.md create mode 100644 docs/plans/MODEL_INTROSPECTION_MASTER_PLAN.md create mode 100644 projects/hydrotech-beam/CONTEXT.sync-conflict-20260214-191800-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/USER_GUIDE.md create mode 100644 projects/hydrotech-beam/dashboard/EXECUTIVE_DASHBOARD.md create mode 100644 projects/hydrotech-beam/dashboard/MASTER_PLAN.md create mode 100644 projects/hydrotech-beam/dashboard/OPERATIONS_DASHBOARD.md create mode 100644 projects/hydrotech-beam/dashboard/TECHNICAL_DASHBOARD.md create mode 100644 projects/hydrotech-beam/kb/_history.sync-conflict-20260214-191758-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/kb/_index.sync-conflict-20260214-191751-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/kb/components/sandwich-beam.sync-conflict-20260214-191802-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/kb/dev/gen-002.sync-conflict-20260214-191754-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/kb/fea/models/sol101-static.sync-conflict-20260214-191758-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/kb/materials/steel-aisi.sync-conflict-20260214-191804-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/models/README.sync-conflict-20260214-191801-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/models/README.sync-conflict-20260214-191802-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/models/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/models/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/playbooks/DOE.md create mode 100644 projects/hydrotech-beam/playbooks/NX_REAL_RUN.md create mode 100644 projects/hydrotech-beam/playbooks/SYNCTHING_RECOVERY.md create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/OPTIMIZATION_STRATEGY.sync-conflict-20260214-191756-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/README.sync-conflict-20260214-191752-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/_model_backup/README.sync-conflict-20260214-191802-VBCUD7Z.md create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/atomizer_spec_draft.sync-conflict-20260214-191806-VBCUD7Z.json mode change 100644 => 100755 projects/hydrotech-beam/studies/01_doe_landscape/geometric_checks.py create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/geometric_checks.sync-conflict-20260214-191802-VBCUD7Z.py mode change 100644 => 100755 projects/hydrotech-beam/studies/01_doe_landscape/history.py create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/history.sync-conflict-20260214-191750-VBCUD7Z.py mode change 100644 => 100755 projects/hydrotech-beam/studies/01_doe_landscape/iteration_manager.py create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iteration_manager.sync-conflict-20260214-191804-VBCUD7Z.py create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/params.sync-conflict-20260214-191807-VBCUD7Z.exp create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/params.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/results.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/params.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/results.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/params.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/results.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/results.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/params.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/results.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/results.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/params.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/params.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/results.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/results.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/params.sync-conflict-20260214-141755-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/results.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_part_properties.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/params.sync-conflict-20260214-141754-RBNC225.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/results.sync-conflict-20260214-141755-RBNC225.json mode change 100644 => 100755 projects/hydrotech-beam/studies/01_doe_landscape/nx_interface.py create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/nx_interface.sync-conflict-20260214-191757-VBCUD7Z.py create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/requirements.sync-conflict-20260214-191754-VBCUD7Z.txt create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/doe_results.csv create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/doe_summary.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/history.db create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/doe_results.sync-conflict-20260214-191750-VBCUD7Z.csv create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/doe_summary.sync-conflict-20260214-191802-VBCUD7Z.json create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/history.sync-conflict-20260214-191804-VBCUD7Z.db create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/optuna_study.sync-conflict-20260214-191758-VBCUD7Z.db mode change 100644 => 100755 projects/hydrotech-beam/studies/01_doe_landscape/run_doe.py create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/run_doe.sync-conflict-20260214-191753-VBCUD7Z.py mode change 100644 => 100755 projects/hydrotech-beam/studies/01_doe_landscape/sampling.py create mode 100644 projects/hydrotech-beam/studies/01_doe_landscape/sampling.sync-conflict-20260214-191800-VBCUD7Z.py diff --git a/docs/guides/DOCUMENTATION_BOUNDARIES.md b/docs/guides/DOCUMENTATION_BOUNDARIES.md new file mode 100644 index 00000000..1bd30ca3 --- /dev/null +++ b/docs/guides/DOCUMENTATION_BOUNDARIES.md @@ -0,0 +1,37 @@ +# Documentation Boundaries (Atomizer Standard) + +## Rule +- **Project-specific content** belongs in `projects//...` +- **Foundational / reusable content** belongs in `docs/...` + +This is a hard rule for all agents. + +## What is project-specific? +Store in `projects/...`: +- Run logs, experiment outcomes, project decisions +- Project playbooks tied to one geometry/client/study +- Project troubleshooting notes and sync incidents +- Project KPIs, deliverables, and channel-specific context + +## What is foundational? +Store in `docs/...`: +- Generic workflows used by multiple projects +- Platform-level operator guides +- Reusable run/checklist templates +- Protocols and standards +- Cross-project troubleshooting procedures + +## Ownership +- **Manager owns documentation quality and structure.** +- Drafting can be delegated, but final responsibility remains with Manager. + +## Operating procedure +When writing docs: +1. Decide: project-specific or foundational. +2. Write in the correct location first (no temporary drift). +3. If a project doc becomes reusable, promote a generalized version into `docs/...` and keep project examples in the project folder. +4. Cross-link both locations when useful. + +## Current Hydrotech application +- Hydrotech run/playbook files remain in `projects/hydrotech-beam/...`. +- Reusable standards from Hydrotech are promoted into `docs/guides/...` as they stabilize. diff --git a/docs/guides/PKM_DASHBOARD_STANDARD.md b/docs/guides/PKM_DASHBOARD_STANDARD.md new file mode 100644 index 00000000..dbe809b2 --- /dev/null +++ b/docs/guides/PKM_DASHBOARD_STANDARD.md @@ -0,0 +1,48 @@ +# PKM Dashboard Standard (Atomizer) + +## Purpose +Standardize how project dashboards and reports are built across Atomizer. + +## Core Standard +1. **Contracts-first:** every metric must map to structured source records. +2. **Markdown-first:** default delivery is markdown snapshots. +3. **Role-based views:** Executive, Technical, Operations. +4. **Governance-first:** clear owner, schema validation, gate policy. + +## Directory split +- Project-specific: `projects//...` +- Foundational: `docs/...` + +## Minimum required artifacts per project +- `dashboard/MASTER_PLAN.md` +- `dashboard/EXECUTIVE_DASHBOARD.md` +- `dashboard/TECHNICAL_DASHBOARD.md` +- `dashboard/OPERATIONS_DASHBOARD.md` +- `reports/` (daily/weekly/gate) +- `runs/` manifests +- `decisions/` append-only log +- `incidents/` append-only log + +## Minimum required contracts +- `run_manifest.v1` +- `study_summary.v1` +- `trial_result.v1` +- `decision_record.v1` +- `risk_record.v1` +- `gate_evaluation.v1` +- `incident_record.v1` + +## Gate semantics +- `PASS` / `CONDITIONAL_PASS` / `FAIL` +- `FAIL` blocks progression +- `CONDITIONAL_PASS` requires owner + due date + explicit acceptance + +## Quality controls +- Ingestion schema validation +- Nightly integrity checks +- Freshness SLA checks +- Auditor spot checks + +## Evolution policy +- Start markdown-native +- Add richer visual layer only over same contracts (no parallel truth) diff --git a/docs/plans/MODEL_INTROSPECTION_MASTER_PLAN.md b/docs/plans/MODEL_INTROSPECTION_MASTER_PLAN.md new file mode 100644 index 00000000..72840cb3 --- /dev/null +++ b/docs/plans/MODEL_INTROSPECTION_MASTER_PLAN.md @@ -0,0 +1,874 @@ +# Model Introspection Master Plan โ€” v1.0 + +**Authors:** Technical Lead ๐Ÿ”ง (plan owner), NX Expert ๐Ÿ–ฅ๏ธ (initial research) +**Date:** 2026-02-15 +**Status:** Approved for R&D implementation +**Location:** `docs/plans/MODEL_INTROSPECTION_MASTER_PLAN.md` + +--- + +## 1. Executive Summary + +Atomizer currently executes optimization studies that users manually configure. It has **basic introspection** โ€” expression extraction, mass properties, partial solver config โ€” but lacks the deep model knowledge needed to *understand* what it's optimizing. + +This plan defines a **four-layer introspection framework** that captures the complete data picture of any NX CAD/FEA model before optimization. The output is a single structured JSON file (`model_introspection.json`) containing everything an engineer or agent needs to design a sound optimization study: + +- **What can change** โ€” design variables, expressions, parametric geometry +- **What the FEA model looks like** โ€” mesh quality, element types, materials, properties +- **What physics governs the problem** โ€” boundary conditions, loads, solver config, subcases +- **Where we're starting from** โ€” baseline displacement, stress, frequency, mass + +A fifth layer (dependency mapping / expression graphs) is **deferred to v2** due to NXOpen API limitations that make reliable extraction impractical today. + +**Timeline:** 12โ€“17 working days for production-quality v1. +**Primary tools:** NXOpen Python API (Layer 1), pyNastran BDF (Layers 2โ€“3), pyNastran OP2 (Layer 4). + +--- + +## 2. Current State + +### 2.1 What Exists + +| Script | Location | Extracts | Output | +|--------|----------|----------|--------| +| `introspect_part.py` | `nx_journals/` | Expressions (user/internal), mass, materials, bodies, features, datums, units | `_temp_introspection.json` | +| `introspect_sim.py` | `nx_journals/` | Solutions (partial), BCs (partial), subcases (exploratory) | `_introspection_sim.json` | +| `discover_model.py` | `nx_journals/` | Quick scan of expressions + solutions | JSON to stdout | +| `extract_displacement.py` | `optimization_engine/extractors/` | Max displacement from OP2 | Per-trial result | +| `extract_von_mises_stress.py` | `optimization_engine/extractors/` | Max von Mises from OP2 | Per-trial result | +| `extract_part_mass_material.py` | `nx_journals/` | Mass + material from part | JSON | + +### 2.2 What's Missing + +- โŒ **Mesh quality metrics** โ€” no aspect ratio, jacobian, warpage, skew +- โŒ **BC/load details** โ€” no magnitudes, DOF specifications, target node/element sets +- โŒ **Solver configuration** โ€” no output requests, convergence settings, solution sequence details +- โŒ **Property cards** โ€” no PSHELL thickness, PSOLID assignments, property-element mapping +- โŒ **Baseline results in one place** โ€” extractors exist but aren't aggregated pre-optimization +- โŒ **Unified output** โ€” no single JSON capturing the full model state +- โŒ **Validation** โ€” no cross-checking between data sources + +--- + +## 3. Framework Architecture + +### 3.1 Four-Layer Model (v1) + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Layer 1: GEOMETRIC PARAMETERS [NXOpen API] โ”‚ +โ”‚ Expressions, features, mass, materials, units โ”‚ +โ”‚ โ†’ What can be optimized? โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ Layer 2: FEA MODEL STRUCTURE [pyNastran BDF] โ”‚ +โ”‚ Mesh quality, element types, materials, properties โ”‚ +โ”‚ โ†’ What's the baseline mesh health? โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ Layer 3: SOLVER CONFIGURATION [pyNastran BDF] โ”‚ +โ”‚ Solutions, subcases, BCs, loads, output requests โ”‚ +โ”‚ โ†’ What physics governs the problem? โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ Layer 4: BASELINE RESULTS [pyNastran OP2] โ”‚ +โ”‚ Pre-optimization stress, displacement, frequency, mass โ”‚ +โ”‚ โ†’ Where are we starting from? โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + + DEFERRED TO v2: + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Layer 5: DEPENDENCIES & RELATIONSHIPS โ”‚ + โ”‚ Expression graph, feature tree, parametric sensitivities โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### 3.2 Design Principles + +1. **One source per data type.** Don't mix NXOpen and pyNastran for the same data. NXOpen owns geometry/expressions; pyNastran owns FEA/solver data. +2. **BDF export is a prerequisite.** Layer 2โ€“3 extraction requires a current BDF file. The orchestrator must trigger a BDF export (or verify freshness) before parsing. +3. **Report, don't recommend.** v1 reports what exists in the model. It does NOT auto-suggest bounds, objectives, or study types. That's the engineer's job. +4. **Fail gracefully.** If one layer fails, the others still produce output. Partial introspection is better than no introspection. +5. **Validate across sources.** Where data overlaps (element count, mass, materials), cross-check and flag discrepancies. + +### 3.3 Data Flow + +``` + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ .prt โ”‚ + โ”‚ file โ”‚ + โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + NXOpen API + โ”‚ + โ–ผ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Layer 1 โ”‚ + โ”‚ Geometric โ”‚ + โ”‚ Parameters โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ โ”‚ โ”‚ + โ–ผ โ–ผ โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ .bdf โ”‚ โ”‚ .sim โ”‚ โ”‚ .op2 โ”‚ +โ”‚ file โ”‚ โ”‚ file โ”‚ โ”‚ file โ”‚ +โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ (metadata only โ”‚ + โ”‚ via NXOpen) โ”‚ +pyNastran BDF pyNastran OP2 + โ”‚ โ”‚ + โ–ผ โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Layer 2+3 โ”‚ โ”‚ Layer 4 โ”‚ +โ”‚ FEA Model โ”‚ โ”‚ Baseline โ”‚ +โ”‚ + Solver โ”‚ โ”‚ Results โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ–ผ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Orchestrator โ”‚ + โ”‚ Merge + JSON โ”‚ + โ”‚ + Validate โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ–ผ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ model_introspection.json โ”‚ + โ”‚ introspection_summary.md โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +--- + +## 4. Master JSON Schema + +### 4.1 Top-Level Structure + +```json +{ + "introspection_version": "1.0.0", + "timestamp": "ISO-8601", + "model_id": "string โ€” derived from part filename", + "files": { + "part": "path/to/model.prt", + "sim": "path/to/model_sim1.sim", + "fem": "path/to/model_fem1.fem", + "bdf": "path/to/exported.bdf", + "op2": "path/to/results.op2" + }, + "geometric_parameters": { "..." }, + "fea_model": { "..." }, + "solver_configuration": { "..." }, + "baseline_results": { "..." }, + "candidate_design_variables": [ "..." ], + "validation": { "..." }, + "metadata": { + "extraction_time_seconds": 0.0, + "layers_completed": ["geometric", "fea_model", "solver", "baseline"], + "layers_failed": [], + "warnings": [] + } +} +``` + +### 4.2 Layer 1 โ€” `geometric_parameters` + +```json +{ + "expressions": { + "user_defined": [ + { + "name": "thickness", + "value": 3.0, + "units": "mm", + "formula": "3.0", + "is_constant": false, + "part": "bracket.prt" + } + ], + "internal": [ + { + "name": "p47", + "value": 6.0, + "units": "mm", + "formula": "thickness * 2" + } + ], + "total_user": 3, + "total_internal": 52 + }, + "mass_properties": { + "mass_kg": 0.234, + "volume_mm3": 85000.0, + "surface_area_mm2": 15000.0, + "center_of_gravity_mm": [12.3, 45.6, 78.9], + "num_solid_bodies": 1 + }, + "materials": [ + { + "name": "Aluminum 6061-T6", + "assigned_to_bodies": ["Body(1)"], + "properties": { + "density_kg_m3": 2700.0, + "youngs_modulus_MPa": 68900.0, + "poisson_ratio": 0.33, + "yield_strength_MPa": 276.0, + "ultimate_strength_MPa": 310.0 + }, + "source": "NXOpen part material" + } + ], + "features": { + "total_count": 12, + "by_type": { + "Extrude": 3, + "Shell": 1, + "Sketch": 2, + "Datum Plane": 2, + "Fillet": 4 + }, + "suppressed_count": 0 + }, + "units": { + "length": "Millimeter", + "mass": "Kilogram", + "force": "Newton", + "temperature": "Celsius", + "system": "Metric (mm, kg, N, ยฐC)" + } +} +``` + +### 4.3 Layer 2 โ€” `fea_model` + +```json +{ + "mesh": { + "total_nodes": 12450, + "total_elements": 8234, + "element_types": { + "CTETRA": { "count": 7800, "order": "linear" }, + "CQUAD4": { "count": 434, "order": "linear" } + }, + "quality_metrics": { + "aspect_ratio": { + "min": 1.02, + "max": 8.34, + "mean": 2.45, + "std": 1.23, + "p95": 5.12, + "threshold": 10.0, + "elements_exceeding": 0 + }, + "jacobian": { + "min": 0.62, + "max": 1.0, + "mean": 0.91, + "threshold": 0.5, + "elements_below": 0 + }, + "warpage_deg": { + "max": 5.2, + "threshold": 10.0, + "elements_exceeding": 0 + }, + "skew_deg": { + "max": 45.2, + "threshold": 60.0, + "elements_exceeding": 0 + } + }, + "quality_verdict": "PASS" + }, + "materials": [ + { + "mat_id": 1, + "card_type": "MAT1", + "name": "Aluminum 6061-T6", + "E_MPa": 68900.0, + "G_MPa": 25900.0, + "nu": 0.33, + "rho": 2.7e-6, + "alpha": 2.36e-5 + } + ], + "properties": [ + { + "prop_id": 1, + "card_type": "PSHELL", + "thickness_mm": 3.0, + "mat_id": 1, + "element_count": 434 + }, + { + "prop_id": 2, + "card_type": "PSOLID", + "mat_id": 1, + "element_count": 7800 + } + ] +} +``` + +**Mesh quality computation:** All quality metrics are computed from element node coordinates using pyNastran's geometry data. For each element, aspect ratio = longest edge / shortest edge. Jacobian is computed at element integration points. This is more reliable than NXOpen's `QualityAuditBuilder` which has limited documentation. + +### 4.4 Layer 3 โ€” `solver_configuration` + +```json +{ + "solutions": [ + { + "name": "Solution 1", + "sol_sequence": 101, + "sol_type": "Static Linear", + "solver": "NX Nastran" + } + ], + "subcases": [ + { + "id": 1, + "label": "Subcase - Static 1", + "load_set_id": 1, + "spc_set_id": 1, + "output_requests": { + "displacement": { "format": "OP2", "scope": "ALL" }, + "stress": { "format": "OP2", "scope": "ALL" }, + "strain": null, + "force": null + } + } + ], + "constraints": [ + { + "spc_id": 1, + "type": "SPC1", + "dofs_constrained": [1, 2, 3, 4, 5, 6], + "dof_labels": ["Tx", "Ty", "Tz", "Rx", "Ry", "Rz"], + "node_count": 145, + "node_ids_sample": [1, 2, 3, 4, 5], + "description": "Fixed support โ€” all 6 DOF" + } + ], + "loads": [ + { + "load_id": 1, + "type": "FORCE", + "node_id": 456, + "magnitude_N": 1000.0, + "direction": [0.0, -1.0, 0.0], + "components_N": { "Fx": 0.0, "Fy": -1000.0, "Fz": 0.0 } + }, + { + "load_id": 2, + "type": "PLOAD4", + "element_count": 25, + "pressure_MPa": 5.0, + "direction": "element normal" + } + ], + "bulk_data_stats": { + "total_cards": 15234, + "card_types": { + "GRID": 12450, + "CTETRA": 7800, + "CQUAD4": 434, + "MAT1": 1, + "PSHELL": 1, + "PSOLID": 1, + "SPC1": 1, + "FORCE": 1, + "PLOAD4": 1 + } + } +} +``` + +**BDF export requirement:** The orchestrator must ensure a current BDF file exists before Layer 2โ€“3 extraction. Options: +1. Export BDF via NXOpen journal (`sim.ExportNastranDeck()`) as the first orchestrator step +2. Accept a user-provided BDF path +3. Find the most recent BDF in the sim output directory and verify its timestamp + +Option 1 is preferred โ€” it guarantees freshness. + +### 4.5 Layer 4 โ€” `baseline_results` + +```json +{ + "source_op2": "bracket_sim1-solution_1.op2", + "solution": "Solution 1", + "subcase_id": 1, + "converged": true, + "displacement": { + "max_magnitude_mm": 2.34, + "max_node_id": 4567, + "max_component": "Tz", + "max_component_value_mm": -2.31, + "mean_magnitude_mm": 0.45 + }, + "stress": { + "von_mises": { + "max_MPa": 145.6, + "max_element_id": 2345, + "mean_MPa": 45.2, + "p95_MPa": 112.0 + }, + "margin_of_safety": { + "yield": 0.89, + "ultimate": 1.13, + "yield_strength_MPa": 276.0, + "ultimate_strength_MPa": 310.0, + "note": "MoS = (allowable / actual) - 1" + } + }, + "modal": null, + "mass_from_solver_kg": 0.234 +} +``` + +**Note:** `modal` is populated only if a SOL 103 result exists. Fields would include `modes: [{number, frequency_hz, effective_mass_fraction}]`. + +### 4.6 `candidate_design_variables` + +This section **reports** expressions that are likely design variable candidates. It does NOT suggest bounds or objectives โ€” that's the engineer's job. + +```json +[ + { + "name": "thickness", + "current_value": 3.0, + "units": "mm", + "reason": "User-defined expression, non-constant, drives geometry" + }, + { + "name": "width", + "current_value": 50.0, + "units": "mm", + "reason": "User-defined expression, non-constant, drives geometry" + } +] +``` + +**Selection criteria:** An expression is a candidate DV if: +1. It is user-defined (not internal `p###`) +2. It is not constant (formula is not just a literal used in a single non-geometric context) +3. It has dimensional units (mm, deg, etc.) or is clearly a count +4. Its name is not a known system expression (e.g., `PI`, `TRUE`, unit names) + +### 4.7 `validation` + +```json +{ + "cross_checks": [ + { + "metric": "element_count", + "nxopen_value": 8234, + "pynastran_value": 8234, + "match": true + }, + { + "metric": "mass_kg", + "nxopen_value": 0.234, + "pynastran_value": 0.2338, + "match": false, + "delta_percent": 0.09, + "tolerance_percent": 1.0, + "within_tolerance": true, + "note": "Small delta due to mesh discretization vs. CAD geometry" + }, + { + "metric": "material_E_MPa", + "nxopen_value": 68900.0, + "pynastran_value": 68900.0, + "match": true + } + ], + "overall_status": "PASS", + "discrepancies": [] +} +``` + +--- + +## 5. Extraction Methods + +### 5.1 Layer 1 โ€” NXOpen Python API + +**Base script:** `nx_journals/introspect_part.py` (enhance, don't rewrite) + +| Data | API | Notes | +|------|-----|-------| +| Expressions | `part.Expressions` iterator | Filter user vs internal via name pattern (`p###` = internal) | +| Expression values | `expr.Value`, `expr.RightHandSide`, `expr.Units.Name` | | +| Mass properties | `part.MeasureManager.NewMassProperties()` | Requires solid body list | +| Materials | `body.GetPhysicalMaterial()` | Per-body; extract property values via `GetPropertyValue()` | +| Features | `part.Features` iterator | Type via `type(feature).__name__`; count suppressed | +| Units | `part.UnitCollection`, `part.PartUnits` | System-level unit identification | + +**Enhancement needed:** +- Add candidate DV identification logic +- Structure output to match schema ยง4.2 +- Add error handling for each extraction block (fail gracefully) + +### 5.2 Layers 2โ€“3 โ€” pyNastran BDF + +**New script:** `nx_journals/introspect_bdf.py` (or `optimization_engine/extractors/introspect_bdf.py`) + +| Data | pyNastran API | Notes | +|------|---------------|-------| +| Elements | `bdf.elements` dict | Key = EID, value has `.type` attribute | +| Nodes | `bdf.nodes` dict | Key = NID | +| Materials | `bdf.materials` dict | MAT1: `.E`, `.G`, `.nu`, `.rho` | +| Properties | `bdf.properties` dict | PSHELL: `.t`, `.mid1`; PSOLID: `.mid` | +| SPCs | `bdf.spcs` dict | SPC1: `.components` (DOF string), `.node_ids` | +| Forces | `bdf.loads` dict | FORCE: `.mag`, `.xyz` (direction vector) | +| Pressures | `bdf.loads` dict | PLOAD4: `.pressures`, element IDs | +| Subcases | `bdf.subcases` dict | `.params` for LOAD, SPC, output requests | +| Bulk stats | `bdf.card_count` | Card type โ†’ count | + +**Mesh quality computation** (pyNastran element geometry): + +```python +import numpy as np +from pyNastran.bdf.bdf import BDF + +def compute_mesh_quality(bdf_model): + """Compute mesh quality metrics from element geometry.""" + aspect_ratios = [] + + for eid, elem in bdf_model.elements.items(): + if elem.type in ('CQUAD4', 'CTRIA3'): + # Get node positions + nodes = [bdf_model.nodes[nid].get_position() for nid in elem.node_ids] + + # Compute edge lengths + edges = [] + n = len(nodes) + for i in range(n): + edge = np.linalg.norm(nodes[(i+1) % n] - nodes[i]) + edges.append(edge) + + ar = max(edges) / max(min(edges), 1e-12) + aspect_ratios.append(ar) + + elif elem.type == 'CTETRA': + nodes = [bdf_model.nodes[nid].get_position() for nid in elem.node_ids[:4]] + edges = [] + for i in range(4): + for j in range(i+1, 4): + edges.append(np.linalg.norm(nodes[j] - nodes[i])) + ar = max(edges) / max(min(edges), 1e-12) + aspect_ratios.append(ar) + + if not aspect_ratios: + return None + + ar = np.array(aspect_ratios) + return { + "min": float(ar.min()), + "max": float(ar.max()), + "mean": float(ar.mean()), + "std": float(ar.std()), + "p95": float(np.percentile(ar, 95)) + } +``` + +### 5.3 Layer 4 โ€” pyNastran OP2 + +**Leverage existing extractors:** +- `optimization_engine/extractors/extract_displacement.py` +- `optimization_engine/extractors/extract_von_mises_stress.py` + +**New aggregation script:** `nx_journals/introspect_baseline.py` + +| Data | pyNastran API | Notes | +|------|---------------|-------| +| Displacement | `op2.displacements[subcase_id].data` | Magnitude = sqrt(Txยฒ + Tyยฒ + Tzยฒ) | +| Stress | `op2.ctetra_stress[sc]` or `op2.cquad4_stress[sc]` | Von Mises column varies by element type | +| Eigenvalues | `op2.eigenvalues` | SOL 103 only | +| Grid point weight | `op2.grid_point_weight` | Solver-computed mass | + +### 5.4 BDF Export (Prerequisite Step) + +```python +# NXOpen journal to export BDF from .sim +def export_bdf(sim_path, output_bdf_path): + """Export Nastran input deck from simulation.""" + theSession = NXOpen.Session.GetSession() + # Open sim, find solution, export deck + # Details depend on NX version โ€” see introspect_sim.py patterns + pass +``` + +**Alternative:** If a solve has already been run, the BDF exists in the solver output directory. The orchestrator should check for it before triggering a fresh export. + +--- + +## 6. Implementation Phases + +### Phase 1: Enhanced Part Introspection (3โ€“4 days) + +**Goal:** Complete Layer 1 extraction with structured JSON output. + +**Tasks:** +1. Refactor `introspect_part.py` output to match schema ยง4.2 +2. Add candidate DV identification logic (ยง4.6 criteria) +3. Add feature type counting and suppression detection +4. Add material property extraction via `GetPropertyValue()` +5. Structured error handling โ€” each block in try/except, log failures +6. Unit tests with known bracket/beam models + +**Output:** `layer1_geometric.json` +**Owner:** NX Expert + +### Phase 2: BDF-Based FEA Model Introspection (3โ€“4 days) + +**Goal:** Complete Layer 2 extraction โ€” mesh, materials, properties, quality. + +**Tasks:** +1. Create `introspect_bdf.py` with pyNastran BDF parsing +2. Implement mesh quality computation (aspect ratio, jacobian, warpage, skew) +3. Extract material cards (MAT1, MAT2 logged but not parsed in v1) +4. Extract property cards (PSHELL, PSOLID) with element assignments +5. Compute quality verdict (PASS/WARN/FAIL based on thresholds) +6. Test on Hydrotech beam BDF and M1 mirror BDF + +**Output:** `layer2_fea_model.json` +**Owner:** NX Expert + +### Phase 3: BDF-Based Solver Configuration (3โ€“4 days) + +**Goal:** Complete Layer 3 extraction โ€” subcases, BCs, loads, output requests. + +**Tasks:** +1. Extend `introspect_bdf.py` with subcase parsing +2. Extract SPC constraints with DOF details and node counts +3. Extract loads (FORCE, PLOAD4, MOMENT, GRAV) with magnitudes and directions +4. Extract output requests from case control +5. Add bulk data card statistics +6. Integrate BDF export step (NXOpen journal or path detection) + +**Output:** `layer3_solver.json` +**Owner:** NX Expert + +### Phase 4: Baseline Results Aggregation (1โ€“2 days) + +**Goal:** Complete Layer 4 โ€” aggregate existing extractors into baseline JSON. + +**Tasks:** +1. Create `introspect_baseline.py` using existing OP2 extractors +2. Compute displacement max/mean with node identification +3. Compute stress max with element identification and MoS +4. Optionally extract modal frequencies if SOL 103 results exist +5. Extract solver-computed mass from grid point weight generator + +**Output:** `layer4_baseline.json` +**Owner:** NX Expert or Technical Lead + +### Phase 5: Orchestrator + Validation (2โ€“3 days) + +**Goal:** Single-command full introspection with cross-validation and summary. + +**Tasks:** +1. Create `run_introspection.py` orchestrator +2. Sequence: BDF export โ†’ Layer 1 โ†’ Layer 2 โ†’ Layer 3 โ†’ Layer 4 +3. Merge all layer JSONs into master `model_introspection.json` +4. Implement cross-validation checks (ยง4.7) +5. Generate `introspection_summary.md` โ€” human-readable report +6. Add CLI interface: `python run_introspection.py model.prt model_sim1.sim [--op2 path]` + +**Output:** `model_introspection.json` + `introspection_summary.md` +**Owner:** NX Expert + Technical Lead (review) + +### Timeline Summary + +| Phase | Days | Cumulative | Depends On | +|-------|------|-----------|------------| +| Phase 1 โ€” Part introspection | 3โ€“4 | 3โ€“4 | โ€” | +| Phase 2 โ€” FEA model (BDF) | 3โ€“4 | 6โ€“8 | โ€” (parallel with Phase 1) | +| Phase 3 โ€” Solver config (BDF) | 3โ€“4 | 9โ€“12 | Phase 2 (shared script) | +| Phase 4 โ€” Baseline (OP2) | 1โ€“2 | 10โ€“14 | โ€” (parallel with Phase 3) | +| Phase 5 โ€” Orchestrator | 2โ€“3 | 12โ€“17 | All prior phases | + +**Phases 1 and 2 can run in parallel** (different APIs, different scripts). Phase 4 can run in parallel with Phase 3. Critical path: Phase 2 โ†’ Phase 3 โ†’ Phase 5. + +--- + +## 7. Validation Strategy + +### 7.1 Cross-Source Checks + +Where data is available from multiple sources, cross-validate: + +| Metric | Source A | Source B | Tolerance | +|--------|----------|----------|-----------| +| Element count | pyNastran `len(bdf.elements)` | NXOpen `FeelementLabelMap.Size` | Exact match | +| Node count | pyNastran `len(bdf.nodes)` | NXOpen `FenodeLabelMap.Size` | Exact match | +| Mass | NXOpen `MeasureManager` | OP2 grid point weight | 1% (mesh vs. CAD geometry) | +| Material E | NXOpen `GetPropertyValue('YoungModulus')` | pyNastran `bdf.materials[id].E` | Exact match | +| Material ฯ | NXOpen `GetPropertyValue('Density')` | pyNastran `bdf.materials[id].rho` | Unit conversion tolerance | + +### 7.2 Self-Consistency Checks + +- Total element count = sum of per-type counts +- Every property ID referenced by elements exists in properties list +- Every material ID referenced by properties exists in materials list +- SPC/LOAD set IDs in subcases exist in constraints/loads lists +- OP2 subcase IDs match BDF subcase IDs + +### 7.3 Sanity Checks + +- Mass > 0 +- Max displacement > 0 (model is loaded and responding) +- Max stress > 0 +- No element type with 0 elements in the count +- At least 1 constraint and 1 load in every subcase + +### 7.4 Validation Verdict + +``` +PASS โ€” All checks pass +WARN โ€” Non-critical discrepancies (mass within tolerance but not exact) +FAIL โ€” Critical mismatch (element count differs, missing materials) +``` + +--- + +## 8. Integration with Atomizer HQ + +### 8.1 How Agents Consume Introspection Data + +| Agent | Uses | For | +|-------|------|-----| +| **Study Builder** | `candidate_design_variables`, `expressions`, `units` | Study configuration, DV setup | +| **Technical Lead** | `mesh.quality_metrics`, `baseline_results`, `validation` | Technical review, go/no-go | +| **Optimizer** | `fea_model.mesh.total_elements`, `baseline_results` | Runtime estimation, convergence criteria | +| **Manager** | `metadata.layers_completed`, `validation.overall_status` | Status tracking, resource planning | + +### 8.2 Usage Workflow + +``` +1. Antoine opens new study +2. Run introspection: python run_introspection.py model.prt model_sim1.sim +3. Review introspection_summary.md +4. Study Builder reads model_introspection.json โ†’ proposes study config +5. Technical Lead reviews โ†’ approves or flags issues +6. Optimization proceeds with full model context +``` + +### 8.3 Knowledge Base Integration + +Every introspection JSON is stored in the study directory: +``` +studies/// + 1_setup/ + model/ + model_introspection.json โ† NEW + introspection_summary.md โ† NEW + model.prt + model_sim1.sim +``` + +This becomes the canonical reference for all agents working on the study. + +--- + +## 9. Scope Exclusions (v1) + +The following are **explicitly NOT handled** in v1. Attempting to introspect models with these features will produce partial results with appropriate warnings. + +| Feature | Why Excluded | v2 Priority | +|---------|-------------|-------------| +| **Assemblies** | Multi-part expression scoping, component transforms, assembly-level materials โ€” significant complexity | High | +| **Composite materials** (MAT2, MAT8) | Ply stack introspection, layup sequences, orientation angles โ€” specialized domain | Medium | +| **Nonlinear analysis** (SOL 106) | Contact definitions, convergence settings, load stepping, large deformation flags | Medium | +| **Topology optimization** regions | Design vs. non-design space, manufacturing constraints, density filters | Low | +| **Expression dependency graphs** | NXOpen `RightHandSide` parsing is fragile: breaks on built-in functions (`if()`, `min()`), unit qualifiers, substring expression names. Feature-to-expression links require rebuilding builder objects โ€” massive effort | Medium | +| **Parametric sensitivity estimation** | Labeling `thickness โ†’ mass` as "linear" is textbook, not model-specific. Real sensitivity requires perturbation studies (separate effort) | Low | +| **Multi-FEM models** | Multiple FEM files linked to one part โ€” need to handle collector mapping across files | Medium | +| **Thermal-structural coupling** | Multi-physics BC detection, thermal load application | Low | +| **Contact pairs** | Contact detection, friction coefficients, contact algorithms | Medium | +| **Dynamic loads** | PSD, time-history, random vibration, frequency-dependent loads | Low | + +--- + +## 10. v2 Roadmap + +### 10.1 Expression Dependency Graph (Layer 5) + +**Challenge:** NXOpen's `RightHandSide` returns the formula as a string, but parsing it reliably requires handling: +- NX built-in math functions +- Unit-qualified references +- Expression names that are substrings of other names +- Conditional expressions (`if(expr > val, a, b)`) + +**Approach for v2:** Build a proper tokenizer/parser for NX expression syntax. Consider exporting expressions to a file and parsing offline rather than relying on API string manipulation. + +### 10.2 Assembly Support + +**Challenge:** Expressions live at component scope. Design variables may be in sub-components. Assembly-level mass is different from component mass. + +**Approach:** Recursive introspection โ€” introspect each component, then merge with assembly context (transforms, overrides). + +### 10.3 Composite Introspection + +**Challenge:** MAT2/MAT8 cards, PCOMP/PCOMPG properties, ply orientations, stacking sequences. + +**Approach:** Extend pyNastran parsing for composite cards. Map plies to design variables (thickness, angle). + +### 10.4 AI-Powered Analysis (Future) + +- Sensitivity prediction from geometry/BC features without running FEA +- Design variable clustering (correlated parameters) +- Failure mode prediction from stress distributions +- Automated study type recommendation (with engineer approval) + +--- + +## 11. Appendix: Key Reference Paths + +### Existing Scripts +| Script | Path | Status | +|--------|------|--------| +| `introspect_part.py` | `nx_journals/introspect_part.py` | Enhance for v1 | +| `introspect_sim.py` | `nx_journals/introspect_sim.py` | Reference only (BDF replaces most functionality) | +| `discover_model.py` | `nx_journals/discover_model.py` | Reference only | +| `extract_displacement.py` | `optimization_engine/extractors/extract_displacement.py` | Use in Layer 4 | +| `extract_von_mises_stress.py` | `optimization_engine/extractors/extract_von_mises_stress.py` | Use in Layer 4 | + +### New Scripts (to create) +| Script | Purpose | +|--------|---------| +| `introspect_bdf.py` | Layers 2โ€“3: BDF parsing for mesh, materials, properties, solver config | +| `introspect_baseline.py` | Layer 4: OP2 result aggregation | +| `run_introspection.py` | Master orchestrator โ€” runs all layers, merges, validates | +| `export_bdf.py` | NXOpen journal to export Nastran input deck | + +### NXOpen API Reference +- Expressions: `NXOpen.Part.Expressions` +- Mass: `NXOpen.Part.MeasureManager.NewMassProperties()` +- Materials: `body.GetPhysicalMaterial()`, `mat.GetPropertyValue()` +- Features: `NXOpen.Part.Features` +- FEM: `NXOpen.CAE.FemPart.BaseFEModel` +- SIM: `NXOpen.CAE.SimPart.Simulation` + +### pyNastran API Reference +- BDF: `pyNastran.bdf.bdf.BDF` โ€” `.elements`, `.nodes`, `.materials`, `.properties`, `.spcs`, `.loads`, `.subcases` +- OP2: `pyNastran.op2.op2.OP2` โ€” `.displacements`, `.ctetra_stress`, `.cquad4_stress`, `.eigenvalues` + +--- + +## 12. Sign-Off + +| Role | Status | Notes | +|------|--------|-------| +| Technical Lead ๐Ÿ”ง | โœ… Approved | Plan owner, technical review complete | +| NX Expert ๐Ÿ–ฅ๏ธ | ๐Ÿ”ฒ Pending | Initial research author, implementation owner | +| Manager ๐ŸŽฏ | ๐Ÿ”ฒ Pending | Resource allocation, timeline approval | +| CEO (Antoine) | ๐Ÿ”ฒ Pending | Strategic direction approval | + +--- + +*The physics is the boss. Introspection just makes sure we understand what the physics is doing before we start changing things.* + +โ€” Technical Lead ๐Ÿ”ง | Atomizer Engineering Co. diff --git a/projects/hydrotech-beam/CONTEXT.md b/projects/hydrotech-beam/CONTEXT.md index 2a211f86..bb5ee263 100644 --- a/projects/hydrotech-beam/CONTEXT.md +++ b/projects/hydrotech-beam/CONTEXT.md @@ -1,171 +1,171 @@ -# CONTEXT.md โ€” Hydrotech Beam Structural Optimization - -## Client -Hydrotech (internal test fixture) - -## Objective -Minimize beam mass while meeting displacement and stress constraints. Single-objective: minimize mass, constrain displacement (โ‰ค 10 mm) and stress (โ‰ค 130 MPa). - -## Key Parameters โ€” Confirmed (Gen 002 + Introspection) - -| Parameter | NX Expression | Current | Range | Units | DV? | Notes | -|-----------|--------------|---------|-------|-------|-----|-------| -| beam_half_core_thickness | `beam_half_core_thickness` | **25.162** | 10โ€“40 | mm | Yes (DV1) | Core half-thickness | -| beam_face_thickness | `beam_face_thickness` | **21.504** | 10โ€“40 | mm | Yes (DV2) | Face sheet thickness | -| holes_diameter | `holes_diameter` | 300 | 150โ€“450 | mm | Yes (DV3) | โœ… G14 closed | -| hole_count | `hole_count` (โ†’ `Pattern_p7`) | 10 | 5โ€“15 | integer | Yes (DV4) | Pattern parameter link | -| beam_length | `beam_lenght` โš ๏ธ | 5,000 | Fixed | mm | No | **Typo in NX โ€” no 'h'** | -| beam_half_height | `beam_half_height` | **250** | โ€” | mm | No | โœ… G12 closed | -| beam_half_width | `beam_half_width` | **150** | โ€” | mm | No | โœ… G13 closed | -| hole_span | `p6` (โ†’ `Pattern_p9`) | 4,000 | TBD | mm | Potential (G15) | Total span for hole distribution | - -## Constraints -- 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]) - -## Baseline Performance โ€” Confirmed (Introspection) - -| Metric | Value | Source | Confidence | -|--------|-------|--------|------------| -| Mass | **1,133.01 kg** | NX expression `p173`, CEO correction + binary introspection | โœ… High | -| Tip displacement | **19.56 mm** (node 5161, Tz = โˆ’19.51 mm) | OP2 parse via pyNastran, SOL 101 baseline run 2026-02-10 | โœ… High | -| Max von Mises stress | **117.48 MPa** | OP2 parse via pyNastran, CQUAD4 shell stress | โœ… High | -| Material | **AISI Steel 1005** | `physicalmateriallibrary.xml` | โœ… High | -| Density | **7.3 g/cmยณ** | KBS session | โœ… High | - -> โš ๏ธ **Mass history:** Intake reported ~974 kg โ†’ CEO corrected to 1,133.01 kg (`p173`). Discrepancy with intake likely due to different parameter state. Binary introspection confirms `p173: body_property147.mass` in kg. - -## NX Expression Map โ€” Full Introspection - -### Design Variables -| Expression | Value | Unit | Type | -|-----------|-------|------|------| -| `beam_face_thickness` | 21.504404775742785 | mm | Number โ€” continuous | -| `beam_half_core_thickness` | 25.162078968746705 | mm | Number โ€” continuous | -| `holes_diameter` | 300 | mm | Number โ€” continuous | -| `hole_count` | 10 | โ€” | Integer (via `Pattern_p7`) | - -### Fixed Geometry Parameters -| Expression | Value | Unit | -|-----------|-------|------| -| `beam_half_height` | 250 | mm | -| `beam_half_width` | 150 | mm | -| `beam_lenght` | 5,000 | mm | - -### Hole Pattern Expressions -| Expression | Value/Formula | Notes | -|-----------|--------------|-------| -| `p6` | 4,000 mm | Hole span โ€” potential DV | -| `Pattern_p7` | โ†’ `hole_count` | Count link | -| `Pattern_p8` | 444.444 mm | Computed spacing (span / (count-1)) | -| `Pattern_p9` | โ†’ `p6` | Span link | -| `Pattern_p10` | (linked) | | -| `Pattern_p11` | 10 mm | | -| `Pattern_p12` | 0 mm | | - -### Mass Properties (Measure Body) -| Expression | Formula | Unit | -|-----------|---------|------| -| `p170` | surface_area | mmยฒ | -| `p171` | volume | mmยณ | -| `p172` | center_of_mass | mm (point) | -| **`p173`** | **mass** | **kg** | -| `p174` | weight | N | -| `p175` | density | kg/mmยณ | - -### Other -| Expression | Value | Notes | -|-----------|-------|-------| -| `p4` | โ†’ `beam_lenght` | Alias | -| `p119` | 4,000 mm | | -| `p132` | 444.444 mm | Computed | -| `p134` | 4,000 mm | | -| `p135` | 4,000 mm | | -| `p139` | 10 mm | | -| `p141` | 0 mm | | - -## Boundary Conditions โ€” Confirmed (Gen 002) - -| BC | Location | Value | Source | -|----|----------|-------|--------| -| Fixed support | Left side (full edge) | All DOF constrained | โœ… KBS session | -| Applied force | Right side (free end) | 10,000 kgf downward (โˆ’Y) | โœ… KBS session โ€” "project requirement" | -| Configuration | Cantilever | Left fixed, right loaded | โœ… KBS session | - -## Mesh โ€” Confirmed (Gen 002) - -| Property | Value | Source | -|----------|-------|--------| -| Element type | CQUAD4 (thin shell) | โœ… KBS session | -| Element size | 33.7 mm (67.4/2) | โœ… KBS session | -| Idealization | Promote body โ†’ mid-surface extraction | โœ… KBS session | - -## Hole Geometry โ€” Confirmed (Gen 002) - -| Property | Value | Notes | -|----------|-------|-------| -| Span | 4,000 mm (expression `p6`) | Total distribution length | -| Start offset | 500 mm from beam start | Fixed requirement โ€” not parametric | -| End offset | 500 mm from beam end | Fixed requirement โ€” not parametric | -| Computed spacing | 444.444 mm (4000 / 9) | At baseline (10 holes) | -| Collision check | Required at DV extremes | 15 ร— 450 mm in 4,000 mm โ†’ overlap | - -## Model Files -- NX Part: `Beam.prt` -- FEM: `Beam_fem1.fem` -- Idealized part: `Beam_fem1_i.prt` -- Simulation: `Beam_sim1.sim` -- Solver: NX Nastran SOL 101 (static structural) -- **NX Version:** DesigncenterNX 2512 (Siemens rebranded NX โ†’ "DesigncenterNX") -- **Install path:** `C:\Program Files\Siemens\DesigncenterNX2512` (on dalidou) - -## First Results (2026-02-11) - -| Metric | Value | Notes | -|--------|-------|-------| -| Displacement | **17.93 mm** | At baseline-ish DVs | -| Von Mises stress | **111.9 MPa** | At baseline-ish DVs | -| Solve time | **~12 s/trial** | On dalidou (Windows) | -| Mass extraction | Via `p173` expression | Journal writes `_temp_mass.txt` | - -> Both constraints violated at baseline (disp > 10mm). Optimization will need to find stiffer/heavier designs or relaxed constraints. - -## Future Expansion -- Material alternatives: Aluminum 6061, Stainless Steel ANSI 310 (per Antoine, KBS session) -- `p6` (hole span) as additional design variable (pending decision G15) -- Mesh refinement (Antoine: "Eventually we're going to be able to refine the element size") - -## Decisions -- 2026-02-08: Project received from Antoine -- 2026-02-10: KBS sessions processed โ†’ Gen 002 KB update. BCs confirmed. -- 2026-02-10: Binary introspection โ€” all expression names/values confirmed. Mass = 1,133.01 kg. -- 2026-02-11: NX version fixed (DesigncenterNX 2512), path resolution bugs fixed, iteration architecture finalized (backup/restore in-place), history DB added. First real solve results. - -## Status -Phase: **DOE re-run** โ€” mass bug fixed, constraint relaxed, ready for clean DOE -Generation: 004 -Channel: #project-hydrotech-beam -Last update: 2026-02-13 - -### Solver Pipeline Status -- โœ… NX journal solve working (DesigncenterNX 2512) -- โœ… Mass extraction fixed โ€” commit `580ed65` (MeasureManager after geometry rebuild, no more stale p173) -- โœ… Displacement & stress extraction via pyNastran OP2 parsing -- โœ… Iteration archival (params, results, OP2, F06) -- โœ… History DB (SQLite + CSV, append-only, survives --clean) -- โœ… Geometric pre-checks (hole overlap, web clearance) -- โœ… Displacement constraint relaxed: 10mm โ†’ 20mm (DEC-HB-012) -- ๐Ÿ”„ Pull fix on dalidou โ†’ test single trial โ†’ re-run full DOE - -### DOE Phase 1 Results (pre-fix, 51 trials) -- 39/51 solved, 12 geo-infeasible (hole overlap) -- **0 fully feasible** at 10mm constraint (min displacement ~19.6mm) -- **Mass = NaN on all trials** โ€” extraction bug (now fixed) -- Stress constraint met by several trials -- With 20mm relaxation, many trials should now be feasible - -### Development Workflow -- **Git only** for development (git push/pull between server and dalidou) -- **Syncthing paused** to avoid conflicts during active development -- Resume Syncthing for production/delivery phases +# CONTEXT.md โ€” Hydrotech Beam Structural Optimization + +## Client +Hydrotech (internal test fixture) + +## Objective +Minimize beam mass while meeting displacement and stress constraints. Single-objective: minimize mass, constrain displacement (โ‰ค 10 mm) and stress (โ‰ค 130 MPa). + +## Key Parameters โ€” Confirmed (Gen 002 + Introspection) + +| Parameter | NX Expression | Current | Range | Units | DV? | Notes | +|-----------|--------------|---------|-------|-------|-----|-------| +| beam_half_core_thickness | `beam_half_core_thickness` | **25.162** | 10โ€“40 | mm | Yes (DV1) | Core half-thickness | +| beam_face_thickness | `beam_face_thickness` | **21.504** | 10โ€“40 | mm | Yes (DV2) | Face sheet thickness | +| holes_diameter | `holes_diameter` | 300 | 150โ€“450 | mm | Yes (DV3) | โœ… G14 closed | +| hole_count | `hole_count` (โ†’ `Pattern_p7`) | 10 | 5โ€“15 | integer | Yes (DV4) | Pattern parameter link | +| beam_length | `beam_lenght` โš ๏ธ | 5,000 | Fixed | mm | No | **Typo in NX โ€” no 'h'** | +| beam_half_height | `beam_half_height` | **250** | โ€” | mm | No | โœ… G12 closed | +| beam_half_width | `beam_half_width` | **150** | โ€” | mm | No | โœ… G13 closed | +| hole_span | `p6` (โ†’ `Pattern_p9`) | 4,000 | TBD | mm | Potential (G15) | Total span for hole distribution | + +## Constraints +- 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]) + +## Baseline Performance โ€” Confirmed (Introspection) + +| Metric | Value | Source | Confidence | +|--------|-------|--------|------------| +| Mass | **1,133.01 kg** | NX expression `p173`, CEO correction + binary introspection | โœ… High | +| Tip displacement | **19.56 mm** (node 5161, Tz = โˆ’19.51 mm) | OP2 parse via pyNastran, SOL 101 baseline run 2026-02-10 | โœ… High | +| Max von Mises stress | **117.48 MPa** | OP2 parse via pyNastran, CQUAD4 shell stress | โœ… High | +| Material | **AISI Steel 1005** | `physicalmateriallibrary.xml` | โœ… High | +| Density | **7.3 g/cmยณ** | KBS session | โœ… High | + +> โš ๏ธ **Mass history:** Intake reported ~974 kg โ†’ CEO corrected to 1,133.01 kg (`p173`). Discrepancy with intake likely due to different parameter state. Binary introspection confirms `p173: body_property147.mass` in kg. + +## NX Expression Map โ€” Full Introspection + +### Design Variables +| Expression | Value | Unit | Type | +|-----------|-------|------|------| +| `beam_face_thickness` | 21.504404775742785 | mm | Number โ€” continuous | +| `beam_half_core_thickness` | 25.162078968746705 | mm | Number โ€” continuous | +| `holes_diameter` | 300 | mm | Number โ€” continuous | +| `hole_count` | 10 | โ€” | Integer (via `Pattern_p7`) | + +### Fixed Geometry Parameters +| Expression | Value | Unit | +|-----------|-------|------| +| `beam_half_height` | 250 | mm | +| `beam_half_width` | 150 | mm | +| `beam_lenght` | 5,000 | mm | + +### Hole Pattern Expressions +| Expression | Value/Formula | Notes | +|-----------|--------------|-------| +| `p6` | 4,000 mm | Hole span โ€” potential DV | +| `Pattern_p7` | โ†’ `hole_count` | Count link | +| `Pattern_p8` | 444.444 mm | Computed spacing (span / (count-1)) | +| `Pattern_p9` | โ†’ `p6` | Span link | +| `Pattern_p10` | (linked) | | +| `Pattern_p11` | 10 mm | | +| `Pattern_p12` | 0 mm | | + +### Mass Properties (Measure Body) +| Expression | Formula | Unit | +|-----------|---------|------| +| `p170` | surface_area | mmยฒ | +| `p171` | volume | mmยณ | +| `p172` | center_of_mass | mm (point) | +| **`p173`** | **mass** | **kg** | +| `p174` | weight | N | +| `p175` | density | kg/mmยณ | + +### Other +| Expression | Value | Notes | +|-----------|-------|-------| +| `p4` | โ†’ `beam_lenght` | Alias | +| `p119` | 4,000 mm | | +| `p132` | 444.444 mm | Computed | +| `p134` | 4,000 mm | | +| `p135` | 4,000 mm | | +| `p139` | 10 mm | | +| `p141` | 0 mm | | + +## Boundary Conditions โ€” Confirmed (Gen 002) + +| BC | Location | Value | Source | +|----|----------|-------|--------| +| Fixed support | Left side (full edge) | All DOF constrained | โœ… KBS session | +| Applied force | Right side (free end) | 10,000 kgf downward (โˆ’Y) | โœ… KBS session โ€” "project requirement" | +| Configuration | Cantilever | Left fixed, right loaded | โœ… KBS session | + +## Mesh โ€” Confirmed (Gen 002) + +| Property | Value | Source | +|----------|-------|--------| +| Element type | CQUAD4 (thin shell) | โœ… KBS session | +| Element size | 33.7 mm (67.4/2) | โœ… KBS session | +| Idealization | Promote body โ†’ mid-surface extraction | โœ… KBS session | + +## Hole Geometry โ€” Confirmed (Gen 002) + +| Property | Value | Notes | +|----------|-------|-------| +| Span | 4,000 mm (expression `p6`) | Total distribution length | +| Start offset | 500 mm from beam start | Fixed requirement โ€” not parametric | +| End offset | 500 mm from beam end | Fixed requirement โ€” not parametric | +| Computed spacing | 444.444 mm (4000 / 9) | At baseline (10 holes) | +| Collision check | Required at DV extremes | 15 ร— 450 mm in 4,000 mm โ†’ overlap | + +## Model Files +- NX Part: `Beam.prt` +- FEM: `Beam_fem1.fem` +- Idealized part: `Beam_fem1_i.prt` +- Simulation: `Beam_sim1.sim` +- Solver: NX Nastran SOL 101 (static structural) +- **NX Version:** DesigncenterNX 2512 (Siemens rebranded NX โ†’ "DesigncenterNX") +- **Install path:** `C:\Program Files\Siemens\DesigncenterNX2512` (on dalidou) + +## First Results (2026-02-11) + +| Metric | Value | Notes | +|--------|-------|-------| +| Displacement | **17.93 mm** | At baseline-ish DVs | +| Von Mises stress | **111.9 MPa** | At baseline-ish DVs | +| Solve time | **~12 s/trial** | On dalidou (Windows) | +| Mass extraction | Via `p173` expression | Journal writes `_temp_mass.txt` | + +> Both constraints violated at baseline (disp > 10mm). Optimization will need to find stiffer/heavier designs or relaxed constraints. + +## Future Expansion +- Material alternatives: Aluminum 6061, Stainless Steel ANSI 310 (per Antoine, KBS session) +- `p6` (hole span) as additional design variable (pending decision G15) +- Mesh refinement (Antoine: "Eventually we're going to be able to refine the element size") + +## Decisions +- 2026-02-08: Project received from Antoine +- 2026-02-10: KBS sessions processed โ†’ Gen 002 KB update. BCs confirmed. +- 2026-02-10: Binary introspection โ€” all expression names/values confirmed. Mass = 1,133.01 kg. +- 2026-02-11: NX version fixed (DesigncenterNX 2512), path resolution bugs fixed, iteration architecture finalized (backup/restore in-place), history DB added. First real solve results. + +## Status +Phase: **DOE re-run** โ€” mass bug fixed, constraint relaxed, ready for clean DOE +Generation: 004 +Channel: #project-hydrotech-beam +Last update: 2026-02-13 + +### Solver Pipeline Status +- โœ… NX journal solve working (DesigncenterNX 2512) +- โœ… Mass extraction fixed โ€” commit `580ed65` (MeasureManager after geometry rebuild, no more stale p173) +- โœ… Displacement & stress extraction via pyNastran OP2 parsing +- โœ… Iteration archival (params, results, OP2, F06) +- โœ… History DB (SQLite + CSV, append-only, survives --clean) +- โœ… Geometric pre-checks (hole overlap, web clearance) +- โœ… Displacement constraint relaxed: 10mm โ†’ 20mm (DEC-HB-012) +- ๐Ÿ”„ Pull fix on dalidou โ†’ test single trial โ†’ re-run full DOE + +### DOE Phase 1 Results (pre-fix, 51 trials) +- 39/51 solved, 12 geo-infeasible (hole overlap) +- **0 fully feasible** at 10mm constraint (min displacement ~19.6mm) +- **Mass = NaN on all trials** โ€” extraction bug (now fixed) +- Stress constraint met by several trials +- With 20mm relaxation, many trials should now be feasible + +### Development Workflow +- **Git only** for development (git push/pull between server and dalidou) +- **Syncthing paused** to avoid conflicts during active development +- Resume Syncthing for production/delivery phases diff --git a/projects/hydrotech-beam/CONTEXT.sync-conflict-20260214-191800-VBCUD7Z.md b/projects/hydrotech-beam/CONTEXT.sync-conflict-20260214-191800-VBCUD7Z.md new file mode 100644 index 00000000..2a211f86 --- /dev/null +++ b/projects/hydrotech-beam/CONTEXT.sync-conflict-20260214-191800-VBCUD7Z.md @@ -0,0 +1,171 @@ +# CONTEXT.md โ€” Hydrotech Beam Structural Optimization + +## Client +Hydrotech (internal test fixture) + +## Objective +Minimize beam mass while meeting displacement and stress constraints. Single-objective: minimize mass, constrain displacement (โ‰ค 10 mm) and stress (โ‰ค 130 MPa). + +## Key Parameters โ€” Confirmed (Gen 002 + Introspection) + +| Parameter | NX Expression | Current | Range | Units | DV? | Notes | +|-----------|--------------|---------|-------|-------|-----|-------| +| beam_half_core_thickness | `beam_half_core_thickness` | **25.162** | 10โ€“40 | mm | Yes (DV1) | Core half-thickness | +| beam_face_thickness | `beam_face_thickness` | **21.504** | 10โ€“40 | mm | Yes (DV2) | Face sheet thickness | +| holes_diameter | `holes_diameter` | 300 | 150โ€“450 | mm | Yes (DV3) | โœ… G14 closed | +| hole_count | `hole_count` (โ†’ `Pattern_p7`) | 10 | 5โ€“15 | integer | Yes (DV4) | Pattern parameter link | +| beam_length | `beam_lenght` โš ๏ธ | 5,000 | Fixed | mm | No | **Typo in NX โ€” no 'h'** | +| beam_half_height | `beam_half_height` | **250** | โ€” | mm | No | โœ… G12 closed | +| beam_half_width | `beam_half_width` | **150** | โ€” | mm | No | โœ… G13 closed | +| hole_span | `p6` (โ†’ `Pattern_p9`) | 4,000 | TBD | mm | Potential (G15) | Total span for hole distribution | + +## Constraints +- 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]) + +## Baseline Performance โ€” Confirmed (Introspection) + +| Metric | Value | Source | Confidence | +|--------|-------|--------|------------| +| Mass | **1,133.01 kg** | NX expression `p173`, CEO correction + binary introspection | โœ… High | +| Tip displacement | **19.56 mm** (node 5161, Tz = โˆ’19.51 mm) | OP2 parse via pyNastran, SOL 101 baseline run 2026-02-10 | โœ… High | +| Max von Mises stress | **117.48 MPa** | OP2 parse via pyNastran, CQUAD4 shell stress | โœ… High | +| Material | **AISI Steel 1005** | `physicalmateriallibrary.xml` | โœ… High | +| Density | **7.3 g/cmยณ** | KBS session | โœ… High | + +> โš ๏ธ **Mass history:** Intake reported ~974 kg โ†’ CEO corrected to 1,133.01 kg (`p173`). Discrepancy with intake likely due to different parameter state. Binary introspection confirms `p173: body_property147.mass` in kg. + +## NX Expression Map โ€” Full Introspection + +### Design Variables +| Expression | Value | Unit | Type | +|-----------|-------|------|------| +| `beam_face_thickness` | 21.504404775742785 | mm | Number โ€” continuous | +| `beam_half_core_thickness` | 25.162078968746705 | mm | Number โ€” continuous | +| `holes_diameter` | 300 | mm | Number โ€” continuous | +| `hole_count` | 10 | โ€” | Integer (via `Pattern_p7`) | + +### Fixed Geometry Parameters +| Expression | Value | Unit | +|-----------|-------|------| +| `beam_half_height` | 250 | mm | +| `beam_half_width` | 150 | mm | +| `beam_lenght` | 5,000 | mm | + +### Hole Pattern Expressions +| Expression | Value/Formula | Notes | +|-----------|--------------|-------| +| `p6` | 4,000 mm | Hole span โ€” potential DV | +| `Pattern_p7` | โ†’ `hole_count` | Count link | +| `Pattern_p8` | 444.444 mm | Computed spacing (span / (count-1)) | +| `Pattern_p9` | โ†’ `p6` | Span link | +| `Pattern_p10` | (linked) | | +| `Pattern_p11` | 10 mm | | +| `Pattern_p12` | 0 mm | | + +### Mass Properties (Measure Body) +| Expression | Formula | Unit | +|-----------|---------|------| +| `p170` | surface_area | mmยฒ | +| `p171` | volume | mmยณ | +| `p172` | center_of_mass | mm (point) | +| **`p173`** | **mass** | **kg** | +| `p174` | weight | N | +| `p175` | density | kg/mmยณ | + +### Other +| Expression | Value | Notes | +|-----------|-------|-------| +| `p4` | โ†’ `beam_lenght` | Alias | +| `p119` | 4,000 mm | | +| `p132` | 444.444 mm | Computed | +| `p134` | 4,000 mm | | +| `p135` | 4,000 mm | | +| `p139` | 10 mm | | +| `p141` | 0 mm | | + +## Boundary Conditions โ€” Confirmed (Gen 002) + +| BC | Location | Value | Source | +|----|----------|-------|--------| +| Fixed support | Left side (full edge) | All DOF constrained | โœ… KBS session | +| Applied force | Right side (free end) | 10,000 kgf downward (โˆ’Y) | โœ… KBS session โ€” "project requirement" | +| Configuration | Cantilever | Left fixed, right loaded | โœ… KBS session | + +## Mesh โ€” Confirmed (Gen 002) + +| Property | Value | Source | +|----------|-------|--------| +| Element type | CQUAD4 (thin shell) | โœ… KBS session | +| Element size | 33.7 mm (67.4/2) | โœ… KBS session | +| Idealization | Promote body โ†’ mid-surface extraction | โœ… KBS session | + +## Hole Geometry โ€” Confirmed (Gen 002) + +| Property | Value | Notes | +|----------|-------|-------| +| Span | 4,000 mm (expression `p6`) | Total distribution length | +| Start offset | 500 mm from beam start | Fixed requirement โ€” not parametric | +| End offset | 500 mm from beam end | Fixed requirement โ€” not parametric | +| Computed spacing | 444.444 mm (4000 / 9) | At baseline (10 holes) | +| Collision check | Required at DV extremes | 15 ร— 450 mm in 4,000 mm โ†’ overlap | + +## Model Files +- NX Part: `Beam.prt` +- FEM: `Beam_fem1.fem` +- Idealized part: `Beam_fem1_i.prt` +- Simulation: `Beam_sim1.sim` +- Solver: NX Nastran SOL 101 (static structural) +- **NX Version:** DesigncenterNX 2512 (Siemens rebranded NX โ†’ "DesigncenterNX") +- **Install path:** `C:\Program Files\Siemens\DesigncenterNX2512` (on dalidou) + +## First Results (2026-02-11) + +| Metric | Value | Notes | +|--------|-------|-------| +| Displacement | **17.93 mm** | At baseline-ish DVs | +| Von Mises stress | **111.9 MPa** | At baseline-ish DVs | +| Solve time | **~12 s/trial** | On dalidou (Windows) | +| Mass extraction | Via `p173` expression | Journal writes `_temp_mass.txt` | + +> Both constraints violated at baseline (disp > 10mm). Optimization will need to find stiffer/heavier designs or relaxed constraints. + +## Future Expansion +- Material alternatives: Aluminum 6061, Stainless Steel ANSI 310 (per Antoine, KBS session) +- `p6` (hole span) as additional design variable (pending decision G15) +- Mesh refinement (Antoine: "Eventually we're going to be able to refine the element size") + +## Decisions +- 2026-02-08: Project received from Antoine +- 2026-02-10: KBS sessions processed โ†’ Gen 002 KB update. BCs confirmed. +- 2026-02-10: Binary introspection โ€” all expression names/values confirmed. Mass = 1,133.01 kg. +- 2026-02-11: NX version fixed (DesigncenterNX 2512), path resolution bugs fixed, iteration architecture finalized (backup/restore in-place), history DB added. First real solve results. + +## Status +Phase: **DOE re-run** โ€” mass bug fixed, constraint relaxed, ready for clean DOE +Generation: 004 +Channel: #project-hydrotech-beam +Last update: 2026-02-13 + +### Solver Pipeline Status +- โœ… NX journal solve working (DesigncenterNX 2512) +- โœ… Mass extraction fixed โ€” commit `580ed65` (MeasureManager after geometry rebuild, no more stale p173) +- โœ… Displacement & stress extraction via pyNastran OP2 parsing +- โœ… Iteration archival (params, results, OP2, F06) +- โœ… History DB (SQLite + CSV, append-only, survives --clean) +- โœ… Geometric pre-checks (hole overlap, web clearance) +- โœ… Displacement constraint relaxed: 10mm โ†’ 20mm (DEC-HB-012) +- ๐Ÿ”„ Pull fix on dalidou โ†’ test single trial โ†’ re-run full DOE + +### DOE Phase 1 Results (pre-fix, 51 trials) +- 39/51 solved, 12 geo-infeasible (hole overlap) +- **0 fully feasible** at 10mm constraint (min displacement ~19.6mm) +- **Mass = NaN on all trials** โ€” extraction bug (now fixed) +- Stress constraint met by several trials +- With 20mm relaxation, many trials should now be feasible + +### Development Workflow +- **Git only** for development (git push/pull between server and dalidou) +- **Syncthing paused** to avoid conflicts during active development +- Resume Syncthing for production/delivery phases diff --git a/projects/hydrotech-beam/README.md b/projects/hydrotech-beam/README.md index 4006162f..d7046eb5 100644 --- a/projects/hydrotech-beam/README.md +++ b/projects/hydrotech-beam/README.md @@ -61,6 +61,10 @@ hydrotech-beam/ - [CONTEXT.md](CONTEXT.md) โ€” Full intake data - [BREAKDOWN.md](BREAKDOWN.md) โ€” Tech Lead's technical analysis - [DECISIONS.md](DECISIONS.md) โ€” All project decisions +- [USER_GUIDE.md](USER_GUIDE.md) โ€” Living user guide (operators + bots) +- [playbooks/DOE.md](playbooks/DOE.md) โ€” DOE execution playbook +- [playbooks/NX_REAL_RUN.md](playbooks/NX_REAL_RUN.md) โ€” Real NX run checklist +- [playbooks/SYNCTHING_RECOVERY.md](playbooks/SYNCTHING_RECOVERY.md) โ€” Sync recovery and cleanup - [kb/_index.md](kb/_index.md) โ€” Knowledge base overview ## Team diff --git a/projects/hydrotech-beam/USER_GUIDE.md b/projects/hydrotech-beam/USER_GUIDE.md new file mode 100644 index 00000000..e0b23711 --- /dev/null +++ b/projects/hydrotech-beam/USER_GUIDE.md @@ -0,0 +1,65 @@ +# Hydrotech Beam โ€” User Guide (Living) + +> Audience: Antoine, future users, and Atomizer agents/bots. +> Status: Living document โ€” update after each meaningful run/decision. + +## 1) Purpose +This guide explains how to run Hydrotech Beam studies safely and reproducibly, how to interpret outputs, and how to avoid common pitfalls (backend mix-up, sync conflicts, stale results). + +## 2) Quick Start + +### Study folder +`C:\Users\antoi\Atomizer\Projects\hydrotech-beam\studies\01_doe_landscape` + +### Core command (real NX run) +```powershell +python .\run_doe.py --backend nxopen --model-dir "" --clean --study-name hydrotech_beam_doe_phase1_real +``` + +### Dev/testing command (fake physics) +```powershell +python .\run_doe.py --backend stub --clean --study-name hydrotech_beam_doe_phase1_stub +``` + +โš ๏ธ `stub` is synthetic. Do **not** use stub outputs for engineering decisions. + +## 3) Critical Rules +1. Always specify `--backend` explicitly (never rely on defaults). +2. Before a decision review, confirm whether results are from `nxopen` or `stub`. +3. Keep result artifacts clean (archive conflicts, avoid mixed appended runs unless intentional). +4. After every run, write a run log entry (template below). + +## 4) Run Log Template (required) +Copy this into `DECISIONS.md` or project log after every run: + +```md +## Run Record โ€” YYYY-MM-DD HH:MM +- Operator: +- Command: +- Backend: nxopen | stub +- Model dir: +- Study name: +- Constraints: displacement=__ mm, stress=__ MPa +- Result summary: total=__, solved=__, geo_infeasible=__, feasible=__ +- Gate check: PASS | FAIL +- Notes/issues: +- Next action: +``` + +## 5) Playbooks +- `playbooks/NX_REAL_RUN.md` โ€” clean real run checklist + validation +- `playbooks/DOE.md` โ€” DOE execution and gate rules +- `playbooks/SYNCTHING_RECOVERY.md` โ€” sync conflict and stale data recovery + +## 6) Current Known Pitfalls +- `run_doe.py` default backend is `stub` unless overridden. +- Mixing old + new runs in same DB/file can produce misleading totals. +- Syncthing conflict files (`*.sync-conflict-*`) can silently fork truth. +- NX expression names must match exactly (e.g., typo-sensitive names in model). + +## 7) Ownership +- CEO (Antoine): go/no-go and final technical decisions. +- Manager: orchestration + process + documentation enforcement. +- Study Builder: run scripts and settings correctness. +- Tech Lead: engineering validity of constraints and interpretation. +- Auditor: quality gate before external conclusions. diff --git a/projects/hydrotech-beam/dashboard/EXECUTIVE_DASHBOARD.md b/projects/hydrotech-beam/dashboard/EXECUTIVE_DASHBOARD.md new file mode 100644 index 00000000..a7c26c1d --- /dev/null +++ b/projects/hydrotech-beam/dashboard/EXECUTIVE_DASHBOARD.md @@ -0,0 +1,33 @@ +# Hydrotech Beam โ€” Executive Dashboard + +Last updated (UTC): 2026-02-14T22:24:29 +Source: `studies/01_doe_landscape/results/doe_summary.json` + `doe_results.csv` + +## CEO 60-second view +- **Phase:** DOE Phase 1 (LHS) +- **Gate status:** โŒ **BLOCKED** (Phase 1 โ†’ Phase 2) +- **Total trials:** 51 +- **Solved:** 39 (**76.5%**, below 80% gate) +- **Geo-infeasible:** 12 +- **Fully feasible designs:** 0 (gate requires โ‰ฅ5) + +## Top signals +- โœ… Mass extraction now looks real (no NaN in solved rows) +- โœ… Syncthing cleanup completed (active conflict files archived) +- โš ๏ธ Displacement constraint is the blocker (0/39 displacement-feasible at 10 mm) +- โš ๏ธ DOEโ†’TPE gate cannot pass with current limits/bounds + +## Decision hotlist (CEO) +1. **Approve next constraint strategy**: + - Option A (recommended): exploratory rerun at **20 mm** displacement + - Option B: keep 10 mm and tighten design bounds for stiffness +2. **Approve TPE entry rule** (with strict gate or conditional gate) + +## Recommendation (current) +Proceed with **Option A** for exploration (20 mm), then tighten toward 10 mm in staged optimization. + +## KPI snapshot +- Solve success rate: **76.5%** +- Feasibility rate (full): **0.0%** +- Best solved mass: **686.289 kg** (not fully feasible) +- Best trial ID by mass: **26** diff --git a/projects/hydrotech-beam/dashboard/MASTER_PLAN.md b/projects/hydrotech-beam/dashboard/MASTER_PLAN.md new file mode 100644 index 00000000..1cc2fddd --- /dev/null +++ b/projects/hydrotech-beam/dashboard/MASTER_PLAN.md @@ -0,0 +1,194 @@ +# Hydrotech Beam โ€” Dashboard & Reporting Master Plan (Multi-LLM Synthesis) + +## Purpose +Give Antoine a single, reliable command view of project health while preserving full technical depth and operational traceability. + +This plan synthesizes recommendations from: +- Codex 5.3 (execution architecture + contracts + KPI gates) +- Opus 4.6 (PKM-first, markdown-native, low-overhead governance) +- Gemini Pro (clear role-based dashboards + event-stream discipline) + +--- + +## North-Star Architecture (Hybrid, future-proof) + +### Phase 1 default (now): **PKM-native dashboards in markdown** +- No new infrastructure required +- Fastest path to value +- Agent-native read/write and auditability + +### Phase 2+ extension: **optional web UI over same contracts** +- If/when needed for richer visual analytics +- No rewrite: UI reads the same structured contracts and event logs + +**Key principle:** Data contracts first, presentation second. + +--- + +## 1) Information Architecture + +## A. Project-specific (lives under `projects/hydrotech-beam/`) +- `dashboard/` โ€” generated dashboard markdown snapshots +- `reports/` โ€” run/phase/executive reports +- `runs/` โ€” run manifests and execution metadata +- `decisions/` โ€” append-only decision records +- `incidents/` โ€” sync/solver/process incident records +- `playbooks/` โ€” project execution procedures + +## B. Foundational (lives under `docs/`) +- `docs/guides/` โ€” reusable dashboard/report standards +- `docs/reference/data-contracts/` โ€” versioned schema definitions +- `docs/protocols/` โ€” gates, QA, governance rules +- `docs/templates/` โ€” report and dashboard templates + +--- + +## 2) Dashboard Modules (3-tier) + +## A. Executive Dashboard (60-second scan) +- Project status (RAG + phase + blockers) +- Milestone confidence and slip risk +- Decision hotlist (items needing CEO approval) +- Top risks + mitigation owners +- Gate state (Pass / Conditional / Fail) + +## B. Technical Dashboard +- DOE/TPE performance (success, feasible points, convergence) +- Constraint panel (disp/stress/geo violations) +- Data quality checks (NaN, stub-vs-nxopen, stale/mixed runs) +- Traceability (requirement โ†’ run โ†’ result โ†’ decision) + +## C. Operations Dashboard +- Queue and throughput (WIP, cycle time, backlog) +- Blocker tracker + MTTR +- Review SLA status +- Syncthing/data integrity panel +- Documentation freshness/compliance + +--- + +## 3) Data Contracts (mandatory) + +All dashboard/report content must come from structured records with lineage. + +Required contracts (v1): +1. `run_manifest.v1.json` +2. `trial_result.v1.json` +3. `study_summary.v1.json` +4. `decision_record.v1.json` +5. `risk_record.v1.json` +6. `gate_evaluation.v1.json` +7. `incident_record.v1.json` + +Required fields in every record: +- `schema_version`, `project_id`, `run_id` (if applicable) +- `timestamp_utc`, `owner`, `source_file`, `source_hash` + +Ingestion rules: +- Reject invalid schema +- Keep append-only history for decisions/gates/incidents +- Nightly integrity check + drift report + +--- + +## 4) Report System + +## A. Daily Ops Brief (auto) +- Blockers, failures, overdue reviews, data integrity warnings + +## B. Weekly Executive Brief (auto + curated) +- Milestone confidence, top risks, decision asks, KPI trend deltas + +## C. Gate Review Pack (formal) +- Criteria checklist, evidence index, exceptions, sign-offs + +## D. Technical Deep Dive (on-demand) +- Methods, assumptions, sensitivity, reproducibility evidence + +Render path: +- Markdown source of truth +- Optional HTML/PDF exports from templates +- Immutable snapshot ID per issued report + +--- + +## 5) Governance & Ownership + +- **Manager (owner):** documentation architecture, dashboard governance, release quality +- **Tech Lead:** technical KPI definitions, thresholds, validation logic +- **Study Builder:** data pipeline and generators +- **Optimizer:** analytics logic and recommendation layer +- **Auditor:** contract compliance + gate QA + +Rule enforced: +- Project-specific content in `projects/...` +- Foundational content in `docs/...` + +--- + +## 6) KPI Set + Gate Rules (starter) + +## KPI starter set +- Solve success rate +- Full feasibility rate +- Best feasible mass +- Constraint violation percentiles (disp/stress) +- Data integrity score +- Decision cycle time +- Blocker MTTR +- Documentation freshness lag + +## Gate policy +- Gate states: `PASS` | `CONDITIONAL_PASS` | `FAIL` +- Conditional pass requires named owner + due date + risk acceptance +- Hard fail blocks progression + +DOEโ†’TPE gate (initial): +- Solve success โ‰ฅ 80% +- Feasible points โ‰ฅ 5 +- Data integrity score โ‰ฅ 95% + +--- + +## 7) Rollout Plan + +## Phase 0 (48h) +- Contract definitions + KPI dictionary +- Dashboard markdown templates +- Run manifest/logger enabled + +## Phase 1 (1 week) +- Live executive + operations dashboards +- Daily ops brief + weekly executive brief + +## Phase 2 (2โ€“3 weeks) +- Technical dashboard depth (DOE/TPE analytics, traceability) +- Gate review packs with evidence linking + +## Phase 3 (ongoing) +- Optional web UI layer +- Predictive risk signals and anomaly detection + +--- + +## 8) Risks & Mitigations + +1. Backend confusion (stub vs nxopen) +- Mitigation: manifest hard-check + dashboard alert + +2. Syncthing conflicts / stale truth +- Mitigation: conflict detector + incident workflow + +3. Schema drift +- Mitigation: versioned contracts + validator + auditor checks + +4. Dashboard sprawl +- Mitigation: role-based views + KPI change control + +5. Documentation decay +- Mitigation: freshness SLA + automated stale flags + +--- + +## Decision +Proceed with **PKM-native dashboard/report system now**, built on strict contracts and governance, with a clean upgrade path to richer UI later. diff --git a/projects/hydrotech-beam/dashboard/OPERATIONS_DASHBOARD.md b/projects/hydrotech-beam/dashboard/OPERATIONS_DASHBOARD.md new file mode 100644 index 00000000..1b2fadc9 --- /dev/null +++ b/projects/hydrotech-beam/dashboard/OPERATIONS_DASHBOARD.md @@ -0,0 +1,43 @@ +# Hydrotech Beam โ€” Operations Dashboard + +Last updated (UTC): 2026-02-14T22:24:29 + +## Pipeline status +- Study folder sync: โœ… healthy (latest artifacts visible) +- Conflict artifacts: โœ… archived under `results/archive_conflicts_*` +- Clean rerun archive: โœ… `results/archive_before_clean_rerun_*` + +## Artifact integrity +Active result files present: +- `doe_results.csv` +- `doe_summary.json` +- `doe_run.log` +- `history.csv` +- `history.db` +- `optuna_study.db` + +## Process compliance +- Backend clarity rule: must always run with explicit `--backend` +- Documentation boundary rule: enforced + - Project-specific docs โ†’ `projects/...` + - Foundational standards โ†’ `docs/...` +- New standards created: + - `projects/hydrotech-beam/dashboard/MASTER_PLAN.md` + - `docs/guides/PKM_DASHBOARD_STANDARD.md` + - `docs/guides/DOCUMENTATION_BOUNDARIES.md` + +## Current blockers +1. Phase gate blocked (insufficient feasible designs) +2. Need CEO decision on constraint path for next DOE/TPE sequence + +## Next operational actions (owner) +1. **Manager:** keep dashboards refreshed after every run +2. **Tech Lead:** finalize recommended constraint staging +3. **Study Builder:** prep reproducible command set for next run +4. **Optimizer:** define TPE launch criteria + seed strategy +5. **Auditor:** enforce gate criteria and data-contract checks + +## SLA targets (initial) +- Dashboard freshness: โ‰ค 1h after major run +- Run log compliance: โ‰ฅ 95% +- Decision backlog age: โ‰ค 48h diff --git a/projects/hydrotech-beam/dashboard/TECHNICAL_DASHBOARD.md b/projects/hydrotech-beam/dashboard/TECHNICAL_DASHBOARD.md new file mode 100644 index 00000000..44486f40 --- /dev/null +++ b/projects/hydrotech-beam/dashboard/TECHNICAL_DASHBOARD.md @@ -0,0 +1,43 @@ +# Hydrotech Beam โ€” Technical Dashboard + +Last updated (UTC): 2026-02-14T22:24:29 +Source: `studies/01_doe_landscape/results/doe_results.csv` + +## Study health +- Trials: 51 +- Solved: 39 +- Geo-infeasible: 12 +- Solve failures: 0 +- Full-feasible: 0 + +## Constraint status +- Displacement limit: **10.0 mm** +- Stress limit: **130.0 MPa** + +Observed solved ranges: +- Tip displacement: **11.7788 โ†’ 39.4912 mm** +- Max von Mises stress: **75.0116 โ†’ 398.4295 MPa** + +Feasibility counts: +- Displacement feasible: **0** +- Stress feasible: **23** +- Fully feasible: **0** + +## Performance / runtime +- Solve time range (solved): **12.42 s โ†’ 59.59 s** +- Pattern indicates real NX execution (non-stub timing) + +## Data quality checks +- Mass NaN (solved): **0** โœ… +- Penalty mass (99999): **12** (geo infeasible trials) โœ… expected +- Mixed appended run state: **cleared** (current clean 51-trial set) + +## Technical interpretation +- Current geometry space + 10 mm displacement limit appears too restrictive. +- Stress limit is partially satisfiable (23/39), so the primary bottleneck is stiffness/displacement. + +## Recommended technical next actions +1. Run a controlled DOE with displacement limit = 20 mm (exploration mode) +2. Identify near-feasible stiff regions +3. Launch TPE with penalty + staged tightening toward 10 mm +4. Re-run gate check with updated feasible counts diff --git a/projects/hydrotech-beam/kb/_history.md b/projects/hydrotech-beam/kb/_history.md index d5e91011..d28c5c2e 100644 --- a/projects/hydrotech-beam/kb/_history.md +++ b/projects/hydrotech-beam/kb/_history.md @@ -1,48 +1,48 @@ -# Knowledge Base History โ€” Hydrotech Beam - -All modifications tracked by generation. - ---- - -## Gen 002 โ€” 2026-02-10 โ€” KBS Session Processing - -**Source:** 3 KBS capture sessions recorded by Antoine (20260210-132817, 20260210-161401, 20260210-163801) -**Author:** Technical Lead ๐Ÿ”ง -**Protocol:** OP_09 โ†’ OP_10 Step 2 - -**Updated:** -- `components/sandwich-beam.md` โ€” confirmed geometry, all expression names, mass corrected to 11.33 kg (`p1`) -- `materials/steel-aisi.md` โ€” confirmed AISI 1005, density 7.3 g/cmยณ, future material expansion noted -- `fea/models/sol101-static.md` โ€” confirmed CQUAD4, 33.7 mm mesh, cantilever BCs, 10,000 kgf load -- `_index.md` โ€” generation 002, gap tracker updated (3 closed, 2 partial, 6 new) - -**Created:** -- `dev/gen-002.md` โ€” full generation document with KBS transcript analysis and mass discrepancy resolution - -**Key findings:** -- **Mass corrected:** 974 kg โ†’ 11.33 kg (expression `p1`, not `p173`) -- **Cantilever confirmed:** Left side fixed, right side loaded (10,000 kgf downward) -- **Beam length confirmed:** 5,000 mm -- **Mesh confirmed:** CQUAD4 thin shell, 33.7 mm elements -- **Material confirmed:** AISI Steel 1005, density 7.3 g/cmยณ -- **3 gaps closed** (G1, G2, G8), **6 new gaps identified** (G10โ€“G15) -- **Antoine's directive:** "Please optimize" โ€” considers model ready - ---- - -## Gen 001 โ€” 2026-02-09 โ€” Initial KB - -**Source:** Project intake (CONTEXT.md) + Technical Lead breakdown (BREAKDOWN.md) -**Author:** Manager ๐ŸŽฏ - -**Created:** -- `components/sandwich-beam.md` โ€” initial component file from intake data -- `materials/steel-aisi.md` โ€” material placeholder -- `fea/models/sol101-static.md` โ€” FEA model placeholder from breakdown -- `dev/gen-001.md` โ€” generation document - -**Key findings from breakdown:** -- Baseline FAILS displacement constraint (22 mm vs 10 mm limit) -- Feasible region may be narrow โ€” stiffness and mass compete -- Sandwich effect (core thickness) is the primary stiffness lever -- 9 gaps identified requiring CEO input before proceeding +# Knowledge Base History โ€” Hydrotech Beam + +All modifications tracked by generation. + +--- + +## Gen 002 โ€” 2026-02-10 โ€” KBS Session Processing + +**Source:** 3 KBS capture sessions recorded by Antoine (20260210-132817, 20260210-161401, 20260210-163801) +**Author:** Technical Lead ๐Ÿ”ง +**Protocol:** OP_09 โ†’ OP_10 Step 2 + +**Updated:** +- `components/sandwich-beam.md` โ€” confirmed geometry, all expression names, mass corrected to 11.33 kg (`p1`) +- `materials/steel-aisi.md` โ€” confirmed AISI 1005, density 7.3 g/cmยณ, future material expansion noted +- `fea/models/sol101-static.md` โ€” confirmed CQUAD4, 33.7 mm mesh, cantilever BCs, 10,000 kgf load +- `_index.md` โ€” generation 002, gap tracker updated (3 closed, 2 partial, 6 new) + +**Created:** +- `dev/gen-002.md` โ€” full generation document with KBS transcript analysis and mass discrepancy resolution + +**Key findings:** +- **Mass corrected:** 974 kg โ†’ 11.33 kg (expression `p1`, not `p173`) +- **Cantilever confirmed:** Left side fixed, right side loaded (10,000 kgf downward) +- **Beam length confirmed:** 5,000 mm +- **Mesh confirmed:** CQUAD4 thin shell, 33.7 mm elements +- **Material confirmed:** AISI Steel 1005, density 7.3 g/cmยณ +- **3 gaps closed** (G1, G2, G8), **6 new gaps identified** (G10โ€“G15) +- **Antoine's directive:** "Please optimize" โ€” considers model ready + +--- + +## Gen 001 โ€” 2026-02-09 โ€” Initial KB + +**Source:** Project intake (CONTEXT.md) + Technical Lead breakdown (BREAKDOWN.md) +**Author:** Manager ๐ŸŽฏ + +**Created:** +- `components/sandwich-beam.md` โ€” initial component file from intake data +- `materials/steel-aisi.md` โ€” material placeholder +- `fea/models/sol101-static.md` โ€” FEA model placeholder from breakdown +- `dev/gen-001.md` โ€” generation document + +**Key findings from breakdown:** +- Baseline FAILS displacement constraint (22 mm vs 10 mm limit) +- Feasible region may be narrow โ€” stiffness and mass compete +- Sandwich effect (core thickness) is the primary stiffness lever +- 9 gaps identified requiring CEO input before proceeding diff --git a/projects/hydrotech-beam/kb/_history.sync-conflict-20260209-202842-VBCUD7Z.md b/projects/hydrotech-beam/kb/_history.sync-conflict-20260209-202842-VBCUD7Z.md index 40adbbd4..1b2266cf 100644 --- a/projects/hydrotech-beam/kb/_history.sync-conflict-20260209-202842-VBCUD7Z.md +++ b/projects/hydrotech-beam/kb/_history.sync-conflict-20260209-202842-VBCUD7Z.md @@ -1,22 +1,22 @@ -# Knowledge Base History โ€” Hydrotech Beam - -All modifications tracked by generation. - ---- - -## Gen 001 โ€” 2026-02-09 โ€” Initial KB - -**Source:** Project intake (CONTEXT.md) + Technical Lead breakdown (BREAKDOWN.md) -**Author:** Manager ๐ŸŽฏ - -**Created:** -- `components/sandwich-beam.md` โ€” initial component file from intake data -- `materials/steel-aisi.md` โ€” material placeholder -- `fea/models/sol101-static.md` โ€” FEA model placeholder from breakdown -- `dev/gen-001.md` โ€” generation document - -**Key findings from breakdown:** -- Baseline FAILS displacement constraint (22 mm vs 10 mm limit) -- Feasible region may be narrow โ€” stiffness and mass compete -- Sandwich effect (core thickness) is the primary stiffness lever -- 9 gaps identified requiring CEO input before proceeding +# Knowledge Base History โ€” Hydrotech Beam + +All modifications tracked by generation. + +--- + +## Gen 001 โ€” 2026-02-09 โ€” Initial KB + +**Source:** Project intake (CONTEXT.md) + Technical Lead breakdown (BREAKDOWN.md) +**Author:** Manager ๐ŸŽฏ + +**Created:** +- `components/sandwich-beam.md` โ€” initial component file from intake data +- `materials/steel-aisi.md` โ€” material placeholder +- `fea/models/sol101-static.md` โ€” FEA model placeholder from breakdown +- `dev/gen-001.md` โ€” generation document + +**Key findings from breakdown:** +- Baseline FAILS displacement constraint (22 mm vs 10 mm limit) +- Feasible region may be narrow โ€” stiffness and mass compete +- Sandwich effect (core thickness) is the primary stiffness lever +- 9 gaps identified requiring CEO input before proceeding diff --git a/projects/hydrotech-beam/kb/_history.sync-conflict-20260214-191758-VBCUD7Z.md b/projects/hydrotech-beam/kb/_history.sync-conflict-20260214-191758-VBCUD7Z.md new file mode 100644 index 00000000..d5e91011 --- /dev/null +++ b/projects/hydrotech-beam/kb/_history.sync-conflict-20260214-191758-VBCUD7Z.md @@ -0,0 +1,48 @@ +# Knowledge Base History โ€” Hydrotech Beam + +All modifications tracked by generation. + +--- + +## Gen 002 โ€” 2026-02-10 โ€” KBS Session Processing + +**Source:** 3 KBS capture sessions recorded by Antoine (20260210-132817, 20260210-161401, 20260210-163801) +**Author:** Technical Lead ๐Ÿ”ง +**Protocol:** OP_09 โ†’ OP_10 Step 2 + +**Updated:** +- `components/sandwich-beam.md` โ€” confirmed geometry, all expression names, mass corrected to 11.33 kg (`p1`) +- `materials/steel-aisi.md` โ€” confirmed AISI 1005, density 7.3 g/cmยณ, future material expansion noted +- `fea/models/sol101-static.md` โ€” confirmed CQUAD4, 33.7 mm mesh, cantilever BCs, 10,000 kgf load +- `_index.md` โ€” generation 002, gap tracker updated (3 closed, 2 partial, 6 new) + +**Created:** +- `dev/gen-002.md` โ€” full generation document with KBS transcript analysis and mass discrepancy resolution + +**Key findings:** +- **Mass corrected:** 974 kg โ†’ 11.33 kg (expression `p1`, not `p173`) +- **Cantilever confirmed:** Left side fixed, right side loaded (10,000 kgf downward) +- **Beam length confirmed:** 5,000 mm +- **Mesh confirmed:** CQUAD4 thin shell, 33.7 mm elements +- **Material confirmed:** AISI Steel 1005, density 7.3 g/cmยณ +- **3 gaps closed** (G1, G2, G8), **6 new gaps identified** (G10โ€“G15) +- **Antoine's directive:** "Please optimize" โ€” considers model ready + +--- + +## Gen 001 โ€” 2026-02-09 โ€” Initial KB + +**Source:** Project intake (CONTEXT.md) + Technical Lead breakdown (BREAKDOWN.md) +**Author:** Manager ๐ŸŽฏ + +**Created:** +- `components/sandwich-beam.md` โ€” initial component file from intake data +- `materials/steel-aisi.md` โ€” material placeholder +- `fea/models/sol101-static.md` โ€” FEA model placeholder from breakdown +- `dev/gen-001.md` โ€” generation document + +**Key findings from breakdown:** +- Baseline FAILS displacement constraint (22 mm vs 10 mm limit) +- Feasible region may be narrow โ€” stiffness and mass compete +- Sandwich effect (core thickness) is the primary stiffness lever +- 9 gaps identified requiring CEO input before proceeding diff --git a/projects/hydrotech-beam/kb/_index.md b/projects/hydrotech-beam/kb/_index.md index f44b2539..feaec488 100644 --- a/projects/hydrotech-beam/kb/_index.md +++ b/projects/hydrotech-beam/kb/_index.md @@ -1,91 +1,91 @@ -# Knowledge Base โ€” Hydrotech Beam - -**Project:** Hydrotech Beam Structural Optimization -**Generation:** 003 -**Last updated:** 2026-02-11 - ---- - -## Overview - -I-beam optimization for a test fixture. Steel (AISI 1005) cantilever beam with lightening holes in the web. Goal: minimize mass from **1,133.01 kg** baseline while meeting displacement (โ‰ค 10 mm) and stress (โ‰ค 130 MPa) constraints. - -**Key confirmed parameters (Gen 003):** -- Beam length: 5,000 mm (`beam_lenght` โš ๏ธ typo in NX), cantilever โ€” left fixed, right loaded -- Load: 10,000 kgf downward at free end -- Baseline mass: **1,133.01 kg** (expression `p173: body_property147.mass`) -- DV baselines: face=21.504mm, core=25.162mm, holes_dia=300mm, hole_count=10 -- Material: AISI Steel 1005 (density 7.3 g/cmยณ) -- Mesh: CQUAD4 thin shell, 33.7 mm element size -- **Solver:** DesigncenterNX 2512 (NX Nastran SOL 101) -- **First results (2026-02-11):** Displacement=17.93mm, Stress=111.9MPa, Solve time ~12s/trial - -## Components - -| Component | File | Status | -|-----------|------|--------| -| Sandwich Beam | [sandwich-beam.md](components/sandwich-beam.md) | โœ… Updated Gen 002 | - -## Materials - -| Material | File | Status | -|----------|------|--------| -| Steel AISI 1005 | [steel-aisi.md](materials/steel-aisi.md) | โœ… Updated Gen 002 | - -## FEA - -| Model | File | Status | -|-------|------|--------| -| Static Analysis (SOL 101) | [fea/models/sol101-static.md](fea/models/sol101-static.md) | โœ… Updated Gen 003 โ€” Solver running, first results obtained | - -## Generations - -| Gen | Date | Summary | -|-----|------|---------| -| 001 | 2026-02-09 | Initial KB from intake + technical breakdown | -| 002 | 2026-02-10 | KBS session processing โ€” confirmed geometry, BCs, mesh, material, mass correction | -| **003** | **2026-02-11** | **First real results! NX version fix (DesigncenterNX 2512), path resolution, backup/restore architecture, history DB, mass extraction via journal** | - -## Gap Tracker - -### โœ… CLOSED - -| # | Item | Resolution | Closed In | -|---|------|------------|-----------| -| G1 | Beam length and support conditions | 5,000 mm cantilever (left fixed, right free) | Gen 002 | -| G2 | Loading definition | 10,000 kgf point load, downward (โˆ’Y), at free end | Gen 002 | -| G8 | Mesh type and density | CQUAD4 thin shell, 33.7 mm element size (67.4/2) | Gen 002 | - -### ๐ŸŸก PARTIALLY RESOLVED - -| # | Item | Known | Remaining | -|---|------|-------|-----------| -| G5 | Hole geometric feasibility | Span = 4,000 mm, offsets = 500 mm fixed. Baseline: 10 holes ร— 300 mm โ†’ ~144 mm ligament (OK) | Need collision check at DV extremes. 15 ร— 450 mm WILL overlap. Need feasibility constraint. | -| G9 | Stress allowable basis | AISI 1005 yield ~285 MPa. 130 MPa โ†’ SF โ‰ˆ 2.2 | Need confirmation that 130 MPa limit still applies to updated model. | - -### โ“ STILL OPEN - -| # | Item | Why It Matters | Priority | -|---|------|---------------|----------| -| G7 | NX parametric rebuild reliability | Untested across full DV range โ€” need corner-case testing | Medium | -| G15 | `p6` (hole_span) as potential DV | Antoine suggested it could be optimized. Decision needed. | Medium | - -### โœ… CLOSED (Gen 003 โ€” 2026-02-11) - -| # | Item | Resolution | Closed In | -|---|------|------------|-----------| -| G3 | Displacement measurement location | Max Tz at free end, extracted via pyNastran OP2 | Gen 003 | -| G4 | Stress constraint scope | Whole model max von Mises (CQUAD4 shell) | Gen 003 | -| G6 | Result sensors in Beam_sim1.sim | Using pyNastran OP2 parsing, not NX sensors | Gen 003 | -| G10 | Baseline displacement | 17.93 mm at baseline-ish DVs | Gen 003 | -| G11 | Baseline stress | 111.9 MPa at baseline-ish DVs | Gen 003 | -| G12 | `beam_half_height` value | 250 mm โ€” confirmed via binary introspection | Gen 002+ | -| G13 | `beam_half_width` value | 150 mm โ€” confirmed via binary introspection | Gen 002+ | -| G14 | Hole diameter expression name | `holes_diameter` โ€” confirmed via binary introspection | Gen 002+ | - -## Open Tasks - -1. **Run full 51-trial DOE** โ€” solver pipeline operational, execute Phase 1 -2. **Corner-case testing** โ€” verify NX rebuild at DV extremes (G7) -3. **Decision on `p6` as DV** โ€” consult Manager/CEO (G15) -4. **Phase 1 โ†’ Phase 2 gate review** โ€” after DOE completes, assess feasibility landscape +# Knowledge Base โ€” Hydrotech Beam + +**Project:** Hydrotech Beam Structural Optimization +**Generation:** 003 +**Last updated:** 2026-02-11 + +--- + +## Overview + +I-beam optimization for a test fixture. Steel (AISI 1005) cantilever beam with lightening holes in the web. Goal: minimize mass from **1,133.01 kg** baseline while meeting displacement (โ‰ค 10 mm) and stress (โ‰ค 130 MPa) constraints. + +**Key confirmed parameters (Gen 003):** +- Beam length: 5,000 mm (`beam_lenght` โš ๏ธ typo in NX), cantilever โ€” left fixed, right loaded +- Load: 10,000 kgf downward at free end +- Baseline mass: **1,133.01 kg** (expression `p173: body_property147.mass`) +- DV baselines: face=21.504mm, core=25.162mm, holes_dia=300mm, hole_count=10 +- Material: AISI Steel 1005 (density 7.3 g/cmยณ) +- Mesh: CQUAD4 thin shell, 33.7 mm element size +- **Solver:** DesigncenterNX 2512 (NX Nastran SOL 101) +- **First results (2026-02-11):** Displacement=17.93mm, Stress=111.9MPa, Solve time ~12s/trial + +## Components + +| Component | File | Status | +|-----------|------|--------| +| Sandwich Beam | [sandwich-beam.md](components/sandwich-beam.md) | โœ… Updated Gen 002 | + +## Materials + +| Material | File | Status | +|----------|------|--------| +| Steel AISI 1005 | [steel-aisi.md](materials/steel-aisi.md) | โœ… Updated Gen 002 | + +## FEA + +| Model | File | Status | +|-------|------|--------| +| Static Analysis (SOL 101) | [fea/models/sol101-static.md](fea/models/sol101-static.md) | โœ… Updated Gen 003 โ€” Solver running, first results obtained | + +## Generations + +| Gen | Date | Summary | +|-----|------|---------| +| 001 | 2026-02-09 | Initial KB from intake + technical breakdown | +| 002 | 2026-02-10 | KBS session processing โ€” confirmed geometry, BCs, mesh, material, mass correction | +| **003** | **2026-02-11** | **First real results! NX version fix (DesigncenterNX 2512), path resolution, backup/restore architecture, history DB, mass extraction via journal** | + +## Gap Tracker + +### โœ… CLOSED + +| # | Item | Resolution | Closed In | +|---|------|------------|-----------| +| G1 | Beam length and support conditions | 5,000 mm cantilever (left fixed, right free) | Gen 002 | +| G2 | Loading definition | 10,000 kgf point load, downward (โˆ’Y), at free end | Gen 002 | +| G8 | Mesh type and density | CQUAD4 thin shell, 33.7 mm element size (67.4/2) | Gen 002 | + +### ๐ŸŸก PARTIALLY RESOLVED + +| # | Item | Known | Remaining | +|---|------|-------|-----------| +| G5 | Hole geometric feasibility | Span = 4,000 mm, offsets = 500 mm fixed. Baseline: 10 holes ร— 300 mm โ†’ ~144 mm ligament (OK) | Need collision check at DV extremes. 15 ร— 450 mm WILL overlap. Need feasibility constraint. | +| G9 | Stress allowable basis | AISI 1005 yield ~285 MPa. 130 MPa โ†’ SF โ‰ˆ 2.2 | Need confirmation that 130 MPa limit still applies to updated model. | + +### โ“ STILL OPEN + +| # | Item | Why It Matters | Priority | +|---|------|---------------|----------| +| G7 | NX parametric rebuild reliability | Untested across full DV range โ€” need corner-case testing | Medium | +| G15 | `p6` (hole_span) as potential DV | Antoine suggested it could be optimized. Decision needed. | Medium | + +### โœ… CLOSED (Gen 003 โ€” 2026-02-11) + +| # | Item | Resolution | Closed In | +|---|------|------------|-----------| +| G3 | Displacement measurement location | Max Tz at free end, extracted via pyNastran OP2 | Gen 003 | +| G4 | Stress constraint scope | Whole model max von Mises (CQUAD4 shell) | Gen 003 | +| G6 | Result sensors in Beam_sim1.sim | Using pyNastran OP2 parsing, not NX sensors | Gen 003 | +| G10 | Baseline displacement | 17.93 mm at baseline-ish DVs | Gen 003 | +| G11 | Baseline stress | 111.9 MPa at baseline-ish DVs | Gen 003 | +| G12 | `beam_half_height` value | 250 mm โ€” confirmed via binary introspection | Gen 002+ | +| G13 | `beam_half_width` value | 150 mm โ€” confirmed via binary introspection | Gen 002+ | +| G14 | Hole diameter expression name | `holes_diameter` โ€” confirmed via binary introspection | Gen 002+ | + +## Open Tasks + +1. **Run full 51-trial DOE** โ€” solver pipeline operational, execute Phase 1 +2. **Corner-case testing** โ€” verify NX rebuild at DV extremes (G7) +3. **Decision on `p6` as DV** โ€” consult Manager/CEO (G15) +4. **Phase 1 โ†’ Phase 2 gate review** โ€” after DOE completes, assess feasibility landscape diff --git a/projects/hydrotech-beam/kb/_index.sync-conflict-20260209-202842-VBCUD7Z.md b/projects/hydrotech-beam/kb/_index.sync-conflict-20260209-202842-VBCUD7Z.md index d4af7131..bc8d476d 100644 --- a/projects/hydrotech-beam/kb/_index.sync-conflict-20260209-202842-VBCUD7Z.md +++ b/projects/hydrotech-beam/kb/_index.sync-conflict-20260209-202842-VBCUD7Z.md @@ -1,47 +1,47 @@ -# Knowledge Base โ€” Hydrotech Beam - -**Project:** Hydrotech Beam Structural Optimization -**Generation:** 001 -**Last updated:** 2026-02-09 - ---- - -## Overview - -Sandwich I-beam optimization for a test fixture. Steel construction with lightening holes in the web. Goal: reduce mass from ~974 kg while meeting displacement (โ‰ค 10 mm) and stress (โ‰ค 130 MPa) constraints. - -## Components - -| Component | File | Status | -|-----------|------|--------| -| Sandwich Beam | [sandwich-beam.md](components/sandwich-beam.md) | Initial | - -## Materials - -| Material | File | Status | -|----------|------|--------| -| Steel (AISI) | [steel-aisi.md](materials/steel-aisi.md) | Initial | - -## FEA - -| Model | File | Status | -|-------|------|--------| -| Static Analysis (SOL 101) | [fea/models/sol101-static.md](fea/models/sol101-static.md) | Pending gap resolution | - -## Generations - -| Gen | Date | Summary | -|-----|------|---------| -| 001 | 2026-02-09 | Initial KB from intake + technical breakdown | - -## Open Tasks - -- โ“ G1: Beam length and support conditions -- โ“ G2: Loading definition (point? distributed? self-weight?) -- โ“ G3: Displacement measurement location and DOF -- โ“ G4: Stress constraint scope (whole model? exclude supports?) -- โ“ G5: Geometric feasibility of hole patterns at extremes -- โ“ G6: Result sensors in Beam_sim1.sim -- โ“ G7: NX parametric rebuild reliability across full range -- โ“ G8: Mesh type, density, convergence status -- โ“ G9: 130 MPa stress limit basis (yield? safety factor?) +# Knowledge Base โ€” Hydrotech Beam + +**Project:** Hydrotech Beam Structural Optimization +**Generation:** 001 +**Last updated:** 2026-02-09 + +--- + +## Overview + +Sandwich I-beam optimization for a test fixture. Steel construction with lightening holes in the web. Goal: reduce mass from ~974 kg while meeting displacement (โ‰ค 10 mm) and stress (โ‰ค 130 MPa) constraints. + +## Components + +| Component | File | Status | +|-----------|------|--------| +| Sandwich Beam | [sandwich-beam.md](components/sandwich-beam.md) | Initial | + +## Materials + +| Material | File | Status | +|----------|------|--------| +| Steel (AISI) | [steel-aisi.md](materials/steel-aisi.md) | Initial | + +## FEA + +| Model | File | Status | +|-------|------|--------| +| Static Analysis (SOL 101) | [fea/models/sol101-static.md](fea/models/sol101-static.md) | Pending gap resolution | + +## Generations + +| Gen | Date | Summary | +|-----|------|---------| +| 001 | 2026-02-09 | Initial KB from intake + technical breakdown | + +## Open Tasks + +- โ“ G1: Beam length and support conditions +- โ“ G2: Loading definition (point? distributed? self-weight?) +- โ“ G3: Displacement measurement location and DOF +- โ“ G4: Stress constraint scope (whole model? exclude supports?) +- โ“ G5: Geometric feasibility of hole patterns at extremes +- โ“ G6: Result sensors in Beam_sim1.sim +- โ“ G7: NX parametric rebuild reliability across full range +- โ“ G8: Mesh type, density, convergence status +- โ“ G9: 130 MPa stress limit basis (yield? safety factor?) diff --git a/projects/hydrotech-beam/kb/_index.sync-conflict-20260214-191751-VBCUD7Z.md b/projects/hydrotech-beam/kb/_index.sync-conflict-20260214-191751-VBCUD7Z.md new file mode 100644 index 00000000..f44b2539 --- /dev/null +++ b/projects/hydrotech-beam/kb/_index.sync-conflict-20260214-191751-VBCUD7Z.md @@ -0,0 +1,91 @@ +# Knowledge Base โ€” Hydrotech Beam + +**Project:** Hydrotech Beam Structural Optimization +**Generation:** 003 +**Last updated:** 2026-02-11 + +--- + +## Overview + +I-beam optimization for a test fixture. Steel (AISI 1005) cantilever beam with lightening holes in the web. Goal: minimize mass from **1,133.01 kg** baseline while meeting displacement (โ‰ค 10 mm) and stress (โ‰ค 130 MPa) constraints. + +**Key confirmed parameters (Gen 003):** +- Beam length: 5,000 mm (`beam_lenght` โš ๏ธ typo in NX), cantilever โ€” left fixed, right loaded +- Load: 10,000 kgf downward at free end +- Baseline mass: **1,133.01 kg** (expression `p173: body_property147.mass`) +- DV baselines: face=21.504mm, core=25.162mm, holes_dia=300mm, hole_count=10 +- Material: AISI Steel 1005 (density 7.3 g/cmยณ) +- Mesh: CQUAD4 thin shell, 33.7 mm element size +- **Solver:** DesigncenterNX 2512 (NX Nastran SOL 101) +- **First results (2026-02-11):** Displacement=17.93mm, Stress=111.9MPa, Solve time ~12s/trial + +## Components + +| Component | File | Status | +|-----------|------|--------| +| Sandwich Beam | [sandwich-beam.md](components/sandwich-beam.md) | โœ… Updated Gen 002 | + +## Materials + +| Material | File | Status | +|----------|------|--------| +| Steel AISI 1005 | [steel-aisi.md](materials/steel-aisi.md) | โœ… Updated Gen 002 | + +## FEA + +| Model | File | Status | +|-------|------|--------| +| Static Analysis (SOL 101) | [fea/models/sol101-static.md](fea/models/sol101-static.md) | โœ… Updated Gen 003 โ€” Solver running, first results obtained | + +## Generations + +| Gen | Date | Summary | +|-----|------|---------| +| 001 | 2026-02-09 | Initial KB from intake + technical breakdown | +| 002 | 2026-02-10 | KBS session processing โ€” confirmed geometry, BCs, mesh, material, mass correction | +| **003** | **2026-02-11** | **First real results! NX version fix (DesigncenterNX 2512), path resolution, backup/restore architecture, history DB, mass extraction via journal** | + +## Gap Tracker + +### โœ… CLOSED + +| # | Item | Resolution | Closed In | +|---|------|------------|-----------| +| G1 | Beam length and support conditions | 5,000 mm cantilever (left fixed, right free) | Gen 002 | +| G2 | Loading definition | 10,000 kgf point load, downward (โˆ’Y), at free end | Gen 002 | +| G8 | Mesh type and density | CQUAD4 thin shell, 33.7 mm element size (67.4/2) | Gen 002 | + +### ๐ŸŸก PARTIALLY RESOLVED + +| # | Item | Known | Remaining | +|---|------|-------|-----------| +| G5 | Hole geometric feasibility | Span = 4,000 mm, offsets = 500 mm fixed. Baseline: 10 holes ร— 300 mm โ†’ ~144 mm ligament (OK) | Need collision check at DV extremes. 15 ร— 450 mm WILL overlap. Need feasibility constraint. | +| G9 | Stress allowable basis | AISI 1005 yield ~285 MPa. 130 MPa โ†’ SF โ‰ˆ 2.2 | Need confirmation that 130 MPa limit still applies to updated model. | + +### โ“ STILL OPEN + +| # | Item | Why It Matters | Priority | +|---|------|---------------|----------| +| G7 | NX parametric rebuild reliability | Untested across full DV range โ€” need corner-case testing | Medium | +| G15 | `p6` (hole_span) as potential DV | Antoine suggested it could be optimized. Decision needed. | Medium | + +### โœ… CLOSED (Gen 003 โ€” 2026-02-11) + +| # | Item | Resolution | Closed In | +|---|------|------------|-----------| +| G3 | Displacement measurement location | Max Tz at free end, extracted via pyNastran OP2 | Gen 003 | +| G4 | Stress constraint scope | Whole model max von Mises (CQUAD4 shell) | Gen 003 | +| G6 | Result sensors in Beam_sim1.sim | Using pyNastran OP2 parsing, not NX sensors | Gen 003 | +| G10 | Baseline displacement | 17.93 mm at baseline-ish DVs | Gen 003 | +| G11 | Baseline stress | 111.9 MPa at baseline-ish DVs | Gen 003 | +| G12 | `beam_half_height` value | 250 mm โ€” confirmed via binary introspection | Gen 002+ | +| G13 | `beam_half_width` value | 150 mm โ€” confirmed via binary introspection | Gen 002+ | +| G14 | Hole diameter expression name | `holes_diameter` โ€” confirmed via binary introspection | Gen 002+ | + +## Open Tasks + +1. **Run full 51-trial DOE** โ€” solver pipeline operational, execute Phase 1 +2. **Corner-case testing** โ€” verify NX rebuild at DV extremes (G7) +3. **Decision on `p6` as DV** โ€” consult Manager/CEO (G15) +4. **Phase 1 โ†’ Phase 2 gate review** โ€” after DOE completes, assess feasibility landscape diff --git a/projects/hydrotech-beam/kb/components/sandwich-beam.md b/projects/hydrotech-beam/kb/components/sandwich-beam.md index b4cd43ab..100cc43f 100644 --- a/projects/hydrotech-beam/kb/components/sandwich-beam.md +++ b/projects/hydrotech-beam/kb/components/sandwich-beam.md @@ -1,76 +1,76 @@ -# Sandwich Beam - -**Type:** Primary structural component -**Material:** Steel (AISI 1005) โ€” see [steel-aisi.md](../materials/steel-aisi.md) -**Status:** Confirmed from KBS session (Gen 002) - ---- - -## Description - -Sandwich I-beam serving as primary load-bearing member in a test fixture assembly. Cross-section features a core layer flanked by face sheets on top and bottom flanges. Web contains a pattern of lightening holes (circular cutouts) to reduce mass. Beam is extruded from an I-beam sketch profile. - -## Geometry โ€” NX Expressions - -| Parameter | NX Expression | Baseline Value | Units | DV? | Range | Notes | -|-----------|--------------|----------------|-------|-----|-------|-------| -| Beam half-height | `beam_half_height` | โ“ TBD (Gap G12) | mm | No (currently) | โ€” | Half total I-beam height | -| Beam half-width | `beam_half_width` | โ“ TBD (Gap G13) | mm | No (currently) | โ€” | Half flange width | -| Face thickness | `beam_face_thickness` | 20 | mm | **Yes (DV2)** | 10โ€“40 | Flange thickness | -| Core half-thickness | `beam_half_core_thickness` | 20 | mm | **Yes (DV1)** | 10โ€“40 | Half web height | -| Beam length | `beam_length` | 5,000 | mm | No | Fixed | โœ… Confirmed (KBS) | -| Hole count | `hole_count` | 10 | โ€” | **Yes (DV4)** | 5โ€“15 (integer) | โœ… Confirmed (KBS) | -| Hole diameter | โ“ (Gap G14) | 300 | mm | **Yes (DV3)** | 150โ€“450 | โœ… Confirmed (KBS) | -| Hole span | `p6` | 4,000 | mm | Potential (G15) | TBD | โœ… Confirmed (KBS) โ€” total span for hole distribution | -| Hole start offset | (fixed) | 500 | mm | No | Fixed | Requirement โ€” not parametric | -| Hole end offset | (fixed) | 500 | mm | No | Fixed | Requirement โ€” not parametric | - -## Mass - -| Parameter | NX Expression | Value | Units | Source | -|-----------|--------------|-------|-------|--------| -| Mass (baseline) | **`p1`** | **11.33** | kg | โœ… KBS session 20260210-163801 | -| Density | (material card) | 7.3 | g/cmยณ | โœ… KBS session โ€” Antoine stated directly | - -> โš ๏ธ **Previous value of ~974 kg (expression `p173`) is superseded.** See Gen 002 mass discrepancy resolution. - -## Construction Method - -1. **Sketch:** I-beam cross-section profile with 4 key expressions (`beam_half_height`, `beam_half_width`, `beam_face_thickness`, `beam_half_core_thickness`) -2. **Extrusion:** Sketch extruded to `beam_length` (5,000 mm) โ†’ solid body -3. **Holes:** `hole_count` circular holes of specified diameter, distributed over `p6` span (4,000 mm), centered in web, starting 500 mm from each end -4. **Idealization:** Promote body โ†’ mid-surface extraction (pair mid-surface function) โ†’ thin shell sheet bodies - -## Hole Geometry Constraints - -- **Span:** Holes are distributed over 4,000 mm (expression `p6`) -- **Fixed offsets:** First hole center at โ‰ฅ500 mm from beam start, last hole center at โ‰ค4,500 mm from beam start -- **Spacing:** At baseline: 10 holes in 4,000 mm โ†’ ~444 mm center-to-center, 300 mm diameter โ†’ ~144 mm ligament -- **Collision risk:** At extremes (15 holes ร— 450 mm dia in 4,000 mm): spacing = 267 mm, ligament = โˆ’183 mm โ†’ **HOLES OVERLAP** โ†’ infeasible -- **Feasibility formula:** `ligament = (hole_span / (hole_count - 1)) - hole_diameter > 0` - - Must also check: `hole_diameter < web_height` (hole fits in web vertically) - -## Structural Behavior - -*From Technical Breakdown (Gen 001), confirmed by KBS session:* - -The beam is a **cantilever** (left side fixed, right side free with 10,000 kgf downward load). It is **bending-dominated**: - -| Behavior | Governing Parameters | Notes | -|----------|---------------------|-------| -| Bending stiffness (EI) | Face thickness, core thickness | Faces carry bending stress, core carries shear. Stiffness scales ~quadratically with distance from neutral axis | -| Mass | All four variables | Core and face add material; holes remove material from web | -| Stress concentrations | Hole diameter, hole spacing | Larger holes โ†’ higher SCF at edges. Closely spaced holes can interact | -| Shear capacity | Core thickness, hole count, diameter | Holes reduce shear-carrying area | - -## Design Variable Interactions - -1. **Core ร— Face** โ€” classic sandwich interaction. Optimal stiffness balances core depth (lever arm) vs face material (bending resistance) -2. **Hole diameter ร— Hole count** โ€” both remove web material. Large + many could leave insufficient ligament width -3. **Face ร— Hole diameter** โ€” thicker faces reduce nominal stress, allowing larger holes -4. **Core ร— Hole diameter** โ€” core thickness determines web height, constrains max feasible hole diameter - -## History - -- **Gen 001** (2026-02-09): Initial documentation from intake + technical breakdown -- **Gen 002** (2026-02-10): Updated with confirmed values from KBS sessions. Mass corrected from 974 kg โ†’ 11.33 kg. Beam length confirmed 5,000 mm. Hole parameters detailed. Expression names confirmed. +# Sandwich Beam + +**Type:** Primary structural component +**Material:** Steel (AISI 1005) โ€” see [steel-aisi.md](../materials/steel-aisi.md) +**Status:** Confirmed from KBS session (Gen 002) + +--- + +## Description + +Sandwich I-beam serving as primary load-bearing member in a test fixture assembly. Cross-section features a core layer flanked by face sheets on top and bottom flanges. Web contains a pattern of lightening holes (circular cutouts) to reduce mass. Beam is extruded from an I-beam sketch profile. + +## Geometry โ€” NX Expressions + +| Parameter | NX Expression | Baseline Value | Units | DV? | Range | Notes | +|-----------|--------------|----------------|-------|-----|-------|-------| +| Beam half-height | `beam_half_height` | โ“ TBD (Gap G12) | mm | No (currently) | โ€” | Half total I-beam height | +| Beam half-width | `beam_half_width` | โ“ TBD (Gap G13) | mm | No (currently) | โ€” | Half flange width | +| Face thickness | `beam_face_thickness` | 20 | mm | **Yes (DV2)** | 10โ€“40 | Flange thickness | +| Core half-thickness | `beam_half_core_thickness` | 20 | mm | **Yes (DV1)** | 10โ€“40 | Half web height | +| Beam length | `beam_length` | 5,000 | mm | No | Fixed | โœ… Confirmed (KBS) | +| Hole count | `hole_count` | 10 | โ€” | **Yes (DV4)** | 5โ€“15 (integer) | โœ… Confirmed (KBS) | +| Hole diameter | โ“ (Gap G14) | 300 | mm | **Yes (DV3)** | 150โ€“450 | โœ… Confirmed (KBS) | +| Hole span | `p6` | 4,000 | mm | Potential (G15) | TBD | โœ… Confirmed (KBS) โ€” total span for hole distribution | +| Hole start offset | (fixed) | 500 | mm | No | Fixed | Requirement โ€” not parametric | +| Hole end offset | (fixed) | 500 | mm | No | Fixed | Requirement โ€” not parametric | + +## Mass + +| Parameter | NX Expression | Value | Units | Source | +|-----------|--------------|-------|-------|--------| +| Mass (baseline) | **`p1`** | **11.33** | kg | โœ… KBS session 20260210-163801 | +| Density | (material card) | 7.3 | g/cmยณ | โœ… KBS session โ€” Antoine stated directly | + +> โš ๏ธ **Previous value of ~974 kg (expression `p173`) is superseded.** See Gen 002 mass discrepancy resolution. + +## Construction Method + +1. **Sketch:** I-beam cross-section profile with 4 key expressions (`beam_half_height`, `beam_half_width`, `beam_face_thickness`, `beam_half_core_thickness`) +2. **Extrusion:** Sketch extruded to `beam_length` (5,000 mm) โ†’ solid body +3. **Holes:** `hole_count` circular holes of specified diameter, distributed over `p6` span (4,000 mm), centered in web, starting 500 mm from each end +4. **Idealization:** Promote body โ†’ mid-surface extraction (pair mid-surface function) โ†’ thin shell sheet bodies + +## Hole Geometry Constraints + +- **Span:** Holes are distributed over 4,000 mm (expression `p6`) +- **Fixed offsets:** First hole center at โ‰ฅ500 mm from beam start, last hole center at โ‰ค4,500 mm from beam start +- **Spacing:** At baseline: 10 holes in 4,000 mm โ†’ ~444 mm center-to-center, 300 mm diameter โ†’ ~144 mm ligament +- **Collision risk:** At extremes (15 holes ร— 450 mm dia in 4,000 mm): spacing = 267 mm, ligament = โˆ’183 mm โ†’ **HOLES OVERLAP** โ†’ infeasible +- **Feasibility formula:** `ligament = (hole_span / (hole_count - 1)) - hole_diameter > 0` + - Must also check: `hole_diameter < web_height` (hole fits in web vertically) + +## Structural Behavior + +*From Technical Breakdown (Gen 001), confirmed by KBS session:* + +The beam is a **cantilever** (left side fixed, right side free with 10,000 kgf downward load). It is **bending-dominated**: + +| Behavior | Governing Parameters | Notes | +|----------|---------------------|-------| +| Bending stiffness (EI) | Face thickness, core thickness | Faces carry bending stress, core carries shear. Stiffness scales ~quadratically with distance from neutral axis | +| Mass | All four variables | Core and face add material; holes remove material from web | +| Stress concentrations | Hole diameter, hole spacing | Larger holes โ†’ higher SCF at edges. Closely spaced holes can interact | +| Shear capacity | Core thickness, hole count, diameter | Holes reduce shear-carrying area | + +## Design Variable Interactions + +1. **Core ร— Face** โ€” classic sandwich interaction. Optimal stiffness balances core depth (lever arm) vs face material (bending resistance) +2. **Hole diameter ร— Hole count** โ€” both remove web material. Large + many could leave insufficient ligament width +3. **Face ร— Hole diameter** โ€” thicker faces reduce nominal stress, allowing larger holes +4. **Core ร— Hole diameter** โ€” core thickness determines web height, constrains max feasible hole diameter + +## History + +- **Gen 001** (2026-02-09): Initial documentation from intake + technical breakdown +- **Gen 002** (2026-02-10): Updated with confirmed values from KBS sessions. Mass corrected from 974 kg โ†’ 11.33 kg. Beam length confirmed 5,000 mm. Hole parameters detailed. Expression names confirmed. diff --git a/projects/hydrotech-beam/kb/components/sandwich-beam.sync-conflict-20260209-202842-VBCUD7Z.md b/projects/hydrotech-beam/kb/components/sandwich-beam.sync-conflict-20260209-202842-VBCUD7Z.md index bdbbaae3..3f369cb5 100644 --- a/projects/hydrotech-beam/kb/components/sandwich-beam.sync-conflict-20260209-202842-VBCUD7Z.md +++ b/projects/hydrotech-beam/kb/components/sandwich-beam.sync-conflict-20260209-202842-VBCUD7Z.md @@ -1,52 +1,52 @@ -# Sandwich Beam - -**Type:** Primary structural component -**Material:** Steel (AISI) โ€” see [steel-aisi.md](../materials/steel-aisi.md) -**Status:** Baseline documented, optimization pending - ---- - -## Description - -Sandwich I-beam serving as primary load-bearing member in a test fixture assembly. Cross-section features a core layer flanked by face sheets on top and bottom flanges. Web contains a pattern of lightening holes (circular cutouts) to reduce mass. - -## Specifications - -| Parameter | Value | Units | Source | -|-----------|-------|-------|--------| -| Mass (baseline) | ~974 | kg | NX expression `p173` | -| Tip displacement (baseline) | ~22 | mm | SOL 101 result | -| Half-core thickness | 20 (baseline) | mm | DV range: 10โ€“40 | -| Face thickness | 20 (baseline) | mm | DV range: 10โ€“40 | -| Hole diameter | 300 (baseline) | mm | DV range: 150โ€“450 | -| Hole count | 10 (baseline) | โ€” | DV range: 5โ€“15 (integer) | -| Beam length | โ“ TBD | mm | Gap G1 | -| Support conditions | โ“ TBD | โ€” | Gap G1 | - -## Structural Behavior - -*From Technical Breakdown (Gen 001):* - -The beam is **bending-dominated**: - -| Behavior | Governing Parameters | Notes | -|----------|---------------------|-------| -| Bending stiffness (EI) | Face thickness, core thickness | Faces carry bending stress, core carries shear. Stiffness scales ~quadratically with distance from neutral axis | -| Mass | All four variables | Core and face add material; holes remove material from web | -| Stress concentrations | Hole diameter, hole spacing | Larger holes โ†’ higher SCF at edges. Closely spaced holes can interact | -| Shear capacity | Core thickness, hole count, diameter | Holes reduce shear-carrying area | - -## Design Variable Interactions - -1. **Core ร— Face** โ€” classic sandwich interaction. Optimal stiffness balances core depth (lever arm) vs face material (bending resistance) -2. **Hole diameter ร— Hole count** โ€” both remove web material. Large + many could leave insufficient ligament width -3. **Face ร— Hole diameter** โ€” thicker faces reduce nominal stress, allowing larger holes -4. **Core ร— Hole diameter** โ€” core thickness determines web height, constrains max feasible hole diameter - -## Key Risk - -> โš ๏ธ Baseline FAILS displacement constraint (22 mm vs 10 mm target). Optimizer must increase stiffness by >50% while reducing mass. Feasible region may be tight. - -## History - -- **Gen 001** (2026-02-09): Initial documentation from intake + technical breakdown +# Sandwich Beam + +**Type:** Primary structural component +**Material:** Steel (AISI) โ€” see [steel-aisi.md](../materials/steel-aisi.md) +**Status:** Baseline documented, optimization pending + +--- + +## Description + +Sandwich I-beam serving as primary load-bearing member in a test fixture assembly. Cross-section features a core layer flanked by face sheets on top and bottom flanges. Web contains a pattern of lightening holes (circular cutouts) to reduce mass. + +## Specifications + +| Parameter | Value | Units | Source | +|-----------|-------|-------|--------| +| Mass (baseline) | ~974 | kg | NX expression `p173` | +| Tip displacement (baseline) | ~22 | mm | SOL 101 result | +| Half-core thickness | 20 (baseline) | mm | DV range: 10โ€“40 | +| Face thickness | 20 (baseline) | mm | DV range: 10โ€“40 | +| Hole diameter | 300 (baseline) | mm | DV range: 150โ€“450 | +| Hole count | 10 (baseline) | โ€” | DV range: 5โ€“15 (integer) | +| Beam length | โ“ TBD | mm | Gap G1 | +| Support conditions | โ“ TBD | โ€” | Gap G1 | + +## Structural Behavior + +*From Technical Breakdown (Gen 001):* + +The beam is **bending-dominated**: + +| Behavior | Governing Parameters | Notes | +|----------|---------------------|-------| +| Bending stiffness (EI) | Face thickness, core thickness | Faces carry bending stress, core carries shear. Stiffness scales ~quadratically with distance from neutral axis | +| Mass | All four variables | Core and face add material; holes remove material from web | +| Stress concentrations | Hole diameter, hole spacing | Larger holes โ†’ higher SCF at edges. Closely spaced holes can interact | +| Shear capacity | Core thickness, hole count, diameter | Holes reduce shear-carrying area | + +## Design Variable Interactions + +1. **Core ร— Face** โ€” classic sandwich interaction. Optimal stiffness balances core depth (lever arm) vs face material (bending resistance) +2. **Hole diameter ร— Hole count** โ€” both remove web material. Large + many could leave insufficient ligament width +3. **Face ร— Hole diameter** โ€” thicker faces reduce nominal stress, allowing larger holes +4. **Core ร— Hole diameter** โ€” core thickness determines web height, constrains max feasible hole diameter + +## Key Risk + +> โš ๏ธ Baseline FAILS displacement constraint (22 mm vs 10 mm target). Optimizer must increase stiffness by >50% while reducing mass. Feasible region may be tight. + +## History + +- **Gen 001** (2026-02-09): Initial documentation from intake + technical breakdown diff --git a/projects/hydrotech-beam/kb/components/sandwich-beam.sync-conflict-20260214-191802-VBCUD7Z.md b/projects/hydrotech-beam/kb/components/sandwich-beam.sync-conflict-20260214-191802-VBCUD7Z.md new file mode 100644 index 00000000..b4cd43ab --- /dev/null +++ b/projects/hydrotech-beam/kb/components/sandwich-beam.sync-conflict-20260214-191802-VBCUD7Z.md @@ -0,0 +1,76 @@ +# Sandwich Beam + +**Type:** Primary structural component +**Material:** Steel (AISI 1005) โ€” see [steel-aisi.md](../materials/steel-aisi.md) +**Status:** Confirmed from KBS session (Gen 002) + +--- + +## Description + +Sandwich I-beam serving as primary load-bearing member in a test fixture assembly. Cross-section features a core layer flanked by face sheets on top and bottom flanges. Web contains a pattern of lightening holes (circular cutouts) to reduce mass. Beam is extruded from an I-beam sketch profile. + +## Geometry โ€” NX Expressions + +| Parameter | NX Expression | Baseline Value | Units | DV? | Range | Notes | +|-----------|--------------|----------------|-------|-----|-------|-------| +| Beam half-height | `beam_half_height` | โ“ TBD (Gap G12) | mm | No (currently) | โ€” | Half total I-beam height | +| Beam half-width | `beam_half_width` | โ“ TBD (Gap G13) | mm | No (currently) | โ€” | Half flange width | +| Face thickness | `beam_face_thickness` | 20 | mm | **Yes (DV2)** | 10โ€“40 | Flange thickness | +| Core half-thickness | `beam_half_core_thickness` | 20 | mm | **Yes (DV1)** | 10โ€“40 | Half web height | +| Beam length | `beam_length` | 5,000 | mm | No | Fixed | โœ… Confirmed (KBS) | +| Hole count | `hole_count` | 10 | โ€” | **Yes (DV4)** | 5โ€“15 (integer) | โœ… Confirmed (KBS) | +| Hole diameter | โ“ (Gap G14) | 300 | mm | **Yes (DV3)** | 150โ€“450 | โœ… Confirmed (KBS) | +| Hole span | `p6` | 4,000 | mm | Potential (G15) | TBD | โœ… Confirmed (KBS) โ€” total span for hole distribution | +| Hole start offset | (fixed) | 500 | mm | No | Fixed | Requirement โ€” not parametric | +| Hole end offset | (fixed) | 500 | mm | No | Fixed | Requirement โ€” not parametric | + +## Mass + +| Parameter | NX Expression | Value | Units | Source | +|-----------|--------------|-------|-------|--------| +| Mass (baseline) | **`p1`** | **11.33** | kg | โœ… KBS session 20260210-163801 | +| Density | (material card) | 7.3 | g/cmยณ | โœ… KBS session โ€” Antoine stated directly | + +> โš ๏ธ **Previous value of ~974 kg (expression `p173`) is superseded.** See Gen 002 mass discrepancy resolution. + +## Construction Method + +1. **Sketch:** I-beam cross-section profile with 4 key expressions (`beam_half_height`, `beam_half_width`, `beam_face_thickness`, `beam_half_core_thickness`) +2. **Extrusion:** Sketch extruded to `beam_length` (5,000 mm) โ†’ solid body +3. **Holes:** `hole_count` circular holes of specified diameter, distributed over `p6` span (4,000 mm), centered in web, starting 500 mm from each end +4. **Idealization:** Promote body โ†’ mid-surface extraction (pair mid-surface function) โ†’ thin shell sheet bodies + +## Hole Geometry Constraints + +- **Span:** Holes are distributed over 4,000 mm (expression `p6`) +- **Fixed offsets:** First hole center at โ‰ฅ500 mm from beam start, last hole center at โ‰ค4,500 mm from beam start +- **Spacing:** At baseline: 10 holes in 4,000 mm โ†’ ~444 mm center-to-center, 300 mm diameter โ†’ ~144 mm ligament +- **Collision risk:** At extremes (15 holes ร— 450 mm dia in 4,000 mm): spacing = 267 mm, ligament = โˆ’183 mm โ†’ **HOLES OVERLAP** โ†’ infeasible +- **Feasibility formula:** `ligament = (hole_span / (hole_count - 1)) - hole_diameter > 0` + - Must also check: `hole_diameter < web_height` (hole fits in web vertically) + +## Structural Behavior + +*From Technical Breakdown (Gen 001), confirmed by KBS session:* + +The beam is a **cantilever** (left side fixed, right side free with 10,000 kgf downward load). It is **bending-dominated**: + +| Behavior | Governing Parameters | Notes | +|----------|---------------------|-------| +| Bending stiffness (EI) | Face thickness, core thickness | Faces carry bending stress, core carries shear. Stiffness scales ~quadratically with distance from neutral axis | +| Mass | All four variables | Core and face add material; holes remove material from web | +| Stress concentrations | Hole diameter, hole spacing | Larger holes โ†’ higher SCF at edges. Closely spaced holes can interact | +| Shear capacity | Core thickness, hole count, diameter | Holes reduce shear-carrying area | + +## Design Variable Interactions + +1. **Core ร— Face** โ€” classic sandwich interaction. Optimal stiffness balances core depth (lever arm) vs face material (bending resistance) +2. **Hole diameter ร— Hole count** โ€” both remove web material. Large + many could leave insufficient ligament width +3. **Face ร— Hole diameter** โ€” thicker faces reduce nominal stress, allowing larger holes +4. **Core ร— Hole diameter** โ€” core thickness determines web height, constrains max feasible hole diameter + +## History + +- **Gen 001** (2026-02-09): Initial documentation from intake + technical breakdown +- **Gen 002** (2026-02-10): Updated with confirmed values from KBS sessions. Mass corrected from 974 kg โ†’ 11.33 kg. Beam length confirmed 5,000 mm. Hole parameters detailed. Expression names confirmed. diff --git a/projects/hydrotech-beam/kb/dev/gen-001.sync-conflict-20260209-202842-VBCUD7Z.md b/projects/hydrotech-beam/kb/dev/gen-001.sync-conflict-20260209-202842-VBCUD7Z.md index ac818456..47c32920 100644 --- a/projects/hydrotech-beam/kb/dev/gen-001.sync-conflict-20260209-202842-VBCUD7Z.md +++ b/projects/hydrotech-beam/kb/dev/gen-001.sync-conflict-20260209-202842-VBCUD7Z.md @@ -1,58 +1,58 @@ -# Gen 001 โ€” Project Intake + Technical Breakdown - -**Date:** 2026-02-09 -**Sources:** CEO intake in #project-hydrotech-beam, Technical Lead analysis -**Author:** Manager ๐ŸŽฏ - ---- - -## What Happened - -1. Antoine submitted the Hydrotech beam optimization request via #project-hydrotech-beam -2. Manager created project folder and CONTEXT.md from intake data -3. Technical Lead produced a full technical breakdown (BREAKDOWN.md) -4. Manager and CEO agreed on project structure (KB-integrated layout) - -## Key Findings - -### From Intake -- Sandwich I-beam, steel, with lightening holes in web -- 4 design variables (3 continuous + 1 integer) -- Current design: ~974 kg, ~22 mm tip displacement -- Targets: minimize mass, displacement โ‰ค 10 mm, stress โ‰ค 130 MPa - -### From Technical Breakdown -- **Critical:** Baseline already violates displacement constraint (22 mm vs 10 mm) -- Single-objective formulation recommended (minimize mass, constrain the rest) -- Two-phase approach: DoE (40โ€“50 trials) then TPE (60โ€“100 trials) -- Significant variable interactions expected (sandwich theory, hole interactions) -- 9 gaps identified needing CEO input -- Overall risk: MEDIUM-HIGH (feasible region may be tight or empty) - -## KB Entries Created - -- `components/sandwich-beam.md` โ€” component file with specs, behavior, interactions -- `materials/steel-aisi.md` โ€” placeholder, needs NX model data -- `fea/models/sol101-static.md` โ€” solver setup, pending gap resolution - -## Decisions Made - -- DEC-HB-004: KB-integrated project structure (Approved) -- DEC-HB-005: No Notion, Gitea + .md only (Approved) -- DEC-HB-006: KB skill extension pattern, no fork (Approved) - -## Open Items - -9 gaps pending CEO input (see [_index.md](../_index.md)): -- G1โ€“G2: Geometry and loading (most critical) -- G3โ€“G4: Result extraction specifics -- G5: Geometric feasibility at extremes -- G6โ€“G8: NX model details -- G9: Stress allowable basis - -## Next Steps - -1. Resolve gaps G1โ€“G9 with Antoine -2. Upload reference models to `models/` -3. Begin NX model introspection -4. Set up first study (DoE) +# Gen 001 โ€” Project Intake + Technical Breakdown + +**Date:** 2026-02-09 +**Sources:** CEO intake in #project-hydrotech-beam, Technical Lead analysis +**Author:** Manager ๐ŸŽฏ + +--- + +## What Happened + +1. Antoine submitted the Hydrotech beam optimization request via #project-hydrotech-beam +2. Manager created project folder and CONTEXT.md from intake data +3. Technical Lead produced a full technical breakdown (BREAKDOWN.md) +4. Manager and CEO agreed on project structure (KB-integrated layout) + +## Key Findings + +### From Intake +- Sandwich I-beam, steel, with lightening holes in web +- 4 design variables (3 continuous + 1 integer) +- Current design: ~974 kg, ~22 mm tip displacement +- Targets: minimize mass, displacement โ‰ค 10 mm, stress โ‰ค 130 MPa + +### From Technical Breakdown +- **Critical:** Baseline already violates displacement constraint (22 mm vs 10 mm) +- Single-objective formulation recommended (minimize mass, constrain the rest) +- Two-phase approach: DoE (40โ€“50 trials) then TPE (60โ€“100 trials) +- Significant variable interactions expected (sandwich theory, hole interactions) +- 9 gaps identified needing CEO input +- Overall risk: MEDIUM-HIGH (feasible region may be tight or empty) + +## KB Entries Created + +- `components/sandwich-beam.md` โ€” component file with specs, behavior, interactions +- `materials/steel-aisi.md` โ€” placeholder, needs NX model data +- `fea/models/sol101-static.md` โ€” solver setup, pending gap resolution + +## Decisions Made + +- DEC-HB-004: KB-integrated project structure (Approved) +- DEC-HB-005: No Notion, Gitea + .md only (Approved) +- DEC-HB-006: KB skill extension pattern, no fork (Approved) + +## Open Items + +9 gaps pending CEO input (see [_index.md](../_index.md)): +- G1โ€“G2: Geometry and loading (most critical) +- G3โ€“G4: Result extraction specifics +- G5: Geometric feasibility at extremes +- G6โ€“G8: NX model details +- G9: Stress allowable basis + +## Next Steps + +1. Resolve gaps G1โ€“G9 with Antoine +2. Upload reference models to `models/` +3. Begin NX model introspection +4. Set up first study (DoE) diff --git a/projects/hydrotech-beam/kb/dev/gen-002.md b/projects/hydrotech-beam/kb/dev/gen-002.md index 9c269aab..3ee873c2 100644 --- a/projects/hydrotech-beam/kb/dev/gen-002.md +++ b/projects/hydrotech-beam/kb/dev/gen-002.md @@ -1,206 +1,206 @@ -# Gen 002 โ€” KBS Session Processing - -**Date:** 2026-02-10 -**Sources:** 3 KBS capture sessions recorded by Antoine -**Author:** Technical Lead ๐Ÿ”ง -**Protocol:** OP_09 โ†’ OP_10 Step 2 (Technical Breakdown Update) - ---- - -## What Happened - -Antoine recorded 3 Knowledge Base Capture (KBS) sessions for the Hydrotech Beam project, walking through the complete NX model in detail. This is the first time we have direct, confirmed model parameters from the CEO's live NX walkthrough. - -### Sessions Processed - -| Session ID | Type | Duration | Content | -|-----------|------|----------|---------| -| `20260210-132817` | Analysis | 6s | No transcript โ€” too short (aborted session) | -| `20260210-161401` | Design | 38s | Brief overview: beam with holes, discretized, one side fixed, other side force in Y-direction | -| `20260210-163801` | Design | 414s (~7 min) | **Full model walkthrough** โ€” geometry, expressions, mesh, BCs, material, mass | - -### Session 2 โ€” Brief Overview (20260210-161401) - -Key quotes: -- "This is the beam that we want to optimize" -- "The beam is discretized into [shell] elements" -- "One side, the full edge is fixed and the other side there's the force in the [Y] direction" -- "This is the holes... just so that we can test the KB setup" - -### Session 3 โ€” Full Walkthrough (20260210-163801) - -This is the primary data source. Antoine walked through every aspect of the NX model. - ---- - -## Confirmed Parameters (from Session 3) - -### Geometry โ€” Part File (`Beam.prt`) - -| Parameter | NX Expression | Value | Units | Notes | -|-----------|--------------|-------|-------|-------| -| I-beam cross-section sketch | โ€” | I-beam profile | โ€” | Base geometry for extrusion | -| Beam half-height | `beam_half_height` | TBD (see screenshot triggers) | mm | Half the total I-beam height | -| Beam half-width | `beam_half_width` | TBD (see screenshot triggers) | mm | Half the flange width | -| Face thickness | `beam_face_thickness` | 20 (baseline) | mm | Flange thickness | -| Core half-thickness | `beam_half_core_thickness` | 20 (baseline) | mm | Half the web height | -| Beam length | `beam_length` | 5,000 | mm | Extrusion distance โ€” **CONFIRMED** | -| Hole count | `hole_count` | 10 | โ€” | Integer parameter โ€” **CONFIRMED** | -| Hole diameter | (expression name TBD) | 300 | mm | Starting value โ€” **CONFIRMED** | -| Hole span | `p6` | 4,000 | mm | Total span over which holes are distributed | -| Hole start offset | (fixed) | 500 | mm | From beam start โ€” **NOT a parameter** (requirement) | -| Hole end offset | (fixed) | 500 | mm | From beam end โ€” **NOT a parameter** (requirement) | - -### Mass - -| Parameter | NX Expression | Value | Units | Notes | -|-----------|--------------|-------|-------|-------| -| Mass | `p1` | 11.33 | kg | **NOT `p173` as previously assumed** | -| Density | (in material card) | 7.3 | g/cmยณ (7300 kg/mยณ) | Antoine stated "set 7,3" | - -> โš ๏ธ **MASS DISCREPANCY RESOLVED:** Intake reported ~974 kg (expression `p173`). Antoine's live session confirms 11.33 kg (expression `p1`). See analysis below. - -### Idealization (Beam_fem1_i.prt) - -| Step | Method | Notes | -|------|--------|-------| -| 1. Promote body | From `Beam.prt` solid | Brings solid geometry into idealized part | -| 2. Mid-surface extraction | Pair mid-surface function | Extracts shell surfaces from solid โ€” "within some center" | -| Output | Sheet bodies | Thin shell representation of I-beam | - -### FEM (Beam_fem1.fem) - -| Parameter | Value | Notes | -|-----------|-------|-------| -| Element type | **CQUAD4** | 4-node quadrilateral shell โ€” **CONFIRMED** | -| Property type | Thin shell collectors | Inherited material from beam material | -| Element size | 67.4 / 2 = **33.7 mm** | Subdivision-based sizing | -| Material assignment | Inherited from beam material | Through thin shell property | - -### Material - -| Property | Value | Notes | -|----------|-------|-------| -| Baseline material | **AISI Steel 1005** | (Antoine said "NSE steel 10 or 5" = ANSI Steel 1005) | -| Future expansion | Aluminum 6061, Stainless Steel ANSI 310 | Antoine's explicit instruction: "add as future expansion" | -| Density | 7.3 g/cmยณ | As stated by Antoine | - -### Simulation (Beam_sim1.sim) - -| Parameter | Value | Notes | -|-----------|-------|-------| -| Solution type | SOL 101 (Static) | Subcase: "Solution 1" โ€” static subcase | -| Fixed constraint | **Left side of beam** | Full edge fixed โ€” **cantilever CONFIRMED** | -| Applied force | **10,000 kgf downward** | Right side (free end) of beam โ€” **CONFIRMED** | -| Force direction | Downward (โˆ’Y) | "The vector is going down" โ€” project requirement | - -### Antoine's Directive -> "And we're all set. Please optimize." - ---- - -## Mass Discrepancy Resolution - -### The Problem -- **Gen 001 (intake):** Mass ~974 kg, expression `p173` -- **Gen 002 (KBS session):** Mass 11.33 kg, expression `p1` - -### Analysis - -The KBS session is the ground truth โ€” Antoine was live in NX, reading the expression value directly. The discrepancy is a factor of ~86ร—. - -Possible explanations: -1. **Different model version:** The intake data may have referenced an earlier, much larger beam geometry that was subsequently scaled down or redesigned before the KBS session -2. **Different expression:** `p173` and `p1` are different NX expressions. `p173` may reference a different body, assembly mass, or a now-deleted feature -3. **Communication error:** The 974 kg value may have been approximate, from memory, or from a different project entirely - -### Resolution - -**The confirmed baseline mass is 11.33 kg** (expression `p1`, density 7.3 g/cmยณ). - -This changes the optimization landscape significantly: -- 11.33 kg is a lightweight beam โ€” optimization will still aim to reduce mass but the absolute numbers are very different -- The displacement constraint (โ‰ค 10 mm) becomes the dominant challenge at this scale -- Stress levels need fresh baseline measurement - -### Action Items -- โœ… Update all references from `p173` โ†’ `p1` for mass expression -- โœ… Update baseline mass from 974 kg โ†’ 11.33 kg -- โš ๏ธ Re-evaluate baseline displacement (22 mm was from the old model state โ€” may need re-verification) -- โš ๏ธ Get baseline stress value (never had one) - ---- - -## New Information Flagged - -| Item | Detail | Impact | -|------|--------|--------| -| Expression `p1` for mass | Replaces `p173` โ€” different expression entirely | Extractor config must be updated | -| Expression `p6` for hole span | 4,000 mm โ€” potential new design variable | Could be added to optimization | -| Expression `beam_length` | 5,000 mm โ€” confirmed but not a DV | Fixed parameter | -| Expression `beam_half_height` | New โ€” not previously known | Need starting value | -| Expression `beam_half_width` | New โ€” not previously known | Need starting value | -| Hole offsets fixed at 500mm | Start and end positions are requirements, not variables | Constrains hole placement | -| Material expansion | Al 6061, SS ANSI 310 as future materials | Future optimization scope | -| Element size = 33.7 mm | 67.4/2 โ€” Antoine says refinement is future work | Mesh convergence still needed | - ---- - -## Gap Resolution Summary - -### Gaps CLOSED - -| Gap | Status | Resolution | -|-----|--------|------------| -| **G1:** Beam length and support conditions | โœ… **CLOSED** | Beam length = 5,000 mm. Left side fully fixed (cantilever). Confirmed by Antoine in KBS session. | -| **G2:** Loading definition | โœ… **CLOSED** | 10,000 kgf point load, downward (โˆ’Y), at right side (free end). Project requirement per Antoine. | -| **G8:** Mesh type, density, convergence | โœ… **CLOSED** (type/density) | CQUAD4 thin shell, element size 33.7 mm (67.4/2). Convergence not yet verified but mesh type confirmed. | - -### Gaps PARTIALLY RESOLVED - -| Gap | Status | What's Known | What Remains | -|-----|--------|-------------|-------------| -| **G5:** Hole geometric feasibility | ๐ŸŸก **PARTIAL** | Hole span = 4,000 mm, start/end at 500 mm from ends, current count = 10, diameter = 300 mm. At baseline: 10 holes in 4,000 mm = 400 mm spacing, 300 mm diameter โ†’ 100 mm ligament. | Need collision check formula across full DV range. At extremes (15 holes ร— 450 mm diameter in 4,000 mm), holes WILL overlap. | -| **G9:** Stress allowable basis | ๐ŸŸก **PARTIAL** | AISI 1005 yield ~285 MPa. 130 MPa limit โ†’ SF โ‰ˆ 2.2. | Still need Antoine to confirm if 130 MPa is the correct limit for this new model scale. | - -### Gaps STILL OPEN - -| Gap | Status | Notes | -|-----|--------|-------| -| **G3:** Displacement measurement location | โ“ **OPEN** | Still need to confirm: which node(s)? Which DOF? Total magnitude or single component? | -| **G4:** Stress constraint scope | โ“ **OPEN** | Whole model? Exclude supports? Stress at hole edges? | -| **G6:** Result sensors in Beam_sim1.sim | โ“ **OPEN** | Need NX model introspection to check | -| **G7:** NX parametric rebuild reliability | โ“ **OPEN** | Need corner-case testing across DV range | - -### NEW Gaps Identified - -| Gap | Description | Priority | -|-----|-------------|----------| -| **G10:** Baseline displacement re-verification | Was 22 mm at 974 kg mass. With true mass of 11.33 kg, displacement may be different. Need fresh baseline run. | **High** | -| **G11:** Baseline stress value | Never measured. Need SOL 101 baseline run to establish. | **High** | -| **G12:** Expression `beam_half_height` starting value | Known to exist but value not captured from screenshot | Medium | -| **G13:** Expression `beam_half_width` starting value | Known to exist but value not captured from screenshot | Medium | -| **G14:** Hole diameter expression name | Antoine mentioned "whole diameters" starts at 300 but didn't state the expression name explicitly | Medium | -| **G15:** `p6` (hole_span) as design variable | Antoine suggested it could be optimized. Need to decide if it enters the DV set. | Medium | - ---- - -## KB Entries Updated - -- `components/sandwich-beam.md` โ€” confirmed geometry, expressions, mass, hole parameters -- `materials/steel-aisi.md` โ€” AISI 1005 specifics, density, future materials -- `fea/models/sol101-static.md` โ€” confirmed BCs, mesh, element type, solver setup -- `kb/_index.md` โ€” gap status updates, generation table -- `kb/_history.md` โ€” Gen 002 entry -- `CONTEXT.md` โ€” confirmed parameter values, corrected mass expression - -## Decisions Needed - -1. **Re-run baseline?** โ€” Mass discrepancy suggests model has changed since intake. A fresh baseline solve would confirm displacement and stress. -2. **Add `p6` (hole_span) as DV?** โ€” Antoine suggested it. Would increase DV count from 4 to 5. -3. **Update atomizer_spec_draft.json?** โ€” Mass extractor needs `p1` not `p173`. Baseline mass is 11.33 kg not 974 kg. -4. **Proceed with optimization?** โ€” Antoine said "please optimize" โ€” but we still have open gaps (G3, G4, G6, G7, G10, G11). - ---- - -*Technical Lead ๐Ÿ”ง โ€” The physics is the boss.* +# Gen 002 โ€” KBS Session Processing + +**Date:** 2026-02-10 +**Sources:** 3 KBS capture sessions recorded by Antoine +**Author:** Technical Lead ๐Ÿ”ง +**Protocol:** OP_09 โ†’ OP_10 Step 2 (Technical Breakdown Update) + +--- + +## What Happened + +Antoine recorded 3 Knowledge Base Capture (KBS) sessions for the Hydrotech Beam project, walking through the complete NX model in detail. This is the first time we have direct, confirmed model parameters from the CEO's live NX walkthrough. + +### Sessions Processed + +| Session ID | Type | Duration | Content | +|-----------|------|----------|---------| +| `20260210-132817` | Analysis | 6s | No transcript โ€” too short (aborted session) | +| `20260210-161401` | Design | 38s | Brief overview: beam with holes, discretized, one side fixed, other side force in Y-direction | +| `20260210-163801` | Design | 414s (~7 min) | **Full model walkthrough** โ€” geometry, expressions, mesh, BCs, material, mass | + +### Session 2 โ€” Brief Overview (20260210-161401) + +Key quotes: +- "This is the beam that we want to optimize" +- "The beam is discretized into [shell] elements" +- "One side, the full edge is fixed and the other side there's the force in the [Y] direction" +- "This is the holes... just so that we can test the KB setup" + +### Session 3 โ€” Full Walkthrough (20260210-163801) + +This is the primary data source. Antoine walked through every aspect of the NX model. + +--- + +## Confirmed Parameters (from Session 3) + +### Geometry โ€” Part File (`Beam.prt`) + +| Parameter | NX Expression | Value | Units | Notes | +|-----------|--------------|-------|-------|-------| +| I-beam cross-section sketch | โ€” | I-beam profile | โ€” | Base geometry for extrusion | +| Beam half-height | `beam_half_height` | TBD (see screenshot triggers) | mm | Half the total I-beam height | +| Beam half-width | `beam_half_width` | TBD (see screenshot triggers) | mm | Half the flange width | +| Face thickness | `beam_face_thickness` | 20 (baseline) | mm | Flange thickness | +| Core half-thickness | `beam_half_core_thickness` | 20 (baseline) | mm | Half the web height | +| Beam length | `beam_length` | 5,000 | mm | Extrusion distance โ€” **CONFIRMED** | +| Hole count | `hole_count` | 10 | โ€” | Integer parameter โ€” **CONFIRMED** | +| Hole diameter | (expression name TBD) | 300 | mm | Starting value โ€” **CONFIRMED** | +| Hole span | `p6` | 4,000 | mm | Total span over which holes are distributed | +| Hole start offset | (fixed) | 500 | mm | From beam start โ€” **NOT a parameter** (requirement) | +| Hole end offset | (fixed) | 500 | mm | From beam end โ€” **NOT a parameter** (requirement) | + +### Mass + +| Parameter | NX Expression | Value | Units | Notes | +|-----------|--------------|-------|-------|-------| +| Mass | `p1` | 11.33 | kg | **NOT `p173` as previously assumed** | +| Density | (in material card) | 7.3 | g/cmยณ (7300 kg/mยณ) | Antoine stated "set 7,3" | + +> โš ๏ธ **MASS DISCREPANCY RESOLVED:** Intake reported ~974 kg (expression `p173`). Antoine's live session confirms 11.33 kg (expression `p1`). See analysis below. + +### Idealization (Beam_fem1_i.prt) + +| Step | Method | Notes | +|------|--------|-------| +| 1. Promote body | From `Beam.prt` solid | Brings solid geometry into idealized part | +| 2. Mid-surface extraction | Pair mid-surface function | Extracts shell surfaces from solid โ€” "within some center" | +| Output | Sheet bodies | Thin shell representation of I-beam | + +### FEM (Beam_fem1.fem) + +| Parameter | Value | Notes | +|-----------|-------|-------| +| Element type | **CQUAD4** | 4-node quadrilateral shell โ€” **CONFIRMED** | +| Property type | Thin shell collectors | Inherited material from beam material | +| Element size | 67.4 / 2 = **33.7 mm** | Subdivision-based sizing | +| Material assignment | Inherited from beam material | Through thin shell property | + +### Material + +| Property | Value | Notes | +|----------|-------|-------| +| Baseline material | **AISI Steel 1005** | (Antoine said "NSE steel 10 or 5" = ANSI Steel 1005) | +| Future expansion | Aluminum 6061, Stainless Steel ANSI 310 | Antoine's explicit instruction: "add as future expansion" | +| Density | 7.3 g/cmยณ | As stated by Antoine | + +### Simulation (Beam_sim1.sim) + +| Parameter | Value | Notes | +|-----------|-------|-------| +| Solution type | SOL 101 (Static) | Subcase: "Solution 1" โ€” static subcase | +| Fixed constraint | **Left side of beam** | Full edge fixed โ€” **cantilever CONFIRMED** | +| Applied force | **10,000 kgf downward** | Right side (free end) of beam โ€” **CONFIRMED** | +| Force direction | Downward (โˆ’Y) | "The vector is going down" โ€” project requirement | + +### Antoine's Directive +> "And we're all set. Please optimize." + +--- + +## Mass Discrepancy Resolution + +### The Problem +- **Gen 001 (intake):** Mass ~974 kg, expression `p173` +- **Gen 002 (KBS session):** Mass 11.33 kg, expression `p1` + +### Analysis + +The KBS session is the ground truth โ€” Antoine was live in NX, reading the expression value directly. The discrepancy is a factor of ~86ร—. + +Possible explanations: +1. **Different model version:** The intake data may have referenced an earlier, much larger beam geometry that was subsequently scaled down or redesigned before the KBS session +2. **Different expression:** `p173` and `p1` are different NX expressions. `p173` may reference a different body, assembly mass, or a now-deleted feature +3. **Communication error:** The 974 kg value may have been approximate, from memory, or from a different project entirely + +### Resolution + +**The confirmed baseline mass is 11.33 kg** (expression `p1`, density 7.3 g/cmยณ). + +This changes the optimization landscape significantly: +- 11.33 kg is a lightweight beam โ€” optimization will still aim to reduce mass but the absolute numbers are very different +- The displacement constraint (โ‰ค 10 mm) becomes the dominant challenge at this scale +- Stress levels need fresh baseline measurement + +### Action Items +- โœ… Update all references from `p173` โ†’ `p1` for mass expression +- โœ… Update baseline mass from 974 kg โ†’ 11.33 kg +- โš ๏ธ Re-evaluate baseline displacement (22 mm was from the old model state โ€” may need re-verification) +- โš ๏ธ Get baseline stress value (never had one) + +--- + +## New Information Flagged + +| Item | Detail | Impact | +|------|--------|--------| +| Expression `p1` for mass | Replaces `p173` โ€” different expression entirely | Extractor config must be updated | +| Expression `p6` for hole span | 4,000 mm โ€” potential new design variable | Could be added to optimization | +| Expression `beam_length` | 5,000 mm โ€” confirmed but not a DV | Fixed parameter | +| Expression `beam_half_height` | New โ€” not previously known | Need starting value | +| Expression `beam_half_width` | New โ€” not previously known | Need starting value | +| Hole offsets fixed at 500mm | Start and end positions are requirements, not variables | Constrains hole placement | +| Material expansion | Al 6061, SS ANSI 310 as future materials | Future optimization scope | +| Element size = 33.7 mm | 67.4/2 โ€” Antoine says refinement is future work | Mesh convergence still needed | + +--- + +## Gap Resolution Summary + +### Gaps CLOSED + +| Gap | Status | Resolution | +|-----|--------|------------| +| **G1:** Beam length and support conditions | โœ… **CLOSED** | Beam length = 5,000 mm. Left side fully fixed (cantilever). Confirmed by Antoine in KBS session. | +| **G2:** Loading definition | โœ… **CLOSED** | 10,000 kgf point load, downward (โˆ’Y), at right side (free end). Project requirement per Antoine. | +| **G8:** Mesh type, density, convergence | โœ… **CLOSED** (type/density) | CQUAD4 thin shell, element size 33.7 mm (67.4/2). Convergence not yet verified but mesh type confirmed. | + +### Gaps PARTIALLY RESOLVED + +| Gap | Status | What's Known | What Remains | +|-----|--------|-------------|-------------| +| **G5:** Hole geometric feasibility | ๐ŸŸก **PARTIAL** | Hole span = 4,000 mm, start/end at 500 mm from ends, current count = 10, diameter = 300 mm. At baseline: 10 holes in 4,000 mm = 400 mm spacing, 300 mm diameter โ†’ 100 mm ligament. | Need collision check formula across full DV range. At extremes (15 holes ร— 450 mm diameter in 4,000 mm), holes WILL overlap. | +| **G9:** Stress allowable basis | ๐ŸŸก **PARTIAL** | AISI 1005 yield ~285 MPa. 130 MPa limit โ†’ SF โ‰ˆ 2.2. | Still need Antoine to confirm if 130 MPa is the correct limit for this new model scale. | + +### Gaps STILL OPEN + +| Gap | Status | Notes | +|-----|--------|-------| +| **G3:** Displacement measurement location | โ“ **OPEN** | Still need to confirm: which node(s)? Which DOF? Total magnitude or single component? | +| **G4:** Stress constraint scope | โ“ **OPEN** | Whole model? Exclude supports? Stress at hole edges? | +| **G6:** Result sensors in Beam_sim1.sim | โ“ **OPEN** | Need NX model introspection to check | +| **G7:** NX parametric rebuild reliability | โ“ **OPEN** | Need corner-case testing across DV range | + +### NEW Gaps Identified + +| Gap | Description | Priority | +|-----|-------------|----------| +| **G10:** Baseline displacement re-verification | Was 22 mm at 974 kg mass. With true mass of 11.33 kg, displacement may be different. Need fresh baseline run. | **High** | +| **G11:** Baseline stress value | Never measured. Need SOL 101 baseline run to establish. | **High** | +| **G12:** Expression `beam_half_height` starting value | Known to exist but value not captured from screenshot | Medium | +| **G13:** Expression `beam_half_width` starting value | Known to exist but value not captured from screenshot | Medium | +| **G14:** Hole diameter expression name | Antoine mentioned "whole diameters" starts at 300 but didn't state the expression name explicitly | Medium | +| **G15:** `p6` (hole_span) as design variable | Antoine suggested it could be optimized. Need to decide if it enters the DV set. | Medium | + +--- + +## KB Entries Updated + +- `components/sandwich-beam.md` โ€” confirmed geometry, expressions, mass, hole parameters +- `materials/steel-aisi.md` โ€” AISI 1005 specifics, density, future materials +- `fea/models/sol101-static.md` โ€” confirmed BCs, mesh, element type, solver setup +- `kb/_index.md` โ€” gap status updates, generation table +- `kb/_history.md` โ€” Gen 002 entry +- `CONTEXT.md` โ€” confirmed parameter values, corrected mass expression + +## Decisions Needed + +1. **Re-run baseline?** โ€” Mass discrepancy suggests model has changed since intake. A fresh baseline solve would confirm displacement and stress. +2. **Add `p6` (hole_span) as DV?** โ€” Antoine suggested it. Would increase DV count from 4 to 5. +3. **Update atomizer_spec_draft.json?** โ€” Mass extractor needs `p1` not `p173`. Baseline mass is 11.33 kg not 974 kg. +4. **Proceed with optimization?** โ€” Antoine said "please optimize" โ€” but we still have open gaps (G3, G4, G6, G7, G10, G11). + +--- + +*Technical Lead ๐Ÿ”ง โ€” The physics is the boss.* diff --git a/projects/hydrotech-beam/kb/dev/gen-002.sync-conflict-20260214-191754-VBCUD7Z.md b/projects/hydrotech-beam/kb/dev/gen-002.sync-conflict-20260214-191754-VBCUD7Z.md new file mode 100644 index 00000000..9c269aab --- /dev/null +++ b/projects/hydrotech-beam/kb/dev/gen-002.sync-conflict-20260214-191754-VBCUD7Z.md @@ -0,0 +1,206 @@ +# Gen 002 โ€” KBS Session Processing + +**Date:** 2026-02-10 +**Sources:** 3 KBS capture sessions recorded by Antoine +**Author:** Technical Lead ๐Ÿ”ง +**Protocol:** OP_09 โ†’ OP_10 Step 2 (Technical Breakdown Update) + +--- + +## What Happened + +Antoine recorded 3 Knowledge Base Capture (KBS) sessions for the Hydrotech Beam project, walking through the complete NX model in detail. This is the first time we have direct, confirmed model parameters from the CEO's live NX walkthrough. + +### Sessions Processed + +| Session ID | Type | Duration | Content | +|-----------|------|----------|---------| +| `20260210-132817` | Analysis | 6s | No transcript โ€” too short (aborted session) | +| `20260210-161401` | Design | 38s | Brief overview: beam with holes, discretized, one side fixed, other side force in Y-direction | +| `20260210-163801` | Design | 414s (~7 min) | **Full model walkthrough** โ€” geometry, expressions, mesh, BCs, material, mass | + +### Session 2 โ€” Brief Overview (20260210-161401) + +Key quotes: +- "This is the beam that we want to optimize" +- "The beam is discretized into [shell] elements" +- "One side, the full edge is fixed and the other side there's the force in the [Y] direction" +- "This is the holes... just so that we can test the KB setup" + +### Session 3 โ€” Full Walkthrough (20260210-163801) + +This is the primary data source. Antoine walked through every aspect of the NX model. + +--- + +## Confirmed Parameters (from Session 3) + +### Geometry โ€” Part File (`Beam.prt`) + +| Parameter | NX Expression | Value | Units | Notes | +|-----------|--------------|-------|-------|-------| +| I-beam cross-section sketch | โ€” | I-beam profile | โ€” | Base geometry for extrusion | +| Beam half-height | `beam_half_height` | TBD (see screenshot triggers) | mm | Half the total I-beam height | +| Beam half-width | `beam_half_width` | TBD (see screenshot triggers) | mm | Half the flange width | +| Face thickness | `beam_face_thickness` | 20 (baseline) | mm | Flange thickness | +| Core half-thickness | `beam_half_core_thickness` | 20 (baseline) | mm | Half the web height | +| Beam length | `beam_length` | 5,000 | mm | Extrusion distance โ€” **CONFIRMED** | +| Hole count | `hole_count` | 10 | โ€” | Integer parameter โ€” **CONFIRMED** | +| Hole diameter | (expression name TBD) | 300 | mm | Starting value โ€” **CONFIRMED** | +| Hole span | `p6` | 4,000 | mm | Total span over which holes are distributed | +| Hole start offset | (fixed) | 500 | mm | From beam start โ€” **NOT a parameter** (requirement) | +| Hole end offset | (fixed) | 500 | mm | From beam end โ€” **NOT a parameter** (requirement) | + +### Mass + +| Parameter | NX Expression | Value | Units | Notes | +|-----------|--------------|-------|-------|-------| +| Mass | `p1` | 11.33 | kg | **NOT `p173` as previously assumed** | +| Density | (in material card) | 7.3 | g/cmยณ (7300 kg/mยณ) | Antoine stated "set 7,3" | + +> โš ๏ธ **MASS DISCREPANCY RESOLVED:** Intake reported ~974 kg (expression `p173`). Antoine's live session confirms 11.33 kg (expression `p1`). See analysis below. + +### Idealization (Beam_fem1_i.prt) + +| Step | Method | Notes | +|------|--------|-------| +| 1. Promote body | From `Beam.prt` solid | Brings solid geometry into idealized part | +| 2. Mid-surface extraction | Pair mid-surface function | Extracts shell surfaces from solid โ€” "within some center" | +| Output | Sheet bodies | Thin shell representation of I-beam | + +### FEM (Beam_fem1.fem) + +| Parameter | Value | Notes | +|-----------|-------|-------| +| Element type | **CQUAD4** | 4-node quadrilateral shell โ€” **CONFIRMED** | +| Property type | Thin shell collectors | Inherited material from beam material | +| Element size | 67.4 / 2 = **33.7 mm** | Subdivision-based sizing | +| Material assignment | Inherited from beam material | Through thin shell property | + +### Material + +| Property | Value | Notes | +|----------|-------|-------| +| Baseline material | **AISI Steel 1005** | (Antoine said "NSE steel 10 or 5" = ANSI Steel 1005) | +| Future expansion | Aluminum 6061, Stainless Steel ANSI 310 | Antoine's explicit instruction: "add as future expansion" | +| Density | 7.3 g/cmยณ | As stated by Antoine | + +### Simulation (Beam_sim1.sim) + +| Parameter | Value | Notes | +|-----------|-------|-------| +| Solution type | SOL 101 (Static) | Subcase: "Solution 1" โ€” static subcase | +| Fixed constraint | **Left side of beam** | Full edge fixed โ€” **cantilever CONFIRMED** | +| Applied force | **10,000 kgf downward** | Right side (free end) of beam โ€” **CONFIRMED** | +| Force direction | Downward (โˆ’Y) | "The vector is going down" โ€” project requirement | + +### Antoine's Directive +> "And we're all set. Please optimize." + +--- + +## Mass Discrepancy Resolution + +### The Problem +- **Gen 001 (intake):** Mass ~974 kg, expression `p173` +- **Gen 002 (KBS session):** Mass 11.33 kg, expression `p1` + +### Analysis + +The KBS session is the ground truth โ€” Antoine was live in NX, reading the expression value directly. The discrepancy is a factor of ~86ร—. + +Possible explanations: +1. **Different model version:** The intake data may have referenced an earlier, much larger beam geometry that was subsequently scaled down or redesigned before the KBS session +2. **Different expression:** `p173` and `p1` are different NX expressions. `p173` may reference a different body, assembly mass, or a now-deleted feature +3. **Communication error:** The 974 kg value may have been approximate, from memory, or from a different project entirely + +### Resolution + +**The confirmed baseline mass is 11.33 kg** (expression `p1`, density 7.3 g/cmยณ). + +This changes the optimization landscape significantly: +- 11.33 kg is a lightweight beam โ€” optimization will still aim to reduce mass but the absolute numbers are very different +- The displacement constraint (โ‰ค 10 mm) becomes the dominant challenge at this scale +- Stress levels need fresh baseline measurement + +### Action Items +- โœ… Update all references from `p173` โ†’ `p1` for mass expression +- โœ… Update baseline mass from 974 kg โ†’ 11.33 kg +- โš ๏ธ Re-evaluate baseline displacement (22 mm was from the old model state โ€” may need re-verification) +- โš ๏ธ Get baseline stress value (never had one) + +--- + +## New Information Flagged + +| Item | Detail | Impact | +|------|--------|--------| +| Expression `p1` for mass | Replaces `p173` โ€” different expression entirely | Extractor config must be updated | +| Expression `p6` for hole span | 4,000 mm โ€” potential new design variable | Could be added to optimization | +| Expression `beam_length` | 5,000 mm โ€” confirmed but not a DV | Fixed parameter | +| Expression `beam_half_height` | New โ€” not previously known | Need starting value | +| Expression `beam_half_width` | New โ€” not previously known | Need starting value | +| Hole offsets fixed at 500mm | Start and end positions are requirements, not variables | Constrains hole placement | +| Material expansion | Al 6061, SS ANSI 310 as future materials | Future optimization scope | +| Element size = 33.7 mm | 67.4/2 โ€” Antoine says refinement is future work | Mesh convergence still needed | + +--- + +## Gap Resolution Summary + +### Gaps CLOSED + +| Gap | Status | Resolution | +|-----|--------|------------| +| **G1:** Beam length and support conditions | โœ… **CLOSED** | Beam length = 5,000 mm. Left side fully fixed (cantilever). Confirmed by Antoine in KBS session. | +| **G2:** Loading definition | โœ… **CLOSED** | 10,000 kgf point load, downward (โˆ’Y), at right side (free end). Project requirement per Antoine. | +| **G8:** Mesh type, density, convergence | โœ… **CLOSED** (type/density) | CQUAD4 thin shell, element size 33.7 mm (67.4/2). Convergence not yet verified but mesh type confirmed. | + +### Gaps PARTIALLY RESOLVED + +| Gap | Status | What's Known | What Remains | +|-----|--------|-------------|-------------| +| **G5:** Hole geometric feasibility | ๐ŸŸก **PARTIAL** | Hole span = 4,000 mm, start/end at 500 mm from ends, current count = 10, diameter = 300 mm. At baseline: 10 holes in 4,000 mm = 400 mm spacing, 300 mm diameter โ†’ 100 mm ligament. | Need collision check formula across full DV range. At extremes (15 holes ร— 450 mm diameter in 4,000 mm), holes WILL overlap. | +| **G9:** Stress allowable basis | ๐ŸŸก **PARTIAL** | AISI 1005 yield ~285 MPa. 130 MPa limit โ†’ SF โ‰ˆ 2.2. | Still need Antoine to confirm if 130 MPa is the correct limit for this new model scale. | + +### Gaps STILL OPEN + +| Gap | Status | Notes | +|-----|--------|-------| +| **G3:** Displacement measurement location | โ“ **OPEN** | Still need to confirm: which node(s)? Which DOF? Total magnitude or single component? | +| **G4:** Stress constraint scope | โ“ **OPEN** | Whole model? Exclude supports? Stress at hole edges? | +| **G6:** Result sensors in Beam_sim1.sim | โ“ **OPEN** | Need NX model introspection to check | +| **G7:** NX parametric rebuild reliability | โ“ **OPEN** | Need corner-case testing across DV range | + +### NEW Gaps Identified + +| Gap | Description | Priority | +|-----|-------------|----------| +| **G10:** Baseline displacement re-verification | Was 22 mm at 974 kg mass. With true mass of 11.33 kg, displacement may be different. Need fresh baseline run. | **High** | +| **G11:** Baseline stress value | Never measured. Need SOL 101 baseline run to establish. | **High** | +| **G12:** Expression `beam_half_height` starting value | Known to exist but value not captured from screenshot | Medium | +| **G13:** Expression `beam_half_width` starting value | Known to exist but value not captured from screenshot | Medium | +| **G14:** Hole diameter expression name | Antoine mentioned "whole diameters" starts at 300 but didn't state the expression name explicitly | Medium | +| **G15:** `p6` (hole_span) as design variable | Antoine suggested it could be optimized. Need to decide if it enters the DV set. | Medium | + +--- + +## KB Entries Updated + +- `components/sandwich-beam.md` โ€” confirmed geometry, expressions, mass, hole parameters +- `materials/steel-aisi.md` โ€” AISI 1005 specifics, density, future materials +- `fea/models/sol101-static.md` โ€” confirmed BCs, mesh, element type, solver setup +- `kb/_index.md` โ€” gap status updates, generation table +- `kb/_history.md` โ€” Gen 002 entry +- `CONTEXT.md` โ€” confirmed parameter values, corrected mass expression + +## Decisions Needed + +1. **Re-run baseline?** โ€” Mass discrepancy suggests model has changed since intake. A fresh baseline solve would confirm displacement and stress. +2. **Add `p6` (hole_span) as DV?** โ€” Antoine suggested it. Would increase DV count from 4 to 5. +3. **Update atomizer_spec_draft.json?** โ€” Mass extractor needs `p1` not `p173`. Baseline mass is 11.33 kg not 974 kg. +4. **Proceed with optimization?** โ€” Antoine said "please optimize" โ€” but we still have open gaps (G3, G4, G6, G7, G10, G11). + +--- + +*Technical Lead ๐Ÿ”ง โ€” The physics is the boss.* diff --git a/projects/hydrotech-beam/kb/fea/models/sol101-static.md b/projects/hydrotech-beam/kb/fea/models/sol101-static.md index 281ed6f5..68911971 100644 --- a/projects/hydrotech-beam/kb/fea/models/sol101-static.md +++ b/projects/hydrotech-beam/kb/fea/models/sol101-static.md @@ -1,148 +1,148 @@ -# SOL 101 โ€” Static Analysis - -**Simulation:** Beam_sim1.sim -**Solver:** NX Nastran SOL 101 (Linear Static) -**Status:** โœ… Running โ€” first real results obtained 2026-02-11. Automated DOE pipeline operational. - ---- - -## Setup โ€” Confirmed - -| Item | Value | Source | Notes | -|------|-------|--------|-------| -| Solution type | **SOL 101** (Linear Static) | KBS session | "Solution 1 โ€” static subcase" | -| Element type | **CQUAD4** (4-node quad shell) | KBS session | โœ… Confirmed โ€” thin shell collectors | -| Property type | Thin shell | KBS session | Material inherited from "beam material" | -| Mesh density | Element size = **33.7 mm** (67.4 / 2) | KBS session | Subdivision-based. Future refinement planned. | -| Idealization | Promote body โ†’ mid-surface extraction | KBS session | Pair mid-surface function | - -## Boundary Conditions โ€” Confirmed - -| BC | Location | Type | Value | Source | -|----|----------|------|-------|--------| -| **Fixed constraint** | Left side of beam (full edge) | SPC (all 6 DOF) | Fixed | โœ… KBS session โ€” "left side fixed" | -| **Applied force** | Right side of beam (free end) | Point/edge force | **10,000 kgf downward** (โˆ’Y) | โœ… KBS session โ€” "project requirement" | - -### Loading Details -- Force magnitude: 10,000 kgf = **98,066.5 N** (โ‰ˆ 98.1 kN) -- Direction: Downward (โˆ’Y in model coordinates) -- Application: Right side (free end) of beam -- Type: This is a **cantilever beam** with end loading โ€” classic bending problem - -## Result Extraction โ€” Confirmed (Gen 003) - -| Output | Method | Expression/Sensor | Status | -|--------|--------|-------------------|--------| -| Mass | NX expression | **`p173`** (`body_property147.mass` in kg) | โœ… Confirmed โ€” journal extracts to `_temp_mass.txt` | -| Tip displacement | OP2 parse via pyNastran | Max Tz at free end | โœ… Working โ€” 17.93 mm at baseline-ish DVs | -| Von Mises stress | OP2 parse via pyNastran | CQUAD4 shell max VM | โœ… Working โ€” 111.9 MPa at baseline-ish DVs | - -> **Mass extraction:** Journal extracts `p173` expression after solve and writes `_temp_mass.txt`. Python reads this file. Expression `p1` from KBS session was incorrect โ€” `p173` confirmed via binary introspection. -> -> **pyNastran note:** Warns "nx version 2512 not supported" but reads OP2 files correctly. Stress output from pyNastran is in kPa โ€” divide by 1000 for MPa. - -## Mesh Details - -| Property | Value | Notes | -|----------|-------|-------| -| Element type | CQUAD4 | 4-node quadrilateral, first-order | -| Element size | 33.7 mm | 67.4 / 2 โ€” Antoine says refinement is "not for now" | -| Mesh method | Subdivision-based | Auto-mesh with size control | -| Shell formulation | Thin shell | Mid-surface extracted from solid | -| Convergence | โ“ **NOT VERIFIED** | Gap G8 partially closed (type known), but convergence check still needed | - -### Mesh Estimate -- Beam length 5,000 mm / 33.7 mm โ‰ˆ 148 elements along length -- Perimeter of I-beam cross-section โ‰ˆ varies โ€” but total mesh likely 10Kโ€“50K elements -- Expected DOF: 60Kโ€“300K โ†’ SOL 101 solve time: seconds to low minutes - -## Solver Considerations - -*From Technical Breakdown (Gen 001), updated with KBS data + Gen 003 run data:* - -- **Linear assumption:** With 1,133 kg beam under 98 kN load, deflections are ~18 mm at 5,000 mm span. L/ฮด โ‰ˆ 280 โ€” linear assumption is reasonable. -- **Mesh sensitivity:** Stress at hole edges is mesh-dependent. CQUAD4 at 33.7 mm may not fully resolve SCF at 300 mm diameter holes (~28 elements around circumference โ€” probably adequate but needs verification). -- **Mesh morphing vs remesh:** Parametric NX models typically remesh on update. Need to confirm behavior across DV range (Gap G7). -- **Runtime:** โœ… Confirmed **~12 seconds per evaluation** (single beam, CQUAD4 thin shell on dalidou). Very fast. -- **Unit system:** NX model uses kg-mm-s (kgf for force). Nastran output stress in kPa โ†’ divide by 1000 for MPa. - -## Validation Checklist - -- [x] Baseline mass matches NX expression `p173` (1,133.01 kg) -- [x] Displacement measured โ€” 17.93 mm at baseline-ish DVs (G10 closed) -- [x] Stress measured โ€” 111.9 MPa at baseline-ish DVs (G11 closed) -- [ ] Mesh convergence verified at baseline -- [ ] Mesh quality acceptable at DV range extremes -- [ ] Model rebuilds cleanly at all 4 corners of design space (Gap G7) -- [ ] Stress at hole edges resolved with current mesh density - -## NX Version & Environment โ€” Confirmed (Gen 003) - -| Item | Value | Notes | -|------|-------|-------| -| **NX Version** | **DesigncenterNX 2512** | Siemens rebranded NX to "DesigncenterNX" | -| **Install path** | `C:\Program Files\Siemens\DesigncenterNX2512` | On dalidou (Windows solver node) | -| **Previous config** | NX 2412 | โŒ Failed โ€” "Part file is from a newer version" | -| **pyNastran compat** | Warns "nx version 2512 not supported" | โœ… But reads OP2 files correctly | - -> โš ๏ธ **Critical lesson (2026-02-11):** Solver was originally configured for NX 2412 but model files are from DesigncenterNX 2512. NX refuses to load with "Part file is from a newer version." Must match version exactly. - -### Path Resolution on Windows โ€” Critical - -**Bug discovered:** `Path.absolute()` on Windows does **NOT** resolve `..` components (unlike `Path.resolve()`). - -```python -# WRONG โ€” leaves ".." in path, NX can't find referenced parts -path = Path("../../models/Beam_sim1.sim").absolute() -# โ†’ C:\Users\antoi\Atomizer\projects\hydrotech-beam\studies\01_doe_landscape\..\..\models\Beam_sim1.sim - -# CORRECT โ€” fully resolves path -path = Path("../../models/Beam_sim1.sim").resolve() -# โ†’ C:\Users\antoi\Atomizer\projects\hydrotech-beam\models\Beam_sim1.sim -``` - -**Rule:** Use `.resolve()` everywhere when constructing paths for NX. NX cannot follow `..` references in paths. - -### NX File References โ€” In-Place Solving Required - -NX `.sim` files store **absolute internal references** to `.fem` and `.prt` files. Copying model files to iteration folders breaks these references (`Parts.Open` returns `None`). - -**Solution:** Solve on master model **in-place** (in the `models/` directory) with backup/restore for isolation: -1. Backup master model files before each trial -2. Write expressions, rebuild, solve in `models/` -3. Archive outputs (OP2, F06, params, results) to iteration folder -4. Restore master from backup - -See DEC-HB-008 in DECISIONS.md. - -## History - -- **Gen 001** (2026-02-09): Initial documentation from technical breakdown. All solver details pending gap resolution. -- **Gen 002** (2026-02-10): Confirmed from KBS session โ€” CQUAD4 thin shell, 33.7 mm element size, cantilever BCs (left fixed, right 10,000 kgf down), mass via `p173`. Material: AISI 1005. -- **Gen 003** (2026-02-11): First real results! DesigncenterNX 2512 version confirmed, path resolution bugs fixed, backup/restore in-place solving architecture, mass extraction via journal. Displacement=17.93mm, Stress=111.9MPa, Solve time ~12s/trial. - -## NX Automation Workflow - -**This model uses the SIMPLE workflow** (single-part, no assembly FEM). - -### Simple Workflow Chain -``` -Beam.prt (geometry) โ†’ Beam_fem1_i.prt (idealized/mid-surface) โ†’ Beam_fem1.fem (mesh) โ†’ Beam_sim1.sim (solve) -``` - -Steps: -1. Open `.sim` file (loads chain) -2. Switch to `Beam.prt` โ€” import `.exp` file, update expressions, rebuild geometry -3. Switch to `Beam_fem1.fem` โ€” update FE model (remesh) -4. Switch back to `.sim` โ€” solve SOL 101 - -### Assembly FEM Workflow (NOT used here) -For multi-part models with `.afm` files (e.g., SAT3 mirror): -- Additional steps: load all components, update each FEM, merge duplicate nodes, resolve label conflicts -- Detected automatically by presence of `.afm` files in working directory - -### Key Automation Notes -- `hole_count` expression unit = `Constant` (not MilliMeter) -- All length DVs = `MilliMeter` -- FEM part is `Beam_fem1` โ€” NOT `Beam_fem1_i` (idealized) -- Journal: `solve_simulation.py` handles both workflows +# SOL 101 โ€” Static Analysis + +**Simulation:** Beam_sim1.sim +**Solver:** NX Nastran SOL 101 (Linear Static) +**Status:** โœ… Running โ€” first real results obtained 2026-02-11. Automated DOE pipeline operational. + +--- + +## Setup โ€” Confirmed + +| Item | Value | Source | Notes | +|------|-------|--------|-------| +| Solution type | **SOL 101** (Linear Static) | KBS session | "Solution 1 โ€” static subcase" | +| Element type | **CQUAD4** (4-node quad shell) | KBS session | โœ… Confirmed โ€” thin shell collectors | +| Property type | Thin shell | KBS session | Material inherited from "beam material" | +| Mesh density | Element size = **33.7 mm** (67.4 / 2) | KBS session | Subdivision-based. Future refinement planned. | +| Idealization | Promote body โ†’ mid-surface extraction | KBS session | Pair mid-surface function | + +## Boundary Conditions โ€” Confirmed + +| BC | Location | Type | Value | Source | +|----|----------|------|-------|--------| +| **Fixed constraint** | Left side of beam (full edge) | SPC (all 6 DOF) | Fixed | โœ… KBS session โ€” "left side fixed" | +| **Applied force** | Right side of beam (free end) | Point/edge force | **10,000 kgf downward** (โˆ’Y) | โœ… KBS session โ€” "project requirement" | + +### Loading Details +- Force magnitude: 10,000 kgf = **98,066.5 N** (โ‰ˆ 98.1 kN) +- Direction: Downward (โˆ’Y in model coordinates) +- Application: Right side (free end) of beam +- Type: This is a **cantilever beam** with end loading โ€” classic bending problem + +## Result Extraction โ€” Confirmed (Gen 003) + +| Output | Method | Expression/Sensor | Status | +|--------|--------|-------------------|--------| +| Mass | NX expression | **`p173`** (`body_property147.mass` in kg) | โœ… Confirmed โ€” journal extracts to `_temp_mass.txt` | +| Tip displacement | OP2 parse via pyNastran | Max Tz at free end | โœ… Working โ€” 17.93 mm at baseline-ish DVs | +| Von Mises stress | OP2 parse via pyNastran | CQUAD4 shell max VM | โœ… Working โ€” 111.9 MPa at baseline-ish DVs | + +> **Mass extraction:** Journal extracts `p173` expression after solve and writes `_temp_mass.txt`. Python reads this file. Expression `p1` from KBS session was incorrect โ€” `p173` confirmed via binary introspection. +> +> **pyNastran note:** Warns "nx version 2512 not supported" but reads OP2 files correctly. Stress output from pyNastran is in kPa โ€” divide by 1000 for MPa. + +## Mesh Details + +| Property | Value | Notes | +|----------|-------|-------| +| Element type | CQUAD4 | 4-node quadrilateral, first-order | +| Element size | 33.7 mm | 67.4 / 2 โ€” Antoine says refinement is "not for now" | +| Mesh method | Subdivision-based | Auto-mesh with size control | +| Shell formulation | Thin shell | Mid-surface extracted from solid | +| Convergence | โ“ **NOT VERIFIED** | Gap G8 partially closed (type known), but convergence check still needed | + +### Mesh Estimate +- Beam length 5,000 mm / 33.7 mm โ‰ˆ 148 elements along length +- Perimeter of I-beam cross-section โ‰ˆ varies โ€” but total mesh likely 10Kโ€“50K elements +- Expected DOF: 60Kโ€“300K โ†’ SOL 101 solve time: seconds to low minutes + +## Solver Considerations + +*From Technical Breakdown (Gen 001), updated with KBS data + Gen 003 run data:* + +- **Linear assumption:** With 1,133 kg beam under 98 kN load, deflections are ~18 mm at 5,000 mm span. L/ฮด โ‰ˆ 280 โ€” linear assumption is reasonable. +- **Mesh sensitivity:** Stress at hole edges is mesh-dependent. CQUAD4 at 33.7 mm may not fully resolve SCF at 300 mm diameter holes (~28 elements around circumference โ€” probably adequate but needs verification). +- **Mesh morphing vs remesh:** Parametric NX models typically remesh on update. Need to confirm behavior across DV range (Gap G7). +- **Runtime:** โœ… Confirmed **~12 seconds per evaluation** (single beam, CQUAD4 thin shell on dalidou). Very fast. +- **Unit system:** NX model uses kg-mm-s (kgf for force). Nastran output stress in kPa โ†’ divide by 1000 for MPa. + +## Validation Checklist + +- [x] Baseline mass matches NX expression `p173` (1,133.01 kg) +- [x] Displacement measured โ€” 17.93 mm at baseline-ish DVs (G10 closed) +- [x] Stress measured โ€” 111.9 MPa at baseline-ish DVs (G11 closed) +- [ ] Mesh convergence verified at baseline +- [ ] Mesh quality acceptable at DV range extremes +- [ ] Model rebuilds cleanly at all 4 corners of design space (Gap G7) +- [ ] Stress at hole edges resolved with current mesh density + +## NX Version & Environment โ€” Confirmed (Gen 003) + +| Item | Value | Notes | +|------|-------|-------| +| **NX Version** | **DesigncenterNX 2512** | Siemens rebranded NX to "DesigncenterNX" | +| **Install path** | `C:\Program Files\Siemens\DesigncenterNX2512` | On dalidou (Windows solver node) | +| **Previous config** | NX 2412 | โŒ Failed โ€” "Part file is from a newer version" | +| **pyNastran compat** | Warns "nx version 2512 not supported" | โœ… But reads OP2 files correctly | + +> โš ๏ธ **Critical lesson (2026-02-11):** Solver was originally configured for NX 2412 but model files are from DesigncenterNX 2512. NX refuses to load with "Part file is from a newer version." Must match version exactly. + +### Path Resolution on Windows โ€” Critical + +**Bug discovered:** `Path.absolute()` on Windows does **NOT** resolve `..` components (unlike `Path.resolve()`). + +```python +# WRONG โ€” leaves ".." in path, NX can't find referenced parts +path = Path("../../models/Beam_sim1.sim").absolute() +# โ†’ C:\Users\antoi\Atomizer\projects\hydrotech-beam\studies\01_doe_landscape\..\..\models\Beam_sim1.sim + +# CORRECT โ€” fully resolves path +path = Path("../../models/Beam_sim1.sim").resolve() +# โ†’ C:\Users\antoi\Atomizer\projects\hydrotech-beam\models\Beam_sim1.sim +``` + +**Rule:** Use `.resolve()` everywhere when constructing paths for NX. NX cannot follow `..` references in paths. + +### NX File References โ€” In-Place Solving Required + +NX `.sim` files store **absolute internal references** to `.fem` and `.prt` files. Copying model files to iteration folders breaks these references (`Parts.Open` returns `None`). + +**Solution:** Solve on master model **in-place** (in the `models/` directory) with backup/restore for isolation: +1. Backup master model files before each trial +2. Write expressions, rebuild, solve in `models/` +3. Archive outputs (OP2, F06, params, results) to iteration folder +4. Restore master from backup + +See DEC-HB-008 in DECISIONS.md. + +## History + +- **Gen 001** (2026-02-09): Initial documentation from technical breakdown. All solver details pending gap resolution. +- **Gen 002** (2026-02-10): Confirmed from KBS session โ€” CQUAD4 thin shell, 33.7 mm element size, cantilever BCs (left fixed, right 10,000 kgf down), mass via `p173`. Material: AISI 1005. +- **Gen 003** (2026-02-11): First real results! DesigncenterNX 2512 version confirmed, path resolution bugs fixed, backup/restore in-place solving architecture, mass extraction via journal. Displacement=17.93mm, Stress=111.9MPa, Solve time ~12s/trial. + +## NX Automation Workflow + +**This model uses the SIMPLE workflow** (single-part, no assembly FEM). + +### Simple Workflow Chain +``` +Beam.prt (geometry) โ†’ Beam_fem1_i.prt (idealized/mid-surface) โ†’ Beam_fem1.fem (mesh) โ†’ Beam_sim1.sim (solve) +``` + +Steps: +1. Open `.sim` file (loads chain) +2. Switch to `Beam.prt` โ€” import `.exp` file, update expressions, rebuild geometry +3. Switch to `Beam_fem1.fem` โ€” update FE model (remesh) +4. Switch back to `.sim` โ€” solve SOL 101 + +### Assembly FEM Workflow (NOT used here) +For multi-part models with `.afm` files (e.g., SAT3 mirror): +- Additional steps: load all components, update each FEM, merge duplicate nodes, resolve label conflicts +- Detected automatically by presence of `.afm` files in working directory + +### Key Automation Notes +- `hole_count` expression unit = `Constant` (not MilliMeter) +- All length DVs = `MilliMeter` +- FEM part is `Beam_fem1` โ€” NOT `Beam_fem1_i` (idealized) +- Journal: `solve_simulation.py` handles both workflows diff --git a/projects/hydrotech-beam/kb/fea/models/sol101-static.sync-conflict-20260209-202842-VBCUD7Z.md b/projects/hydrotech-beam/kb/fea/models/sol101-static.sync-conflict-20260209-202842-VBCUD7Z.md index 002cb786..46eeed02 100644 --- a/projects/hydrotech-beam/kb/fea/models/sol101-static.sync-conflict-20260209-202842-VBCUD7Z.md +++ b/projects/hydrotech-beam/kb/fea/models/sol101-static.sync-conflict-20260209-202842-VBCUD7Z.md @@ -1,46 +1,46 @@ -# SOL 101 โ€” Static Analysis - -**Simulation:** Beam_sim1.sim -**Solver:** NX Nastran SOL 101 (Linear Static) -**Status:** Pending gap resolution - ---- - -## Setup - -| Item | Value | Notes | -|------|-------|-------| -| Solution type | SOL 101 (Linear Static) | Appropriate for this problem | -| Element type | โ“ TBD | Gap G8: CQUAD4/CQUAD8 (shell) or CTETRA/CHEXA (solid)? | -| Mesh density | โ“ TBD | Gap G8: convergence checked? | -| Loading | โ“ TBD | Gap G2: point load? distributed? self-weight? | -| BCs | โ“ TBD | Gap G1: cantilever? simply-supported? | - -## Result Extraction - -| Output | Method | Expression/Sensor | Status | -|--------|--------|-------------------|--------| -| Mass | NX expression | `p173` | โœ… Known | -| Tip displacement | โ“ Sensor or .f06 parse | TBD | Gap G3, G6 | -| Von Mises stress | โ“ Sensor or .f06 parse | TBD | Gap G4, G6 | - -## Solver Considerations - -*From Technical Breakdown:* - -- **Linear assumption:** 22 mm displacement on likely 2+ m beam โ†’ L/ฮด probably OK. Verify. -- **Mesh sensitivity:** Stress at hole edges is mesh-dependent. Need convergence check (Gap G8). -- **Mesh morphing vs remesh:** Parametric NX models typically remesh on update. Need to confirm behavior across DV range (Gap G7). -- **Runtime estimate:** Single beam, ~10Kโ€“100K DOF โ†’ probably seconds to low minutes per evaluation. - -## Validation Checklist - -- [ ] Baseline mass matches NX expression `p173` -- [ ] Baseline displacement matches reported ~22 mm -- [ ] Mesh convergence verified at baseline -- [ ] Mesh quality acceptable at DV range extremes -- [ ] Model rebuilds cleanly at all 4 corners of design space - -## History - -- **Gen 001** (2026-02-09): Initial documentation from technical breakdown. All solver details pending gap resolution. +# SOL 101 โ€” Static Analysis + +**Simulation:** Beam_sim1.sim +**Solver:** NX Nastran SOL 101 (Linear Static) +**Status:** Pending gap resolution + +--- + +## Setup + +| Item | Value | Notes | +|------|-------|-------| +| Solution type | SOL 101 (Linear Static) | Appropriate for this problem | +| Element type | โ“ TBD | Gap G8: CQUAD4/CQUAD8 (shell) or CTETRA/CHEXA (solid)? | +| Mesh density | โ“ TBD | Gap G8: convergence checked? | +| Loading | โ“ TBD | Gap G2: point load? distributed? self-weight? | +| BCs | โ“ TBD | Gap G1: cantilever? simply-supported? | + +## Result Extraction + +| Output | Method | Expression/Sensor | Status | +|--------|--------|-------------------|--------| +| Mass | NX expression | `p173` | โœ… Known | +| Tip displacement | โ“ Sensor or .f06 parse | TBD | Gap G3, G6 | +| Von Mises stress | โ“ Sensor or .f06 parse | TBD | Gap G4, G6 | + +## Solver Considerations + +*From Technical Breakdown:* + +- **Linear assumption:** 22 mm displacement on likely 2+ m beam โ†’ L/ฮด probably OK. Verify. +- **Mesh sensitivity:** Stress at hole edges is mesh-dependent. Need convergence check (Gap G8). +- **Mesh morphing vs remesh:** Parametric NX models typically remesh on update. Need to confirm behavior across DV range (Gap G7). +- **Runtime estimate:** Single beam, ~10Kโ€“100K DOF โ†’ probably seconds to low minutes per evaluation. + +## Validation Checklist + +- [ ] Baseline mass matches NX expression `p173` +- [ ] Baseline displacement matches reported ~22 mm +- [ ] Mesh convergence verified at baseline +- [ ] Mesh quality acceptable at DV range extremes +- [ ] Model rebuilds cleanly at all 4 corners of design space + +## History + +- **Gen 001** (2026-02-09): Initial documentation from technical breakdown. All solver details pending gap resolution. diff --git a/projects/hydrotech-beam/kb/fea/models/sol101-static.sync-conflict-20260214-191758-VBCUD7Z.md b/projects/hydrotech-beam/kb/fea/models/sol101-static.sync-conflict-20260214-191758-VBCUD7Z.md new file mode 100644 index 00000000..281ed6f5 --- /dev/null +++ b/projects/hydrotech-beam/kb/fea/models/sol101-static.sync-conflict-20260214-191758-VBCUD7Z.md @@ -0,0 +1,148 @@ +# SOL 101 โ€” Static Analysis + +**Simulation:** Beam_sim1.sim +**Solver:** NX Nastran SOL 101 (Linear Static) +**Status:** โœ… Running โ€” first real results obtained 2026-02-11. Automated DOE pipeline operational. + +--- + +## Setup โ€” Confirmed + +| Item | Value | Source | Notes | +|------|-------|--------|-------| +| Solution type | **SOL 101** (Linear Static) | KBS session | "Solution 1 โ€” static subcase" | +| Element type | **CQUAD4** (4-node quad shell) | KBS session | โœ… Confirmed โ€” thin shell collectors | +| Property type | Thin shell | KBS session | Material inherited from "beam material" | +| Mesh density | Element size = **33.7 mm** (67.4 / 2) | KBS session | Subdivision-based. Future refinement planned. | +| Idealization | Promote body โ†’ mid-surface extraction | KBS session | Pair mid-surface function | + +## Boundary Conditions โ€” Confirmed + +| BC | Location | Type | Value | Source | +|----|----------|------|-------|--------| +| **Fixed constraint** | Left side of beam (full edge) | SPC (all 6 DOF) | Fixed | โœ… KBS session โ€” "left side fixed" | +| **Applied force** | Right side of beam (free end) | Point/edge force | **10,000 kgf downward** (โˆ’Y) | โœ… KBS session โ€” "project requirement" | + +### Loading Details +- Force magnitude: 10,000 kgf = **98,066.5 N** (โ‰ˆ 98.1 kN) +- Direction: Downward (โˆ’Y in model coordinates) +- Application: Right side (free end) of beam +- Type: This is a **cantilever beam** with end loading โ€” classic bending problem + +## Result Extraction โ€” Confirmed (Gen 003) + +| Output | Method | Expression/Sensor | Status | +|--------|--------|-------------------|--------| +| Mass | NX expression | **`p173`** (`body_property147.mass` in kg) | โœ… Confirmed โ€” journal extracts to `_temp_mass.txt` | +| Tip displacement | OP2 parse via pyNastran | Max Tz at free end | โœ… Working โ€” 17.93 mm at baseline-ish DVs | +| Von Mises stress | OP2 parse via pyNastran | CQUAD4 shell max VM | โœ… Working โ€” 111.9 MPa at baseline-ish DVs | + +> **Mass extraction:** Journal extracts `p173` expression after solve and writes `_temp_mass.txt`. Python reads this file. Expression `p1` from KBS session was incorrect โ€” `p173` confirmed via binary introspection. +> +> **pyNastran note:** Warns "nx version 2512 not supported" but reads OP2 files correctly. Stress output from pyNastran is in kPa โ€” divide by 1000 for MPa. + +## Mesh Details + +| Property | Value | Notes | +|----------|-------|-------| +| Element type | CQUAD4 | 4-node quadrilateral, first-order | +| Element size | 33.7 mm | 67.4 / 2 โ€” Antoine says refinement is "not for now" | +| Mesh method | Subdivision-based | Auto-mesh with size control | +| Shell formulation | Thin shell | Mid-surface extracted from solid | +| Convergence | โ“ **NOT VERIFIED** | Gap G8 partially closed (type known), but convergence check still needed | + +### Mesh Estimate +- Beam length 5,000 mm / 33.7 mm โ‰ˆ 148 elements along length +- Perimeter of I-beam cross-section โ‰ˆ varies โ€” but total mesh likely 10Kโ€“50K elements +- Expected DOF: 60Kโ€“300K โ†’ SOL 101 solve time: seconds to low minutes + +## Solver Considerations + +*From Technical Breakdown (Gen 001), updated with KBS data + Gen 003 run data:* + +- **Linear assumption:** With 1,133 kg beam under 98 kN load, deflections are ~18 mm at 5,000 mm span. L/ฮด โ‰ˆ 280 โ€” linear assumption is reasonable. +- **Mesh sensitivity:** Stress at hole edges is mesh-dependent. CQUAD4 at 33.7 mm may not fully resolve SCF at 300 mm diameter holes (~28 elements around circumference โ€” probably adequate but needs verification). +- **Mesh morphing vs remesh:** Parametric NX models typically remesh on update. Need to confirm behavior across DV range (Gap G7). +- **Runtime:** โœ… Confirmed **~12 seconds per evaluation** (single beam, CQUAD4 thin shell on dalidou). Very fast. +- **Unit system:** NX model uses kg-mm-s (kgf for force). Nastran output stress in kPa โ†’ divide by 1000 for MPa. + +## Validation Checklist + +- [x] Baseline mass matches NX expression `p173` (1,133.01 kg) +- [x] Displacement measured โ€” 17.93 mm at baseline-ish DVs (G10 closed) +- [x] Stress measured โ€” 111.9 MPa at baseline-ish DVs (G11 closed) +- [ ] Mesh convergence verified at baseline +- [ ] Mesh quality acceptable at DV range extremes +- [ ] Model rebuilds cleanly at all 4 corners of design space (Gap G7) +- [ ] Stress at hole edges resolved with current mesh density + +## NX Version & Environment โ€” Confirmed (Gen 003) + +| Item | Value | Notes | +|------|-------|-------| +| **NX Version** | **DesigncenterNX 2512** | Siemens rebranded NX to "DesigncenterNX" | +| **Install path** | `C:\Program Files\Siemens\DesigncenterNX2512` | On dalidou (Windows solver node) | +| **Previous config** | NX 2412 | โŒ Failed โ€” "Part file is from a newer version" | +| **pyNastran compat** | Warns "nx version 2512 not supported" | โœ… But reads OP2 files correctly | + +> โš ๏ธ **Critical lesson (2026-02-11):** Solver was originally configured for NX 2412 but model files are from DesigncenterNX 2512. NX refuses to load with "Part file is from a newer version." Must match version exactly. + +### Path Resolution on Windows โ€” Critical + +**Bug discovered:** `Path.absolute()` on Windows does **NOT** resolve `..` components (unlike `Path.resolve()`). + +```python +# WRONG โ€” leaves ".." in path, NX can't find referenced parts +path = Path("../../models/Beam_sim1.sim").absolute() +# โ†’ C:\Users\antoi\Atomizer\projects\hydrotech-beam\studies\01_doe_landscape\..\..\models\Beam_sim1.sim + +# CORRECT โ€” fully resolves path +path = Path("../../models/Beam_sim1.sim").resolve() +# โ†’ C:\Users\antoi\Atomizer\projects\hydrotech-beam\models\Beam_sim1.sim +``` + +**Rule:** Use `.resolve()` everywhere when constructing paths for NX. NX cannot follow `..` references in paths. + +### NX File References โ€” In-Place Solving Required + +NX `.sim` files store **absolute internal references** to `.fem` and `.prt` files. Copying model files to iteration folders breaks these references (`Parts.Open` returns `None`). + +**Solution:** Solve on master model **in-place** (in the `models/` directory) with backup/restore for isolation: +1. Backup master model files before each trial +2. Write expressions, rebuild, solve in `models/` +3. Archive outputs (OP2, F06, params, results) to iteration folder +4. Restore master from backup + +See DEC-HB-008 in DECISIONS.md. + +## History + +- **Gen 001** (2026-02-09): Initial documentation from technical breakdown. All solver details pending gap resolution. +- **Gen 002** (2026-02-10): Confirmed from KBS session โ€” CQUAD4 thin shell, 33.7 mm element size, cantilever BCs (left fixed, right 10,000 kgf down), mass via `p173`. Material: AISI 1005. +- **Gen 003** (2026-02-11): First real results! DesigncenterNX 2512 version confirmed, path resolution bugs fixed, backup/restore in-place solving architecture, mass extraction via journal. Displacement=17.93mm, Stress=111.9MPa, Solve time ~12s/trial. + +## NX Automation Workflow + +**This model uses the SIMPLE workflow** (single-part, no assembly FEM). + +### Simple Workflow Chain +``` +Beam.prt (geometry) โ†’ Beam_fem1_i.prt (idealized/mid-surface) โ†’ Beam_fem1.fem (mesh) โ†’ Beam_sim1.sim (solve) +``` + +Steps: +1. Open `.sim` file (loads chain) +2. Switch to `Beam.prt` โ€” import `.exp` file, update expressions, rebuild geometry +3. Switch to `Beam_fem1.fem` โ€” update FE model (remesh) +4. Switch back to `.sim` โ€” solve SOL 101 + +### Assembly FEM Workflow (NOT used here) +For multi-part models with `.afm` files (e.g., SAT3 mirror): +- Additional steps: load all components, update each FEM, merge duplicate nodes, resolve label conflicts +- Detected automatically by presence of `.afm` files in working directory + +### Key Automation Notes +- `hole_count` expression unit = `Constant` (not MilliMeter) +- All length DVs = `MilliMeter` +- FEM part is `Beam_fem1` โ€” NOT `Beam_fem1_i` (idealized) +- Journal: `solve_simulation.py` handles both workflows diff --git a/projects/hydrotech-beam/kb/materials/steel-aisi.md b/projects/hydrotech-beam/kb/materials/steel-aisi.md index 8ab6ef30..9da5b123 100644 --- a/projects/hydrotech-beam/kb/materials/steel-aisi.md +++ b/projects/hydrotech-beam/kb/materials/steel-aisi.md @@ -1,59 +1,59 @@ -# Steel โ€” AISI 1005 - -**Status:** Baseline material confirmed (Gen 002) -**NX Material Name:** "beam material" (inherited by thin shell property) - ---- - -## Properties - -| Property | Value | Units | Source | Confidence | -|----------|-------|-------|--------|------------| -| Standard | AISI / SAE | โ€” | KBS session | โœ… Confirmed | -| Grade | **1005** | โ€” | KBS session ("NSE steel 10 or 5" = ANSI Steel 1005) | โœ… Confirmed | -| Density | **7.3** | g/cmยณ (7,300 kg/mยณ) | KBS session โ€” Antoine stated directly | โœ… Confirmed | -| E (Young's modulus) | ~200 | GPa | Typical for AISI 1005 โ€” **needs NX verification** | ๐ŸŸก Estimated | -| Yield strength | ~285 | MPa | Typical for AISI 1005 (cold-drawn) โ€” **needs NX verification** | ๐ŸŸก Estimated | -| UTS | ~330 | MPa | Typical for AISI 1005 โ€” **needs NX verification** | ๐ŸŸก Estimated | -| Poisson's ratio | ~0.29 | โ€” | Typical for carbon steel โ€” **needs NX verification** | ๐ŸŸก Estimated | -| Elongation | ~20 | % | Typical for AISI 1005 โ€” **needs verification** | ๐ŸŸก Estimated | - -## Notes on AISI 1005 - -- AISI 1005 is a **low-carbon plain steel** (0.06% max C) in the 10xx series -- Commonly used for structural applications โ€” good weldability, moderate strength -- NX material library designation may vary (Antoine said "NSE steel 10 or 5" which maps to ANSI/AISI Steel 1005) -- **Density note:** Antoine stated 7.3 g/cmยณ. Standard steel is 7.85 g/cmยณ. The 7.3 value may be the NX library default or a specific setting. This affects mass calculation directly. - -## Stress Allowable - -| Parameter | Value | Basis | Status | -|-----------|-------|-------|--------| -| Stress constraint | 130 MPa | From intake โ€” Gap G9 | ๐ŸŸก Not yet confirmed for Gen 002 | -| Implied safety factor | ~2.2 | vs. 285 MPa yield (estimated) | Conservative | - -> โš ๏ธ Need to confirm whether the 130 MPa stress limit from Gen 001 intake still applies given the mass discrepancy resolution. The 130 MPa may have been set for the ~974 kg model. - -## Future Material Expansion - -Antoine explicitly requested future material support (KBS session 20260210-163801): - -| Material | Standard | Priority | Notes | -|----------|----------|----------|-------| -| **Aluminum 6061** | โ€” | Future | Antoine: "change this type of material for aluminum, 6061" | -| **Stainless Steel ANSI 310** | ANSI 310 | Future | Antoine: "stainless steel. ANSI, let's say 310" | - -> Antoine's instruction: "Don't start with [these]. Just add this as a future expansion for the optimization process." - -### Material Expansion Implications - -- Multi-material optimization would change the problem fundamentally: - - Different E, ฯ, ฯƒ_y for each material โ†’ different optimal geometries - - Could be handled as a categorical DV or separate optimization runs per material - - Aluminum 6061: E โ‰ˆ 69 GPa, ฯ โ‰ˆ 2.7 g/cmยณ, ฯƒ_y โ‰ˆ 276 MPa โ€” lighter but much less stiff - - SS 310: E โ‰ˆ 200 GPa, ฯ โ‰ˆ 8.0 g/cmยณ, ฯƒ_y โ‰ˆ 205 MPa โ€” heavier, similar stiffness, lower yield - -## History - -- **Gen 001** (2026-02-09): Placeholder โ€” only knew "Steel (AISI)" -- **Gen 002** (2026-02-10): Confirmed AISI 1005 from KBS session. Density confirmed 7.3 g/cmยณ. Future materials flagged (Al 6061, SS 310). Typical mechanical properties added (pending NX verification). +# Steel โ€” AISI 1005 + +**Status:** Baseline material confirmed (Gen 002) +**NX Material Name:** "beam material" (inherited by thin shell property) + +--- + +## Properties + +| Property | Value | Units | Source | Confidence | +|----------|-------|-------|--------|------------| +| Standard | AISI / SAE | โ€” | KBS session | โœ… Confirmed | +| Grade | **1005** | โ€” | KBS session ("NSE steel 10 or 5" = ANSI Steel 1005) | โœ… Confirmed | +| Density | **7.3** | g/cmยณ (7,300 kg/mยณ) | KBS session โ€” Antoine stated directly | โœ… Confirmed | +| E (Young's modulus) | ~200 | GPa | Typical for AISI 1005 โ€” **needs NX verification** | ๐ŸŸก Estimated | +| Yield strength | ~285 | MPa | Typical for AISI 1005 (cold-drawn) โ€” **needs NX verification** | ๐ŸŸก Estimated | +| UTS | ~330 | MPa | Typical for AISI 1005 โ€” **needs NX verification** | ๐ŸŸก Estimated | +| Poisson's ratio | ~0.29 | โ€” | Typical for carbon steel โ€” **needs NX verification** | ๐ŸŸก Estimated | +| Elongation | ~20 | % | Typical for AISI 1005 โ€” **needs verification** | ๐ŸŸก Estimated | + +## Notes on AISI 1005 + +- AISI 1005 is a **low-carbon plain steel** (0.06% max C) in the 10xx series +- Commonly used for structural applications โ€” good weldability, moderate strength +- NX material library designation may vary (Antoine said "NSE steel 10 or 5" which maps to ANSI/AISI Steel 1005) +- **Density note:** Antoine stated 7.3 g/cmยณ. Standard steel is 7.85 g/cmยณ. The 7.3 value may be the NX library default or a specific setting. This affects mass calculation directly. + +## Stress Allowable + +| Parameter | Value | Basis | Status | +|-----------|-------|-------|--------| +| Stress constraint | 130 MPa | From intake โ€” Gap G9 | ๐ŸŸก Not yet confirmed for Gen 002 | +| Implied safety factor | ~2.2 | vs. 285 MPa yield (estimated) | Conservative | + +> โš ๏ธ Need to confirm whether the 130 MPa stress limit from Gen 001 intake still applies given the mass discrepancy resolution. The 130 MPa may have been set for the ~974 kg model. + +## Future Material Expansion + +Antoine explicitly requested future material support (KBS session 20260210-163801): + +| Material | Standard | Priority | Notes | +|----------|----------|----------|-------| +| **Aluminum 6061** | โ€” | Future | Antoine: "change this type of material for aluminum, 6061" | +| **Stainless Steel ANSI 310** | ANSI 310 | Future | Antoine: "stainless steel. ANSI, let's say 310" | + +> Antoine's instruction: "Don't start with [these]. Just add this as a future expansion for the optimization process." + +### Material Expansion Implications + +- Multi-material optimization would change the problem fundamentally: + - Different E, ฯ, ฯƒ_y for each material โ†’ different optimal geometries + - Could be handled as a categorical DV or separate optimization runs per material + - Aluminum 6061: E โ‰ˆ 69 GPa, ฯ โ‰ˆ 2.7 g/cmยณ, ฯƒ_y โ‰ˆ 276 MPa โ€” lighter but much less stiff + - SS 310: E โ‰ˆ 200 GPa, ฯ โ‰ˆ 8.0 g/cmยณ, ฯƒ_y โ‰ˆ 205 MPa โ€” heavier, similar stiffness, lower yield + +## History + +- **Gen 001** (2026-02-09): Placeholder โ€” only knew "Steel (AISI)" +- **Gen 002** (2026-02-10): Confirmed AISI 1005 from KBS session. Density confirmed 7.3 g/cmยณ. Future materials flagged (Al 6061, SS 310). Typical mechanical properties added (pending NX verification). diff --git a/projects/hydrotech-beam/kb/materials/steel-aisi.sync-conflict-20260209-202842-VBCUD7Z.md b/projects/hydrotech-beam/kb/materials/steel-aisi.sync-conflict-20260209-202842-VBCUD7Z.md index 4d729d35..567ccdff 100644 --- a/projects/hydrotech-beam/kb/materials/steel-aisi.sync-conflict-20260209-202842-VBCUD7Z.md +++ b/projects/hydrotech-beam/kb/materials/steel-aisi.sync-conflict-20260209-202842-VBCUD7Z.md @@ -1,26 +1,26 @@ -# Steel (AISI) - -**Status:** Placeholder โ€” needs full material card details - ---- - -## Properties - -| Property | Value | Units | Source | -|----------|-------|-------|--------| -| Standard | AISI | โ€” | Intake | -| Grade | โ“ TBD | โ€” | Need from NX model | -| Yield strength | โ“ TBD | MPa | โ€” | -| E (Young's modulus) | โ“ TBD | GPa | โ€” | -| Density | โ“ TBD | kg/mยณ | โ€” | -| Poisson's ratio | โ“ TBD | โ€” | โ€” | - -## Notes - -- Stress allowable of 130 MPa was given as constraint โ€” need to confirm basis (Gap G9) -- If yield is ~250 MPa, then 130 MPa implies SF โ‰ˆ 1.9 โ€” conservative -- Material card should be extracted from NX model during introspection - -## History - -- **Gen 001** (2026-02-09): Placeholder from intake โ€” "Steel (AISI)" is all we have +# Steel (AISI) + +**Status:** Placeholder โ€” needs full material card details + +--- + +## Properties + +| Property | Value | Units | Source | +|----------|-------|-------|--------| +| Standard | AISI | โ€” | Intake | +| Grade | โ“ TBD | โ€” | Need from NX model | +| Yield strength | โ“ TBD | MPa | โ€” | +| E (Young's modulus) | โ“ TBD | GPa | โ€” | +| Density | โ“ TBD | kg/mยณ | โ€” | +| Poisson's ratio | โ“ TBD | โ€” | โ€” | + +## Notes + +- Stress allowable of 130 MPa was given as constraint โ€” need to confirm basis (Gap G9) +- If yield is ~250 MPa, then 130 MPa implies SF โ‰ˆ 1.9 โ€” conservative +- Material card should be extracted from NX model during introspection + +## History + +- **Gen 001** (2026-02-09): Placeholder from intake โ€” "Steel (AISI)" is all we have diff --git a/projects/hydrotech-beam/kb/materials/steel-aisi.sync-conflict-20260214-191804-VBCUD7Z.md b/projects/hydrotech-beam/kb/materials/steel-aisi.sync-conflict-20260214-191804-VBCUD7Z.md new file mode 100644 index 00000000..8ab6ef30 --- /dev/null +++ b/projects/hydrotech-beam/kb/materials/steel-aisi.sync-conflict-20260214-191804-VBCUD7Z.md @@ -0,0 +1,59 @@ +# Steel โ€” AISI 1005 + +**Status:** Baseline material confirmed (Gen 002) +**NX Material Name:** "beam material" (inherited by thin shell property) + +--- + +## Properties + +| Property | Value | Units | Source | Confidence | +|----------|-------|-------|--------|------------| +| Standard | AISI / SAE | โ€” | KBS session | โœ… Confirmed | +| Grade | **1005** | โ€” | KBS session ("NSE steel 10 or 5" = ANSI Steel 1005) | โœ… Confirmed | +| Density | **7.3** | g/cmยณ (7,300 kg/mยณ) | KBS session โ€” Antoine stated directly | โœ… Confirmed | +| E (Young's modulus) | ~200 | GPa | Typical for AISI 1005 โ€” **needs NX verification** | ๐ŸŸก Estimated | +| Yield strength | ~285 | MPa | Typical for AISI 1005 (cold-drawn) โ€” **needs NX verification** | ๐ŸŸก Estimated | +| UTS | ~330 | MPa | Typical for AISI 1005 โ€” **needs NX verification** | ๐ŸŸก Estimated | +| Poisson's ratio | ~0.29 | โ€” | Typical for carbon steel โ€” **needs NX verification** | ๐ŸŸก Estimated | +| Elongation | ~20 | % | Typical for AISI 1005 โ€” **needs verification** | ๐ŸŸก Estimated | + +## Notes on AISI 1005 + +- AISI 1005 is a **low-carbon plain steel** (0.06% max C) in the 10xx series +- Commonly used for structural applications โ€” good weldability, moderate strength +- NX material library designation may vary (Antoine said "NSE steel 10 or 5" which maps to ANSI/AISI Steel 1005) +- **Density note:** Antoine stated 7.3 g/cmยณ. Standard steel is 7.85 g/cmยณ. The 7.3 value may be the NX library default or a specific setting. This affects mass calculation directly. + +## Stress Allowable + +| Parameter | Value | Basis | Status | +|-----------|-------|-------|--------| +| Stress constraint | 130 MPa | From intake โ€” Gap G9 | ๐ŸŸก Not yet confirmed for Gen 002 | +| Implied safety factor | ~2.2 | vs. 285 MPa yield (estimated) | Conservative | + +> โš ๏ธ Need to confirm whether the 130 MPa stress limit from Gen 001 intake still applies given the mass discrepancy resolution. The 130 MPa may have been set for the ~974 kg model. + +## Future Material Expansion + +Antoine explicitly requested future material support (KBS session 20260210-163801): + +| Material | Standard | Priority | Notes | +|----------|----------|----------|-------| +| **Aluminum 6061** | โ€” | Future | Antoine: "change this type of material for aluminum, 6061" | +| **Stainless Steel ANSI 310** | ANSI 310 | Future | Antoine: "stainless steel. ANSI, let's say 310" | + +> Antoine's instruction: "Don't start with [these]. Just add this as a future expansion for the optimization process." + +### Material Expansion Implications + +- Multi-material optimization would change the problem fundamentally: + - Different E, ฯ, ฯƒ_y for each material โ†’ different optimal geometries + - Could be handled as a categorical DV or separate optimization runs per material + - Aluminum 6061: E โ‰ˆ 69 GPa, ฯ โ‰ˆ 2.7 g/cmยณ, ฯƒ_y โ‰ˆ 276 MPa โ€” lighter but much less stiff + - SS 310: E โ‰ˆ 200 GPa, ฯ โ‰ˆ 8.0 g/cmยณ, ฯƒ_y โ‰ˆ 205 MPa โ€” heavier, similar stiffness, lower yield + +## History + +- **Gen 001** (2026-02-09): Placeholder โ€” only knew "Steel (AISI)" +- **Gen 002** (2026-02-10): Confirmed AISI 1005 from KBS session. Density confirmed 7.3 g/cmยณ. Future materials flagged (Al 6061, SS 310). Typical mechanical properties added (pending NX verification). diff --git a/projects/hydrotech-beam/models/Beam.prt b/projects/hydrotech-beam/models/Beam.prt index 37152686cee4a05441185491fad404dc06570fb9..c026cf13cbfaf1aa30ec38f933e6f56d2b081514 100644 GIT binary patch delta 28942 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

Wj89o7n1_AXa*w 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 R{7tAs!MpCi9824-`Cnw8(bE6` delta 19023 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 delta 32226 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 diff --git a/projects/hydrotech-beam/models/Beam_sim1.sim b/projects/hydrotech-beam/models/Beam_sim1.sim index 2a498f1a820c1d36a5425a078d7b5a7272ee8918..3eefcbd177eef21038c1bffaeb896b9ec6cc18a9 100644 GIT binary patch delta 2322 zcmaLZeNdD|90%}y?ttTtqvPQ25RNzDfD;h(B!q)R5Ku&3ghLS!U-p@)92`XdprH|A z(5#RKeUfc*N(VKaY$Bz)Qc?yREz%q_$7#rk(#A<@d_bfK?di*ba9(52%;%nG_xU}) z-Dhua_gW9^mA8OzNzsb@k`i07N97{oI5JQAavXO-&~b4}F?F)8TFB+Xl#e5B3tPEZ zxOqf)j|=5)3l}&OS1$~5G2D<~kVSBt1gk7sp)a~*CZxD)?-dOv)X7D+Z*5oA zLappK!RBt18%NvN`Ulk1)$Sgs^JyD34r~rTcci0cEXc2;=I^eafU3Y4^TUrXN!6PZ z?(RGJzJz14M~c&h`5cF96-4_H@;H@HofyV-bhRc@H*d9q5G%X@?Ol(OyE(W1-1>s9 zv({)X_)dM5tiJZuc>TriTC&S*#o0L+h3t%cITCsavb3F^ZKM7JFXBUZBP~lcu&u74 zTpK6gR?)2GzYGc`WZ9MQln;l`qv>6dIar|&kWb07mb zxPd!(fC4;0X*Z{<#-CTLN#V#yjf}+{Q*GcL*!LV$m3jVWgm#Z~s=y1>;0@1!27EvZ zzTgKs(1QW|VHN~HAQ&MCf*}M#Aq-|iI7EO6A|VQ*!3=XC24W!&=0ZFqz&w}_7FYm@ z@GK<3LP&-butF-N!E>+((qS=VKqf4KEXal&SPIJ^7nVaFtblwdfI=vOl~4>NV1rUv z1*@S9)<8K_Kqah&Dp&{WVFPTmk95)`FI8=+k57TS?C@H>J@E|9`r#hP{Rx=`b0HH} zLp3x(JDh+XxDI#i!FOnjT!AlZVu`VTbeC#4tP=kA-a)!cu2Q>kQe#GAp#)V6w(j|`FyhlSqoN|RqqS`ekK%^l# zPBB4}sBwuQMoIK>Z7)y~wJvpRyGSh|PW?1UqTV&;GGbISF-$o`%bmw$^AHWu&)g?o zk{IU5p<;)IXsUC1g(R9LZ%q#k8^hzy>1Ge3Jd=*)4pWQ4>2(|3Br$F(|K4FbhaDZJ z8bg9hugOH%Ja%Z9>bUui6Z5=Jn`D(gu;%-;!MCboVo8u6Ub}PhTzQlEjnHUq+En$5 zsq=A`H$vlmtM*Kl`=-uX)-pmHnDzk;R+lAR+ftffTlLPo{Hx6RfJRKL0(R_;Z)zOt zg|09>#s1KZPYDg(_=+z1mn7RiNx{L%lH|H|r7)X`kK>fA$;2OdVw++gM)DQYn;O$b z@!6^Y^Z5NNsdd?YZ?-OqcWi5HEQ)V-cdR(9DVjg(Xdc_3k%_T_A>j- z%x|1d=dwq#m9ac~>XZN9k>#=cyH9)6ULVI71*j5j7K_!AXtgY4-)HmTPUZF-{&lCK c>uq~a4*$7_t}b`-zsolhn>~)Dwk*GY0ZEl+^Z)<= delta 2278 zcmaLZdrVVT90%}oT1sgtqm)v4*;XkJ0TGZ16qF$rbpu2mipoR0*DP)tr!$#4L7dMh zE;H9~%%+P`r`zHpTXEe28K8>~T(%iDGj%5XfrFtN7eog>cK2-&>AJX^eA?b~e&^S7 z?>#-g?)%_?d_U-l3-j`ei%W{UD`yecG3Rp~rSzhpr}3(RjM26=!hC90jYZrR*3&pu zj)ix19v4nh6a7uNKrM8OaEHdydxAk0L01b_S+r7DxFn2_BKv_K{HgGkT=Y6}wfeHK zPWHP{q6m}sHP*9=`<%tzpa|7#>l^#JHnoM-99uga9(Zi+h`lYOGBkFt{jA;Gzotp$ z_(CJ$7@d)_c|ks<_^gKLKtje+39D1g^tgS03g_kLrz9i;Rspj=No%3Xu9ZEyLK-f# zTSM*Ft-f^Pl?PP`=hF5xt@P`hYAY)#vgP7t*s}BG$g2@(<_@3w#-6TKcwwd~{2aB^ z{a0GAv~PNuOt_iJ z5cCiP2ABZB5CWkP2H`Lfj9>yYOoGV}0TzgaD2Rp_m;$j72k|f!5?~r6LJ}mybVz|0 zVFsi^8l;02G9VLX!Ys&wmmnKvLk`RV8|1=Vm$Sm~C%1-Ac-;B$GD|)09$s^vo2_() zcctEbd)RTYgWIo7`07M{^TC=YPl^`D+-SQx@W=S754q&F>LcA&Jv(`mGs{S@!lU-CE;E$Xv9(3D>|^OGpWf01d!T2&|9?<**KRK?5{GGql2a zxC=o#LLy)~l*3MtV?x!?0zX1G41-2bwi2_RkQA5)N!}x3|Aw+#*Sy)hLZEwD9rNht*p66aOwe?9)ho8L~YsRDMysO(lKu zNDzhSf&%AGy9Bm93Bc8OLu^IZLn9GLjz2&c^U7#iq34eL-+9c7<3D`fNXN!_zA!|cRFX8qnv!TuN@QjmAE_KW`P;_teO{;I Zn_T{DZ~dnEqyI;)I4d`f8@b;t`UfxSb}s+` diff --git a/projects/hydrotech-beam/models/README.md b/projects/hydrotech-beam/models/README.md index 7e99d3c5..c8e52f51 100644 --- a/projects/hydrotech-beam/models/README.md +++ b/projects/hydrotech-beam/models/README.md @@ -1,25 +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 +# 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 diff --git a/projects/hydrotech-beam/models/README.sync-conflict-20260214-191801-VBCUD7Z.md b/projects/hydrotech-beam/models/README.sync-conflict-20260214-191801-VBCUD7Z.md new file mode 100644 index 00000000..7e99d3c5 --- /dev/null +++ b/projects/hydrotech-beam/models/README.sync-conflict-20260214-191801-VBCUD7Z.md @@ -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 diff --git a/projects/hydrotech-beam/models/README.sync-conflict-20260214-191802-VBCUD7Z.md b/projects/hydrotech-beam/models/README.sync-conflict-20260214-191802-VBCUD7Z.md new file mode 100644 index 00000000..7e99d3c5 --- /dev/null +++ b/projects/hydrotech-beam/models/README.sync-conflict-20260214-191802-VBCUD7Z.md @@ -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 diff --git a/projects/hydrotech-beam/models/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/models/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/models/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/models/_temp_mass.txt b/projects/hydrotech-beam/models/_temp_mass.txt index 8ce62fe2..80cb5e66 100644 --- a/projects/hydrotech-beam/models/_temp_mass.txt +++ b/projects/hydrotech-beam/models/_temp_mass.txt @@ -1 +1 @@ -p173=1343.5034206648418 +1343.5034206648418 \ No newline at end of file diff --git a/projects/hydrotech-beam/models/_temp_part_properties.json b/projects/hydrotech-beam/models/_temp_part_properties.json new file mode 100644 index 00000000..e673b207 --- /dev/null +++ b/projects/hydrotech-beam/models/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1343.5034206648418, + "mass_g": 1343503.4206648418, + "volume_mm3": 170668625.59258664, + "surface_area_mm2": 9376402.579189494, + "center_of_gravity_mm": [ + 2499.9999999999995, + -3.0683416327531814e-13, + -7.359365939967888e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/playbooks/DOE.md b/projects/hydrotech-beam/playbooks/DOE.md new file mode 100644 index 00000000..a116e73d --- /dev/null +++ b/projects/hydrotech-beam/playbooks/DOE.md @@ -0,0 +1,39 @@ +# Playbook โ€” DOE (Phase 1 Landscape) + +## Scope +Design of Experiments for Hydrotech Beam: baseline + LHS samples to map design space. + +## Standard flow +1. Prepare clean state (archive previous results if needed) +2. Run DOE with explicit backend +3. Validate outputs +4. Evaluate gate criteria +5. Decide next phase (rerun bounds/constraints or move to TPE) + +## Commands +### Real engineering DOE +```powershell +python .\run_doe.py --backend nxopen --model-dir "" --clean --study-name hydrotech_beam_doe_phase1_real +``` + +### Debug DOE only (synthetic) +```powershell +python .\run_doe.py --backend stub --clean --study-name hydrotech_beam_doe_phase1_stub +``` + +## Interpretation checklist +- `geo_infeasible` high: geometry bounds/checks may be too broad or too strict +- `fully_feasible = 0`: constraints may be too strict for current design space +- Very low runtime and suspiciously smooth outputs: likely stub execution + +## Decision branch +- If gate passed: proceed to TPE seeded by feasible points +- If gate failed: + - adjust constraints and/or DV bounds + - rerun clean DOE + +## Documentation requirement +After each DOE run, update: +- run record +- decision and rationale +- next action owner diff --git a/projects/hydrotech-beam/playbooks/NX_REAL_RUN.md b/projects/hydrotech-beam/playbooks/NX_REAL_RUN.md new file mode 100644 index 00000000..e2fdb414 --- /dev/null +++ b/projects/hydrotech-beam/playbooks/NX_REAL_RUN.md @@ -0,0 +1,38 @@ +# Playbook โ€” NX Real Run (Hydrotech Beam) + +## Objective +Run a **real** DOE using NX/Simcenter (not stub), with clean artifacts and traceability. + +## Pre-Run Checklist +- [ ] In study folder: `...\studies\01_doe_landscape` +- [ ] Model directory contains expected `.sim`, `.fem`, `.prt` +- [ ] Syncthing healthy on both ends (no major errors) +- [ ] No unintended `*.sync-conflict-*` in active results root +- [ ] Command prepared with explicit `--backend nxopen` + +## Recommended command +```powershell +python .\run_doe.py --backend nxopen --model-dir "" --clean --study-name hydrotech_beam_doe_phase1_real +``` + +## During run โ€” sanity checks +- Runtime per solved trial should be non-trivial (not near-zero for all trials). +- Log should indicate NX solve flow, not stub messages. +- Iteration outputs should be created. + +## Post-run validation +1. Check `results/doe_summary.json` +2. Check `results/doe_results.csv` +3. Verify: + - trial count expected (typically 51 for baseline + 50 LHS) + - solved/failed counts make sense + - mass values are numeric for solved trials + - no obvious synthetic patterns indicating stub run + +## Quality gate reminder +Phase 1 gate (current): +- โ‰ฅ5 feasible points +- โ‰ฅ80% solve success + +## Mandatory logging +Record the run in project docs using the run log template from `USER_GUIDE.md`. diff --git a/projects/hydrotech-beam/playbooks/SYNCTHING_RECOVERY.md b/projects/hydrotech-beam/playbooks/SYNCTHING_RECOVERY.md new file mode 100644 index 00000000..560f5e77 --- /dev/null +++ b/projects/hydrotech-beam/playbooks/SYNCTHING_RECOVERY.md @@ -0,0 +1,38 @@ +# Playbook โ€” Syncthing Recovery (Hydrotech Beam) + +## Symptoms +- Latest run not visible on other side +- Old timestamps persist remotely +- `*.sync-conflict-*` files appear in `results/` + +## Recovery steps +1. Confirm both sides point to the same folder path +2. Rescan folder in Syncthing GUI +3. Pause/resume folder if stale +4. Restart Syncthing if needed +5. Archive conflicts from active results root + +## Conflict archive snippet (PowerShell) +```powershell +$ts = Get-Date -Format "yyyyMMdd_HHmmss" +$archive = ".\results\archive_conflicts_$ts" +New-Item -ItemType Directory -Path $archive -Force | Out-Null +Get-ChildItem .\results -File -Filter "*.sync-conflict-*" | Move-Item -Destination $archive +``` + +## Clean rerun prep snippet (PowerShell) +```powershell +$ts = Get-Date -Format "yyyyMMdd_HHmmss" +$old = ".\results\archive_before_clean_rerun_$ts" +New-Item -ItemType Directory -Path $old -Force | Out-Null + +$toArchive = @("doe_results.csv","doe_summary.json","doe_run.log","history.csv","history.db","optuna_study.db") +foreach ($f in $toArchive) { + if (Test-Path ".\results\$f") { Move-Item ".\results\$f" $old } +} +``` + +## Verification +- Main `results/` has only current active artifacts +- Conflict files moved to archive folder +- Remote side sees same timestamps/files diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/OPTIMIZATION_STRATEGY.md b/projects/hydrotech-beam/studies/01_doe_landscape/OPTIMIZATION_STRATEGY.md index 1447c5c1..3e92fedf 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/OPTIMIZATION_STRATEGY.md +++ b/projects/hydrotech-beam/studies/01_doe_landscape/OPTIMIZATION_STRATEGY.md @@ -1,514 +1,514 @@ -# Optimization Strategy โ€” Hydrotech Beam DoE & Landscape Mapping - -**Study:** `01_doe_landscape` -**Project:** Hydrotech Beam Structural Optimization -**Author:** โšก Optimizer Agent -**Date:** 2025-02-09 (updated 2026-02-10 โ€” introspection corrections) -**Status:** APPROVED WITH CONDITIONS โ€” Auditor review 2026-02-10, blockers resolved inline -**References:** [BREAKDOWN.md](../../BREAKDOWN.md), [DECISIONS.md](../../DECISIONS.md), [CONTEXT.md](../../CONTEXT.md) - ---- - -## 1. Problem Formulation - -### 1.1 Objective - -$$\min_{x} \quad f(x) = \text{mass}(x) \quad [\text{kg}]$$ - -Single-objective minimization of total beam mass. This aligns with DEC-HB-001 (approved by Tech Lead, pending CEO confirmation). - -### 1.2 Constraints - -| ID | Constraint | Operator | Limit | Units | Source | -|----|-----------|----------|-------|-------|--------| -| gโ‚ | Tip displacement | โ‰ค | 10.0 | mm | NX Nastran SOL 101 โ€” displacement sensor at beam tip | -| gโ‚‚ | Max von Mises stress | โ‰ค | 130.0 | MPa | NX Nastran SOL 101 โ€” max elemental nodal VM stress | - -Both are **hard constraints** โ€” no trade-off or relaxation without CEO approval. - -### 1.3 Design Variables - -| ID | NX Expression | Type | Lower | Upper | Baseline | Units | Notes | -|----|---------------|------|-------|-------|----------|-------|-------| -| DV1 | `beam_half_core_thickness` | Continuous | 10 | 40 | **25.162** | mm | Core half-thickness; stiffness scales ~quadratically via sandwich effect | -| DV2 | `beam_face_thickness` | Continuous | 10 | 40 | **21.504** | mm | Face sheet thickness; primary bending stiffness contributor | -| DV3 | `holes_diameter` | Continuous | 150 | 450 | 300 | mm | Lightening hole diameter; mass โˆ dยฒ reduction | -| DV4 | `hole_count` (โ†’ `Pattern_p7`) | **Integer** | 5 | 15 | 10 | โ€” | Number of lightening holes; 11 discrete levels | - -**Total design space:** 3 continuous ร— 1 integer (11 levels) = effectively 3D continuous ร— 11 slices. - -### 1.4 Integer Handling - -Per DEC-HB-003, `hole_count` is treated as a **true integer** throughout: - -- **Phase 1 (LHS):** Generate continuous LHS, round DV4 to nearest integer. Use stratified integer sampling to ensure coverage across all 11 levels. -- **Phase 2 (TPE):** Optuna `IntDistribution(5, 15)` โ€” native integer support, no rounding hacks. -- **NX rebuild:** The model requires integer hole count. Non-integer values will cause geometry failures. - -### 1.5 Baseline Assessment - -| Metric | Baseline Value | Constraint | Status | -|--------|---------------|------------|--------| -| Mass | **1,133.01 kg** | (minimize) | Overbuilt โ€” room to reduce | -| Tip displacement | ~22 mm (unverified โ€” awaiting baseline re-run) | โ‰ค 10 mm | โŒ **Likely FAILS** | -| VM stress | (unknown โ€” awaiting baseline re-run) | โ‰ค 130 MPa | โš ๏ธ Unconfirmed | - -> โš ๏ธ **Critical:** The baseline design likely **violates** the displacement constraint (~22 mm vs 10 mm limit). Baseline re-run pending โ€” CEO running SOL 101 in parallel. The optimizer must first find the feasible region before it can meaningfully minimize mass. This shapes the entire strategy. -> -> **Introspection note (2026-02-10):** Mass expression is `p173` (body_property147.mass, kg). DV baselines are NOT round numbers (face=21.504mm, core=25.162mm). NX expression `beam_lenght` has a typo (no 'h'). `hole_count` links to `Pattern_p7` in the NX pattern feature. - ---- - -## 2. Algorithm Selection - -### 2.1 Tech Lead's Recommendation - -DEC-HB-002 proposes a two-phase strategy: -- **Phase 1:** Latin Hypercube Sampling (LHS) โ€” 40โ€“50 trials -- **Phase 2:** TPE via Optuna โ€” 60โ€“100 trials - -### 2.2 My Assessment: **CONFIRMED with refinements** - -The two-phase approach is the right call. Here's why, and what I'd adjust: - -#### Why LHS โ†’ TPE is correct for this problem - -| Factor | Implication | Algorithm Fit | -|--------|------------|---------------| -| 4 design variables (low-dim) | All methods work; sample efficiency less critical | Any | -| 1 integer variable | Need native mixed-type support | TPE โœ“, CMA-ES โ‰ˆ (rounding) | -| Infeasible baseline | Must map feasibility BEFORE optimizing | LHS first โœ“ | -| Expected significant interactions (DV1ร—DV2, DV3ร—DV4) | Need space-filling to detect interactions | LHS โœ“ | -| Potentially narrow feasible region | Risk of missing it with random search | LHS gives systematic coverage โœ“ | -| NX-in-the-loop (medium cost) | ~100-200 trials is budget-appropriate | TPE efficient enough โœ“ | - -#### What I'd modify - -1. **Phase 1 budget: 50 trials (not 40).** With 4 variables, we want at least 10ร— the dimensionality for a reliable DoE. 50 trials also divides cleanly for stratified integer sampling (โ‰ˆ4-5 trials per hole_count level). - -2. **Enqueue baseline as Trial 0.** LAC critical lesson: CMA-ES doesn't evaluate x0 first. While we're using LHS (not CMA-ES), the same principle applies โ€” **always evaluate the baseline explicitly** so we have a verified anchor point. This also validates the extractor pipeline before burning 50 trials. - -3. **Phase 2 budget: 80 trials (flexible 60-100).** Start with 60, apply convergence criteria (Section 6), extend to 100 if still improving. - -4. **Seed Phase 2 from Phase 1 data.** Use Optuna's `enqueue_trial()` to warm-start TPE with the best feasible point(s) from the DoE. This avoids the TPE startup penalty (first `n_startup_trials` are random). - -#### Algorithms NOT selected (and why) - -| Algorithm | Why Not | -|-----------|---------| -| **CMA-ES** | Good option, but integer rounding is a hack; doesn't evaluate x0 first (LAC lesson); TPE is equally good at 4D | -| **NSGA-II** | Overkill for single-objective; population size wastes budget | -| **Surrogate + L-BFGS** | **LAC CRITICAL: Gradient descent on surrogates finds fake optima.** V5 mirror study: L-BFGS was 22% WORSE than pure TPE (WS=325 vs WS=290). V6 confirmed simple TPE beats complex surrogate methods. Do not use. | -| **SOL 200 (Nastran native)** | No integer support for hole_count; gradient-based so may miss global optimum; more NX setup effort. Keep as backup (Tech Lead's suggestion). | -| **Nelder-Mead** | No integer support; poor exploration; would miss the feasible region | - -### 2.3 Final Algorithm Configuration - -``` -Phase 1: LHS DoE - - Trials: 50 (+ 1 baseline = 51 total) - - Sampling: Maximin LHS, DV4 rounded to nearest integer - - Purpose: Landscape mapping, feasibility identification, sensitivity analysis - -Phase 2: TPE Optimization - - Trials: 60-100 (adaptive, see convergence criteria) - - Sampler: Optuna TPEsampler - - n_startup_trials: 0 (warm-started from Phase 1 best) - - Constraint handling: Optuna constraint interface with Deb's rules - - Purpose: Converge to minimum-mass feasible design - -Total budget: 111-151 evaluations -``` - ---- - -## 3. Constraint Handling - -### 3.1 The Challenge - -The baseline FAILS the displacement constraint by 120% (22 mm vs 10 mm). This means: -- A significant portion of the design space may be infeasible -- Random sampling may return few or zero feasible points -- The optimizer must navigate toward feasibility AND optimality simultaneously - -### 3.2 Approach: Deb's Feasibility Rules (Constraint Domination) - -For ranking solutions during optimization, use **Deb's feasibility rules** (Deb 2000): - -1. **Feasible vs feasible** โ†’ compare by objective (lower mass wins) -2. **Feasible vs infeasible** โ†’ feasible always wins -3. **Infeasible vs infeasible** โ†’ lower total constraint violation wins - -This is implemented via Optuna's constraint interface: - -```python -def constraints(trial): - """Return constraint violations (negative = feasible, positive = infeasible).""" - disp = trial.user_attrs["tip_displacement"] - stress = trial.user_attrs["max_von_mises"] - return [ - disp - 10.0, # โ‰ค 0 means displacement โ‰ค 10 mm - stress - 130.0, # โ‰ค 0 means stress โ‰ค 130 MPa - ] -``` - -### 3.3 Why NOT Penalty Functions - -| Method | Pros | Cons | Verdict | -|--------|------|------|---------| -| **Deb's rules** (selected) | No tuning params; feasible always beats infeasible; explores infeasible region for learning | Requires custom Optuna integration | โœ… Best for this case | -| **Quadratic penalty** | Simple to implement | Penalty weight requires tuning; wrong weight โ†’ optimizer ignores constraint OR over-penalizes | โŒ Fragile | -| **Adaptive penalty** | Self-tuning | Complex implementation; may oscillate | โŒ Over-engineered for 4 DVs | -| **Death penalty** (reject infeasible) | Simplest | With infeasible baseline, may reject 80%+ of trials โ†’ wasted budget | โŒ Dangerous | - -### 3.4 Phase 1 (DoE) Constraint Handling - -During the DoE phase, **record all results without filtering.** Every trial runs, every result is stored. Infeasible points are valuable for: -- Mapping the feasibility boundary -- Training the TPE model in Phase 2 -- Understanding which variables drive constraint violation - -### 3.5 Constraint Margin Buffer - -Consider a 5% inner margin during optimization to account for numerical noise: -- Displacement target for optimizer: โ‰ค 9.5 mm (vs hard limit 10.0 mm) -- Stress target for optimizer: โ‰ค 123.5 MPa (vs hard limit 130.0 MPa) - -The hard limits remain 10 mm / 130 MPa for final validation. The buffer prevents the optimizer from converging to designs that are right on the boundary and may flip infeasible under mesh variation. - ---- - -## 4. Search Space Analysis - -### 4.1 Bound Reasonableness - -| Variable | Range | Span | Concern | -|----------|-------|------|---------| -| DV1: half_core_thickness | 10โ€“40 mm | 4ร— range | Reasonable. Lower bound = thin core, upper = thick. Stiffness-mass trade-off | -| DV2: face_thickness | 10โ€“40 mm | 4ร— range | Reasonable. 10 mm face is already substantial for steel | -| DV3: holes_diameter | 150โ€“450 mm | 3ร— range | โš ๏ธ **Needs geometric check** โ€” see ยง4.2 | -| DV4: hole_count | 5โ€“15 | 3ร— range | โš ๏ธ **Needs geometric check** โ€” see ยง4.2 | - -### 4.2 Geometric Feasibility: Hole Overlap Analysis - -**Critical concern:** At extreme DV3 ร— DV4 combinations, holes may overlap or leave insufficient ligament (material between holes). - -#### Overlap condition (CORRECTED โ€” Auditor review 2026-02-10) - -The NX pattern places `n` holes across a span of `p6` mm using `n-1` intervals (holes at both endpoints of the span). Confirmed by introspection: `Pattern_p8 = 4000/9 = 444.44 mm`. - -``` -Spacing between hole centers = hole_span / (hole_count - 1) -Ligament between holes = spacing - d = hole_span/(hole_count - 1) - d -``` - -For **no overlap**, we need: `hole_span/(n-1) - d > 0`, i.e., `d < hole_span/(n-1)` - -With `hole_span = 4,000 mm` (fixed, `p6`): - -#### Worst case: n=15 holes, d=450 mm - -``` -Spacing = 4000 / (15-1) = 285.7 mm -Ligament = 285.7 - 450 = -164.3 mm โ†’ INFEASIBLE (overlap) -``` - -#### Minimum ligament width - -For structural integrity and mesh quality, a minimum ligament of ~30 mm is advisable: - -``` -Minimum ligament constraint: hole_span / (hole_count - 1) - holes_diameter โ‰ฅ 30 mm -``` - -#### Pre-flight geometric filter - -Before sending any trial to NX, compute: -1. `ligament = 4000 / (hole_count - 1) - holes_diameter` โ†’ must be โ‰ฅ 30 mm -2. `web_clear = 2 ร— beam_half_height - 2 ร— beam_face_thickness - holes_diameter` โ†’ must be > 0 - -If either fails, skip NX evaluation and record as infeasible with max constraint violation. This saves compute and avoids NX geometry crashes. - -### 4.3 Hole-to-Web-Height Ratio (CORRECTED โ€” Auditor review 2026-02-10) - -The hole diameter must fit within the web clear height. From introspection: -- Total beam height = `2 ร— beam_half_height = 2 ร— 250 = 500 mm` (fixed) -- Web clear height = `total_height - 2 ร— face_thickness = 500 - 2 ร— beam_face_thickness` - -``` -At baseline (face=21.504mm): web_clear = 500 - 2ร—21.504 = 456.99 mm โ†’ holes of 450mm barely fit (7mm clearance) -At face=40mm: web_clear = 500 - 2ร—40 = 420 mm โ†’ holes of 450mm DO NOT FIT -At face=10mm: web_clear = 500 - 2ร—10 = 480 mm โ†’ holes of 450mm fit (30mm clearance) -``` - -This means `beam_face_thickness` and `holes_diameter` interact geometrically โ€” thicker faces reduce the web clear height available for holes. This constraint is captured in the pre-flight filter (ยง4.2): - -``` -web_clear = 500 - 2 ร— beam_face_thickness - holes_diameter > 0 -``` - -### 4.4 Expected Feasible Region - -Based on the physics (Tech Lead's analysis ยง1.2 and ยง1.3): - -| To reduce displacement (currently 22โ†’10 mm) | Effect on mass | -|----------------------------------------------|---------------| -| โ†‘ DV1 (thicker core) | โ†‘ mass (but stiffness scales ~dยฒ, mass scales ~d) โ†’ **efficient** | -| โ†‘ DV2 (thicker face) | โ†‘ mass (direct) | -| โ†“ DV3 (smaller holes) | โ†‘ mass (more web material) | -| โ†“ DV4 (fewer holes) | โ†‘ mass (more web material) | - -**Prediction:** The feasible region (displacement โ‰ค 10 mm) likely requires: -- DV1 in upper range (25-40 mm) โ€” the sandwich effect is the most mass-efficient stiffness lever -- DV2 moderate (15-30 mm) โ€” thicker faces help stiffness but cost mass directly -- DV3 and DV4 constrained by stress โ€” large/many holes save mass but increase stress - -The optimizer should find a "sweet spot" where core thickness provides stiffness, and holes are sized to save mass without violating stress limits. - -### 4.5 Estimated Design Space Volume - -- DV1: 30 mm span (continuous) -- DV2: 30 mm span (continuous) -- DV3: 300 mm span (continuous) -- DV4: 11 integer levels - -Total configurations: effectively infinite (3 continuous), but the integer dimension creates 11 "slices" of the space. With 50 DoE trials, we get ~4-5 trials per slice โ€” sufficient for trend identification. - ---- - -## 5. Trial Budget & Compute Estimate - -### 5.1 Budget Breakdown - -| Phase | Trials | Purpose | -|-------|--------|---------| -| **Trial 0** | 1 | Baseline validation (enqueued) | -| **Phase 1: LHS DoE** | 50 | Landscape mapping, feasibility, sensitivity | -| **Phase 2: TPE** | 60โ€“100 | Directed optimization | -| **Validation** | 3โ€“5 | Confirm optimum, check mesh sensitivity | -| **Total** | **114โ€“156** | | - -### 5.2 Compute Time Estimate - -| Parameter | Estimate | Notes | -|-----------|----------|-------| -| DOF count | 10Kโ€“100K | Steel beam, SOL 101 | -| Single solve time | 30sโ€“3min | Depends on mesh density | -| Model rebuild time | 10โ€“30s | NX parametric update + remesh | -| Total per trial | 1โ€“4 min | Rebuild + solve + extraction | -| Phase 1 (51 trials) | 1โ€“3.5 hrs | | -| Phase 2 (60โ€“100 trials) | 1โ€“7 hrs | | -| **Total compute** | **2โ€“10 hrs** | Likely ~4โ€“5 hrs | - -### 5.3 Budget Justification - -For 4 design variables, rule-of-thumb budgets: -- **Minimum viable:** 10 ร— n_vars = 40 trials (DoE only) -- **Standard:** 25 ร— n_vars = 100 trials (DoE + optimization) -- **Thorough:** 50 ร— n_vars = 200 trials (with validation) - -Our budget of 114โ€“156 falls in the **standard-to-thorough** range. Appropriate for a first study where we're mapping an unknown landscape with an infeasible baseline. - ---- - -## 6. Convergence Criteria - -### 6.1 Phase 1 (DoE) โ€” No Convergence Criteria - -The DoE runs all 50 planned trials. It's not iterative โ€” it's a one-shot space-filling design. Stop conditions: -- All 50 trials complete (or fail with documented errors) -- **Early abort:** If >80% of trials fail to solve (NX crashes), stop and investigate - -### 6.2 Phase 2 (TPE) โ€” Convergence Criteria - -| Criterion | Threshold | Action | -|-----------|-----------|--------| -| **Improvement stall** | Best feasible objective unchanged for 20 consecutive trials | Consider stopping | -| **Relative improvement** | < 1% improvement over last 20 trials | Consider stopping | -| **Budget exhausted** | 100 trials completed in Phase 2 | Hard stop | -| **Perfect convergence** | Multiple trials within 0.5% of each other from different regions | Confident optimum found | -| **Minimum budget** | Always run at least 60 trials in Phase 2 | Ensures adequate exploration | - -### 6.3 Decision Logic - -``` -After 60 Phase 2 trials: - IF best_feasible improved by >2% in last 20 trials โ†’ continue to 80 - IF no feasible solution found โ†’ STOP, escalate (see ยง7.1) - ELSE โ†’ assess convergence, decide 80 or 100 - -After 80 Phase 2 trials: - IF still improving >1% per 20 trials โ†’ continue to 100 - ELSE โ†’ STOP, declare converged - -After 100 Phase 2 trials: - HARD STOP regardless -``` - -### 6.4 Phase 1 โ†’ Phase 2 Gate - -Before starting Phase 2, review DoE results: - -| Check | Action if FAIL | -|-------|---------------| -| At least 5 feasible points found | If 0 feasible: expand bounds or relax constraints (escalate to CEO) | -| NX solve success rate > 80% | If <80%: investigate failures, fix model, re-run failed trials | -| No systematic NX crashes at bounds | If crashes: tighten bounds away from failure region | -| Sensitivity trends visible | If flat: check extractors, may be reading wrong output | - ---- - -## 7. Risk Mitigation - -### 7.1 Risk: Feasible Region is Empty - -**Likelihood: Medium** (baseline fails displacement by 120%) - -**Detection:** After Phase 1, zero feasible points found. - -**Mitigation ladder:** -1. **Check the data** โ€” Are extractors reading correctly? Validate against manual NX check. -2. **Examine near-feasible** โ€” Find the trial closest to feasibility. How far off? If displacement = 10.5 mm, we're close. If displacement = 18 mm, we have a problem. -3. **Targeted exploration** โ€” Run additional trials at extreme stiffness (max DV1, max DV2, min DV3, min DV4). If even the stiffest/heaviest design fails, the constraint is physically impossible with this geometry. -4. **Constraint relaxation** โ€” Propose to CEO: relax displacement to 12 or 15 mm. Document the mass-displacement Pareto front from DoE data to support the discussion. -5. **Geometric redesign** โ€” If the problem is fundamentally infeasible, the beam geometry needs redesign (out of optimization scope). - -### 7.2 Risk: NX Crashes at Parameter Extremes - -**Likelihood: Medium** (LAC: rib_thickness had undocumented CAD constraint at 9mm, causing 34% failure rate in V13) - -**Detection:** Solver returns no results for certain parameter combinations. - -**Mitigation:** -1. **Pre-flight corner tests** โ€” Before Phase 1, manually test the 16 corners of the design space (2โด combinations of min/max for each variable). This catches geometric rebuild failures early. -2. **Error-handling in run script** โ€” Every trial must catch exceptions and log: - - NX rebuild failure (geometry Boolean crash) - - Meshing failure (degenerate elements) - - Solver failure (singularity, divergence) - - Extraction failure (missing result) -3. **Infeasible-by-default** โ€” If a trial fails for any reason, record it as infeasible with maximum constraint violation (displacement=9999, stress=9999). This lets Deb's rules naturally steer away from crashing regions. -4. **NEVER kill NX processes directly** โ€” LAC CRITICAL RULE. Use NXSessionManager.close_nx_if_allowed() only. If NX hangs, implement a timeout (e.g., 10 min per trial) and let NX time out gracefully. - -### 7.3 Risk: Mesh-Dependent Stress Results - -**Likelihood: Medium** (stress at hole edges is mesh-sensitive) - -**Mitigation:** -1. **Mesh convergence pre-study** โ€” Run baseline at 3 mesh densities. If stress varies >10%, refine mesh or use stress averaging region. -2. **Consistent mesh controls** โ€” Ensure NX applies the same mesh size/refinement strategy regardless of parameter values. The parametric model should have mesh controls tied to hole geometry. -3. **Stress extraction method** โ€” Use elemental nodal stress (conservative) per LAC success pattern. Note: pyNastran returns stress in kPa for NX kg-mm-s unit system โ€” **divide by 1000 for MPa**. - -### 7.4 Risk: Surrogate Temptation - -**Mitigation: DON'T DO IT (yet).** - -LAC lessons from the M1 Mirror project are unequivocal: -- V5 surrogate + L-BFGS was 22% **worse** than V6 pure TPE -- MLP surrogates have smooth gradients everywhere โ†’ L-BFGS descends to fake optima outside training distribution -- No uncertainty quantification = no way to detect out-of-distribution predictions - -With only 4 variables and affordable FEA (~2 min/trial), direct FEA evaluation via TPE is both simpler and more reliable. Surrogate methods should only be considered if: -- FEA solve time exceeds 30 minutes per trial, AND -- We have 100+ validated training points, AND -- We use ensemble surrogates with uncertainty quantification (SYS_16 protocol) - -### 7.5 Risk: Study Corruption - -**Mitigation:** LAC CRITICAL โ€” **Always copy working studies, never rewrite from scratch.** - -- Phase 2 study will be created by **copying** the Phase 1 study directory and adding optimization logic -- Never modify `run_optimization.py` in-place for a new phase โ€” copy to a new version -- Git-commit the study directory after each phase completion - ---- - -## 8. AtomizerSpec Draft - -See [`atomizer_spec_draft.json`](./atomizer_spec_draft.json) for the full JSON config. - -### 8.1 Key Configuration Decisions - -| Setting | Value | Rationale | -|---------|-------|-----------| -| `algorithm.phase1.type` | `LHS` | Space-filling DoE for landscape mapping | -| `algorithm.phase2.type` | `TPE` | Native mixed-integer, sample-efficient, LAC-proven | -| `hole_count.type` | `integer` | DEC-HB-003: true integer, no rounding | -| `constraint_handling` | `deb_feasibility_rules` | Best for infeasible baseline | -| `baseline_trial` | `enqueued` | LAC lesson: always validate baseline first | -| `penalty_config.method` | `deb_rules` | No penalty weight tuning needed | - -### 8.2 Extractor Requirements - -| ID | Type | Output | Source | Notes | -|----|------|--------|--------|-------| -| `ext_001` | `expression` | `mass` | NX expression `p173` | Direct read from NX | -| `ext_002` | `displacement` | `tip_displacement` | SOL 101 result sensor or .op2 parse | โš ๏ธ Need sensor setup or node ID | -| `ext_003` | `stress` | `max_von_mises` | SOL 101 elemental nodal | kPa โ†’ MPa conversion needed | - -### 8.3 Open Items for Spec Finalization - -Before this spec can be promoted from `_draft` to production: - -1. **Beam web length** โ€” Required to validate DV3 ร— DV4 geometric feasibility -2. **Displacement extraction method** โ€” Sensor in .sim, or node ID for .op2 parsing? -3. **Stress extraction scope** โ€” Whole model max, or specific element group? -4. **NX expression names confirmed** โ€” Verify `p173` is mass, confirm displacement/stress expression names -5. **Solver runtime benchmark** โ€” Time one SOL 101 run to refine compute estimates -6. **Corner test results** โ€” Validate model rebuilds at all 16 bound corners - ---- - -## 9. Execution Plan Summary - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ HYDROTECH BEAM OPTIMIZATION โ”‚ -โ”‚ Study: 01_doe_landscape โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”‚ -โ”‚ PRE-FLIGHT (before any trials) โ”‚ -โ”‚ โ”œโ”€โ”€ Validate baseline: run Trial 0, verify mass/disp/stress โ”‚ -โ”‚ โ”œโ”€โ”€ Corner tests: 16 extreme combinations, check NX rebuilds โ”‚ -โ”‚ โ”œโ”€โ”€ Mesh convergence: 3 density levels at baseline โ”‚ -โ”‚ โ””โ”€โ”€ Confirm extractors: mass, displacement, stress pipelines โ”‚ -โ”‚ โ”‚ -โ”‚ PHASE 1: DoE LANDSCAPE (51 trials) โ”‚ -โ”‚ โ”œโ”€โ”€ Trial 0: Baseline (enqueued) โ”‚ -โ”‚ โ”œโ”€โ”€ Trials 1-50: LHS with integer rounding for hole_count โ”‚ -โ”‚ โ”œโ”€โ”€ Analysis: sensitivity, interaction, feasibility mapping โ”‚ -โ”‚ โ””โ”€โ”€ GATE: โ‰ฅ5 feasible? NX success >80%? Proceed/escalate โ”‚ -โ”‚ โ”‚ -โ”‚ PHASE 2: TPE OPTIMIZATION (60-100 trials) โ”‚ -โ”‚ โ”œโ”€โ”€ Warm-start from best Phase 1 feasible point(s) โ”‚ -โ”‚ โ”œโ”€โ”€ Deb's feasibility rules for constraint handling โ”‚ -โ”‚ โ”œโ”€โ”€ Convergence check every 20 trials โ”‚ -โ”‚ โ””โ”€โ”€ Hard stop at 100 trials โ”‚ -โ”‚ โ”‚ -โ”‚ VALIDATION (3-5 trials) โ”‚ -โ”‚ โ”œโ”€โ”€ Re-run best design to confirm repeatability โ”‚ -โ”‚ โ”œโ”€โ”€ Perturb ยฑ5% on each variable to check sensitivity โ”‚ -โ”‚ โ””โ”€โ”€ Document final design with full NX results โ”‚ -โ”‚ โ”‚ -โ”‚ TOTAL: 114-156 NX evaluations | ~4-5 hours compute โ”‚ -โ”‚ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - ---- - -## 10. LAC Lessons Incorporated - -| LAC Lesson | Source | How Applied | -|------------|--------|-------------| -| CMA-ES doesn't evaluate x0 first | Mirror V7 failure | Baseline enqueued as Trial 0 for both phases | -| Surrogate + L-BFGS = fake optima | Mirror V5 failure | No surrogates in this study; direct FEA only | -| Never kill NX processes directly | Dec 2025 incident | Timeout-based error handling; NXSessionManager only | -| Copy working studies, never rewrite | Mirror V5 failure | Phase 2 created by copying Phase 1 | -| pyNastran stress in kPa | Support arm success | Extractor divides by 1000 for MPa | -| CAD constraints can limit bounds | Mirror V13 (rib_thickness) | Pre-flight corner tests before DoE | -| Always include README.md | Repeated failures (Dec 2025, Jan 2026) | README.md created with study | -| Simple beats complex (TPE > surrogate) | Mirror V6 vs V5 | TPE selected over surrogate-based methods | - ---- - -*โšก Optimizer โ€” The algorithm is the strategy.* +# Optimization Strategy โ€” Hydrotech Beam DoE & Landscape Mapping + +**Study:** `01_doe_landscape` +**Project:** Hydrotech Beam Structural Optimization +**Author:** โšก Optimizer Agent +**Date:** 2025-02-09 (updated 2026-02-10 โ€” introspection corrections) +**Status:** APPROVED WITH CONDITIONS โ€” Auditor review 2026-02-10, blockers resolved inline +**References:** [BREAKDOWN.md](../../BREAKDOWN.md), [DECISIONS.md](../../DECISIONS.md), [CONTEXT.md](../../CONTEXT.md) + +--- + +## 1. Problem Formulation + +### 1.1 Objective + +$$\min_{x} \quad f(x) = \text{mass}(x) \quad [\text{kg}]$$ + +Single-objective minimization of total beam mass. This aligns with DEC-HB-001 (approved by Tech Lead, pending CEO confirmation). + +### 1.2 Constraints + +| ID | Constraint | Operator | Limit | Units | Source | +|----|-----------|----------|-------|-------|--------| +| gโ‚ | Tip displacement | โ‰ค | 10.0 | mm | NX Nastran SOL 101 โ€” displacement sensor at beam tip | +| gโ‚‚ | Max von Mises stress | โ‰ค | 130.0 | MPa | NX Nastran SOL 101 โ€” max elemental nodal VM stress | + +Both are **hard constraints** โ€” no trade-off or relaxation without CEO approval. + +### 1.3 Design Variables + +| ID | NX Expression | Type | Lower | Upper | Baseline | Units | Notes | +|----|---------------|------|-------|-------|----------|-------|-------| +| DV1 | `beam_half_core_thickness` | Continuous | 10 | 40 | **25.162** | mm | Core half-thickness; stiffness scales ~quadratically via sandwich effect | +| DV2 | `beam_face_thickness` | Continuous | 10 | 40 | **21.504** | mm | Face sheet thickness; primary bending stiffness contributor | +| DV3 | `holes_diameter` | Continuous | 150 | 450 | 300 | mm | Lightening hole diameter; mass โˆ dยฒ reduction | +| DV4 | `hole_count` (โ†’ `Pattern_p7`) | **Integer** | 5 | 15 | 10 | โ€” | Number of lightening holes; 11 discrete levels | + +**Total design space:** 3 continuous ร— 1 integer (11 levels) = effectively 3D continuous ร— 11 slices. + +### 1.4 Integer Handling + +Per DEC-HB-003, `hole_count` is treated as a **true integer** throughout: + +- **Phase 1 (LHS):** Generate continuous LHS, round DV4 to nearest integer. Use stratified integer sampling to ensure coverage across all 11 levels. +- **Phase 2 (TPE):** Optuna `IntDistribution(5, 15)` โ€” native integer support, no rounding hacks. +- **NX rebuild:** The model requires integer hole count. Non-integer values will cause geometry failures. + +### 1.5 Baseline Assessment + +| Metric | Baseline Value | Constraint | Status | +|--------|---------------|------------|--------| +| Mass | **1,133.01 kg** | (minimize) | Overbuilt โ€” room to reduce | +| Tip displacement | ~22 mm (unverified โ€” awaiting baseline re-run) | โ‰ค 10 mm | โŒ **Likely FAILS** | +| VM stress | (unknown โ€” awaiting baseline re-run) | โ‰ค 130 MPa | โš ๏ธ Unconfirmed | + +> โš ๏ธ **Critical:** The baseline design likely **violates** the displacement constraint (~22 mm vs 10 mm limit). Baseline re-run pending โ€” CEO running SOL 101 in parallel. The optimizer must first find the feasible region before it can meaningfully minimize mass. This shapes the entire strategy. +> +> **Introspection note (2026-02-10):** Mass expression is `p173` (body_property147.mass, kg). DV baselines are NOT round numbers (face=21.504mm, core=25.162mm). NX expression `beam_lenght` has a typo (no 'h'). `hole_count` links to `Pattern_p7` in the NX pattern feature. + +--- + +## 2. Algorithm Selection + +### 2.1 Tech Lead's Recommendation + +DEC-HB-002 proposes a two-phase strategy: +- **Phase 1:** Latin Hypercube Sampling (LHS) โ€” 40โ€“50 trials +- **Phase 2:** TPE via Optuna โ€” 60โ€“100 trials + +### 2.2 My Assessment: **CONFIRMED with refinements** + +The two-phase approach is the right call. Here's why, and what I'd adjust: + +#### Why LHS โ†’ TPE is correct for this problem + +| Factor | Implication | Algorithm Fit | +|--------|------------|---------------| +| 4 design variables (low-dim) | All methods work; sample efficiency less critical | Any | +| 1 integer variable | Need native mixed-type support | TPE โœ“, CMA-ES โ‰ˆ (rounding) | +| Infeasible baseline | Must map feasibility BEFORE optimizing | LHS first โœ“ | +| Expected significant interactions (DV1ร—DV2, DV3ร—DV4) | Need space-filling to detect interactions | LHS โœ“ | +| Potentially narrow feasible region | Risk of missing it with random search | LHS gives systematic coverage โœ“ | +| NX-in-the-loop (medium cost) | ~100-200 trials is budget-appropriate | TPE efficient enough โœ“ | + +#### What I'd modify + +1. **Phase 1 budget: 50 trials (not 40).** With 4 variables, we want at least 10ร— the dimensionality for a reliable DoE. 50 trials also divides cleanly for stratified integer sampling (โ‰ˆ4-5 trials per hole_count level). + +2. **Enqueue baseline as Trial 0.** LAC critical lesson: CMA-ES doesn't evaluate x0 first. While we're using LHS (not CMA-ES), the same principle applies โ€” **always evaluate the baseline explicitly** so we have a verified anchor point. This also validates the extractor pipeline before burning 50 trials. + +3. **Phase 2 budget: 80 trials (flexible 60-100).** Start with 60, apply convergence criteria (Section 6), extend to 100 if still improving. + +4. **Seed Phase 2 from Phase 1 data.** Use Optuna's `enqueue_trial()` to warm-start TPE with the best feasible point(s) from the DoE. This avoids the TPE startup penalty (first `n_startup_trials` are random). + +#### Algorithms NOT selected (and why) + +| Algorithm | Why Not | +|-----------|---------| +| **CMA-ES** | Good option, but integer rounding is a hack; doesn't evaluate x0 first (LAC lesson); TPE is equally good at 4D | +| **NSGA-II** | Overkill for single-objective; population size wastes budget | +| **Surrogate + L-BFGS** | **LAC CRITICAL: Gradient descent on surrogates finds fake optima.** V5 mirror study: L-BFGS was 22% WORSE than pure TPE (WS=325 vs WS=290). V6 confirmed simple TPE beats complex surrogate methods. Do not use. | +| **SOL 200 (Nastran native)** | No integer support for hole_count; gradient-based so may miss global optimum; more NX setup effort. Keep as backup (Tech Lead's suggestion). | +| **Nelder-Mead** | No integer support; poor exploration; would miss the feasible region | + +### 2.3 Final Algorithm Configuration + +``` +Phase 1: LHS DoE + - Trials: 50 (+ 1 baseline = 51 total) + - Sampling: Maximin LHS, DV4 rounded to nearest integer + - Purpose: Landscape mapping, feasibility identification, sensitivity analysis + +Phase 2: TPE Optimization + - Trials: 60-100 (adaptive, see convergence criteria) + - Sampler: Optuna TPEsampler + - n_startup_trials: 0 (warm-started from Phase 1 best) + - Constraint handling: Optuna constraint interface with Deb's rules + - Purpose: Converge to minimum-mass feasible design + +Total budget: 111-151 evaluations +``` + +--- + +## 3. Constraint Handling + +### 3.1 The Challenge + +The baseline FAILS the displacement constraint by 120% (22 mm vs 10 mm). This means: +- A significant portion of the design space may be infeasible +- Random sampling may return few or zero feasible points +- The optimizer must navigate toward feasibility AND optimality simultaneously + +### 3.2 Approach: Deb's Feasibility Rules (Constraint Domination) + +For ranking solutions during optimization, use **Deb's feasibility rules** (Deb 2000): + +1. **Feasible vs feasible** โ†’ compare by objective (lower mass wins) +2. **Feasible vs infeasible** โ†’ feasible always wins +3. **Infeasible vs infeasible** โ†’ lower total constraint violation wins + +This is implemented via Optuna's constraint interface: + +```python +def constraints(trial): + """Return constraint violations (negative = feasible, positive = infeasible).""" + disp = trial.user_attrs["tip_displacement"] + stress = trial.user_attrs["max_von_mises"] + return [ + disp - 10.0, # โ‰ค 0 means displacement โ‰ค 10 mm + stress - 130.0, # โ‰ค 0 means stress โ‰ค 130 MPa + ] +``` + +### 3.3 Why NOT Penalty Functions + +| Method | Pros | Cons | Verdict | +|--------|------|------|---------| +| **Deb's rules** (selected) | No tuning params; feasible always beats infeasible; explores infeasible region for learning | Requires custom Optuna integration | โœ… Best for this case | +| **Quadratic penalty** | Simple to implement | Penalty weight requires tuning; wrong weight โ†’ optimizer ignores constraint OR over-penalizes | โŒ Fragile | +| **Adaptive penalty** | Self-tuning | Complex implementation; may oscillate | โŒ Over-engineered for 4 DVs | +| **Death penalty** (reject infeasible) | Simplest | With infeasible baseline, may reject 80%+ of trials โ†’ wasted budget | โŒ Dangerous | + +### 3.4 Phase 1 (DoE) Constraint Handling + +During the DoE phase, **record all results without filtering.** Every trial runs, every result is stored. Infeasible points are valuable for: +- Mapping the feasibility boundary +- Training the TPE model in Phase 2 +- Understanding which variables drive constraint violation + +### 3.5 Constraint Margin Buffer + +Consider a 5% inner margin during optimization to account for numerical noise: +- Displacement target for optimizer: โ‰ค 9.5 mm (vs hard limit 10.0 mm) +- Stress target for optimizer: โ‰ค 123.5 MPa (vs hard limit 130.0 MPa) + +The hard limits remain 10 mm / 130 MPa for final validation. The buffer prevents the optimizer from converging to designs that are right on the boundary and may flip infeasible under mesh variation. + +--- + +## 4. Search Space Analysis + +### 4.1 Bound Reasonableness + +| Variable | Range | Span | Concern | +|----------|-------|------|---------| +| DV1: half_core_thickness | 10โ€“40 mm | 4ร— range | Reasonable. Lower bound = thin core, upper = thick. Stiffness-mass trade-off | +| DV2: face_thickness | 10โ€“40 mm | 4ร— range | Reasonable. 10 mm face is already substantial for steel | +| DV3: holes_diameter | 150โ€“450 mm | 3ร— range | โš ๏ธ **Needs geometric check** โ€” see ยง4.2 | +| DV4: hole_count | 5โ€“15 | 3ร— range | โš ๏ธ **Needs geometric check** โ€” see ยง4.2 | + +### 4.2 Geometric Feasibility: Hole Overlap Analysis + +**Critical concern:** At extreme DV3 ร— DV4 combinations, holes may overlap or leave insufficient ligament (material between holes). + +#### Overlap condition (CORRECTED โ€” Auditor review 2026-02-10) + +The NX pattern places `n` holes across a span of `p6` mm using `n-1` intervals (holes at both endpoints of the span). Confirmed by introspection: `Pattern_p8 = 4000/9 = 444.44 mm`. + +``` +Spacing between hole centers = hole_span / (hole_count - 1) +Ligament between holes = spacing - d = hole_span/(hole_count - 1) - d +``` + +For **no overlap**, we need: `hole_span/(n-1) - d > 0`, i.e., `d < hole_span/(n-1)` + +With `hole_span = 4,000 mm` (fixed, `p6`): + +#### Worst case: n=15 holes, d=450 mm + +``` +Spacing = 4000 / (15-1) = 285.7 mm +Ligament = 285.7 - 450 = -164.3 mm โ†’ INFEASIBLE (overlap) +``` + +#### Minimum ligament width + +For structural integrity and mesh quality, a minimum ligament of ~30 mm is advisable: + +``` +Minimum ligament constraint: hole_span / (hole_count - 1) - holes_diameter โ‰ฅ 30 mm +``` + +#### Pre-flight geometric filter + +Before sending any trial to NX, compute: +1. `ligament = 4000 / (hole_count - 1) - holes_diameter` โ†’ must be โ‰ฅ 30 mm +2. `web_clear = 2 ร— beam_half_height - 2 ร— beam_face_thickness - holes_diameter` โ†’ must be > 0 + +If either fails, skip NX evaluation and record as infeasible with max constraint violation. This saves compute and avoids NX geometry crashes. + +### 4.3 Hole-to-Web-Height Ratio (CORRECTED โ€” Auditor review 2026-02-10) + +The hole diameter must fit within the web clear height. From introspection: +- Total beam height = `2 ร— beam_half_height = 2 ร— 250 = 500 mm` (fixed) +- Web clear height = `total_height - 2 ร— face_thickness = 500 - 2 ร— beam_face_thickness` + +``` +At baseline (face=21.504mm): web_clear = 500 - 2ร—21.504 = 456.99 mm โ†’ holes of 450mm barely fit (7mm clearance) +At face=40mm: web_clear = 500 - 2ร—40 = 420 mm โ†’ holes of 450mm DO NOT FIT +At face=10mm: web_clear = 500 - 2ร—10 = 480 mm โ†’ holes of 450mm fit (30mm clearance) +``` + +This means `beam_face_thickness` and `holes_diameter` interact geometrically โ€” thicker faces reduce the web clear height available for holes. This constraint is captured in the pre-flight filter (ยง4.2): + +``` +web_clear = 500 - 2 ร— beam_face_thickness - holes_diameter > 0 +``` + +### 4.4 Expected Feasible Region + +Based on the physics (Tech Lead's analysis ยง1.2 and ยง1.3): + +| To reduce displacement (currently 22โ†’10 mm) | Effect on mass | +|----------------------------------------------|---------------| +| โ†‘ DV1 (thicker core) | โ†‘ mass (but stiffness scales ~dยฒ, mass scales ~d) โ†’ **efficient** | +| โ†‘ DV2 (thicker face) | โ†‘ mass (direct) | +| โ†“ DV3 (smaller holes) | โ†‘ mass (more web material) | +| โ†“ DV4 (fewer holes) | โ†‘ mass (more web material) | + +**Prediction:** The feasible region (displacement โ‰ค 10 mm) likely requires: +- DV1 in upper range (25-40 mm) โ€” the sandwich effect is the most mass-efficient stiffness lever +- DV2 moderate (15-30 mm) โ€” thicker faces help stiffness but cost mass directly +- DV3 and DV4 constrained by stress โ€” large/many holes save mass but increase stress + +The optimizer should find a "sweet spot" where core thickness provides stiffness, and holes are sized to save mass without violating stress limits. + +### 4.5 Estimated Design Space Volume + +- DV1: 30 mm span (continuous) +- DV2: 30 mm span (continuous) +- DV3: 300 mm span (continuous) +- DV4: 11 integer levels + +Total configurations: effectively infinite (3 continuous), but the integer dimension creates 11 "slices" of the space. With 50 DoE trials, we get ~4-5 trials per slice โ€” sufficient for trend identification. + +--- + +## 5. Trial Budget & Compute Estimate + +### 5.1 Budget Breakdown + +| Phase | Trials | Purpose | +|-------|--------|---------| +| **Trial 0** | 1 | Baseline validation (enqueued) | +| **Phase 1: LHS DoE** | 50 | Landscape mapping, feasibility, sensitivity | +| **Phase 2: TPE** | 60โ€“100 | Directed optimization | +| **Validation** | 3โ€“5 | Confirm optimum, check mesh sensitivity | +| **Total** | **114โ€“156** | | + +### 5.2 Compute Time Estimate + +| Parameter | Estimate | Notes | +|-----------|----------|-------| +| DOF count | 10Kโ€“100K | Steel beam, SOL 101 | +| Single solve time | 30sโ€“3min | Depends on mesh density | +| Model rebuild time | 10โ€“30s | NX parametric update + remesh | +| Total per trial | 1โ€“4 min | Rebuild + solve + extraction | +| Phase 1 (51 trials) | 1โ€“3.5 hrs | | +| Phase 2 (60โ€“100 trials) | 1โ€“7 hrs | | +| **Total compute** | **2โ€“10 hrs** | Likely ~4โ€“5 hrs | + +### 5.3 Budget Justification + +For 4 design variables, rule-of-thumb budgets: +- **Minimum viable:** 10 ร— n_vars = 40 trials (DoE only) +- **Standard:** 25 ร— n_vars = 100 trials (DoE + optimization) +- **Thorough:** 50 ร— n_vars = 200 trials (with validation) + +Our budget of 114โ€“156 falls in the **standard-to-thorough** range. Appropriate for a first study where we're mapping an unknown landscape with an infeasible baseline. + +--- + +## 6. Convergence Criteria + +### 6.1 Phase 1 (DoE) โ€” No Convergence Criteria + +The DoE runs all 50 planned trials. It's not iterative โ€” it's a one-shot space-filling design. Stop conditions: +- All 50 trials complete (or fail with documented errors) +- **Early abort:** If >80% of trials fail to solve (NX crashes), stop and investigate + +### 6.2 Phase 2 (TPE) โ€” Convergence Criteria + +| Criterion | Threshold | Action | +|-----------|-----------|--------| +| **Improvement stall** | Best feasible objective unchanged for 20 consecutive trials | Consider stopping | +| **Relative improvement** | < 1% improvement over last 20 trials | Consider stopping | +| **Budget exhausted** | 100 trials completed in Phase 2 | Hard stop | +| **Perfect convergence** | Multiple trials within 0.5% of each other from different regions | Confident optimum found | +| **Minimum budget** | Always run at least 60 trials in Phase 2 | Ensures adequate exploration | + +### 6.3 Decision Logic + +``` +After 60 Phase 2 trials: + IF best_feasible improved by >2% in last 20 trials โ†’ continue to 80 + IF no feasible solution found โ†’ STOP, escalate (see ยง7.1) + ELSE โ†’ assess convergence, decide 80 or 100 + +After 80 Phase 2 trials: + IF still improving >1% per 20 trials โ†’ continue to 100 + ELSE โ†’ STOP, declare converged + +After 100 Phase 2 trials: + HARD STOP regardless +``` + +### 6.4 Phase 1 โ†’ Phase 2 Gate + +Before starting Phase 2, review DoE results: + +| Check | Action if FAIL | +|-------|---------------| +| At least 5 feasible points found | If 0 feasible: expand bounds or relax constraints (escalate to CEO) | +| NX solve success rate > 80% | If <80%: investigate failures, fix model, re-run failed trials | +| No systematic NX crashes at bounds | If crashes: tighten bounds away from failure region | +| Sensitivity trends visible | If flat: check extractors, may be reading wrong output | + +--- + +## 7. Risk Mitigation + +### 7.1 Risk: Feasible Region is Empty + +**Likelihood: Medium** (baseline fails displacement by 120%) + +**Detection:** After Phase 1, zero feasible points found. + +**Mitigation ladder:** +1. **Check the data** โ€” Are extractors reading correctly? Validate against manual NX check. +2. **Examine near-feasible** โ€” Find the trial closest to feasibility. How far off? If displacement = 10.5 mm, we're close. If displacement = 18 mm, we have a problem. +3. **Targeted exploration** โ€” Run additional trials at extreme stiffness (max DV1, max DV2, min DV3, min DV4). If even the stiffest/heaviest design fails, the constraint is physically impossible with this geometry. +4. **Constraint relaxation** โ€” Propose to CEO: relax displacement to 12 or 15 mm. Document the mass-displacement Pareto front from DoE data to support the discussion. +5. **Geometric redesign** โ€” If the problem is fundamentally infeasible, the beam geometry needs redesign (out of optimization scope). + +### 7.2 Risk: NX Crashes at Parameter Extremes + +**Likelihood: Medium** (LAC: rib_thickness had undocumented CAD constraint at 9mm, causing 34% failure rate in V13) + +**Detection:** Solver returns no results for certain parameter combinations. + +**Mitigation:** +1. **Pre-flight corner tests** โ€” Before Phase 1, manually test the 16 corners of the design space (2โด combinations of min/max for each variable). This catches geometric rebuild failures early. +2. **Error-handling in run script** โ€” Every trial must catch exceptions and log: + - NX rebuild failure (geometry Boolean crash) + - Meshing failure (degenerate elements) + - Solver failure (singularity, divergence) + - Extraction failure (missing result) +3. **Infeasible-by-default** โ€” If a trial fails for any reason, record it as infeasible with maximum constraint violation (displacement=9999, stress=9999). This lets Deb's rules naturally steer away from crashing regions. +4. **NEVER kill NX processes directly** โ€” LAC CRITICAL RULE. Use NXSessionManager.close_nx_if_allowed() only. If NX hangs, implement a timeout (e.g., 10 min per trial) and let NX time out gracefully. + +### 7.3 Risk: Mesh-Dependent Stress Results + +**Likelihood: Medium** (stress at hole edges is mesh-sensitive) + +**Mitigation:** +1. **Mesh convergence pre-study** โ€” Run baseline at 3 mesh densities. If stress varies >10%, refine mesh or use stress averaging region. +2. **Consistent mesh controls** โ€” Ensure NX applies the same mesh size/refinement strategy regardless of parameter values. The parametric model should have mesh controls tied to hole geometry. +3. **Stress extraction method** โ€” Use elemental nodal stress (conservative) per LAC success pattern. Note: pyNastran returns stress in kPa for NX kg-mm-s unit system โ€” **divide by 1000 for MPa**. + +### 7.4 Risk: Surrogate Temptation + +**Mitigation: DON'T DO IT (yet).** + +LAC lessons from the M1 Mirror project are unequivocal: +- V5 surrogate + L-BFGS was 22% **worse** than V6 pure TPE +- MLP surrogates have smooth gradients everywhere โ†’ L-BFGS descends to fake optima outside training distribution +- No uncertainty quantification = no way to detect out-of-distribution predictions + +With only 4 variables and affordable FEA (~2 min/trial), direct FEA evaluation via TPE is both simpler and more reliable. Surrogate methods should only be considered if: +- FEA solve time exceeds 30 minutes per trial, AND +- We have 100+ validated training points, AND +- We use ensemble surrogates with uncertainty quantification (SYS_16 protocol) + +### 7.5 Risk: Study Corruption + +**Mitigation:** LAC CRITICAL โ€” **Always copy working studies, never rewrite from scratch.** + +- Phase 2 study will be created by **copying** the Phase 1 study directory and adding optimization logic +- Never modify `run_optimization.py` in-place for a new phase โ€” copy to a new version +- Git-commit the study directory after each phase completion + +--- + +## 8. AtomizerSpec Draft + +See [`atomizer_spec_draft.json`](./atomizer_spec_draft.json) for the full JSON config. + +### 8.1 Key Configuration Decisions + +| Setting | Value | Rationale | +|---------|-------|-----------| +| `algorithm.phase1.type` | `LHS` | Space-filling DoE for landscape mapping | +| `algorithm.phase2.type` | `TPE` | Native mixed-integer, sample-efficient, LAC-proven | +| `hole_count.type` | `integer` | DEC-HB-003: true integer, no rounding | +| `constraint_handling` | `deb_feasibility_rules` | Best for infeasible baseline | +| `baseline_trial` | `enqueued` | LAC lesson: always validate baseline first | +| `penalty_config.method` | `deb_rules` | No penalty weight tuning needed | + +### 8.2 Extractor Requirements + +| ID | Type | Output | Source | Notes | +|----|------|--------|--------|-------| +| `ext_001` | `expression` | `mass` | NX expression `p173` | Direct read from NX | +| `ext_002` | `displacement` | `tip_displacement` | SOL 101 result sensor or .op2 parse | โš ๏ธ Need sensor setup or node ID | +| `ext_003` | `stress` | `max_von_mises` | SOL 101 elemental nodal | kPa โ†’ MPa conversion needed | + +### 8.3 Open Items for Spec Finalization + +Before this spec can be promoted from `_draft` to production: + +1. **Beam web length** โ€” Required to validate DV3 ร— DV4 geometric feasibility +2. **Displacement extraction method** โ€” Sensor in .sim, or node ID for .op2 parsing? +3. **Stress extraction scope** โ€” Whole model max, or specific element group? +4. **NX expression names confirmed** โ€” Verify `p173` is mass, confirm displacement/stress expression names +5. **Solver runtime benchmark** โ€” Time one SOL 101 run to refine compute estimates +6. **Corner test results** โ€” Validate model rebuilds at all 16 bound corners + +--- + +## 9. Execution Plan Summary + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ HYDROTECH BEAM OPTIMIZATION โ”‚ +โ”‚ Study: 01_doe_landscape โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ PRE-FLIGHT (before any trials) โ”‚ +โ”‚ โ”œโ”€โ”€ Validate baseline: run Trial 0, verify mass/disp/stress โ”‚ +โ”‚ โ”œโ”€โ”€ Corner tests: 16 extreme combinations, check NX rebuilds โ”‚ +โ”‚ โ”œโ”€โ”€ Mesh convergence: 3 density levels at baseline โ”‚ +โ”‚ โ””โ”€โ”€ Confirm extractors: mass, displacement, stress pipelines โ”‚ +โ”‚ โ”‚ +โ”‚ PHASE 1: DoE LANDSCAPE (51 trials) โ”‚ +โ”‚ โ”œโ”€โ”€ Trial 0: Baseline (enqueued) โ”‚ +โ”‚ โ”œโ”€โ”€ Trials 1-50: LHS with integer rounding for hole_count โ”‚ +โ”‚ โ”œโ”€โ”€ Analysis: sensitivity, interaction, feasibility mapping โ”‚ +โ”‚ โ””โ”€โ”€ GATE: โ‰ฅ5 feasible? NX success >80%? Proceed/escalate โ”‚ +โ”‚ โ”‚ +โ”‚ PHASE 2: TPE OPTIMIZATION (60-100 trials) โ”‚ +โ”‚ โ”œโ”€โ”€ Warm-start from best Phase 1 feasible point(s) โ”‚ +โ”‚ โ”œโ”€โ”€ Deb's feasibility rules for constraint handling โ”‚ +โ”‚ โ”œโ”€โ”€ Convergence check every 20 trials โ”‚ +โ”‚ โ””โ”€โ”€ Hard stop at 100 trials โ”‚ +โ”‚ โ”‚ +โ”‚ VALIDATION (3-5 trials) โ”‚ +โ”‚ โ”œโ”€โ”€ Re-run best design to confirm repeatability โ”‚ +โ”‚ โ”œโ”€โ”€ Perturb ยฑ5% on each variable to check sensitivity โ”‚ +โ”‚ โ””โ”€โ”€ Document final design with full NX results โ”‚ +โ”‚ โ”‚ +โ”‚ TOTAL: 114-156 NX evaluations | ~4-5 hours compute โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +--- + +## 10. LAC Lessons Incorporated + +| LAC Lesson | Source | How Applied | +|------------|--------|-------------| +| CMA-ES doesn't evaluate x0 first | Mirror V7 failure | Baseline enqueued as Trial 0 for both phases | +| Surrogate + L-BFGS = fake optima | Mirror V5 failure | No surrogates in this study; direct FEA only | +| Never kill NX processes directly | Dec 2025 incident | Timeout-based error handling; NXSessionManager only | +| Copy working studies, never rewrite | Mirror V5 failure | Phase 2 created by copying Phase 1 | +| pyNastran stress in kPa | Support arm success | Extractor divides by 1000 for MPa | +| CAD constraints can limit bounds | Mirror V13 (rib_thickness) | Pre-flight corner tests before DoE | +| Always include README.md | Repeated failures (Dec 2025, Jan 2026) | README.md created with study | +| Simple beats complex (TPE > surrogate) | Mirror V6 vs V5 | TPE selected over surrogate-based methods | + +--- + +*โšก Optimizer โ€” The algorithm is the strategy.* diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/OPTIMIZATION_STRATEGY.sync-conflict-20260214-191756-VBCUD7Z.md b/projects/hydrotech-beam/studies/01_doe_landscape/OPTIMIZATION_STRATEGY.sync-conflict-20260214-191756-VBCUD7Z.md new file mode 100644 index 00000000..1447c5c1 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/OPTIMIZATION_STRATEGY.sync-conflict-20260214-191756-VBCUD7Z.md @@ -0,0 +1,514 @@ +# Optimization Strategy โ€” Hydrotech Beam DoE & Landscape Mapping + +**Study:** `01_doe_landscape` +**Project:** Hydrotech Beam Structural Optimization +**Author:** โšก Optimizer Agent +**Date:** 2025-02-09 (updated 2026-02-10 โ€” introspection corrections) +**Status:** APPROVED WITH CONDITIONS โ€” Auditor review 2026-02-10, blockers resolved inline +**References:** [BREAKDOWN.md](../../BREAKDOWN.md), [DECISIONS.md](../../DECISIONS.md), [CONTEXT.md](../../CONTEXT.md) + +--- + +## 1. Problem Formulation + +### 1.1 Objective + +$$\min_{x} \quad f(x) = \text{mass}(x) \quad [\text{kg}]$$ + +Single-objective minimization of total beam mass. This aligns with DEC-HB-001 (approved by Tech Lead, pending CEO confirmation). + +### 1.2 Constraints + +| ID | Constraint | Operator | Limit | Units | Source | +|----|-----------|----------|-------|-------|--------| +| gโ‚ | Tip displacement | โ‰ค | 10.0 | mm | NX Nastran SOL 101 โ€” displacement sensor at beam tip | +| gโ‚‚ | Max von Mises stress | โ‰ค | 130.0 | MPa | NX Nastran SOL 101 โ€” max elemental nodal VM stress | + +Both are **hard constraints** โ€” no trade-off or relaxation without CEO approval. + +### 1.3 Design Variables + +| ID | NX Expression | Type | Lower | Upper | Baseline | Units | Notes | +|----|---------------|------|-------|-------|----------|-------|-------| +| DV1 | `beam_half_core_thickness` | Continuous | 10 | 40 | **25.162** | mm | Core half-thickness; stiffness scales ~quadratically via sandwich effect | +| DV2 | `beam_face_thickness` | Continuous | 10 | 40 | **21.504** | mm | Face sheet thickness; primary bending stiffness contributor | +| DV3 | `holes_diameter` | Continuous | 150 | 450 | 300 | mm | Lightening hole diameter; mass โˆ dยฒ reduction | +| DV4 | `hole_count` (โ†’ `Pattern_p7`) | **Integer** | 5 | 15 | 10 | โ€” | Number of lightening holes; 11 discrete levels | + +**Total design space:** 3 continuous ร— 1 integer (11 levels) = effectively 3D continuous ร— 11 slices. + +### 1.4 Integer Handling + +Per DEC-HB-003, `hole_count` is treated as a **true integer** throughout: + +- **Phase 1 (LHS):** Generate continuous LHS, round DV4 to nearest integer. Use stratified integer sampling to ensure coverage across all 11 levels. +- **Phase 2 (TPE):** Optuna `IntDistribution(5, 15)` โ€” native integer support, no rounding hacks. +- **NX rebuild:** The model requires integer hole count. Non-integer values will cause geometry failures. + +### 1.5 Baseline Assessment + +| Metric | Baseline Value | Constraint | Status | +|--------|---------------|------------|--------| +| Mass | **1,133.01 kg** | (minimize) | Overbuilt โ€” room to reduce | +| Tip displacement | ~22 mm (unverified โ€” awaiting baseline re-run) | โ‰ค 10 mm | โŒ **Likely FAILS** | +| VM stress | (unknown โ€” awaiting baseline re-run) | โ‰ค 130 MPa | โš ๏ธ Unconfirmed | + +> โš ๏ธ **Critical:** The baseline design likely **violates** the displacement constraint (~22 mm vs 10 mm limit). Baseline re-run pending โ€” CEO running SOL 101 in parallel. The optimizer must first find the feasible region before it can meaningfully minimize mass. This shapes the entire strategy. +> +> **Introspection note (2026-02-10):** Mass expression is `p173` (body_property147.mass, kg). DV baselines are NOT round numbers (face=21.504mm, core=25.162mm). NX expression `beam_lenght` has a typo (no 'h'). `hole_count` links to `Pattern_p7` in the NX pattern feature. + +--- + +## 2. Algorithm Selection + +### 2.1 Tech Lead's Recommendation + +DEC-HB-002 proposes a two-phase strategy: +- **Phase 1:** Latin Hypercube Sampling (LHS) โ€” 40โ€“50 trials +- **Phase 2:** TPE via Optuna โ€” 60โ€“100 trials + +### 2.2 My Assessment: **CONFIRMED with refinements** + +The two-phase approach is the right call. Here's why, and what I'd adjust: + +#### Why LHS โ†’ TPE is correct for this problem + +| Factor | Implication | Algorithm Fit | +|--------|------------|---------------| +| 4 design variables (low-dim) | All methods work; sample efficiency less critical | Any | +| 1 integer variable | Need native mixed-type support | TPE โœ“, CMA-ES โ‰ˆ (rounding) | +| Infeasible baseline | Must map feasibility BEFORE optimizing | LHS first โœ“ | +| Expected significant interactions (DV1ร—DV2, DV3ร—DV4) | Need space-filling to detect interactions | LHS โœ“ | +| Potentially narrow feasible region | Risk of missing it with random search | LHS gives systematic coverage โœ“ | +| NX-in-the-loop (medium cost) | ~100-200 trials is budget-appropriate | TPE efficient enough โœ“ | + +#### What I'd modify + +1. **Phase 1 budget: 50 trials (not 40).** With 4 variables, we want at least 10ร— the dimensionality for a reliable DoE. 50 trials also divides cleanly for stratified integer sampling (โ‰ˆ4-5 trials per hole_count level). + +2. **Enqueue baseline as Trial 0.** LAC critical lesson: CMA-ES doesn't evaluate x0 first. While we're using LHS (not CMA-ES), the same principle applies โ€” **always evaluate the baseline explicitly** so we have a verified anchor point. This also validates the extractor pipeline before burning 50 trials. + +3. **Phase 2 budget: 80 trials (flexible 60-100).** Start with 60, apply convergence criteria (Section 6), extend to 100 if still improving. + +4. **Seed Phase 2 from Phase 1 data.** Use Optuna's `enqueue_trial()` to warm-start TPE with the best feasible point(s) from the DoE. This avoids the TPE startup penalty (first `n_startup_trials` are random). + +#### Algorithms NOT selected (and why) + +| Algorithm | Why Not | +|-----------|---------| +| **CMA-ES** | Good option, but integer rounding is a hack; doesn't evaluate x0 first (LAC lesson); TPE is equally good at 4D | +| **NSGA-II** | Overkill for single-objective; population size wastes budget | +| **Surrogate + L-BFGS** | **LAC CRITICAL: Gradient descent on surrogates finds fake optima.** V5 mirror study: L-BFGS was 22% WORSE than pure TPE (WS=325 vs WS=290). V6 confirmed simple TPE beats complex surrogate methods. Do not use. | +| **SOL 200 (Nastran native)** | No integer support for hole_count; gradient-based so may miss global optimum; more NX setup effort. Keep as backup (Tech Lead's suggestion). | +| **Nelder-Mead** | No integer support; poor exploration; would miss the feasible region | + +### 2.3 Final Algorithm Configuration + +``` +Phase 1: LHS DoE + - Trials: 50 (+ 1 baseline = 51 total) + - Sampling: Maximin LHS, DV4 rounded to nearest integer + - Purpose: Landscape mapping, feasibility identification, sensitivity analysis + +Phase 2: TPE Optimization + - Trials: 60-100 (adaptive, see convergence criteria) + - Sampler: Optuna TPEsampler + - n_startup_trials: 0 (warm-started from Phase 1 best) + - Constraint handling: Optuna constraint interface with Deb's rules + - Purpose: Converge to minimum-mass feasible design + +Total budget: 111-151 evaluations +``` + +--- + +## 3. Constraint Handling + +### 3.1 The Challenge + +The baseline FAILS the displacement constraint by 120% (22 mm vs 10 mm). This means: +- A significant portion of the design space may be infeasible +- Random sampling may return few or zero feasible points +- The optimizer must navigate toward feasibility AND optimality simultaneously + +### 3.2 Approach: Deb's Feasibility Rules (Constraint Domination) + +For ranking solutions during optimization, use **Deb's feasibility rules** (Deb 2000): + +1. **Feasible vs feasible** โ†’ compare by objective (lower mass wins) +2. **Feasible vs infeasible** โ†’ feasible always wins +3. **Infeasible vs infeasible** โ†’ lower total constraint violation wins + +This is implemented via Optuna's constraint interface: + +```python +def constraints(trial): + """Return constraint violations (negative = feasible, positive = infeasible).""" + disp = trial.user_attrs["tip_displacement"] + stress = trial.user_attrs["max_von_mises"] + return [ + disp - 10.0, # โ‰ค 0 means displacement โ‰ค 10 mm + stress - 130.0, # โ‰ค 0 means stress โ‰ค 130 MPa + ] +``` + +### 3.3 Why NOT Penalty Functions + +| Method | Pros | Cons | Verdict | +|--------|------|------|---------| +| **Deb's rules** (selected) | No tuning params; feasible always beats infeasible; explores infeasible region for learning | Requires custom Optuna integration | โœ… Best for this case | +| **Quadratic penalty** | Simple to implement | Penalty weight requires tuning; wrong weight โ†’ optimizer ignores constraint OR over-penalizes | โŒ Fragile | +| **Adaptive penalty** | Self-tuning | Complex implementation; may oscillate | โŒ Over-engineered for 4 DVs | +| **Death penalty** (reject infeasible) | Simplest | With infeasible baseline, may reject 80%+ of trials โ†’ wasted budget | โŒ Dangerous | + +### 3.4 Phase 1 (DoE) Constraint Handling + +During the DoE phase, **record all results without filtering.** Every trial runs, every result is stored. Infeasible points are valuable for: +- Mapping the feasibility boundary +- Training the TPE model in Phase 2 +- Understanding which variables drive constraint violation + +### 3.5 Constraint Margin Buffer + +Consider a 5% inner margin during optimization to account for numerical noise: +- Displacement target for optimizer: โ‰ค 9.5 mm (vs hard limit 10.0 mm) +- Stress target for optimizer: โ‰ค 123.5 MPa (vs hard limit 130.0 MPa) + +The hard limits remain 10 mm / 130 MPa for final validation. The buffer prevents the optimizer from converging to designs that are right on the boundary and may flip infeasible under mesh variation. + +--- + +## 4. Search Space Analysis + +### 4.1 Bound Reasonableness + +| Variable | Range | Span | Concern | +|----------|-------|------|---------| +| DV1: half_core_thickness | 10โ€“40 mm | 4ร— range | Reasonable. Lower bound = thin core, upper = thick. Stiffness-mass trade-off | +| DV2: face_thickness | 10โ€“40 mm | 4ร— range | Reasonable. 10 mm face is already substantial for steel | +| DV3: holes_diameter | 150โ€“450 mm | 3ร— range | โš ๏ธ **Needs geometric check** โ€” see ยง4.2 | +| DV4: hole_count | 5โ€“15 | 3ร— range | โš ๏ธ **Needs geometric check** โ€” see ยง4.2 | + +### 4.2 Geometric Feasibility: Hole Overlap Analysis + +**Critical concern:** At extreme DV3 ร— DV4 combinations, holes may overlap or leave insufficient ligament (material between holes). + +#### Overlap condition (CORRECTED โ€” Auditor review 2026-02-10) + +The NX pattern places `n` holes across a span of `p6` mm using `n-1` intervals (holes at both endpoints of the span). Confirmed by introspection: `Pattern_p8 = 4000/9 = 444.44 mm`. + +``` +Spacing between hole centers = hole_span / (hole_count - 1) +Ligament between holes = spacing - d = hole_span/(hole_count - 1) - d +``` + +For **no overlap**, we need: `hole_span/(n-1) - d > 0`, i.e., `d < hole_span/(n-1)` + +With `hole_span = 4,000 mm` (fixed, `p6`): + +#### Worst case: n=15 holes, d=450 mm + +``` +Spacing = 4000 / (15-1) = 285.7 mm +Ligament = 285.7 - 450 = -164.3 mm โ†’ INFEASIBLE (overlap) +``` + +#### Minimum ligament width + +For structural integrity and mesh quality, a minimum ligament of ~30 mm is advisable: + +``` +Minimum ligament constraint: hole_span / (hole_count - 1) - holes_diameter โ‰ฅ 30 mm +``` + +#### Pre-flight geometric filter + +Before sending any trial to NX, compute: +1. `ligament = 4000 / (hole_count - 1) - holes_diameter` โ†’ must be โ‰ฅ 30 mm +2. `web_clear = 2 ร— beam_half_height - 2 ร— beam_face_thickness - holes_diameter` โ†’ must be > 0 + +If either fails, skip NX evaluation and record as infeasible with max constraint violation. This saves compute and avoids NX geometry crashes. + +### 4.3 Hole-to-Web-Height Ratio (CORRECTED โ€” Auditor review 2026-02-10) + +The hole diameter must fit within the web clear height. From introspection: +- Total beam height = `2 ร— beam_half_height = 2 ร— 250 = 500 mm` (fixed) +- Web clear height = `total_height - 2 ร— face_thickness = 500 - 2 ร— beam_face_thickness` + +``` +At baseline (face=21.504mm): web_clear = 500 - 2ร—21.504 = 456.99 mm โ†’ holes of 450mm barely fit (7mm clearance) +At face=40mm: web_clear = 500 - 2ร—40 = 420 mm โ†’ holes of 450mm DO NOT FIT +At face=10mm: web_clear = 500 - 2ร—10 = 480 mm โ†’ holes of 450mm fit (30mm clearance) +``` + +This means `beam_face_thickness` and `holes_diameter` interact geometrically โ€” thicker faces reduce the web clear height available for holes. This constraint is captured in the pre-flight filter (ยง4.2): + +``` +web_clear = 500 - 2 ร— beam_face_thickness - holes_diameter > 0 +``` + +### 4.4 Expected Feasible Region + +Based on the physics (Tech Lead's analysis ยง1.2 and ยง1.3): + +| To reduce displacement (currently 22โ†’10 mm) | Effect on mass | +|----------------------------------------------|---------------| +| โ†‘ DV1 (thicker core) | โ†‘ mass (but stiffness scales ~dยฒ, mass scales ~d) โ†’ **efficient** | +| โ†‘ DV2 (thicker face) | โ†‘ mass (direct) | +| โ†“ DV3 (smaller holes) | โ†‘ mass (more web material) | +| โ†“ DV4 (fewer holes) | โ†‘ mass (more web material) | + +**Prediction:** The feasible region (displacement โ‰ค 10 mm) likely requires: +- DV1 in upper range (25-40 mm) โ€” the sandwich effect is the most mass-efficient stiffness lever +- DV2 moderate (15-30 mm) โ€” thicker faces help stiffness but cost mass directly +- DV3 and DV4 constrained by stress โ€” large/many holes save mass but increase stress + +The optimizer should find a "sweet spot" where core thickness provides stiffness, and holes are sized to save mass without violating stress limits. + +### 4.5 Estimated Design Space Volume + +- DV1: 30 mm span (continuous) +- DV2: 30 mm span (continuous) +- DV3: 300 mm span (continuous) +- DV4: 11 integer levels + +Total configurations: effectively infinite (3 continuous), but the integer dimension creates 11 "slices" of the space. With 50 DoE trials, we get ~4-5 trials per slice โ€” sufficient for trend identification. + +--- + +## 5. Trial Budget & Compute Estimate + +### 5.1 Budget Breakdown + +| Phase | Trials | Purpose | +|-------|--------|---------| +| **Trial 0** | 1 | Baseline validation (enqueued) | +| **Phase 1: LHS DoE** | 50 | Landscape mapping, feasibility, sensitivity | +| **Phase 2: TPE** | 60โ€“100 | Directed optimization | +| **Validation** | 3โ€“5 | Confirm optimum, check mesh sensitivity | +| **Total** | **114โ€“156** | | + +### 5.2 Compute Time Estimate + +| Parameter | Estimate | Notes | +|-----------|----------|-------| +| DOF count | 10Kโ€“100K | Steel beam, SOL 101 | +| Single solve time | 30sโ€“3min | Depends on mesh density | +| Model rebuild time | 10โ€“30s | NX parametric update + remesh | +| Total per trial | 1โ€“4 min | Rebuild + solve + extraction | +| Phase 1 (51 trials) | 1โ€“3.5 hrs | | +| Phase 2 (60โ€“100 trials) | 1โ€“7 hrs | | +| **Total compute** | **2โ€“10 hrs** | Likely ~4โ€“5 hrs | + +### 5.3 Budget Justification + +For 4 design variables, rule-of-thumb budgets: +- **Minimum viable:** 10 ร— n_vars = 40 trials (DoE only) +- **Standard:** 25 ร— n_vars = 100 trials (DoE + optimization) +- **Thorough:** 50 ร— n_vars = 200 trials (with validation) + +Our budget of 114โ€“156 falls in the **standard-to-thorough** range. Appropriate for a first study where we're mapping an unknown landscape with an infeasible baseline. + +--- + +## 6. Convergence Criteria + +### 6.1 Phase 1 (DoE) โ€” No Convergence Criteria + +The DoE runs all 50 planned trials. It's not iterative โ€” it's a one-shot space-filling design. Stop conditions: +- All 50 trials complete (or fail with documented errors) +- **Early abort:** If >80% of trials fail to solve (NX crashes), stop and investigate + +### 6.2 Phase 2 (TPE) โ€” Convergence Criteria + +| Criterion | Threshold | Action | +|-----------|-----------|--------| +| **Improvement stall** | Best feasible objective unchanged for 20 consecutive trials | Consider stopping | +| **Relative improvement** | < 1% improvement over last 20 trials | Consider stopping | +| **Budget exhausted** | 100 trials completed in Phase 2 | Hard stop | +| **Perfect convergence** | Multiple trials within 0.5% of each other from different regions | Confident optimum found | +| **Minimum budget** | Always run at least 60 trials in Phase 2 | Ensures adequate exploration | + +### 6.3 Decision Logic + +``` +After 60 Phase 2 trials: + IF best_feasible improved by >2% in last 20 trials โ†’ continue to 80 + IF no feasible solution found โ†’ STOP, escalate (see ยง7.1) + ELSE โ†’ assess convergence, decide 80 or 100 + +After 80 Phase 2 trials: + IF still improving >1% per 20 trials โ†’ continue to 100 + ELSE โ†’ STOP, declare converged + +After 100 Phase 2 trials: + HARD STOP regardless +``` + +### 6.4 Phase 1 โ†’ Phase 2 Gate + +Before starting Phase 2, review DoE results: + +| Check | Action if FAIL | +|-------|---------------| +| At least 5 feasible points found | If 0 feasible: expand bounds or relax constraints (escalate to CEO) | +| NX solve success rate > 80% | If <80%: investigate failures, fix model, re-run failed trials | +| No systematic NX crashes at bounds | If crashes: tighten bounds away from failure region | +| Sensitivity trends visible | If flat: check extractors, may be reading wrong output | + +--- + +## 7. Risk Mitigation + +### 7.1 Risk: Feasible Region is Empty + +**Likelihood: Medium** (baseline fails displacement by 120%) + +**Detection:** After Phase 1, zero feasible points found. + +**Mitigation ladder:** +1. **Check the data** โ€” Are extractors reading correctly? Validate against manual NX check. +2. **Examine near-feasible** โ€” Find the trial closest to feasibility. How far off? If displacement = 10.5 mm, we're close. If displacement = 18 mm, we have a problem. +3. **Targeted exploration** โ€” Run additional trials at extreme stiffness (max DV1, max DV2, min DV3, min DV4). If even the stiffest/heaviest design fails, the constraint is physically impossible with this geometry. +4. **Constraint relaxation** โ€” Propose to CEO: relax displacement to 12 or 15 mm. Document the mass-displacement Pareto front from DoE data to support the discussion. +5. **Geometric redesign** โ€” If the problem is fundamentally infeasible, the beam geometry needs redesign (out of optimization scope). + +### 7.2 Risk: NX Crashes at Parameter Extremes + +**Likelihood: Medium** (LAC: rib_thickness had undocumented CAD constraint at 9mm, causing 34% failure rate in V13) + +**Detection:** Solver returns no results for certain parameter combinations. + +**Mitigation:** +1. **Pre-flight corner tests** โ€” Before Phase 1, manually test the 16 corners of the design space (2โด combinations of min/max for each variable). This catches geometric rebuild failures early. +2. **Error-handling in run script** โ€” Every trial must catch exceptions and log: + - NX rebuild failure (geometry Boolean crash) + - Meshing failure (degenerate elements) + - Solver failure (singularity, divergence) + - Extraction failure (missing result) +3. **Infeasible-by-default** โ€” If a trial fails for any reason, record it as infeasible with maximum constraint violation (displacement=9999, stress=9999). This lets Deb's rules naturally steer away from crashing regions. +4. **NEVER kill NX processes directly** โ€” LAC CRITICAL RULE. Use NXSessionManager.close_nx_if_allowed() only. If NX hangs, implement a timeout (e.g., 10 min per trial) and let NX time out gracefully. + +### 7.3 Risk: Mesh-Dependent Stress Results + +**Likelihood: Medium** (stress at hole edges is mesh-sensitive) + +**Mitigation:** +1. **Mesh convergence pre-study** โ€” Run baseline at 3 mesh densities. If stress varies >10%, refine mesh or use stress averaging region. +2. **Consistent mesh controls** โ€” Ensure NX applies the same mesh size/refinement strategy regardless of parameter values. The parametric model should have mesh controls tied to hole geometry. +3. **Stress extraction method** โ€” Use elemental nodal stress (conservative) per LAC success pattern. Note: pyNastran returns stress in kPa for NX kg-mm-s unit system โ€” **divide by 1000 for MPa**. + +### 7.4 Risk: Surrogate Temptation + +**Mitigation: DON'T DO IT (yet).** + +LAC lessons from the M1 Mirror project are unequivocal: +- V5 surrogate + L-BFGS was 22% **worse** than V6 pure TPE +- MLP surrogates have smooth gradients everywhere โ†’ L-BFGS descends to fake optima outside training distribution +- No uncertainty quantification = no way to detect out-of-distribution predictions + +With only 4 variables and affordable FEA (~2 min/trial), direct FEA evaluation via TPE is both simpler and more reliable. Surrogate methods should only be considered if: +- FEA solve time exceeds 30 minutes per trial, AND +- We have 100+ validated training points, AND +- We use ensemble surrogates with uncertainty quantification (SYS_16 protocol) + +### 7.5 Risk: Study Corruption + +**Mitigation:** LAC CRITICAL โ€” **Always copy working studies, never rewrite from scratch.** + +- Phase 2 study will be created by **copying** the Phase 1 study directory and adding optimization logic +- Never modify `run_optimization.py` in-place for a new phase โ€” copy to a new version +- Git-commit the study directory after each phase completion + +--- + +## 8. AtomizerSpec Draft + +See [`atomizer_spec_draft.json`](./atomizer_spec_draft.json) for the full JSON config. + +### 8.1 Key Configuration Decisions + +| Setting | Value | Rationale | +|---------|-------|-----------| +| `algorithm.phase1.type` | `LHS` | Space-filling DoE for landscape mapping | +| `algorithm.phase2.type` | `TPE` | Native mixed-integer, sample-efficient, LAC-proven | +| `hole_count.type` | `integer` | DEC-HB-003: true integer, no rounding | +| `constraint_handling` | `deb_feasibility_rules` | Best for infeasible baseline | +| `baseline_trial` | `enqueued` | LAC lesson: always validate baseline first | +| `penalty_config.method` | `deb_rules` | No penalty weight tuning needed | + +### 8.2 Extractor Requirements + +| ID | Type | Output | Source | Notes | +|----|------|--------|--------|-------| +| `ext_001` | `expression` | `mass` | NX expression `p173` | Direct read from NX | +| `ext_002` | `displacement` | `tip_displacement` | SOL 101 result sensor or .op2 parse | โš ๏ธ Need sensor setup or node ID | +| `ext_003` | `stress` | `max_von_mises` | SOL 101 elemental nodal | kPa โ†’ MPa conversion needed | + +### 8.3 Open Items for Spec Finalization + +Before this spec can be promoted from `_draft` to production: + +1. **Beam web length** โ€” Required to validate DV3 ร— DV4 geometric feasibility +2. **Displacement extraction method** โ€” Sensor in .sim, or node ID for .op2 parsing? +3. **Stress extraction scope** โ€” Whole model max, or specific element group? +4. **NX expression names confirmed** โ€” Verify `p173` is mass, confirm displacement/stress expression names +5. **Solver runtime benchmark** โ€” Time one SOL 101 run to refine compute estimates +6. **Corner test results** โ€” Validate model rebuilds at all 16 bound corners + +--- + +## 9. Execution Plan Summary + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ HYDROTECH BEAM OPTIMIZATION โ”‚ +โ”‚ Study: 01_doe_landscape โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ PRE-FLIGHT (before any trials) โ”‚ +โ”‚ โ”œโ”€โ”€ Validate baseline: run Trial 0, verify mass/disp/stress โ”‚ +โ”‚ โ”œโ”€โ”€ Corner tests: 16 extreme combinations, check NX rebuilds โ”‚ +โ”‚ โ”œโ”€โ”€ Mesh convergence: 3 density levels at baseline โ”‚ +โ”‚ โ””โ”€โ”€ Confirm extractors: mass, displacement, stress pipelines โ”‚ +โ”‚ โ”‚ +โ”‚ PHASE 1: DoE LANDSCAPE (51 trials) โ”‚ +โ”‚ โ”œโ”€โ”€ Trial 0: Baseline (enqueued) โ”‚ +โ”‚ โ”œโ”€โ”€ Trials 1-50: LHS with integer rounding for hole_count โ”‚ +โ”‚ โ”œโ”€โ”€ Analysis: sensitivity, interaction, feasibility mapping โ”‚ +โ”‚ โ””โ”€โ”€ GATE: โ‰ฅ5 feasible? NX success >80%? Proceed/escalate โ”‚ +โ”‚ โ”‚ +โ”‚ PHASE 2: TPE OPTIMIZATION (60-100 trials) โ”‚ +โ”‚ โ”œโ”€โ”€ Warm-start from best Phase 1 feasible point(s) โ”‚ +โ”‚ โ”œโ”€โ”€ Deb's feasibility rules for constraint handling โ”‚ +โ”‚ โ”œโ”€โ”€ Convergence check every 20 trials โ”‚ +โ”‚ โ””โ”€โ”€ Hard stop at 100 trials โ”‚ +โ”‚ โ”‚ +โ”‚ VALIDATION (3-5 trials) โ”‚ +โ”‚ โ”œโ”€โ”€ Re-run best design to confirm repeatability โ”‚ +โ”‚ โ”œโ”€โ”€ Perturb ยฑ5% on each variable to check sensitivity โ”‚ +โ”‚ โ””โ”€โ”€ Document final design with full NX results โ”‚ +โ”‚ โ”‚ +โ”‚ TOTAL: 114-156 NX evaluations | ~4-5 hours compute โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +--- + +## 10. LAC Lessons Incorporated + +| LAC Lesson | Source | How Applied | +|------------|--------|-------------| +| CMA-ES doesn't evaluate x0 first | Mirror V7 failure | Baseline enqueued as Trial 0 for both phases | +| Surrogate + L-BFGS = fake optima | Mirror V5 failure | No surrogates in this study; direct FEA only | +| Never kill NX processes directly | Dec 2025 incident | Timeout-based error handling; NXSessionManager only | +| Copy working studies, never rewrite | Mirror V5 failure | Phase 2 created by copying Phase 1 | +| pyNastran stress in kPa | Support arm success | Extractor divides by 1000 for MPa | +| CAD constraints can limit bounds | Mirror V13 (rib_thickness) | Pre-flight corner tests before DoE | +| Always include README.md | Repeated failures (Dec 2025, Jan 2026) | README.md created with study | +| Simple beats complex (TPE > surrogate) | Mirror V6 vs V5 | TPE selected over surrogate-based methods | + +--- + +*โšก Optimizer โ€” The algorithm is the strategy.* diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/README.md b/projects/hydrotech-beam/studies/01_doe_landscape/README.md index fa0362d1..222ddbd0 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/README.md +++ b/projects/hydrotech-beam/studies/01_doe_landscape/README.md @@ -1,235 +1,235 @@ -# Study: 01_doe_landscape โ€” Hydrotech Beam - -> See [../../README.md](../../README.md) for project overview. - -## Purpose - -Map the design space of the Hydrotech sandwich I-beam to identify feasible regions, characterize variable sensitivities, and prepare data for Phase 2 (TPE optimization). This is Phase 1 of the two-phase strategy (DEC-HB-002). - -## Quick Facts - -| Item | Value | -|------|-------| -| **Objective** | Minimize mass (kg) | -| **Constraints** | Tip displacement โ‰ค 10 mm, Von Mises stress โ‰ค 130 MPa | -| **Design variables** | 4 (3 continuous + 1 integer) | -| **Algorithm** | Phase 1: LHS DoE (50 trials + 1 baseline) | -| **Total budget** | 51 evaluations | -| **Constraint handling** | Deb's feasibility rules (Optuna constraint interface) | -| **Status** | โœ… Pipeline operational โ€” first results obtained 2026-02-11 | -| **Solver** | DesigncenterNX 2512 (NX Nastran SOL 101) | -| **Solve time** | ~12 s/trial (~10 min for full DOE) | -| **First results** | Displacement=17.93mm, Stress=111.9MPa | - -## Design Variables (confirmed via NX binary introspection) - -| ID | NX Expression | Range | Type | Baseline | Unit | -|----|--------------|-------|------|----------|------| -| DV1 | `beam_half_core_thickness` | 10โ€“40 | Continuous | 25.162 | mm | -| DV2 | `beam_face_thickness` | 10โ€“40 | Continuous | 21.504 | mm | -| DV3 | `holes_diameter` | 150โ€“450 | Continuous | 300 | mm | -| DV4 | `hole_count` | 5โ€“15 | Integer | 10 | โ€” | - -## Usage - -### Prerequisites - -```bash -pip install -r requirements.txt -``` - -Requires: Python 3.10+, optuna, scipy, numpy, pandas. - -### Development Run (stub solver) - -```bash -# From the study directory: -cd projects/hydrotech-beam/studies/01_doe_landscape/ - -# Run with synthetic results (for pipeline testing): -python run_doe.py --backend stub - -# With verbose logging: -python run_doe.py --backend stub -v - -# Custom study name: -python run_doe.py --backend stub --study-name my_test_run -``` - -### Production Run (NXOpen on Windows) - -```bash -# On dalidou (Windows node with DesigncenterNX 2512): -cd C:\Users\antoi\Atomizer\projects\hydrotech-beam\studies\01_doe_landscape -conda activate atomizer -python run_doe.py --backend nxopen --model-dir "../../models" -``` - -> โš ๏ธ `--model-dir` can be relative โ€” the code resolves it with `Path.resolve()`. NX requires fully resolved paths (no `..` components). - -### Resume Interrupted Study - -```bash -python run_doe.py --backend stub --resume --study-name hydrotech_beam_doe_phase1 -``` - -### CLI Options - -| Flag | Default | Description | -|------|---------|-------------| -| `--backend` | `stub` | `stub` (testing) or `nxopen` (real NX) | -| `--model-dir` | โ€” | Path to NX model files (required for `nxopen`) | -| `--study-name` | `hydrotech_beam_doe_phase1` | Optuna study name | -| `--n-samples` | 50 | Number of LHS sample points | -| `--seed` | 42 | Random seed for reproducibility | -| `--results-dir` | `results/` | Output directory | -| `--resume` | false | Resume existing study | -| `--clean` | false | Delete Optuna DB and start fresh (history DB preserved) | -| `-v` | false | Verbose (DEBUG) logging | - -## Files - -| File | Description | -|------|-------------| -| `run_doe.py` | Main entry point โ€” orchestrates the DoE study | -| `sampling.py` | LHS generation with stratified integer sampling | -| `geometric_checks.py` | Pre-flight geometric feasibility filter | -| `nx_interface.py` | AtomizerNXSolver wrapping optimization_engine | -| `iteration_manager.py` | Smart retention โ€” last 10 + best 3 with full data | -| `requirements.txt` | Python dependencies | -| `OPTIMIZATION_STRATEGY.md` | Full strategy document | -| `results/` | Output directory (CSV, JSON, Optuna DB) | -| `iterations/` | Per-trial archived outputs | - -## Output Files - -After a study run, `results/` will contain: - -| File | Description | -|------|-------------| -| `doe_results.csv` | All trials โ€” DVs, objectives, constraints, status | -| `doe_summary.json` | Study metadata, statistics, best feasible design | -| `optuna_study.db` | SQLite database (Optuna persistence, resume support) | -| `history.db` | **Persistent** SQLite โ€” all DVs, results, feasibility (survives `--clean`) | -| `history.csv` | CSV mirror of history DB | - -### Iteration Folders - -Each trial produces `iterations/iterNNN/` containing: - -| File | Description | -|------|-------------| -| `params.json` | Design variable values for this trial | -| `params.exp` | NX expression file used for this trial | -| `results.json` | Extracted results (mass, displacement, stress, feasibility) | -| `*.op2` | Nastran binary results (for post-processing) | -| `*.f06` | Nastran text output (for debugging) | - -**Smart retention policy** (runs every 5 iterations): -- Keep last 10 iterations with full data -- Keep best 3 feasible iterations with full data -- Strip model files from all others, keep solver outputs - -## Architecture - -``` -run_doe.py - โ”œโ”€โ”€ sampling.py Generate 50 LHS + 1 baseline - โ”‚ โ””โ”€โ”€ scipy.stats.qmc.LatinHypercube - โ”œโ”€โ”€ geometric_checks.py Pre-flight feasibility filter - โ”‚ โ”œโ”€โ”€ Hole overlap: span/(n-1) - d โ‰ฅ 30mm - โ”‚ โ””โ”€โ”€ Web clearance: 500 - 2ยทface - d > 0 - โ”œโ”€โ”€ nx_interface.py AtomizerNXSolver (wraps optimization_engine) - โ”‚ โ”œโ”€โ”€ NXStubSolver โ†’ synthetic results (development) - โ”‚ โ””โ”€โ”€ AtomizerNXSolver โ†’ real DesigncenterNX 2512 SOL 101 (production) - โ”œโ”€โ”€ iteration_manager.py Smart retention (last 10 + best 3) - โ”œโ”€โ”€ Optuna study - โ”‚ โ”œโ”€โ”€ SQLite storage (resume support) - โ”‚ โ”œโ”€โ”€ Enqueued trials (deterministic LHS) - โ”‚ โ””โ”€โ”€ Deb's feasibility rules (constraint interface) - โ””โ”€โ”€ history.db Persistent history (append-only, survives --clean) -``` - -## Pipeline per Trial - -``` -Trial N - โ”‚ - โ”œโ”€โ”€ 1. Suggest DVs (from enqueued LHS point) - โ”‚ - โ”œโ”€โ”€ 2. Geometric pre-check - โ”‚ โ”œโ”€โ”€ FAIL โ†’ record as infeasible, skip NX - โ”‚ โ””โ”€โ”€ PASS โ†“ - โ”‚ - โ”œโ”€โ”€ 3. Restore master model from backup - โ”‚ - โ”œโ”€โ”€ 4. NX evaluation (IN-PLACE in models/ directory) - โ”‚ โ”œโ”€โ”€ Write .exp file with trial DVs - โ”‚ โ”œโ”€โ”€ Open .sim (loads model chain) - โ”‚ โ”œโ”€โ”€ Import expressions, rebuild geometry - โ”‚ โ”œโ”€โ”€ Update FEM (remesh) - โ”‚ โ”œโ”€โ”€ Solve SOL 101 (~12s) - โ”‚ โ””โ”€โ”€ Extract: mass (p173 โ†’ _temp_mass.txt), displacement, stress (via OP2) - โ”‚ - โ”œโ”€โ”€ 5. Archive outputs to iterations/iterNNN/ - โ”‚ โ””โ”€โ”€ params.json, params.exp, results.json, OP2, F06 - โ”‚ - โ”œโ”€โ”€ 6. Record results + constraint violations - โ”‚ - โ”œโ”€โ”€ 7. Log to Optuna DB + history.db + CSV - โ”‚ - โ””โ”€โ”€ 8. Smart retention check (every 5 iterations) -``` - -## Phase 1 โ†’ Phase 2 Gate Criteria - -Before proceeding to Phase 2 (TPE optimization), these checks must pass: - -| Check | Threshold | Action if FAIL | -|-------|-----------|----------------| -| Feasible points found | โ‰ฅ 5 | Expand bounds or relax constraints (escalate to CEO) | -| NX solve success rate | โ‰ฅ 80% | Investigate failures, fix model, re-run | -| No systematic crashes at bounds | Visual check | Tighten bounds away from failure region | - -## Key Design Decisions - -- **Baseline as Trial 0** โ€” LAC lesson: always validate baseline first -- **Pre-flight geometric filter** โ€” catches infeasible geometry before NX (saves compute, avoids crashes) -- **Stratified integer sampling** โ€” ensures all 11 hole_count levels (5-15) are covered -- **Deb's feasibility rules** โ€” no penalty weight tuning; feasible always beats infeasible -- **SQLite persistence** โ€” study can be interrupted and resumed -- **No surrogates** โ€” LAC lesson: direct FEA via TPE beats surrogate + L-BFGS - -## NX Integration Notes - -**Solver:** DesigncenterNX 2512 (Siemens rebrand of NX). Install: `C:\Program Files\Siemens\DesigncenterNX2512`. - -The `nx_interface.py` module provides: -- **`NXStubSolver`** โ€” synthetic results from simplified beam mechanics (for pipeline testing) -- **`AtomizerNXSolver`** โ€” wraps `optimization_engine` NXSolver for real DesigncenterNX 2512 solves - -Expression names are exact from binary introspection. Critical: `beam_lenght` has a typo in NX (no 'h') โ€” use exact spelling. - -### Outputs extracted from NX: -| Output | Source | Unit | Notes | -|--------|--------|------|-------| -| Mass | Expression `p173` โ†’ `_temp_mass.txt` | kg | Journal extracts after solve | -| Tip displacement | OP2 parse via pyNastran | mm | Max Tz at free end | -| Max VM stress | OP2 parse via pyNastran | MPa | โš ๏ธ pyNastran returns kPa โ€” divide by 1000 | - -### Critical Lessons Learned (2026-02-11) -1. **Path resolution:** Use `Path.resolve()` NOT `Path.absolute()` on Windows. `.absolute()` doesn't resolve `..` components โ€” NX can't follow them. -2. **File references:** NX `.sim` files have absolute internal refs to `.fem`/`.prt`. Never copy model files to iteration folders โ€” solve in-place with backup/restore. -3. **NX version:** Must match exactly. Model files from 2512 won't load in 2412 ("Part file is from a newer version"). -4. **pyNastran:** Warns about unsupported NX version 2512 but works fine. Safe to ignore. - -## References - -- [OPTIMIZATION_STRATEGY.md](./OPTIMIZATION_STRATEGY.md) โ€” Full strategy document -- [../../BREAKDOWN.md](../../BREAKDOWN.md) โ€” Tech Lead's technical analysis -- [../../DECISIONS.md](../../DECISIONS.md) โ€” Decision log -- [../../CONTEXT.md](../../CONTEXT.md) โ€” Project context & expression map - ---- - -*Code: ๐Ÿ—๏ธ Study Builder (Technical Lead) | Strategy: โšก Optimizer Agent | Updated: 2026-02-11* +# Study: 01_doe_landscape โ€” Hydrotech Beam + +> See [../../README.md](../../README.md) for project overview. + +## Purpose + +Map the design space of the Hydrotech sandwich I-beam to identify feasible regions, characterize variable sensitivities, and prepare data for Phase 2 (TPE optimization). This is Phase 1 of the two-phase strategy (DEC-HB-002). + +## Quick Facts + +| Item | Value | +|------|-------| +| **Objective** | Minimize mass (kg) | +| **Constraints** | Tip displacement โ‰ค 10 mm, Von Mises stress โ‰ค 130 MPa | +| **Design variables** | 4 (3 continuous + 1 integer) | +| **Algorithm** | Phase 1: LHS DoE (50 trials + 1 baseline) | +| **Total budget** | 51 evaluations | +| **Constraint handling** | Deb's feasibility rules (Optuna constraint interface) | +| **Status** | โœ… Pipeline operational โ€” first results obtained 2026-02-11 | +| **Solver** | DesigncenterNX 2512 (NX Nastran SOL 101) | +| **Solve time** | ~12 s/trial (~10 min for full DOE) | +| **First results** | Displacement=17.93mm, Stress=111.9MPa | + +## Design Variables (confirmed via NX binary introspection) + +| ID | NX Expression | Range | Type | Baseline | Unit | +|----|--------------|-------|------|----------|------| +| DV1 | `beam_half_core_thickness` | 10โ€“40 | Continuous | 25.162 | mm | +| DV2 | `beam_face_thickness` | 10โ€“40 | Continuous | 21.504 | mm | +| DV3 | `holes_diameter` | 150โ€“450 | Continuous | 300 | mm | +| DV4 | `hole_count` | 5โ€“15 | Integer | 10 | โ€” | + +## Usage + +### Prerequisites + +```bash +pip install -r requirements.txt +``` + +Requires: Python 3.10+, optuna, scipy, numpy, pandas. + +### Development Run (stub solver) + +```bash +# From the study directory: +cd projects/hydrotech-beam/studies/01_doe_landscape/ + +# Run with synthetic results (for pipeline testing): +python run_doe.py --backend stub + +# With verbose logging: +python run_doe.py --backend stub -v + +# Custom study name: +python run_doe.py --backend stub --study-name my_test_run +``` + +### Production Run (NXOpen on Windows) + +```bash +# On dalidou (Windows node with DesigncenterNX 2512): +cd C:\Users\antoi\Atomizer\projects\hydrotech-beam\studies\01_doe_landscape +conda activate atomizer +python run_doe.py --backend nxopen --model-dir "../../models" +``` + +> โš ๏ธ `--model-dir` can be relative โ€” the code resolves it with `Path.resolve()`. NX requires fully resolved paths (no `..` components). + +### Resume Interrupted Study + +```bash +python run_doe.py --backend stub --resume --study-name hydrotech_beam_doe_phase1 +``` + +### CLI Options + +| Flag | Default | Description | +|------|---------|-------------| +| `--backend` | `stub` | `stub` (testing) or `nxopen` (real NX) | +| `--model-dir` | โ€” | Path to NX model files (required for `nxopen`) | +| `--study-name` | `hydrotech_beam_doe_phase1` | Optuna study name | +| `--n-samples` | 50 | Number of LHS sample points | +| `--seed` | 42 | Random seed for reproducibility | +| `--results-dir` | `results/` | Output directory | +| `--resume` | false | Resume existing study | +| `--clean` | false | Delete Optuna DB and start fresh (history DB preserved) | +| `-v` | false | Verbose (DEBUG) logging | + +## Files + +| File | Description | +|------|-------------| +| `run_doe.py` | Main entry point โ€” orchestrates the DoE study | +| `sampling.py` | LHS generation with stratified integer sampling | +| `geometric_checks.py` | Pre-flight geometric feasibility filter | +| `nx_interface.py` | AtomizerNXSolver wrapping optimization_engine | +| `iteration_manager.py` | Smart retention โ€” last 10 + best 3 with full data | +| `requirements.txt` | Python dependencies | +| `OPTIMIZATION_STRATEGY.md` | Full strategy document | +| `results/` | Output directory (CSV, JSON, Optuna DB) | +| `iterations/` | Per-trial archived outputs | + +## Output Files + +After a study run, `results/` will contain: + +| File | Description | +|------|-------------| +| `doe_results.csv` | All trials โ€” DVs, objectives, constraints, status | +| `doe_summary.json` | Study metadata, statistics, best feasible design | +| `optuna_study.db` | SQLite database (Optuna persistence, resume support) | +| `history.db` | **Persistent** SQLite โ€” all DVs, results, feasibility (survives `--clean`) | +| `history.csv` | CSV mirror of history DB | + +### Iteration Folders + +Each trial produces `iterations/iterNNN/` containing: + +| File | Description | +|------|-------------| +| `params.json` | Design variable values for this trial | +| `params.exp` | NX expression file used for this trial | +| `results.json` | Extracted results (mass, displacement, stress, feasibility) | +| `*.op2` | Nastran binary results (for post-processing) | +| `*.f06` | Nastran text output (for debugging) | + +**Smart retention policy** (runs every 5 iterations): +- Keep last 10 iterations with full data +- Keep best 3 feasible iterations with full data +- Strip model files from all others, keep solver outputs + +## Architecture + +``` +run_doe.py + โ”œโ”€โ”€ sampling.py Generate 50 LHS + 1 baseline + โ”‚ โ””โ”€โ”€ scipy.stats.qmc.LatinHypercube + โ”œโ”€โ”€ geometric_checks.py Pre-flight feasibility filter + โ”‚ โ”œโ”€โ”€ Hole overlap: span/(n-1) - d โ‰ฅ 30mm + โ”‚ โ””โ”€โ”€ Web clearance: 500 - 2ยทface - d > 0 + โ”œโ”€โ”€ nx_interface.py AtomizerNXSolver (wraps optimization_engine) + โ”‚ โ”œโ”€โ”€ NXStubSolver โ†’ synthetic results (development) + โ”‚ โ””โ”€โ”€ AtomizerNXSolver โ†’ real DesigncenterNX 2512 SOL 101 (production) + โ”œโ”€โ”€ iteration_manager.py Smart retention (last 10 + best 3) + โ”œโ”€โ”€ Optuna study + โ”‚ โ”œโ”€โ”€ SQLite storage (resume support) + โ”‚ โ”œโ”€โ”€ Enqueued trials (deterministic LHS) + โ”‚ โ””โ”€โ”€ Deb's feasibility rules (constraint interface) + โ””โ”€โ”€ history.db Persistent history (append-only, survives --clean) +``` + +## Pipeline per Trial + +``` +Trial N + โ”‚ + โ”œโ”€โ”€ 1. Suggest DVs (from enqueued LHS point) + โ”‚ + โ”œโ”€โ”€ 2. Geometric pre-check + โ”‚ โ”œโ”€โ”€ FAIL โ†’ record as infeasible, skip NX + โ”‚ โ””โ”€โ”€ PASS โ†“ + โ”‚ + โ”œโ”€โ”€ 3. Restore master model from backup + โ”‚ + โ”œโ”€โ”€ 4. NX evaluation (IN-PLACE in models/ directory) + โ”‚ โ”œโ”€โ”€ Write .exp file with trial DVs + โ”‚ โ”œโ”€โ”€ Open .sim (loads model chain) + โ”‚ โ”œโ”€โ”€ Import expressions, rebuild geometry + โ”‚ โ”œโ”€โ”€ Update FEM (remesh) + โ”‚ โ”œโ”€โ”€ Solve SOL 101 (~12s) + โ”‚ โ””โ”€โ”€ Extract: mass (p173 โ†’ _temp_mass.txt), displacement, stress (via OP2) + โ”‚ + โ”œโ”€โ”€ 5. Archive outputs to iterations/iterNNN/ + โ”‚ โ””โ”€โ”€ params.json, params.exp, results.json, OP2, F06 + โ”‚ + โ”œโ”€โ”€ 6. Record results + constraint violations + โ”‚ + โ”œโ”€โ”€ 7. Log to Optuna DB + history.db + CSV + โ”‚ + โ””โ”€โ”€ 8. Smart retention check (every 5 iterations) +``` + +## Phase 1 โ†’ Phase 2 Gate Criteria + +Before proceeding to Phase 2 (TPE optimization), these checks must pass: + +| Check | Threshold | Action if FAIL | +|-------|-----------|----------------| +| Feasible points found | โ‰ฅ 5 | Expand bounds or relax constraints (escalate to CEO) | +| NX solve success rate | โ‰ฅ 80% | Investigate failures, fix model, re-run | +| No systematic crashes at bounds | Visual check | Tighten bounds away from failure region | + +## Key Design Decisions + +- **Baseline as Trial 0** โ€” LAC lesson: always validate baseline first +- **Pre-flight geometric filter** โ€” catches infeasible geometry before NX (saves compute, avoids crashes) +- **Stratified integer sampling** โ€” ensures all 11 hole_count levels (5-15) are covered +- **Deb's feasibility rules** โ€” no penalty weight tuning; feasible always beats infeasible +- **SQLite persistence** โ€” study can be interrupted and resumed +- **No surrogates** โ€” LAC lesson: direct FEA via TPE beats surrogate + L-BFGS + +## NX Integration Notes + +**Solver:** DesigncenterNX 2512 (Siemens rebrand of NX). Install: `C:\Program Files\Siemens\DesigncenterNX2512`. + +The `nx_interface.py` module provides: +- **`NXStubSolver`** โ€” synthetic results from simplified beam mechanics (for pipeline testing) +- **`AtomizerNXSolver`** โ€” wraps `optimization_engine` NXSolver for real DesigncenterNX 2512 solves + +Expression names are exact from binary introspection. Critical: `beam_lenght` has a typo in NX (no 'h') โ€” use exact spelling. + +### Outputs extracted from NX: +| Output | Source | Unit | Notes | +|--------|--------|------|-------| +| Mass | Expression `p173` โ†’ `_temp_mass.txt` | kg | Journal extracts after solve | +| Tip displacement | OP2 parse via pyNastran | mm | Max Tz at free end | +| Max VM stress | OP2 parse via pyNastran | MPa | โš ๏ธ pyNastran returns kPa โ€” divide by 1000 | + +### Critical Lessons Learned (2026-02-11) +1. **Path resolution:** Use `Path.resolve()` NOT `Path.absolute()` on Windows. `.absolute()` doesn't resolve `..` components โ€” NX can't follow them. +2. **File references:** NX `.sim` files have absolute internal refs to `.fem`/`.prt`. Never copy model files to iteration folders โ€” solve in-place with backup/restore. +3. **NX version:** Must match exactly. Model files from 2512 won't load in 2412 ("Part file is from a newer version"). +4. **pyNastran:** Warns about unsupported NX version 2512 but works fine. Safe to ignore. + +## References + +- [OPTIMIZATION_STRATEGY.md](./OPTIMIZATION_STRATEGY.md) โ€” Full strategy document +- [../../BREAKDOWN.md](../../BREAKDOWN.md) โ€” Tech Lead's technical analysis +- [../../DECISIONS.md](../../DECISIONS.md) โ€” Decision log +- [../../CONTEXT.md](../../CONTEXT.md) โ€” Project context & expression map + +--- + +*Code: ๐Ÿ—๏ธ Study Builder (Technical Lead) | Strategy: โšก Optimizer Agent | Updated: 2026-02-11* diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/README.sync-conflict-20260214-191752-VBCUD7Z.md b/projects/hydrotech-beam/studies/01_doe_landscape/README.sync-conflict-20260214-191752-VBCUD7Z.md new file mode 100644 index 00000000..fa0362d1 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/README.sync-conflict-20260214-191752-VBCUD7Z.md @@ -0,0 +1,235 @@ +# Study: 01_doe_landscape โ€” Hydrotech Beam + +> See [../../README.md](../../README.md) for project overview. + +## Purpose + +Map the design space of the Hydrotech sandwich I-beam to identify feasible regions, characterize variable sensitivities, and prepare data for Phase 2 (TPE optimization). This is Phase 1 of the two-phase strategy (DEC-HB-002). + +## Quick Facts + +| Item | Value | +|------|-------| +| **Objective** | Minimize mass (kg) | +| **Constraints** | Tip displacement โ‰ค 10 mm, Von Mises stress โ‰ค 130 MPa | +| **Design variables** | 4 (3 continuous + 1 integer) | +| **Algorithm** | Phase 1: LHS DoE (50 trials + 1 baseline) | +| **Total budget** | 51 evaluations | +| **Constraint handling** | Deb's feasibility rules (Optuna constraint interface) | +| **Status** | โœ… Pipeline operational โ€” first results obtained 2026-02-11 | +| **Solver** | DesigncenterNX 2512 (NX Nastran SOL 101) | +| **Solve time** | ~12 s/trial (~10 min for full DOE) | +| **First results** | Displacement=17.93mm, Stress=111.9MPa | + +## Design Variables (confirmed via NX binary introspection) + +| ID | NX Expression | Range | Type | Baseline | Unit | +|----|--------------|-------|------|----------|------| +| DV1 | `beam_half_core_thickness` | 10โ€“40 | Continuous | 25.162 | mm | +| DV2 | `beam_face_thickness` | 10โ€“40 | Continuous | 21.504 | mm | +| DV3 | `holes_diameter` | 150โ€“450 | Continuous | 300 | mm | +| DV4 | `hole_count` | 5โ€“15 | Integer | 10 | โ€” | + +## Usage + +### Prerequisites + +```bash +pip install -r requirements.txt +``` + +Requires: Python 3.10+, optuna, scipy, numpy, pandas. + +### Development Run (stub solver) + +```bash +# From the study directory: +cd projects/hydrotech-beam/studies/01_doe_landscape/ + +# Run with synthetic results (for pipeline testing): +python run_doe.py --backend stub + +# With verbose logging: +python run_doe.py --backend stub -v + +# Custom study name: +python run_doe.py --backend stub --study-name my_test_run +``` + +### Production Run (NXOpen on Windows) + +```bash +# On dalidou (Windows node with DesigncenterNX 2512): +cd C:\Users\antoi\Atomizer\projects\hydrotech-beam\studies\01_doe_landscape +conda activate atomizer +python run_doe.py --backend nxopen --model-dir "../../models" +``` + +> โš ๏ธ `--model-dir` can be relative โ€” the code resolves it with `Path.resolve()`. NX requires fully resolved paths (no `..` components). + +### Resume Interrupted Study + +```bash +python run_doe.py --backend stub --resume --study-name hydrotech_beam_doe_phase1 +``` + +### CLI Options + +| Flag | Default | Description | +|------|---------|-------------| +| `--backend` | `stub` | `stub` (testing) or `nxopen` (real NX) | +| `--model-dir` | โ€” | Path to NX model files (required for `nxopen`) | +| `--study-name` | `hydrotech_beam_doe_phase1` | Optuna study name | +| `--n-samples` | 50 | Number of LHS sample points | +| `--seed` | 42 | Random seed for reproducibility | +| `--results-dir` | `results/` | Output directory | +| `--resume` | false | Resume existing study | +| `--clean` | false | Delete Optuna DB and start fresh (history DB preserved) | +| `-v` | false | Verbose (DEBUG) logging | + +## Files + +| File | Description | +|------|-------------| +| `run_doe.py` | Main entry point โ€” orchestrates the DoE study | +| `sampling.py` | LHS generation with stratified integer sampling | +| `geometric_checks.py` | Pre-flight geometric feasibility filter | +| `nx_interface.py` | AtomizerNXSolver wrapping optimization_engine | +| `iteration_manager.py` | Smart retention โ€” last 10 + best 3 with full data | +| `requirements.txt` | Python dependencies | +| `OPTIMIZATION_STRATEGY.md` | Full strategy document | +| `results/` | Output directory (CSV, JSON, Optuna DB) | +| `iterations/` | Per-trial archived outputs | + +## Output Files + +After a study run, `results/` will contain: + +| File | Description | +|------|-------------| +| `doe_results.csv` | All trials โ€” DVs, objectives, constraints, status | +| `doe_summary.json` | Study metadata, statistics, best feasible design | +| `optuna_study.db` | SQLite database (Optuna persistence, resume support) | +| `history.db` | **Persistent** SQLite โ€” all DVs, results, feasibility (survives `--clean`) | +| `history.csv` | CSV mirror of history DB | + +### Iteration Folders + +Each trial produces `iterations/iterNNN/` containing: + +| File | Description | +|------|-------------| +| `params.json` | Design variable values for this trial | +| `params.exp` | NX expression file used for this trial | +| `results.json` | Extracted results (mass, displacement, stress, feasibility) | +| `*.op2` | Nastran binary results (for post-processing) | +| `*.f06` | Nastran text output (for debugging) | + +**Smart retention policy** (runs every 5 iterations): +- Keep last 10 iterations with full data +- Keep best 3 feasible iterations with full data +- Strip model files from all others, keep solver outputs + +## Architecture + +``` +run_doe.py + โ”œโ”€โ”€ sampling.py Generate 50 LHS + 1 baseline + โ”‚ โ””โ”€โ”€ scipy.stats.qmc.LatinHypercube + โ”œโ”€โ”€ geometric_checks.py Pre-flight feasibility filter + โ”‚ โ”œโ”€โ”€ Hole overlap: span/(n-1) - d โ‰ฅ 30mm + โ”‚ โ””โ”€โ”€ Web clearance: 500 - 2ยทface - d > 0 + โ”œโ”€โ”€ nx_interface.py AtomizerNXSolver (wraps optimization_engine) + โ”‚ โ”œโ”€โ”€ NXStubSolver โ†’ synthetic results (development) + โ”‚ โ””โ”€โ”€ AtomizerNXSolver โ†’ real DesigncenterNX 2512 SOL 101 (production) + โ”œโ”€โ”€ iteration_manager.py Smart retention (last 10 + best 3) + โ”œโ”€โ”€ Optuna study + โ”‚ โ”œโ”€โ”€ SQLite storage (resume support) + โ”‚ โ”œโ”€โ”€ Enqueued trials (deterministic LHS) + โ”‚ โ””โ”€โ”€ Deb's feasibility rules (constraint interface) + โ””โ”€โ”€ history.db Persistent history (append-only, survives --clean) +``` + +## Pipeline per Trial + +``` +Trial N + โ”‚ + โ”œโ”€โ”€ 1. Suggest DVs (from enqueued LHS point) + โ”‚ + โ”œโ”€โ”€ 2. Geometric pre-check + โ”‚ โ”œโ”€โ”€ FAIL โ†’ record as infeasible, skip NX + โ”‚ โ””โ”€โ”€ PASS โ†“ + โ”‚ + โ”œโ”€โ”€ 3. Restore master model from backup + โ”‚ + โ”œโ”€โ”€ 4. NX evaluation (IN-PLACE in models/ directory) + โ”‚ โ”œโ”€โ”€ Write .exp file with trial DVs + โ”‚ โ”œโ”€โ”€ Open .sim (loads model chain) + โ”‚ โ”œโ”€โ”€ Import expressions, rebuild geometry + โ”‚ โ”œโ”€โ”€ Update FEM (remesh) + โ”‚ โ”œโ”€โ”€ Solve SOL 101 (~12s) + โ”‚ โ””โ”€โ”€ Extract: mass (p173 โ†’ _temp_mass.txt), displacement, stress (via OP2) + โ”‚ + โ”œโ”€โ”€ 5. Archive outputs to iterations/iterNNN/ + โ”‚ โ””โ”€โ”€ params.json, params.exp, results.json, OP2, F06 + โ”‚ + โ”œโ”€โ”€ 6. Record results + constraint violations + โ”‚ + โ”œโ”€โ”€ 7. Log to Optuna DB + history.db + CSV + โ”‚ + โ””โ”€โ”€ 8. Smart retention check (every 5 iterations) +``` + +## Phase 1 โ†’ Phase 2 Gate Criteria + +Before proceeding to Phase 2 (TPE optimization), these checks must pass: + +| Check | Threshold | Action if FAIL | +|-------|-----------|----------------| +| Feasible points found | โ‰ฅ 5 | Expand bounds or relax constraints (escalate to CEO) | +| NX solve success rate | โ‰ฅ 80% | Investigate failures, fix model, re-run | +| No systematic crashes at bounds | Visual check | Tighten bounds away from failure region | + +## Key Design Decisions + +- **Baseline as Trial 0** โ€” LAC lesson: always validate baseline first +- **Pre-flight geometric filter** โ€” catches infeasible geometry before NX (saves compute, avoids crashes) +- **Stratified integer sampling** โ€” ensures all 11 hole_count levels (5-15) are covered +- **Deb's feasibility rules** โ€” no penalty weight tuning; feasible always beats infeasible +- **SQLite persistence** โ€” study can be interrupted and resumed +- **No surrogates** โ€” LAC lesson: direct FEA via TPE beats surrogate + L-BFGS + +## NX Integration Notes + +**Solver:** DesigncenterNX 2512 (Siemens rebrand of NX). Install: `C:\Program Files\Siemens\DesigncenterNX2512`. + +The `nx_interface.py` module provides: +- **`NXStubSolver`** โ€” synthetic results from simplified beam mechanics (for pipeline testing) +- **`AtomizerNXSolver`** โ€” wraps `optimization_engine` NXSolver for real DesigncenterNX 2512 solves + +Expression names are exact from binary introspection. Critical: `beam_lenght` has a typo in NX (no 'h') โ€” use exact spelling. + +### Outputs extracted from NX: +| Output | Source | Unit | Notes | +|--------|--------|------|-------| +| Mass | Expression `p173` โ†’ `_temp_mass.txt` | kg | Journal extracts after solve | +| Tip displacement | OP2 parse via pyNastran | mm | Max Tz at free end | +| Max VM stress | OP2 parse via pyNastran | MPa | โš ๏ธ pyNastran returns kPa โ€” divide by 1000 | + +### Critical Lessons Learned (2026-02-11) +1. **Path resolution:** Use `Path.resolve()` NOT `Path.absolute()` on Windows. `.absolute()` doesn't resolve `..` components โ€” NX can't follow them. +2. **File references:** NX `.sim` files have absolute internal refs to `.fem`/`.prt`. Never copy model files to iteration folders โ€” solve in-place with backup/restore. +3. **NX version:** Must match exactly. Model files from 2512 won't load in 2412 ("Part file is from a newer version"). +4. **pyNastran:** Warns about unsupported NX version 2512 but works fine. Safe to ignore. + +## References + +- [OPTIMIZATION_STRATEGY.md](./OPTIMIZATION_STRATEGY.md) โ€” Full strategy document +- [../../BREAKDOWN.md](../../BREAKDOWN.md) โ€” Tech Lead's technical analysis +- [../../DECISIONS.md](../../DECISIONS.md) โ€” Decision log +- [../../CONTEXT.md](../../CONTEXT.md) โ€” Project context & expression map + +--- + +*Code: ๐Ÿ—๏ธ Study Builder (Technical Lead) | Strategy: โšก Optimizer Agent | Updated: 2026-02-11* diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/_model_backup/README.md b/projects/hydrotech-beam/studies/01_doe_landscape/_model_backup/README.md index 7e99d3c5..c8e52f51 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/_model_backup/README.md +++ b/projects/hydrotech-beam/studies/01_doe_landscape/_model_backup/README.md @@ -1,25 +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 +# 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 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/_model_backup/README.sync-conflict-20260214-191802-VBCUD7Z.md b/projects/hydrotech-beam/studies/01_doe_landscape/_model_backup/README.sync-conflict-20260214-191802-VBCUD7Z.md new file mode 100644 index 00000000..7e99d3c5 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/_model_backup/README.sync-conflict-20260214-191802-VBCUD7Z.md @@ -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 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/atomizer_spec_draft.json b/projects/hydrotech-beam/studies/01_doe_landscape/atomizer_spec_draft.json index 64d7df36..d053ab1e 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/atomizer_spec_draft.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/atomizer_spec_draft.json @@ -1,302 +1,302 @@ -{ - "meta": { - "version": "2.0", - "created": "2026-02-09T12:00:00Z", - "modified": "2026-02-09T12:00:00Z", - "created_by": "optimizer-agent", - "modified_by": "optimizer-agent", - "study_name": "01_doe_landscape", - "description": "Hydrotech Beam โ€” DoE landscape mapping + TPE optimization. Minimize mass subject to displacement and stress constraints.", - "tags": ["hydrotech-beam", "doe", "landscape", "tpe", "single-objective"] - }, - "model": { - "sim": { - "path": "models/Beam_sim1.sim", - "solver": "nastran" - }, - "part": "models/Beam.prt", - "fem": "models/Beam_fem1.fem", - "idealized": "models/Beam_fem1_i.prt" - }, - "design_variables": [ - { - "id": "dv_001", - "name": "beam_half_core_thickness", - "expression_name": "beam_half_core_thickness", - "type": "continuous", - "bounds": { - "min": 10, - "max": 40 - }, - "baseline": 20, - "units": "mm", - "enabled": true, - "description": "Core half-thickness. Stiffness scales ~quadratically via sandwich effect (lever arm). Also adds core mass linearly." - }, - { - "id": "dv_002", - "name": "beam_face_thickness", - "expression_name": "beam_face_thickness", - "type": "continuous", - "bounds": { - "min": 10, - "max": 40 - }, - "baseline": 20, - "units": "mm", - "enabled": true, - "description": "Face sheet thickness. Primary bending stiffness contributor AND primary mass contributor. Key trade-off variable." - }, - { - "id": "dv_003", - "name": "holes_diameter", - "expression_name": "holes_diameter", - "type": "continuous", - "bounds": { - "min": 150, - "max": 450 - }, - "baseline": 300, - "units": "mm", - "enabled": true, - "description": "Lightening hole diameter. Mass reduction scales with dยฒ. Stress concentration scales with hole size. Geometric feasibility depends on beam web length." - }, - { - "id": "dv_004", - "name": "hole_count", - "expression_name": "hole_count", - "type": "integer", - "bounds": { - "min": 5, - "max": 15 - }, - "baseline": 10, - "units": "", - "enabled": true, - "description": "Number of lightening holes in the web. Integer variable (11 levels). Interacts strongly with holes_diameter for geometric feasibility." - } - ], - "extractors": [ - { - "id": "ext_001", - "name": "Mass Extractor", - "type": "expression", - "config": { - "expression_name": "p173", - "description": "Total beam mass from NX expression" - }, - "outputs": [ - { - "name": "mass", - "metric": "total", - "units": "kg" - } - ] - }, - { - "id": "ext_002", - "name": "Displacement Extractor", - "type": "displacement", - "config": { - "method": "result_sensor_or_op2", - "component": "magnitude", - "location": "beam_tip", - "description": "Tip displacement magnitude from SOL 101. Preferred: NX result sensor. Fallback: parse .op2 at tip node ID.", - "TODO": "Confirm displacement sensor exists in Beam_sim1.sim OR identify tip node ID" - }, - "outputs": [ - { - "name": "tip_displacement", - "metric": "max_magnitude", - "units": "mm" - } - ] - }, - { - "id": "ext_003", - "name": "Stress Extractor", - "type": "stress", - "config": { - "method": "op2_elemental_nodal", - "stress_type": "von_mises", - "scope": "all_elements", - "unit_conversion": "kPa_to_MPa", - "description": "Max von Mises stress (elemental nodal) from SOL 101. pyNastran returns kPa for NX kg-mm-s units โ€” divide by 1000 for MPa.", - "TODO": "Verify element types in model (CQUAD4? CTETRA? CHEXA?) and confirm stress scope" - }, - "outputs": [ - { - "name": "max_von_mises", - "metric": "max", - "units": "MPa" - } - ] - } - ], - "objectives": [ - { - "id": "obj_001", - "name": "Minimize beam mass", - "direction": "minimize", - "weight": 1.0, - "source": { - "extractor_id": "ext_001", - "output_name": "mass" - }, - "units": "kg" - } - ], - "constraints": [ - { - "id": "con_001", - "name": "Tip displacement limit", - "type": "hard", - "operator": "<=", - "threshold": 10.0, - "source": { - "extractor_id": "ext_002", - "output_name": "tip_displacement" - }, - "penalty_config": { - "method": "deb_rules", - "description": "Deb's feasibility rules: feasible always beats infeasible; among infeasible, lower violation wins" - }, - "margin_buffer": { - "optimizer_target": 9.5, - "hard_limit": 10.0, - "description": "5% inner margin during optimization to account for numerical noise" - } - }, - { - "id": "con_002", - "name": "Von Mises stress limit", - "type": "hard", - "operator": "<=", - "threshold": 130.0, - "source": { - "extractor_id": "ext_003", - "output_name": "max_von_mises" - }, - "penalty_config": { - "method": "deb_rules", - "description": "Deb's feasibility rules: feasible always beats infeasible; among infeasible, lower violation wins" - }, - "margin_buffer": { - "optimizer_target": 123.5, - "hard_limit": 130.0, - "description": "5% inner margin during optimization to account for mesh sensitivity" - } - } - ], - "optimization": { - "phases": [ - { - "id": "phase_1", - "name": "DoE Landscape Mapping", - "algorithm": { - "type": "LHS", - "config": { - "criterion": "maximin", - "integer_handling": "round_nearest_stratified", - "seed": 42 - } - }, - "budget": { - "max_trials": 50, - "baseline_enqueued": true, - "total_with_baseline": 51 - }, - "purpose": "Map design space, identify feasible region, assess sensitivities and interactions" - }, - { - "id": "phase_2", - "name": "TPE Optimization", - "algorithm": { - "type": "TPE", - "config": { - "sampler": "TPESampler", - "n_startup_trials": 0, - "warm_start_from": "phase_1_best_feasible", - "seed": 42, - "constraint_handling": "deb_feasibility_rules" - } - }, - "budget": { - "min_trials": 60, - "max_trials": 100, - "adaptive": true - }, - "convergence": { - "stall_window": 20, - "stall_threshold_pct": 1.0, - "min_trials_before_check": 60 - }, - "purpose": "Directed optimization to converge on minimum-mass feasible design" - } - ], - "total_budget": { - "min": 114, - "max": 156, - "estimated_hours": "4-5" - }, - "baseline_trial": { - "enqueue": true, - "values": { - "beam_half_core_thickness": 20, - "beam_face_thickness": 20, - "holes_diameter": 300, - "hole_count": 10 - }, - "expected_results": { - "mass_kg": 974, - "tip_displacement_mm": 22, - "max_von_mises_mpa": "UNKNOWN โ€” must measure" - } - } - }, - "error_handling": { - "trial_timeout_seconds": 600, - "on_nx_rebuild_failure": "record_infeasible_max_violation", - "on_solver_failure": "record_infeasible_max_violation", - "on_extraction_failure": "record_infeasible_max_violation", - "max_consecutive_failures": 5, - "max_failure_rate_pct": 30, - "nx_process_management": "NEVER kill NX directly. Use NXSessionManager.close_nx_if_allowed() only." - }, - "pre_flight_checks": [ - { - "id": "pf_001", - "name": "Baseline validation", - "description": "Run Trial 0 with baseline parameters, verify mass โ‰ˆ 974 kg and displacement โ‰ˆ 22 mm" - }, - { - "id": "pf_002", - "name": "Corner tests", - "description": "Test 16 corner combinations (min/max for each DV). Verify NX rebuilds and solves at all corners." - }, - { - "id": "pf_003", - "name": "Mesh convergence", - "description": "Run baseline at 3 mesh densities. Verify stress convergence within 10%." - }, - { - "id": "pf_004", - "name": "Extractor validation", - "description": "Confirm mass, displacement, and stress extractors return correct values at baseline." - }, - { - "id": "pf_005", - "name": "Geometric feasibility", - "description": "Determine beam web length. Verify max(hole_count) ร— max(holes_diameter) fits. Add ligament constraint if needed." - } - ], - "open_items": [ - "Beam web length needed for geometric feasibility validation (holes_diameter ร— hole_count vs available length)", - "Displacement extraction method: result sensor in .sim or .op2 node ID parsing?", - "Stress extraction scope: whole model or specific element group?", - "Verify NX expression name 'p173' maps to mass", - "Benchmark single SOL 101 runtime to refine compute estimates", - "Confirm baseline stress value (currently unknown)", - "Clarify relationship between core/face thickness DVs and web height where holes are placed" - ] -} +{ + "meta": { + "version": "2.0", + "created": "2026-02-09T12:00:00Z", + "modified": "2026-02-09T12:00:00Z", + "created_by": "optimizer-agent", + "modified_by": "optimizer-agent", + "study_name": "01_doe_landscape", + "description": "Hydrotech Beam โ€” DoE landscape mapping + TPE optimization. Minimize mass subject to displacement and stress constraints.", + "tags": ["hydrotech-beam", "doe", "landscape", "tpe", "single-objective"] + }, + "model": { + "sim": { + "path": "models/Beam_sim1.sim", + "solver": "nastran" + }, + "part": "models/Beam.prt", + "fem": "models/Beam_fem1.fem", + "idealized": "models/Beam_fem1_i.prt" + }, + "design_variables": [ + { + "id": "dv_001", + "name": "beam_half_core_thickness", + "expression_name": "beam_half_core_thickness", + "type": "continuous", + "bounds": { + "min": 10, + "max": 40 + }, + "baseline": 20, + "units": "mm", + "enabled": true, + "description": "Core half-thickness. Stiffness scales ~quadratically via sandwich effect (lever arm). Also adds core mass linearly." + }, + { + "id": "dv_002", + "name": "beam_face_thickness", + "expression_name": "beam_face_thickness", + "type": "continuous", + "bounds": { + "min": 10, + "max": 40 + }, + "baseline": 20, + "units": "mm", + "enabled": true, + "description": "Face sheet thickness. Primary bending stiffness contributor AND primary mass contributor. Key trade-off variable." + }, + { + "id": "dv_003", + "name": "holes_diameter", + "expression_name": "holes_diameter", + "type": "continuous", + "bounds": { + "min": 150, + "max": 450 + }, + "baseline": 300, + "units": "mm", + "enabled": true, + "description": "Lightening hole diameter. Mass reduction scales with dยฒ. Stress concentration scales with hole size. Geometric feasibility depends on beam web length." + }, + { + "id": "dv_004", + "name": "hole_count", + "expression_name": "hole_count", + "type": "integer", + "bounds": { + "min": 5, + "max": 15 + }, + "baseline": 10, + "units": "", + "enabled": true, + "description": "Number of lightening holes in the web. Integer variable (11 levels). Interacts strongly with holes_diameter for geometric feasibility." + } + ], + "extractors": [ + { + "id": "ext_001", + "name": "Mass Extractor", + "type": "expression", + "config": { + "expression_name": "p173", + "description": "Total beam mass from NX expression" + }, + "outputs": [ + { + "name": "mass", + "metric": "total", + "units": "kg" + } + ] + }, + { + "id": "ext_002", + "name": "Displacement Extractor", + "type": "displacement", + "config": { + "method": "result_sensor_or_op2", + "component": "magnitude", + "location": "beam_tip", + "description": "Tip displacement magnitude from SOL 101. Preferred: NX result sensor. Fallback: parse .op2 at tip node ID.", + "TODO": "Confirm displacement sensor exists in Beam_sim1.sim OR identify tip node ID" + }, + "outputs": [ + { + "name": "tip_displacement", + "metric": "max_magnitude", + "units": "mm" + } + ] + }, + { + "id": "ext_003", + "name": "Stress Extractor", + "type": "stress", + "config": { + "method": "op2_elemental_nodal", + "stress_type": "von_mises", + "scope": "all_elements", + "unit_conversion": "kPa_to_MPa", + "description": "Max von Mises stress (elemental nodal) from SOL 101. pyNastran returns kPa for NX kg-mm-s units โ€” divide by 1000 for MPa.", + "TODO": "Verify element types in model (CQUAD4? CTETRA? CHEXA?) and confirm stress scope" + }, + "outputs": [ + { + "name": "max_von_mises", + "metric": "max", + "units": "MPa" + } + ] + } + ], + "objectives": [ + { + "id": "obj_001", + "name": "Minimize beam mass", + "direction": "minimize", + "weight": 1.0, + "source": { + "extractor_id": "ext_001", + "output_name": "mass" + }, + "units": "kg" + } + ], + "constraints": [ + { + "id": "con_001", + "name": "Tip displacement limit", + "type": "hard", + "operator": "<=", + "threshold": 10.0, + "source": { + "extractor_id": "ext_002", + "output_name": "tip_displacement" + }, + "penalty_config": { + "method": "deb_rules", + "description": "Deb's feasibility rules: feasible always beats infeasible; among infeasible, lower violation wins" + }, + "margin_buffer": { + "optimizer_target": 9.5, + "hard_limit": 10.0, + "description": "5% inner margin during optimization to account for numerical noise" + } + }, + { + "id": "con_002", + "name": "Von Mises stress limit", + "type": "hard", + "operator": "<=", + "threshold": 130.0, + "source": { + "extractor_id": "ext_003", + "output_name": "max_von_mises" + }, + "penalty_config": { + "method": "deb_rules", + "description": "Deb's feasibility rules: feasible always beats infeasible; among infeasible, lower violation wins" + }, + "margin_buffer": { + "optimizer_target": 123.5, + "hard_limit": 130.0, + "description": "5% inner margin during optimization to account for mesh sensitivity" + } + } + ], + "optimization": { + "phases": [ + { + "id": "phase_1", + "name": "DoE Landscape Mapping", + "algorithm": { + "type": "LHS", + "config": { + "criterion": "maximin", + "integer_handling": "round_nearest_stratified", + "seed": 42 + } + }, + "budget": { + "max_trials": 50, + "baseline_enqueued": true, + "total_with_baseline": 51 + }, + "purpose": "Map design space, identify feasible region, assess sensitivities and interactions" + }, + { + "id": "phase_2", + "name": "TPE Optimization", + "algorithm": { + "type": "TPE", + "config": { + "sampler": "TPESampler", + "n_startup_trials": 0, + "warm_start_from": "phase_1_best_feasible", + "seed": 42, + "constraint_handling": "deb_feasibility_rules" + } + }, + "budget": { + "min_trials": 60, + "max_trials": 100, + "adaptive": true + }, + "convergence": { + "stall_window": 20, + "stall_threshold_pct": 1.0, + "min_trials_before_check": 60 + }, + "purpose": "Directed optimization to converge on minimum-mass feasible design" + } + ], + "total_budget": { + "min": 114, + "max": 156, + "estimated_hours": "4-5" + }, + "baseline_trial": { + "enqueue": true, + "values": { + "beam_half_core_thickness": 20, + "beam_face_thickness": 20, + "holes_diameter": 300, + "hole_count": 10 + }, + "expected_results": { + "mass_kg": 974, + "tip_displacement_mm": 22, + "max_von_mises_mpa": "UNKNOWN โ€” must measure" + } + } + }, + "error_handling": { + "trial_timeout_seconds": 600, + "on_nx_rebuild_failure": "record_infeasible_max_violation", + "on_solver_failure": "record_infeasible_max_violation", + "on_extraction_failure": "record_infeasible_max_violation", + "max_consecutive_failures": 5, + "max_failure_rate_pct": 30, + "nx_process_management": "NEVER kill NX directly. Use NXSessionManager.close_nx_if_allowed() only." + }, + "pre_flight_checks": [ + { + "id": "pf_001", + "name": "Baseline validation", + "description": "Run Trial 0 with baseline parameters, verify mass โ‰ˆ 974 kg and displacement โ‰ˆ 22 mm" + }, + { + "id": "pf_002", + "name": "Corner tests", + "description": "Test 16 corner combinations (min/max for each DV). Verify NX rebuilds and solves at all corners." + }, + { + "id": "pf_003", + "name": "Mesh convergence", + "description": "Run baseline at 3 mesh densities. Verify stress convergence within 10%." + }, + { + "id": "pf_004", + "name": "Extractor validation", + "description": "Confirm mass, displacement, and stress extractors return correct values at baseline." + }, + { + "id": "pf_005", + "name": "Geometric feasibility", + "description": "Determine beam web length. Verify max(hole_count) ร— max(holes_diameter) fits. Add ligament constraint if needed." + } + ], + "open_items": [ + "Beam web length needed for geometric feasibility validation (holes_diameter ร— hole_count vs available length)", + "Displacement extraction method: result sensor in .sim or .op2 node ID parsing?", + "Stress extraction scope: whole model or specific element group?", + "Verify NX expression name 'p173' maps to mass", + "Benchmark single SOL 101 runtime to refine compute estimates", + "Confirm baseline stress value (currently unknown)", + "Clarify relationship between core/face thickness DVs and web height where holes are placed" + ] +} diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/atomizer_spec_draft.sync-conflict-20260214-191806-VBCUD7Z.json b/projects/hydrotech-beam/studies/01_doe_landscape/atomizer_spec_draft.sync-conflict-20260214-191806-VBCUD7Z.json new file mode 100644 index 00000000..64d7df36 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/atomizer_spec_draft.sync-conflict-20260214-191806-VBCUD7Z.json @@ -0,0 +1,302 @@ +{ + "meta": { + "version": "2.0", + "created": "2026-02-09T12:00:00Z", + "modified": "2026-02-09T12:00:00Z", + "created_by": "optimizer-agent", + "modified_by": "optimizer-agent", + "study_name": "01_doe_landscape", + "description": "Hydrotech Beam โ€” DoE landscape mapping + TPE optimization. Minimize mass subject to displacement and stress constraints.", + "tags": ["hydrotech-beam", "doe", "landscape", "tpe", "single-objective"] + }, + "model": { + "sim": { + "path": "models/Beam_sim1.sim", + "solver": "nastran" + }, + "part": "models/Beam.prt", + "fem": "models/Beam_fem1.fem", + "idealized": "models/Beam_fem1_i.prt" + }, + "design_variables": [ + { + "id": "dv_001", + "name": "beam_half_core_thickness", + "expression_name": "beam_half_core_thickness", + "type": "continuous", + "bounds": { + "min": 10, + "max": 40 + }, + "baseline": 20, + "units": "mm", + "enabled": true, + "description": "Core half-thickness. Stiffness scales ~quadratically via sandwich effect (lever arm). Also adds core mass linearly." + }, + { + "id": "dv_002", + "name": "beam_face_thickness", + "expression_name": "beam_face_thickness", + "type": "continuous", + "bounds": { + "min": 10, + "max": 40 + }, + "baseline": 20, + "units": "mm", + "enabled": true, + "description": "Face sheet thickness. Primary bending stiffness contributor AND primary mass contributor. Key trade-off variable." + }, + { + "id": "dv_003", + "name": "holes_diameter", + "expression_name": "holes_diameter", + "type": "continuous", + "bounds": { + "min": 150, + "max": 450 + }, + "baseline": 300, + "units": "mm", + "enabled": true, + "description": "Lightening hole diameter. Mass reduction scales with dยฒ. Stress concentration scales with hole size. Geometric feasibility depends on beam web length." + }, + { + "id": "dv_004", + "name": "hole_count", + "expression_name": "hole_count", + "type": "integer", + "bounds": { + "min": 5, + "max": 15 + }, + "baseline": 10, + "units": "", + "enabled": true, + "description": "Number of lightening holes in the web. Integer variable (11 levels). Interacts strongly with holes_diameter for geometric feasibility." + } + ], + "extractors": [ + { + "id": "ext_001", + "name": "Mass Extractor", + "type": "expression", + "config": { + "expression_name": "p173", + "description": "Total beam mass from NX expression" + }, + "outputs": [ + { + "name": "mass", + "metric": "total", + "units": "kg" + } + ] + }, + { + "id": "ext_002", + "name": "Displacement Extractor", + "type": "displacement", + "config": { + "method": "result_sensor_or_op2", + "component": "magnitude", + "location": "beam_tip", + "description": "Tip displacement magnitude from SOL 101. Preferred: NX result sensor. Fallback: parse .op2 at tip node ID.", + "TODO": "Confirm displacement sensor exists in Beam_sim1.sim OR identify tip node ID" + }, + "outputs": [ + { + "name": "tip_displacement", + "metric": "max_magnitude", + "units": "mm" + } + ] + }, + { + "id": "ext_003", + "name": "Stress Extractor", + "type": "stress", + "config": { + "method": "op2_elemental_nodal", + "stress_type": "von_mises", + "scope": "all_elements", + "unit_conversion": "kPa_to_MPa", + "description": "Max von Mises stress (elemental nodal) from SOL 101. pyNastran returns kPa for NX kg-mm-s units โ€” divide by 1000 for MPa.", + "TODO": "Verify element types in model (CQUAD4? CTETRA? CHEXA?) and confirm stress scope" + }, + "outputs": [ + { + "name": "max_von_mises", + "metric": "max", + "units": "MPa" + } + ] + } + ], + "objectives": [ + { + "id": "obj_001", + "name": "Minimize beam mass", + "direction": "minimize", + "weight": 1.0, + "source": { + "extractor_id": "ext_001", + "output_name": "mass" + }, + "units": "kg" + } + ], + "constraints": [ + { + "id": "con_001", + "name": "Tip displacement limit", + "type": "hard", + "operator": "<=", + "threshold": 10.0, + "source": { + "extractor_id": "ext_002", + "output_name": "tip_displacement" + }, + "penalty_config": { + "method": "deb_rules", + "description": "Deb's feasibility rules: feasible always beats infeasible; among infeasible, lower violation wins" + }, + "margin_buffer": { + "optimizer_target": 9.5, + "hard_limit": 10.0, + "description": "5% inner margin during optimization to account for numerical noise" + } + }, + { + "id": "con_002", + "name": "Von Mises stress limit", + "type": "hard", + "operator": "<=", + "threshold": 130.0, + "source": { + "extractor_id": "ext_003", + "output_name": "max_von_mises" + }, + "penalty_config": { + "method": "deb_rules", + "description": "Deb's feasibility rules: feasible always beats infeasible; among infeasible, lower violation wins" + }, + "margin_buffer": { + "optimizer_target": 123.5, + "hard_limit": 130.0, + "description": "5% inner margin during optimization to account for mesh sensitivity" + } + } + ], + "optimization": { + "phases": [ + { + "id": "phase_1", + "name": "DoE Landscape Mapping", + "algorithm": { + "type": "LHS", + "config": { + "criterion": "maximin", + "integer_handling": "round_nearest_stratified", + "seed": 42 + } + }, + "budget": { + "max_trials": 50, + "baseline_enqueued": true, + "total_with_baseline": 51 + }, + "purpose": "Map design space, identify feasible region, assess sensitivities and interactions" + }, + { + "id": "phase_2", + "name": "TPE Optimization", + "algorithm": { + "type": "TPE", + "config": { + "sampler": "TPESampler", + "n_startup_trials": 0, + "warm_start_from": "phase_1_best_feasible", + "seed": 42, + "constraint_handling": "deb_feasibility_rules" + } + }, + "budget": { + "min_trials": 60, + "max_trials": 100, + "adaptive": true + }, + "convergence": { + "stall_window": 20, + "stall_threshold_pct": 1.0, + "min_trials_before_check": 60 + }, + "purpose": "Directed optimization to converge on minimum-mass feasible design" + } + ], + "total_budget": { + "min": 114, + "max": 156, + "estimated_hours": "4-5" + }, + "baseline_trial": { + "enqueue": true, + "values": { + "beam_half_core_thickness": 20, + "beam_face_thickness": 20, + "holes_diameter": 300, + "hole_count": 10 + }, + "expected_results": { + "mass_kg": 974, + "tip_displacement_mm": 22, + "max_von_mises_mpa": "UNKNOWN โ€” must measure" + } + } + }, + "error_handling": { + "trial_timeout_seconds": 600, + "on_nx_rebuild_failure": "record_infeasible_max_violation", + "on_solver_failure": "record_infeasible_max_violation", + "on_extraction_failure": "record_infeasible_max_violation", + "max_consecutive_failures": 5, + "max_failure_rate_pct": 30, + "nx_process_management": "NEVER kill NX directly. Use NXSessionManager.close_nx_if_allowed() only." + }, + "pre_flight_checks": [ + { + "id": "pf_001", + "name": "Baseline validation", + "description": "Run Trial 0 with baseline parameters, verify mass โ‰ˆ 974 kg and displacement โ‰ˆ 22 mm" + }, + { + "id": "pf_002", + "name": "Corner tests", + "description": "Test 16 corner combinations (min/max for each DV). Verify NX rebuilds and solves at all corners." + }, + { + "id": "pf_003", + "name": "Mesh convergence", + "description": "Run baseline at 3 mesh densities. Verify stress convergence within 10%." + }, + { + "id": "pf_004", + "name": "Extractor validation", + "description": "Confirm mass, displacement, and stress extractors return correct values at baseline." + }, + { + "id": "pf_005", + "name": "Geometric feasibility", + "description": "Determine beam web length. Verify max(hole_count) ร— max(holes_diameter) fits. Add ligament constraint if needed." + } + ], + "open_items": [ + "Beam web length needed for geometric feasibility validation (holes_diameter ร— hole_count vs available length)", + "Displacement extraction method: result sensor in .sim or .op2 node ID parsing?", + "Stress extraction scope: whole model or specific element group?", + "Verify NX expression name 'p173' maps to mass", + "Benchmark single SOL 101 runtime to refine compute estimates", + "Confirm baseline stress value (currently unknown)", + "Clarify relationship between core/face thickness DVs and web height where holes are placed" + ] +} diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/geometric_checks.py b/projects/hydrotech-beam/studies/01_doe_landscape/geometric_checks.py old mode 100644 new mode 100755 index 70910b29..b3c4e507 --- a/projects/hydrotech-beam/studies/01_doe_landscape/geometric_checks.py +++ b/projects/hydrotech-beam/studies/01_doe_landscape/geometric_checks.py @@ -1,205 +1,205 @@ -"""Geometric feasibility pre-flight checks for Hydrotech Beam. - -Validates trial design variable combinations against physical geometry -constraints BEFORE sending to NX. Catches infeasible combos that would -cause NX rebuild failures or physically impossible geometries. - -References: - OPTIMIZATION_STRATEGY.md ยง4.2 โ€” Hole overlap analysis - Auditor review 2026-02-10 โ€” Corrected spacing formula -""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import NamedTuple - - -# --------------------------------------------------------------------------- -# Fixed geometry constants (from NX introspection โ€” CONTEXT.md) -# --------------------------------------------------------------------------- -BEAM_HALF_HEIGHT: float = 250.0 # mm โ€” fixed, expression `beam_half_height` -HOLE_SPAN: float = 4000.0 # mm โ€” expression `p6`, total distribution length -MIN_LIGAMENT: float = 30.0 # mm โ€” minimum material between holes (mesh + structural) - - -class FeasibilityResult(NamedTuple): - """Result of a geometric feasibility check.""" - - feasible: bool - reason: str - ligament: float # mm โ€” material between adjacent holes - web_clearance: float # mm โ€” clearance between hole edge and faces - - -@dataclass(frozen=True) -class DesignPoint: - """A single design variable combination.""" - - beam_half_core_thickness: float # mm โ€” DV1 - beam_face_thickness: float # mm โ€” DV2 - holes_diameter: float # mm โ€” DV3 - hole_count: int # โ€” DV4 - - -def compute_ligament(holes_diameter: float, hole_count: int) -> float: - """Compute ligament width (material between adjacent holes). - - The NX pattern places `n` holes across `hole_span` mm using `n-1` - intervals (holes at both endpoints of the span). - - Formula (corrected per auditor review): - spacing = hole_span / (hole_count - 1) - ligament = spacing - holes_diameter - - Args: - holes_diameter: Hole diameter in mm. - hole_count: Number of holes (integer โ‰ฅ 2). - - Returns: - Ligament width in mm. Negative means overlap. - """ - if hole_count < 2: - # Single hole โ€” no overlap possible, return large ligament - return HOLE_SPAN - spacing = HOLE_SPAN / (hole_count - 1) - return spacing - holes_diameter - - -def compute_web_clearance( - beam_face_thickness: float, holes_diameter: float -) -> float: - """Compute clearance between hole edge and face sheets. - - Total beam height = 2 ร— beam_half_height = 500 mm (fixed). - Web clear height = total_height - 2 ร— face_thickness. - Clearance = web_clear_height - holes_diameter. - - Args: - beam_face_thickness: Face sheet thickness in mm. - holes_diameter: Hole diameter in mm. - - Returns: - Web clearance in mm. โ‰ค 0 means hole doesn't fit. - """ - total_height = 2.0 * BEAM_HALF_HEIGHT # 500 mm - web_clear_height = total_height - 2.0 * beam_face_thickness - return web_clear_height - holes_diameter - - -def check_feasibility(point: DesignPoint) -> FeasibilityResult: - """Run all geometric feasibility checks on a design point. - - Checks (in order): - 1. Hole overlap โ€” ligament between adjacent holes โ‰ฅ MIN_LIGAMENT - 2. Web clearance โ€” hole fits within the web (between face sheets) - - Args: - point: Design variable combination to check. - - Returns: - FeasibilityResult with feasibility status and diagnostic info. - """ - ligament = compute_ligament(point.holes_diameter, point.hole_count) - web_clearance = compute_web_clearance( - point.beam_face_thickness, point.holes_diameter - ) - - # Check 1: Hole overlap - if ligament < MIN_LIGAMENT: - return FeasibilityResult( - feasible=False, - reason=( - f"Hole overlap: ligament={ligament:.1f}mm < " - f"{MIN_LIGAMENT}mm minimum " - f"(d={point.holes_diameter}mm, n={point.hole_count})" - ), - ligament=ligament, - web_clearance=web_clearance, - ) - - # Check 2: Web clearance - if web_clearance <= 0: - return FeasibilityResult( - feasible=False, - reason=( - f"Hole exceeds web: clearance={web_clearance:.1f}mm โ‰ค 0 " - f"(face={point.beam_face_thickness}mm, " - f"d={point.holes_diameter}mm)" - ), - ligament=ligament, - web_clearance=web_clearance, - ) - - return FeasibilityResult( - feasible=True, - reason="OK", - ligament=ligament, - web_clearance=web_clearance, - ) - - -def check_feasibility_from_values( - beam_half_core_thickness: float, - beam_face_thickness: float, - holes_diameter: float, - hole_count: int, -) -> FeasibilityResult: - """Convenience wrapper โ€” check feasibility from raw DV values. - - Args: - beam_half_core_thickness: Core half-thickness in mm (DV1). - beam_face_thickness: Face sheet thickness in mm (DV2). - holes_diameter: Hole diameter in mm (DV3). - hole_count: Number of holes (DV4). - - Returns: - FeasibilityResult with feasibility status and diagnostic info. - """ - point = DesignPoint( - beam_half_core_thickness=beam_half_core_thickness, - beam_face_thickness=beam_face_thickness, - holes_diameter=holes_diameter, - hole_count=hole_count, - ) - return check_feasibility(point) - - -# --------------------------------------------------------------------------- -# Quick self-test -# --------------------------------------------------------------------------- -if __name__ == "__main__": - # Baseline: should be feasible - baseline = DesignPoint( - beam_half_core_thickness=25.162, - beam_face_thickness=21.504, - holes_diameter=300.0, - hole_count=10, - ) - result = check_feasibility(baseline) - print(f"Baseline: {result}") - assert result.feasible, f"Baseline should be feasible! Got: {result}" - - # Worst case: n=15, d=450 โ€” should be infeasible (overlap) - worst_overlap = DesignPoint( - beam_half_core_thickness=25.0, - beam_face_thickness=20.0, - holes_diameter=450.0, - hole_count=15, - ) - result = check_feasibility(worst_overlap) - print(f"Worst overlap: {result}") - assert not result.feasible, "n=15, d=450 should be infeasible" - - # Web clearance fail: face=40, d=450 - web_fail = DesignPoint( - beam_half_core_thickness=25.0, - beam_face_thickness=40.0, - holes_diameter=450.0, - hole_count=5, - ) - result = check_feasibility(web_fail) - print(f"Web clearance fail: {result}") - assert not result.feasible, "face=40, d=450 should fail web clearance" - - print("\nAll self-tests passed โœ“") +"""Geometric feasibility pre-flight checks for Hydrotech Beam. + +Validates trial design variable combinations against physical geometry +constraints BEFORE sending to NX. Catches infeasible combos that would +cause NX rebuild failures or physically impossible geometries. + +References: + OPTIMIZATION_STRATEGY.md ยง4.2 โ€” Hole overlap analysis + Auditor review 2026-02-10 โ€” Corrected spacing formula +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import NamedTuple + + +# --------------------------------------------------------------------------- +# Fixed geometry constants (from NX introspection โ€” CONTEXT.md) +# --------------------------------------------------------------------------- +BEAM_HALF_HEIGHT: float = 250.0 # mm โ€” fixed, expression `beam_half_height` +HOLE_SPAN: float = 4000.0 # mm โ€” expression `p6`, total distribution length +MIN_LIGAMENT: float = 30.0 # mm โ€” minimum material between holes (mesh + structural) + + +class FeasibilityResult(NamedTuple): + """Result of a geometric feasibility check.""" + + feasible: bool + reason: str + ligament: float # mm โ€” material between adjacent holes + web_clearance: float # mm โ€” clearance between hole edge and faces + + +@dataclass(frozen=True) +class DesignPoint: + """A single design variable combination.""" + + beam_half_core_thickness: float # mm โ€” DV1 + beam_face_thickness: float # mm โ€” DV2 + holes_diameter: float # mm โ€” DV3 + hole_count: int # โ€” DV4 + + +def compute_ligament(holes_diameter: float, hole_count: int) -> float: + """Compute ligament width (material between adjacent holes). + + The NX pattern places `n` holes across `hole_span` mm using `n-1` + intervals (holes at both endpoints of the span). + + Formula (corrected per auditor review): + spacing = hole_span / (hole_count - 1) + ligament = spacing - holes_diameter + + Args: + holes_diameter: Hole diameter in mm. + hole_count: Number of holes (integer โ‰ฅ 2). + + Returns: + Ligament width in mm. Negative means overlap. + """ + if hole_count < 2: + # Single hole โ€” no overlap possible, return large ligament + return HOLE_SPAN + spacing = HOLE_SPAN / (hole_count - 1) + return spacing - holes_diameter + + +def compute_web_clearance( + beam_face_thickness: float, holes_diameter: float +) -> float: + """Compute clearance between hole edge and face sheets. + + Total beam height = 2 ร— beam_half_height = 500 mm (fixed). + Web clear height = total_height - 2 ร— face_thickness. + Clearance = web_clear_height - holes_diameter. + + Args: + beam_face_thickness: Face sheet thickness in mm. + holes_diameter: Hole diameter in mm. + + Returns: + Web clearance in mm. โ‰ค 0 means hole doesn't fit. + """ + total_height = 2.0 * BEAM_HALF_HEIGHT # 500 mm + web_clear_height = total_height - 2.0 * beam_face_thickness + return web_clear_height - holes_diameter + + +def check_feasibility(point: DesignPoint) -> FeasibilityResult: + """Run all geometric feasibility checks on a design point. + + Checks (in order): + 1. Hole overlap โ€” ligament between adjacent holes โ‰ฅ MIN_LIGAMENT + 2. Web clearance โ€” hole fits within the web (between face sheets) + + Args: + point: Design variable combination to check. + + Returns: + FeasibilityResult with feasibility status and diagnostic info. + """ + ligament = compute_ligament(point.holes_diameter, point.hole_count) + web_clearance = compute_web_clearance( + point.beam_face_thickness, point.holes_diameter + ) + + # Check 1: Hole overlap + if ligament < MIN_LIGAMENT: + return FeasibilityResult( + feasible=False, + reason=( + f"Hole overlap: ligament={ligament:.1f}mm < " + f"{MIN_LIGAMENT}mm minimum " + f"(d={point.holes_diameter}mm, n={point.hole_count})" + ), + ligament=ligament, + web_clearance=web_clearance, + ) + + # Check 2: Web clearance + if web_clearance <= 0: + return FeasibilityResult( + feasible=False, + reason=( + f"Hole exceeds web: clearance={web_clearance:.1f}mm โ‰ค 0 " + f"(face={point.beam_face_thickness}mm, " + f"d={point.holes_diameter}mm)" + ), + ligament=ligament, + web_clearance=web_clearance, + ) + + return FeasibilityResult( + feasible=True, + reason="OK", + ligament=ligament, + web_clearance=web_clearance, + ) + + +def check_feasibility_from_values( + beam_half_core_thickness: float, + beam_face_thickness: float, + holes_diameter: float, + hole_count: int, +) -> FeasibilityResult: + """Convenience wrapper โ€” check feasibility from raw DV values. + + Args: + beam_half_core_thickness: Core half-thickness in mm (DV1). + beam_face_thickness: Face sheet thickness in mm (DV2). + holes_diameter: Hole diameter in mm (DV3). + hole_count: Number of holes (DV4). + + Returns: + FeasibilityResult with feasibility status and diagnostic info. + """ + point = DesignPoint( + beam_half_core_thickness=beam_half_core_thickness, + beam_face_thickness=beam_face_thickness, + holes_diameter=holes_diameter, + hole_count=hole_count, + ) + return check_feasibility(point) + + +# --------------------------------------------------------------------------- +# Quick self-test +# --------------------------------------------------------------------------- +if __name__ == "__main__": + # Baseline: should be feasible + baseline = DesignPoint( + beam_half_core_thickness=25.162, + beam_face_thickness=21.504, + holes_diameter=300.0, + hole_count=10, + ) + result = check_feasibility(baseline) + print(f"Baseline: {result}") + assert result.feasible, f"Baseline should be feasible! Got: {result}" + + # Worst case: n=15, d=450 โ€” should be infeasible (overlap) + worst_overlap = DesignPoint( + beam_half_core_thickness=25.0, + beam_face_thickness=20.0, + holes_diameter=450.0, + hole_count=15, + ) + result = check_feasibility(worst_overlap) + print(f"Worst overlap: {result}") + assert not result.feasible, "n=15, d=450 should be infeasible" + + # Web clearance fail: face=40, d=450 + web_fail = DesignPoint( + beam_half_core_thickness=25.0, + beam_face_thickness=40.0, + holes_diameter=450.0, + hole_count=5, + ) + result = check_feasibility(web_fail) + print(f"Web clearance fail: {result}") + assert not result.feasible, "face=40, d=450 should fail web clearance" + + print("\nAll self-tests passed โœ“") diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/geometric_checks.sync-conflict-20260214-191802-VBCUD7Z.py b/projects/hydrotech-beam/studies/01_doe_landscape/geometric_checks.sync-conflict-20260214-191802-VBCUD7Z.py new file mode 100644 index 00000000..70910b29 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/geometric_checks.sync-conflict-20260214-191802-VBCUD7Z.py @@ -0,0 +1,205 @@ +"""Geometric feasibility pre-flight checks for Hydrotech Beam. + +Validates trial design variable combinations against physical geometry +constraints BEFORE sending to NX. Catches infeasible combos that would +cause NX rebuild failures or physically impossible geometries. + +References: + OPTIMIZATION_STRATEGY.md ยง4.2 โ€” Hole overlap analysis + Auditor review 2026-02-10 โ€” Corrected spacing formula +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import NamedTuple + + +# --------------------------------------------------------------------------- +# Fixed geometry constants (from NX introspection โ€” CONTEXT.md) +# --------------------------------------------------------------------------- +BEAM_HALF_HEIGHT: float = 250.0 # mm โ€” fixed, expression `beam_half_height` +HOLE_SPAN: float = 4000.0 # mm โ€” expression `p6`, total distribution length +MIN_LIGAMENT: float = 30.0 # mm โ€” minimum material between holes (mesh + structural) + + +class FeasibilityResult(NamedTuple): + """Result of a geometric feasibility check.""" + + feasible: bool + reason: str + ligament: float # mm โ€” material between adjacent holes + web_clearance: float # mm โ€” clearance between hole edge and faces + + +@dataclass(frozen=True) +class DesignPoint: + """A single design variable combination.""" + + beam_half_core_thickness: float # mm โ€” DV1 + beam_face_thickness: float # mm โ€” DV2 + holes_diameter: float # mm โ€” DV3 + hole_count: int # โ€” DV4 + + +def compute_ligament(holes_diameter: float, hole_count: int) -> float: + """Compute ligament width (material between adjacent holes). + + The NX pattern places `n` holes across `hole_span` mm using `n-1` + intervals (holes at both endpoints of the span). + + Formula (corrected per auditor review): + spacing = hole_span / (hole_count - 1) + ligament = spacing - holes_diameter + + Args: + holes_diameter: Hole diameter in mm. + hole_count: Number of holes (integer โ‰ฅ 2). + + Returns: + Ligament width in mm. Negative means overlap. + """ + if hole_count < 2: + # Single hole โ€” no overlap possible, return large ligament + return HOLE_SPAN + spacing = HOLE_SPAN / (hole_count - 1) + return spacing - holes_diameter + + +def compute_web_clearance( + beam_face_thickness: float, holes_diameter: float +) -> float: + """Compute clearance between hole edge and face sheets. + + Total beam height = 2 ร— beam_half_height = 500 mm (fixed). + Web clear height = total_height - 2 ร— face_thickness. + Clearance = web_clear_height - holes_diameter. + + Args: + beam_face_thickness: Face sheet thickness in mm. + holes_diameter: Hole diameter in mm. + + Returns: + Web clearance in mm. โ‰ค 0 means hole doesn't fit. + """ + total_height = 2.0 * BEAM_HALF_HEIGHT # 500 mm + web_clear_height = total_height - 2.0 * beam_face_thickness + return web_clear_height - holes_diameter + + +def check_feasibility(point: DesignPoint) -> FeasibilityResult: + """Run all geometric feasibility checks on a design point. + + Checks (in order): + 1. Hole overlap โ€” ligament between adjacent holes โ‰ฅ MIN_LIGAMENT + 2. Web clearance โ€” hole fits within the web (between face sheets) + + Args: + point: Design variable combination to check. + + Returns: + FeasibilityResult with feasibility status and diagnostic info. + """ + ligament = compute_ligament(point.holes_diameter, point.hole_count) + web_clearance = compute_web_clearance( + point.beam_face_thickness, point.holes_diameter + ) + + # Check 1: Hole overlap + if ligament < MIN_LIGAMENT: + return FeasibilityResult( + feasible=False, + reason=( + f"Hole overlap: ligament={ligament:.1f}mm < " + f"{MIN_LIGAMENT}mm minimum " + f"(d={point.holes_diameter}mm, n={point.hole_count})" + ), + ligament=ligament, + web_clearance=web_clearance, + ) + + # Check 2: Web clearance + if web_clearance <= 0: + return FeasibilityResult( + feasible=False, + reason=( + f"Hole exceeds web: clearance={web_clearance:.1f}mm โ‰ค 0 " + f"(face={point.beam_face_thickness}mm, " + f"d={point.holes_diameter}mm)" + ), + ligament=ligament, + web_clearance=web_clearance, + ) + + return FeasibilityResult( + feasible=True, + reason="OK", + ligament=ligament, + web_clearance=web_clearance, + ) + + +def check_feasibility_from_values( + beam_half_core_thickness: float, + beam_face_thickness: float, + holes_diameter: float, + hole_count: int, +) -> FeasibilityResult: + """Convenience wrapper โ€” check feasibility from raw DV values. + + Args: + beam_half_core_thickness: Core half-thickness in mm (DV1). + beam_face_thickness: Face sheet thickness in mm (DV2). + holes_diameter: Hole diameter in mm (DV3). + hole_count: Number of holes (DV4). + + Returns: + FeasibilityResult with feasibility status and diagnostic info. + """ + point = DesignPoint( + beam_half_core_thickness=beam_half_core_thickness, + beam_face_thickness=beam_face_thickness, + holes_diameter=holes_diameter, + hole_count=hole_count, + ) + return check_feasibility(point) + + +# --------------------------------------------------------------------------- +# Quick self-test +# --------------------------------------------------------------------------- +if __name__ == "__main__": + # Baseline: should be feasible + baseline = DesignPoint( + beam_half_core_thickness=25.162, + beam_face_thickness=21.504, + holes_diameter=300.0, + hole_count=10, + ) + result = check_feasibility(baseline) + print(f"Baseline: {result}") + assert result.feasible, f"Baseline should be feasible! Got: {result}" + + # Worst case: n=15, d=450 โ€” should be infeasible (overlap) + worst_overlap = DesignPoint( + beam_half_core_thickness=25.0, + beam_face_thickness=20.0, + holes_diameter=450.0, + hole_count=15, + ) + result = check_feasibility(worst_overlap) + print(f"Worst overlap: {result}") + assert not result.feasible, "n=15, d=450 should be infeasible" + + # Web clearance fail: face=40, d=450 + web_fail = DesignPoint( + beam_half_core_thickness=25.0, + beam_face_thickness=40.0, + holes_diameter=450.0, + hole_count=5, + ) + result = check_feasibility(web_fail) + print(f"Web clearance fail: {result}") + assert not result.feasible, "face=40, d=450 should fail web clearance" + + print("\nAll self-tests passed โœ“") diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/history.py b/projects/hydrotech-beam/studies/01_doe_landscape/history.py old mode 100644 new mode 100755 index b5b6509d..ca63bf1e --- a/projects/hydrotech-beam/studies/01_doe_landscape/history.py +++ b/projects/hydrotech-beam/studies/01_doe_landscape/history.py @@ -1,235 +1,235 @@ -"""Persistent trial history โ€” append-only, survives Optuna resets. - -Every trial is logged to `history.db` (SQLite) and exported to `history.csv`. -Never deleted by --clean. Full lineage across all studies and phases. - -Usage: - history = TrialHistory(results_dir) - history.log_trial(study_name, trial_id, params, results, ...) - history.export_csv() - df = history.query("SELECT * FROM trials WHERE mass_kg < 100") -""" - -from __future__ import annotations - -import csv -import json -import logging -import sqlite3 -from datetime import datetime, timezone -from pathlib import Path -from typing import Any, Optional - -logger = logging.getLogger(__name__) - -# Schema version โ€” bump if columns change -SCHEMA_VERSION = 1 - -CREATE_TABLE = """ -CREATE TABLE IF NOT EXISTS trials ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - study_name TEXT NOT NULL, - trial_id INTEGER NOT NULL, - iteration TEXT, - timestamp TEXT NOT NULL, - - -- Design variables - beam_half_core_thickness REAL, - beam_face_thickness REAL, - holes_diameter REAL, - hole_count INTEGER, - - -- Results - mass_kg REAL, - tip_displacement_mm REAL, - max_von_mises_mpa REAL, - - -- Constraint checks - disp_feasible INTEGER, -- 0/1 - stress_feasible INTEGER, -- 0/1 - geo_feasible INTEGER, -- 0/1 - fully_feasible INTEGER, -- 0/1 - - -- Meta - status TEXT DEFAULT 'COMPLETE', -- COMPLETE, FAILED, PRUNED - error_message TEXT, - solve_time_s REAL, - iter_path TEXT, - notes TEXT, - - -- Unique constraint: no duplicate (study, trial) pairs - UNIQUE(study_name, trial_id) -); - -CREATE TABLE IF NOT EXISTS schema_version ( - version INTEGER PRIMARY KEY -); -""" - -# Constraint thresholds (from OPTIMIZATION_STRATEGY.md) -DISP_LIMIT_MM = 10.0 -STRESS_LIMIT_MPA = 130.0 - -# CSV column order -CSV_COLUMNS = [ - "study_name", "trial_id", "iteration", "timestamp", - "beam_half_core_thickness", "beam_face_thickness", - "holes_diameter", "hole_count", - "mass_kg", "tip_displacement_mm", "max_von_mises_mpa", - "disp_feasible", "stress_feasible", "geo_feasible", "fully_feasible", - "status", "error_message", "solve_time_s", "iter_path", -] - - -class TrialHistory: - """Append-only trial history database.""" - - def __init__(self, results_dir: Path | str): - self.results_dir = Path(results_dir) - self.results_dir.mkdir(parents=True, exist_ok=True) - - self.db_path = self.results_dir / "history.db" - self.csv_path = self.results_dir / "history.csv" - - self._conn = sqlite3.connect(str(self.db_path)) - self._conn.row_factory = sqlite3.Row - self._conn.execute("PRAGMA journal_mode=WAL") # safe concurrent reads - self._init_schema() - - count = self._conn.execute("SELECT COUNT(*) FROM trials").fetchone()[0] - logger.info("Trial history: %s (%d records)", self.db_path.name, count) - - def _init_schema(self) -> None: - """Create tables if they don't exist.""" - self._conn.executescript(CREATE_TABLE) - - # Check/set schema version - row = self._conn.execute( - "SELECT version FROM schema_version ORDER BY version DESC LIMIT 1" - ).fetchone() - if row is None: - self._conn.execute( - "INSERT INTO schema_version (version) VALUES (?)", - (SCHEMA_VERSION,), - ) - self._conn.commit() - - def log_trial( - self, - study_name: str, - trial_id: int, - params: dict[str, float], - mass_kg: float = float("nan"), - tip_displacement_mm: float = float("nan"), - max_von_mises_mpa: float = float("nan"), - geo_feasible: bool = True, - status: str = "COMPLETE", - error_message: str | None = None, - solve_time_s: float = 0.0, - iter_path: str | None = None, - notes: str | None = None, - iteration_number: int | None = None, - ) -> None: - """Log a single trial result. - - Uses INSERT OR REPLACE so re-runs of the same trial update cleanly. - """ - import math - - disp_ok = ( - not math.isnan(tip_displacement_mm) - and tip_displacement_mm <= DISP_LIMIT_MM - ) - stress_ok = ( - not math.isnan(max_von_mises_mpa) - and max_von_mises_mpa <= STRESS_LIMIT_MPA - ) - - iteration = f"iter{iteration_number:03d}" if iteration_number else None - - try: - self._conn.execute( - """ - INSERT OR REPLACE INTO trials ( - study_name, trial_id, iteration, timestamp, - beam_half_core_thickness, beam_face_thickness, - holes_diameter, hole_count, - mass_kg, tip_displacement_mm, max_von_mises_mpa, - disp_feasible, stress_feasible, geo_feasible, fully_feasible, - status, error_message, solve_time_s, iter_path, notes - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - study_name, - trial_id, - iteration, - datetime.now(timezone.utc).isoformat(), - params.get("beam_half_core_thickness"), - params.get("beam_face_thickness"), - params.get("holes_diameter"), - params.get("hole_count"), - mass_kg, - tip_displacement_mm, - max_von_mises_mpa, - int(disp_ok), - int(stress_ok), - int(geo_feasible), - int(disp_ok and stress_ok and geo_feasible), - status, - error_message, - solve_time_s, - iter_path, - notes, - ), - ) - self._conn.commit() - except sqlite3.Error as e: - logger.error("Failed to log trial %d: %s", trial_id, e) - - def export_csv(self) -> Path: - """Export all trials to CSV (overwrite). Returns path.""" - rows = self._conn.execute( - f"SELECT {', '.join(CSV_COLUMNS)} FROM trials ORDER BY study_name, trial_id" - ).fetchall() - - with open(self.csv_path, "w", newline="") as f: - writer = csv.writer(f) - writer.writerow(CSV_COLUMNS) - for row in rows: - writer.writerow([row[col] for col in CSV_COLUMNS]) - - logger.info("Exported %d trials to %s", len(rows), self.csv_path.name) - return self.csv_path - - def query(self, sql: str, params: tuple = ()) -> list[dict]: - """Run an arbitrary SELECT query. Returns list of dicts.""" - rows = self._conn.execute(sql, params).fetchall() - return [dict(row) for row in rows] - - def get_study_summary(self, study_name: str) -> dict[str, Any]: - """Get summary stats for a study.""" - rows = self.query( - "SELECT * FROM trials WHERE study_name = ?", (study_name,) - ) - if not rows: - return {"study_name": study_name, "total": 0} - - complete = [r for r in rows if r["status"] == "COMPLETE"] - feasible = [r for r in complete if r["fully_feasible"]] - masses = [r["mass_kg"] for r in feasible if r["mass_kg"] is not None] - - return { - "study_name": study_name, - "total": len(rows), - "complete": len(complete), - "failed": len(rows) - len(complete), - "feasible": len(feasible), - "best_mass_kg": min(masses) if masses else None, - "solve_rate": len(complete) / len(rows) * 100 if rows else 0, - "feasibility_rate": len(feasible) / len(complete) * 100 if complete else 0, - } - - def close(self) -> None: - """Export CSV and close connection.""" - self.export_csv() - self._conn.close() +"""Persistent trial history โ€” append-only, survives Optuna resets. + +Every trial is logged to `history.db` (SQLite) and exported to `history.csv`. +Never deleted by --clean. Full lineage across all studies and phases. + +Usage: + history = TrialHistory(results_dir) + history.log_trial(study_name, trial_id, params, results, ...) + history.export_csv() + df = history.query("SELECT * FROM trials WHERE mass_kg < 100") +""" + +from __future__ import annotations + +import csv +import json +import logging +import sqlite3 +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Optional + +logger = logging.getLogger(__name__) + +# Schema version โ€” bump if columns change +SCHEMA_VERSION = 1 + +CREATE_TABLE = """ +CREATE TABLE IF NOT EXISTS trials ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + study_name TEXT NOT NULL, + trial_id INTEGER NOT NULL, + iteration TEXT, + timestamp TEXT NOT NULL, + + -- Design variables + beam_half_core_thickness REAL, + beam_face_thickness REAL, + holes_diameter REAL, + hole_count INTEGER, + + -- Results + mass_kg REAL, + tip_displacement_mm REAL, + max_von_mises_mpa REAL, + + -- Constraint checks + disp_feasible INTEGER, -- 0/1 + stress_feasible INTEGER, -- 0/1 + geo_feasible INTEGER, -- 0/1 + fully_feasible INTEGER, -- 0/1 + + -- Meta + status TEXT DEFAULT 'COMPLETE', -- COMPLETE, FAILED, PRUNED + error_message TEXT, + solve_time_s REAL, + iter_path TEXT, + notes TEXT, + + -- Unique constraint: no duplicate (study, trial) pairs + UNIQUE(study_name, trial_id) +); + +CREATE TABLE IF NOT EXISTS schema_version ( + version INTEGER PRIMARY KEY +); +""" + +# Constraint thresholds (from OPTIMIZATION_STRATEGY.md) +DISP_LIMIT_MM = 10.0 +STRESS_LIMIT_MPA = 130.0 + +# CSV column order +CSV_COLUMNS = [ + "study_name", "trial_id", "iteration", "timestamp", + "beam_half_core_thickness", "beam_face_thickness", + "holes_diameter", "hole_count", + "mass_kg", "tip_displacement_mm", "max_von_mises_mpa", + "disp_feasible", "stress_feasible", "geo_feasible", "fully_feasible", + "status", "error_message", "solve_time_s", "iter_path", +] + + +class TrialHistory: + """Append-only trial history database.""" + + def __init__(self, results_dir: Path | str): + self.results_dir = Path(results_dir) + self.results_dir.mkdir(parents=True, exist_ok=True) + + self.db_path = self.results_dir / "history.db" + self.csv_path = self.results_dir / "history.csv" + + self._conn = sqlite3.connect(str(self.db_path)) + self._conn.row_factory = sqlite3.Row + self._conn.execute("PRAGMA journal_mode=WAL") # safe concurrent reads + self._init_schema() + + count = self._conn.execute("SELECT COUNT(*) FROM trials").fetchone()[0] + logger.info("Trial history: %s (%d records)", self.db_path.name, count) + + def _init_schema(self) -> None: + """Create tables if they don't exist.""" + self._conn.executescript(CREATE_TABLE) + + # Check/set schema version + row = self._conn.execute( + "SELECT version FROM schema_version ORDER BY version DESC LIMIT 1" + ).fetchone() + if row is None: + self._conn.execute( + "INSERT INTO schema_version (version) VALUES (?)", + (SCHEMA_VERSION,), + ) + self._conn.commit() + + def log_trial( + self, + study_name: str, + trial_id: int, + params: dict[str, float], + mass_kg: float = float("nan"), + tip_displacement_mm: float = float("nan"), + max_von_mises_mpa: float = float("nan"), + geo_feasible: bool = True, + status: str = "COMPLETE", + error_message: str | None = None, + solve_time_s: float = 0.0, + iter_path: str | None = None, + notes: str | None = None, + iteration_number: int | None = None, + ) -> None: + """Log a single trial result. + + Uses INSERT OR REPLACE so re-runs of the same trial update cleanly. + """ + import math + + disp_ok = ( + not math.isnan(tip_displacement_mm) + and tip_displacement_mm <= DISP_LIMIT_MM + ) + stress_ok = ( + not math.isnan(max_von_mises_mpa) + and max_von_mises_mpa <= STRESS_LIMIT_MPA + ) + + iteration = f"iter{iteration_number:03d}" if iteration_number else None + + try: + self._conn.execute( + """ + INSERT OR REPLACE INTO trials ( + study_name, trial_id, iteration, timestamp, + beam_half_core_thickness, beam_face_thickness, + holes_diameter, hole_count, + mass_kg, tip_displacement_mm, max_von_mises_mpa, + disp_feasible, stress_feasible, geo_feasible, fully_feasible, + status, error_message, solve_time_s, iter_path, notes + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + study_name, + trial_id, + iteration, + datetime.now(timezone.utc).isoformat(), + params.get("beam_half_core_thickness"), + params.get("beam_face_thickness"), + params.get("holes_diameter"), + params.get("hole_count"), + mass_kg, + tip_displacement_mm, + max_von_mises_mpa, + int(disp_ok), + int(stress_ok), + int(geo_feasible), + int(disp_ok and stress_ok and geo_feasible), + status, + error_message, + solve_time_s, + iter_path, + notes, + ), + ) + self._conn.commit() + except sqlite3.Error as e: + logger.error("Failed to log trial %d: %s", trial_id, e) + + def export_csv(self) -> Path: + """Export all trials to CSV (overwrite). Returns path.""" + rows = self._conn.execute( + f"SELECT {', '.join(CSV_COLUMNS)} FROM trials ORDER BY study_name, trial_id" + ).fetchall() + + with open(self.csv_path, "w", newline="") as f: + writer = csv.writer(f) + writer.writerow(CSV_COLUMNS) + for row in rows: + writer.writerow([row[col] for col in CSV_COLUMNS]) + + logger.info("Exported %d trials to %s", len(rows), self.csv_path.name) + return self.csv_path + + def query(self, sql: str, params: tuple = ()) -> list[dict]: + """Run an arbitrary SELECT query. Returns list of dicts.""" + rows = self._conn.execute(sql, params).fetchall() + return [dict(row) for row in rows] + + def get_study_summary(self, study_name: str) -> dict[str, Any]: + """Get summary stats for a study.""" + rows = self.query( + "SELECT * FROM trials WHERE study_name = ?", (study_name,) + ) + if not rows: + return {"study_name": study_name, "total": 0} + + complete = [r for r in rows if r["status"] == "COMPLETE"] + feasible = [r for r in complete if r["fully_feasible"]] + masses = [r["mass_kg"] for r in feasible if r["mass_kg"] is not None] + + return { + "study_name": study_name, + "total": len(rows), + "complete": len(complete), + "failed": len(rows) - len(complete), + "feasible": len(feasible), + "best_mass_kg": min(masses) if masses else None, + "solve_rate": len(complete) / len(rows) * 100 if rows else 0, + "feasibility_rate": len(feasible) / len(complete) * 100 if complete else 0, + } + + def close(self) -> None: + """Export CSV and close connection.""" + self.export_csv() + self._conn.close() diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/history.sync-conflict-20260214-191750-VBCUD7Z.py b/projects/hydrotech-beam/studies/01_doe_landscape/history.sync-conflict-20260214-191750-VBCUD7Z.py new file mode 100644 index 00000000..b5b6509d --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/history.sync-conflict-20260214-191750-VBCUD7Z.py @@ -0,0 +1,235 @@ +"""Persistent trial history โ€” append-only, survives Optuna resets. + +Every trial is logged to `history.db` (SQLite) and exported to `history.csv`. +Never deleted by --clean. Full lineage across all studies and phases. + +Usage: + history = TrialHistory(results_dir) + history.log_trial(study_name, trial_id, params, results, ...) + history.export_csv() + df = history.query("SELECT * FROM trials WHERE mass_kg < 100") +""" + +from __future__ import annotations + +import csv +import json +import logging +import sqlite3 +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Optional + +logger = logging.getLogger(__name__) + +# Schema version โ€” bump if columns change +SCHEMA_VERSION = 1 + +CREATE_TABLE = """ +CREATE TABLE IF NOT EXISTS trials ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + study_name TEXT NOT NULL, + trial_id INTEGER NOT NULL, + iteration TEXT, + timestamp TEXT NOT NULL, + + -- Design variables + beam_half_core_thickness REAL, + beam_face_thickness REAL, + holes_diameter REAL, + hole_count INTEGER, + + -- Results + mass_kg REAL, + tip_displacement_mm REAL, + max_von_mises_mpa REAL, + + -- Constraint checks + disp_feasible INTEGER, -- 0/1 + stress_feasible INTEGER, -- 0/1 + geo_feasible INTEGER, -- 0/1 + fully_feasible INTEGER, -- 0/1 + + -- Meta + status TEXT DEFAULT 'COMPLETE', -- COMPLETE, FAILED, PRUNED + error_message TEXT, + solve_time_s REAL, + iter_path TEXT, + notes TEXT, + + -- Unique constraint: no duplicate (study, trial) pairs + UNIQUE(study_name, trial_id) +); + +CREATE TABLE IF NOT EXISTS schema_version ( + version INTEGER PRIMARY KEY +); +""" + +# Constraint thresholds (from OPTIMIZATION_STRATEGY.md) +DISP_LIMIT_MM = 10.0 +STRESS_LIMIT_MPA = 130.0 + +# CSV column order +CSV_COLUMNS = [ + "study_name", "trial_id", "iteration", "timestamp", + "beam_half_core_thickness", "beam_face_thickness", + "holes_diameter", "hole_count", + "mass_kg", "tip_displacement_mm", "max_von_mises_mpa", + "disp_feasible", "stress_feasible", "geo_feasible", "fully_feasible", + "status", "error_message", "solve_time_s", "iter_path", +] + + +class TrialHistory: + """Append-only trial history database.""" + + def __init__(self, results_dir: Path | str): + self.results_dir = Path(results_dir) + self.results_dir.mkdir(parents=True, exist_ok=True) + + self.db_path = self.results_dir / "history.db" + self.csv_path = self.results_dir / "history.csv" + + self._conn = sqlite3.connect(str(self.db_path)) + self._conn.row_factory = sqlite3.Row + self._conn.execute("PRAGMA journal_mode=WAL") # safe concurrent reads + self._init_schema() + + count = self._conn.execute("SELECT COUNT(*) FROM trials").fetchone()[0] + logger.info("Trial history: %s (%d records)", self.db_path.name, count) + + def _init_schema(self) -> None: + """Create tables if they don't exist.""" + self._conn.executescript(CREATE_TABLE) + + # Check/set schema version + row = self._conn.execute( + "SELECT version FROM schema_version ORDER BY version DESC LIMIT 1" + ).fetchone() + if row is None: + self._conn.execute( + "INSERT INTO schema_version (version) VALUES (?)", + (SCHEMA_VERSION,), + ) + self._conn.commit() + + def log_trial( + self, + study_name: str, + trial_id: int, + params: dict[str, float], + mass_kg: float = float("nan"), + tip_displacement_mm: float = float("nan"), + max_von_mises_mpa: float = float("nan"), + geo_feasible: bool = True, + status: str = "COMPLETE", + error_message: str | None = None, + solve_time_s: float = 0.0, + iter_path: str | None = None, + notes: str | None = None, + iteration_number: int | None = None, + ) -> None: + """Log a single trial result. + + Uses INSERT OR REPLACE so re-runs of the same trial update cleanly. + """ + import math + + disp_ok = ( + not math.isnan(tip_displacement_mm) + and tip_displacement_mm <= DISP_LIMIT_MM + ) + stress_ok = ( + not math.isnan(max_von_mises_mpa) + and max_von_mises_mpa <= STRESS_LIMIT_MPA + ) + + iteration = f"iter{iteration_number:03d}" if iteration_number else None + + try: + self._conn.execute( + """ + INSERT OR REPLACE INTO trials ( + study_name, trial_id, iteration, timestamp, + beam_half_core_thickness, beam_face_thickness, + holes_diameter, hole_count, + mass_kg, tip_displacement_mm, max_von_mises_mpa, + disp_feasible, stress_feasible, geo_feasible, fully_feasible, + status, error_message, solve_time_s, iter_path, notes + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + study_name, + trial_id, + iteration, + datetime.now(timezone.utc).isoformat(), + params.get("beam_half_core_thickness"), + params.get("beam_face_thickness"), + params.get("holes_diameter"), + params.get("hole_count"), + mass_kg, + tip_displacement_mm, + max_von_mises_mpa, + int(disp_ok), + int(stress_ok), + int(geo_feasible), + int(disp_ok and stress_ok and geo_feasible), + status, + error_message, + solve_time_s, + iter_path, + notes, + ), + ) + self._conn.commit() + except sqlite3.Error as e: + logger.error("Failed to log trial %d: %s", trial_id, e) + + def export_csv(self) -> Path: + """Export all trials to CSV (overwrite). Returns path.""" + rows = self._conn.execute( + f"SELECT {', '.join(CSV_COLUMNS)} FROM trials ORDER BY study_name, trial_id" + ).fetchall() + + with open(self.csv_path, "w", newline="") as f: + writer = csv.writer(f) + writer.writerow(CSV_COLUMNS) + for row in rows: + writer.writerow([row[col] for col in CSV_COLUMNS]) + + logger.info("Exported %d trials to %s", len(rows), self.csv_path.name) + return self.csv_path + + def query(self, sql: str, params: tuple = ()) -> list[dict]: + """Run an arbitrary SELECT query. Returns list of dicts.""" + rows = self._conn.execute(sql, params).fetchall() + return [dict(row) for row in rows] + + def get_study_summary(self, study_name: str) -> dict[str, Any]: + """Get summary stats for a study.""" + rows = self.query( + "SELECT * FROM trials WHERE study_name = ?", (study_name,) + ) + if not rows: + return {"study_name": study_name, "total": 0} + + complete = [r for r in rows if r["status"] == "COMPLETE"] + feasible = [r for r in complete if r["fully_feasible"]] + masses = [r["mass_kg"] for r in feasible if r["mass_kg"] is not None] + + return { + "study_name": study_name, + "total": len(rows), + "complete": len(complete), + "failed": len(rows) - len(complete), + "feasible": len(feasible), + "best_mass_kg": min(masses) if masses else None, + "solve_rate": len(complete) / len(rows) * 100 if rows else 0, + "feasibility_rate": len(feasible) / len(complete) * 100 if complete else 0, + } + + def close(self) -> None: + """Export CSV and close connection.""" + self.export_csv() + self._conn.close() diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iteration_manager.py b/projects/hydrotech-beam/studies/01_doe_landscape/iteration_manager.py old mode 100644 new mode 100755 index 61cb42b4..4315cd38 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iteration_manager.py +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iteration_manager.py @@ -1,249 +1,249 @@ -"""Smart iteration folder management for Hydrotech Beam optimization. - -Manages iteration folders with intelligent retention: -- Each iteration gets a full copy of model files (openable in NX for debug) -- Last N iterations: keep full model files (rolling window) -- Best K iterations: keep full model files (by objective value) -- All others: strip model files, keep only solver outputs + params - -This gives debuggability (open any recent/best iteration in NX) while -keeping disk usage bounded. - -References: - CEO design brief (2026-02-11): "all models properly saved in their - iteration folder, keep last 10, keep best 3, delete stacking models" -""" - -from __future__ import annotations - -import json -import logging -import shutil -from dataclasses import dataclass, field -from pathlib import Path -from typing import Optional - -logger = logging.getLogger(__name__) - -# NX model file extensions (copied to each iteration) -MODEL_EXTENSIONS = {".prt", ".fem", ".sim"} - -# Solver output extensions (always kept, even after stripping) -KEEP_EXTENSIONS = {".op2", ".f06", ".dat", ".log", ".json", ".txt", ".csv"} - -# Default retention policy -DEFAULT_KEEP_RECENT = 10 # keep last N iterations with full models -DEFAULT_KEEP_BEST = 3 # keep best K iterations with full models - - -@dataclass -class IterationInfo: - """Metadata for a single iteration.""" - - number: int - path: Path - mass: float = float("inf") - displacement: float = float("inf") - stress: float = float("inf") - feasible: bool = False - has_models: bool = True # False after stripping - - -@dataclass -class IterationManager: - """Manages iteration folders with smart retention. - - Usage: - mgr = IterationManager(study_dir, master_model_dir) - - # Before each trial: - iter_dir = mgr.prepare_iteration(iteration_number) - - # After trial completes: - mgr.record_result(iteration_number, mass=..., displacement=..., stress=...) - - # Periodically or at study end: - mgr.apply_retention() - """ - - study_dir: Path - master_model_dir: Path - keep_recent: int = DEFAULT_KEEP_RECENT - keep_best: int = DEFAULT_KEEP_BEST - _iterations: dict[int, IterationInfo] = field(default_factory=dict, repr=False) - - def __post_init__(self) -> None: - self.iterations_dir = self.study_dir / "iterations" - self.iterations_dir.mkdir(parents=True, exist_ok=True) - - # Scan existing iterations (for resume support) - for d in sorted(self.iterations_dir.iterdir()): - if d.is_dir() and d.name.startswith("iter"): - try: - num = int(d.name.replace("iter", "")) - info = IterationInfo(number=num, path=d) - - # Load results if available - results_file = d / "results.json" - if results_file.exists(): - data = json.loads(results_file.read_text()) - info.mass = data.get("mass_kg", float("inf")) - info.displacement = data.get("tip_displacement_mm", float("inf")) - info.stress = data.get("max_von_mises_mpa", float("inf")) - info.feasible = ( - info.displacement <= 10.0 and info.stress <= 130.0 - ) - - # Check if model files are present - info.has_models = any( - f.suffix in MODEL_EXTENSIONS for f in d.iterdir() - ) - - self._iterations[num] = info - except (ValueError, json.JSONDecodeError): - continue - - if self._iterations: - logger.info( - "Loaded %d existing iterations (resume support)", - len(self._iterations), - ) - - def prepare_iteration(self, iteration_number: int) -> Path: - """Set up an iteration folder with fresh model copies. - - Copies all model files from master_model_dir to the iteration folder. - All paths are resolved to absolute to avoid NX reference issues. - - Args: - iteration_number: Trial number (1-indexed). - - Returns: - Absolute path to the iteration folder. - """ - iter_dir = (self.iterations_dir / f"iter{iteration_number:03d}").resolve() - - # Clean up if exists (failed previous run) - if iter_dir.exists(): - shutil.rmtree(iter_dir) - - iter_dir.mkdir(parents=True) - - # Copy ALL model files (so NX can resolve references within the folder) - master = self.master_model_dir.resolve() - copied = 0 - for ext in MODEL_EXTENSIONS: - for src in master.glob(f"*{ext}"): - shutil.copy2(src, iter_dir / src.name) - copied += 1 - - logger.info( - "Prepared iter%03d: copied %d model files to %s", - iteration_number, copied, iter_dir, - ) - - # Track iteration - self._iterations[iteration_number] = IterationInfo( - number=iteration_number, - path=iter_dir, - has_models=True, - ) - - return iter_dir - - def record_result( - self, - iteration_number: int, - mass: float, - displacement: float, - stress: float, - ) -> None: - """Record results for an iteration and run retention check. - - Args: - iteration_number: Trial number. - mass: Extracted mass in kg. - displacement: Tip displacement in mm. - stress: Max von Mises stress in MPa. - """ - if iteration_number in self._iterations: - info = self._iterations[iteration_number] - info.mass = mass - info.displacement = displacement - info.stress = stress - info.feasible = displacement <= 10.0 and stress <= 130.0 - - # Apply retention every 5 iterations to keep disk in check - if iteration_number % 5 == 0: - self.apply_retention() - - def apply_retention(self) -> None: - """Apply the smart retention policy. - - Keep full model files for: - 1. Last `keep_recent` iterations (rolling window) - 2. Best `keep_best` iterations (by mass, feasible first) - - Strip model files from everything else (keep solver outputs only). - """ - if not self._iterations: - return - - all_nums = sorted(self._iterations.keys()) - - # Set 1: Last N iterations - recent_set = set(all_nums[-self.keep_recent:]) - - # Set 2: Best K by objective (feasible first, then lowest mass) - sorted_by_quality = sorted( - self._iterations.values(), - key=lambda info: ( - 0 if info.feasible else 1, # feasible first - info.mass, # then lowest mass - ), - ) - best_set = {info.number for info in sorted_by_quality[:self.keep_best]} - - # Keep set = recent โˆช best - keep_set = recent_set | best_set - - # Strip model files from everything NOT in keep set - stripped = 0 - for num, info in self._iterations.items(): - if num not in keep_set and info.has_models: - self._strip_models(info) - stripped += 1 - - if stripped > 0: - logger.info( - "Retention: kept %d recent + %d best, stripped %d iterations", - len(recent_set), len(best_set), stripped, - ) - - def _strip_models(self, info: IterationInfo) -> None: - """Remove model files from an iteration folder, keep solver outputs.""" - if not info.path.exists(): - return - - removed = 0 - for f in info.path.iterdir(): - if f.is_file() and f.suffix in MODEL_EXTENSIONS: - f.unlink() - removed += 1 - - info.has_models = False - if removed > 0: - logger.debug( - "Stripped %d model files from iter%03d", - removed, info.number, - ) - - def get_best_iterations(self, n: int = 3) -> list[IterationInfo]: - """Return the N best iterations (feasible first, then lowest mass).""" - return sorted( - self._iterations.values(), - key=lambda info: ( - 0 if info.feasible else 1, - info.mass, - ), - )[:n] +"""Smart iteration folder management for Hydrotech Beam optimization. + +Manages iteration folders with intelligent retention: +- Each iteration gets a full copy of model files (openable in NX for debug) +- Last N iterations: keep full model files (rolling window) +- Best K iterations: keep full model files (by objective value) +- All others: strip model files, keep only solver outputs + params + +This gives debuggability (open any recent/best iteration in NX) while +keeping disk usage bounded. + +References: + CEO design brief (2026-02-11): "all models properly saved in their + iteration folder, keep last 10, keep best 3, delete stacking models" +""" + +from __future__ import annotations + +import json +import logging +import shutil +from dataclasses import dataclass, field +from pathlib import Path +from typing import Optional + +logger = logging.getLogger(__name__) + +# NX model file extensions (copied to each iteration) +MODEL_EXTENSIONS = {".prt", ".fem", ".sim"} + +# Solver output extensions (always kept, even after stripping) +KEEP_EXTENSIONS = {".op2", ".f06", ".dat", ".log", ".json", ".txt", ".csv"} + +# Default retention policy +DEFAULT_KEEP_RECENT = 10 # keep last N iterations with full models +DEFAULT_KEEP_BEST = 3 # keep best K iterations with full models + + +@dataclass +class IterationInfo: + """Metadata for a single iteration.""" + + number: int + path: Path + mass: float = float("inf") + displacement: float = float("inf") + stress: float = float("inf") + feasible: bool = False + has_models: bool = True # False after stripping + + +@dataclass +class IterationManager: + """Manages iteration folders with smart retention. + + Usage: + mgr = IterationManager(study_dir, master_model_dir) + + # Before each trial: + iter_dir = mgr.prepare_iteration(iteration_number) + + # After trial completes: + mgr.record_result(iteration_number, mass=..., displacement=..., stress=...) + + # Periodically or at study end: + mgr.apply_retention() + """ + + study_dir: Path + master_model_dir: Path + keep_recent: int = DEFAULT_KEEP_RECENT + keep_best: int = DEFAULT_KEEP_BEST + _iterations: dict[int, IterationInfo] = field(default_factory=dict, repr=False) + + def __post_init__(self) -> None: + self.iterations_dir = self.study_dir / "iterations" + self.iterations_dir.mkdir(parents=True, exist_ok=True) + + # Scan existing iterations (for resume support) + for d in sorted(self.iterations_dir.iterdir()): + if d.is_dir() and d.name.startswith("iter"): + try: + num = int(d.name.replace("iter", "")) + info = IterationInfo(number=num, path=d) + + # Load results if available + results_file = d / "results.json" + if results_file.exists(): + data = json.loads(results_file.read_text()) + info.mass = data.get("mass_kg", float("inf")) + info.displacement = data.get("tip_displacement_mm", float("inf")) + info.stress = data.get("max_von_mises_mpa", float("inf")) + info.feasible = ( + info.displacement <= 10.0 and info.stress <= 130.0 + ) + + # Check if model files are present + info.has_models = any( + f.suffix in MODEL_EXTENSIONS for f in d.iterdir() + ) + + self._iterations[num] = info + except (ValueError, json.JSONDecodeError): + continue + + if self._iterations: + logger.info( + "Loaded %d existing iterations (resume support)", + len(self._iterations), + ) + + def prepare_iteration(self, iteration_number: int) -> Path: + """Set up an iteration folder with fresh model copies. + + Copies all model files from master_model_dir to the iteration folder. + All paths are resolved to absolute to avoid NX reference issues. + + Args: + iteration_number: Trial number (1-indexed). + + Returns: + Absolute path to the iteration folder. + """ + iter_dir = (self.iterations_dir / f"iter{iteration_number:03d}").resolve() + + # Clean up if exists (failed previous run) + if iter_dir.exists(): + shutil.rmtree(iter_dir) + + iter_dir.mkdir(parents=True) + + # Copy ALL model files (so NX can resolve references within the folder) + master = self.master_model_dir.resolve() + copied = 0 + for ext in MODEL_EXTENSIONS: + for src in master.glob(f"*{ext}"): + shutil.copy2(src, iter_dir / src.name) + copied += 1 + + logger.info( + "Prepared iter%03d: copied %d model files to %s", + iteration_number, copied, iter_dir, + ) + + # Track iteration + self._iterations[iteration_number] = IterationInfo( + number=iteration_number, + path=iter_dir, + has_models=True, + ) + + return iter_dir + + def record_result( + self, + iteration_number: int, + mass: float, + displacement: float, + stress: float, + ) -> None: + """Record results for an iteration and run retention check. + + Args: + iteration_number: Trial number. + mass: Extracted mass in kg. + displacement: Tip displacement in mm. + stress: Max von Mises stress in MPa. + """ + if iteration_number in self._iterations: + info = self._iterations[iteration_number] + info.mass = mass + info.displacement = displacement + info.stress = stress + info.feasible = displacement <= 10.0 and stress <= 130.0 + + # Apply retention every 5 iterations to keep disk in check + if iteration_number % 5 == 0: + self.apply_retention() + + def apply_retention(self) -> None: + """Apply the smart retention policy. + + Keep full model files for: + 1. Last `keep_recent` iterations (rolling window) + 2. Best `keep_best` iterations (by mass, feasible first) + + Strip model files from everything else (keep solver outputs only). + """ + if not self._iterations: + return + + all_nums = sorted(self._iterations.keys()) + + # Set 1: Last N iterations + recent_set = set(all_nums[-self.keep_recent:]) + + # Set 2: Best K by objective (feasible first, then lowest mass) + sorted_by_quality = sorted( + self._iterations.values(), + key=lambda info: ( + 0 if info.feasible else 1, # feasible first + info.mass, # then lowest mass + ), + ) + best_set = {info.number for info in sorted_by_quality[:self.keep_best]} + + # Keep set = recent โˆช best + keep_set = recent_set | best_set + + # Strip model files from everything NOT in keep set + stripped = 0 + for num, info in self._iterations.items(): + if num not in keep_set and info.has_models: + self._strip_models(info) + stripped += 1 + + if stripped > 0: + logger.info( + "Retention: kept %d recent + %d best, stripped %d iterations", + len(recent_set), len(best_set), stripped, + ) + + def _strip_models(self, info: IterationInfo) -> None: + """Remove model files from an iteration folder, keep solver outputs.""" + if not info.path.exists(): + return + + removed = 0 + for f in info.path.iterdir(): + if f.is_file() and f.suffix in MODEL_EXTENSIONS: + f.unlink() + removed += 1 + + info.has_models = False + if removed > 0: + logger.debug( + "Stripped %d model files from iter%03d", + removed, info.number, + ) + + def get_best_iterations(self, n: int = 3) -> list[IterationInfo]: + """Return the N best iterations (feasible first, then lowest mass).""" + return sorted( + self._iterations.values(), + key=lambda info: ( + 0 if info.feasible else 1, + info.mass, + ), + )[:n] diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iteration_manager.sync-conflict-20260214-191804-VBCUD7Z.py b/projects/hydrotech-beam/studies/01_doe_landscape/iteration_manager.sync-conflict-20260214-191804-VBCUD7Z.py new file mode 100644 index 00000000..61cb42b4 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iteration_manager.sync-conflict-20260214-191804-VBCUD7Z.py @@ -0,0 +1,249 @@ +"""Smart iteration folder management for Hydrotech Beam optimization. + +Manages iteration folders with intelligent retention: +- Each iteration gets a full copy of model files (openable in NX for debug) +- Last N iterations: keep full model files (rolling window) +- Best K iterations: keep full model files (by objective value) +- All others: strip model files, keep only solver outputs + params + +This gives debuggability (open any recent/best iteration in NX) while +keeping disk usage bounded. + +References: + CEO design brief (2026-02-11): "all models properly saved in their + iteration folder, keep last 10, keep best 3, delete stacking models" +""" + +from __future__ import annotations + +import json +import logging +import shutil +from dataclasses import dataclass, field +from pathlib import Path +from typing import Optional + +logger = logging.getLogger(__name__) + +# NX model file extensions (copied to each iteration) +MODEL_EXTENSIONS = {".prt", ".fem", ".sim"} + +# Solver output extensions (always kept, even after stripping) +KEEP_EXTENSIONS = {".op2", ".f06", ".dat", ".log", ".json", ".txt", ".csv"} + +# Default retention policy +DEFAULT_KEEP_RECENT = 10 # keep last N iterations with full models +DEFAULT_KEEP_BEST = 3 # keep best K iterations with full models + + +@dataclass +class IterationInfo: + """Metadata for a single iteration.""" + + number: int + path: Path + mass: float = float("inf") + displacement: float = float("inf") + stress: float = float("inf") + feasible: bool = False + has_models: bool = True # False after stripping + + +@dataclass +class IterationManager: + """Manages iteration folders with smart retention. + + Usage: + mgr = IterationManager(study_dir, master_model_dir) + + # Before each trial: + iter_dir = mgr.prepare_iteration(iteration_number) + + # After trial completes: + mgr.record_result(iteration_number, mass=..., displacement=..., stress=...) + + # Periodically or at study end: + mgr.apply_retention() + """ + + study_dir: Path + master_model_dir: Path + keep_recent: int = DEFAULT_KEEP_RECENT + keep_best: int = DEFAULT_KEEP_BEST + _iterations: dict[int, IterationInfo] = field(default_factory=dict, repr=False) + + def __post_init__(self) -> None: + self.iterations_dir = self.study_dir / "iterations" + self.iterations_dir.mkdir(parents=True, exist_ok=True) + + # Scan existing iterations (for resume support) + for d in sorted(self.iterations_dir.iterdir()): + if d.is_dir() and d.name.startswith("iter"): + try: + num = int(d.name.replace("iter", "")) + info = IterationInfo(number=num, path=d) + + # Load results if available + results_file = d / "results.json" + if results_file.exists(): + data = json.loads(results_file.read_text()) + info.mass = data.get("mass_kg", float("inf")) + info.displacement = data.get("tip_displacement_mm", float("inf")) + info.stress = data.get("max_von_mises_mpa", float("inf")) + info.feasible = ( + info.displacement <= 10.0 and info.stress <= 130.0 + ) + + # Check if model files are present + info.has_models = any( + f.suffix in MODEL_EXTENSIONS for f in d.iterdir() + ) + + self._iterations[num] = info + except (ValueError, json.JSONDecodeError): + continue + + if self._iterations: + logger.info( + "Loaded %d existing iterations (resume support)", + len(self._iterations), + ) + + def prepare_iteration(self, iteration_number: int) -> Path: + """Set up an iteration folder with fresh model copies. + + Copies all model files from master_model_dir to the iteration folder. + All paths are resolved to absolute to avoid NX reference issues. + + Args: + iteration_number: Trial number (1-indexed). + + Returns: + Absolute path to the iteration folder. + """ + iter_dir = (self.iterations_dir / f"iter{iteration_number:03d}").resolve() + + # Clean up if exists (failed previous run) + if iter_dir.exists(): + shutil.rmtree(iter_dir) + + iter_dir.mkdir(parents=True) + + # Copy ALL model files (so NX can resolve references within the folder) + master = self.master_model_dir.resolve() + copied = 0 + for ext in MODEL_EXTENSIONS: + for src in master.glob(f"*{ext}"): + shutil.copy2(src, iter_dir / src.name) + copied += 1 + + logger.info( + "Prepared iter%03d: copied %d model files to %s", + iteration_number, copied, iter_dir, + ) + + # Track iteration + self._iterations[iteration_number] = IterationInfo( + number=iteration_number, + path=iter_dir, + has_models=True, + ) + + return iter_dir + + def record_result( + self, + iteration_number: int, + mass: float, + displacement: float, + stress: float, + ) -> None: + """Record results for an iteration and run retention check. + + Args: + iteration_number: Trial number. + mass: Extracted mass in kg. + displacement: Tip displacement in mm. + stress: Max von Mises stress in MPa. + """ + if iteration_number in self._iterations: + info = self._iterations[iteration_number] + info.mass = mass + info.displacement = displacement + info.stress = stress + info.feasible = displacement <= 10.0 and stress <= 130.0 + + # Apply retention every 5 iterations to keep disk in check + if iteration_number % 5 == 0: + self.apply_retention() + + def apply_retention(self) -> None: + """Apply the smart retention policy. + + Keep full model files for: + 1. Last `keep_recent` iterations (rolling window) + 2. Best `keep_best` iterations (by mass, feasible first) + + Strip model files from everything else (keep solver outputs only). + """ + if not self._iterations: + return + + all_nums = sorted(self._iterations.keys()) + + # Set 1: Last N iterations + recent_set = set(all_nums[-self.keep_recent:]) + + # Set 2: Best K by objective (feasible first, then lowest mass) + sorted_by_quality = sorted( + self._iterations.values(), + key=lambda info: ( + 0 if info.feasible else 1, # feasible first + info.mass, # then lowest mass + ), + ) + best_set = {info.number for info in sorted_by_quality[:self.keep_best]} + + # Keep set = recent โˆช best + keep_set = recent_set | best_set + + # Strip model files from everything NOT in keep set + stripped = 0 + for num, info in self._iterations.items(): + if num not in keep_set and info.has_models: + self._strip_models(info) + stripped += 1 + + if stripped > 0: + logger.info( + "Retention: kept %d recent + %d best, stripped %d iterations", + len(recent_set), len(best_set), stripped, + ) + + def _strip_models(self, info: IterationInfo) -> None: + """Remove model files from an iteration folder, keep solver outputs.""" + if not info.path.exists(): + return + + removed = 0 + for f in info.path.iterdir(): + if f.is_file() and f.suffix in MODEL_EXTENSIONS: + f.unlink() + removed += 1 + + info.has_models = False + if removed > 0: + logger.debug( + "Stripped %d model files from iter%03d", + removed, info.number, + ) + + def get_best_iterations(self, n: int = 3) -> list[IterationInfo]: + """Return the N best iterations (feasible first, then lowest mass).""" + return sorted( + self._iterations.values(), + key=lambda info: ( + 0 if info.feasible else 1, + info.mass, + ), + )[:n] diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..e456e7ce --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1133.0042670507723 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.txt index 3f08d065..cf67d22d 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_mass.txt @@ -1 +1 @@ -p173=1133.0042670507723 +1133.0042670507721 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_part_properties.json new file mode 100644 index 00000000..8f77b9bd --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1133.0042670507721, + "mass_g": 1133004.2670507722, + "volume_mm3": 143928387.58266923, + "surface_area_mm2": 9629135.962798359, + "center_of_gravity_mm": [ + 2500.0, + -3.9849188813805173e-13, + -9.732850072582063e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/results.json index bde9c72e..021f1b4c 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 1, - "mass_kg": NaN, - "tip_displacement_mm": 19.556875228881836, - "max_von_mises_mpa": 117.484125, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 1, + "mass_kg": 1133.0042670507721, + "tip_displacement_mm": 19.556875228881836, + "max_von_mises_mpa": 117.484125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..199c724a --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter001/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 1, + "mass_kg": NaN, + "tip_displacement_mm": 19.556875228881836, + "max_von_mises_mpa": 117.484125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..03c459a1 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1266.203458914867 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.txt index c8a2a38c..dc61b59b 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_mass.txt @@ -1 +1 @@ -p173=1266.203458914867 +1266.2034589148668 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_part_properties.json new file mode 100644 index 00000000..b26debae --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1266.2034589148668, + "mass_g": 1266203.458914867, + "volume_mm3": 160849016.63044548, + "surface_area_mm2": 10302410.46378126, + "center_of_gravity_mm": [ + 2499.9999999999995, + -4.5228728964456235e-13, + -1.0297952705302583e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/params.sync-conflict-20260214-191807-VBCUD7Z.exp b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/params.sync-conflict-20260214-191807-VBCUD7Z.exp new file mode 100644 index 00000000..0f3d1c3b --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/params.sync-conflict-20260214-191807-VBCUD7Z.exp @@ -0,0 +1,4 @@ +beam_half_core_thickness=16.7813185433211 [MilliMeter] +beam_face_thickness=26.83364680743843 [MilliMeter] +holes_diameter=192.42062402658527 [MilliMeter] +hole_count=8.0 [Constant] diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/results.json index 3c813f45..3cbdbac7 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 2, - "mass_kg": NaN, - "tip_displacement_mm": 24.064523696899414, - "max_von_mises_mpa": 398.4295, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 2, + "mass_kg": 1266.2034589148668, + "tip_displacement_mm": 24.064523696899414, + "max_von_mises_mpa": 398.4295, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..c710d2bc --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter002/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 2, + "mass_kg": NaN, + "tip_displacement_mm": 24.064523696899414, + "max_von_mises_mpa": 398.4295, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..1574f276 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1109.9630535376045 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.txt index 94a0a212..3da02335 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_mass.txt @@ -1 +1 @@ -p173=1109.9630535376045 +1109.963053537604 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_part_properties.json new file mode 100644 index 00000000..77a740e3 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1109.963053537604, + "mass_g": 1109963.0535376042, + "volume_mm3": 141001404.15874043, + "surface_area_mm2": 10224850.427177388, + "center_of_gravity_mm": [ + 2500.0, + -4.038805106202274e-13, + -8.979130674139967e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/params.json index 9c281a22..21523bcb 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 3, - "expressions": { - "beam_half_core_thickness": 16.654851585575436, - "beam_face_thickness": 25.92976843726266, - "holes_diameter": 249.7752118546045, - "hole_count": 7.0 - }, - "trial_input": { - "beam_half_core_thickness": 16.654851585575436, - "beam_face_thickness": 25.92976843726266, - "holes_diameter": 249.7752118546045, - "hole_count": 7 - } +{ + "iteration": 3, + "expressions": { + "beam_half_core_thickness": 16.654851585575436, + "beam_face_thickness": 25.92976843726266, + "holes_diameter": 249.7752118546045, + "hole_count": 7.0 + }, + "trial_input": { + "beam_half_core_thickness": 16.654851585575436, + "beam_face_thickness": 25.92976843726266, + "holes_diameter": 249.7752118546045, + "hole_count": 7 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/params.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/params.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..21523bcb --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/params.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 3, + "expressions": { + "beam_half_core_thickness": 16.654851585575436, + "beam_face_thickness": 25.92976843726266, + "holes_diameter": 249.7752118546045, + "hole_count": 7.0 + }, + "trial_input": { + "beam_half_core_thickness": 16.654851585575436, + "beam_face_thickness": 25.92976843726266, + "holes_diameter": 249.7752118546045, + "hole_count": 7 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/results.json index 607de7c8..c176b40d 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 3, - "mass_kg": NaN, - "tip_displacement_mm": 18.68077850341797, - "max_von_mises_mpa": 114.6584609375, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 3, + "mass_kg": 1109.963053537604, + "tip_displacement_mm": 18.68077850341797, + "max_von_mises_mpa": 114.6584609375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..80c40a57 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter003/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 3, + "mass_kg": NaN, + "tip_displacement_mm": 18.68077850341797, + "max_von_mises_mpa": 114.6584609375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..d7fd3449 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1718.1024059936985 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.txt index f8160e44..73124a12 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_mass.txt @@ -1 +1 @@ -p173=1718.1024059936985 +1718.102405993698 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_part_properties.json new file mode 100644 index 00000000..d8dba301 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1718.102405993698, + "mass_g": 1718102.4059936982, + "volume_mm3": 218254878.8101751, + "surface_area_mm2": 9526891.624035092, + "center_of_gravity_mm": [ + 2499.9999999999995, + -2.513603829927714e-13, + -5.326334731363442e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/params.json index 58350c84..a6ed9abf 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 4, - "expressions": { - "beam_half_core_thickness": 39.4879581560391, - "beam_face_thickness": 37.70634303203751, - "holes_diameter": 317.49333407316067, - "hole_count": 10.0 - }, - "trial_input": { - "beam_half_core_thickness": 39.4879581560391, - "beam_face_thickness": 37.70634303203751, - "holes_diameter": 317.49333407316067, - "hole_count": 10 - } +{ + "iteration": 4, + "expressions": { + "beam_half_core_thickness": 39.4879581560391, + "beam_face_thickness": 37.70634303203751, + "holes_diameter": 317.49333407316067, + "hole_count": 10.0 + }, + "trial_input": { + "beam_half_core_thickness": 39.4879581560391, + "beam_face_thickness": 37.70634303203751, + "holes_diameter": 317.49333407316067, + "hole_count": 10 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..a6ed9abf --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 4, + "expressions": { + "beam_half_core_thickness": 39.4879581560391, + "beam_face_thickness": 37.70634303203751, + "holes_diameter": 317.49333407316067, + "hole_count": 10.0 + }, + "trial_input": { + "beam_half_core_thickness": 39.4879581560391, + "beam_face_thickness": 37.70634303203751, + "holes_diameter": 317.49333407316067, + "hole_count": 10 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/results.json index 610183c9..2ecf346c 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 4, - "mass_kg": NaN, - "tip_displacement_mm": 12.852874755859375, - "max_von_mises_mpa": 81.574234375, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 4, + "mass_kg": 1718.102405993698, + "tip_displacement_mm": 12.852874755859375, + "max_von_mises_mpa": 81.574234375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..66844f6b --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter004/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 4, + "mass_kg": NaN, + "tip_displacement_mm": 12.852874755859375, + "max_von_mises_mpa": 81.574234375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..fa249cc6 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1205.9185440163983 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.txt index 26adfa2e..6c6ac70d 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_mass.txt @@ -1 +1 @@ -p173=1205.9185440163983 +1205.918544016398 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_part_properties.json new file mode 100644 index 00000000..fb1d6d7c --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1205.918544016398, + "mass_g": 1205918.544016398, + "volume_mm3": 153190871.95330262, + "surface_area_mm2": 10217809.311285218, + "center_of_gravity_mm": [ + 2532.261923730059, + -3.772285845536082e-13, + -8.015628081698969e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/params.json index c54559a2..7c8e334d 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 5, - "expressions": { - "beam_half_core_thickness": 18.667249127790498, - "beam_face_thickness": 27.961709646337493, - "holes_diameter": 215.2919645872769, - "hole_count": 12.0 - }, - "trial_input": { - "beam_half_core_thickness": 18.667249127790498, - "beam_face_thickness": 27.961709646337493, - "holes_diameter": 215.2919645872769, - "hole_count": 12 - } +{ + "iteration": 5, + "expressions": { + "beam_half_core_thickness": 18.667249127790498, + "beam_face_thickness": 27.961709646337493, + "holes_diameter": 215.2919645872769, + "hole_count": 12.0 + }, + "trial_input": { + "beam_half_core_thickness": 18.667249127790498, + "beam_face_thickness": 27.961709646337493, + "holes_diameter": 215.2919645872769, + "hole_count": 12 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..7c8e334d --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 5, + "expressions": { + "beam_half_core_thickness": 18.667249127790498, + "beam_face_thickness": 27.961709646337493, + "holes_diameter": 215.2919645872769, + "hole_count": 12.0 + }, + "trial_input": { + "beam_half_core_thickness": 18.667249127790498, + "beam_face_thickness": 27.961709646337493, + "holes_diameter": 215.2919645872769, + "hole_count": 12 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/results.json index cd3fab36..a042887b 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 5, - "mass_kg": NaN, - "tip_displacement_mm": 17.29557228088379, - "max_von_mises_mpa": 106.283703125, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 5, + "mass_kg": 1205.918544016398, + "tip_displacement_mm": 17.29557228088379, + "max_von_mises_mpa": 106.283703125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..87297c17 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter005/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 5, + "mass_kg": NaN, + "tip_displacement_mm": 17.29557228088379, + "max_von_mises_mpa": 106.283703125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..fd0e5682 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1085.4467002717115 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.txt index 4f9d3cf2..9eac0b0c 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_mass.txt @@ -1 +1 @@ -p173=1085.4467002717115 +1085.4467002717115 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_part_properties.json new file mode 100644 index 00000000..897d02be --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1085.4467002717115, + "mass_g": 1085446.7002717115, + "volume_mm3": 137887030.0141911, + "surface_area_mm2": 10367090.942113385, + "center_of_gravity_mm": [ + 2509.0298234434026, + -3.739827148611049e-13, + -8.302431198574242e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/params.json index 42eda315..246ddd13 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 6, - "expressions": { - "beam_half_core_thickness": 10.145147355948776, - "beam_face_thickness": 33.3870327520718, - "holes_diameter": 197.6501835498656, - "hole_count": 11.0 - }, - "trial_input": { - "beam_half_core_thickness": 10.145147355948776, - "beam_face_thickness": 33.3870327520718, - "holes_diameter": 197.6501835498656, - "hole_count": 11 - } +{ + "iteration": 6, + "expressions": { + "beam_half_core_thickness": 10.145147355948776, + "beam_face_thickness": 33.3870327520718, + "holes_diameter": 197.6501835498656, + "hole_count": 11.0 + }, + "trial_input": { + "beam_half_core_thickness": 10.145147355948776, + "beam_face_thickness": 33.3870327520718, + "holes_diameter": 197.6501835498656, + "hole_count": 11 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..246ddd13 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 6, + "expressions": { + "beam_half_core_thickness": 10.145147355948776, + "beam_face_thickness": 33.3870327520718, + "holes_diameter": 197.6501835498656, + "hole_count": 11.0 + }, + "trial_input": { + "beam_half_core_thickness": 10.145147355948776, + "beam_face_thickness": 33.3870327520718, + "holes_diameter": 197.6501835498656, + "hole_count": 11 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/results.json index a4a699c3..d3ff3b03 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 6, - "mass_kg": NaN, - "tip_displacement_mm": 17.468721389770508, - "max_von_mises_mpa": 108.2625078125, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 6, + "mass_kg": 1085.4467002717115, + "tip_displacement_mm": 17.468721389770508, + "max_von_mises_mpa": 108.2625078125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/results.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/results.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..7abcef12 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter006/results.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 6, + "mass_kg": NaN, + "tip_displacement_mm": 17.468721389770508, + "max_von_mises_mpa": 108.2625078125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..1e59b905 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1722.6740568945102 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.txt index 1170ab79..492d6787 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_mass.txt @@ -1 +1 @@ -p173=1722.6740568945102 +1722.674056894509 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_part_properties.json new file mode 100644 index 00000000..b6ed8cbc --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1722.674056894509, + "mass_g": 1722674.0568945091, + "volume_mm3": 218835627.1461522, + "surface_area_mm2": 10233741.689197954, + "center_of_gravity_mm": [ + 2499.9999999999995, + -3.37147834654746e-13, + -6.319681629445577e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/params.json index 7e8b070d..aab48e53 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 7, - "expressions": { - "beam_half_core_thickness": 36.424864472055134, - "beam_face_thickness": 22.317342276314122, - "holes_diameter": 221.080294100253, - "hole_count": 5.0 - }, - "trial_input": { - "beam_half_core_thickness": 36.424864472055134, - "beam_face_thickness": 22.317342276314122, - "holes_diameter": 221.080294100253, - "hole_count": 5 - } +{ + "iteration": 7, + "expressions": { + "beam_half_core_thickness": 36.424864472055134, + "beam_face_thickness": 22.317342276314122, + "holes_diameter": 221.080294100253, + "hole_count": 5.0 + }, + "trial_input": { + "beam_half_core_thickness": 36.424864472055134, + "beam_face_thickness": 22.317342276314122, + "holes_diameter": 221.080294100253, + "hole_count": 5 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/params.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/params.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..aab48e53 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/params.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 7, + "expressions": { + "beam_half_core_thickness": 36.424864472055134, + "beam_face_thickness": 22.317342276314122, + "holes_diameter": 221.080294100253, + "hole_count": 5.0 + }, + "trial_input": { + "beam_half_core_thickness": 36.424864472055134, + "beam_face_thickness": 22.317342276314122, + "holes_diameter": 221.080294100253, + "hole_count": 5 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/results.json index c6f21f5d..04885e8f 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 7, - "mass_kg": NaN, - "tip_displacement_mm": 15.069981575012207, - "max_von_mises_mpa": 94.8715, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 7, + "mass_kg": 1722.674056894509, + "tip_displacement_mm": 15.069981575012207, + "max_von_mises_mpa": 94.8715, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..d554c919 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter007/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 7, + "mass_kg": NaN, + "tip_displacement_mm": 15.069981575012207, + "max_von_mises_mpa": 94.8715, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..a616fea8 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1182.0927892029772 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.txt index 8dfedf85..5edacb9b 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_mass.txt @@ -1 +1 @@ -p173=1182.0927892029772 +1182.0927892029772 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_part_properties.json new file mode 100644 index 00000000..3d6ec97d --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1182.0927892029772, + "mass_g": 1182092.7892029772, + "volume_mm3": 150164226.27070343, + "surface_area_mm2": 9735417.428499741, + "center_of_gravity_mm": [ + 2499.9999999999995, + -3.655180487736917e-13, + -8.402491975466155e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/params.json index 5285dfb7..c8efd532 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 8, - "expressions": { - "beam_half_core_thickness": 21.20971666430637, - "beam_face_thickness": 27.18728441912208, - "holes_diameter": 333.33951480703604, - "hole_count": 7.0 - }, - "trial_input": { - "beam_half_core_thickness": 21.20971666430637, - "beam_face_thickness": 27.18728441912208, - "holes_diameter": 333.33951480703604, - "hole_count": 7 - } +{ + "iteration": 8, + "expressions": { + "beam_half_core_thickness": 21.20971666430637, + "beam_face_thickness": 27.18728441912208, + "holes_diameter": 333.33951480703604, + "hole_count": 7.0 + }, + "trial_input": { + "beam_half_core_thickness": 21.20971666430637, + "beam_face_thickness": 27.18728441912208, + "holes_diameter": 333.33951480703604, + "hole_count": 7 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..c8efd532 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 8, + "expressions": { + "beam_half_core_thickness": 21.20971666430637, + "beam_face_thickness": 27.18728441912208, + "holes_diameter": 333.33951480703604, + "hole_count": 7.0 + }, + "trial_input": { + "beam_half_core_thickness": 21.20971666430637, + "beam_face_thickness": 27.18728441912208, + "holes_diameter": 333.33951480703604, + "hole_count": 7 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/results.json index a2f71493..c281e851 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 8, - "mass_kg": NaN, - "tip_displacement_mm": 18.190187454223633, - "max_von_mises_mpa": 123.9728203125, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 8, + "mass_kg": 1182.0927892029772, + "tip_displacement_mm": 18.190187454223633, + "max_von_mises_mpa": 123.9728203125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/results.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/results.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..408e5213 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter008/results.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 8, + "mass_kg": NaN, + "tip_displacement_mm": 18.190187454223633, + "max_von_mises_mpa": 123.9728203125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..4c11ad1f --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1185.767642455542 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.txt index a0abc6be..ea9785fc 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_mass.txt @@ -1 +1 @@ -p173=1185.767642455542 +1185.7676424555418 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_part_properties.json new file mode 100644 index 00000000..9e4f92d5 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1185.7676424555418, + "mass_g": 1185767.6424555418, + "volume_mm3": 150631052.141202, + "surface_area_mm2": 10301945.288366752, + "center_of_gravity_mm": [ + 2499.9999999999995, + -4.4050129115418514e-13, + -9.788917581204114e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/params.json index 8589d4ef..8c441aa1 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 9, - "expressions": { - "beam_half_core_thickness": 25.10064411916288, - "beam_face_thickness": 15.259636308480797, - "holes_diameter": 202.9393633023646, - "hole_count": 8.0 - }, - "trial_input": { - "beam_half_core_thickness": 25.10064411916288, - "beam_face_thickness": 15.259636308480797, - "holes_diameter": 202.9393633023646, - "hole_count": 8 - } +{ + "iteration": 9, + "expressions": { + "beam_half_core_thickness": 25.10064411916288, + "beam_face_thickness": 15.259636308480797, + "holes_diameter": 202.9393633023646, + "hole_count": 8.0 + }, + "trial_input": { + "beam_half_core_thickness": 25.10064411916288, + "beam_face_thickness": 15.259636308480797, + "holes_diameter": 202.9393633023646, + "hole_count": 8 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..8c441aa1 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 9, + "expressions": { + "beam_half_core_thickness": 25.10064411916288, + "beam_face_thickness": 15.259636308480797, + "holes_diameter": 202.9393633023646, + "hole_count": 8.0 + }, + "trial_input": { + "beam_half_core_thickness": 25.10064411916288, + "beam_face_thickness": 15.259636308480797, + "holes_diameter": 202.9393633023646, + "hole_count": 8 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/results.json index e9529bb1..468f991e 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 9, - "mass_kg": NaN, - "tip_displacement_mm": 21.658220291137695, - "max_von_mises_mpa": 202.82671875, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 9, + "mass_kg": 1185.7676424555418, + "tip_displacement_mm": 21.658220291137695, + "max_von_mises_mpa": 202.82671875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..c00aae3f --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter009/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 9, + "mass_kg": NaN, + "tip_displacement_mm": 21.658220291137695, + "max_von_mises_mpa": 202.82671875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..ee5ae665 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=987.0960407947643 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.txt index d8fc134a..51eea735 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_mass.txt @@ -1 +1 @@ -p173=987.0960407947643 +987.0960407947646 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_part_properties.json new file mode 100644 index 00000000..d93e9742 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 987.0960407947646, + "mass_g": 987096.0407947645, + "volume_mm3": 125393297.86518857, + "surface_area_mm2": 9660676.047581872, + "center_of_gravity_mm": [ + 2593.3817905056576, + -4.790112500292729e-13, + -1.1348015633811292e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/params.json index 968d2828..807db769 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 10, - "expressions": { - "beam_half_core_thickness": 23.5377088486766, - "beam_face_thickness": 15.77772417637908, - "holes_diameter": 295.1158776920038, - "hole_count": 12.0 - }, - "trial_input": { - "beam_half_core_thickness": 23.5377088486766, - "beam_face_thickness": 15.77772417637908, - "holes_diameter": 295.1158776920038, - "hole_count": 12 - } +{ + "iteration": 10, + "expressions": { + "beam_half_core_thickness": 23.5377088486766, + "beam_face_thickness": 15.77772417637908, + "holes_diameter": 295.1158776920038, + "hole_count": 12.0 + }, + "trial_input": { + "beam_half_core_thickness": 23.5377088486766, + "beam_face_thickness": 15.77772417637908, + "holes_diameter": 295.1158776920038, + "hole_count": 12 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/params.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/params.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..807db769 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/params.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 10, + "expressions": { + "beam_half_core_thickness": 23.5377088486766, + "beam_face_thickness": 15.77772417637908, + "holes_diameter": 295.1158776920038, + "hole_count": 12.0 + }, + "trial_input": { + "beam_half_core_thickness": 23.5377088486766, + "beam_face_thickness": 15.77772417637908, + "holes_diameter": 295.1158776920038, + "hole_count": 12 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/results.json index 7ff1cb5f..3543fc43 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 10, - "mass_kg": NaN, - "tip_displacement_mm": 24.221370697021484, - "max_von_mises_mpa": 190.267625, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 10, + "mass_kg": 987.0960407947646, + "tip_displacement_mm": 24.221370697021484, + "max_von_mises_mpa": 190.267625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..ce50270e --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter010/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 10, + "mass_kg": NaN, + "tip_displacement_mm": 24.221370697021484, + "max_von_mises_mpa": 190.267625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..f15bf8f4 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1082.1233483234582 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.txt index 6ed18683..8d3ee4af 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_mass.txt @@ -1 +1 @@ -p173=1082.1233483234582 +1082.1233483234585 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_part_properties.json new file mode 100644 index 00000000..df3e41ac --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1082.1233483234585, + "mass_g": 1082123.3483234586, + "volume_mm3": 137464856.24027672, + "surface_area_mm2": 9262188.327352257, + "center_of_gravity_mm": [ + 2499.999999999999, + -3.268459079235936e-13, + -7.571637682920178e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/params.json index 22262540..412a495e 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 11, - "expressions": { - "beam_half_core_thickness": 11.932749457620524, - "beam_face_thickness": 35.9698658865046, - "holes_diameter": 357.1996739776378, - "hole_count": 9.0 - }, - "trial_input": { - "beam_half_core_thickness": 11.932749457620524, - "beam_face_thickness": 35.9698658865046, - "holes_diameter": 357.1996739776378, - "hole_count": 9 - } +{ + "iteration": 11, + "expressions": { + "beam_half_core_thickness": 11.932749457620524, + "beam_face_thickness": 35.9698658865046, + "holes_diameter": 357.1996739776378, + "hole_count": 9.0 + }, + "trial_input": { + "beam_half_core_thickness": 11.932749457620524, + "beam_face_thickness": 35.9698658865046, + "holes_diameter": 357.1996739776378, + "hole_count": 9 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..412a495e --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 11, + "expressions": { + "beam_half_core_thickness": 11.932749457620524, + "beam_face_thickness": 35.9698658865046, + "holes_diameter": 357.1996739776378, + "hole_count": 9.0 + }, + "trial_input": { + "beam_half_core_thickness": 11.932749457620524, + "beam_face_thickness": 35.9698658865046, + "holes_diameter": 357.1996739776378, + "hole_count": 9 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/results.json index a6358483..e32552fe 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 11, - "mass_kg": NaN, - "tip_displacement_mm": 19.1060733795166, - "max_von_mises_mpa": 133.927765625, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 11, + "mass_kg": 1082.1233483234585, + "tip_displacement_mm": 19.1060733795166, + "max_von_mises_mpa": 133.927765625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..a40add4f --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter011/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 11, + "mass_kg": NaN, + "tip_displacement_mm": 19.1060733795166, + "max_von_mises_mpa": 133.927765625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..36783719 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=783.4437976533501 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.txt index 42957ad8..c19f91b9 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_mass.txt @@ -1 +1 @@ -p173=783.4437976533501 +783.4437976533503 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_part_properties.json new file mode 100644 index 00000000..4ca443f1 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 783.4437976533503, + "mass_g": 783443.7976533503, + "volume_mm3": 99522840.14905366, + "surface_area_mm2": 8680580.33954012, + "center_of_gravity_mm": [ + 2500.0, + -4.2268733719249244e-13, + -1.3582819155283757e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/params.json index d7c270fa..57ddc5b9 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 12, - "expressions": { - "beam_half_core_thickness": 12.590502697615015, - "beam_face_thickness": 24.283216775288818, - "holes_diameter": 436.6385439056947, - "hole_count": 8.0 - }, - "trial_input": { - "beam_half_core_thickness": 12.590502697615015, - "beam_face_thickness": 24.283216775288818, - "holes_diameter": 436.6385439056947, - "hole_count": 8 - } +{ + "iteration": 12, + "expressions": { + "beam_half_core_thickness": 12.590502697615015, + "beam_face_thickness": 24.283216775288818, + "holes_diameter": 436.6385439056947, + "hole_count": 8.0 + }, + "trial_input": { + "beam_half_core_thickness": 12.590502697615015, + "beam_face_thickness": 24.283216775288818, + "holes_diameter": 436.6385439056947, + "hole_count": 8 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..57ddc5b9 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 12, + "expressions": { + "beam_half_core_thickness": 12.590502697615015, + "beam_face_thickness": 24.283216775288818, + "holes_diameter": 436.6385439056947, + "hole_count": 8.0 + }, + "trial_input": { + "beam_half_core_thickness": 12.590502697615015, + "beam_face_thickness": 24.283216775288818, + "holes_diameter": 436.6385439056947, + "hole_count": 8 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/results.json index 56abf664..24e241f4 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 12, - "mass_kg": NaN, - "tip_displacement_mm": 31.39129066467285, - "max_von_mises_mpa": 278.0956875, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 12, + "mass_kg": 783.4437976533503, + "tip_displacement_mm": 31.39129066467285, + "max_von_mises_mpa": 278.0956875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..88dfd7e2 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter012/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 12, + "mass_kg": NaN, + "tip_displacement_mm": 31.39129066467285, + "max_von_mises_mpa": 278.0956875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..2936639d --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=911.7858841605309 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.txt index a2dc16cd..10715785 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_mass.txt @@ -1 +1 @@ -p173=911.7858841605309 +911.7858841605309 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_part_properties.json new file mode 100644 index 00000000..2dac5566 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 911.7858841605309, + "mass_g": 911785.884160531, + "volume_mm3": 115826458.86185607, + "surface_area_mm2": 10080898.538291577, + "center_of_gravity_mm": [ + 2592.2697990772162, + -5.401051363145431e-13, + -1.3372826490605775e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/params.json index d2ac2e5f..d2dbcd97 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 13, - "expressions": { - "beam_half_core_thickness": 20.01642602550961, - "beam_face_thickness": 12.736672936148768, - "holes_diameter": 235.02126154120555, - "hole_count": 14.0 - }, - "trial_input": { - "beam_half_core_thickness": 20.01642602550961, - "beam_face_thickness": 12.736672936148768, - "holes_diameter": 235.02126154120555, - "hole_count": 14 - } +{ + "iteration": 13, + "expressions": { + "beam_half_core_thickness": 20.01642602550961, + "beam_face_thickness": 12.736672936148768, + "holes_diameter": 235.02126154120555, + "hole_count": 14.0 + }, + "trial_input": { + "beam_half_core_thickness": 20.01642602550961, + "beam_face_thickness": 12.736672936148768, + "holes_diameter": 235.02126154120555, + "hole_count": 14 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..d2dbcd97 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 13, + "expressions": { + "beam_half_core_thickness": 20.01642602550961, + "beam_face_thickness": 12.736672936148768, + "holes_diameter": 235.02126154120555, + "hole_count": 14.0 + }, + "trial_input": { + "beam_half_core_thickness": 20.01642602550961, + "beam_face_thickness": 12.736672936148768, + "holes_diameter": 235.02126154120555, + "hole_count": 14 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/results.json index 6f5f54a2..b3fe1092 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 13, - "mass_kg": NaN, - "tip_displacement_mm": 27.53583335876465, - "max_von_mises_mpa": 291.43825, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 13, + "mass_kg": 911.7858841605309, + "tip_displacement_mm": 27.53583335876465, + "max_von_mises_mpa": 291.43825, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..2d7d44c1 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter013/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 13, + "mass_kg": NaN, + "tip_displacement_mm": 27.53583335876465, + "max_von_mises_mpa": 291.43825, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..65792304 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1937.3276879524951 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.txt index 4e7dc57f..b70ca813 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_mass.txt @@ -1 +1 @@ -p173=1937.3276879524951 +1937.327687952495 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_part_properties.json new file mode 100644 index 00000000..54c228ab --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1937.327687952495, + "mass_g": 1937327.687952495, + "volume_mm3": 246103618.89640436, + "surface_area_mm2": 10368136.397250943, + "center_of_gravity_mm": [ + 2500.0, + -2.722580617061503e-13, + -4.510466178384429e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/params.json index 1613565f..765953e8 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 14, - "expressions": { - "beam_half_core_thickness": 32.781509299259234, - "beam_face_thickness": 39.647413256693376, - "holes_diameter": 172.7124919630828, - "hole_count": 7.0 - }, - "trial_input": { - "beam_half_core_thickness": 32.781509299259234, - "beam_face_thickness": 39.647413256693376, - "holes_diameter": 172.7124919630828, - "hole_count": 7 - } +{ + "iteration": 14, + "expressions": { + "beam_half_core_thickness": 32.781509299259234, + "beam_face_thickness": 39.647413256693376, + "holes_diameter": 172.7124919630828, + "hole_count": 7.0 + }, + "trial_input": { + "beam_half_core_thickness": 32.781509299259234, + "beam_face_thickness": 39.647413256693376, + "holes_diameter": 172.7124919630828, + "hole_count": 7 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..765953e8 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 14, + "expressions": { + "beam_half_core_thickness": 32.781509299259234, + "beam_face_thickness": 39.647413256693376, + "holes_diameter": 172.7124919630828, + "hole_count": 7.0 + }, + "trial_input": { + "beam_half_core_thickness": 32.781509299259234, + "beam_face_thickness": 39.647413256693376, + "holes_diameter": 172.7124919630828, + "hole_count": 7 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/results.json index 96dd4484..1dab9ec0 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 14, - "mass_kg": NaN, - "tip_displacement_mm": 11.77879810333252, - "max_von_mises_mpa": 75.011625, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 14, + "mass_kg": 1937.327687952495, + "tip_displacement_mm": 11.77879810333252, + "max_von_mises_mpa": 75.011625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..9a8f4898 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter014/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 14, + "mass_kg": NaN, + "tip_displacement_mm": 11.77879810333252, + "max_von_mises_mpa": 75.011625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_mass.txt index c91b09d7..33043b25 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_mass.txt @@ -1 +1 @@ -p173=1641.9393126854027 +1641.9393126854027 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_part_properties.json new file mode 100644 index 00000000..7fcc547a --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1641.9393126854027, + "mass_g": 1641939.3126854026, + "volume_mm3": 208579689.1114587, + "surface_area_mm2": 10228113.446390914, + "center_of_gravity_mm": [ + 2536.4983660630182, + -3.519062061601101e-13, + -7.270214105211191e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/params.json index c0ec4214..f9add7ce 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 15, - "expressions": { - "beam_half_core_thickness": 38.451363316179766, - "beam_face_thickness": 16.7003930823653, - "holes_diameter": 186.1758118536306, - "hole_count": 12.0 - }, - "trial_input": { - "beam_half_core_thickness": 38.451363316179766, - "beam_face_thickness": 16.7003930823653, - "holes_diameter": 186.1758118536306, - "hole_count": 12 - } +{ + "iteration": 15, + "expressions": { + "beam_half_core_thickness": 38.451363316179766, + "beam_face_thickness": 16.7003930823653, + "holes_diameter": 186.1758118536306, + "hole_count": 12.0 + }, + "trial_input": { + "beam_half_core_thickness": 38.451363316179766, + "beam_face_thickness": 16.7003930823653, + "holes_diameter": 186.1758118536306, + "hole_count": 12 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..f9add7ce --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 15, + "expressions": { + "beam_half_core_thickness": 38.451363316179766, + "beam_face_thickness": 16.7003930823653, + "holes_diameter": 186.1758118536306, + "hole_count": 12.0 + }, + "trial_input": { + "beam_half_core_thickness": 38.451363316179766, + "beam_face_thickness": 16.7003930823653, + "holes_diameter": 186.1758118536306, + "hole_count": 12 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/results.json index ef20bf09..fc34f24f 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 15, - "mass_kg": NaN, - "tip_displacement_mm": 16.785146713256836, - "max_von_mises_mpa": 169.51878125, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 15, + "mass_kg": 1641.9393126854027, + "tip_displacement_mm": 16.785146713256836, + "max_von_mises_mpa": 169.51878125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/results.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/results.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..db6f2bc0 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter015/results.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 15, + "mass_kg": NaN, + "tip_displacement_mm": 16.785146713256836, + "max_von_mises_mpa": 169.51878125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_mass.txt index a16d7f1e..532eab54 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_mass.txt @@ -1 +1 @@ -p173=1166.4986109547917 +1166.4986109547924 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_part_properties.json new file mode 100644 index 00000000..24595beb --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1166.4986109547924, + "mass_g": 1166498.6109547925, + "volume_mm3": 148183258.50543594, + "surface_area_mm2": 9435867.270131094, + "center_of_gravity_mm": [ + 2499.999999999999, + -3.6583153300199953e-13, + -9.478896789158208e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/params.json index 0c2c1c8e..83d3e38a 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 16, - "expressions": { - "beam_half_core_thickness": 27.22384374533999, - "beam_face_thickness": 21.60434641918486, - "holes_diameter": 399.6751297258189, - "hole_count": 6.0 - }, - "trial_input": { - "beam_half_core_thickness": 27.22384374533999, - "beam_face_thickness": 21.60434641918486, - "holes_diameter": 399.6751297258189, - "hole_count": 6 - } +{ + "iteration": 16, + "expressions": { + "beam_half_core_thickness": 27.22384374533999, + "beam_face_thickness": 21.60434641918486, + "holes_diameter": 399.6751297258189, + "hole_count": 6.0 + }, + "trial_input": { + "beam_half_core_thickness": 27.22384374533999, + "beam_face_thickness": 21.60434641918486, + "holes_diameter": 399.6751297258189, + "hole_count": 6 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..83d3e38a --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 16, + "expressions": { + "beam_half_core_thickness": 27.22384374533999, + "beam_face_thickness": 21.60434641918486, + "holes_diameter": 399.6751297258189, + "hole_count": 6.0 + }, + "trial_input": { + "beam_half_core_thickness": 27.22384374533999, + "beam_face_thickness": 21.60434641918486, + "holes_diameter": 399.6751297258189, + "hole_count": 6 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/results.json index be95d3f6..4df50a08 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 16, - "mass_kg": NaN, - "tip_displacement_mm": 21.478872299194336, - "max_von_mises_mpa": 185.178296875, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 16, + "mass_kg": 1166.4986109547924, + "tip_displacement_mm": 21.478872299194336, + "max_von_mises_mpa": 185.178296875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..c91ea136 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter016/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 16, + "mass_kg": NaN, + "tip_displacement_mm": 21.478872299194336, + "max_von_mises_mpa": 185.178296875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..ec9070ce --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1165.9879420724315 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.txt index 2e461f39..7b4d5e61 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_mass.txt @@ -1 +1 @@ -p173=1165.9879420724315 +1165.9879420724315 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_part_properties.json new file mode 100644 index 00000000..4701b574 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1165.9879420724315, + "mass_g": 1165987.9420724316, + "volume_mm3": 148118386.95025808, + "surface_area_mm2": 9559676.284427067, + "center_of_gravity_mm": [ + 2499.999999999999, + -3.981990311778012e-13, + -1.1183178199835983e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/params.json index af1371f9..2be84c47 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 17, - "expressions": { - "beam_half_core_thickness": 33.00141187580357, - "beam_face_thickness": 12.103605508453288, - "holes_diameter": 352.97644218877866, - "hole_count": 7.0 - }, - "trial_input": { - "beam_half_core_thickness": 33.00141187580357, - "beam_face_thickness": 12.103605508453288, - "holes_diameter": 352.97644218877866, - "hole_count": 7 - } +{ + "iteration": 17, + "expressions": { + "beam_half_core_thickness": 33.00141187580357, + "beam_face_thickness": 12.103605508453288, + "holes_diameter": 352.97644218877866, + "hole_count": 7.0 + }, + "trial_input": { + "beam_half_core_thickness": 33.00141187580357, + "beam_face_thickness": 12.103605508453288, + "holes_diameter": 352.97644218877866, + "hole_count": 7 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..2be84c47 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 17, + "expressions": { + "beam_half_core_thickness": 33.00141187580357, + "beam_face_thickness": 12.103605508453288, + "holes_diameter": 352.97644218877866, + "hole_count": 7.0 + }, + "trial_input": { + "beam_half_core_thickness": 33.00141187580357, + "beam_face_thickness": 12.103605508453288, + "holes_diameter": 352.97644218877866, + "hole_count": 7 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/results.json index 1b90714c..21b89335 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 17, - "mass_kg": NaN, - "tip_displacement_mm": 24.657197952270508, - "max_von_mises_mpa": 320.08565625, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 17, + "mass_kg": 1165.9879420724315, + "tip_displacement_mm": 24.657197952270508, + "max_von_mises_mpa": 320.08565625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..e43d5a4f --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter017/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 17, + "mass_kg": NaN, + "tip_displacement_mm": 24.657197952270508, + "max_von_mises_mpa": 320.08565625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..53ed45c4 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1487.6954250173942 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.txt index 6d7e3111..969cb750 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_mass.txt @@ -1 +1 @@ -p173=1487.6954250173942 +1487.6954250173944 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_part_properties.json new file mode 100644 index 00000000..74ac9dc2 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1487.6954250173944, + "mass_g": 1487695.4250173944, + "volume_mm3": 188985699.31623402, + "surface_area_mm2": 10387240.406795025, + "center_of_gravity_mm": [ + 2543.7372307351898, + -4.0058277937275266e-13, + -7.709545419100078e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/params.json index 12ed025c..718ee6bc 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 18, - "expressions": { - "beam_half_core_thickness": 30.986372756119685, - "beam_face_thickness": 19.16832222642944, - "holes_diameter": 154.19092746511274, - "hole_count": 15.0 - }, - "trial_input": { - "beam_half_core_thickness": 30.986372756119685, - "beam_face_thickness": 19.16832222642944, - "holes_diameter": 154.19092746511274, - "hole_count": 15 - } +{ + "iteration": 18, + "expressions": { + "beam_half_core_thickness": 30.986372756119685, + "beam_face_thickness": 19.16832222642944, + "holes_diameter": 154.19092746511274, + "hole_count": 15.0 + }, + "trial_input": { + "beam_half_core_thickness": 30.986372756119685, + "beam_face_thickness": 19.16832222642944, + "holes_diameter": 154.19092746511274, + "hole_count": 15 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..718ee6bc --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 18, + "expressions": { + "beam_half_core_thickness": 30.986372756119685, + "beam_face_thickness": 19.16832222642944, + "holes_diameter": 154.19092746511274, + "hole_count": 15.0 + }, + "trial_input": { + "beam_half_core_thickness": 30.986372756119685, + "beam_face_thickness": 19.16832222642944, + "holes_diameter": 154.19092746511274, + "hole_count": 15 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/results.json index 95609d82..efa0b0d3 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 18, - "mass_kg": NaN, - "tip_displacement_mm": 17.36627769470215, - "max_von_mises_mpa": 129.0215078125, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 18, + "mass_kg": 1487.6954250173944, + "tip_displacement_mm": 17.36627769470215, + "max_von_mises_mpa": 129.0215078125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..b7677336 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter018/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 18, + "mass_kg": NaN, + "tip_displacement_mm": 17.36627769470215, + "max_von_mises_mpa": 129.0215078125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_mass.txt index 449740b7..d6dfb006 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_mass.txt @@ -1 +1 @@ -p173=1211.87211970739 +1211.87211970739 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_part_properties.json new file mode 100644 index 00000000..ab9ecef2 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1211.87211970739, + "mass_g": 1211872.1197073902, + "volume_mm3": 153947169.6782762, + "surface_area_mm2": 9587741.412384577, + "center_of_gravity_mm": [ + 2500.0, + -3.211466580948486e-13, + -7.493422022213132e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/params.json index 8e751c64..f3ccb6f3 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 19, - "expressions": { - "beam_half_core_thickness": 19.50323693257984, - "beam_face_thickness": 31.89937313493798, - "holes_diameter": 382.0105860159447, - "hole_count": 6.0 - }, - "trial_input": { - "beam_half_core_thickness": 19.50323693257984, - "beam_face_thickness": 31.89937313493798, - "holes_diameter": 382.0105860159447, - "hole_count": 6 - } +{ + "iteration": 19, + "expressions": { + "beam_half_core_thickness": 19.50323693257984, + "beam_face_thickness": 31.89937313493798, + "holes_diameter": 382.0105860159447, + "hole_count": 6.0 + }, + "trial_input": { + "beam_half_core_thickness": 19.50323693257984, + "beam_face_thickness": 31.89937313493798, + "holes_diameter": 382.0105860159447, + "hole_count": 6 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..f3ccb6f3 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 19, + "expressions": { + "beam_half_core_thickness": 19.50323693257984, + "beam_face_thickness": 31.89937313493798, + "holes_diameter": 382.0105860159447, + "hole_count": 6.0 + }, + "trial_input": { + "beam_half_core_thickness": 19.50323693257984, + "beam_face_thickness": 31.89937313493798, + "holes_diameter": 382.0105860159447, + "hole_count": 6 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/results.json index ac3e238d..900657bd 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 19, - "mass_kg": NaN, - "tip_displacement_mm": 17.949995040893555, - "max_von_mises_mpa": 127.061125, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 19, + "mass_kg": 1211.87211970739, + "tip_displacement_mm": 17.949995040893555, + "max_von_mises_mpa": 127.061125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..4c2fce3e --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter019/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 19, + "mass_kg": NaN, + "tip_displacement_mm": 17.949995040893555, + "max_von_mises_mpa": 127.061125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_mass.txt index 7780690a..787c0856 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_mass.txt @@ -1 +1 @@ -p173=1149.200700206242 +1149.2007002062417 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_part_properties.json new file mode 100644 index 00000000..50b35b9e --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1149.2007002062417, + "mass_g": 1149200.7002062416, + "volume_mm3": 145985861.30668727, + "surface_area_mm2": 10630505.683400746, + "center_of_gravity_mm": [ + 2500.0, + -3.896264745428142e-13, + -8.218538517747473e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/params.json index 082789f0..a17c829b 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 20, - "expressions": { - "beam_half_core_thickness": 13.727845373498717, - "beam_face_thickness": 29.571387264342114, - "holes_diameter": 158.4545070511115, - "hole_count": 6.0 - }, - "trial_input": { - "beam_half_core_thickness": 13.727845373498717, - "beam_face_thickness": 29.571387264342114, - "holes_diameter": 158.4545070511115, - "hole_count": 6 - } +{ + "iteration": 20, + "expressions": { + "beam_half_core_thickness": 13.727845373498717, + "beam_face_thickness": 29.571387264342114, + "holes_diameter": 158.4545070511115, + "hole_count": 6.0 + }, + "trial_input": { + "beam_half_core_thickness": 13.727845373498717, + "beam_face_thickness": 29.571387264342114, + "holes_diameter": 158.4545070511115, + "hole_count": 6 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..a17c829b --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 20, + "expressions": { + "beam_half_core_thickness": 13.727845373498717, + "beam_face_thickness": 29.571387264342114, + "holes_diameter": 158.4545070511115, + "hole_count": 6.0 + }, + "trial_input": { + "beam_half_core_thickness": 13.727845373498717, + "beam_face_thickness": 29.571387264342114, + "holes_diameter": 158.4545070511115, + "hole_count": 6 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/results.json index b334c236..34aa5368 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 20, - "mass_kg": NaN, - "tip_displacement_mm": 17.506193161010742, - "max_von_mises_mpa": 110.5740859375, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 20, + "mass_kg": 1149.2007002062417, + "tip_displacement_mm": 17.506193161010742, + "max_von_mises_mpa": 110.5740859375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..30b5f83a --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter020/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 20, + "mass_kg": NaN, + "tip_displacement_mm": 17.506193161010742, + "max_von_mises_mpa": 110.5740859375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..e6e88e98 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1493.3399471409589 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.txt index 5e0bf78e..b50653f1 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_mass.txt @@ -1 +1 @@ -p173=1493.3399471409589 +1493.3399471409589 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_part_properties.json new file mode 100644 index 00000000..5ce91f6d --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1493.3399471409589, + "mass_g": 1493339.9471409589, + "volume_mm3": 189702737.18762186, + "surface_area_mm2": 9979263.91596761, + "center_of_gravity_mm": [ + 2500.0000000000005, + -3.886382917927919e-13, + -8.973427008789857e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/params.json index cd4dac3f..2a44a801 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 21, - "expressions": { - "beam_half_core_thickness": 39.182912433667966, - "beam_face_thickness": 13.547410048410338, - "holes_diameter": 242.6447570355275, - "hole_count": 10.0 - }, - "trial_input": { - "beam_half_core_thickness": 39.182912433667966, - "beam_face_thickness": 13.547410048410338, - "holes_diameter": 242.6447570355275, - "hole_count": 10 - } +{ + "iteration": 21, + "expressions": { + "beam_half_core_thickness": 39.182912433667966, + "beam_face_thickness": 13.547410048410338, + "holes_diameter": 242.6447570355275, + "hole_count": 10.0 + }, + "trial_input": { + "beam_half_core_thickness": 39.182912433667966, + "beam_face_thickness": 13.547410048410338, + "holes_diameter": 242.6447570355275, + "hole_count": 10 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..2a44a801 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 21, + "expressions": { + "beam_half_core_thickness": 39.182912433667966, + "beam_face_thickness": 13.547410048410338, + "holes_diameter": 242.6447570355275, + "hole_count": 10.0 + }, + "trial_input": { + "beam_half_core_thickness": 39.182912433667966, + "beam_face_thickness": 13.547410048410338, + "holes_diameter": 242.6447570355275, + "hole_count": 10 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/results.json index e372db28..a6d7264c 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 21, - "mass_kg": NaN, - "tip_displacement_mm": 18.839136123657227, - "max_von_mises_mpa": 256.434484375, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 21, + "mass_kg": 1493.3399471409589, + "tip_displacement_mm": 18.839136123657227, + "max_von_mises_mpa": 256.434484375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..460b77cd --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter021/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 21, + "mass_kg": NaN, + "tip_displacement_mm": 18.839136123657227, + "max_von_mises_mpa": 256.434484375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..d6dfb494 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=686.2888916637669 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.txt index 05d1ba37..1afaf912 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_mass.txt @@ -1 +1 @@ -p173=686.2888916637669 +686.2888916637671 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_part_properties.json new file mode 100644 index 00000000..5c7ebccc --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 686.2888916637671, + "mass_g": 686288.8916637672, + "volume_mm3": 87181007.57923874, + "surface_area_mm2": 10188734.195542168, + "center_of_gravity_mm": [ + 2500.0, + -6.382889811435283e-13, + -1.6947894538480383e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/params.json index 0bf85a45..1dcddc6d 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 22, - "expressions": { - "beam_half_core_thickness": 11.508005001497104, - "beam_face_thickness": 13.78017571971355, - "holes_diameter": 280.40478023124285, - "hole_count": 6.0 - }, - "trial_input": { - "beam_half_core_thickness": 11.508005001497104, - "beam_face_thickness": 13.78017571971355, - "holes_diameter": 280.40478023124285, - "hole_count": 6 - } +{ + "iteration": 22, + "expressions": { + "beam_half_core_thickness": 11.508005001497104, + "beam_face_thickness": 13.78017571971355, + "holes_diameter": 280.40478023124285, + "hole_count": 6.0 + }, + "trial_input": { + "beam_half_core_thickness": 11.508005001497104, + "beam_face_thickness": 13.78017571971355, + "holes_diameter": 280.40478023124285, + "hole_count": 6 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..1dcddc6d --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 22, + "expressions": { + "beam_half_core_thickness": 11.508005001497104, + "beam_face_thickness": 13.78017571971355, + "holes_diameter": 280.40478023124285, + "hole_count": 6.0 + }, + "trial_input": { + "beam_half_core_thickness": 11.508005001497104, + "beam_face_thickness": 13.78017571971355, + "holes_diameter": 280.40478023124285, + "hole_count": 6 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/results.json index 3fbf1288..7c5bbbb8 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 22, - "mass_kg": NaN, - "tip_displacement_mm": 31.85018539428711, - "max_von_mises_mpa": 248.273609375, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 22, + "mass_kg": 686.2888916637671, + "tip_displacement_mm": 31.85018539428711, + "max_von_mises_mpa": 248.273609375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..6ca61dfe --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter022/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 22, + "mass_kg": NaN, + "tip_displacement_mm": 31.85018539428711, + "max_von_mises_mpa": 248.273609375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..1e526dd9 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=999.7016550018517 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.txt index 3075169d..16a2e5b7 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_mass.txt @@ -1 +1 @@ -p173=999.7016550018517 +999.7016550018517 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_part_properties.json new file mode 100644 index 00000000..c336b0cc --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 999.7016550018517, + "mass_g": 999701.6550018517, + "volume_mm3": 126994620.80816203, + "surface_area_mm2": 10441715.276017135, + "center_of_gravity_mm": [ + 2500.0000000000005, + -4.812951689205875e-13, + -1.0723622455216203e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/params.json index bbfb7fa3..d8319570 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 23, - "expressions": { - "beam_half_core_thickness": 17.419169007999646, - "beam_face_thickness": 17.917141385501917, - "holes_diameter": 175.7984093879865, - "hole_count": 9.0 - }, - "trial_input": { - "beam_half_core_thickness": 17.419169007999646, - "beam_face_thickness": 17.917141385501917, - "holes_diameter": 175.7984093879865, - "hole_count": 9 - } +{ + "iteration": 23, + "expressions": { + "beam_half_core_thickness": 17.419169007999646, + "beam_face_thickness": 17.917141385501917, + "holes_diameter": 175.7984093879865, + "hole_count": 9.0 + }, + "trial_input": { + "beam_half_core_thickness": 17.419169007999646, + "beam_face_thickness": 17.917141385501917, + "holes_diameter": 175.7984093879865, + "hole_count": 9 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..d8319570 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 23, + "expressions": { + "beam_half_core_thickness": 17.419169007999646, + "beam_face_thickness": 17.917141385501917, + "holes_diameter": 175.7984093879865, + "hole_count": 9.0 + }, + "trial_input": { + "beam_half_core_thickness": 17.419169007999646, + "beam_face_thickness": 17.917141385501917, + "holes_diameter": 175.7984093879865, + "hole_count": 9 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/results.json index 022d25cf..ade4e48d 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 23, - "mass_kg": NaN, - "tip_displacement_mm": 22.727807998657227, - "max_von_mises_mpa": 147.5830625, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 23, + "mass_kg": 999.7016550018517, + "tip_displacement_mm": 22.727807998657227, + "max_von_mises_mpa": 147.5830625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..be616edd --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter023/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 23, + "mass_kg": NaN, + "tip_displacement_mm": 22.727807998657227, + "max_von_mises_mpa": 147.5830625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..2c57a4ec --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1435.135535503974 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.txt index 5a9ece2e..b3c48468 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_mass.txt @@ -1 +1 @@ -p173=1435.135535503974 +1435.135535503974 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_part_properties.json new file mode 100644 index 00000000..dc12b55c --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1435.135535503974, + "mass_g": 1435135.5355039742, + "volume_mm3": 182308884.08333007, + "surface_area_mm2": 9826726.62077617, + "center_of_gravity_mm": [ + 2499.9999999999995, + -3.318460399167294e-13, + -7.464161463187597e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/params.json index fe7fc846..a0029f2a 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 24, - "expressions": { - "beam_half_core_thickness": 31.240370325087493, - "beam_face_thickness": 26.41009203906711, - "holes_diameter": 273.18266513234516, - "hole_count": 10.0 - }, - "trial_input": { - "beam_half_core_thickness": 31.240370325087493, - "beam_face_thickness": 26.41009203906711, - "holes_diameter": 273.18266513234516, - "hole_count": 10 - } +{ + "iteration": 24, + "expressions": { + "beam_half_core_thickness": 31.240370325087493, + "beam_face_thickness": 26.41009203906711, + "holes_diameter": 273.18266513234516, + "hole_count": 10.0 + }, + "trial_input": { + "beam_half_core_thickness": 31.240370325087493, + "beam_face_thickness": 26.41009203906711, + "holes_diameter": 273.18266513234516, + "hole_count": 10 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..a0029f2a --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 24, + "expressions": { + "beam_half_core_thickness": 31.240370325087493, + "beam_face_thickness": 26.41009203906711, + "holes_diameter": 273.18266513234516, + "hole_count": 10.0 + }, + "trial_input": { + "beam_half_core_thickness": 31.240370325087493, + "beam_face_thickness": 26.41009203906711, + "holes_diameter": 273.18266513234516, + "hole_count": 10 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/results.json index c71368dc..8c04abd8 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 24, - "mass_kg": NaN, - "tip_displacement_mm": 15.669999122619629, - "max_von_mises_mpa": 93.16090625, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 24, + "mass_kg": 1435.135535503974, + "tip_displacement_mm": 15.669999122619629, + "max_von_mises_mpa": 93.16090625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/results.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/results.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..dbb8cff1 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter024/results.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 24, + "mass_kg": NaN, + "tip_displacement_mm": 15.669999122619629, + "max_von_mises_mpa": 93.16090625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..579ba9b4 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1528.3897999240266 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.txt index f6270153..88a78aeb 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_mass.txt @@ -1 +1 @@ -p173=1528.3897999240266 +1528.3897999240266 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_part_properties.json new file mode 100644 index 00000000..11c86823 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1528.3897999240266, + "mass_g": 1528389.7999240267, + "volume_mm3": 194155208.32368228, + "surface_area_mm2": 10066847.33956532, + "center_of_gravity_mm": [ + 2500.0, + -3.155074915286682e-13, + -6.173096565383677e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/params.json index d9f57f12..22a9cd2a 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 25, - "expressions": { - "beam_half_core_thickness": 26.00450478511711, - "beam_face_thickness": 33.73796956646058, - "holes_diameter": 309.2653422609983, - "hole_count": 5.0 - }, - "trial_input": { - "beam_half_core_thickness": 26.00450478511711, - "beam_face_thickness": 33.73796956646058, - "holes_diameter": 309.2653422609983, - "hole_count": 5 - } +{ + "iteration": 25, + "expressions": { + "beam_half_core_thickness": 26.00450478511711, + "beam_face_thickness": 33.73796956646058, + "holes_diameter": 309.2653422609983, + "hole_count": 5.0 + }, + "trial_input": { + "beam_half_core_thickness": 26.00450478511711, + "beam_face_thickness": 33.73796956646058, + "holes_diameter": 309.2653422609983, + "hole_count": 5 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/params.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/params.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..22a9cd2a --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/params.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 25, + "expressions": { + "beam_half_core_thickness": 26.00450478511711, + "beam_face_thickness": 33.73796956646058, + "holes_diameter": 309.2653422609983, + "hole_count": 5.0 + }, + "trial_input": { + "beam_half_core_thickness": 26.00450478511711, + "beam_face_thickness": 33.73796956646058, + "holes_diameter": 309.2653422609983, + "hole_count": 5 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/results.json index c8975a0c..99a72011 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 25, - "mass_kg": NaN, - "tip_displacement_mm": 14.4290132522583, - "max_von_mises_mpa": 93.7885234375, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 25, + "mass_kg": 1528.3897999240266, + "tip_displacement_mm": 14.4290132522583, + "max_von_mises_mpa": 93.7885234375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..cd395c20 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter025/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 25, + "mass_kg": NaN, + "tip_displacement_mm": 14.4290132522583, + "max_von_mises_mpa": 93.7885234375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..cbe34b17 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1276.5504502481265 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.txt index 4833a7d5..46e20465 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_mass.txt @@ -1 +1 @@ -p173=1276.5504502481265 +1276.5504502481267 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_part_properties.json new file mode 100644 index 00000000..3b00b5ff --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1276.5504502481267, + "mass_g": 1276550.4502481266, + "volume_mm3": 162163421.01729244, + "surface_area_mm2": 10431659.31401184, + "center_of_gravity_mm": [ + 2500.0, + -3.782526140825955e-13, + -8.277577875840479e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/params.json index 66dd4c8a..c3d1715e 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 26, - "expressions": { - "beam_half_core_thickness": 22.249541218652357, - "beam_face_thickness": 23.591878117279098, - "holes_diameter": 163.6658438964213, - "hole_count": 10.0 - }, - "trial_input": { - "beam_half_core_thickness": 22.249541218652357, - "beam_face_thickness": 23.591878117279098, - "holes_diameter": 163.6658438964213, - "hole_count": 10 - } +{ + "iteration": 26, + "expressions": { + "beam_half_core_thickness": 22.249541218652357, + "beam_face_thickness": 23.591878117279098, + "holes_diameter": 163.6658438964213, + "hole_count": 10.0 + }, + "trial_input": { + "beam_half_core_thickness": 22.249541218652357, + "beam_face_thickness": 23.591878117279098, + "holes_diameter": 163.6658438964213, + "hole_count": 10 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..c3d1715e --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 26, + "expressions": { + "beam_half_core_thickness": 22.249541218652357, + "beam_face_thickness": 23.591878117279098, + "holes_diameter": 163.6658438964213, + "hole_count": 10.0 + }, + "trial_input": { + "beam_half_core_thickness": 22.249541218652357, + "beam_face_thickness": 23.591878117279098, + "holes_diameter": 163.6658438964213, + "hole_count": 10 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/results.json index 9b152550..0faca6a5 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 26, - "mass_kg": NaN, - "tip_displacement_mm": 17.734634399414062, - "max_von_mises_mpa": 110.49809375, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 26, + "mass_kg": 1276.5504502481267, + "tip_displacement_mm": 17.734634399414062, + "max_von_mises_mpa": 110.49809375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/results.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/results.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..1a55f78e --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter026/results.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 26, + "mass_kg": NaN, + "tip_displacement_mm": 17.734634399414062, + "max_von_mises_mpa": 110.49809375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..749bd50c --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1410.1593512483248 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.txt index 0de6dd19..ca5149c7 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_mass.txt @@ -1 +1 @@ -p173=1410.1593512483248 +1410.1593512483248 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_part_properties.json new file mode 100644 index 00000000..fdde35d8 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1410.1593512483248, + "mass_g": 1410159.3512483248, + "volume_mm3": 179136096.44922823, + "surface_area_mm2": 9778306.856311752, + "center_of_gravity_mm": [ + 2500.0, + -3.558807465708975e-13, + -8.129002395608496e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/params.json index 74699eb2..990b4b5a 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 27, - "expressions": { - "beam_half_core_thickness": 34.50742630475948, - "beam_face_thickness": 19.91457704426444, - "holes_diameter": 301.4331617880579, - "hole_count": 8.0 - }, - "trial_input": { - "beam_half_core_thickness": 34.50742630475948, - "beam_face_thickness": 19.91457704426444, - "holes_diameter": 301.4331617880579, - "hole_count": 8 - } +{ + "iteration": 27, + "expressions": { + "beam_half_core_thickness": 34.50742630475948, + "beam_face_thickness": 19.91457704426444, + "holes_diameter": 301.4331617880579, + "hole_count": 8.0 + }, + "trial_input": { + "beam_half_core_thickness": 34.50742630475948, + "beam_face_thickness": 19.91457704426444, + "holes_diameter": 301.4331617880579, + "hole_count": 8 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..990b4b5a --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 27, + "expressions": { + "beam_half_core_thickness": 34.50742630475948, + "beam_face_thickness": 19.91457704426444, + "holes_diameter": 301.4331617880579, + "hole_count": 8.0 + }, + "trial_input": { + "beam_half_core_thickness": 34.50742630475948, + "beam_face_thickness": 19.91457704426444, + "holes_diameter": 301.4331617880579, + "hole_count": 8 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/results.json index 8a895d5c..54e4fdb6 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 27, - "mass_kg": NaN, - "tip_displacement_mm": 17.536535263061523, - "max_von_mises_mpa": 118.634125, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 27, + "mass_kg": 1410.1593512483248, + "tip_displacement_mm": 17.536535263061523, + "max_von_mises_mpa": 118.634125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..0658aa78 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter027/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 27, + "mass_kg": NaN, + "tip_displacement_mm": 17.536535263061523, + "max_von_mises_mpa": 118.634125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_mass.txt index 16efa515..4eca9fc0 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_mass.txt @@ -1 +1 @@ -p173=1582.291153913998 +1582.291153913998 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_part_properties.json new file mode 100644 index 00000000..0ab00d8c --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1582.291153913998, + "mass_g": 1582291.153913998, + "volume_mm3": 201002433.16996926, + "surface_area_mm2": 9219417.492508749, + "center_of_gravity_mm": [ + 2499.9999999999986, + -2.357167233769352e-13, + -6.265373651064361e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/params.json index ba3cb395..4b7e4bed 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 28, - "expressions": { - "beam_half_core_thickness": 37.19895822292572, - "beam_face_thickness": 36.450392892917435, - "holes_diameter": 405.40744176134933, - "hole_count": 7.0 - }, - "trial_input": { - "beam_half_core_thickness": 37.19895822292572, - "beam_face_thickness": 36.450392892917435, - "holes_diameter": 405.40744176134933, - "hole_count": 7 - } +{ + "iteration": 28, + "expressions": { + "beam_half_core_thickness": 37.19895822292572, + "beam_face_thickness": 36.450392892917435, + "holes_diameter": 405.40744176134933, + "hole_count": 7.0 + }, + "trial_input": { + "beam_half_core_thickness": 37.19895822292572, + "beam_face_thickness": 36.450392892917435, + "holes_diameter": 405.40744176134933, + "hole_count": 7 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..4b7e4bed --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 28, + "expressions": { + "beam_half_core_thickness": 37.19895822292572, + "beam_face_thickness": 36.450392892917435, + "holes_diameter": 405.40744176134933, + "hole_count": 7.0 + }, + "trial_input": { + "beam_half_core_thickness": 37.19895822292572, + "beam_face_thickness": 36.450392892917435, + "holes_diameter": 405.40744176134933, + "hole_count": 7 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/results.json index 7f0fa1b3..24bf8426 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 28, - "mass_kg": NaN, - "tip_displacement_mm": 15.368349075317383, - "max_von_mises_mpa": 122.3722265625, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 28, + "mass_kg": 1582.291153913998, + "tip_displacement_mm": 15.368349075317383, + "max_von_mises_mpa": 122.3722265625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..9fc4ca20 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter028/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 28, + "mass_kg": NaN, + "tip_displacement_mm": 15.368349075317383, + "max_von_mises_mpa": 122.3722265625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..6934e010 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1398.2771475400532 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.txt index 638aa4cb..edfb3a71 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_mass.txt @@ -1 +1 @@ -p173=1398.2771475400532 +1398.2771475400532 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_part_properties.json new file mode 100644 index 00000000..a0d12f02 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1398.2771475400532, + "mass_g": 1398277.1475400531, + "volume_mm3": 177626670.16514903, + "surface_area_mm2": 10076329.334871126, + "center_of_gravity_mm": [ + 2499.9999999999995, + -3.3204809913046283e-13, + -6.845655792137992e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/params.json index 873686aa..69bd0495 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 29, - "expressions": { - "beam_half_core_thickness": 24.13665312381697, - "beam_face_thickness": 30.60108948604478, - "holes_diameter": 257.34854555318736, - "hole_count": 8.0 - }, - "trial_input": { - "beam_half_core_thickness": 24.13665312381697, - "beam_face_thickness": 30.60108948604478, - "holes_diameter": 257.34854555318736, - "hole_count": 8 - } +{ + "iteration": 29, + "expressions": { + "beam_half_core_thickness": 24.13665312381697, + "beam_face_thickness": 30.60108948604478, + "holes_diameter": 257.34854555318736, + "hole_count": 8.0 + }, + "trial_input": { + "beam_half_core_thickness": 24.13665312381697, + "beam_face_thickness": 30.60108948604478, + "holes_diameter": 257.34854555318736, + "hole_count": 8 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..69bd0495 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 29, + "expressions": { + "beam_half_core_thickness": 24.13665312381697, + "beam_face_thickness": 30.60108948604478, + "holes_diameter": 257.34854555318736, + "hole_count": 8.0 + }, + "trial_input": { + "beam_half_core_thickness": 24.13665312381697, + "beam_face_thickness": 30.60108948604478, + "holes_diameter": 257.34854555318736, + "hole_count": 8 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/results.json index 11b46e5c..479a5fb8 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 29, - "mass_kg": NaN, - "tip_displacement_mm": 15.375892639160156, - "max_von_mises_mpa": 94.1200546875, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 29, + "mass_kg": 1398.2771475400532, + "tip_displacement_mm": 15.375892639160156, + "max_von_mises_mpa": 94.1200546875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..a7f5469c --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter029/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 29, + "mass_kg": NaN, + "tip_displacement_mm": 15.375892639160156, + "max_von_mises_mpa": 94.1200546875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_mass.txt index d27a9720..df611d2a 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_mass.txt @@ -1 +1 @@ -p173=1428.798569319311 +1428.798569319311 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_part_properties.json new file mode 100644 index 00000000..46dd6fa9 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1428.798569319311, + "mass_g": 1428798.5693193108, + "volume_mm3": 181503883.2976767, + "surface_area_mm2": 9740014.468738623, + "center_of_gravity_mm": [ + 2542.997017607625, + -3.357072090504704e-13, + -7.048632759403923e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/params.json index 4bd509df..51d533ff 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 30, - "expressions": { - "beam_half_core_thickness": 30.315850546600835, - "beam_face_thickness": 28.803003242099344, - "holes_diameter": 286.25449297115927, - "hole_count": 11.0 - }, - "trial_input": { - "beam_half_core_thickness": 30.315850546600835, - "beam_face_thickness": 28.803003242099344, - "holes_diameter": 286.25449297115927, - "hole_count": 11 - } +{ + "iteration": 30, + "expressions": { + "beam_half_core_thickness": 30.315850546600835, + "beam_face_thickness": 28.803003242099344, + "holes_diameter": 286.25449297115927, + "hole_count": 11.0 + }, + "trial_input": { + "beam_half_core_thickness": 30.315850546600835, + "beam_face_thickness": 28.803003242099344, + "holes_diameter": 286.25449297115927, + "hole_count": 11 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..51d533ff --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 30, + "expressions": { + "beam_half_core_thickness": 30.315850546600835, + "beam_face_thickness": 28.803003242099344, + "holes_diameter": 286.25449297115927, + "hole_count": 11.0 + }, + "trial_input": { + "beam_half_core_thickness": 30.315850546600835, + "beam_face_thickness": 28.803003242099344, + "holes_diameter": 286.25449297115927, + "hole_count": 11 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/results.json index 835d34d5..69cb25f4 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 30, - "mass_kg": NaN, - "tip_displacement_mm": 15.49566650390625, - "max_von_mises_mpa": 93.2840390625, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 30, + "mass_kg": 1428.798569319311, + "tip_displacement_mm": 15.49566650390625, + "max_von_mises_mpa": 93.2840390625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..5afac5b5 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter030/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 30, + "mass_kg": NaN, + "tip_displacement_mm": 15.49566650390625, + "max_von_mises_mpa": 93.2840390625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..eeb3ca84 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=815.486611683971 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.txt index f1d66c91..8e8803b4 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_mass.txt @@ -1 +1 @@ -p173=815.486611683971 +815.486611683971 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_part_properties.json new file mode 100644 index 00000000..c90af068 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 815.486611683971, + "mass_g": 815486.611683971, + "volume_mm3": 103593319.5736752, + "surface_area_mm2": 9246179.767880265, + "center_of_gravity_mm": [ + 2500.000000000001, + -4.641647410047006e-13, + -1.2775969584129842e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/params.json index dbeb4239..2ec2671e 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 31, - "expressions": { - "beam_half_core_thickness": 15.231259729696596, - "beam_face_thickness": 20.51061793783426, - "holes_diameter": 341.16121801123404, - "hole_count": 10.0 - }, - "trial_input": { - "beam_half_core_thickness": 15.231259729696596, - "beam_face_thickness": 20.51061793783426, - "holes_diameter": 341.16121801123404, - "hole_count": 10 - } +{ + "iteration": 31, + "expressions": { + "beam_half_core_thickness": 15.231259729696596, + "beam_face_thickness": 20.51061793783426, + "holes_diameter": 341.16121801123404, + "hole_count": 10.0 + }, + "trial_input": { + "beam_half_core_thickness": 15.231259729696596, + "beam_face_thickness": 20.51061793783426, + "holes_diameter": 341.16121801123404, + "hole_count": 10 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..2ec2671e --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 31, + "expressions": { + "beam_half_core_thickness": 15.231259729696596, + "beam_face_thickness": 20.51061793783426, + "holes_diameter": 341.16121801123404, + "hole_count": 10.0 + }, + "trial_input": { + "beam_half_core_thickness": 15.231259729696596, + "beam_face_thickness": 20.51061793783426, + "holes_diameter": 341.16121801123404, + "hole_count": 10 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/results.json index f29fa207..331baf48 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 31, - "mass_kg": NaN, - "tip_displacement_mm": 25.575746536254883, - "max_von_mises_mpa": 154.783734375, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 31, + "mass_kg": 815.486611683971, + "tip_displacement_mm": 25.575746536254883, + "max_von_mises_mpa": 154.783734375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..f7a62527 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter031/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 31, + "mass_kg": NaN, + "tip_displacement_mm": 25.575746536254883, + "max_von_mises_mpa": 154.783734375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..13fa162f --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1231.4483565590738 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.txt index 5789d5da..3ff9851f 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_mass.txt @@ -1 +1 @@ -p173=1231.4483565590738 +1231.4483565590738 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_part_properties.json new file mode 100644 index 00000000..0344f19b --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1231.4483565590738, + "mass_g": 1231448.3565590738, + "volume_mm3": 156433988.3840287, + "surface_area_mm2": 9255507.726981124, + "center_of_gravity_mm": [ + 2499.999999999999, + -3.2609097768583817e-13, + -1.0146122493128114e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/params.json index 24573bcb..0ed9a62c 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 32, - "expressions": { - "beam_half_core_thickness": 35.135355432426465, - "beam_face_thickness": 17.452468258634863, - "holes_diameter": 423.30383098713725, - "hole_count": 6.0 - }, - "trial_input": { - "beam_half_core_thickness": 35.135355432426465, - "beam_face_thickness": 17.452468258634863, - "holes_diameter": 423.30383098713725, - "hole_count": 6 - } +{ + "iteration": 32, + "expressions": { + "beam_half_core_thickness": 35.135355432426465, + "beam_face_thickness": 17.452468258634863, + "holes_diameter": 423.30383098713725, + "hole_count": 6.0 + }, + "trial_input": { + "beam_half_core_thickness": 35.135355432426465, + "beam_face_thickness": 17.452468258634863, + "holes_diameter": 423.30383098713725, + "hole_count": 6 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..0ed9a62c --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 32, + "expressions": { + "beam_half_core_thickness": 35.135355432426465, + "beam_face_thickness": 17.452468258634863, + "holes_diameter": 423.30383098713725, + "hole_count": 6.0 + }, + "trial_input": { + "beam_half_core_thickness": 35.135355432426465, + "beam_face_thickness": 17.452468258634863, + "holes_diameter": 423.30383098713725, + "hole_count": 6 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/results.json index b6f4e7c1..bb3c184d 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 32, - "mass_kg": NaN, - "tip_displacement_mm": 23.97252655029297, - "max_von_mises_mpa": 196.272515625, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 32, + "mass_kg": 1231.4483565590738, + "tip_displacement_mm": 23.97252655029297, + "max_von_mises_mpa": 196.272515625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/results.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/results.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..c0f7ee73 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter032/results.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 32, + "mass_kg": NaN, + "tip_displacement_mm": 23.97252655029297, + "max_von_mises_mpa": 196.272515625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..c167417e --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1754.139697344678 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.txt index 88d6ef6a..15c4be62 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_mass.txt @@ -1 +1 @@ -p173=1754.139697344678 +1754.139697344678 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_part_properties.json new file mode 100644 index 00000000..ee36f666 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1754.139697344678, + "mass_g": 1754139.697344678, + "volume_mm3": 222832786.75618342, + "surface_area_mm2": 10079755.04168897, + "center_of_gravity_mm": [ + 2565.3549517607903, + -2.9071610488312117e-13, + -5.806139390179323e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/params.json index 38ddf7b8..e4eebb2a 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 33, - "expressions": { - "beam_half_core_thickness": 35.46708851836511, - "beam_face_thickness": 32.57744662968254, - "holes_diameter": 228.6499364024705, - "hole_count": 13.0 - }, - "trial_input": { - "beam_half_core_thickness": 35.46708851836511, - "beam_face_thickness": 32.57744662968254, - "holes_diameter": 228.6499364024705, - "hole_count": 13 - } +{ + "iteration": 33, + "expressions": { + "beam_half_core_thickness": 35.46708851836511, + "beam_face_thickness": 32.57744662968254, + "holes_diameter": 228.6499364024705, + "hole_count": 13.0 + }, + "trial_input": { + "beam_half_core_thickness": 35.46708851836511, + "beam_face_thickness": 32.57744662968254, + "holes_diameter": 228.6499364024705, + "hole_count": 13 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/params.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/params.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..e4eebb2a --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/params.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 33, + "expressions": { + "beam_half_core_thickness": 35.46708851836511, + "beam_face_thickness": 32.57744662968254, + "holes_diameter": 228.6499364024705, + "hole_count": 13.0 + }, + "trial_input": { + "beam_half_core_thickness": 35.46708851836511, + "beam_face_thickness": 32.57744662968254, + "holes_diameter": 228.6499364024705, + "hole_count": 13 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/results.json index cc517709..5b4dfa18 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 33, - "mass_kg": NaN, - "tip_displacement_mm": 13.120881080627441, - "max_von_mises_mpa": 80.2448125, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 33, + "mass_kg": 1754.139697344678, + "tip_displacement_mm": 13.120881080627441, + "max_von_mises_mpa": 80.2448125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..1cb991a6 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter033/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 33, + "mass_kg": NaN, + "tip_displacement_mm": 13.120881080627441, + "max_von_mises_mpa": 80.2448125, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..aadd9e2d --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=688.6297607768506 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.txt index 96b435a3..74b3be11 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_mass.txt @@ -1 +1 @@ -p173=688.6297607768506 +688.6297607768505 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_part_properties.json new file mode 100644 index 00000000..a5bd08ca --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 688.6297607768505, + "mass_g": 688629.7607768505, + "volume_mm3": 87478374.08242513, + "surface_area_mm2": 8665043.525596287, + "center_of_gravity_mm": [ + 2499.9999999999986, + -4.892509858326666e-13, + -1.799044679012995e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/params.json index 19c23399..7eba1733 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 34, - "expressions": { - "beam_half_core_thickness": 23.046420945659438, - "beam_face_thickness": 11.556167883135958, - "holes_diameter": 418.6187160546307, - "hole_count": 9.0 - }, - "trial_input": { - "beam_half_core_thickness": 23.046420945659438, - "beam_face_thickness": 11.556167883135958, - "holes_diameter": 418.6187160546307, - "hole_count": 9 - } +{ + "iteration": 34, + "expressions": { + "beam_half_core_thickness": 23.046420945659438, + "beam_face_thickness": 11.556167883135958, + "holes_diameter": 418.6187160546307, + "hole_count": 9.0 + }, + "trial_input": { + "beam_half_core_thickness": 23.046420945659438, + "beam_face_thickness": 11.556167883135958, + "holes_diameter": 418.6187160546307, + "hole_count": 9 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..7eba1733 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 34, + "expressions": { + "beam_half_core_thickness": 23.046420945659438, + "beam_face_thickness": 11.556167883135958, + "holes_diameter": 418.6187160546307, + "hole_count": 9.0 + }, + "trial_input": { + "beam_half_core_thickness": 23.046420945659438, + "beam_face_thickness": 11.556167883135958, + "holes_diameter": 418.6187160546307, + "hole_count": 9 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/results.json index f21b9a53..d812dbca 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 34, - "mass_kg": NaN, - "tip_displacement_mm": 39.49115753173828, - "max_von_mises_mpa": 351.02815625, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 34, + "mass_kg": 688.6297607768505, + "tip_displacement_mm": 39.49115753173828, + "max_von_mises_mpa": 351.02815625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..80086c55 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter034/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 34, + "mass_kg": NaN, + "tip_displacement_mm": 39.49115753173828, + "max_von_mises_mpa": 351.02815625, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..9cfab1aa --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1652.729145472504 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.txt index 2ccbaf26..b9f723de 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_mass.txt @@ -1 +1 @@ -p173=1652.729145472504 +1652.7291454725032 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_part_properties.json new file mode 100644 index 00000000..9a1f034e --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1652.7291454725032, + "mass_g": 1652729.1454725033, + "volume_mm3": 209950348.76429164, + "surface_area_mm2": 10333771.866468234, + "center_of_gravity_mm": [ + 2533.619470233298, + -3.018409702806645e-13, + -5.131898088078599e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/params.json index a80b0daf..09bd3575 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 35, - "expressions": { - "beam_half_core_thickness": 26.538941763998174, - "beam_face_thickness": 37.0045746615665, - "holes_diameter": 184.02083272600328, - "hole_count": 13.0 - }, - "trial_input": { - "beam_half_core_thickness": 26.538941763998174, - "beam_face_thickness": 37.0045746615665, - "holes_diameter": 184.02083272600328, - "hole_count": 13 - } +{ + "iteration": 35, + "expressions": { + "beam_half_core_thickness": 26.538941763998174, + "beam_face_thickness": 37.0045746615665, + "holes_diameter": 184.02083272600328, + "hole_count": 13.0 + }, + "trial_input": { + "beam_half_core_thickness": 26.538941763998174, + "beam_face_thickness": 37.0045746615665, + "holes_diameter": 184.02083272600328, + "hole_count": 13 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/params.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/params.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..09bd3575 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/params.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 35, + "expressions": { + "beam_half_core_thickness": 26.538941763998174, + "beam_face_thickness": 37.0045746615665, + "holes_diameter": 184.02083272600328, + "hole_count": 13.0 + }, + "trial_input": { + "beam_half_core_thickness": 26.538941763998174, + "beam_face_thickness": 37.0045746615665, + "holes_diameter": 184.02083272600328, + "hole_count": 13 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/results.json index 1aa3d24f..4b9f145b 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 35, - "mass_kg": NaN, - "tip_displacement_mm": 13.15087890625, - "max_von_mises_mpa": 82.6500234375, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 35, + "mass_kg": 1652.7291454725032, + "tip_displacement_mm": 13.15087890625, + "max_von_mises_mpa": 82.6500234375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..bba57c37 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter035/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 35, + "mass_kg": NaN, + "tip_displacement_mm": 13.15087890625, + "max_von_mises_mpa": 82.6500234375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..3fb7b2e0 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=899.6629740129987 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.txt index ac4a5d07..ad3ed540 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_mass.txt @@ -1 +1 @@ -p173=899.6629740129987 +899.6629740129987 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_part_properties.json new file mode 100644 index 00000000..a9360d7d --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 899.6629740129987, + "mass_g": 899662.9740129986, + "volume_mm3": 114286455.03213908, + "surface_area_mm2": 9934198.596869424, + "center_of_gravity_mm": [ + 2562.35980918501, + -4.682875999983144e-13, + -1.154215211342867e-12 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/params.json index 9ed5249e..d36bee6f 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 36, - "expressions": { - "beam_half_core_thickness": 13.332306234655581, - "beam_face_thickness": 22.63837385797062, - "holes_diameter": 260.8868498376813, - "hole_count": 13.0 - }, - "trial_input": { - "beam_half_core_thickness": 13.332306234655581, - "beam_face_thickness": 22.63837385797062, - "holes_diameter": 260.8868498376813, - "hole_count": 13 - } +{ + "iteration": 36, + "expressions": { + "beam_half_core_thickness": 13.332306234655581, + "beam_face_thickness": 22.63837385797062, + "holes_diameter": 260.8868498376813, + "hole_count": 13.0 + }, + "trial_input": { + "beam_half_core_thickness": 13.332306234655581, + "beam_face_thickness": 22.63837385797062, + "holes_diameter": 260.8868498376813, + "hole_count": 13 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..d36bee6f --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 36, + "expressions": { + "beam_half_core_thickness": 13.332306234655581, + "beam_face_thickness": 22.63837385797062, + "holes_diameter": 260.8868498376813, + "hole_count": 13.0 + }, + "trial_input": { + "beam_half_core_thickness": 13.332306234655581, + "beam_face_thickness": 22.63837385797062, + "holes_diameter": 260.8868498376813, + "hole_count": 13 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/results.json index 52ec619a..69bee738 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 36, - "mass_kg": NaN, - "tip_displacement_mm": 22.770187377929688, - "max_von_mises_mpa": 132.01121875, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 36, + "mass_kg": 899.6629740129987, + "tip_displacement_mm": 22.770187377929688, + "max_von_mises_mpa": 132.01121875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/results.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/results.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..c5e355ab --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter036/results.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 36, + "mass_kg": NaN, + "tip_displacement_mm": 22.770187377929688, + "max_von_mises_mpa": 132.01121875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..9f1e3595 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1539.426155950673 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.txt index 2597c13f..aa9161e4 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_mass.txt @@ -1 +1 @@ -p173=1539.426155950673 +1539.4261559506735 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_part_properties.json new file mode 100644 index 00000000..fbe8e9e9 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1539.4261559506735, + "mass_g": 1539426.1559506736, + "volume_mm3": 195557184.4449534, + "surface_area_mm2": 9893096.72264754, + "center_of_gravity_mm": [ + 2532.163089844956, + -3.0049398550865047e-13, + -5.93494683669812e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/params.json index 86496a1a..b92dbf71 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 37, - "expressions": { - "beam_half_core_thickness": 28.542165422707903, - "beam_face_thickness": 34.70133239672506, - "holes_diameter": 264.8484124805317, - "hole_count": 11.0 - }, - "trial_input": { - "beam_half_core_thickness": 28.542165422707903, - "beam_face_thickness": 34.70133239672506, - "holes_diameter": 264.8484124805317, - "hole_count": 11 - } +{ + "iteration": 37, + "expressions": { + "beam_half_core_thickness": 28.542165422707903, + "beam_face_thickness": 34.70133239672506, + "holes_diameter": 264.8484124805317, + "hole_count": 11.0 + }, + "trial_input": { + "beam_half_core_thickness": 28.542165422707903, + "beam_face_thickness": 34.70133239672506, + "holes_diameter": 264.8484124805317, + "hole_count": 11 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..b92dbf71 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 37, + "expressions": { + "beam_half_core_thickness": 28.542165422707903, + "beam_face_thickness": 34.70133239672506, + "holes_diameter": 264.8484124805317, + "hole_count": 11.0 + }, + "trial_input": { + "beam_half_core_thickness": 28.542165422707903, + "beam_face_thickness": 34.70133239672506, + "holes_diameter": 264.8484124805317, + "hole_count": 11 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/results.json index 52900fd2..bc98c914 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 37, - "mass_kg": NaN, - "tip_displacement_mm": 13.971890449523926, - "max_von_mises_mpa": 83.8096796875, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 37, + "mass_kg": 1539.4261559506735, + "tip_displacement_mm": 13.971890449523926, + "max_von_mises_mpa": 83.8096796875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/results.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/results.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..169e4578 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter037/results.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 37, + "mass_kg": NaN, + "tip_displacement_mm": 13.971890449523926, + "max_von_mises_mpa": 83.8096796875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_mass.txt index a2e6adec..db09efea 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_mass.txt @@ -1 +1 @@ -p173=1300.5025884507997 +1300.5025884507997 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_part_properties.json new file mode 100644 index 00000000..0d3774e5 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1300.5025884507997, + "mass_g": 1300502.5884507997, + "volume_mm3": 165206121.5003557, + "surface_area_mm2": 10302607.604769433, + "center_of_gravity_mm": [ + 2500.0000000000005, + -3.5135183419530684e-13, + -6.133581482599509e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/params.json index fbe3431f..7fee7393 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 38, - "expressions": { - "beam_half_core_thickness": 15.926345240553108, - "beam_face_thickness": 35.64252369044628, - "holes_diameter": 223.5314270645531, - "hole_count": 8.0 - }, - "trial_input": { - "beam_half_core_thickness": 15.926345240553108, - "beam_face_thickness": 35.64252369044628, - "holes_diameter": 223.5314270645531, - "hole_count": 8 - } +{ + "iteration": 38, + "expressions": { + "beam_half_core_thickness": 15.926345240553108, + "beam_face_thickness": 35.64252369044628, + "holes_diameter": 223.5314270645531, + "hole_count": 8.0 + }, + "trial_input": { + "beam_half_core_thickness": 15.926345240553108, + "beam_face_thickness": 35.64252369044628, + "holes_diameter": 223.5314270645531, + "hole_count": 8 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/params.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/params.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..7fee7393 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/params.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 38, + "expressions": { + "beam_half_core_thickness": 15.926345240553108, + "beam_face_thickness": 35.64252369044628, + "holes_diameter": 223.5314270645531, + "hole_count": 8.0 + }, + "trial_input": { + "beam_half_core_thickness": 15.926345240553108, + "beam_face_thickness": 35.64252369044628, + "holes_diameter": 223.5314270645531, + "hole_count": 8 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/results.json index 5534b4d5..f301518e 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 38, - "mass_kg": NaN, - "tip_displacement_mm": 15.33069133758545, - "max_von_mises_mpa": 95.525046875, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 38, + "mass_kg": 1300.5025884507997, + "tip_displacement_mm": 15.33069133758545, + "max_von_mises_mpa": 95.525046875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/results.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/results.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..81239629 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter038/results.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 38, + "mass_kg": NaN, + "tip_displacement_mm": 15.33069133758545, + "max_von_mises_mpa": 95.525046875, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.sync-conflict-20260214-141754-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt new file mode 100644 index 00000000..06fc8517 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.sync-conflict-20260214-141755-RBNC225.txt @@ -0,0 +1 @@ +p173=1343.5034206648418 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.txt b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.txt index 8ce62fe2..80cb5e66 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_mass.txt @@ -1 +1 @@ -p173=1343.5034206648418 +1343.5034206648418 \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_part_properties.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_part_properties.json new file mode 100644 index 00000000..e673b207 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/_temp_part_properties.json @@ -0,0 +1,15 @@ +{ + "part_file": "Beam", + "mass_kg": 1343.5034206648418, + "mass_g": 1343503.4206648418, + "volume_mm3": 170668625.59258664, + "surface_area_mm2": 9376402.579189494, + "center_of_gravity_mm": [ + 2499.9999999999995, + -3.0683416327531814e-13, + -7.359365939967888e-13 + ], + "num_bodies": 1, + "success": true, + "error": null +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/params.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/params.json index d86bb161..ba716e1f 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/params.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/params.json @@ -1,15 +1,15 @@ -{ - "iteration": 39, - "expressions": { - "beam_half_core_thickness": 28.915906359365827, - "beam_face_thickness": 31.075262775497517, - "holes_diameter": 346.80055078514937, - "hole_count": 9.0 - }, - "trial_input": { - "beam_half_core_thickness": 28.915906359365827, - "beam_face_thickness": 31.075262775497517, - "holes_diameter": 346.80055078514937, - "hole_count": 9 - } +{ + "iteration": 39, + "expressions": { + "beam_half_core_thickness": 28.915906359365827, + "beam_face_thickness": 31.075262775497517, + "holes_diameter": 346.80055078514937, + "hole_count": 9.0 + }, + "trial_input": { + "beam_half_core_thickness": 28.915906359365827, + "beam_face_thickness": 31.075262775497517, + "holes_diameter": 346.80055078514937, + "hole_count": 9 + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/params.sync-conflict-20260214-141754-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/params.sync-conflict-20260214-141754-RBNC225.json new file mode 100644 index 00000000..ba716e1f --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/params.sync-conflict-20260214-141754-RBNC225.json @@ -0,0 +1,15 @@ +{ + "iteration": 39, + "expressions": { + "beam_half_core_thickness": 28.915906359365827, + "beam_face_thickness": 31.075262775497517, + "holes_diameter": 346.80055078514937, + "hole_count": 9.0 + }, + "trial_input": { + "beam_half_core_thickness": 28.915906359365827, + "beam_face_thickness": 31.075262775497517, + "holes_diameter": 346.80055078514937, + "hole_count": 9 + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/results.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/results.json index 3276d192..48f0d11a 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/results.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/results.json @@ -1,8 +1,8 @@ -{ - "iteration": 39, - "mass_kg": NaN, - "tip_displacement_mm": 16.25737953186035, - "max_von_mises_mpa": 106.1287734375, - "feasible": false, - "op2_file": "beam_sim1-solution_1.op2" +{ + "iteration": 39, + "mass_kg": 1343.5034206648418, + "tip_displacement_mm": 16.25737953186035, + "max_von_mises_mpa": 106.1287734375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/results.sync-conflict-20260214-141755-RBNC225.json b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/results.sync-conflict-20260214-141755-RBNC225.json new file mode 100644 index 00000000..ea7e8128 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/iterations/iter039/results.sync-conflict-20260214-141755-RBNC225.json @@ -0,0 +1,8 @@ +{ + "iteration": 39, + "mass_kg": NaN, + "tip_displacement_mm": 16.25737953186035, + "max_von_mises_mpa": 106.1287734375, + "feasible": false, + "op2_file": "beam_sim1-solution_1.op2" +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/nx_interface.py b/projects/hydrotech-beam/studies/01_doe_landscape/nx_interface.py old mode 100644 new mode 100755 index 4761d507..8da61c77 --- a/projects/hydrotech-beam/studies/01_doe_landscape/nx_interface.py +++ b/projects/hydrotech-beam/studies/01_doe_landscape/nx_interface.py @@ -1,573 +1,573 @@ -"""NX automation interface for Hydrotech Beam optimization. - -Integrates with the existing Atomizer optimization_engine: -- NXSolver for journal-based solve (run_journal.exe โ†’ solve_simulation.py) -- pyNastran OP2 extractors for displacement + stress -- Expression-based mass extraction via journal temp file - -The proven SAT3 pipeline: write .exp โ†’ NX journal updates + solves โ†’ pyNastran reads OP2. - -NX Expression Names (confirmed via binary introspection โ€” CONTEXT.md): - Design Variables: - - beam_half_core_thickness (mm, continuous) - - beam_face_thickness (mm, continuous) - - holes_diameter (mm, continuous) - - hole_count (integer, links to Pattern_p7) - Outputs: - - p173 (mass in kg, body_property147.mass) - Fixed: - - beam_lenght (โš ๏ธ TYPO in NX โ€” no 'h', 5000 mm) - - beam_half_height (250 mm) - - beam_half_width (150 mm) - -References: - CONTEXT.md โ€” Full expression map - optimization_engine/nx/solver.py โ€” NXSolver class - optimization_engine/extractors/ โ€” pyNastran-based result extractors - studies/M1_Mirror/SAT3_Trajectory_V7/run_optimization.py โ€” proven pattern -""" - -from __future__ import annotations - -import logging -import os -import sys -import time -from dataclasses import dataclass -from pathlib import Path -from typing import Any, Dict, Optional, Protocol - -logger = logging.getLogger(__name__) - - -# --------------------------------------------------------------------------- -# Data types -# --------------------------------------------------------------------------- -@dataclass(frozen=True) -class TrialInput: - """Design variable values for a single trial.""" - - beam_half_core_thickness: float # mm โ€” DV1 - beam_face_thickness: float # mm โ€” DV2 - holes_diameter: float # mm โ€” DV3 - hole_count: int # โ€” DV4 - - -@dataclass -class TrialResult: - """Results extracted from NX after a trial solve. - - All values populated after a successful SOL 101 solve. - On failure, success=False and error_message explains the failure. - """ - - success: bool - mass: float = float("nan") # kg โ€” from expression `p173` - tip_displacement: float = float("nan") # mm โ€” from SOL 101 results - max_von_mises: float = float("nan") # MPa โ€” from SOL 101 results - solve_time: float = 0.0 # seconds - error_message: str = "" - iteration_dir: Optional[str] = None # path to iteration folder - - -# --------------------------------------------------------------------------- -# NX expression name constants -# --------------------------------------------------------------------------- -# โš ๏ธ These are EXACT NX expression names from binary introspection. -# Do NOT change spelling โ€” `beam_lenght` has a typo (no 'h') in NX. -EXPR_HALF_CORE_THICKNESS = "beam_half_core_thickness" -EXPR_FACE_THICKNESS = "beam_face_thickness" -EXPR_HOLES_DIAMETER = "holes_diameter" -EXPR_HOLE_COUNT = "hole_count" -EXPR_MASS = "p173" # body_property147.mass, kg -EXPR_BEAM_LENGTH = "beam_lenght" # โš ๏ธ TYPO IN NX โ€” intentional - -# Unit mapping for .exp file generation (NXSolver._write_expression_file) -UNIT_MAPPING = { - EXPR_HALF_CORE_THICKNESS: "mm", - EXPR_FACE_THICKNESS: "mm", - EXPR_HOLES_DIAMETER: "mm", - EXPR_HOLE_COUNT: "Constant", # integer โ€” no unit -} - - -# --------------------------------------------------------------------------- -# Interface protocol -# --------------------------------------------------------------------------- -class NXSolverInterface(Protocol): - """Protocol for NX solver backends (enables stub/real swap).""" - - def solve(self, trial: TrialInput) -> TrialResult: ... - def close(self) -> None: ... - - -# --------------------------------------------------------------------------- -# Factory -# --------------------------------------------------------------------------- -def create_solver(backend: str = "stub", **kwargs: Any) -> NXSolverInterface: - """Create the appropriate solver backend. - - Args: - backend: "stub" for testing, "nxopen" for real NX runs. - **kwargs: Passed to solver constructor. - For "nxopen": - model_dir: Path to NX model files (required) - nx_version: NX version string (default: "2412") - timeout: Max solve time in seconds (default: 600) - use_iteration_folders: HEEDS-style per-trial folders (default: True) - - Returns: - Solver instance implementing NXSolverInterface. - """ - if backend == "stub": - return StubSolver(**kwargs) - elif backend == "nxopen": - return AtomizerNXSolver(**kwargs) - else: - raise ValueError(f"Unknown backend: {backend!r}. Use 'stub' or 'nxopen'.") - - -# --------------------------------------------------------------------------- -# Real solver โ€” wraps optimization_engine -# --------------------------------------------------------------------------- -class AtomizerNXSolver: - """Production solver using Atomizer's optimization_engine. - - Pipeline (proven in SAT3): - 1. Create iteration folder with fresh model copies - 2. Write .exp file with updated expression values - 3. NX journal: open .sim โ†’ import .exp โ†’ update geometry โ†’ solve SOL 101 - 4. Journal writes mass to _temp_mass.txt - 5. pyNastran reads .op2 โ†’ extract displacement + stress - 6. Return results - """ - - def __init__( - self, - model_dir: str | Path = ".", - nx_version: str = "2512", - timeout: int = 600, - use_iteration_folders: bool = False, # Disabled: copied NX files break internal references - ): - model_dir = Path(model_dir) - if not model_dir.exists(): - raise FileNotFoundError(f"Model directory not found: {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 - - self.study_dir = Path(__file__).parent.resolve() - - # Iteration output folders (solver outputs + params, NOT model copies) - self.iterations_dir = self.study_dir / "iterations" - self.iterations_dir.mkdir(parents=True, exist_ok=True) - - # One-time backup of master model (restored before each trial for isolation) - # NX .sim files store absolute internal references to .fem/.prt โ€” copying - # them to iteration folders breaks these references. Instead we solve on - # the master model in-place and archive outputs to iteration folders. - import shutil - self._backup_dir = self.study_dir / "_model_backup" - if not self._backup_dir.exists(): - logger.info("Creating master model backup at %s", self._backup_dir) - self._backup_dir.mkdir(parents=True) - for f in model_dir.iterdir(): - if f.is_file(): - shutil.copy2(f, self._backup_dir / f.name) - n_backed = len(list(self._backup_dir.iterdir())) - logger.info("Backed up %d model files", n_backed) - else: - logger.info("Using existing model backup at %s", self._backup_dir) - - # Find the .sim file - # 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: - raise FileNotFoundError(f"No .sim file found in {self.model_dir}") - self.sim_file = sim_files[0] # Already absolute (from resolved parent) - logger.info("SIM file: %s", self.sim_file.name) - logger.info("SIM path: %s", self.sim_file) - - # Find the .prt file (for mass extraction) - prt_files = [f for f in self.model_dir.glob("*.prt") if "_i." not in f.name] - if not prt_files: - raise FileNotFoundError(f"No .prt file found in {self.model_dir}") - self.prt_file = prt_files[0] - logger.info("PRT file: %s", self.prt_file.name) - - # Add Atomizer root to path for imports - atomizer_root = self._find_atomizer_root() - if atomizer_root and str(atomizer_root) not in sys.path: - sys.path.insert(0, str(atomizer_root)) - logger.info("Added Atomizer root to path: %s", atomizer_root) - - # Import optimization_engine components - try: - from optimization_engine.nx.solver import NXSolver - self._nx_solver = NXSolver( - nastran_version=nx_version, - timeout=timeout, - use_journal=True, - study_name="hydrotech_beam_doe", - use_iteration_folders=False, # Direct model: avoid broken NX file references - master_model_dir=model_dir, - ) - logger.info( - "NXSolver initialized (NX %s, timeout=%ds, journal mode)", - nx_version, timeout, - ) - except ImportError as e: - raise ImportError( - f"Could not import optimization_engine. " - f"Ensure Atomizer repo root is on PYTHONPATH.\n" - f"Error: {e}" - ) from e - - # Import extractors - try: - from optimization_engine.extractors.extract_displacement import ( - extract_displacement, - ) - from optimization_engine.extractors.extract_von_mises_stress import ( - extract_solid_stress, - ) - from optimization_engine.extractors.extract_mass_from_expression import ( - extract_mass_from_expression, - ) - self._extract_displacement = extract_displacement - self._extract_stress = extract_solid_stress - self._extract_mass = extract_mass_from_expression - logger.info("Extractors loaded: displacement, von_mises, mass") - except ImportError as e: - raise ImportError( - f"Could not import extractors from optimization_engine.\n" - f"Error: {e}" - ) from e - - def _find_atomizer_root(self) -> Optional[Path]: - """Walk up from model_dir to find the Atomizer repo root.""" - # Look for optimization_engine directory - candidate = self.model_dir - for _ in range(10): - candidate = candidate.parent - if (candidate / "optimization_engine").is_dir(): - return candidate - if candidate == candidate.parent: - break - - # Fallback: common paths - for path in [ - Path("C:/Users/antoi/Atomizer"), - Path("/home/papa/repos/Atomizer"), - ]: - if (path / "optimization_engine").is_dir(): - return path - - logger.warning("Could not find Atomizer root with optimization_engine/") - return None - - def solve(self, trial: TrialInput) -> TrialResult: - """Run a single trial through the NX pipeline. - - Args: - trial: Design variable values. - - Returns: - TrialResult with mass, displacement, stress (or failure info). - """ - self._iteration += 1 - start_time = time.time() - - # Build expression update dict - expressions: Dict[str, float] = { - EXPR_HALF_CORE_THICKNESS: trial.beam_half_core_thickness, - EXPR_FACE_THICKNESS: trial.beam_face_thickness, - EXPR_HOLES_DIAMETER: trial.holes_diameter, - EXPR_HOLE_COUNT: float(trial.hole_count), # NX expects float in .exp - } - - logger.info( - "Trial %d: core=%.2f face=%.2f dia=%.1f count=%d", - self._iteration, - trial.beam_half_core_thickness, - trial.beam_face_thickness, - trial.holes_diameter, - trial.hole_count, - ) - - # Create iteration output folder - iter_dir = self.iterations_dir / f"iter{self._iteration:03d}" - iter_dir.mkdir(parents=True, exist_ok=True) - - try: - # Step 0: Restore master model from backup (clean state) - import shutil - import json - restored = 0 - for bf in self._backup_dir.iterdir(): - if bf.is_file(): - shutil.copy2(bf, self.model_dir / bf.name) - restored += 1 - logger.info("Restored %d model files from backup", restored) - - # Save trial params to iteration folder - 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)) - - # Also write .exp file to iteration folder (import into NX to recreate) - exp_file = iter_dir / "params.exp" - with open(exp_file, "w") as f: - for name, val in expressions.items(): - unit = "Constant" if name in ("hole_count",) else "MilliMeter" - f.write(f'{name}={val} [{unit}]\n') - - # Step 1: Solve on MASTER model (NX internal references intact) - sim_file = self.sim_file - prt_file = self.prt_file - solve_result = self._nx_solver.run_simulation( - sim_file=sim_file, - working_dir=self.model_dir, - expression_updates=expressions, - ) - - if not solve_result.get("success", False): - errors = solve_result.get("errors", ["Unknown solver error"]) - return TrialResult( - success=False, - solve_time=time.time() - start_time, - error_message=f"NX solve failed: {'; '.join(errors)}", - iteration_dir=str(iter_dir), - ) - - op2_file = solve_result.get("op2_file") - if not op2_file or not Path(op2_file).exists(): - return TrialResult( - success=False, - solve_time=time.time() - start_time, - error_message="OP2 file not generated after solve", - iteration_dir=str(iter_dir), - ) - - op2_path = Path(op2_file) - - # Step 3: Extract mass from journal temp file - # The journal writes _temp_mass.txt to working_dir (= model_dir). - # The extractor looks in prt_file.parent (= model_dir). These MUST match. - try: - mass_kg = self._extract_mass(prt_file, expression_name=EXPR_MASS) - except FileNotFoundError: - # Fallback: parse mass from journal stdout captured in solve_result - # The journal prints "[JOURNAL] Mass expression p173 = " or - # "[JOURNAL] MeasureManager mass = " - mass_kg = float("nan") - stdout = solve_result.get("stdout", "") - if stdout: - import re - # Match either extraction method's output - m = re.search( - r'\[JOURNAL\]\s+(?:Mass expression p173|MeasureManager mass)\s*=\s*([0-9.eE+-]+)', - stdout, - ) - if m: - try: - mass_kg = float(m.group(1)) - logger.info("Mass recovered from journal stdout: %.6f kg", mass_kg) - except ValueError: - pass - if mass_kg != mass_kg: # NaN check - logger.warning( - "Mass temp file not found in %s and no mass in journal stdout", - self.model_dir, - ) - except Exception as e: - logger.warning("Mass extraction failed: %s", e) - mass_kg = float("nan") - - # Step 4: Extract displacement from OP2 - try: - disp_result = self._extract_displacement(op2_path) - # For cantilever beam, max displacement IS tip displacement - tip_displacement = disp_result["max_displacement"] - except Exception as e: - logger.warning("Displacement extraction failed: %s", e) - tip_displacement = float("nan") - - # Step 5: Extract max von Mises stress from OP2 - # Use shell element extraction (CQUAD4 mesh) - try: - stress_result = self._extract_stress( - op2_path, - element_type="cquad4", - convert_to_mpa=True, # โš ๏ธ LAC lesson: NX outputs kPa, must convert - ) - max_vm_stress = stress_result["max_von_mises"] - except Exception as e: - logger.warning("Stress extraction failed: %s", e) - max_vm_stress = float("nan") - - # Step 6: Archive solver outputs to iteration folder - # Copy OP2, F06, and other solver outputs from models/ dir - for suffix in (".op2", ".f06", ".log", ".dat"): - for src in self.model_dir.glob(f"*{suffix}"): - try: - shutil.copy2(src, iter_dir / src.name) - except Exception as e: - logger.warning("Could not archive %s: %s", src.name, e) - - # Copy temp files (mass extraction, etc.) - for pattern in ("_temp_*",): - for src in self.model_dir.glob(pattern): - try: - shutil.copy2(src, iter_dir / src.name) - except Exception: - pass - - # Write results summary JSON - results_file = iter_dir / "results.json" - results_file.write_text(json.dumps({ - "iteration": self._iteration, - "mass_kg": mass_kg, - "tip_displacement_mm": tip_displacement, - "max_von_mises_mpa": max_vm_stress, - "feasible": tip_displacement <= 10.0 and max_vm_stress <= 130.0, - "op2_file": op2_path.name if op2_path else None, - }, indent=2)) - - logger.info("Archived iter%03d: results + solver outputs", self._iteration) - - elapsed = time.time() - start_time - logger.info( - "Trial %d complete: mass=%.2f kg, disp=%.3f mm, stress=%.1f MPa (%.1fs)", - self._iteration, mass_kg, tip_displacement, max_vm_stress, elapsed, - ) - - return TrialResult( - success=True, - mass=mass_kg, - tip_displacement=tip_displacement, - max_von_mises=max_vm_stress, - solve_time=elapsed, - iteration_dir=str(iter_dir), - ) - - except Exception as e: - elapsed = time.time() - start_time - logger.error("Trial %d failed: %s", self._iteration, e, exc_info=True) - return TrialResult( - success=False, - solve_time=elapsed, - error_message=str(e), - iteration_dir=str(iter_dir) if 'iter_dir' in locals() else None, - ) - - def close(self) -> None: - """Clean up NX solver resources.""" - logger.info("AtomizerNXSolver closed. %d iterations completed.", self._iteration) - - -# --------------------------------------------------------------------------- -# Stub solver โ€” for development/testing without NX -# --------------------------------------------------------------------------- -class StubSolver: - """Synthetic solver for testing without NX. - - Generates physically-plausible approximate results based on - beam theory. NOT accurate โ€” only for pipeline validation. - """ - - def __init__(self, **kwargs: Any): - self._call_count = 0 - logger.warning( - "Using NX STUB solver โ€” results are synthetic approximations. " - "Replace with AtomizerNXSolver (--backend nxopen) for real evaluations." - ) - - def solve(self, trial: TrialInput) -> TrialResult: - """Generate approximate results from beam theory. - - Uses simplified cantilever beam formulas: - - Mass โˆ cross-section area ร— length - hole_volume - - Displacement ~ PLยณ/3EI (Euler-Bernoulli) - - Stress ~ Mc/I (nominal) with hole SCF - """ - self._call_count += 1 - import numpy as np - - # Geometry (mm) - L = 5000.0 # beam length - h_half = 250.0 # beam half-height (fixed) - w_half = 150.0 # beam half-width (fixed) - h_core = trial.beam_half_core_thickness - t_face = trial.beam_face_thickness - d_hole = trial.holes_diameter - n_hole = trial.hole_count - - # Material: AISI 1005 - E = 205000.0 # MPa (Young's modulus) - rho = 7.3e-6 # kg/mmยณ (7.3 g/cmยณ) - - # I-beam cross-section second moment of area (approximate) - # Full section: 2*w_half ร— 2*h_half rectangle - # Minus core cutouts (simplified) - H = 2 * h_half # 500 mm total height - W = 2 * w_half # 300 mm total width - I_full = W * H**3 / 12 - # Subtract inner rectangle (core region without faces) - h_web = H - 2 * t_face - w_web = W - 2 * h_core # approximate - I_inner = max(0, w_web) * max(0, h_web)**3 / 12 - I_eff = max(I_full - I_inner, I_full * 0.01) # don't go to zero - - # Cross-section area (approximate) - A_section = W * H - max(0, w_web) * max(0, h_web) - # Hole volume removal - web_height = H - 2 * t_face - hole_area = n_hole * np.pi * (d_hole / 2)**2 - # Only remove from web if holes fit - if d_hole < web_height: - effective_hole_area = min(hole_area, 0.8 * web_height * 4000) - else: - effective_hole_area = 0 - # Mass - vol = A_section * L - effective_hole_area * min(h_core * 2, 50) - mass = max(rho * vol, 1.0) - - # Tip displacement: ฮด = PLยณ / 3EI - P = 10000 * 9.80665 # 10,000 kgf โ†’ N - delta = P * L**3 / (3 * E * I_eff) - - # Stress: ฯƒ = M*c/I with SCF from holes - M = P * L # max moment at fixed end - c = h_half # distance to extreme fiber - sigma_nominal = M * c / I_eff / 1000 # kPa โ†’ MPa - # Stress concentration from holes (simplified) - scf = 1.0 + 0.5 * (d_hole / (web_height + 1)) - sigma_max = sigma_nominal * scf - - # Add noise (ยฑ5%) to simulate model variability - rng = np.random.default_rng(self._call_count) - noise = rng.uniform(0.95, 1.05, 3) - - return TrialResult( - success=True, - mass=float(mass * noise[0]), - tip_displacement=float(delta * noise[1]), - max_von_mises=float(sigma_max * noise[2]), - solve_time=0.1, - ) - - def close(self) -> None: - """Clean up stub solver.""" - logger.info("Stub solver closed.") +"""NX automation interface for Hydrotech Beam optimization. + +Integrates with the existing Atomizer optimization_engine: +- NXSolver for journal-based solve (run_journal.exe โ†’ solve_simulation.py) +- pyNastran OP2 extractors for displacement + stress +- Expression-based mass extraction via journal temp file + +The proven SAT3 pipeline: write .exp โ†’ NX journal updates + solves โ†’ pyNastran reads OP2. + +NX Expression Names (confirmed via binary introspection โ€” CONTEXT.md): + Design Variables: + - beam_half_core_thickness (mm, continuous) + - beam_face_thickness (mm, continuous) + - holes_diameter (mm, continuous) + - hole_count (integer, links to Pattern_p7) + Outputs: + - p173 (mass in kg, body_property147.mass) + Fixed: + - beam_lenght (โš ๏ธ TYPO in NX โ€” no 'h', 5000 mm) + - beam_half_height (250 mm) + - beam_half_width (150 mm) + +References: + CONTEXT.md โ€” Full expression map + optimization_engine/nx/solver.py โ€” NXSolver class + optimization_engine/extractors/ โ€” pyNastran-based result extractors + studies/M1_Mirror/SAT3_Trajectory_V7/run_optimization.py โ€” proven pattern +""" + +from __future__ import annotations + +import logging +import os +import sys +import time +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Dict, Optional, Protocol + +logger = logging.getLogger(__name__) + + +# --------------------------------------------------------------------------- +# Data types +# --------------------------------------------------------------------------- +@dataclass(frozen=True) +class TrialInput: + """Design variable values for a single trial.""" + + beam_half_core_thickness: float # mm โ€” DV1 + beam_face_thickness: float # mm โ€” DV2 + holes_diameter: float # mm โ€” DV3 + hole_count: int # โ€” DV4 + + +@dataclass +class TrialResult: + """Results extracted from NX after a trial solve. + + All values populated after a successful SOL 101 solve. + On failure, success=False and error_message explains the failure. + """ + + success: bool + mass: float = float("nan") # kg โ€” from expression `p173` + tip_displacement: float = float("nan") # mm โ€” from SOL 101 results + max_von_mises: float = float("nan") # MPa โ€” from SOL 101 results + solve_time: float = 0.0 # seconds + error_message: str = "" + iteration_dir: Optional[str] = None # path to iteration folder + + +# --------------------------------------------------------------------------- +# NX expression name constants +# --------------------------------------------------------------------------- +# โš ๏ธ These are EXACT NX expression names from binary introspection. +# Do NOT change spelling โ€” `beam_lenght` has a typo (no 'h') in NX. +EXPR_HALF_CORE_THICKNESS = "beam_half_core_thickness" +EXPR_FACE_THICKNESS = "beam_face_thickness" +EXPR_HOLES_DIAMETER = "holes_diameter" +EXPR_HOLE_COUNT = "hole_count" +EXPR_MASS = "p173" # body_property147.mass, kg +EXPR_BEAM_LENGTH = "beam_lenght" # โš ๏ธ TYPO IN NX โ€” intentional + +# Unit mapping for .exp file generation (NXSolver._write_expression_file) +UNIT_MAPPING = { + EXPR_HALF_CORE_THICKNESS: "mm", + EXPR_FACE_THICKNESS: "mm", + EXPR_HOLES_DIAMETER: "mm", + EXPR_HOLE_COUNT: "Constant", # integer โ€” no unit +} + + +# --------------------------------------------------------------------------- +# Interface protocol +# --------------------------------------------------------------------------- +class NXSolverInterface(Protocol): + """Protocol for NX solver backends (enables stub/real swap).""" + + def solve(self, trial: TrialInput) -> TrialResult: ... + def close(self) -> None: ... + + +# --------------------------------------------------------------------------- +# Factory +# --------------------------------------------------------------------------- +def create_solver(backend: str = "stub", **kwargs: Any) -> NXSolverInterface: + """Create the appropriate solver backend. + + Args: + backend: "stub" for testing, "nxopen" for real NX runs. + **kwargs: Passed to solver constructor. + For "nxopen": + model_dir: Path to NX model files (required) + nx_version: NX version string (default: "2412") + timeout: Max solve time in seconds (default: 600) + use_iteration_folders: HEEDS-style per-trial folders (default: True) + + Returns: + Solver instance implementing NXSolverInterface. + """ + if backend == "stub": + return StubSolver(**kwargs) + elif backend == "nxopen": + return AtomizerNXSolver(**kwargs) + else: + raise ValueError(f"Unknown backend: {backend!r}. Use 'stub' or 'nxopen'.") + + +# --------------------------------------------------------------------------- +# Real solver โ€” wraps optimization_engine +# --------------------------------------------------------------------------- +class AtomizerNXSolver: + """Production solver using Atomizer's optimization_engine. + + Pipeline (proven in SAT3): + 1. Create iteration folder with fresh model copies + 2. Write .exp file with updated expression values + 3. NX journal: open .sim โ†’ import .exp โ†’ update geometry โ†’ solve SOL 101 + 4. Journal writes mass to _temp_mass.txt + 5. pyNastran reads .op2 โ†’ extract displacement + stress + 6. Return results + """ + + def __init__( + self, + model_dir: str | Path = ".", + nx_version: str = "2512", + timeout: int = 600, + use_iteration_folders: bool = False, # Disabled: copied NX files break internal references + ): + model_dir = Path(model_dir) + if not model_dir.exists(): + raise FileNotFoundError(f"Model directory not found: {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 + + self.study_dir = Path(__file__).parent.resolve() + + # Iteration output folders (solver outputs + params, NOT model copies) + self.iterations_dir = self.study_dir / "iterations" + self.iterations_dir.mkdir(parents=True, exist_ok=True) + + # One-time backup of master model (restored before each trial for isolation) + # NX .sim files store absolute internal references to .fem/.prt โ€” copying + # them to iteration folders breaks these references. Instead we solve on + # the master model in-place and archive outputs to iteration folders. + import shutil + self._backup_dir = self.study_dir / "_model_backup" + if not self._backup_dir.exists(): + logger.info("Creating master model backup at %s", self._backup_dir) + self._backup_dir.mkdir(parents=True) + for f in model_dir.iterdir(): + if f.is_file(): + shutil.copy2(f, self._backup_dir / f.name) + n_backed = len(list(self._backup_dir.iterdir())) + logger.info("Backed up %d model files", n_backed) + else: + logger.info("Using existing model backup at %s", self._backup_dir) + + # Find the .sim file + # 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: + raise FileNotFoundError(f"No .sim file found in {self.model_dir}") + self.sim_file = sim_files[0] # Already absolute (from resolved parent) + logger.info("SIM file: %s", self.sim_file.name) + logger.info("SIM path: %s", self.sim_file) + + # Find the .prt file (for mass extraction) + prt_files = [f for f in self.model_dir.glob("*.prt") if "_i." not in f.name] + if not prt_files: + raise FileNotFoundError(f"No .prt file found in {self.model_dir}") + self.prt_file = prt_files[0] + logger.info("PRT file: %s", self.prt_file.name) + + # Add Atomizer root to path for imports + atomizer_root = self._find_atomizer_root() + if atomizer_root and str(atomizer_root) not in sys.path: + sys.path.insert(0, str(atomizer_root)) + logger.info("Added Atomizer root to path: %s", atomizer_root) + + # Import optimization_engine components + try: + from optimization_engine.nx.solver import NXSolver + self._nx_solver = NXSolver( + nastran_version=nx_version, + timeout=timeout, + use_journal=True, + study_name="hydrotech_beam_doe", + use_iteration_folders=False, # Direct model: avoid broken NX file references + master_model_dir=model_dir, + ) + logger.info( + "NXSolver initialized (NX %s, timeout=%ds, journal mode)", + nx_version, timeout, + ) + except ImportError as e: + raise ImportError( + f"Could not import optimization_engine. " + f"Ensure Atomizer repo root is on PYTHONPATH.\n" + f"Error: {e}" + ) from e + + # Import extractors + try: + from optimization_engine.extractors.extract_displacement import ( + extract_displacement, + ) + from optimization_engine.extractors.extract_von_mises_stress import ( + extract_solid_stress, + ) + from optimization_engine.extractors.extract_mass_from_expression import ( + extract_mass_from_expression, + ) + self._extract_displacement = extract_displacement + self._extract_stress = extract_solid_stress + self._extract_mass = extract_mass_from_expression + logger.info("Extractors loaded: displacement, von_mises, mass") + except ImportError as e: + raise ImportError( + f"Could not import extractors from optimization_engine.\n" + f"Error: {e}" + ) from e + + def _find_atomizer_root(self) -> Optional[Path]: + """Walk up from model_dir to find the Atomizer repo root.""" + # Look for optimization_engine directory + candidate = self.model_dir + for _ in range(10): + candidate = candidate.parent + if (candidate / "optimization_engine").is_dir(): + return candidate + if candidate == candidate.parent: + break + + # Fallback: common paths + for path in [ + Path("C:/Users/antoi/Atomizer"), + Path("/home/papa/repos/Atomizer"), + ]: + if (path / "optimization_engine").is_dir(): + return path + + logger.warning("Could not find Atomizer root with optimization_engine/") + return None + + def solve(self, trial: TrialInput) -> TrialResult: + """Run a single trial through the NX pipeline. + + Args: + trial: Design variable values. + + Returns: + TrialResult with mass, displacement, stress (or failure info). + """ + self._iteration += 1 + start_time = time.time() + + # Build expression update dict + expressions: Dict[str, float] = { + EXPR_HALF_CORE_THICKNESS: trial.beam_half_core_thickness, + EXPR_FACE_THICKNESS: trial.beam_face_thickness, + EXPR_HOLES_DIAMETER: trial.holes_diameter, + EXPR_HOLE_COUNT: float(trial.hole_count), # NX expects float in .exp + } + + logger.info( + "Trial %d: core=%.2f face=%.2f dia=%.1f count=%d", + self._iteration, + trial.beam_half_core_thickness, + trial.beam_face_thickness, + trial.holes_diameter, + trial.hole_count, + ) + + # Create iteration output folder + iter_dir = self.iterations_dir / f"iter{self._iteration:03d}" + iter_dir.mkdir(parents=True, exist_ok=True) + + try: + # Step 0: Restore master model from backup (clean state) + import shutil + import json + restored = 0 + for bf in self._backup_dir.iterdir(): + if bf.is_file(): + shutil.copy2(bf, self.model_dir / bf.name) + restored += 1 + logger.info("Restored %d model files from backup", restored) + + # Save trial params to iteration folder + 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)) + + # Also write .exp file to iteration folder (import into NX to recreate) + exp_file = iter_dir / "params.exp" + with open(exp_file, "w") as f: + for name, val in expressions.items(): + unit = "Constant" if name in ("hole_count",) else "MilliMeter" + f.write(f'{name}={val} [{unit}]\n') + + # Step 1: Solve on MASTER model (NX internal references intact) + sim_file = self.sim_file + prt_file = self.prt_file + solve_result = self._nx_solver.run_simulation( + sim_file=sim_file, + working_dir=self.model_dir, + expression_updates=expressions, + ) + + if not solve_result.get("success", False): + errors = solve_result.get("errors", ["Unknown solver error"]) + return TrialResult( + success=False, + solve_time=time.time() - start_time, + error_message=f"NX solve failed: {'; '.join(errors)}", + iteration_dir=str(iter_dir), + ) + + op2_file = solve_result.get("op2_file") + if not op2_file or not Path(op2_file).exists(): + return TrialResult( + success=False, + solve_time=time.time() - start_time, + error_message="OP2 file not generated after solve", + iteration_dir=str(iter_dir), + ) + + op2_path = Path(op2_file) + + # Step 3: Extract mass from journal temp file + # The journal writes _temp_mass.txt to working_dir (= model_dir). + # The extractor looks in prt_file.parent (= model_dir). These MUST match. + try: + mass_kg = self._extract_mass(prt_file, expression_name=EXPR_MASS) + except FileNotFoundError: + # Fallback: parse mass from journal stdout captured in solve_result + # The journal prints "[JOURNAL] Mass expression p173 = " or + # "[JOURNAL] MeasureManager mass = " + mass_kg = float("nan") + stdout = solve_result.get("stdout", "") + if stdout: + import re + # Match either extraction method's output + m = re.search( + r'\[JOURNAL\]\s+(?:Mass expression p173|MeasureManager mass)\s*=\s*([0-9.eE+-]+)', + stdout, + ) + if m: + try: + mass_kg = float(m.group(1)) + logger.info("Mass recovered from journal stdout: %.6f kg", mass_kg) + except ValueError: + pass + if mass_kg != mass_kg: # NaN check + logger.warning( + "Mass temp file not found in %s and no mass in journal stdout", + self.model_dir, + ) + except Exception as e: + logger.warning("Mass extraction failed: %s", e) + mass_kg = float("nan") + + # Step 4: Extract displacement from OP2 + try: + disp_result = self._extract_displacement(op2_path) + # For cantilever beam, max displacement IS tip displacement + tip_displacement = disp_result["max_displacement"] + except Exception as e: + logger.warning("Displacement extraction failed: %s", e) + tip_displacement = float("nan") + + # Step 5: Extract max von Mises stress from OP2 + # Use shell element extraction (CQUAD4 mesh) + try: + stress_result = self._extract_stress( + op2_path, + element_type="cquad4", + convert_to_mpa=True, # โš ๏ธ LAC lesson: NX outputs kPa, must convert + ) + max_vm_stress = stress_result["max_von_mises"] + except Exception as e: + logger.warning("Stress extraction failed: %s", e) + max_vm_stress = float("nan") + + # Step 6: Archive solver outputs to iteration folder + # Copy OP2, F06, and other solver outputs from models/ dir + for suffix in (".op2", ".f06", ".log", ".dat"): + for src in self.model_dir.glob(f"*{suffix}"): + try: + shutil.copy2(src, iter_dir / src.name) + except Exception as e: + logger.warning("Could not archive %s: %s", src.name, e) + + # Copy temp files (mass extraction, etc.) + for pattern in ("_temp_*",): + for src in self.model_dir.glob(pattern): + try: + shutil.copy2(src, iter_dir / src.name) + except Exception: + pass + + # Write results summary JSON + results_file = iter_dir / "results.json" + results_file.write_text(json.dumps({ + "iteration": self._iteration, + "mass_kg": mass_kg, + "tip_displacement_mm": tip_displacement, + "max_von_mises_mpa": max_vm_stress, + "feasible": tip_displacement <= 10.0 and max_vm_stress <= 130.0, + "op2_file": op2_path.name if op2_path else None, + }, indent=2)) + + logger.info("Archived iter%03d: results + solver outputs", self._iteration) + + elapsed = time.time() - start_time + logger.info( + "Trial %d complete: mass=%.2f kg, disp=%.3f mm, stress=%.1f MPa (%.1fs)", + self._iteration, mass_kg, tip_displacement, max_vm_stress, elapsed, + ) + + return TrialResult( + success=True, + mass=mass_kg, + tip_displacement=tip_displacement, + max_von_mises=max_vm_stress, + solve_time=elapsed, + iteration_dir=str(iter_dir), + ) + + except Exception as e: + elapsed = time.time() - start_time + logger.error("Trial %d failed: %s", self._iteration, e, exc_info=True) + return TrialResult( + success=False, + solve_time=elapsed, + error_message=str(e), + iteration_dir=str(iter_dir) if 'iter_dir' in locals() else None, + ) + + def close(self) -> None: + """Clean up NX solver resources.""" + logger.info("AtomizerNXSolver closed. %d iterations completed.", self._iteration) + + +# --------------------------------------------------------------------------- +# Stub solver โ€” for development/testing without NX +# --------------------------------------------------------------------------- +class StubSolver: + """Synthetic solver for testing without NX. + + Generates physically-plausible approximate results based on + beam theory. NOT accurate โ€” only for pipeline validation. + """ + + def __init__(self, **kwargs: Any): + self._call_count = 0 + logger.warning( + "Using NX STUB solver โ€” results are synthetic approximations. " + "Replace with AtomizerNXSolver (--backend nxopen) for real evaluations." + ) + + def solve(self, trial: TrialInput) -> TrialResult: + """Generate approximate results from beam theory. + + Uses simplified cantilever beam formulas: + - Mass โˆ cross-section area ร— length - hole_volume + - Displacement ~ PLยณ/3EI (Euler-Bernoulli) + - Stress ~ Mc/I (nominal) with hole SCF + """ + self._call_count += 1 + import numpy as np + + # Geometry (mm) + L = 5000.0 # beam length + h_half = 250.0 # beam half-height (fixed) + w_half = 150.0 # beam half-width (fixed) + h_core = trial.beam_half_core_thickness + t_face = trial.beam_face_thickness + d_hole = trial.holes_diameter + n_hole = trial.hole_count + + # Material: AISI 1005 + E = 205000.0 # MPa (Young's modulus) + rho = 7.3e-6 # kg/mmยณ (7.3 g/cmยณ) + + # I-beam cross-section second moment of area (approximate) + # Full section: 2*w_half ร— 2*h_half rectangle + # Minus core cutouts (simplified) + H = 2 * h_half # 500 mm total height + W = 2 * w_half # 300 mm total width + I_full = W * H**3 / 12 + # Subtract inner rectangle (core region without faces) + h_web = H - 2 * t_face + w_web = W - 2 * h_core # approximate + I_inner = max(0, w_web) * max(0, h_web)**3 / 12 + I_eff = max(I_full - I_inner, I_full * 0.01) # don't go to zero + + # Cross-section area (approximate) + A_section = W * H - max(0, w_web) * max(0, h_web) + # Hole volume removal + web_height = H - 2 * t_face + hole_area = n_hole * np.pi * (d_hole / 2)**2 + # Only remove from web if holes fit + if d_hole < web_height: + effective_hole_area = min(hole_area, 0.8 * web_height * 4000) + else: + effective_hole_area = 0 + # Mass + vol = A_section * L - effective_hole_area * min(h_core * 2, 50) + mass = max(rho * vol, 1.0) + + # Tip displacement: ฮด = PLยณ / 3EI + P = 10000 * 9.80665 # 10,000 kgf โ†’ N + delta = P * L**3 / (3 * E * I_eff) + + # Stress: ฯƒ = M*c/I with SCF from holes + M = P * L # max moment at fixed end + c = h_half # distance to extreme fiber + sigma_nominal = M * c / I_eff / 1000 # kPa โ†’ MPa + # Stress concentration from holes (simplified) + scf = 1.0 + 0.5 * (d_hole / (web_height + 1)) + sigma_max = sigma_nominal * scf + + # Add noise (ยฑ5%) to simulate model variability + rng = np.random.default_rng(self._call_count) + noise = rng.uniform(0.95, 1.05, 3) + + return TrialResult( + success=True, + mass=float(mass * noise[0]), + tip_displacement=float(delta * noise[1]), + max_von_mises=float(sigma_max * noise[2]), + solve_time=0.1, + ) + + def close(self) -> None: + """Clean up stub solver.""" + logger.info("Stub solver closed.") diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/nx_interface.sync-conflict-20260214-191757-VBCUD7Z.py b/projects/hydrotech-beam/studies/01_doe_landscape/nx_interface.sync-conflict-20260214-191757-VBCUD7Z.py new file mode 100644 index 00000000..4761d507 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/nx_interface.sync-conflict-20260214-191757-VBCUD7Z.py @@ -0,0 +1,573 @@ +"""NX automation interface for Hydrotech Beam optimization. + +Integrates with the existing Atomizer optimization_engine: +- NXSolver for journal-based solve (run_journal.exe โ†’ solve_simulation.py) +- pyNastran OP2 extractors for displacement + stress +- Expression-based mass extraction via journal temp file + +The proven SAT3 pipeline: write .exp โ†’ NX journal updates + solves โ†’ pyNastran reads OP2. + +NX Expression Names (confirmed via binary introspection โ€” CONTEXT.md): + Design Variables: + - beam_half_core_thickness (mm, continuous) + - beam_face_thickness (mm, continuous) + - holes_diameter (mm, continuous) + - hole_count (integer, links to Pattern_p7) + Outputs: + - p173 (mass in kg, body_property147.mass) + Fixed: + - beam_lenght (โš ๏ธ TYPO in NX โ€” no 'h', 5000 mm) + - beam_half_height (250 mm) + - beam_half_width (150 mm) + +References: + CONTEXT.md โ€” Full expression map + optimization_engine/nx/solver.py โ€” NXSolver class + optimization_engine/extractors/ โ€” pyNastran-based result extractors + studies/M1_Mirror/SAT3_Trajectory_V7/run_optimization.py โ€” proven pattern +""" + +from __future__ import annotations + +import logging +import os +import sys +import time +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Dict, Optional, Protocol + +logger = logging.getLogger(__name__) + + +# --------------------------------------------------------------------------- +# Data types +# --------------------------------------------------------------------------- +@dataclass(frozen=True) +class TrialInput: + """Design variable values for a single trial.""" + + beam_half_core_thickness: float # mm โ€” DV1 + beam_face_thickness: float # mm โ€” DV2 + holes_diameter: float # mm โ€” DV3 + hole_count: int # โ€” DV4 + + +@dataclass +class TrialResult: + """Results extracted from NX after a trial solve. + + All values populated after a successful SOL 101 solve. + On failure, success=False and error_message explains the failure. + """ + + success: bool + mass: float = float("nan") # kg โ€” from expression `p173` + tip_displacement: float = float("nan") # mm โ€” from SOL 101 results + max_von_mises: float = float("nan") # MPa โ€” from SOL 101 results + solve_time: float = 0.0 # seconds + error_message: str = "" + iteration_dir: Optional[str] = None # path to iteration folder + + +# --------------------------------------------------------------------------- +# NX expression name constants +# --------------------------------------------------------------------------- +# โš ๏ธ These are EXACT NX expression names from binary introspection. +# Do NOT change spelling โ€” `beam_lenght` has a typo (no 'h') in NX. +EXPR_HALF_CORE_THICKNESS = "beam_half_core_thickness" +EXPR_FACE_THICKNESS = "beam_face_thickness" +EXPR_HOLES_DIAMETER = "holes_diameter" +EXPR_HOLE_COUNT = "hole_count" +EXPR_MASS = "p173" # body_property147.mass, kg +EXPR_BEAM_LENGTH = "beam_lenght" # โš ๏ธ TYPO IN NX โ€” intentional + +# Unit mapping for .exp file generation (NXSolver._write_expression_file) +UNIT_MAPPING = { + EXPR_HALF_CORE_THICKNESS: "mm", + EXPR_FACE_THICKNESS: "mm", + EXPR_HOLES_DIAMETER: "mm", + EXPR_HOLE_COUNT: "Constant", # integer โ€” no unit +} + + +# --------------------------------------------------------------------------- +# Interface protocol +# --------------------------------------------------------------------------- +class NXSolverInterface(Protocol): + """Protocol for NX solver backends (enables stub/real swap).""" + + def solve(self, trial: TrialInput) -> TrialResult: ... + def close(self) -> None: ... + + +# --------------------------------------------------------------------------- +# Factory +# --------------------------------------------------------------------------- +def create_solver(backend: str = "stub", **kwargs: Any) -> NXSolverInterface: + """Create the appropriate solver backend. + + Args: + backend: "stub" for testing, "nxopen" for real NX runs. + **kwargs: Passed to solver constructor. + For "nxopen": + model_dir: Path to NX model files (required) + nx_version: NX version string (default: "2412") + timeout: Max solve time in seconds (default: 600) + use_iteration_folders: HEEDS-style per-trial folders (default: True) + + Returns: + Solver instance implementing NXSolverInterface. + """ + if backend == "stub": + return StubSolver(**kwargs) + elif backend == "nxopen": + return AtomizerNXSolver(**kwargs) + else: + raise ValueError(f"Unknown backend: {backend!r}. Use 'stub' or 'nxopen'.") + + +# --------------------------------------------------------------------------- +# Real solver โ€” wraps optimization_engine +# --------------------------------------------------------------------------- +class AtomizerNXSolver: + """Production solver using Atomizer's optimization_engine. + + Pipeline (proven in SAT3): + 1. Create iteration folder with fresh model copies + 2. Write .exp file with updated expression values + 3. NX journal: open .sim โ†’ import .exp โ†’ update geometry โ†’ solve SOL 101 + 4. Journal writes mass to _temp_mass.txt + 5. pyNastran reads .op2 โ†’ extract displacement + stress + 6. Return results + """ + + def __init__( + self, + model_dir: str | Path = ".", + nx_version: str = "2512", + timeout: int = 600, + use_iteration_folders: bool = False, # Disabled: copied NX files break internal references + ): + model_dir = Path(model_dir) + if not model_dir.exists(): + raise FileNotFoundError(f"Model directory not found: {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 + + self.study_dir = Path(__file__).parent.resolve() + + # Iteration output folders (solver outputs + params, NOT model copies) + self.iterations_dir = self.study_dir / "iterations" + self.iterations_dir.mkdir(parents=True, exist_ok=True) + + # One-time backup of master model (restored before each trial for isolation) + # NX .sim files store absolute internal references to .fem/.prt โ€” copying + # them to iteration folders breaks these references. Instead we solve on + # the master model in-place and archive outputs to iteration folders. + import shutil + self._backup_dir = self.study_dir / "_model_backup" + if not self._backup_dir.exists(): + logger.info("Creating master model backup at %s", self._backup_dir) + self._backup_dir.mkdir(parents=True) + for f in model_dir.iterdir(): + if f.is_file(): + shutil.copy2(f, self._backup_dir / f.name) + n_backed = len(list(self._backup_dir.iterdir())) + logger.info("Backed up %d model files", n_backed) + else: + logger.info("Using existing model backup at %s", self._backup_dir) + + # Find the .sim file + # 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: + raise FileNotFoundError(f"No .sim file found in {self.model_dir}") + self.sim_file = sim_files[0] # Already absolute (from resolved parent) + logger.info("SIM file: %s", self.sim_file.name) + logger.info("SIM path: %s", self.sim_file) + + # Find the .prt file (for mass extraction) + prt_files = [f for f in self.model_dir.glob("*.prt") if "_i." not in f.name] + if not prt_files: + raise FileNotFoundError(f"No .prt file found in {self.model_dir}") + self.prt_file = prt_files[0] + logger.info("PRT file: %s", self.prt_file.name) + + # Add Atomizer root to path for imports + atomizer_root = self._find_atomizer_root() + if atomizer_root and str(atomizer_root) not in sys.path: + sys.path.insert(0, str(atomizer_root)) + logger.info("Added Atomizer root to path: %s", atomizer_root) + + # Import optimization_engine components + try: + from optimization_engine.nx.solver import NXSolver + self._nx_solver = NXSolver( + nastran_version=nx_version, + timeout=timeout, + use_journal=True, + study_name="hydrotech_beam_doe", + use_iteration_folders=False, # Direct model: avoid broken NX file references + master_model_dir=model_dir, + ) + logger.info( + "NXSolver initialized (NX %s, timeout=%ds, journal mode)", + nx_version, timeout, + ) + except ImportError as e: + raise ImportError( + f"Could not import optimization_engine. " + f"Ensure Atomizer repo root is on PYTHONPATH.\n" + f"Error: {e}" + ) from e + + # Import extractors + try: + from optimization_engine.extractors.extract_displacement import ( + extract_displacement, + ) + from optimization_engine.extractors.extract_von_mises_stress import ( + extract_solid_stress, + ) + from optimization_engine.extractors.extract_mass_from_expression import ( + extract_mass_from_expression, + ) + self._extract_displacement = extract_displacement + self._extract_stress = extract_solid_stress + self._extract_mass = extract_mass_from_expression + logger.info("Extractors loaded: displacement, von_mises, mass") + except ImportError as e: + raise ImportError( + f"Could not import extractors from optimization_engine.\n" + f"Error: {e}" + ) from e + + def _find_atomizer_root(self) -> Optional[Path]: + """Walk up from model_dir to find the Atomizer repo root.""" + # Look for optimization_engine directory + candidate = self.model_dir + for _ in range(10): + candidate = candidate.parent + if (candidate / "optimization_engine").is_dir(): + return candidate + if candidate == candidate.parent: + break + + # Fallback: common paths + for path in [ + Path("C:/Users/antoi/Atomizer"), + Path("/home/papa/repos/Atomizer"), + ]: + if (path / "optimization_engine").is_dir(): + return path + + logger.warning("Could not find Atomizer root with optimization_engine/") + return None + + def solve(self, trial: TrialInput) -> TrialResult: + """Run a single trial through the NX pipeline. + + Args: + trial: Design variable values. + + Returns: + TrialResult with mass, displacement, stress (or failure info). + """ + self._iteration += 1 + start_time = time.time() + + # Build expression update dict + expressions: Dict[str, float] = { + EXPR_HALF_CORE_THICKNESS: trial.beam_half_core_thickness, + EXPR_FACE_THICKNESS: trial.beam_face_thickness, + EXPR_HOLES_DIAMETER: trial.holes_diameter, + EXPR_HOLE_COUNT: float(trial.hole_count), # NX expects float in .exp + } + + logger.info( + "Trial %d: core=%.2f face=%.2f dia=%.1f count=%d", + self._iteration, + trial.beam_half_core_thickness, + trial.beam_face_thickness, + trial.holes_diameter, + trial.hole_count, + ) + + # Create iteration output folder + iter_dir = self.iterations_dir / f"iter{self._iteration:03d}" + iter_dir.mkdir(parents=True, exist_ok=True) + + try: + # Step 0: Restore master model from backup (clean state) + import shutil + import json + restored = 0 + for bf in self._backup_dir.iterdir(): + if bf.is_file(): + shutil.copy2(bf, self.model_dir / bf.name) + restored += 1 + logger.info("Restored %d model files from backup", restored) + + # Save trial params to iteration folder + 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)) + + # Also write .exp file to iteration folder (import into NX to recreate) + exp_file = iter_dir / "params.exp" + with open(exp_file, "w") as f: + for name, val in expressions.items(): + unit = "Constant" if name in ("hole_count",) else "MilliMeter" + f.write(f'{name}={val} [{unit}]\n') + + # Step 1: Solve on MASTER model (NX internal references intact) + sim_file = self.sim_file + prt_file = self.prt_file + solve_result = self._nx_solver.run_simulation( + sim_file=sim_file, + working_dir=self.model_dir, + expression_updates=expressions, + ) + + if not solve_result.get("success", False): + errors = solve_result.get("errors", ["Unknown solver error"]) + return TrialResult( + success=False, + solve_time=time.time() - start_time, + error_message=f"NX solve failed: {'; '.join(errors)}", + iteration_dir=str(iter_dir), + ) + + op2_file = solve_result.get("op2_file") + if not op2_file or not Path(op2_file).exists(): + return TrialResult( + success=False, + solve_time=time.time() - start_time, + error_message="OP2 file not generated after solve", + iteration_dir=str(iter_dir), + ) + + op2_path = Path(op2_file) + + # Step 3: Extract mass from journal temp file + # The journal writes _temp_mass.txt to working_dir (= model_dir). + # The extractor looks in prt_file.parent (= model_dir). These MUST match. + try: + mass_kg = self._extract_mass(prt_file, expression_name=EXPR_MASS) + except FileNotFoundError: + # Fallback: parse mass from journal stdout captured in solve_result + # The journal prints "[JOURNAL] Mass expression p173 = " or + # "[JOURNAL] MeasureManager mass = " + mass_kg = float("nan") + stdout = solve_result.get("stdout", "") + if stdout: + import re + # Match either extraction method's output + m = re.search( + r'\[JOURNAL\]\s+(?:Mass expression p173|MeasureManager mass)\s*=\s*([0-9.eE+-]+)', + stdout, + ) + if m: + try: + mass_kg = float(m.group(1)) + logger.info("Mass recovered from journal stdout: %.6f kg", mass_kg) + except ValueError: + pass + if mass_kg != mass_kg: # NaN check + logger.warning( + "Mass temp file not found in %s and no mass in journal stdout", + self.model_dir, + ) + except Exception as e: + logger.warning("Mass extraction failed: %s", e) + mass_kg = float("nan") + + # Step 4: Extract displacement from OP2 + try: + disp_result = self._extract_displacement(op2_path) + # For cantilever beam, max displacement IS tip displacement + tip_displacement = disp_result["max_displacement"] + except Exception as e: + logger.warning("Displacement extraction failed: %s", e) + tip_displacement = float("nan") + + # Step 5: Extract max von Mises stress from OP2 + # Use shell element extraction (CQUAD4 mesh) + try: + stress_result = self._extract_stress( + op2_path, + element_type="cquad4", + convert_to_mpa=True, # โš ๏ธ LAC lesson: NX outputs kPa, must convert + ) + max_vm_stress = stress_result["max_von_mises"] + except Exception as e: + logger.warning("Stress extraction failed: %s", e) + max_vm_stress = float("nan") + + # Step 6: Archive solver outputs to iteration folder + # Copy OP2, F06, and other solver outputs from models/ dir + for suffix in (".op2", ".f06", ".log", ".dat"): + for src in self.model_dir.glob(f"*{suffix}"): + try: + shutil.copy2(src, iter_dir / src.name) + except Exception as e: + logger.warning("Could not archive %s: %s", src.name, e) + + # Copy temp files (mass extraction, etc.) + for pattern in ("_temp_*",): + for src in self.model_dir.glob(pattern): + try: + shutil.copy2(src, iter_dir / src.name) + except Exception: + pass + + # Write results summary JSON + results_file = iter_dir / "results.json" + results_file.write_text(json.dumps({ + "iteration": self._iteration, + "mass_kg": mass_kg, + "tip_displacement_mm": tip_displacement, + "max_von_mises_mpa": max_vm_stress, + "feasible": tip_displacement <= 10.0 and max_vm_stress <= 130.0, + "op2_file": op2_path.name if op2_path else None, + }, indent=2)) + + logger.info("Archived iter%03d: results + solver outputs", self._iteration) + + elapsed = time.time() - start_time + logger.info( + "Trial %d complete: mass=%.2f kg, disp=%.3f mm, stress=%.1f MPa (%.1fs)", + self._iteration, mass_kg, tip_displacement, max_vm_stress, elapsed, + ) + + return TrialResult( + success=True, + mass=mass_kg, + tip_displacement=tip_displacement, + max_von_mises=max_vm_stress, + solve_time=elapsed, + iteration_dir=str(iter_dir), + ) + + except Exception as e: + elapsed = time.time() - start_time + logger.error("Trial %d failed: %s", self._iteration, e, exc_info=True) + return TrialResult( + success=False, + solve_time=elapsed, + error_message=str(e), + iteration_dir=str(iter_dir) if 'iter_dir' in locals() else None, + ) + + def close(self) -> None: + """Clean up NX solver resources.""" + logger.info("AtomizerNXSolver closed. %d iterations completed.", self._iteration) + + +# --------------------------------------------------------------------------- +# Stub solver โ€” for development/testing without NX +# --------------------------------------------------------------------------- +class StubSolver: + """Synthetic solver for testing without NX. + + Generates physically-plausible approximate results based on + beam theory. NOT accurate โ€” only for pipeline validation. + """ + + def __init__(self, **kwargs: Any): + self._call_count = 0 + logger.warning( + "Using NX STUB solver โ€” results are synthetic approximations. " + "Replace with AtomizerNXSolver (--backend nxopen) for real evaluations." + ) + + def solve(self, trial: TrialInput) -> TrialResult: + """Generate approximate results from beam theory. + + Uses simplified cantilever beam formulas: + - Mass โˆ cross-section area ร— length - hole_volume + - Displacement ~ PLยณ/3EI (Euler-Bernoulli) + - Stress ~ Mc/I (nominal) with hole SCF + """ + self._call_count += 1 + import numpy as np + + # Geometry (mm) + L = 5000.0 # beam length + h_half = 250.0 # beam half-height (fixed) + w_half = 150.0 # beam half-width (fixed) + h_core = trial.beam_half_core_thickness + t_face = trial.beam_face_thickness + d_hole = trial.holes_diameter + n_hole = trial.hole_count + + # Material: AISI 1005 + E = 205000.0 # MPa (Young's modulus) + rho = 7.3e-6 # kg/mmยณ (7.3 g/cmยณ) + + # I-beam cross-section second moment of area (approximate) + # Full section: 2*w_half ร— 2*h_half rectangle + # Minus core cutouts (simplified) + H = 2 * h_half # 500 mm total height + W = 2 * w_half # 300 mm total width + I_full = W * H**3 / 12 + # Subtract inner rectangle (core region without faces) + h_web = H - 2 * t_face + w_web = W - 2 * h_core # approximate + I_inner = max(0, w_web) * max(0, h_web)**3 / 12 + I_eff = max(I_full - I_inner, I_full * 0.01) # don't go to zero + + # Cross-section area (approximate) + A_section = W * H - max(0, w_web) * max(0, h_web) + # Hole volume removal + web_height = H - 2 * t_face + hole_area = n_hole * np.pi * (d_hole / 2)**2 + # Only remove from web if holes fit + if d_hole < web_height: + effective_hole_area = min(hole_area, 0.8 * web_height * 4000) + else: + effective_hole_area = 0 + # Mass + vol = A_section * L - effective_hole_area * min(h_core * 2, 50) + mass = max(rho * vol, 1.0) + + # Tip displacement: ฮด = PLยณ / 3EI + P = 10000 * 9.80665 # 10,000 kgf โ†’ N + delta = P * L**3 / (3 * E * I_eff) + + # Stress: ฯƒ = M*c/I with SCF from holes + M = P * L # max moment at fixed end + c = h_half # distance to extreme fiber + sigma_nominal = M * c / I_eff / 1000 # kPa โ†’ MPa + # Stress concentration from holes (simplified) + scf = 1.0 + 0.5 * (d_hole / (web_height + 1)) + sigma_max = sigma_nominal * scf + + # Add noise (ยฑ5%) to simulate model variability + rng = np.random.default_rng(self._call_count) + noise = rng.uniform(0.95, 1.05, 3) + + return TrialResult( + success=True, + mass=float(mass * noise[0]), + tip_displacement=float(delta * noise[1]), + max_von_mises=float(sigma_max * noise[2]), + solve_time=0.1, + ) + + def close(self) -> None: + """Clean up stub solver.""" + logger.info("Stub solver closed.") diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/requirements.sync-conflict-20260214-191754-VBCUD7Z.txt b/projects/hydrotech-beam/studies/01_doe_landscape/requirements.sync-conflict-20260214-191754-VBCUD7Z.txt new file mode 100644 index 00000000..962559ff --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/requirements.sync-conflict-20260214-191754-VBCUD7Z.txt @@ -0,0 +1,7 @@ +# Hydrotech Beam โ€” Phase 1 LHS DoE Study +# Python 3.10+ + +optuna>=3.5.0 +scipy>=1.10.0 +numpy>=1.24.0 +pandas>=2.0.0 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/requirements.txt b/projects/hydrotech-beam/studies/01_doe_landscape/requirements.txt index 962559ff..7a3df48e 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/requirements.txt +++ b/projects/hydrotech-beam/studies/01_doe_landscape/requirements.txt @@ -1,7 +1,7 @@ -# Hydrotech Beam โ€” Phase 1 LHS DoE Study -# Python 3.10+ - -optuna>=3.5.0 -scipy>=1.10.0 -numpy>=1.24.0 -pandas>=2.0.0 +# Hydrotech Beam โ€” Phase 1 LHS DoE Study +# Python 3.10+ + +optuna>=3.5.0 +scipy>=1.10.0 +numpy>=1.24.0 +pandas>=2.0.0 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/doe_results.csv b/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/doe_results.csv new file mode 100644 index 00000000..f6e8a68a --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/doe_results.csv @@ -0,0 +1,103 @@ +trial_number,status,beam_half_core_thickness,beam_face_thickness,holes_diameter,hole_count,mass_kg,tip_displacement_mm,max_von_mises_MPa,geo_feasible,displacement_feasible,stress_feasible,fully_feasible,ligament_mm,web_clearance_mm,solve_time_s +0,solved,25.162,21.504,300.0,10,nan,19.556875228881836,117.484125,True,False,True,False,144.44444444444446,156.99200000000002,11.73 +1,solved,29.33562637086642,10.867852359605202,208.82139200571257,6,nan,24.064523696899414,398.4295,True,False,False,False,591.1786079942874,269.442903275077,12.22 +2,geo_infeasible,18.34349359126741,39.31614850983442,289.04224845483384,14,99999.0,99999.0,99999.0,False,,,,18.65005923747384,132.32545452549732, +3,solved,16.654851585575436,25.92976843726266,249.7752118546045,7,nan,18.68077850341797,114.6584609375,True,False,True,False,416.89145481206214,198.3652512708702,12.84 +4,solved,39.4879581560391,37.70634303203751,317.49333407316067,10,nan,12.852874755859375,81.574234375,True,False,True,False,126.95111037128379,107.09397986276429,12.99 +5,solved,18.667249127790498,27.961709646337493,215.2919645872769,12,nan,17.29557228088379,106.283703125,True,False,True,False,148.34439904908672,228.78461612004813,11.23 +6,solved,10.145147355948776,33.3870327520718,197.6501835498656,11,nan,17.468721389770508,108.2625078125,True,False,True,False,202.3498164501344,235.5757509459908,12.88 +7,geo_infeasible,31.736792540733404,28.439814602144864,445.0342129680445,13,99999.0,99999.0,99999.0,False,,,,-111.70087963471121,-1.9138421723342276, +8,solved,36.424864472055134,22.317342276314122,221.080294100253,5,nan,15.069981575012207,94.8715,True,False,True,False,778.919705899747,234.28502134711874,12.95 +9,geo_infeasible,36.013680927951604,24.658755282428263,325.6380323142787,15,99999.0,99999.0,99999.0,False,,,,-39.92374659999297,125.04445712086476, +10,solved,21.20971666430637,27.18728441912208,333.33951480703604,7,nan,18.190187454223633,123.9728203125,True,False,True,False,333.3271518596306,112.2859163547198,12.59 +11,solved,25.10064411916288,15.259636308480797,202.9393633023646,8,nan,21.658220291137695,202.82671875,True,False,False,False,368.48920812620685,266.5413640806738,12.86 +12,solved,23.5377088486766,15.77772417637908,295.1158776920038,12,nan,24.221370697021484,190.267625,True,False,False,False,68.52048594435985,173.32867395523806,12.66 +13,solved,11.932749457620524,35.9698658865046,357.1996739776378,9,nan,19.1060733795166,133.927765625,True,False,False,False,142.80032602236219,70.86059424935297,12.19 +14,solved,12.590502697615015,24.283216775288818,436.6385439056947,8,nan,31.39129066467285,278.0956875,True,False,False,False,134.79002752287676,14.795022543727669,12.08 +15,geo_infeasible,33.724650534677,25.545971283534612,391.769007728242,11,99999.0,99999.0,99999.0,False,,,,8.230992271757998,57.13904970468877, +16,geo_infeasible,24.937958219366468,34.305575803387285,428.6085833611129,15,99999.0,99999.0,99999.0,False,,,,-142.89429764682717,2.7802650321125384, +17,solved,20.01642602550961,12.736672936148768,235.02126154120555,14,nan,27.53583335876465,291.43825,True,False,False,False,72.67104615110213,239.5053925864969,13.28 +18,solved,32.781509299259234,39.647413256693376,172.7124919630828,7,nan,11.77879810333252,75.011625,True,False,True,False,493.9541747035838,247.99268152353045,12.56 +19,solved,38.451363316179766,16.7003930823653,186.1758118536306,12,nan,16.785146713256836,169.51878125,True,False,False,False,177.46055178273303,280.4234019816388,12.98 +20,solved,27.22384374533999,21.60434641918486,399.6751297258189,6,nan,21.478872299194336,185.178296875,True,False,False,False,400.3248702741811,57.11617743581138,13.42 +21,solved,33.00141187580357,12.103605508453288,352.97644218877866,7,nan,24.657197952270508,320.08565625,True,False,False,False,313.69022447788797,122.81634679431477,12.55 +22,solved,30.986372756119685,19.16832222642944,154.19092746511274,15,nan,17.36627769470215,129.0215078125,True,False,True,False,131.523358249173,307.4724280820284,13.28 +23,solved,19.50323693257984,31.89937313493798,382.0105860159447,6,nan,17.949995040893555,127.061125,True,False,True,False,417.9894139840553,54.19066771417937,13.42 +24,solved,13.727845373498717,29.571387264342114,158.4545070511115,6,nan,17.506193161010742,110.5740859375,True,False,True,False,641.5454929488885,282.4027184202042,12.81 +25,solved,39.182912433667966,13.547410048410338,242.6447570355275,10,nan,18.839136123657227,256.434484375,True,False,False,False,201.79968740891695,230.2604228676518,12.81 +26,solved,11.508005001497104,13.78017571971355,280.40478023124285,6,nan,31.85018539428711,248.273609375,True,False,False,False,519.5952197687571,192.03486832933004,12.5 +27,geo_infeasible,14.723131820394673,38.214626589017946,410.6578070859523,11,99999.0,99999.0,99999.0,False,,,,-10.6578070859523,12.912939736011822, +28,geo_infeasible,16.065524505472887,10.058438562073695,323.01235309450794,13,99999.0,99999.0,99999.0,False,,,,10.32098023882537,156.87076978134468, +29,solved,17.419169007999646,17.917141385501917,175.7984093879865,9,nan,22.727807998657227,147.5830625,True,False,False,False,324.2015906120135,288.3673078410097,12.61 +30,geo_infeasible,10.744888282098874,16.028260362958154,389.08612738372096,14,99999.0,99999.0,99999.0,False,,,,-81.39381969141328,78.85735189036274, +31,solved,31.240370325087493,26.41009203906711,273.18266513234516,10,nan,15.669999122619629,93.16090625,True,False,True,False,171.2617793120993,173.99715078952062,12.66 +32,geo_infeasible,20.775031495682867,18.602850091163862,439.30358863649826,10,99999.0,99999.0,99999.0,False,,,,5.1408558079461955,23.490711181173992, +33,solved,26.00450478511711,33.73796956646058,309.2653422609983,5,nan,14.4290132522583,93.7885234375,True,False,True,False,690.7346577390017,123.25871860608055,12.31 +34,solved,22.249541218652357,23.591878117279098,163.6658438964213,10,nan,17.734634399414062,110.49809375,True,False,True,False,280.7786005480232,289.1503998690205,13.06 +35,solved,34.50742630475948,19.91457704426444,301.4331617880579,8,nan,17.536535263061523,118.634125,True,False,True,False,269.99540964051357,158.73768412341326,13.78 +36,geo_infeasible,37.732969901755745,29.990170628054525,366.3730412701501,14,99999.0,99999.0,99999.0,False,,,,-58.68073357784243,73.64661747374083, +37,solved,37.19895822292572,36.450392892917435,405.40744176134933,7,nan,15.368349075317383,122.3722265625,True,False,True,False,261.2592249053173,21.691772452815826,13.09 +38,solved,24.13665312381697,30.60108948604478,257.34854555318736,8,nan,15.375892639160156,94.1200546875,True,False,True,False,314.0800258753841,181.4492754747231,12.28 +39,solved,30.315850546600835,28.803003242099344,286.25449297115927,11,nan,15.49566650390625,93.2840390625,True,False,True,False,113.74550702884073,156.13950054464203,12.64 +40,solved,15.231259729696596,20.51061793783426,341.16121801123404,10,nan,25.575746536254883,154.783734375,True,False,False,False,103.28322643321042,117.81754611309742,12.0 +41,solved,35.135355432426465,17.452468258634863,423.30383098713725,6,nan,23.97252655029297,196.272515625,True,False,False,False,376.69616901286275,41.79123249559302,12.84 +42,solved,35.46708851836511,32.57744662968254,228.6499364024705,13,nan,13.120881080627441,80.2448125,True,False,True,False,104.68339693086281,206.19517033816444,13.05 +43,solved,23.046420945659438,11.556167883135958,418.6187160546307,9,nan,39.49115753173828,351.02815625,True,False,False,False,81.38128394536932,58.26894817909738,12.69 +44,geo_infeasible,21.922047096798718,20.86393201618128,360.37912109679735,13,99999.0,99999.0,99999.0,False,,,,-27.045787763464034,97.89301487084009, +45,solved,26.538941763998174,37.0045746615665,184.02083272600328,13,nan,13.15087890625,82.6500234375,True,False,True,False,149.31250060733004,241.97001795086373,12.36 +46,solved,13.332306234655581,22.63837385797062,260.8868498376813,13,nan,22.770187377929688,132.01121875,True,False,False,False,72.44648349565199,193.8364024463774,11.94 +47,geo_infeasible,27.551926233190585,14.46757831387657,375.7580536267577,12,99999.0,99999.0,99999.0,False,,,,-12.121689990394088,95.30678974548914, +48,solved,28.542165422707903,34.70133239672506,264.8484124805317,11,nan,13.971890449523926,83.8096796875,True,False,True,False,135.1515875194683,165.74892272601818,12.08 +49,solved,15.926345240553108,35.64252369044628,223.5314270645531,8,nan,15.33069133758545,95.525046875,True,False,True,False,347.89714436401835,205.1835255545543,12.02 +50,solved,28.915906359365827,31.075262775497517,346.80055078514937,9,nan,16.25737953186035,106.1287734375,True,False,True,False,153.19944921485063,91.04892366385559,12.5 +51,solved,25.162,21.504,300.0,10,1053.5931383163481,18.28380041850322,0.13775595729301632,True,False,True,False,144.44444444444446,156.99200000000002,0.0 +52,solved,29.33562637086642,10.867852359605202,208.82139200571257,6,1158.9038448682088,21.115164911525884,0.16647270297547148,True,False,True,False,591.1786079942874,269.442903275077,0.0 +53,geo_infeasible,18.34349359126741,39.31614850983442,289.04224845483384,14,99999.0,99999.0,99999.0,False,,,,18.65005923747384,132.32545452549732, +54,solved,16.654851585575436,25.92976843726266,249.7752118546045,7,986.6606944084174,17.253933923718122,0.1434790305424834,True,False,True,False,416.89145481206214,198.3652512708702,0.0 +55,solved,39.4879581560391,37.70634303203751,317.49333407316067,10,1838.7336612796366,11.634133093842687,0.10280057272185458,True,False,True,False,126.95111037128379,107.09397986276429,0.0 +56,solved,18.667249127790498,27.961709646337493,215.2919645872769,12,1131.9533582636498,17.006626297277133,0.12619986517854023,True,False,True,False,148.34439904908672,228.78461612004813,0.02 +57,solved,10.145147355948776,33.3870327520718,197.6501835498656,11,1005.8549056564786,15.954492546161308,0.12076686934112976,True,False,True,False,202.3498164501344,235.5757509459908,0.0 +58,geo_infeasible,31.736792540733404,28.439814602144864,445.0342129680445,13,99999.0,99999.0,99999.0,False,,,,-111.70087963471121,-1.9138421723342276, +59,solved,36.424864472055134,22.317342276314122,221.080294100253,5,1649.900346311668,15.492912326520932,0.11697687846078192,True,False,True,False,778.919705899747,234.28502134711874,0.0 +60,geo_infeasible,36.013680927951604,24.658755282428263,325.6380323142787,15,99999.0,99999.0,99999.0,False,,,,-39.92374659999297,125.04445712086476, +61,solved,21.20971666430637,27.18728441912208,333.33951480703604,7,1077.2315726050838,17.057241224511344,0.13486577542292694,True,False,True,False,333.3271518596306,112.2859163547198,0.0 +62,solved,25.10064411916288,15.259636308480797,202.9393633023646,8,1140.7138450941914,20.091601147242528,0.15506773571378413,True,False,True,False,368.48920812620685,266.5413640806738,0.0 +63,solved,23.5377088486766,15.77772417637908,295.1158776920038,12,907.9548863510008,20.190033302787498,0.1736337594393851,True,False,True,False,68.52048594435985,173.32867395523806,0.0 +64,solved,11.932749457620524,35.9698658865046,357.1996739776378,9,966.2207136678429,15.097978379203779,0.1328475349228414,True,False,True,False,142.80032602236219,70.86059424935297,0.0 +65,solved,12.590502697615015,24.283216775288818,436.6385439056947,8,708.4137130220377,20.45280693959873,0.17296081697620275,True,False,True,False,134.79002752287676,14.795022543727669,0.0 +66,geo_infeasible,33.724650534677,25.545971283534612,391.769007728242,11,99999.0,99999.0,99999.0,False,,,,8.230992271757998,57.13904970468877, +67,geo_infeasible,24.937958219366468,34.305575803387285,428.6085833611129,15,99999.0,99999.0,99999.0,False,,,,-142.89429764682717,2.7802650321125384, +68,solved,20.01642602550961,12.736672936148768,235.02126154120555,14,823.8169009842226,25.476636001599687,0.19456437058746717,True,False,True,False,72.67104615110213,239.5053925864969,0.0 +69,solved,32.781509299259234,39.647413256693376,172.7124919630828,7,1875.2678005786793,11.767495771178126,0.09021206725189611,True,False,True,False,493.9541747035838,247.99268152353045,0.0 +70,solved,38.451363316179766,16.7003930823653,186.1758118536306,12,1586.2173467080522,16.63059961468884,0.1170349965923253,True,False,True,False,177.46055178273303,280.4234019816388,0.0 +71,solved,27.22384374533999,21.60434641918486,399.6751297258189,6,1113.5806195736284,16.85136395784387,0.1438284392074168,True,False,True,False,400.3248702741811,57.11617743581138,0.0 +72,solved,33.00141187580357,12.103605508453288,352.97644218877866,7,1201.3572731032048,18.792432511170524,0.16485730618508931,True,False,True,False,313.69022447788797,122.81634679431477,0.0 +73,solved,30.986372756119685,19.16832222642944,154.19092746511274,15,1348.1251219982225,17.358085066300976,0.11921913498887263,True,False,True,False,131.523358249173,307.4724280820284,0.0 +74,solved,19.50323693257984,31.89937313493798,382.0105860159447,6,1114.8663935493946,15.744819866869829,0.13043320054853486,True,False,True,False,417.9894139840553,54.19066771417937,0.0 +75,solved,13.727845373498717,29.571387264342114,158.4545070511115,6,1042.2595535260446,16.84079291954431,0.11797967398351183,True,False,True,False,641.5454929488885,282.4027184202042,0.0 +76,solved,39.182912433667966,13.547410048410338,242.6447570355275,10,1522.2042275309977,17.192001540743806,0.13416452493302586,True,False,True,False,201.79968740891695,230.2604228676518,0.0 +77,solved,11.508005001497104,13.78017571971355,280.40478023124285,6,627.9153876059761,27.976220416507978,0.22045896619191882,True,False,True,False,519.5952197687571,192.03486832933004,0.0 +78,geo_infeasible,14.723131820394673,38.214626589017946,410.6578070859523,11,99999.0,99999.0,99999.0,False,,,,-10.6578070859523,12.912939736011822, +79,geo_infeasible,16.065524505472887,10.058438562073695,323.01235309450794,13,99999.0,99999.0,99999.0,False,,,,10.32098023882537,156.87076978134468, +80,solved,17.419169007999646,17.917141385501917,175.7984093879865,9,945.0395062689457,22.086636499502408,0.15333378212242593,True,False,True,False,324.2015906120135,288.3673078410097,0.0 +81,geo_infeasible,10.744888282098874,16.028260362958154,389.08612738372096,14,99999.0,99999.0,99999.0,False,,,,-81.39381969141328,78.85735189036274, +82,solved,31.240370325087493,26.41009203906711,273.18266513234516,10,1360.7619251710942,14.570237821053082,0.11891780882533094,True,False,True,False,171.2617793120993,173.99715078952062,0.0 +83,geo_infeasible,20.775031495682867,18.602850091163862,439.30358863649826,10,99999.0,99999.0,99999.0,False,,,,5.1408558079461955,23.490711181173992, +84,solved,26.00450478511711,33.73796956646058,309.2653422609983,5,1374.5674544303447,13.034202085077842,0.1112284328449336,True,False,True,False,690.7346577390017,123.25871860608055,0.0 +85,solved,22.249541218652357,23.591878117279098,163.6658438964213,10,1182.798459195376,16.899432707045637,0.12057071418338186,True,False,True,False,280.7786005480232,289.1503998690205,0.0 +86,solved,34.50742630475948,19.91457704426444,301.4331617880579,8,1414.364395456454,15.657476238183712,0.12525579821509578,True,False,True,False,269.99540964051357,158.73768412341326,0.0 +87,geo_infeasible,37.732969901755745,29.990170628054525,366.3730412701501,14,99999.0,99999.0,99999.0,False,,,,-58.68073357784243,73.64661747374083, +88,solved,37.19895822292572,36.450392892917435,405.40744176134933,7,1685.5388580525696,12.467247592960161,0.111681664976656,True,False,True,False,261.2592249053173,21.691772452815826,0.0 +89,solved,24.13665312381697,30.60108948604478,257.34854555318736,8,1238.330421583267,14.744635475834825,0.11736139594609407,True,False,True,False,314.0800258753841,181.4492754747231,0.0 +90,solved,30.315850546600835,28.803003242099344,286.25449297115927,11,1315.7312352420151,14.157422206852862,0.11126653881147827,True,False,True,False,113.74550702884073,156.13950054464203,0.0 +91,solved,15.231259729696596,20.51061793783426,341.16121801123404,10,786.7203444511985,20.00638783817639,0.17933414769622463,True,False,True,False,103.28322643321042,117.81754611309742,0.0 +92,solved,35.135355432426465,17.452468258634863,423.30383098713725,6,1223.873971953269,16.744357022570288,0.14683922370497737,True,False,True,False,376.69616901286275,41.79123249559302,0.0 +93,solved,35.46708851836511,32.57744662968254,228.6499364024705,13,1635.1990162889988,12.90243378315901,0.10354252793328764,True,False,True,False,104.68339693086281,206.19517033816444,0.0 +94,solved,23.046420945659438,11.556167883135958,418.6187160546307,9,606.9197647211752,24.902033177172868,0.20686007391063263,True,False,True,False,81.38128394536932,58.26894817909738,0.0 +95,geo_infeasible,21.922047096798718,20.86393201618128,360.37912109679735,13,99999.0,99999.0,99999.0,False,,,,-27.045787763464034,97.89301487084009, +96,solved,26.538941763998174,37.0045746615665,184.02083272600328,13,1485.0289033431302,12.936892804021637,0.10133371129659173,True,False,True,False,149.31250060733004,241.97001795086373,0.0 +97,solved,13.332306234655581,22.63837385797062,260.8868498376813,13,777.427810458009,20.064200191814933,0.16666130488128264,True,False,True,False,72.44648349565199,193.8364024463774,0.0 +98,geo_infeasible,27.551926233190585,14.46757831387657,375.7580536267577,12,99999.0,99999.0,99999.0,False,,,,-12.121689990394088,95.30678974548914, +99,solved,28.542165422707903,34.70133239672506,264.8484124805317,11,1465.2653114146049,13.42399062520343,0.10159560191596892,True,False,True,False,135.1515875194683,165.74892272601818,0.0 +100,solved,15.926345240553108,35.64252369044628,223.5314270645531,8,1204.2874622863012,14.244754483676394,0.11571957123078148,True,False,True,False,347.89714436401835,205.1835255545543,0.0 +101,solved,28.915906359365827,31.075262775497517,346.80055078514937,9,1294.707146798082,13.983544771506192,0.11515732934296288,True,False,True,False,153.19944921485063,91.04892366385559,0.0 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/doe_summary.json b/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/doe_summary.json new file mode 100644 index 00000000..54f7af54 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/doe_summary.json @@ -0,0 +1,64 @@ +{ + "study_name": "hydrotech_beam_doe_phase1", + "phase": "Phase 1 \u2014 LHS DoE", + "project": "Hydrotech Beam Structural Optimization", + "timestamp": "2026-02-14T18:38:01.551391+00:00", + "configuration": { + "n_lhs_samples": 50, + "seed": 42, + "baseline_included": true, + "algorithm": "Latin Hypercube Sampling (scipy.stats.qmc)", + "constraint_handling": "Deb's feasibility rules", + "displacement_limit_mm": 10.0, + "stress_limit_MPa": 130.0 + }, + "design_variables": [ + { + "name": "beam_half_core_thickness", + "nx_expression": "beam_half_core_thickness", + "lower": 10.0, + "upper": 40.0, + "baseline": 25.162, + "type": "continuous" + }, + { + "name": "beam_face_thickness", + "nx_expression": "beam_face_thickness", + "lower": 10.0, + "upper": 40.0, + "baseline": 21.504, + "type": "continuous" + }, + { + "name": "holes_diameter", + "nx_expression": "holes_diameter", + "lower": 150.0, + "upper": 450.0, + "baseline": 300.0, + "type": "continuous" + }, + { + "name": "hole_count", + "nx_expression": "hole_count", + "lower": 5.0, + "upper": 15.0, + "baseline": 10.0, + "type": "integer" + } + ], + "results": { + "total_trials": 102, + "solved": 78, + "geo_infeasible": 24, + "solve_failed": 0, + "fully_feasible": 0, + "solve_success_rate": 76.5, + "feasibility_rate": 0.0 + }, + "best_feasible": null, + "phase1_gate_check": { + "min_feasible_5": false, + "solve_success_80pct": false, + "gate_passed": false + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/history.db b/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_before_clean_rerun_20260214_142013/history.db new file mode 100644 index 0000000000000000000000000000000000000000..d90dc27882acfb003c5f83b8ac8f652520f05e62 GIT binary patch literal 45056 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 literal 0 HcmV?d00001 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/doe_results.sync-conflict-20260214-191750-VBCUD7Z.csv b/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/doe_results.sync-conflict-20260214-191750-VBCUD7Z.csv new file mode 100644 index 00000000..f7115ab6 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/doe_results.sync-conflict-20260214-191750-VBCUD7Z.csv @@ -0,0 +1,52 @@ +trial_number,status,beam_half_core_thickness,beam_face_thickness,holes_diameter,hole_count,mass_kg,tip_displacement_mm,max_von_mises_MPa,geo_feasible,displacement_feasible,stress_feasible,fully_feasible,ligament_mm,web_clearance_mm,solve_time_s +0,solved,25.162,21.504,300.0,10,nan,19.556875228881836,117.484125,True,False,True,False,144.44444444444446,156.99200000000002,11.73 +1,solved,29.33562637086642,10.867852359605202,208.82139200571257,6,nan,24.064523696899414,398.4295,True,False,False,False,591.1786079942874,269.442903275077,12.22 +2,geo_infeasible,18.34349359126741,39.31614850983442,289.04224845483384,14,99999.0,99999.0,99999.0,False,,,,18.65005923747384,132.32545452549732, +3,solved,16.654851585575436,25.92976843726266,249.7752118546045,7,nan,18.68077850341797,114.6584609375,True,False,True,False,416.89145481206214,198.3652512708702,12.84 +4,solved,39.4879581560391,37.70634303203751,317.49333407316067,10,nan,12.852874755859375,81.574234375,True,False,True,False,126.95111037128379,107.09397986276429,12.99 +5,solved,18.667249127790498,27.961709646337493,215.2919645872769,12,nan,17.29557228088379,106.283703125,True,False,True,False,148.34439904908672,228.78461612004813,11.23 +6,solved,10.145147355948776,33.3870327520718,197.6501835498656,11,nan,17.468721389770508,108.2625078125,True,False,True,False,202.3498164501344,235.5757509459908,12.88 +7,geo_infeasible,31.736792540733404,28.439814602144864,445.0342129680445,13,99999.0,99999.0,99999.0,False,,,,-111.70087963471121,-1.9138421723342276, +8,solved,36.424864472055134,22.317342276314122,221.080294100253,5,nan,15.069981575012207,94.8715,True,False,True,False,778.919705899747,234.28502134711874,12.95 +9,geo_infeasible,36.013680927951604,24.658755282428263,325.6380323142787,15,99999.0,99999.0,99999.0,False,,,,-39.92374659999297,125.04445712086476, +10,solved,21.20971666430637,27.18728441912208,333.33951480703604,7,nan,18.190187454223633,123.9728203125,True,False,True,False,333.3271518596306,112.2859163547198,12.59 +11,solved,25.10064411916288,15.259636308480797,202.9393633023646,8,nan,21.658220291137695,202.82671875,True,False,False,False,368.48920812620685,266.5413640806738,12.86 +12,solved,23.5377088486766,15.77772417637908,295.1158776920038,12,nan,24.221370697021484,190.267625,True,False,False,False,68.52048594435985,173.32867395523806,12.66 +13,solved,11.932749457620524,35.9698658865046,357.1996739776378,9,nan,19.1060733795166,133.927765625,True,False,False,False,142.80032602236219,70.86059424935297,12.19 +14,solved,12.590502697615015,24.283216775288818,436.6385439056947,8,nan,31.39129066467285,278.0956875,True,False,False,False,134.79002752287676,14.795022543727669,12.08 +15,geo_infeasible,33.724650534677,25.545971283534612,391.769007728242,11,99999.0,99999.0,99999.0,False,,,,8.230992271757998,57.13904970468877, +16,geo_infeasible,24.937958219366468,34.305575803387285,428.6085833611129,15,99999.0,99999.0,99999.0,False,,,,-142.89429764682717,2.7802650321125384, +17,solved,20.01642602550961,12.736672936148768,235.02126154120555,14,nan,27.53583335876465,291.43825,True,False,False,False,72.67104615110213,239.5053925864969,13.28 +18,solved,32.781509299259234,39.647413256693376,172.7124919630828,7,nan,11.77879810333252,75.011625,True,False,True,False,493.9541747035838,247.99268152353045,12.56 +19,solved,38.451363316179766,16.7003930823653,186.1758118536306,12,nan,16.785146713256836,169.51878125,True,False,False,False,177.46055178273303,280.4234019816388,12.98 +20,solved,27.22384374533999,21.60434641918486,399.6751297258189,6,nan,21.478872299194336,185.178296875,True,False,False,False,400.3248702741811,57.11617743581138,13.42 +21,solved,33.00141187580357,12.103605508453288,352.97644218877866,7,nan,24.657197952270508,320.08565625,True,False,False,False,313.69022447788797,122.81634679431477,12.55 +22,solved,30.986372756119685,19.16832222642944,154.19092746511274,15,nan,17.36627769470215,129.0215078125,True,False,True,False,131.523358249173,307.4724280820284,13.28 +23,solved,19.50323693257984,31.89937313493798,382.0105860159447,6,nan,17.949995040893555,127.061125,True,False,True,False,417.9894139840553,54.19066771417937,13.42 +24,solved,13.727845373498717,29.571387264342114,158.4545070511115,6,nan,17.506193161010742,110.5740859375,True,False,True,False,641.5454929488885,282.4027184202042,12.81 +25,solved,39.182912433667966,13.547410048410338,242.6447570355275,10,nan,18.839136123657227,256.434484375,True,False,False,False,201.79968740891695,230.2604228676518,12.81 +26,solved,11.508005001497104,13.78017571971355,280.40478023124285,6,nan,31.85018539428711,248.273609375,True,False,False,False,519.5952197687571,192.03486832933004,12.5 +27,geo_infeasible,14.723131820394673,38.214626589017946,410.6578070859523,11,99999.0,99999.0,99999.0,False,,,,-10.6578070859523,12.912939736011822, +28,geo_infeasible,16.065524505472887,10.058438562073695,323.01235309450794,13,99999.0,99999.0,99999.0,False,,,,10.32098023882537,156.87076978134468, +29,solved,17.419169007999646,17.917141385501917,175.7984093879865,9,nan,22.727807998657227,147.5830625,True,False,False,False,324.2015906120135,288.3673078410097,12.61 +30,geo_infeasible,10.744888282098874,16.028260362958154,389.08612738372096,14,99999.0,99999.0,99999.0,False,,,,-81.39381969141328,78.85735189036274, +31,solved,31.240370325087493,26.41009203906711,273.18266513234516,10,nan,15.669999122619629,93.16090625,True,False,True,False,171.2617793120993,173.99715078952062,12.66 +32,geo_infeasible,20.775031495682867,18.602850091163862,439.30358863649826,10,99999.0,99999.0,99999.0,False,,,,5.1408558079461955,23.490711181173992, +33,solved,26.00450478511711,33.73796956646058,309.2653422609983,5,nan,14.4290132522583,93.7885234375,True,False,True,False,690.7346577390017,123.25871860608055,12.31 +34,solved,22.249541218652357,23.591878117279098,163.6658438964213,10,nan,17.734634399414062,110.49809375,True,False,True,False,280.7786005480232,289.1503998690205,13.06 +35,solved,34.50742630475948,19.91457704426444,301.4331617880579,8,nan,17.536535263061523,118.634125,True,False,True,False,269.99540964051357,158.73768412341326,13.78 +36,geo_infeasible,37.732969901755745,29.990170628054525,366.3730412701501,14,99999.0,99999.0,99999.0,False,,,,-58.68073357784243,73.64661747374083, +37,solved,37.19895822292572,36.450392892917435,405.40744176134933,7,nan,15.368349075317383,122.3722265625,True,False,True,False,261.2592249053173,21.691772452815826,13.09 +38,solved,24.13665312381697,30.60108948604478,257.34854555318736,8,nan,15.375892639160156,94.1200546875,True,False,True,False,314.0800258753841,181.4492754747231,12.28 +39,solved,30.315850546600835,28.803003242099344,286.25449297115927,11,nan,15.49566650390625,93.2840390625,True,False,True,False,113.74550702884073,156.13950054464203,12.64 +40,solved,15.231259729696596,20.51061793783426,341.16121801123404,10,nan,25.575746536254883,154.783734375,True,False,False,False,103.28322643321042,117.81754611309742,12.0 +41,solved,35.135355432426465,17.452468258634863,423.30383098713725,6,nan,23.97252655029297,196.272515625,True,False,False,False,376.69616901286275,41.79123249559302,12.84 +42,solved,35.46708851836511,32.57744662968254,228.6499364024705,13,nan,13.120881080627441,80.2448125,True,False,True,False,104.68339693086281,206.19517033816444,13.05 +43,solved,23.046420945659438,11.556167883135958,418.6187160546307,9,nan,39.49115753173828,351.02815625,True,False,False,False,81.38128394536932,58.26894817909738,12.69 +44,geo_infeasible,21.922047096798718,20.86393201618128,360.37912109679735,13,99999.0,99999.0,99999.0,False,,,,-27.045787763464034,97.89301487084009, +45,solved,26.538941763998174,37.0045746615665,184.02083272600328,13,nan,13.15087890625,82.6500234375,True,False,True,False,149.31250060733004,241.97001795086373,12.36 +46,solved,13.332306234655581,22.63837385797062,260.8868498376813,13,nan,22.770187377929688,132.01121875,True,False,False,False,72.44648349565199,193.8364024463774,11.94 +47,geo_infeasible,27.551926233190585,14.46757831387657,375.7580536267577,12,99999.0,99999.0,99999.0,False,,,,-12.121689990394088,95.30678974548914, +48,solved,28.542165422707903,34.70133239672506,264.8484124805317,11,nan,13.971890449523926,83.8096796875,True,False,True,False,135.1515875194683,165.74892272601818,12.08 +49,solved,15.926345240553108,35.64252369044628,223.5314270645531,8,nan,15.33069133758545,95.525046875,True,False,True,False,347.89714436401835,205.1835255545543,12.02 +50,solved,28.915906359365827,31.075262775497517,346.80055078514937,9,nan,16.25737953186035,106.1287734375,True,False,True,False,153.19944921485063,91.04892366385559,12.5 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/doe_summary.sync-conflict-20260214-191802-VBCUD7Z.json b/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/doe_summary.sync-conflict-20260214-191802-VBCUD7Z.json new file mode 100644 index 00000000..6d2728ef --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/doe_summary.sync-conflict-20260214-191802-VBCUD7Z.json @@ -0,0 +1,64 @@ +{ + "study_name": "hydrotech_beam_doe_phase1", + "phase": "Phase 1 \u2014 LHS DoE", + "project": "Hydrotech Beam Structural Optimization", + "timestamp": "2026-02-11T16:40:12.707007+00:00", + "configuration": { + "n_lhs_samples": 50, + "seed": 42, + "baseline_included": true, + "algorithm": "Latin Hypercube Sampling (scipy.stats.qmc)", + "constraint_handling": "Deb's feasibility rules", + "displacement_limit_mm": 10.0, + "stress_limit_MPa": 130.0 + }, + "design_variables": [ + { + "name": "beam_half_core_thickness", + "nx_expression": "beam_half_core_thickness", + "lower": 10.0, + "upper": 40.0, + "baseline": 25.162, + "type": "continuous" + }, + { + "name": "beam_face_thickness", + "nx_expression": "beam_face_thickness", + "lower": 10.0, + "upper": 40.0, + "baseline": 21.504, + "type": "continuous" + }, + { + "name": "holes_diameter", + "nx_expression": "holes_diameter", + "lower": 150.0, + "upper": 450.0, + "baseline": 300.0, + "type": "continuous" + }, + { + "name": "hole_count", + "nx_expression": "hole_count", + "lower": 5.0, + "upper": 15.0, + "baseline": 10.0, + "type": "integer" + } + ], + "results": { + "total_trials": 51, + "solved": 39, + "geo_infeasible": 12, + "solve_failed": 0, + "fully_feasible": 0, + "solve_success_rate": 76.5, + "feasibility_rate": 0.0 + }, + "best_feasible": null, + "phase1_gate_check": { + "min_feasible_5": false, + "solve_success_80pct": false, + "gate_passed": false + } +} \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/history.sync-conflict-20260214-191804-VBCUD7Z.db b/projects/hydrotech-beam/studies/01_doe_landscape/results/archive_conflicts_20260214_142013/history.sync-conflict-20260214-191804-VBCUD7Z.db new file mode 100644 index 0000000000000000000000000000000000000000..39d22daf9088d20d3bd1a85d7e525d929545f3f8 GIT binary patch literal 32768 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 Q0ubm3^h8f2&FlaF0v5>PH2?qr literal 0 HcmV?d00001 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/results/doe_results.csv b/projects/hydrotech-beam/studies/01_doe_landscape/results/doe_results.csv index f7115ab6..2c90d32c 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/results/doe_results.csv +++ b/projects/hydrotech-beam/studies/01_doe_landscape/results/doe_results.csv @@ -1,52 +1,52 @@ -trial_number,status,beam_half_core_thickness,beam_face_thickness,holes_diameter,hole_count,mass_kg,tip_displacement_mm,max_von_mises_MPa,geo_feasible,displacement_feasible,stress_feasible,fully_feasible,ligament_mm,web_clearance_mm,solve_time_s -0,solved,25.162,21.504,300.0,10,nan,19.556875228881836,117.484125,True,False,True,False,144.44444444444446,156.99200000000002,11.73 -1,solved,29.33562637086642,10.867852359605202,208.82139200571257,6,nan,24.064523696899414,398.4295,True,False,False,False,591.1786079942874,269.442903275077,12.22 -2,geo_infeasible,18.34349359126741,39.31614850983442,289.04224845483384,14,99999.0,99999.0,99999.0,False,,,,18.65005923747384,132.32545452549732, -3,solved,16.654851585575436,25.92976843726266,249.7752118546045,7,nan,18.68077850341797,114.6584609375,True,False,True,False,416.89145481206214,198.3652512708702,12.84 -4,solved,39.4879581560391,37.70634303203751,317.49333407316067,10,nan,12.852874755859375,81.574234375,True,False,True,False,126.95111037128379,107.09397986276429,12.99 -5,solved,18.667249127790498,27.961709646337493,215.2919645872769,12,nan,17.29557228088379,106.283703125,True,False,True,False,148.34439904908672,228.78461612004813,11.23 -6,solved,10.145147355948776,33.3870327520718,197.6501835498656,11,nan,17.468721389770508,108.2625078125,True,False,True,False,202.3498164501344,235.5757509459908,12.88 -7,geo_infeasible,31.736792540733404,28.439814602144864,445.0342129680445,13,99999.0,99999.0,99999.0,False,,,,-111.70087963471121,-1.9138421723342276, -8,solved,36.424864472055134,22.317342276314122,221.080294100253,5,nan,15.069981575012207,94.8715,True,False,True,False,778.919705899747,234.28502134711874,12.95 -9,geo_infeasible,36.013680927951604,24.658755282428263,325.6380323142787,15,99999.0,99999.0,99999.0,False,,,,-39.92374659999297,125.04445712086476, -10,solved,21.20971666430637,27.18728441912208,333.33951480703604,7,nan,18.190187454223633,123.9728203125,True,False,True,False,333.3271518596306,112.2859163547198,12.59 -11,solved,25.10064411916288,15.259636308480797,202.9393633023646,8,nan,21.658220291137695,202.82671875,True,False,False,False,368.48920812620685,266.5413640806738,12.86 -12,solved,23.5377088486766,15.77772417637908,295.1158776920038,12,nan,24.221370697021484,190.267625,True,False,False,False,68.52048594435985,173.32867395523806,12.66 -13,solved,11.932749457620524,35.9698658865046,357.1996739776378,9,nan,19.1060733795166,133.927765625,True,False,False,False,142.80032602236219,70.86059424935297,12.19 -14,solved,12.590502697615015,24.283216775288818,436.6385439056947,8,nan,31.39129066467285,278.0956875,True,False,False,False,134.79002752287676,14.795022543727669,12.08 -15,geo_infeasible,33.724650534677,25.545971283534612,391.769007728242,11,99999.0,99999.0,99999.0,False,,,,8.230992271757998,57.13904970468877, -16,geo_infeasible,24.937958219366468,34.305575803387285,428.6085833611129,15,99999.0,99999.0,99999.0,False,,,,-142.89429764682717,2.7802650321125384, -17,solved,20.01642602550961,12.736672936148768,235.02126154120555,14,nan,27.53583335876465,291.43825,True,False,False,False,72.67104615110213,239.5053925864969,13.28 -18,solved,32.781509299259234,39.647413256693376,172.7124919630828,7,nan,11.77879810333252,75.011625,True,False,True,False,493.9541747035838,247.99268152353045,12.56 -19,solved,38.451363316179766,16.7003930823653,186.1758118536306,12,nan,16.785146713256836,169.51878125,True,False,False,False,177.46055178273303,280.4234019816388,12.98 -20,solved,27.22384374533999,21.60434641918486,399.6751297258189,6,nan,21.478872299194336,185.178296875,True,False,False,False,400.3248702741811,57.11617743581138,13.42 -21,solved,33.00141187580357,12.103605508453288,352.97644218877866,7,nan,24.657197952270508,320.08565625,True,False,False,False,313.69022447788797,122.81634679431477,12.55 -22,solved,30.986372756119685,19.16832222642944,154.19092746511274,15,nan,17.36627769470215,129.0215078125,True,False,True,False,131.523358249173,307.4724280820284,13.28 -23,solved,19.50323693257984,31.89937313493798,382.0105860159447,6,nan,17.949995040893555,127.061125,True,False,True,False,417.9894139840553,54.19066771417937,13.42 -24,solved,13.727845373498717,29.571387264342114,158.4545070511115,6,nan,17.506193161010742,110.5740859375,True,False,True,False,641.5454929488885,282.4027184202042,12.81 -25,solved,39.182912433667966,13.547410048410338,242.6447570355275,10,nan,18.839136123657227,256.434484375,True,False,False,False,201.79968740891695,230.2604228676518,12.81 -26,solved,11.508005001497104,13.78017571971355,280.40478023124285,6,nan,31.85018539428711,248.273609375,True,False,False,False,519.5952197687571,192.03486832933004,12.5 -27,geo_infeasible,14.723131820394673,38.214626589017946,410.6578070859523,11,99999.0,99999.0,99999.0,False,,,,-10.6578070859523,12.912939736011822, -28,geo_infeasible,16.065524505472887,10.058438562073695,323.01235309450794,13,99999.0,99999.0,99999.0,False,,,,10.32098023882537,156.87076978134468, -29,solved,17.419169007999646,17.917141385501917,175.7984093879865,9,nan,22.727807998657227,147.5830625,True,False,False,False,324.2015906120135,288.3673078410097,12.61 -30,geo_infeasible,10.744888282098874,16.028260362958154,389.08612738372096,14,99999.0,99999.0,99999.0,False,,,,-81.39381969141328,78.85735189036274, -31,solved,31.240370325087493,26.41009203906711,273.18266513234516,10,nan,15.669999122619629,93.16090625,True,False,True,False,171.2617793120993,173.99715078952062,12.66 -32,geo_infeasible,20.775031495682867,18.602850091163862,439.30358863649826,10,99999.0,99999.0,99999.0,False,,,,5.1408558079461955,23.490711181173992, -33,solved,26.00450478511711,33.73796956646058,309.2653422609983,5,nan,14.4290132522583,93.7885234375,True,False,True,False,690.7346577390017,123.25871860608055,12.31 -34,solved,22.249541218652357,23.591878117279098,163.6658438964213,10,nan,17.734634399414062,110.49809375,True,False,True,False,280.7786005480232,289.1503998690205,13.06 -35,solved,34.50742630475948,19.91457704426444,301.4331617880579,8,nan,17.536535263061523,118.634125,True,False,True,False,269.99540964051357,158.73768412341326,13.78 -36,geo_infeasible,37.732969901755745,29.990170628054525,366.3730412701501,14,99999.0,99999.0,99999.0,False,,,,-58.68073357784243,73.64661747374083, -37,solved,37.19895822292572,36.450392892917435,405.40744176134933,7,nan,15.368349075317383,122.3722265625,True,False,True,False,261.2592249053173,21.691772452815826,13.09 -38,solved,24.13665312381697,30.60108948604478,257.34854555318736,8,nan,15.375892639160156,94.1200546875,True,False,True,False,314.0800258753841,181.4492754747231,12.28 -39,solved,30.315850546600835,28.803003242099344,286.25449297115927,11,nan,15.49566650390625,93.2840390625,True,False,True,False,113.74550702884073,156.13950054464203,12.64 -40,solved,15.231259729696596,20.51061793783426,341.16121801123404,10,nan,25.575746536254883,154.783734375,True,False,False,False,103.28322643321042,117.81754611309742,12.0 -41,solved,35.135355432426465,17.452468258634863,423.30383098713725,6,nan,23.97252655029297,196.272515625,True,False,False,False,376.69616901286275,41.79123249559302,12.84 -42,solved,35.46708851836511,32.57744662968254,228.6499364024705,13,nan,13.120881080627441,80.2448125,True,False,True,False,104.68339693086281,206.19517033816444,13.05 -43,solved,23.046420945659438,11.556167883135958,418.6187160546307,9,nan,39.49115753173828,351.02815625,True,False,False,False,81.38128394536932,58.26894817909738,12.69 -44,geo_infeasible,21.922047096798718,20.86393201618128,360.37912109679735,13,99999.0,99999.0,99999.0,False,,,,-27.045787763464034,97.89301487084009, -45,solved,26.538941763998174,37.0045746615665,184.02083272600328,13,nan,13.15087890625,82.6500234375,True,False,True,False,149.31250060733004,241.97001795086373,12.36 -46,solved,13.332306234655581,22.63837385797062,260.8868498376813,13,nan,22.770187377929688,132.01121875,True,False,False,False,72.44648349565199,193.8364024463774,11.94 -47,geo_infeasible,27.551926233190585,14.46757831387657,375.7580536267577,12,99999.0,99999.0,99999.0,False,,,,-12.121689990394088,95.30678974548914, -48,solved,28.542165422707903,34.70133239672506,264.8484124805317,11,nan,13.971890449523926,83.8096796875,True,False,True,False,135.1515875194683,165.74892272601818,12.08 -49,solved,15.926345240553108,35.64252369044628,223.5314270645531,8,nan,15.33069133758545,95.525046875,True,False,True,False,347.89714436401835,205.1835255545543,12.02 -50,solved,28.915906359365827,31.075262775497517,346.80055078514937,9,nan,16.25737953186035,106.1287734375,True,False,True,False,153.19944921485063,91.04892366385559,12.5 +trial_number,status,beam_half_core_thickness,beam_face_thickness,holes_diameter,hole_count,mass_kg,tip_displacement_mm,max_von_mises_MPa,geo_feasible,displacement_feasible,stress_feasible,fully_feasible,ligament_mm,web_clearance_mm,solve_time_s +0,solved,25.162,21.504,300.0,10,1133.0042670507721,19.556875228881836,117.484125,True,False,True,False,144.44444444444446,156.99200000000002,59.59 +1,solved,29.33562637086642,10.867852359605202,208.82139200571257,6,1266.2034589148668,24.064523696899414,398.4295,True,False,False,False,591.1786079942874,269.442903275077,13.86 +2,geo_infeasible,18.34349359126741,39.31614850983442,289.04224845483384,14,99999.0,99999.0,99999.0,False,,,,18.65005923747384,132.32545452549732, +3,solved,16.654851585575436,25.92976843726266,249.7752118546045,7,1109.963053537604,18.68077850341797,114.6584609375,True,False,True,False,416.89145481206214,198.3652512708702,13.52 +4,solved,39.4879581560391,37.70634303203751,317.49333407316067,10,1718.102405993698,12.852874755859375,81.574234375,True,False,True,False,126.95111037128379,107.09397986276429,13.23 +5,solved,18.667249127790498,27.961709646337493,215.2919645872769,12,1205.918544016398,17.29557228088379,106.283703125,True,False,True,False,148.34439904908672,228.78461612004813,12.91 +6,solved,10.145147355948776,33.3870327520718,197.6501835498656,11,1085.4467002717115,17.468721389770508,108.2625078125,True,False,True,False,202.3498164501344,235.5757509459908,12.73 +7,geo_infeasible,31.736792540733404,28.439814602144864,445.0342129680445,13,99999.0,99999.0,99999.0,False,,,,-111.70087963471121,-1.9138421723342276, +8,solved,36.424864472055134,22.317342276314122,221.080294100253,5,1722.674056894509,15.069981575012207,94.8715,True,False,True,False,778.919705899747,234.28502134711874,13.02 +9,geo_infeasible,36.013680927951604,24.658755282428263,325.6380323142787,15,99999.0,99999.0,99999.0,False,,,,-39.92374659999297,125.04445712086476, +10,solved,21.20971666430637,27.18728441912208,333.33951480703604,7,1182.0927892029772,18.190187454223633,123.9728203125,True,False,True,False,333.3271518596306,112.2859163547198,12.98 +11,solved,25.10064411916288,15.259636308480797,202.9393633023646,8,1185.7676424555418,21.658220291137695,202.82671875,True,False,False,False,368.48920812620685,266.5413640806738,13.33 +12,solved,23.5377088486766,15.77772417637908,295.1158776920038,12,987.0960407947646,24.221370697021484,190.267625,True,False,False,False,68.52048594435985,173.32867395523806,12.92 +13,solved,11.932749457620524,35.9698658865046,357.1996739776378,9,1082.1233483234585,19.1060733795166,133.927765625,True,False,False,False,142.80032602236219,70.86059424935297,12.84 +14,solved,12.590502697615015,24.283216775288818,436.6385439056947,8,783.4437976533503,31.39129066467285,278.0956875,True,False,False,False,134.79002752287676,14.795022543727669,12.84 +15,geo_infeasible,33.724650534677,25.545971283534612,391.769007728242,11,99999.0,99999.0,99999.0,False,,,,8.230992271757998,57.13904970468877, +16,geo_infeasible,24.937958219366468,34.305575803387285,428.6085833611129,15,99999.0,99999.0,99999.0,False,,,,-142.89429764682717,2.7802650321125384, +17,solved,20.01642602550961,12.736672936148768,235.02126154120555,14,911.7858841605309,27.53583335876465,291.43825,True,False,False,False,72.67104615110213,239.5053925864969,13.16 +18,solved,32.781509299259234,39.647413256693376,172.7124919630828,7,1937.327687952495,11.77879810333252,75.011625,True,False,True,False,493.9541747035838,247.99268152353045,13.36 +19,solved,38.451363316179766,16.7003930823653,186.1758118536306,12,1641.9393126854027,16.785146713256836,169.51878125,True,False,False,False,177.46055178273303,280.4234019816388,13.77 +20,solved,27.22384374533999,21.60434641918486,399.6751297258189,6,1166.4986109547924,21.478872299194336,185.178296875,True,False,False,False,400.3248702741811,57.11617743581138,13.81 +21,solved,33.00141187580357,12.103605508453288,352.97644218877866,7,1165.9879420724315,24.657197952270508,320.08565625,True,False,False,False,313.69022447788797,122.81634679431477,13.44 +22,solved,30.986372756119685,19.16832222642944,154.19092746511274,15,1487.6954250173944,17.36627769470215,129.0215078125,True,False,True,False,131.523358249173,307.4724280820284,13.44 +23,solved,19.50323693257984,31.89937313493798,382.0105860159447,6,1211.87211970739,17.949995040893555,127.061125,True,False,True,False,417.9894139840553,54.19066771417937,12.5 +24,solved,13.727845373498717,29.571387264342114,158.4545070511115,6,1149.2007002062417,17.506193161010742,110.5740859375,True,False,True,False,641.5454929488885,282.4027184202042,13.2 +25,solved,39.182912433667966,13.547410048410338,242.6447570355275,10,1493.3399471409589,18.839136123657227,256.434484375,True,False,False,False,201.79968740891695,230.2604228676518,13.7 +26,solved,11.508005001497104,13.78017571971355,280.40478023124285,6,686.2888916637671,31.85018539428711,248.273609375,True,False,False,False,519.5952197687571,192.03486832933004,13.25 +27,geo_infeasible,14.723131820394673,38.214626589017946,410.6578070859523,11,99999.0,99999.0,99999.0,False,,,,-10.6578070859523,12.912939736011822, +28,geo_infeasible,16.065524505472887,10.058438562073695,323.01235309450794,13,99999.0,99999.0,99999.0,False,,,,10.32098023882537,156.87076978134468, +29,solved,17.419169007999646,17.917141385501917,175.7984093879865,9,999.7016550018517,22.727807998657227,147.5830625,True,False,False,False,324.2015906120135,288.3673078410097,13.28 +30,geo_infeasible,10.744888282098874,16.028260362958154,389.08612738372096,14,99999.0,99999.0,99999.0,False,,,,-81.39381969141328,78.85735189036274, +31,solved,31.240370325087493,26.41009203906711,273.18266513234516,10,1435.135535503974,15.669999122619629,93.16090625,True,False,True,False,171.2617793120993,173.99715078952062,12.94 +32,geo_infeasible,20.775031495682867,18.602850091163862,439.30358863649826,10,99999.0,99999.0,99999.0,False,,,,5.1408558079461955,23.490711181173992, +33,solved,26.00450478511711,33.73796956646058,309.2653422609983,5,1528.3897999240266,14.4290132522583,93.7885234375,True,False,True,False,690.7346577390017,123.25871860608055,12.64 +34,solved,22.249541218652357,23.591878117279098,163.6658438964213,10,1276.5504502481267,17.734634399414062,110.49809375,True,False,True,False,280.7786005480232,289.1503998690205,13.17 +35,solved,34.50742630475948,19.91457704426444,301.4331617880579,8,1410.1593512483248,17.536535263061523,118.634125,True,False,True,False,269.99540964051357,158.73768412341326,12.98 +36,geo_infeasible,37.732969901755745,29.990170628054525,366.3730412701501,14,99999.0,99999.0,99999.0,False,,,,-58.68073357784243,73.64661747374083, +37,solved,37.19895822292572,36.450392892917435,405.40744176134933,7,1582.291153913998,15.368349075317383,122.3722265625,True,False,True,False,261.2592249053173,21.691772452815826,13.69 +38,solved,24.13665312381697,30.60108948604478,257.34854555318736,8,1398.2771475400532,15.375892639160156,94.1200546875,True,False,True,False,314.0800258753841,181.4492754747231,13.11 +39,solved,30.315850546600835,28.803003242099344,286.25449297115927,11,1428.798569319311,15.49566650390625,93.2840390625,True,False,True,False,113.74550702884073,156.13950054464203,12.78 +40,solved,15.231259729696596,20.51061793783426,341.16121801123404,10,815.486611683971,25.575746536254883,154.783734375,True,False,False,False,103.28322643321042,117.81754611309742,12.66 +41,solved,35.135355432426465,17.452468258634863,423.30383098713725,6,1231.4483565590738,23.97252655029297,196.272515625,True,False,False,False,376.69616901286275,41.79123249559302,12.89 +42,solved,35.46708851836511,32.57744662968254,228.6499364024705,13,1754.139697344678,13.120881080627441,80.2448125,True,False,True,False,104.68339693086281,206.19517033816444,13.42 +43,solved,23.046420945659438,11.556167883135958,418.6187160546307,9,688.6297607768505,39.49115753173828,351.02815625,True,False,False,False,81.38128394536932,58.26894817909738,13.09 +44,geo_infeasible,21.922047096798718,20.86393201618128,360.37912109679735,13,99999.0,99999.0,99999.0,False,,,,-27.045787763464034,97.89301487084009, +45,solved,26.538941763998174,37.0045746615665,184.02083272600328,13,1652.7291454725032,13.15087890625,82.6500234375,True,False,True,False,149.31250060733004,241.97001795086373,12.81 +46,solved,13.332306234655581,22.63837385797062,260.8868498376813,13,899.6629740129987,22.770187377929688,132.01121875,True,False,False,False,72.44648349565199,193.8364024463774,13.55 +47,geo_infeasible,27.551926233190585,14.46757831387657,375.7580536267577,12,99999.0,99999.0,99999.0,False,,,,-12.121689990394088,95.30678974548914, +48,solved,28.542165422707903,34.70133239672506,264.8484124805317,11,1539.4261559506735,13.971890449523926,83.8096796875,True,False,True,False,135.1515875194683,165.74892272601818,12.42 +49,solved,15.926345240553108,35.64252369044628,223.5314270645531,8,1300.5025884507997,15.33069133758545,95.525046875,True,False,True,False,347.89714436401835,205.1835255545543,12.99 +50,solved,28.915906359365827,31.075262775497517,346.80055078514937,9,1343.5034206648418,16.25737953186035,106.1287734375,True,False,True,False,153.19944921485063,91.04892366385559,12.72 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/results/doe_summary.json b/projects/hydrotech-beam/studies/01_doe_landscape/results/doe_summary.json index 6d2728ef..1e8ae4dd 100644 --- a/projects/hydrotech-beam/studies/01_doe_landscape/results/doe_summary.json +++ b/projects/hydrotech-beam/studies/01_doe_landscape/results/doe_summary.json @@ -1,64 +1,64 @@ -{ - "study_name": "hydrotech_beam_doe_phase1", - "phase": "Phase 1 \u2014 LHS DoE", - "project": "Hydrotech Beam Structural Optimization", - "timestamp": "2026-02-11T16:40:12.707007+00:00", - "configuration": { - "n_lhs_samples": 50, - "seed": 42, - "baseline_included": true, - "algorithm": "Latin Hypercube Sampling (scipy.stats.qmc)", - "constraint_handling": "Deb's feasibility rules", - "displacement_limit_mm": 10.0, - "stress_limit_MPa": 130.0 - }, - "design_variables": [ - { - "name": "beam_half_core_thickness", - "nx_expression": "beam_half_core_thickness", - "lower": 10.0, - "upper": 40.0, - "baseline": 25.162, - "type": "continuous" - }, - { - "name": "beam_face_thickness", - "nx_expression": "beam_face_thickness", - "lower": 10.0, - "upper": 40.0, - "baseline": 21.504, - "type": "continuous" - }, - { - "name": "holes_diameter", - "nx_expression": "holes_diameter", - "lower": 150.0, - "upper": 450.0, - "baseline": 300.0, - "type": "continuous" - }, - { - "name": "hole_count", - "nx_expression": "hole_count", - "lower": 5.0, - "upper": 15.0, - "baseline": 10.0, - "type": "integer" - } - ], - "results": { - "total_trials": 51, - "solved": 39, - "geo_infeasible": 12, - "solve_failed": 0, - "fully_feasible": 0, - "solve_success_rate": 76.5, - "feasibility_rate": 0.0 - }, - "best_feasible": null, - "phase1_gate_check": { - "min_feasible_5": false, - "solve_success_80pct": false, - "gate_passed": false - } +{ + "study_name": "hydrotech_beam_doe_phase1_real", + "phase": "Phase 1 \u2014 LHS DoE", + "project": "Hydrotech Beam Structural Optimization", + "timestamp": "2026-02-14T22:24:29.123157+00:00", + "configuration": { + "n_lhs_samples": 50, + "seed": 42, + "baseline_included": true, + "algorithm": "Latin Hypercube Sampling (scipy.stats.qmc)", + "constraint_handling": "Deb's feasibility rules", + "displacement_limit_mm": 10.0, + "stress_limit_MPa": 130.0 + }, + "design_variables": [ + { + "name": "beam_half_core_thickness", + "nx_expression": "beam_half_core_thickness", + "lower": 10.0, + "upper": 40.0, + "baseline": 25.162, + "type": "continuous" + }, + { + "name": "beam_face_thickness", + "nx_expression": "beam_face_thickness", + "lower": 10.0, + "upper": 40.0, + "baseline": 21.504, + "type": "continuous" + }, + { + "name": "holes_diameter", + "nx_expression": "holes_diameter", + "lower": 150.0, + "upper": 450.0, + "baseline": 300.0, + "type": "continuous" + }, + { + "name": "hole_count", + "nx_expression": "hole_count", + "lower": 5.0, + "upper": 15.0, + "baseline": 10.0, + "type": "integer" + } + ], + "results": { + "total_trials": 51, + "solved": 39, + "geo_infeasible": 12, + "solve_failed": 0, + "fully_feasible": 0, + "solve_success_rate": 76.5, + "feasibility_rate": 0.0 + }, + "best_feasible": null, + "phase1_gate_check": { + "min_feasible_5": false, + "solve_success_80pct": false, + "gate_passed": false + } } \ No newline at end of file diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/results/history.db b/projects/hydrotech-beam/studies/01_doe_landscape/results/history.db index 39d22daf9088d20d3bd1a85d7e525d929545f3f8..14e96468cc8f3630c6a81af807d4a339755c7931 100644 GIT binary patch literal 45056 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 z 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 diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/run_doe.py b/projects/hydrotech-beam/studies/01_doe_landscape/run_doe.py old mode 100644 new mode 100755 index 0fc53203..ba16f0d3 --- a/projects/hydrotech-beam/studies/01_doe_landscape/run_doe.py +++ b/projects/hydrotech-beam/studies/01_doe_landscape/run_doe.py @@ -1,737 +1,737 @@ -#!/usr/bin/env python3 -"""Phase 1 LHS DoE โ€” Hydrotech Beam Optimization. - -Main entry point for the Design of Experiments landscape mapping study. -Generates 50 LHS sample points + 1 baseline (Trial 0), runs pre-flight -geometric checks, evaluates via NX (or stub), and manages the Optuna study. - -Usage: - # Dry run with stub solver (development/testing): - python run_doe.py --backend stub --study-name hydrotech_doe_dev - - # Real run with NXOpen (on Windows/dalidou): - python run_doe.py --backend nxopen --model-dir /path/to/nx/models - - # Resume interrupted study: - python run_doe.py --backend stub --study-name hydrotech_doe_dev --resume - -References: - OPTIMIZATION_STRATEGY.md โ€” Full strategy document - CONTEXT.md โ€” Confirmed expression names and values -""" - -from __future__ import annotations - -import argparse -import csv -import json -import logging -import os -import sys -import time -from datetime import datetime, timezone -from pathlib import Path -from typing import Any - -import numpy as np -import optuna - -from geometric_checks import ( - DesignPoint, - FeasibilityResult, - check_feasibility, -) -from history import TrialHistory -from nx_interface import TrialInput, TrialResult, create_solver -from sampling import DV_DEFINITIONS, generate_lhs_samples, points_to_dicts - -# --------------------------------------------------------------------------- -# Configuration -# --------------------------------------------------------------------------- -DEFAULT_STUDY_NAME = "hydrotech_beam_doe_phase1" -DEFAULT_N_SAMPLES = 50 -DEFAULT_SEED = 42 -DEFAULT_RESULTS_DIR = "results" -DEFAULT_DB_PATH = "results/optuna_study.db" - -# Constraint limits (hard limits โ€” OPTIMIZATION_STRATEGY.md ยง1.2) -DISPLACEMENT_LIMIT = 10.0 # mm -STRESS_LIMIT = 130.0 # MPa - -# Infeasible placeholder values (for geometric pre-check failures) -INFEASIBLE_MASS = 99999.0 # kg -INFEASIBLE_DISPLACEMENT = 99999.0 # mm -INFEASIBLE_STRESS = 99999.0 # MPa - -logger = logging.getLogger(__name__) - - -# --------------------------------------------------------------------------- -# Optuna constraint callback -# --------------------------------------------------------------------------- -def constraints_func(trial: optuna.trial.FrozenTrial) -> list[float]: - """Compute constraint violations for Deb's feasibility rules. - - Returns list of constraint values where: - value โ‰ค 0 โ†’ feasible (constraint satisfied) - value > 0 โ†’ infeasible (constraint violated) - - This implements Deb's feasibility rules (Deb 2000): - 1. Feasible vs feasible โ†’ lower objective wins - 2. Feasible vs infeasible โ†’ feasible wins - 3. Infeasible vs infeasible โ†’ lower total violation wins - - References: - OPTIMIZATION_STRATEGY.md ยง3.2 - - Args: - trial: Optuna frozen trial with user attributes set. - - Returns: - [displacement_violation, stress_violation] - """ - disp = trial.user_attrs.get("tip_displacement", INFEASIBLE_DISPLACEMENT) - stress = trial.user_attrs.get("max_von_mises", INFEASIBLE_STRESS) - return [ - disp - DISPLACEMENT_LIMIT, # โ‰ค 0 means displacement โ‰ค 10 mm - stress - STRESS_LIMIT, # โ‰ค 0 means stress โ‰ค 130 MPa - ] - - -# --------------------------------------------------------------------------- -# Trial evaluation -# --------------------------------------------------------------------------- -def evaluate_trial( - trial: optuna.Trial, - solver: Any, - history: TrialHistory | None = None, - study_name: str = "", -) -> float: - """Evaluate a single trial: geometric check โ†’ NX solve โ†’ extract. - - Args: - trial: Optuna trial (with parameters already suggested/enqueued). - solver: NX solver instance (stub or real). - history: Persistent trial history logger (append-only). - study_name: Study name for history logging. - - Returns: - Objective value (mass in kg). Returns INFEASIBLE_MASS for - geometrically infeasible or failed evaluations. - """ - # Extract design variables from trial - dv1 = trial.suggest_float( - "beam_half_core_thickness", 10.0, 40.0, - ) - dv2 = trial.suggest_float( - "beam_face_thickness", 10.0, 40.0, - ) - dv3 = trial.suggest_float( - "holes_diameter", 150.0, 450.0, - ) - dv4 = trial.suggest_int( - "hole_count", 5, 15, - ) - - trial_num = trial.number - logger.info( - "Trial %d: DV1=%.2f, DV2=%.2f, DV3=%.1f, DV4=%d", - trial_num, dv1, dv2, dv3, dv4, - ) - - # Store DVs in user attributes for logging - trial.set_user_attr("beam_half_core_thickness", dv1) - trial.set_user_attr("beam_face_thickness", dv2) - trial.set_user_attr("holes_diameter", dv3) - trial.set_user_attr("hole_count", dv4) - - # Pre-flight geometric check - point = DesignPoint( - beam_half_core_thickness=dv1, - beam_face_thickness=dv2, - holes_diameter=dv3, - hole_count=dv4, - ) - geo_result: FeasibilityResult = check_feasibility(point) - trial.set_user_attr("geo_feasible", geo_result.feasible) - trial.set_user_attr("ligament", geo_result.ligament) - trial.set_user_attr("web_clearance", geo_result.web_clearance) - - params = { - "beam_half_core_thickness": dv1, - "beam_face_thickness": dv2, - "holes_diameter": dv3, - "hole_count": dv4, - } - - if not geo_result.feasible: - logger.warning( - "Trial %d: GEOMETRICALLY INFEASIBLE โ€” %s", - trial_num, geo_result.reason, - ) - trial.set_user_attr("status", "geo_infeasible") - trial.set_user_attr("geo_reason", geo_result.reason) - trial.set_user_attr("tip_displacement", INFEASIBLE_DISPLACEMENT) - trial.set_user_attr("max_von_mises", INFEASIBLE_STRESS) - trial.set_user_attr("mass", INFEASIBLE_MASS) - if history: - history.log_trial( - study_name=study_name, trial_id=trial_num, params=params, - geo_feasible=False, status="GEO_INFEASIBLE", - error_message=geo_result.reason, - iteration_number=trial_num + 1, - ) - return INFEASIBLE_MASS - - # NX evaluation - trial_input = TrialInput( - beam_half_core_thickness=dv1, - beam_face_thickness=dv2, - holes_diameter=dv3, - hole_count=dv4, - ) - - t_start = time.monotonic() - nx_result: TrialResult = solver.solve(trial_input) - t_elapsed = time.monotonic() - t_start - - trial.set_user_attr("solve_time_s", round(t_elapsed, 2)) - - if not nx_result.success: - logger.error( - "Trial %d: NX SOLVE FAILED โ€” %s (%.1fs)", - trial_num, nx_result.error_message, t_elapsed, - ) - trial.set_user_attr("status", "solve_failed") - trial.set_user_attr("error_message", nx_result.error_message) - trial.set_user_attr("tip_displacement", INFEASIBLE_DISPLACEMENT) - trial.set_user_attr("max_von_mises", INFEASIBLE_STRESS) - trial.set_user_attr("mass", INFEASIBLE_MASS) - if history: - history.log_trial( - study_name=study_name, trial_id=trial_num, params=params, - status="FAILED", error_message=nx_result.error_message, - solve_time_s=round(t_elapsed, 2), - iter_path=nx_result.iteration_dir, - iteration_number=trial_num + 1, - ) - return INFEASIBLE_MASS - - # Record successful results - trial.set_user_attr("status", "solved") - trial.set_user_attr("mass", nx_result.mass) - trial.set_user_attr("tip_displacement", nx_result.tip_displacement) - trial.set_user_attr("max_von_mises", nx_result.max_von_mises) - - # Check constraint feasibility - disp_ok = nx_result.tip_displacement <= DISPLACEMENT_LIMIT - stress_ok = nx_result.max_von_mises <= STRESS_LIMIT - trial.set_user_attr("displacement_feasible", disp_ok) - trial.set_user_attr("stress_feasible", stress_ok) - trial.set_user_attr("fully_feasible", disp_ok and stress_ok) - - logger.info( - "Trial %d: mass=%.2f kg, disp=%.2f mm%s, stress=%.1f MPa%s (%.1fs)", - trial_num, - nx_result.mass, - nx_result.tip_displacement, - " โœ“" if disp_ok else " โœ—", - nx_result.max_von_mises, - " โœ“" if stress_ok else " โœ—", - t_elapsed, - ) - - if history: - history.log_trial( - study_name=study_name, trial_id=trial_num, params=params, - mass_kg=nx_result.mass, - tip_displacement_mm=nx_result.tip_displacement, - max_von_mises_mpa=nx_result.max_von_mises, - geo_feasible=True, - status="COMPLETE", - solve_time_s=round(t_elapsed, 2), - iter_path=nx_result.iteration_dir, - iteration_number=trial_num + 1, - ) - history.export_csv() # Live update CSV after each trial - - # Optuna rejects nan โ€” use INFEASIBLE_MASS as fallback - import math - if math.isnan(nx_result.mass): - logger.warning("Trial %d: mass is NaN (extraction failed), using penalty value", trial_num) - return INFEASIBLE_MASS - - return nx_result.mass - - -# --------------------------------------------------------------------------- -# Results export -# --------------------------------------------------------------------------- -def export_csv(study: optuna.Study, output_path: str) -> None: - """Export all trial results to CSV. - - Columns: trial_number, status, beam_half_core_thickness, beam_face_thickness, - holes_diameter, hole_count, mass, tip_displacement, max_von_mises, - geo_feasible, displacement_feasible, stress_feasible, fully_feasible, - ligament, web_clearance, solve_time_s - - Args: - study: Completed Optuna study. - output_path: Path to output CSV file. - """ - fieldnames = [ - "trial_number", - "status", - "beam_half_core_thickness", - "beam_face_thickness", - "holes_diameter", - "hole_count", - "mass_kg", - "tip_displacement_mm", - "max_von_mises_MPa", - "geo_feasible", - "displacement_feasible", - "stress_feasible", - "fully_feasible", - "ligament_mm", - "web_clearance_mm", - "solve_time_s", - ] - - with open(output_path, "w", newline="") as f: - writer = csv.DictWriter(f, fieldnames=fieldnames) - writer.writeheader() - - for trial in study.trials: - ua = trial.user_attrs - writer.writerow({ - "trial_number": trial.number, - "status": ua.get("status", "unknown"), - "beam_half_core_thickness": ua.get("beam_half_core_thickness", ""), - "beam_face_thickness": ua.get("beam_face_thickness", ""), - "holes_diameter": ua.get("holes_diameter", ""), - "hole_count": ua.get("hole_count", ""), - "mass_kg": ua.get("mass", ""), - "tip_displacement_mm": ua.get("tip_displacement", ""), - "max_von_mises_MPa": ua.get("max_von_mises", ""), - "geo_feasible": ua.get("geo_feasible", ""), - "displacement_feasible": ua.get("displacement_feasible", ""), - "stress_feasible": ua.get("stress_feasible", ""), - "fully_feasible": ua.get("fully_feasible", ""), - "ligament_mm": ua.get("ligament", ""), - "web_clearance_mm": ua.get("web_clearance", ""), - "solve_time_s": ua.get("solve_time_s", ""), - }) - - logger.info("CSV results exported to %s (%d trials)", output_path, len(study.trials)) - - -def export_summary(study: optuna.Study, output_path: str) -> None: - """Export study summary as JSON. - - Includes metadata, statistics, best feasible design, and - constraint satisfaction rates. - - Args: - study: Completed Optuna study. - output_path: Path to output JSON file. - """ - trials = study.trials - n_total = len(trials) - - # Count by status - n_solved = sum(1 for t in trials if t.user_attrs.get("status") == "solved") - n_geo_infeasible = sum( - 1 for t in trials if t.user_attrs.get("status") == "geo_infeasible" - ) - n_failed = sum( - 1 for t in trials if t.user_attrs.get("status") == "solve_failed" - ) - n_feasible = sum( - 1 for t in trials if t.user_attrs.get("fully_feasible", False) - ) - - # Best feasible trial - best_feasible = None - best_mass = float("inf") - for t in trials: - if t.user_attrs.get("fully_feasible", False): - mass = t.user_attrs.get("mass", float("inf")) - if mass < best_mass: - best_mass = mass - best_feasible = t - - best_info = None - if best_feasible is not None: - ua = best_feasible.user_attrs - best_info = { - "trial_number": best_feasible.number, - "mass_kg": ua.get("mass"), - "tip_displacement_mm": ua.get("tip_displacement"), - "max_von_mises_MPa": ua.get("max_von_mises"), - "design_variables": { - "beam_half_core_thickness": ua.get("beam_half_core_thickness"), - "beam_face_thickness": ua.get("beam_face_thickness"), - "holes_diameter": ua.get("holes_diameter"), - "hole_count": ua.get("hole_count"), - }, - } - - summary = { - "study_name": study.study_name, - "phase": "Phase 1 โ€” LHS DoE", - "project": "Hydrotech Beam Structural Optimization", - "timestamp": datetime.now(timezone.utc).isoformat(), - "configuration": { - "n_lhs_samples": DEFAULT_N_SAMPLES, - "seed": DEFAULT_SEED, - "baseline_included": True, - "algorithm": "Latin Hypercube Sampling (scipy.stats.qmc)", - "constraint_handling": "Deb's feasibility rules", - "displacement_limit_mm": DISPLACEMENT_LIMIT, - "stress_limit_MPa": STRESS_LIMIT, - }, - "design_variables": [ - { - "name": dv.name, - "nx_expression": dv.nx_expression, - "lower": dv.lower, - "upper": dv.upper, - "baseline": dv.baseline, - "type": "integer" if dv.is_integer else "continuous", - } - for dv in DV_DEFINITIONS - ], - "results": { - "total_trials": n_total, - "solved": n_solved, - "geo_infeasible": n_geo_infeasible, - "solve_failed": n_failed, - "fully_feasible": n_feasible, - "solve_success_rate": round(n_solved / max(n_total, 1) * 100, 1), - "feasibility_rate": round(n_feasible / max(n_solved, 1) * 100, 1), - }, - "best_feasible": best_info, - "phase1_gate_check": { - "min_feasible_5": n_feasible >= 5, - "solve_success_80pct": (n_solved / max(n_total, 1)) >= 0.80, - "gate_passed": n_feasible >= 5 and (n_solved / max(n_total, 1)) >= 0.80, - }, - } - - with open(output_path, "w") as f: - json.dump(summary, f, indent=2) - - logger.info("Summary exported to %s", output_path) - - -# --------------------------------------------------------------------------- -# Main study runner -# --------------------------------------------------------------------------- -def run_study(args: argparse.Namespace) -> None: - """Execute the Phase 1 LHS DoE study. - - Steps: - 1. Generate LHS sample points + baseline - 2. Create/load Optuna study with SQLite storage - 3. Enqueue all trials (baseline first, then LHS) - 4. Run optimization (all trials evaluated via objective fn) - 5. Export results to CSV and JSON - - Args: - args: Parsed command-line arguments. - """ - results_dir = Path(args.results_dir) - results_dir.mkdir(parents=True, exist_ok=True) - - # ----------------------------------------------------------------------- - # 1. Generate sample points - # ----------------------------------------------------------------------- - logger.info("=" * 70) - logger.info("HYDROTECH BEAM โ€” Phase 1 LHS DoE") - logger.info("=" * 70) - - points = generate_lhs_samples( - n_samples=args.n_samples, - seed=args.seed, - include_baseline=True, - ) - n_trials = len(points) - logger.info("Generated %d trial points (1 baseline + %d LHS)", n_trials, n_trials - 1) - - # ----------------------------------------------------------------------- - # 2. Create Optuna study - # ----------------------------------------------------------------------- - db_path = results_dir / "optuna_study.db" - storage = f"sqlite:///{db_path}" - - if args.clean and db_path.exists(): - logger.info("--clean flag: deleting Optuna DB at %s (history.db preserved)", db_path) - db_path.unlink() - - if args.resume: - logger.info("Resuming existing study: %s", args.study_name) - study = optuna.load_study( - study_name=args.study_name, - storage=storage, - ) - logger.info("Loaded study with %d existing trials", len(study.trials)) - else: - study = optuna.create_study( - study_name=args.study_name, - storage=storage, - direction="minimize", # minimize mass - load_if_exists=True, # safe re-run: reuse if exists - sampler=optuna.samplers.TPESampler(seed=args.seed), - ) - logger.info("Created new study: %s (storage: %s)", args.study_name, db_path) - - # Enqueue all trial points - trial_dicts = points_to_dicts(points) - for i, td in enumerate(trial_dicts): - study.enqueue_trial(td) - if i == 0: - logger.info("Enqueued Trial 0 (baseline): %s", td) - - logger.info("Enqueued %d trials (1 baseline + %d LHS)", n_trials, n_trials - 1) - - # ----------------------------------------------------------------------- - # 3. Create solver + history - # ----------------------------------------------------------------------- - solver = create_solver( - backend=args.backend, - model_dir=args.model_dir, - ) - - # Persistent history โ€” NEVER deleted by --clean - history = TrialHistory(results_dir) - study_name = args.study_name - - # ----------------------------------------------------------------------- - # 4. Run all trials - # ----------------------------------------------------------------------- - logger.info("-" * 70) - logger.info("Starting trial evaluations...") - logger.info("-" * 70) - - t_study_start = time.monotonic() - - # Suppress Optuna's verbose logging during trials - optuna.logging.set_verbosity(optuna.logging.WARNING) - - study.optimize( - lambda trial: evaluate_trial(trial, solver, history, study_name), - n_trials=n_trials, - callbacks=[_progress_callback], - ) - - t_study_elapsed = time.monotonic() - t_study_start - - # ----------------------------------------------------------------------- - # 5. Export results - # ----------------------------------------------------------------------- - logger.info("-" * 70) - logger.info("Study complete. Exporting results...") - logger.info("-" * 70) - - csv_path = str(results_dir / "doe_results.csv") - json_path = str(results_dir / "doe_summary.json") - - export_csv(study, csv_path) - export_summary(study, json_path) - - # ----------------------------------------------------------------------- - # 6. Print summary - # ----------------------------------------------------------------------- - _print_summary(study, t_study_elapsed) - - # Cleanup - solver.close() - - # Final history summary (before close!) - hist_summary = history.get_study_summary(study_name) - logger.info("History DB: %d total records across all studies", hist_summary["total"]) - history.close() - - -def _progress_callback(study: optuna.Study, trial: optuna.trial.FrozenTrial) -> None: - """Log progress after each trial.""" - n_complete = len(study.trials) - status = trial.user_attrs.get("status", "unknown") - mass = trial.user_attrs.get("mass", "N/A") - feasible = trial.user_attrs.get("fully_feasible", False) - logger.info( - " [%d/%d] Trial %d: status=%s, mass=%s, feasible=%s", - n_complete, - DEFAULT_N_SAMPLES + 1, - trial.number, - status, - f"{mass:.2f} kg" if isinstance(mass, (int, float)) else mass, - feasible, - ) - - -def _print_summary(study: optuna.Study, elapsed: float) -> None: - """Print a human-readable summary to stdout.""" - trials = study.trials - n_total = len(trials) - n_solved = sum(1 for t in trials if t.user_attrs.get("status") == "solved") - n_geo_inf = sum(1 for t in trials if t.user_attrs.get("status") == "geo_infeasible") - n_failed = sum(1 for t in trials if t.user_attrs.get("status") == "solve_failed") - n_feasible = sum(1 for t in trials if t.user_attrs.get("fully_feasible", False)) - - print("\n" + "=" * 70) - print("PHASE 1 DoE โ€” RESULTS SUMMARY") - print("=" * 70) - print(f" Total trials: {n_total}") - print(f" Solved: {n_solved}") - print(f" Geometrically infeasible: {n_geo_inf}") - print(f" Solve failures: {n_failed}") - print(f" Fully feasible: {n_feasible}") - print(f" Solve success rate: {n_solved/max(n_total,1)*100:.1f}%") - print(f" Feasibility rate: {n_feasible/max(n_solved,1)*100:.1f}%") - print(f" Total time: {elapsed:.1f}s ({elapsed/60:.1f} min)") - - # Phase 1 โ†’ Phase 2 gate check - print("\n GATE CHECK (Phase 1 โ†’ Phase 2):") - gate_feasible = n_feasible >= 5 - gate_solve = (n_solved / max(n_total, 1)) >= 0.80 - print(f" โ‰ฅ5 feasible points: {'โœ“ PASS' if gate_feasible else 'โœ— FAIL'} ({n_feasible})") - print(f" โ‰ฅ80% solve success: {'โœ“ PASS' if gate_solve else 'โœ— FAIL'} ({n_solved/max(n_total,1)*100:.0f}%)") - print(f" GATE: {'โœ“ PASSED' if gate_feasible and gate_solve else 'โœ— BLOCKED'}") - - # Best feasible - best_mass = float("inf") - best_trial = None - for t in trials: - if t.user_attrs.get("fully_feasible", False): - m = t.user_attrs.get("mass", float("inf")) - if m < best_mass: - best_mass = m - best_trial = t - - if best_trial is not None: - ua = best_trial.user_attrs - print(f"\n BEST FEASIBLE DESIGN (Trial {best_trial.number}):") - print(f" Mass: {ua['mass']:.2f} kg") - print(f" Displacement: {ua['tip_displacement']:.2f} mm (limit: {DISPLACEMENT_LIMIT} mm)") - print(f" VM Stress: {ua['max_von_mises']:.1f} MPa (limit: {STRESS_LIMIT} MPa)") - print(f" Core thickness: {ua['beam_half_core_thickness']:.3f} mm") - print(f" Face thickness: {ua['beam_face_thickness']:.3f} mm") - print(f" Hole diameter: {ua['holes_diameter']:.1f} mm") - print(f" Hole count: {ua['hole_count']}") - else: - print("\n โš ๏ธ NO FEASIBLE DESIGN FOUND โ€” see OPTIMIZATION_STRATEGY.md ยง7.1") - - print("=" * 70) - - -# --------------------------------------------------------------------------- -# CLI -# --------------------------------------------------------------------------- -def parse_args() -> argparse.Namespace: - """Parse command-line arguments.""" - parser = argparse.ArgumentParser( - description="Hydrotech Beam โ€” Phase 1 LHS DoE Study", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=( - "Examples:\n" - " # Development (stub solver):\n" - " python run_doe.py --backend stub\n\n" - " # Real NX evaluation:\n" - " python run_doe.py --backend nxopen --model-dir /path/to/models\n\n" - " # Resume interrupted study:\n" - " python run_doe.py --backend stub --resume\n" - ), - ) - parser.add_argument( - "--backend", - choices=["stub", "nxopen"], - default="stub", - help="NX solver backend: 'stub' for testing, 'nxopen' for real (default: stub)", - ) - parser.add_argument( - "--model-dir", - default="", - help="Path to NX model directory (required for --backend nxopen)", - ) - parser.add_argument( - "--study-name", - default=DEFAULT_STUDY_NAME, - help=f"Optuna study name (default: {DEFAULT_STUDY_NAME})", - ) - parser.add_argument( - "--n-samples", - type=int, - default=DEFAULT_N_SAMPLES, - help=f"Number of LHS sample points (default: {DEFAULT_N_SAMPLES})", - ) - parser.add_argument( - "--seed", - type=int, - default=DEFAULT_SEED, - help=f"Random seed for LHS (default: {DEFAULT_SEED})", - ) - parser.add_argument( - "--results-dir", - default=DEFAULT_RESULTS_DIR, - help=f"Output directory for results (default: {DEFAULT_RESULTS_DIR})", - ) - parser.add_argument( - "--resume", - action="store_true", - help="Resume an existing study instead of creating a new one", - ) - parser.add_argument( - "--clean", - action="store_true", - help="Delete existing results DB before starting (fresh run)", - ) - parser.add_argument( - "--verbose", "-v", - action="store_true", - help="Enable verbose (DEBUG) logging", - ) - - return parser.parse_args() - - -def main() -> None: - """Entry point.""" - args = parse_args() - - # Configure logging โ€” console + file - log_level = logging.DEBUG if args.verbose else logging.INFO - log_format = "%(asctime)s [%(levelname)-7s] %(name)s: %(message)s" - log_datefmt = "%Y-%m-%d %H:%M:%S" - - # Ensure results dir exists for log file - results_dir = Path(args.results_dir) - results_dir.mkdir(parents=True, exist_ok=True) - log_file = results_dir / "doe_run.log" - - logging.basicConfig( - level=log_level, - format=log_format, - datefmt=log_datefmt, - handlers=[ - logging.StreamHandler(), # console - logging.FileHandler(log_file, mode="a", encoding="utf-8"), # file - ], - ) - logger.info("Log file: %s", log_file.resolve()) - - # Run - try: - run_study(args) - except KeyboardInterrupt: - logger.warning("Study interrupted by user. Progress saved to Optuna DB.") - logger.info("Resume with: python run_doe.py --resume --study-name %s", args.study_name) - sys.exit(1) - except Exception: - logger.exception("Study failed with unexpected error") - sys.exit(2) - - -if __name__ == "__main__": - main() +#!/usr/bin/env python3 +"""Phase 1 LHS DoE โ€” Hydrotech Beam Optimization. + +Main entry point for the Design of Experiments landscape mapping study. +Generates 50 LHS sample points + 1 baseline (Trial 0), runs pre-flight +geometric checks, evaluates via NX (or stub), and manages the Optuna study. + +Usage: + # Dry run with stub solver (development/testing): + python run_doe.py --backend stub --study-name hydrotech_doe_dev + + # Real run with NXOpen (on Windows/dalidou): + python run_doe.py --backend nxopen --model-dir /path/to/nx/models + + # Resume interrupted study: + python run_doe.py --backend stub --study-name hydrotech_doe_dev --resume + +References: + OPTIMIZATION_STRATEGY.md โ€” Full strategy document + CONTEXT.md โ€” Confirmed expression names and values +""" + +from __future__ import annotations + +import argparse +import csv +import json +import logging +import os +import sys +import time +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + +import numpy as np +import optuna + +from geometric_checks import ( + DesignPoint, + FeasibilityResult, + check_feasibility, +) +from history import TrialHistory +from nx_interface import TrialInput, TrialResult, create_solver +from sampling import DV_DEFINITIONS, generate_lhs_samples, points_to_dicts + +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- +DEFAULT_STUDY_NAME = "hydrotech_beam_doe_phase1" +DEFAULT_N_SAMPLES = 50 +DEFAULT_SEED = 42 +DEFAULT_RESULTS_DIR = "results" +DEFAULT_DB_PATH = "results/optuna_study.db" + +# Constraint limits (hard limits โ€” OPTIMIZATION_STRATEGY.md ยง1.2) +DISPLACEMENT_LIMIT = 10.0 # mm +STRESS_LIMIT = 130.0 # MPa + +# Infeasible placeholder values (for geometric pre-check failures) +INFEASIBLE_MASS = 99999.0 # kg +INFEASIBLE_DISPLACEMENT = 99999.0 # mm +INFEASIBLE_STRESS = 99999.0 # MPa + +logger = logging.getLogger(__name__) + + +# --------------------------------------------------------------------------- +# Optuna constraint callback +# --------------------------------------------------------------------------- +def constraints_func(trial: optuna.trial.FrozenTrial) -> list[float]: + """Compute constraint violations for Deb's feasibility rules. + + Returns list of constraint values where: + value โ‰ค 0 โ†’ feasible (constraint satisfied) + value > 0 โ†’ infeasible (constraint violated) + + This implements Deb's feasibility rules (Deb 2000): + 1. Feasible vs feasible โ†’ lower objective wins + 2. Feasible vs infeasible โ†’ feasible wins + 3. Infeasible vs infeasible โ†’ lower total violation wins + + References: + OPTIMIZATION_STRATEGY.md ยง3.2 + + Args: + trial: Optuna frozen trial with user attributes set. + + Returns: + [displacement_violation, stress_violation] + """ + disp = trial.user_attrs.get("tip_displacement", INFEASIBLE_DISPLACEMENT) + stress = trial.user_attrs.get("max_von_mises", INFEASIBLE_STRESS) + return [ + disp - DISPLACEMENT_LIMIT, # โ‰ค 0 means displacement โ‰ค 10 mm + stress - STRESS_LIMIT, # โ‰ค 0 means stress โ‰ค 130 MPa + ] + + +# --------------------------------------------------------------------------- +# Trial evaluation +# --------------------------------------------------------------------------- +def evaluate_trial( + trial: optuna.Trial, + solver: Any, + history: TrialHistory | None = None, + study_name: str = "", +) -> float: + """Evaluate a single trial: geometric check โ†’ NX solve โ†’ extract. + + Args: + trial: Optuna trial (with parameters already suggested/enqueued). + solver: NX solver instance (stub or real). + history: Persistent trial history logger (append-only). + study_name: Study name for history logging. + + Returns: + Objective value (mass in kg). Returns INFEASIBLE_MASS for + geometrically infeasible or failed evaluations. + """ + # Extract design variables from trial + dv1 = trial.suggest_float( + "beam_half_core_thickness", 10.0, 40.0, + ) + dv2 = trial.suggest_float( + "beam_face_thickness", 10.0, 40.0, + ) + dv3 = trial.suggest_float( + "holes_diameter", 150.0, 450.0, + ) + dv4 = trial.suggest_int( + "hole_count", 5, 15, + ) + + trial_num = trial.number + logger.info( + "Trial %d: DV1=%.2f, DV2=%.2f, DV3=%.1f, DV4=%d", + trial_num, dv1, dv2, dv3, dv4, + ) + + # Store DVs in user attributes for logging + trial.set_user_attr("beam_half_core_thickness", dv1) + trial.set_user_attr("beam_face_thickness", dv2) + trial.set_user_attr("holes_diameter", dv3) + trial.set_user_attr("hole_count", dv4) + + # Pre-flight geometric check + point = DesignPoint( + beam_half_core_thickness=dv1, + beam_face_thickness=dv2, + holes_diameter=dv3, + hole_count=dv4, + ) + geo_result: FeasibilityResult = check_feasibility(point) + trial.set_user_attr("geo_feasible", geo_result.feasible) + trial.set_user_attr("ligament", geo_result.ligament) + trial.set_user_attr("web_clearance", geo_result.web_clearance) + + params = { + "beam_half_core_thickness": dv1, + "beam_face_thickness": dv2, + "holes_diameter": dv3, + "hole_count": dv4, + } + + if not geo_result.feasible: + logger.warning( + "Trial %d: GEOMETRICALLY INFEASIBLE โ€” %s", + trial_num, geo_result.reason, + ) + trial.set_user_attr("status", "geo_infeasible") + trial.set_user_attr("geo_reason", geo_result.reason) + trial.set_user_attr("tip_displacement", INFEASIBLE_DISPLACEMENT) + trial.set_user_attr("max_von_mises", INFEASIBLE_STRESS) + trial.set_user_attr("mass", INFEASIBLE_MASS) + if history: + history.log_trial( + study_name=study_name, trial_id=trial_num, params=params, + geo_feasible=False, status="GEO_INFEASIBLE", + error_message=geo_result.reason, + iteration_number=trial_num + 1, + ) + return INFEASIBLE_MASS + + # NX evaluation + trial_input = TrialInput( + beam_half_core_thickness=dv1, + beam_face_thickness=dv2, + holes_diameter=dv3, + hole_count=dv4, + ) + + t_start = time.monotonic() + nx_result: TrialResult = solver.solve(trial_input) + t_elapsed = time.monotonic() - t_start + + trial.set_user_attr("solve_time_s", round(t_elapsed, 2)) + + if not nx_result.success: + logger.error( + "Trial %d: NX SOLVE FAILED โ€” %s (%.1fs)", + trial_num, nx_result.error_message, t_elapsed, + ) + trial.set_user_attr("status", "solve_failed") + trial.set_user_attr("error_message", nx_result.error_message) + trial.set_user_attr("tip_displacement", INFEASIBLE_DISPLACEMENT) + trial.set_user_attr("max_von_mises", INFEASIBLE_STRESS) + trial.set_user_attr("mass", INFEASIBLE_MASS) + if history: + history.log_trial( + study_name=study_name, trial_id=trial_num, params=params, + status="FAILED", error_message=nx_result.error_message, + solve_time_s=round(t_elapsed, 2), + iter_path=nx_result.iteration_dir, + iteration_number=trial_num + 1, + ) + return INFEASIBLE_MASS + + # Record successful results + trial.set_user_attr("status", "solved") + trial.set_user_attr("mass", nx_result.mass) + trial.set_user_attr("tip_displacement", nx_result.tip_displacement) + trial.set_user_attr("max_von_mises", nx_result.max_von_mises) + + # Check constraint feasibility + disp_ok = nx_result.tip_displacement <= DISPLACEMENT_LIMIT + stress_ok = nx_result.max_von_mises <= STRESS_LIMIT + trial.set_user_attr("displacement_feasible", disp_ok) + trial.set_user_attr("stress_feasible", stress_ok) + trial.set_user_attr("fully_feasible", disp_ok and stress_ok) + + logger.info( + "Trial %d: mass=%.2f kg, disp=%.2f mm%s, stress=%.1f MPa%s (%.1fs)", + trial_num, + nx_result.mass, + nx_result.tip_displacement, + " โœ“" if disp_ok else " โœ—", + nx_result.max_von_mises, + " โœ“" if stress_ok else " โœ—", + t_elapsed, + ) + + if history: + history.log_trial( + study_name=study_name, trial_id=trial_num, params=params, + mass_kg=nx_result.mass, + tip_displacement_mm=nx_result.tip_displacement, + max_von_mises_mpa=nx_result.max_von_mises, + geo_feasible=True, + status="COMPLETE", + solve_time_s=round(t_elapsed, 2), + iter_path=nx_result.iteration_dir, + iteration_number=trial_num + 1, + ) + history.export_csv() # Live update CSV after each trial + + # Optuna rejects nan โ€” use INFEASIBLE_MASS as fallback + import math + if math.isnan(nx_result.mass): + logger.warning("Trial %d: mass is NaN (extraction failed), using penalty value", trial_num) + return INFEASIBLE_MASS + + return nx_result.mass + + +# --------------------------------------------------------------------------- +# Results export +# --------------------------------------------------------------------------- +def export_csv(study: optuna.Study, output_path: str) -> None: + """Export all trial results to CSV. + + Columns: trial_number, status, beam_half_core_thickness, beam_face_thickness, + holes_diameter, hole_count, mass, tip_displacement, max_von_mises, + geo_feasible, displacement_feasible, stress_feasible, fully_feasible, + ligament, web_clearance, solve_time_s + + Args: + study: Completed Optuna study. + output_path: Path to output CSV file. + """ + fieldnames = [ + "trial_number", + "status", + "beam_half_core_thickness", + "beam_face_thickness", + "holes_diameter", + "hole_count", + "mass_kg", + "tip_displacement_mm", + "max_von_mises_MPa", + "geo_feasible", + "displacement_feasible", + "stress_feasible", + "fully_feasible", + "ligament_mm", + "web_clearance_mm", + "solve_time_s", + ] + + with open(output_path, "w", newline="") as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + writer.writeheader() + + for trial in study.trials: + ua = trial.user_attrs + writer.writerow({ + "trial_number": trial.number, + "status": ua.get("status", "unknown"), + "beam_half_core_thickness": ua.get("beam_half_core_thickness", ""), + "beam_face_thickness": ua.get("beam_face_thickness", ""), + "holes_diameter": ua.get("holes_diameter", ""), + "hole_count": ua.get("hole_count", ""), + "mass_kg": ua.get("mass", ""), + "tip_displacement_mm": ua.get("tip_displacement", ""), + "max_von_mises_MPa": ua.get("max_von_mises", ""), + "geo_feasible": ua.get("geo_feasible", ""), + "displacement_feasible": ua.get("displacement_feasible", ""), + "stress_feasible": ua.get("stress_feasible", ""), + "fully_feasible": ua.get("fully_feasible", ""), + "ligament_mm": ua.get("ligament", ""), + "web_clearance_mm": ua.get("web_clearance", ""), + "solve_time_s": ua.get("solve_time_s", ""), + }) + + logger.info("CSV results exported to %s (%d trials)", output_path, len(study.trials)) + + +def export_summary(study: optuna.Study, output_path: str) -> None: + """Export study summary as JSON. + + Includes metadata, statistics, best feasible design, and + constraint satisfaction rates. + + Args: + study: Completed Optuna study. + output_path: Path to output JSON file. + """ + trials = study.trials + n_total = len(trials) + + # Count by status + n_solved = sum(1 for t in trials if t.user_attrs.get("status") == "solved") + n_geo_infeasible = sum( + 1 for t in trials if t.user_attrs.get("status") == "geo_infeasible" + ) + n_failed = sum( + 1 for t in trials if t.user_attrs.get("status") == "solve_failed" + ) + n_feasible = sum( + 1 for t in trials if t.user_attrs.get("fully_feasible", False) + ) + + # Best feasible trial + best_feasible = None + best_mass = float("inf") + for t in trials: + if t.user_attrs.get("fully_feasible", False): + mass = t.user_attrs.get("mass", float("inf")) + if mass < best_mass: + best_mass = mass + best_feasible = t + + best_info = None + if best_feasible is not None: + ua = best_feasible.user_attrs + best_info = { + "trial_number": best_feasible.number, + "mass_kg": ua.get("mass"), + "tip_displacement_mm": ua.get("tip_displacement"), + "max_von_mises_MPa": ua.get("max_von_mises"), + "design_variables": { + "beam_half_core_thickness": ua.get("beam_half_core_thickness"), + "beam_face_thickness": ua.get("beam_face_thickness"), + "holes_diameter": ua.get("holes_diameter"), + "hole_count": ua.get("hole_count"), + }, + } + + summary = { + "study_name": study.study_name, + "phase": "Phase 1 โ€” LHS DoE", + "project": "Hydrotech Beam Structural Optimization", + "timestamp": datetime.now(timezone.utc).isoformat(), + "configuration": { + "n_lhs_samples": DEFAULT_N_SAMPLES, + "seed": DEFAULT_SEED, + "baseline_included": True, + "algorithm": "Latin Hypercube Sampling (scipy.stats.qmc)", + "constraint_handling": "Deb's feasibility rules", + "displacement_limit_mm": DISPLACEMENT_LIMIT, + "stress_limit_MPa": STRESS_LIMIT, + }, + "design_variables": [ + { + "name": dv.name, + "nx_expression": dv.nx_expression, + "lower": dv.lower, + "upper": dv.upper, + "baseline": dv.baseline, + "type": "integer" if dv.is_integer else "continuous", + } + for dv in DV_DEFINITIONS + ], + "results": { + "total_trials": n_total, + "solved": n_solved, + "geo_infeasible": n_geo_infeasible, + "solve_failed": n_failed, + "fully_feasible": n_feasible, + "solve_success_rate": round(n_solved / max(n_total, 1) * 100, 1), + "feasibility_rate": round(n_feasible / max(n_solved, 1) * 100, 1), + }, + "best_feasible": best_info, + "phase1_gate_check": { + "min_feasible_5": n_feasible >= 5, + "solve_success_80pct": (n_solved / max(n_total, 1)) >= 0.80, + "gate_passed": n_feasible >= 5 and (n_solved / max(n_total, 1)) >= 0.80, + }, + } + + with open(output_path, "w") as f: + json.dump(summary, f, indent=2) + + logger.info("Summary exported to %s", output_path) + + +# --------------------------------------------------------------------------- +# Main study runner +# --------------------------------------------------------------------------- +def run_study(args: argparse.Namespace) -> None: + """Execute the Phase 1 LHS DoE study. + + Steps: + 1. Generate LHS sample points + baseline + 2. Create/load Optuna study with SQLite storage + 3. Enqueue all trials (baseline first, then LHS) + 4. Run optimization (all trials evaluated via objective fn) + 5. Export results to CSV and JSON + + Args: + args: Parsed command-line arguments. + """ + results_dir = Path(args.results_dir) + results_dir.mkdir(parents=True, exist_ok=True) + + # ----------------------------------------------------------------------- + # 1. Generate sample points + # ----------------------------------------------------------------------- + logger.info("=" * 70) + logger.info("HYDROTECH BEAM โ€” Phase 1 LHS DoE") + logger.info("=" * 70) + + points = generate_lhs_samples( + n_samples=args.n_samples, + seed=args.seed, + include_baseline=True, + ) + n_trials = len(points) + logger.info("Generated %d trial points (1 baseline + %d LHS)", n_trials, n_trials - 1) + + # ----------------------------------------------------------------------- + # 2. Create Optuna study + # ----------------------------------------------------------------------- + db_path = results_dir / "optuna_study.db" + storage = f"sqlite:///{db_path}" + + if args.clean and db_path.exists(): + logger.info("--clean flag: deleting Optuna DB at %s (history.db preserved)", db_path) + db_path.unlink() + + if args.resume: + logger.info("Resuming existing study: %s", args.study_name) + study = optuna.load_study( + study_name=args.study_name, + storage=storage, + ) + logger.info("Loaded study with %d existing trials", len(study.trials)) + else: + study = optuna.create_study( + study_name=args.study_name, + storage=storage, + direction="minimize", # minimize mass + load_if_exists=True, # safe re-run: reuse if exists + sampler=optuna.samplers.TPESampler(seed=args.seed), + ) + logger.info("Created new study: %s (storage: %s)", args.study_name, db_path) + + # Enqueue all trial points + trial_dicts = points_to_dicts(points) + for i, td in enumerate(trial_dicts): + study.enqueue_trial(td) + if i == 0: + logger.info("Enqueued Trial 0 (baseline): %s", td) + + logger.info("Enqueued %d trials (1 baseline + %d LHS)", n_trials, n_trials - 1) + + # ----------------------------------------------------------------------- + # 3. Create solver + history + # ----------------------------------------------------------------------- + solver = create_solver( + backend=args.backend, + model_dir=args.model_dir, + ) + + # Persistent history โ€” NEVER deleted by --clean + history = TrialHistory(results_dir) + study_name = args.study_name + + # ----------------------------------------------------------------------- + # 4. Run all trials + # ----------------------------------------------------------------------- + logger.info("-" * 70) + logger.info("Starting trial evaluations...") + logger.info("-" * 70) + + t_study_start = time.monotonic() + + # Suppress Optuna's verbose logging during trials + optuna.logging.set_verbosity(optuna.logging.WARNING) + + study.optimize( + lambda trial: evaluate_trial(trial, solver, history, study_name), + n_trials=n_trials, + callbacks=[_progress_callback], + ) + + t_study_elapsed = time.monotonic() - t_study_start + + # ----------------------------------------------------------------------- + # 5. Export results + # ----------------------------------------------------------------------- + logger.info("-" * 70) + logger.info("Study complete. Exporting results...") + logger.info("-" * 70) + + csv_path = str(results_dir / "doe_results.csv") + json_path = str(results_dir / "doe_summary.json") + + export_csv(study, csv_path) + export_summary(study, json_path) + + # ----------------------------------------------------------------------- + # 6. Print summary + # ----------------------------------------------------------------------- + _print_summary(study, t_study_elapsed) + + # Cleanup + solver.close() + + # Final history summary (before close!) + hist_summary = history.get_study_summary(study_name) + logger.info("History DB: %d total records across all studies", hist_summary["total"]) + history.close() + + +def _progress_callback(study: optuna.Study, trial: optuna.trial.FrozenTrial) -> None: + """Log progress after each trial.""" + n_complete = len(study.trials) + status = trial.user_attrs.get("status", "unknown") + mass = trial.user_attrs.get("mass", "N/A") + feasible = trial.user_attrs.get("fully_feasible", False) + logger.info( + " [%d/%d] Trial %d: status=%s, mass=%s, feasible=%s", + n_complete, + DEFAULT_N_SAMPLES + 1, + trial.number, + status, + f"{mass:.2f} kg" if isinstance(mass, (int, float)) else mass, + feasible, + ) + + +def _print_summary(study: optuna.Study, elapsed: float) -> None: + """Print a human-readable summary to stdout.""" + trials = study.trials + n_total = len(trials) + n_solved = sum(1 for t in trials if t.user_attrs.get("status") == "solved") + n_geo_inf = sum(1 for t in trials if t.user_attrs.get("status") == "geo_infeasible") + n_failed = sum(1 for t in trials if t.user_attrs.get("status") == "solve_failed") + n_feasible = sum(1 for t in trials if t.user_attrs.get("fully_feasible", False)) + + print("\n" + "=" * 70) + print("PHASE 1 DoE โ€” RESULTS SUMMARY") + print("=" * 70) + print(f" Total trials: {n_total}") + print(f" Solved: {n_solved}") + print(f" Geometrically infeasible: {n_geo_inf}") + print(f" Solve failures: {n_failed}") + print(f" Fully feasible: {n_feasible}") + print(f" Solve success rate: {n_solved/max(n_total,1)*100:.1f}%") + print(f" Feasibility rate: {n_feasible/max(n_solved,1)*100:.1f}%") + print(f" Total time: {elapsed:.1f}s ({elapsed/60:.1f} min)") + + # Phase 1 โ†’ Phase 2 gate check + print("\n GATE CHECK (Phase 1 โ†’ Phase 2):") + gate_feasible = n_feasible >= 5 + gate_solve = (n_solved / max(n_total, 1)) >= 0.80 + print(f" โ‰ฅ5 feasible points: {'โœ“ PASS' if gate_feasible else 'โœ— FAIL'} ({n_feasible})") + print(f" โ‰ฅ80% solve success: {'โœ“ PASS' if gate_solve else 'โœ— FAIL'} ({n_solved/max(n_total,1)*100:.0f}%)") + print(f" GATE: {'โœ“ PASSED' if gate_feasible and gate_solve else 'โœ— BLOCKED'}") + + # Best feasible + best_mass = float("inf") + best_trial = None + for t in trials: + if t.user_attrs.get("fully_feasible", False): + m = t.user_attrs.get("mass", float("inf")) + if m < best_mass: + best_mass = m + best_trial = t + + if best_trial is not None: + ua = best_trial.user_attrs + print(f"\n BEST FEASIBLE DESIGN (Trial {best_trial.number}):") + print(f" Mass: {ua['mass']:.2f} kg") + print(f" Displacement: {ua['tip_displacement']:.2f} mm (limit: {DISPLACEMENT_LIMIT} mm)") + print(f" VM Stress: {ua['max_von_mises']:.1f} MPa (limit: {STRESS_LIMIT} MPa)") + print(f" Core thickness: {ua['beam_half_core_thickness']:.3f} mm") + print(f" Face thickness: {ua['beam_face_thickness']:.3f} mm") + print(f" Hole diameter: {ua['holes_diameter']:.1f} mm") + print(f" Hole count: {ua['hole_count']}") + else: + print("\n โš ๏ธ NO FEASIBLE DESIGN FOUND โ€” see OPTIMIZATION_STRATEGY.md ยง7.1") + + print("=" * 70) + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- +def parse_args() -> argparse.Namespace: + """Parse command-line arguments.""" + parser = argparse.ArgumentParser( + description="Hydrotech Beam โ€” Phase 1 LHS DoE Study", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=( + "Examples:\n" + " # Development (stub solver):\n" + " python run_doe.py --backend stub\n\n" + " # Real NX evaluation:\n" + " python run_doe.py --backend nxopen --model-dir /path/to/models\n\n" + " # Resume interrupted study:\n" + " python run_doe.py --backend stub --resume\n" + ), + ) + parser.add_argument( + "--backend", + choices=["stub", "nxopen"], + default="stub", + help="NX solver backend: 'stub' for testing, 'nxopen' for real (default: stub)", + ) + parser.add_argument( + "--model-dir", + default="", + help="Path to NX model directory (required for --backend nxopen)", + ) + parser.add_argument( + "--study-name", + default=DEFAULT_STUDY_NAME, + help=f"Optuna study name (default: {DEFAULT_STUDY_NAME})", + ) + parser.add_argument( + "--n-samples", + type=int, + default=DEFAULT_N_SAMPLES, + help=f"Number of LHS sample points (default: {DEFAULT_N_SAMPLES})", + ) + parser.add_argument( + "--seed", + type=int, + default=DEFAULT_SEED, + help=f"Random seed for LHS (default: {DEFAULT_SEED})", + ) + parser.add_argument( + "--results-dir", + default=DEFAULT_RESULTS_DIR, + help=f"Output directory for results (default: {DEFAULT_RESULTS_DIR})", + ) + parser.add_argument( + "--resume", + action="store_true", + help="Resume an existing study instead of creating a new one", + ) + parser.add_argument( + "--clean", + action="store_true", + help="Delete existing results DB before starting (fresh run)", + ) + parser.add_argument( + "--verbose", "-v", + action="store_true", + help="Enable verbose (DEBUG) logging", + ) + + return parser.parse_args() + + +def main() -> None: + """Entry point.""" + args = parse_args() + + # Configure logging โ€” console + file + log_level = logging.DEBUG if args.verbose else logging.INFO + log_format = "%(asctime)s [%(levelname)-7s] %(name)s: %(message)s" + log_datefmt = "%Y-%m-%d %H:%M:%S" + + # Ensure results dir exists for log file + results_dir = Path(args.results_dir) + results_dir.mkdir(parents=True, exist_ok=True) + log_file = results_dir / "doe_run.log" + + logging.basicConfig( + level=log_level, + format=log_format, + datefmt=log_datefmt, + handlers=[ + logging.StreamHandler(), # console + logging.FileHandler(log_file, mode="a", encoding="utf-8"), # file + ], + ) + logger.info("Log file: %s", log_file.resolve()) + + # Run + try: + run_study(args) + except KeyboardInterrupt: + logger.warning("Study interrupted by user. Progress saved to Optuna DB.") + logger.info("Resume with: python run_doe.py --resume --study-name %s", args.study_name) + sys.exit(1) + except Exception: + logger.exception("Study failed with unexpected error") + sys.exit(2) + + +if __name__ == "__main__": + main() diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/run_doe.sync-conflict-20260214-191753-VBCUD7Z.py b/projects/hydrotech-beam/studies/01_doe_landscape/run_doe.sync-conflict-20260214-191753-VBCUD7Z.py new file mode 100644 index 00000000..0fc53203 --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/run_doe.sync-conflict-20260214-191753-VBCUD7Z.py @@ -0,0 +1,737 @@ +#!/usr/bin/env python3 +"""Phase 1 LHS DoE โ€” Hydrotech Beam Optimization. + +Main entry point for the Design of Experiments landscape mapping study. +Generates 50 LHS sample points + 1 baseline (Trial 0), runs pre-flight +geometric checks, evaluates via NX (or stub), and manages the Optuna study. + +Usage: + # Dry run with stub solver (development/testing): + python run_doe.py --backend stub --study-name hydrotech_doe_dev + + # Real run with NXOpen (on Windows/dalidou): + python run_doe.py --backend nxopen --model-dir /path/to/nx/models + + # Resume interrupted study: + python run_doe.py --backend stub --study-name hydrotech_doe_dev --resume + +References: + OPTIMIZATION_STRATEGY.md โ€” Full strategy document + CONTEXT.md โ€” Confirmed expression names and values +""" + +from __future__ import annotations + +import argparse +import csv +import json +import logging +import os +import sys +import time +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + +import numpy as np +import optuna + +from geometric_checks import ( + DesignPoint, + FeasibilityResult, + check_feasibility, +) +from history import TrialHistory +from nx_interface import TrialInput, TrialResult, create_solver +from sampling import DV_DEFINITIONS, generate_lhs_samples, points_to_dicts + +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- +DEFAULT_STUDY_NAME = "hydrotech_beam_doe_phase1" +DEFAULT_N_SAMPLES = 50 +DEFAULT_SEED = 42 +DEFAULT_RESULTS_DIR = "results" +DEFAULT_DB_PATH = "results/optuna_study.db" + +# Constraint limits (hard limits โ€” OPTIMIZATION_STRATEGY.md ยง1.2) +DISPLACEMENT_LIMIT = 10.0 # mm +STRESS_LIMIT = 130.0 # MPa + +# Infeasible placeholder values (for geometric pre-check failures) +INFEASIBLE_MASS = 99999.0 # kg +INFEASIBLE_DISPLACEMENT = 99999.0 # mm +INFEASIBLE_STRESS = 99999.0 # MPa + +logger = logging.getLogger(__name__) + + +# --------------------------------------------------------------------------- +# Optuna constraint callback +# --------------------------------------------------------------------------- +def constraints_func(trial: optuna.trial.FrozenTrial) -> list[float]: + """Compute constraint violations for Deb's feasibility rules. + + Returns list of constraint values where: + value โ‰ค 0 โ†’ feasible (constraint satisfied) + value > 0 โ†’ infeasible (constraint violated) + + This implements Deb's feasibility rules (Deb 2000): + 1. Feasible vs feasible โ†’ lower objective wins + 2. Feasible vs infeasible โ†’ feasible wins + 3. Infeasible vs infeasible โ†’ lower total violation wins + + References: + OPTIMIZATION_STRATEGY.md ยง3.2 + + Args: + trial: Optuna frozen trial with user attributes set. + + Returns: + [displacement_violation, stress_violation] + """ + disp = trial.user_attrs.get("tip_displacement", INFEASIBLE_DISPLACEMENT) + stress = trial.user_attrs.get("max_von_mises", INFEASIBLE_STRESS) + return [ + disp - DISPLACEMENT_LIMIT, # โ‰ค 0 means displacement โ‰ค 10 mm + stress - STRESS_LIMIT, # โ‰ค 0 means stress โ‰ค 130 MPa + ] + + +# --------------------------------------------------------------------------- +# Trial evaluation +# --------------------------------------------------------------------------- +def evaluate_trial( + trial: optuna.Trial, + solver: Any, + history: TrialHistory | None = None, + study_name: str = "", +) -> float: + """Evaluate a single trial: geometric check โ†’ NX solve โ†’ extract. + + Args: + trial: Optuna trial (with parameters already suggested/enqueued). + solver: NX solver instance (stub or real). + history: Persistent trial history logger (append-only). + study_name: Study name for history logging. + + Returns: + Objective value (mass in kg). Returns INFEASIBLE_MASS for + geometrically infeasible or failed evaluations. + """ + # Extract design variables from trial + dv1 = trial.suggest_float( + "beam_half_core_thickness", 10.0, 40.0, + ) + dv2 = trial.suggest_float( + "beam_face_thickness", 10.0, 40.0, + ) + dv3 = trial.suggest_float( + "holes_diameter", 150.0, 450.0, + ) + dv4 = trial.suggest_int( + "hole_count", 5, 15, + ) + + trial_num = trial.number + logger.info( + "Trial %d: DV1=%.2f, DV2=%.2f, DV3=%.1f, DV4=%d", + trial_num, dv1, dv2, dv3, dv4, + ) + + # Store DVs in user attributes for logging + trial.set_user_attr("beam_half_core_thickness", dv1) + trial.set_user_attr("beam_face_thickness", dv2) + trial.set_user_attr("holes_diameter", dv3) + trial.set_user_attr("hole_count", dv4) + + # Pre-flight geometric check + point = DesignPoint( + beam_half_core_thickness=dv1, + beam_face_thickness=dv2, + holes_diameter=dv3, + hole_count=dv4, + ) + geo_result: FeasibilityResult = check_feasibility(point) + trial.set_user_attr("geo_feasible", geo_result.feasible) + trial.set_user_attr("ligament", geo_result.ligament) + trial.set_user_attr("web_clearance", geo_result.web_clearance) + + params = { + "beam_half_core_thickness": dv1, + "beam_face_thickness": dv2, + "holes_diameter": dv3, + "hole_count": dv4, + } + + if not geo_result.feasible: + logger.warning( + "Trial %d: GEOMETRICALLY INFEASIBLE โ€” %s", + trial_num, geo_result.reason, + ) + trial.set_user_attr("status", "geo_infeasible") + trial.set_user_attr("geo_reason", geo_result.reason) + trial.set_user_attr("tip_displacement", INFEASIBLE_DISPLACEMENT) + trial.set_user_attr("max_von_mises", INFEASIBLE_STRESS) + trial.set_user_attr("mass", INFEASIBLE_MASS) + if history: + history.log_trial( + study_name=study_name, trial_id=trial_num, params=params, + geo_feasible=False, status="GEO_INFEASIBLE", + error_message=geo_result.reason, + iteration_number=trial_num + 1, + ) + return INFEASIBLE_MASS + + # NX evaluation + trial_input = TrialInput( + beam_half_core_thickness=dv1, + beam_face_thickness=dv2, + holes_diameter=dv3, + hole_count=dv4, + ) + + t_start = time.monotonic() + nx_result: TrialResult = solver.solve(trial_input) + t_elapsed = time.monotonic() - t_start + + trial.set_user_attr("solve_time_s", round(t_elapsed, 2)) + + if not nx_result.success: + logger.error( + "Trial %d: NX SOLVE FAILED โ€” %s (%.1fs)", + trial_num, nx_result.error_message, t_elapsed, + ) + trial.set_user_attr("status", "solve_failed") + trial.set_user_attr("error_message", nx_result.error_message) + trial.set_user_attr("tip_displacement", INFEASIBLE_DISPLACEMENT) + trial.set_user_attr("max_von_mises", INFEASIBLE_STRESS) + trial.set_user_attr("mass", INFEASIBLE_MASS) + if history: + history.log_trial( + study_name=study_name, trial_id=trial_num, params=params, + status="FAILED", error_message=nx_result.error_message, + solve_time_s=round(t_elapsed, 2), + iter_path=nx_result.iteration_dir, + iteration_number=trial_num + 1, + ) + return INFEASIBLE_MASS + + # Record successful results + trial.set_user_attr("status", "solved") + trial.set_user_attr("mass", nx_result.mass) + trial.set_user_attr("tip_displacement", nx_result.tip_displacement) + trial.set_user_attr("max_von_mises", nx_result.max_von_mises) + + # Check constraint feasibility + disp_ok = nx_result.tip_displacement <= DISPLACEMENT_LIMIT + stress_ok = nx_result.max_von_mises <= STRESS_LIMIT + trial.set_user_attr("displacement_feasible", disp_ok) + trial.set_user_attr("stress_feasible", stress_ok) + trial.set_user_attr("fully_feasible", disp_ok and stress_ok) + + logger.info( + "Trial %d: mass=%.2f kg, disp=%.2f mm%s, stress=%.1f MPa%s (%.1fs)", + trial_num, + nx_result.mass, + nx_result.tip_displacement, + " โœ“" if disp_ok else " โœ—", + nx_result.max_von_mises, + " โœ“" if stress_ok else " โœ—", + t_elapsed, + ) + + if history: + history.log_trial( + study_name=study_name, trial_id=trial_num, params=params, + mass_kg=nx_result.mass, + tip_displacement_mm=nx_result.tip_displacement, + max_von_mises_mpa=nx_result.max_von_mises, + geo_feasible=True, + status="COMPLETE", + solve_time_s=round(t_elapsed, 2), + iter_path=nx_result.iteration_dir, + iteration_number=trial_num + 1, + ) + history.export_csv() # Live update CSV after each trial + + # Optuna rejects nan โ€” use INFEASIBLE_MASS as fallback + import math + if math.isnan(nx_result.mass): + logger.warning("Trial %d: mass is NaN (extraction failed), using penalty value", trial_num) + return INFEASIBLE_MASS + + return nx_result.mass + + +# --------------------------------------------------------------------------- +# Results export +# --------------------------------------------------------------------------- +def export_csv(study: optuna.Study, output_path: str) -> None: + """Export all trial results to CSV. + + Columns: trial_number, status, beam_half_core_thickness, beam_face_thickness, + holes_diameter, hole_count, mass, tip_displacement, max_von_mises, + geo_feasible, displacement_feasible, stress_feasible, fully_feasible, + ligament, web_clearance, solve_time_s + + Args: + study: Completed Optuna study. + output_path: Path to output CSV file. + """ + fieldnames = [ + "trial_number", + "status", + "beam_half_core_thickness", + "beam_face_thickness", + "holes_diameter", + "hole_count", + "mass_kg", + "tip_displacement_mm", + "max_von_mises_MPa", + "geo_feasible", + "displacement_feasible", + "stress_feasible", + "fully_feasible", + "ligament_mm", + "web_clearance_mm", + "solve_time_s", + ] + + with open(output_path, "w", newline="") as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + writer.writeheader() + + for trial in study.trials: + ua = trial.user_attrs + writer.writerow({ + "trial_number": trial.number, + "status": ua.get("status", "unknown"), + "beam_half_core_thickness": ua.get("beam_half_core_thickness", ""), + "beam_face_thickness": ua.get("beam_face_thickness", ""), + "holes_diameter": ua.get("holes_diameter", ""), + "hole_count": ua.get("hole_count", ""), + "mass_kg": ua.get("mass", ""), + "tip_displacement_mm": ua.get("tip_displacement", ""), + "max_von_mises_MPa": ua.get("max_von_mises", ""), + "geo_feasible": ua.get("geo_feasible", ""), + "displacement_feasible": ua.get("displacement_feasible", ""), + "stress_feasible": ua.get("stress_feasible", ""), + "fully_feasible": ua.get("fully_feasible", ""), + "ligament_mm": ua.get("ligament", ""), + "web_clearance_mm": ua.get("web_clearance", ""), + "solve_time_s": ua.get("solve_time_s", ""), + }) + + logger.info("CSV results exported to %s (%d trials)", output_path, len(study.trials)) + + +def export_summary(study: optuna.Study, output_path: str) -> None: + """Export study summary as JSON. + + Includes metadata, statistics, best feasible design, and + constraint satisfaction rates. + + Args: + study: Completed Optuna study. + output_path: Path to output JSON file. + """ + trials = study.trials + n_total = len(trials) + + # Count by status + n_solved = sum(1 for t in trials if t.user_attrs.get("status") == "solved") + n_geo_infeasible = sum( + 1 for t in trials if t.user_attrs.get("status") == "geo_infeasible" + ) + n_failed = sum( + 1 for t in trials if t.user_attrs.get("status") == "solve_failed" + ) + n_feasible = sum( + 1 for t in trials if t.user_attrs.get("fully_feasible", False) + ) + + # Best feasible trial + best_feasible = None + best_mass = float("inf") + for t in trials: + if t.user_attrs.get("fully_feasible", False): + mass = t.user_attrs.get("mass", float("inf")) + if mass < best_mass: + best_mass = mass + best_feasible = t + + best_info = None + if best_feasible is not None: + ua = best_feasible.user_attrs + best_info = { + "trial_number": best_feasible.number, + "mass_kg": ua.get("mass"), + "tip_displacement_mm": ua.get("tip_displacement"), + "max_von_mises_MPa": ua.get("max_von_mises"), + "design_variables": { + "beam_half_core_thickness": ua.get("beam_half_core_thickness"), + "beam_face_thickness": ua.get("beam_face_thickness"), + "holes_diameter": ua.get("holes_diameter"), + "hole_count": ua.get("hole_count"), + }, + } + + summary = { + "study_name": study.study_name, + "phase": "Phase 1 โ€” LHS DoE", + "project": "Hydrotech Beam Structural Optimization", + "timestamp": datetime.now(timezone.utc).isoformat(), + "configuration": { + "n_lhs_samples": DEFAULT_N_SAMPLES, + "seed": DEFAULT_SEED, + "baseline_included": True, + "algorithm": "Latin Hypercube Sampling (scipy.stats.qmc)", + "constraint_handling": "Deb's feasibility rules", + "displacement_limit_mm": DISPLACEMENT_LIMIT, + "stress_limit_MPa": STRESS_LIMIT, + }, + "design_variables": [ + { + "name": dv.name, + "nx_expression": dv.nx_expression, + "lower": dv.lower, + "upper": dv.upper, + "baseline": dv.baseline, + "type": "integer" if dv.is_integer else "continuous", + } + for dv in DV_DEFINITIONS + ], + "results": { + "total_trials": n_total, + "solved": n_solved, + "geo_infeasible": n_geo_infeasible, + "solve_failed": n_failed, + "fully_feasible": n_feasible, + "solve_success_rate": round(n_solved / max(n_total, 1) * 100, 1), + "feasibility_rate": round(n_feasible / max(n_solved, 1) * 100, 1), + }, + "best_feasible": best_info, + "phase1_gate_check": { + "min_feasible_5": n_feasible >= 5, + "solve_success_80pct": (n_solved / max(n_total, 1)) >= 0.80, + "gate_passed": n_feasible >= 5 and (n_solved / max(n_total, 1)) >= 0.80, + }, + } + + with open(output_path, "w") as f: + json.dump(summary, f, indent=2) + + logger.info("Summary exported to %s", output_path) + + +# --------------------------------------------------------------------------- +# Main study runner +# --------------------------------------------------------------------------- +def run_study(args: argparse.Namespace) -> None: + """Execute the Phase 1 LHS DoE study. + + Steps: + 1. Generate LHS sample points + baseline + 2. Create/load Optuna study with SQLite storage + 3. Enqueue all trials (baseline first, then LHS) + 4. Run optimization (all trials evaluated via objective fn) + 5. Export results to CSV and JSON + + Args: + args: Parsed command-line arguments. + """ + results_dir = Path(args.results_dir) + results_dir.mkdir(parents=True, exist_ok=True) + + # ----------------------------------------------------------------------- + # 1. Generate sample points + # ----------------------------------------------------------------------- + logger.info("=" * 70) + logger.info("HYDROTECH BEAM โ€” Phase 1 LHS DoE") + logger.info("=" * 70) + + points = generate_lhs_samples( + n_samples=args.n_samples, + seed=args.seed, + include_baseline=True, + ) + n_trials = len(points) + logger.info("Generated %d trial points (1 baseline + %d LHS)", n_trials, n_trials - 1) + + # ----------------------------------------------------------------------- + # 2. Create Optuna study + # ----------------------------------------------------------------------- + db_path = results_dir / "optuna_study.db" + storage = f"sqlite:///{db_path}" + + if args.clean and db_path.exists(): + logger.info("--clean flag: deleting Optuna DB at %s (history.db preserved)", db_path) + db_path.unlink() + + if args.resume: + logger.info("Resuming existing study: %s", args.study_name) + study = optuna.load_study( + study_name=args.study_name, + storage=storage, + ) + logger.info("Loaded study with %d existing trials", len(study.trials)) + else: + study = optuna.create_study( + study_name=args.study_name, + storage=storage, + direction="minimize", # minimize mass + load_if_exists=True, # safe re-run: reuse if exists + sampler=optuna.samplers.TPESampler(seed=args.seed), + ) + logger.info("Created new study: %s (storage: %s)", args.study_name, db_path) + + # Enqueue all trial points + trial_dicts = points_to_dicts(points) + for i, td in enumerate(trial_dicts): + study.enqueue_trial(td) + if i == 0: + logger.info("Enqueued Trial 0 (baseline): %s", td) + + logger.info("Enqueued %d trials (1 baseline + %d LHS)", n_trials, n_trials - 1) + + # ----------------------------------------------------------------------- + # 3. Create solver + history + # ----------------------------------------------------------------------- + solver = create_solver( + backend=args.backend, + model_dir=args.model_dir, + ) + + # Persistent history โ€” NEVER deleted by --clean + history = TrialHistory(results_dir) + study_name = args.study_name + + # ----------------------------------------------------------------------- + # 4. Run all trials + # ----------------------------------------------------------------------- + logger.info("-" * 70) + logger.info("Starting trial evaluations...") + logger.info("-" * 70) + + t_study_start = time.monotonic() + + # Suppress Optuna's verbose logging during trials + optuna.logging.set_verbosity(optuna.logging.WARNING) + + study.optimize( + lambda trial: evaluate_trial(trial, solver, history, study_name), + n_trials=n_trials, + callbacks=[_progress_callback], + ) + + t_study_elapsed = time.monotonic() - t_study_start + + # ----------------------------------------------------------------------- + # 5. Export results + # ----------------------------------------------------------------------- + logger.info("-" * 70) + logger.info("Study complete. Exporting results...") + logger.info("-" * 70) + + csv_path = str(results_dir / "doe_results.csv") + json_path = str(results_dir / "doe_summary.json") + + export_csv(study, csv_path) + export_summary(study, json_path) + + # ----------------------------------------------------------------------- + # 6. Print summary + # ----------------------------------------------------------------------- + _print_summary(study, t_study_elapsed) + + # Cleanup + solver.close() + + # Final history summary (before close!) + hist_summary = history.get_study_summary(study_name) + logger.info("History DB: %d total records across all studies", hist_summary["total"]) + history.close() + + +def _progress_callback(study: optuna.Study, trial: optuna.trial.FrozenTrial) -> None: + """Log progress after each trial.""" + n_complete = len(study.trials) + status = trial.user_attrs.get("status", "unknown") + mass = trial.user_attrs.get("mass", "N/A") + feasible = trial.user_attrs.get("fully_feasible", False) + logger.info( + " [%d/%d] Trial %d: status=%s, mass=%s, feasible=%s", + n_complete, + DEFAULT_N_SAMPLES + 1, + trial.number, + status, + f"{mass:.2f} kg" if isinstance(mass, (int, float)) else mass, + feasible, + ) + + +def _print_summary(study: optuna.Study, elapsed: float) -> None: + """Print a human-readable summary to stdout.""" + trials = study.trials + n_total = len(trials) + n_solved = sum(1 for t in trials if t.user_attrs.get("status") == "solved") + n_geo_inf = sum(1 for t in trials if t.user_attrs.get("status") == "geo_infeasible") + n_failed = sum(1 for t in trials if t.user_attrs.get("status") == "solve_failed") + n_feasible = sum(1 for t in trials if t.user_attrs.get("fully_feasible", False)) + + print("\n" + "=" * 70) + print("PHASE 1 DoE โ€” RESULTS SUMMARY") + print("=" * 70) + print(f" Total trials: {n_total}") + print(f" Solved: {n_solved}") + print(f" Geometrically infeasible: {n_geo_inf}") + print(f" Solve failures: {n_failed}") + print(f" Fully feasible: {n_feasible}") + print(f" Solve success rate: {n_solved/max(n_total,1)*100:.1f}%") + print(f" Feasibility rate: {n_feasible/max(n_solved,1)*100:.1f}%") + print(f" Total time: {elapsed:.1f}s ({elapsed/60:.1f} min)") + + # Phase 1 โ†’ Phase 2 gate check + print("\n GATE CHECK (Phase 1 โ†’ Phase 2):") + gate_feasible = n_feasible >= 5 + gate_solve = (n_solved / max(n_total, 1)) >= 0.80 + print(f" โ‰ฅ5 feasible points: {'โœ“ PASS' if gate_feasible else 'โœ— FAIL'} ({n_feasible})") + print(f" โ‰ฅ80% solve success: {'โœ“ PASS' if gate_solve else 'โœ— FAIL'} ({n_solved/max(n_total,1)*100:.0f}%)") + print(f" GATE: {'โœ“ PASSED' if gate_feasible and gate_solve else 'โœ— BLOCKED'}") + + # Best feasible + best_mass = float("inf") + best_trial = None + for t in trials: + if t.user_attrs.get("fully_feasible", False): + m = t.user_attrs.get("mass", float("inf")) + if m < best_mass: + best_mass = m + best_trial = t + + if best_trial is not None: + ua = best_trial.user_attrs + print(f"\n BEST FEASIBLE DESIGN (Trial {best_trial.number}):") + print(f" Mass: {ua['mass']:.2f} kg") + print(f" Displacement: {ua['tip_displacement']:.2f} mm (limit: {DISPLACEMENT_LIMIT} mm)") + print(f" VM Stress: {ua['max_von_mises']:.1f} MPa (limit: {STRESS_LIMIT} MPa)") + print(f" Core thickness: {ua['beam_half_core_thickness']:.3f} mm") + print(f" Face thickness: {ua['beam_face_thickness']:.3f} mm") + print(f" Hole diameter: {ua['holes_diameter']:.1f} mm") + print(f" Hole count: {ua['hole_count']}") + else: + print("\n โš ๏ธ NO FEASIBLE DESIGN FOUND โ€” see OPTIMIZATION_STRATEGY.md ยง7.1") + + print("=" * 70) + + +# --------------------------------------------------------------------------- +# CLI +# --------------------------------------------------------------------------- +def parse_args() -> argparse.Namespace: + """Parse command-line arguments.""" + parser = argparse.ArgumentParser( + description="Hydrotech Beam โ€” Phase 1 LHS DoE Study", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=( + "Examples:\n" + " # Development (stub solver):\n" + " python run_doe.py --backend stub\n\n" + " # Real NX evaluation:\n" + " python run_doe.py --backend nxopen --model-dir /path/to/models\n\n" + " # Resume interrupted study:\n" + " python run_doe.py --backend stub --resume\n" + ), + ) + parser.add_argument( + "--backend", + choices=["stub", "nxopen"], + default="stub", + help="NX solver backend: 'stub' for testing, 'nxopen' for real (default: stub)", + ) + parser.add_argument( + "--model-dir", + default="", + help="Path to NX model directory (required for --backend nxopen)", + ) + parser.add_argument( + "--study-name", + default=DEFAULT_STUDY_NAME, + help=f"Optuna study name (default: {DEFAULT_STUDY_NAME})", + ) + parser.add_argument( + "--n-samples", + type=int, + default=DEFAULT_N_SAMPLES, + help=f"Number of LHS sample points (default: {DEFAULT_N_SAMPLES})", + ) + parser.add_argument( + "--seed", + type=int, + default=DEFAULT_SEED, + help=f"Random seed for LHS (default: {DEFAULT_SEED})", + ) + parser.add_argument( + "--results-dir", + default=DEFAULT_RESULTS_DIR, + help=f"Output directory for results (default: {DEFAULT_RESULTS_DIR})", + ) + parser.add_argument( + "--resume", + action="store_true", + help="Resume an existing study instead of creating a new one", + ) + parser.add_argument( + "--clean", + action="store_true", + help="Delete existing results DB before starting (fresh run)", + ) + parser.add_argument( + "--verbose", "-v", + action="store_true", + help="Enable verbose (DEBUG) logging", + ) + + return parser.parse_args() + + +def main() -> None: + """Entry point.""" + args = parse_args() + + # Configure logging โ€” console + file + log_level = logging.DEBUG if args.verbose else logging.INFO + log_format = "%(asctime)s [%(levelname)-7s] %(name)s: %(message)s" + log_datefmt = "%Y-%m-%d %H:%M:%S" + + # Ensure results dir exists for log file + results_dir = Path(args.results_dir) + results_dir.mkdir(parents=True, exist_ok=True) + log_file = results_dir / "doe_run.log" + + logging.basicConfig( + level=log_level, + format=log_format, + datefmt=log_datefmt, + handlers=[ + logging.StreamHandler(), # console + logging.FileHandler(log_file, mode="a", encoding="utf-8"), # file + ], + ) + logger.info("Log file: %s", log_file.resolve()) + + # Run + try: + run_study(args) + except KeyboardInterrupt: + logger.warning("Study interrupted by user. Progress saved to Optuna DB.") + logger.info("Resume with: python run_doe.py --resume --study-name %s", args.study_name) + sys.exit(1) + except Exception: + logger.exception("Study failed with unexpected error") + sys.exit(2) + + +if __name__ == "__main__": + main() diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/sampling.py b/projects/hydrotech-beam/studies/01_doe_landscape/sampling.py old mode 100644 new mode 100755 index 4cfa6c0c..320d4c50 --- a/projects/hydrotech-beam/studies/01_doe_landscape/sampling.py +++ b/projects/hydrotech-beam/studies/01_doe_landscape/sampling.py @@ -1,274 +1,274 @@ -"""Latin Hypercube Sampling for Hydrotech Beam DoE. - -Generates LHS sample points for the 4 design variables with: -- Maximin LHS for space-filling coverage -- Integer rounding for hole_count (DV4) -- Stratified integer sampling to ensure all 11 hole_count levels are covered -- Baseline (Trial 0) always included - -References: - OPTIMIZATION_STRATEGY.md ยง2.3 โ€” Phase 1 configuration - OPTIMIZATION_STRATEGY.md ยง1.4 โ€” Integer handling -""" - -from __future__ import annotations - -import logging -from dataclasses import dataclass -from typing import Any - -import numpy as np -from scipy.stats.qmc import LatinHypercube - -from geometric_checks import DesignPoint - -logger = logging.getLogger(__name__) - - -# --------------------------------------------------------------------------- -# Design variable bounds -# --------------------------------------------------------------------------- -@dataclass(frozen=True) -class DVBounds: - """Design variable bounds and metadata.""" - - name: str - nx_expression: str - lower: float - upper: float - baseline: float - is_integer: bool = False - - -DV_DEFINITIONS: list[DVBounds] = [ - DVBounds("beam_half_core_thickness", "beam_half_core_thickness", 10.0, 40.0, 25.162), - DVBounds("beam_face_thickness", "beam_face_thickness", 10.0, 40.0, 21.504), - DVBounds("holes_diameter", "holes_diameter", 150.0, 450.0, 300.0), - DVBounds("hole_count", "hole_count", 5.0, 15.0, 10.0, is_integer=True), -] - -N_DVS = len(DV_DEFINITIONS) -BASELINE_VALUES = [dv.baseline for dv in DV_DEFINITIONS] - - -def get_baseline_point() -> DesignPoint: - """Return the baseline design point (Trial 0). - - Returns: - DesignPoint with confirmed baseline values from NX introspection. - """ - return DesignPoint( - beam_half_core_thickness=25.162, - beam_face_thickness=21.504, - holes_diameter=300.0, - hole_count=10, - ) - - -def generate_lhs_samples( - n_samples: int = 50, - seed: int = 42, - include_baseline: bool = True, -) -> list[DesignPoint]: - """Generate LHS sample points for the DoE study. - - Strategy for integer coverage (hole_count = 5..15, 11 levels): - 1. Generate `n_samples` LHS points with continuous DV4 - 2. Round DV4 to nearest integer - 3. Check coverage โ€” if any of the 11 levels are missing, replace - the closest duplicate trial with the missing level - This ensures all integer levels are represented while maintaining - the space-filling property of LHS for the continuous variables. - - Args: - n_samples: Number of LHS sample points (default: 50). - seed: Random seed for reproducibility. - include_baseline: If True, prepend baseline as Trial 0. - - Returns: - List of DesignPoint instances. If include_baseline is True, - the first element is the baseline (Trial 0). - """ - logger.info( - "Generating %d LHS samples (seed=%d, baseline=%s)", - n_samples, seed, include_baseline, - ) - - # Generate unit hypercube LHS samples - sampler = LatinHypercube(d=N_DVS, seed=seed, optimization="random-cd") - unit_samples = sampler.random(n=n_samples) # shape: (n_samples, 4) - - # Scale to design variable bounds - samples = _scale_samples(unit_samples) - - # Round hole_count to nearest integer - samples[:, 3] = np.round(samples[:, 3]).astype(int) - samples[:, 3] = np.clip(samples[:, 3], 5, 15) - - # Ensure full integer coverage for hole_count - samples = _ensure_integer_coverage(samples, rng=np.random.default_rng(seed)) - - # Convert to DesignPoint list - points: list[DesignPoint] = [] - if include_baseline: - points.append(get_baseline_point()) - - for row in samples: - points.append( - DesignPoint( - beam_half_core_thickness=float(row[0]), - beam_face_thickness=float(row[1]), - holes_diameter=float(row[2]), - hole_count=int(row[3]), - ) - ) - - logger.info( - "Generated %d total points (%d LHS + %s baseline)", - len(points), - n_samples, - "1" if include_baseline else "0", - ) - _log_coverage(points) - - return points - - -def _scale_samples(unit_samples: np.ndarray) -> np.ndarray: - """Scale unit hypercube [0,1]^d samples to design variable bounds. - - Args: - unit_samples: Array of shape (n, 4) with values in [0, 1]. - - Returns: - Scaled array with values in [lower, upper] for each DV. - """ - lower = np.array([dv.lower for dv in DV_DEFINITIONS]) - upper = np.array([dv.upper for dv in DV_DEFINITIONS]) - return lower + unit_samples * (upper - lower) - - -def _ensure_integer_coverage( - samples: np.ndarray, - rng: np.random.Generator, -) -> np.ndarray: - """Ensure all 11 hole_count levels (5-15) are represented. - - If any integer level is missing, replace a duplicate from the most - over-represented level with a sample at the missing level. - Continuous DVs for replacement samples are drawn randomly within bounds. - - Args: - samples: Array of shape (n, 4) with rounded hole_count in col 3. - rng: NumPy random generator for reproducibility. - - Returns: - Modified samples array with full integer coverage. - """ - all_levels = set(range(5, 16)) # {5, 6, 7, ..., 15} - present_levels = set(int(x) for x in samples[:, 3]) - missing_levels = all_levels - present_levels - - if not missing_levels: - logger.info("All 11 hole_count levels represented โœ“") - return samples - - # Skip patching when sample size is too small to cover all levels - n_samples = len(samples) - if n_samples < len(all_levels): - logger.info( - "Only %d samples โ€” too few to cover all 11 hole_count levels " - "(need โ‰ฅ11). Skipping stratified patching.", - n_samples, - ) - return samples - - logger.warning( - "Missing hole_count levels: %s โ€” patching with replacements", - sorted(missing_levels), - ) - - for missing_level in sorted(missing_levels): - # Find the most over-represented level - unique, counts = np.unique(samples[:, 3].astype(int), return_counts=True) - most_common_idx = np.argmax(counts) - most_common_level = unique[most_common_idx] - - # Find indices with the most common level - candidates = np.where(samples[:, 3].astype(int) == most_common_level)[0] - replace_idx = rng.choice(candidates) - - # Replace: keep continuous DVs random within bounds, set hole_count - for j, dv in enumerate(DV_DEFINITIONS): - if dv.is_integer: - samples[replace_idx, j] = missing_level - else: - samples[replace_idx, j] = rng.uniform(dv.lower, dv.upper) - - logger.info( - " Replaced trial at idx %d: hole_count %d โ†’ %d", - replace_idx, most_common_level, missing_level, - ) - - return samples - - -def _log_coverage(points: list[DesignPoint]) -> None: - """Log hole_count coverage statistics.""" - counts: dict[int, int] = {} - for p in points: - counts[p.hole_count] = counts.get(p.hole_count, 0) + 1 - - logger.info("Hole count coverage:") - for level in range(5, 16): - n = counts.get(level, 0) - logger.info(" hole_count=%2d: %d trials", level, n) - - -def points_to_dicts(points: list[DesignPoint]) -> list[dict[str, Any]]: - """Convert DesignPoint list to list of dicts (for Optuna enqueue). - - Args: - points: List of DesignPoint instances. - - Returns: - List of dicts with DV names as keys. - """ - return [ - { - "beam_half_core_thickness": p.beam_half_core_thickness, - "beam_face_thickness": p.beam_face_thickness, - "holes_diameter": p.holes_diameter, - "hole_count": p.hole_count, - } - for p in points - ] - - -# --------------------------------------------------------------------------- -# Quick self-test -# --------------------------------------------------------------------------- -if __name__ == "__main__": - logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") - - points = generate_lhs_samples(n_samples=50, seed=42) - print(f"\nGenerated {len(points)} total points") - print(f" Trial 0 (baseline): {points[0]}") - print(f" Trial 1 (first LHS): {points[1]}") - print(f" Trial {len(points)-1} (last LHS): {points[-1]}") - - # Verify coverage - hole_counts = {p.hole_count for p in points} - expected = set(range(5, 16)) - assert hole_counts == expected, ( - f"Missing hole_count levels: {expected - hole_counts}" - ) - print("\nAll 11 hole_count levels covered โœ“") - - # Verify bounds - for p in points[1:]: # skip baseline - assert 10.0 <= p.beam_half_core_thickness <= 40.0 - assert 10.0 <= p.beam_face_thickness <= 40.0 - assert 150.0 <= p.holes_diameter <= 450.0 - assert 5 <= p.hole_count <= 15 - print("All samples within bounds โœ“") +"""Latin Hypercube Sampling for Hydrotech Beam DoE. + +Generates LHS sample points for the 4 design variables with: +- Maximin LHS for space-filling coverage +- Integer rounding for hole_count (DV4) +- Stratified integer sampling to ensure all 11 hole_count levels are covered +- Baseline (Trial 0) always included + +References: + OPTIMIZATION_STRATEGY.md ยง2.3 โ€” Phase 1 configuration + OPTIMIZATION_STRATEGY.md ยง1.4 โ€” Integer handling +""" + +from __future__ import annotations + +import logging +from dataclasses import dataclass +from typing import Any + +import numpy as np +from scipy.stats.qmc import LatinHypercube + +from geometric_checks import DesignPoint + +logger = logging.getLogger(__name__) + + +# --------------------------------------------------------------------------- +# Design variable bounds +# --------------------------------------------------------------------------- +@dataclass(frozen=True) +class DVBounds: + """Design variable bounds and metadata.""" + + name: str + nx_expression: str + lower: float + upper: float + baseline: float + is_integer: bool = False + + +DV_DEFINITIONS: list[DVBounds] = [ + DVBounds("beam_half_core_thickness", "beam_half_core_thickness", 10.0, 40.0, 25.162), + DVBounds("beam_face_thickness", "beam_face_thickness", 10.0, 40.0, 21.504), + DVBounds("holes_diameter", "holes_diameter", 150.0, 450.0, 300.0), + DVBounds("hole_count", "hole_count", 5.0, 15.0, 10.0, is_integer=True), +] + +N_DVS = len(DV_DEFINITIONS) +BASELINE_VALUES = [dv.baseline for dv in DV_DEFINITIONS] + + +def get_baseline_point() -> DesignPoint: + """Return the baseline design point (Trial 0). + + Returns: + DesignPoint with confirmed baseline values from NX introspection. + """ + return DesignPoint( + beam_half_core_thickness=25.162, + beam_face_thickness=21.504, + holes_diameter=300.0, + hole_count=10, + ) + + +def generate_lhs_samples( + n_samples: int = 50, + seed: int = 42, + include_baseline: bool = True, +) -> list[DesignPoint]: + """Generate LHS sample points for the DoE study. + + Strategy for integer coverage (hole_count = 5..15, 11 levels): + 1. Generate `n_samples` LHS points with continuous DV4 + 2. Round DV4 to nearest integer + 3. Check coverage โ€” if any of the 11 levels are missing, replace + the closest duplicate trial with the missing level + This ensures all integer levels are represented while maintaining + the space-filling property of LHS for the continuous variables. + + Args: + n_samples: Number of LHS sample points (default: 50). + seed: Random seed for reproducibility. + include_baseline: If True, prepend baseline as Trial 0. + + Returns: + List of DesignPoint instances. If include_baseline is True, + the first element is the baseline (Trial 0). + """ + logger.info( + "Generating %d LHS samples (seed=%d, baseline=%s)", + n_samples, seed, include_baseline, + ) + + # Generate unit hypercube LHS samples + sampler = LatinHypercube(d=N_DVS, seed=seed, optimization="random-cd") + unit_samples = sampler.random(n=n_samples) # shape: (n_samples, 4) + + # Scale to design variable bounds + samples = _scale_samples(unit_samples) + + # Round hole_count to nearest integer + samples[:, 3] = np.round(samples[:, 3]).astype(int) + samples[:, 3] = np.clip(samples[:, 3], 5, 15) + + # Ensure full integer coverage for hole_count + samples = _ensure_integer_coverage(samples, rng=np.random.default_rng(seed)) + + # Convert to DesignPoint list + points: list[DesignPoint] = [] + if include_baseline: + points.append(get_baseline_point()) + + for row in samples: + points.append( + DesignPoint( + beam_half_core_thickness=float(row[0]), + beam_face_thickness=float(row[1]), + holes_diameter=float(row[2]), + hole_count=int(row[3]), + ) + ) + + logger.info( + "Generated %d total points (%d LHS + %s baseline)", + len(points), + n_samples, + "1" if include_baseline else "0", + ) + _log_coverage(points) + + return points + + +def _scale_samples(unit_samples: np.ndarray) -> np.ndarray: + """Scale unit hypercube [0,1]^d samples to design variable bounds. + + Args: + unit_samples: Array of shape (n, 4) with values in [0, 1]. + + Returns: + Scaled array with values in [lower, upper] for each DV. + """ + lower = np.array([dv.lower for dv in DV_DEFINITIONS]) + upper = np.array([dv.upper for dv in DV_DEFINITIONS]) + return lower + unit_samples * (upper - lower) + + +def _ensure_integer_coverage( + samples: np.ndarray, + rng: np.random.Generator, +) -> np.ndarray: + """Ensure all 11 hole_count levels (5-15) are represented. + + If any integer level is missing, replace a duplicate from the most + over-represented level with a sample at the missing level. + Continuous DVs for replacement samples are drawn randomly within bounds. + + Args: + samples: Array of shape (n, 4) with rounded hole_count in col 3. + rng: NumPy random generator for reproducibility. + + Returns: + Modified samples array with full integer coverage. + """ + all_levels = set(range(5, 16)) # {5, 6, 7, ..., 15} + present_levels = set(int(x) for x in samples[:, 3]) + missing_levels = all_levels - present_levels + + if not missing_levels: + logger.info("All 11 hole_count levels represented โœ“") + return samples + + # Skip patching when sample size is too small to cover all levels + n_samples = len(samples) + if n_samples < len(all_levels): + logger.info( + "Only %d samples โ€” too few to cover all 11 hole_count levels " + "(need โ‰ฅ11). Skipping stratified patching.", + n_samples, + ) + return samples + + logger.warning( + "Missing hole_count levels: %s โ€” patching with replacements", + sorted(missing_levels), + ) + + for missing_level in sorted(missing_levels): + # Find the most over-represented level + unique, counts = np.unique(samples[:, 3].astype(int), return_counts=True) + most_common_idx = np.argmax(counts) + most_common_level = unique[most_common_idx] + + # Find indices with the most common level + candidates = np.where(samples[:, 3].astype(int) == most_common_level)[0] + replace_idx = rng.choice(candidates) + + # Replace: keep continuous DVs random within bounds, set hole_count + for j, dv in enumerate(DV_DEFINITIONS): + if dv.is_integer: + samples[replace_idx, j] = missing_level + else: + samples[replace_idx, j] = rng.uniform(dv.lower, dv.upper) + + logger.info( + " Replaced trial at idx %d: hole_count %d โ†’ %d", + replace_idx, most_common_level, missing_level, + ) + + return samples + + +def _log_coverage(points: list[DesignPoint]) -> None: + """Log hole_count coverage statistics.""" + counts: dict[int, int] = {} + for p in points: + counts[p.hole_count] = counts.get(p.hole_count, 0) + 1 + + logger.info("Hole count coverage:") + for level in range(5, 16): + n = counts.get(level, 0) + logger.info(" hole_count=%2d: %d trials", level, n) + + +def points_to_dicts(points: list[DesignPoint]) -> list[dict[str, Any]]: + """Convert DesignPoint list to list of dicts (for Optuna enqueue). + + Args: + points: List of DesignPoint instances. + + Returns: + List of dicts with DV names as keys. + """ + return [ + { + "beam_half_core_thickness": p.beam_half_core_thickness, + "beam_face_thickness": p.beam_face_thickness, + "holes_diameter": p.holes_diameter, + "hole_count": p.hole_count, + } + for p in points + ] + + +# --------------------------------------------------------------------------- +# Quick self-test +# --------------------------------------------------------------------------- +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") + + points = generate_lhs_samples(n_samples=50, seed=42) + print(f"\nGenerated {len(points)} total points") + print(f" Trial 0 (baseline): {points[0]}") + print(f" Trial 1 (first LHS): {points[1]}") + print(f" Trial {len(points)-1} (last LHS): {points[-1]}") + + # Verify coverage + hole_counts = {p.hole_count for p in points} + expected = set(range(5, 16)) + assert hole_counts == expected, ( + f"Missing hole_count levels: {expected - hole_counts}" + ) + print("\nAll 11 hole_count levels covered โœ“") + + # Verify bounds + for p in points[1:]: # skip baseline + assert 10.0 <= p.beam_half_core_thickness <= 40.0 + assert 10.0 <= p.beam_face_thickness <= 40.0 + assert 150.0 <= p.holes_diameter <= 450.0 + assert 5 <= p.hole_count <= 15 + print("All samples within bounds โœ“") diff --git a/projects/hydrotech-beam/studies/01_doe_landscape/sampling.sync-conflict-20260214-191800-VBCUD7Z.py b/projects/hydrotech-beam/studies/01_doe_landscape/sampling.sync-conflict-20260214-191800-VBCUD7Z.py new file mode 100644 index 00000000..4cfa6c0c --- /dev/null +++ b/projects/hydrotech-beam/studies/01_doe_landscape/sampling.sync-conflict-20260214-191800-VBCUD7Z.py @@ -0,0 +1,274 @@ +"""Latin Hypercube Sampling for Hydrotech Beam DoE. + +Generates LHS sample points for the 4 design variables with: +- Maximin LHS for space-filling coverage +- Integer rounding for hole_count (DV4) +- Stratified integer sampling to ensure all 11 hole_count levels are covered +- Baseline (Trial 0) always included + +References: + OPTIMIZATION_STRATEGY.md ยง2.3 โ€” Phase 1 configuration + OPTIMIZATION_STRATEGY.md ยง1.4 โ€” Integer handling +""" + +from __future__ import annotations + +import logging +from dataclasses import dataclass +from typing import Any + +import numpy as np +from scipy.stats.qmc import LatinHypercube + +from geometric_checks import DesignPoint + +logger = logging.getLogger(__name__) + + +# --------------------------------------------------------------------------- +# Design variable bounds +# --------------------------------------------------------------------------- +@dataclass(frozen=True) +class DVBounds: + """Design variable bounds and metadata.""" + + name: str + nx_expression: str + lower: float + upper: float + baseline: float + is_integer: bool = False + + +DV_DEFINITIONS: list[DVBounds] = [ + DVBounds("beam_half_core_thickness", "beam_half_core_thickness", 10.0, 40.0, 25.162), + DVBounds("beam_face_thickness", "beam_face_thickness", 10.0, 40.0, 21.504), + DVBounds("holes_diameter", "holes_diameter", 150.0, 450.0, 300.0), + DVBounds("hole_count", "hole_count", 5.0, 15.0, 10.0, is_integer=True), +] + +N_DVS = len(DV_DEFINITIONS) +BASELINE_VALUES = [dv.baseline for dv in DV_DEFINITIONS] + + +def get_baseline_point() -> DesignPoint: + """Return the baseline design point (Trial 0). + + Returns: + DesignPoint with confirmed baseline values from NX introspection. + """ + return DesignPoint( + beam_half_core_thickness=25.162, + beam_face_thickness=21.504, + holes_diameter=300.0, + hole_count=10, + ) + + +def generate_lhs_samples( + n_samples: int = 50, + seed: int = 42, + include_baseline: bool = True, +) -> list[DesignPoint]: + """Generate LHS sample points for the DoE study. + + Strategy for integer coverage (hole_count = 5..15, 11 levels): + 1. Generate `n_samples` LHS points with continuous DV4 + 2. Round DV4 to nearest integer + 3. Check coverage โ€” if any of the 11 levels are missing, replace + the closest duplicate trial with the missing level + This ensures all integer levels are represented while maintaining + the space-filling property of LHS for the continuous variables. + + Args: + n_samples: Number of LHS sample points (default: 50). + seed: Random seed for reproducibility. + include_baseline: If True, prepend baseline as Trial 0. + + Returns: + List of DesignPoint instances. If include_baseline is True, + the first element is the baseline (Trial 0). + """ + logger.info( + "Generating %d LHS samples (seed=%d, baseline=%s)", + n_samples, seed, include_baseline, + ) + + # Generate unit hypercube LHS samples + sampler = LatinHypercube(d=N_DVS, seed=seed, optimization="random-cd") + unit_samples = sampler.random(n=n_samples) # shape: (n_samples, 4) + + # Scale to design variable bounds + samples = _scale_samples(unit_samples) + + # Round hole_count to nearest integer + samples[:, 3] = np.round(samples[:, 3]).astype(int) + samples[:, 3] = np.clip(samples[:, 3], 5, 15) + + # Ensure full integer coverage for hole_count + samples = _ensure_integer_coverage(samples, rng=np.random.default_rng(seed)) + + # Convert to DesignPoint list + points: list[DesignPoint] = [] + if include_baseline: + points.append(get_baseline_point()) + + for row in samples: + points.append( + DesignPoint( + beam_half_core_thickness=float(row[0]), + beam_face_thickness=float(row[1]), + holes_diameter=float(row[2]), + hole_count=int(row[3]), + ) + ) + + logger.info( + "Generated %d total points (%d LHS + %s baseline)", + len(points), + n_samples, + "1" if include_baseline else "0", + ) + _log_coverage(points) + + return points + + +def _scale_samples(unit_samples: np.ndarray) -> np.ndarray: + """Scale unit hypercube [0,1]^d samples to design variable bounds. + + Args: + unit_samples: Array of shape (n, 4) with values in [0, 1]. + + Returns: + Scaled array with values in [lower, upper] for each DV. + """ + lower = np.array([dv.lower for dv in DV_DEFINITIONS]) + upper = np.array([dv.upper for dv in DV_DEFINITIONS]) + return lower + unit_samples * (upper - lower) + + +def _ensure_integer_coverage( + samples: np.ndarray, + rng: np.random.Generator, +) -> np.ndarray: + """Ensure all 11 hole_count levels (5-15) are represented. + + If any integer level is missing, replace a duplicate from the most + over-represented level with a sample at the missing level. + Continuous DVs for replacement samples are drawn randomly within bounds. + + Args: + samples: Array of shape (n, 4) with rounded hole_count in col 3. + rng: NumPy random generator for reproducibility. + + Returns: + Modified samples array with full integer coverage. + """ + all_levels = set(range(5, 16)) # {5, 6, 7, ..., 15} + present_levels = set(int(x) for x in samples[:, 3]) + missing_levels = all_levels - present_levels + + if not missing_levels: + logger.info("All 11 hole_count levels represented โœ“") + return samples + + # Skip patching when sample size is too small to cover all levels + n_samples = len(samples) + if n_samples < len(all_levels): + logger.info( + "Only %d samples โ€” too few to cover all 11 hole_count levels " + "(need โ‰ฅ11). Skipping stratified patching.", + n_samples, + ) + return samples + + logger.warning( + "Missing hole_count levels: %s โ€” patching with replacements", + sorted(missing_levels), + ) + + for missing_level in sorted(missing_levels): + # Find the most over-represented level + unique, counts = np.unique(samples[:, 3].astype(int), return_counts=True) + most_common_idx = np.argmax(counts) + most_common_level = unique[most_common_idx] + + # Find indices with the most common level + candidates = np.where(samples[:, 3].astype(int) == most_common_level)[0] + replace_idx = rng.choice(candidates) + + # Replace: keep continuous DVs random within bounds, set hole_count + for j, dv in enumerate(DV_DEFINITIONS): + if dv.is_integer: + samples[replace_idx, j] = missing_level + else: + samples[replace_idx, j] = rng.uniform(dv.lower, dv.upper) + + logger.info( + " Replaced trial at idx %d: hole_count %d โ†’ %d", + replace_idx, most_common_level, missing_level, + ) + + return samples + + +def _log_coverage(points: list[DesignPoint]) -> None: + """Log hole_count coverage statistics.""" + counts: dict[int, int] = {} + for p in points: + counts[p.hole_count] = counts.get(p.hole_count, 0) + 1 + + logger.info("Hole count coverage:") + for level in range(5, 16): + n = counts.get(level, 0) + logger.info(" hole_count=%2d: %d trials", level, n) + + +def points_to_dicts(points: list[DesignPoint]) -> list[dict[str, Any]]: + """Convert DesignPoint list to list of dicts (for Optuna enqueue). + + Args: + points: List of DesignPoint instances. + + Returns: + List of dicts with DV names as keys. + """ + return [ + { + "beam_half_core_thickness": p.beam_half_core_thickness, + "beam_face_thickness": p.beam_face_thickness, + "holes_diameter": p.holes_diameter, + "hole_count": p.hole_count, + } + for p in points + ] + + +# --------------------------------------------------------------------------- +# Quick self-test +# --------------------------------------------------------------------------- +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") + + points = generate_lhs_samples(n_samples=50, seed=42) + print(f"\nGenerated {len(points)} total points") + print(f" Trial 0 (baseline): {points[0]}") + print(f" Trial 1 (first LHS): {points[1]}") + print(f" Trial {len(points)-1} (last LHS): {points[-1]}") + + # Verify coverage + hole_counts = {p.hole_count for p in points} + expected = set(range(5, 16)) + assert hole_counts == expected, ( + f"Missing hole_count levels: {expected - hole_counts}" + ) + print("\nAll 11 hole_count levels covered โœ“") + + # Verify bounds + for p in points[1:]: # skip baseline + assert 10.0 <= p.beam_half_core_thickness <= 40.0 + assert 10.0 <= p.beam_face_thickness <= 40.0 + assert 150.0 <= p.holes_diameter <= 450.0 + assert 5 <= p.hole_count <= 15 + print("All samples within bounds โœ“")