feat(hydrotech-beam): Phase 1 LHS DoE study code

Implements the optimization study code for Phase 1 (LHS DoE) of the
Hydrotech Beam structural optimization.

Files added:
- run_doe.py: Main entry point — Optuna study with SQLite persistence,
  Deb's feasibility rules, CSV/JSON export, Phase 1→2 gate check
- sampling.py: 50-point LHS via scipy.stats.qmc with stratified integer
  sampling ensuring all 11 hole_count levels (5-15) are covered
- geometric_checks.py: Pre-flight feasibility filter — hole overlap
  (corrected formula: span/(n-1) - d ≥ 30mm) and web clearance checks
- nx_interface.py: NX automation module with stub solver for development
  and NXOpen template for Windows/dalidou integration
- requirements.txt: optuna, scipy, numpy, pandas

Key design decisions:
- Baseline enqueued as Trial 0 (LAC lesson)
- All 4 DV expression names from binary introspection (exact spelling)
- Pre-flight geometric filter saves compute and prevents NX crashes
- No surrogates (LAC lesson: direct FEA via TPE beats surrogate+L-BFGS)
- SQLite persistence enables resume after interruption

Tested end-to-end with stub solver: 51 trials, 12 geometric rejects,
39 solved, correct CSV/JSON output.

Ref: OPTIMIZATION_STRATEGY.md, auditor review 2026-02-10
This commit is contained in:
2026-02-10 22:15:06 +00:00
parent 94bff37a67
commit 017b90f11e
6 changed files with 1687 additions and 38 deletions

View File

@@ -4,7 +4,7 @@
## Purpose
Map the design space of the Hydrotech sandwich I-beam to identify feasible regions, characterize variable sensitivities, and converge on a minimum-mass design that satisfies displacement and stress constraints.
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
@@ -13,59 +13,175 @@ Map the design space of the Hydrotech sandwich I-beam to identify feasible regio
| **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) → Phase 2: TPE (60-100 trials) |
| **Total budget** | 114156 NX evaluations |
| **Estimated compute** | ~45 hours |
| **Status** | DRAFT — Awaiting pre-flight checks and review |
| **Algorithm** | Phase 1: LHS DoE (50 trials + 1 baseline) |
| **Total budget** | 51 evaluations |
| **Constraint handling** | Deb's feasibility rules (Optuna constraint interface) |
| **Status** | Code complete — ready for execution |
## Design Variables
## Design Variables (confirmed via NX binary introspection)
| ID | Name | Range | Type | Baseline |
|----|------|-------|------|----------|
| DV1 | `beam_half_core_thickness` | 1040 mm | Continuous | 20 mm |
| DV2 | `beam_face_thickness` | 1040 mm | Continuous | 20 mm |
| DV3 | `holes_diameter` | 150450 mm | Continuous | 300 mm |
| DV4 | `hole_count` | 515 | Integer | 10 |
| ID | NX Expression | Range | Type | Baseline | Unit |
|----|--------------|-------|------|----------|------|
| DV1 | `beam_half_core_thickness` | 1040 | Continuous | 25.162 | mm |
| DV2 | `beam_face_thickness` | 1040 | Continuous | 21.504 | mm |
| DV3 | `holes_diameter` | 150450 | Continuous | 300 | mm |
| DV4 | `hole_count` | 515 | Integer | 10 | — |
## Baseline Performance
## Usage
| Metric | Value | Constraint | Status |
|--------|-------|------------|--------|
| Mass | ~974 kg | minimize | Overbuilt |
| Tip displacement | ~22 mm | ≤ 10 mm | ❌ FAILS |
| VM stress | unknown | ≤ 130 MPa | ⚠️ TBD |
### Prerequisites
## Key Decisions
```bash
pip install -r requirements.txt
```
- **Single-objective** formulation (DEC-HB-001)
- **Two-phase** algorithm: LHS → TPE (DEC-HB-002)
- **True integer** handling for hole_count (DEC-HB-003)
- **Deb's feasibility rules** for constraint handling (infeasible baseline)
- **Baseline enqueued** as Trial 0 (LAC lesson)
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 NX):
python run_doe.py --backend nxopen --model-dir "C:/path/to/syncthing/Beam"
```
### 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 |
| `-v` | false | Verbose (DEBUG) logging |
## Files
| File | Description |
|------|-------------|
| `OPTIMIZATION_STRATEGY.md` | Full strategy document (problem formulation, algorithm selection, risk mitigation) |
| `atomizer_spec_draft.json` | AtomizerSpec configuration skeleton (DRAFT — open items must be resolved) |
| `README.md` | This file |
| `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` | NX automation module (stub + NXOpen template) |
| `requirements.txt` | Python dependencies |
| `OPTIMIZATION_STRATEGY.md` | Full strategy document |
| `results/` | Output directory (CSV, JSON, Optuna DB) |
## Open Items Before Execution
## Output Files
1. Beam web length needed (geometric feasibility of hole patterns)
2. Displacement extraction method (sensor vs .op2 node parsing)
3. Stress extraction scope (whole model vs element group)
4. Baseline stress measurement
5. SOL 101 runtime benchmark
6. Corner test validation (16 bound combinations)
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) |
## 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 NX solver (stub or NXOpen)
│ ├── NXStubSolver → synthetic results (development)
│ └── NXOpenSolver → real NX Nastran SOL 101 (production)
└── Optuna study
├── SQLite storage (resume support)
├── Enqueued trials (deterministic LHS)
└── Deb's feasibility rules (constraint interface)
```
## Pipeline per Trial
```
Trial N
├── 1. Suggest DVs (from enqueued LHS point)
├── 2. Geometric pre-check
│ ├── FAIL → record as infeasible, skip NX
│ └── PASS ↓
├── 3. NX evaluation
│ ├── Update expressions (exact NX names)
│ ├── Rebuild model
│ ├── Solve SOL 101
│ └── Extract: mass (p173), displacement, stress
├── 4. Record results + constraint violations
└── 5. Log to CSV + Optuna DB
```
## 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
The `nx_interface.py` module provides:
- **`NXStubSolver`** — synthetic results from simplified beam mechanics (for pipeline testing)
- **`NXOpenSolver`** — template for real NXOpen Python API integration (to be completed on Windows)
Expression names are exact from binary introspection. Critical: `beam_lenght` has a typo in NX (no 'h') — use exact spelling.
### Outputs to extract from NX:
| Output | Source | Unit | Notes |
|--------|--------|------|-------|
| Mass | Expression `p173` | kg | Direct read |
| Tip displacement | SOL 101 results | mm | TBD: sensor or .op2 parsing |
| Max VM stress | SOL 101 results | MPa | ⚠️ pyNastran returns kPa — divide by 1000 |
## References
- [BREAKDOWN.md](../../BREAKDOWN.md) — Tech Lead's technical analysis
- [DECISIONS.md](../../DECISIONS.md) — Decision log
- [CONTEXT.md](../../CONTEXT.md) — Project context
- [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
---
*Created by ⚡ Optimizer Agent | 2026-02-09*
*Code: 🏗️ Study Builder (Technical Lead) | Strategy: ⚡ Optimizer Agent | 2026-02-10*