# Dihedral-aware CAD GNN diagnostic

`graph_6000.npz` is the only graph used here: its requested node budget is
approximately 6,000 and its measured prepared CAD surface has 5,925 nodes.
Training, direct node-wise inference, viewer colors, and saved outputs use those
same 5,925 nodes.

The input has CAD geometry only: coordinates, normal, area, incident angle,
mean/Gaussian/principal curvature, boundary and feature/ridge flags. It does
not include node density. Each edge explicitly includes length, direction,
normal dot product, dihedral angle, boundary-edge flag and feature-edge flag.
The global condition contains XLS values plus observed/imputed flags; missing
heater readings use the already-prepared linear interpolation. Heater
footprints are not available, so these are not local CAD thermal fields.

Run the bounded diagnostic:

```bash
.venv/bin/python -m dihedral_gnn.run_experiment \
  --allow-diagnostic-weak-labels --epochs 56
```

Open the explainer through a local server, then visit the printed URL:

```bash
cd dihedral_gnn/outputs
../../.venv/bin/python -m http.server 8000
```

Open `http://localhost:8000/viewer.html`.

The explainer has Korean walkthrough stages, a single manipulable CAD
scene, selected node/edge numeric evidence, process/heater conditions, layer
switching, node picking/hover, normal/edge visibility, thresholding, and an
animated message-passing architecture overlay. It vendors Three.js locally in
`outputs/vendor/three.min.js`; it does not require an internet connection once
the server is running.

Important limits: both observations are NG `찍힘`, registration has not been
independently validated, defect areas are weak point-dilated labels, no held-out
sample exists, and no material, contact, FLC, local temperature field, actual
strain/stress, or tear measurement is supplied. Curvature and dihedral are
geometric cues only; neither constitutes a physical tear predictor.

Outputs:

- `input_processed/prepared_graph_5925.npz`: copied fixed prepared graph features
- `input_processed/feature_contract.json`: complete feature and validity contract
- `model/normal_baseline_gnn.pt`, `model/dihedral_aware_gnn.pt`: comparison checkpoints
- `outputs/prepared_graph_5925_direct_predictions.npz`: both direct model outputs at the same 5,925 nodes
- `outputs/report.json`, `loss_comparison.png`, and `fixed_6000_model_overlays.png`
- `outputs/viewer.html` and `viewer_data.json`: local interactive explainer

Run structural validation:

```bash
.venv/bin/python -m unittest dihedral_gnn.tests.test_validation
```
# Fixed prepared-graph comparison update

This experiment now uses **only** `data_processed/graphs/graph_6000.npz`: its requested budget is approximately 6,000 and its measured prepared-surface graph has **5,925 nodes**. Training, direct node-wise inference, viewer colors, and exported predictions all use those same 5,925 CAD nodes. The retired 11,956-node inference path is not part of this experiment.

The comparison is controlled: the normal baseline and dihedral-aware model use the same graph, process vectors, weak targets, random seed, epochs, losses, and 0.5 threshold. The normal baseline omits explicit discrete/analytic curvature, boundary/ridge, and face-dihedral channels; its remaining node/edge architecture is the same. This is a feature ablation, not an independent generalization test.

## Changed CAD

A model implementation can execute on a new mesh with a different topology and node count. For this approximately-6k protocol, each new CAD must first be independently simplified/prepared with the same graph rule. Node indices never correspond between two designs; output is computed directly on each new CAD node. Current evidence contains one CAD and two runs, so cross-design generalization is unmeasured. Before use on changed CAD, train across multiple designs, hold out CAD families during evaluation, and report out-of-distribution uncertainty. The current weak labels, unvalidated registration, and missing thermal/contact/material/FLC fields also prevent any claim of physical stress or tear prediction.

## Conceptual next model — not trained here

The desired explainable formulation is: CAD graph geometry encoder (curvature, face-dihedral, ridge and boundary) plus an XLS thermal/process encoder; add a **local thermal projection branch only when heater footprints are available**; fuse these branches into per-node segmentation/risk/deformation/thinning heads; then region-pool to defect type and physical area in mm².

The current dataset cannot identify separate “thermal risk” and “geometry risk” from one fused label. That requires branch-specific targets, controlled process variation, or concept supervision. It has one CAD, two NG runs, only 찍힘, and no observed defect area; a point-dilated weak mask is not physical area. Therefore this experiment does not train or claim tear type, defect area, thinning, causal stress, or causal sharp-bump→strain→tear explanation. Those claims require local temperature, thickness/material/FLC/contact data and measured strain/stress or simulation targets.

## Signed CAD curvature channels

The dihedral-aware branch additionally maps signed STEP analytic principal curvatures into the prepared graph only when the nearest analytic sample is within 3 mm and `abs(prepared_normal · parameter_normal) > 0.8`. Both principal signs are converted into the prepared-normal convention with `sign(dot)`, then ordered `k1 ≥ k2`; all other 3,624 of 5,925 nodes remain explicitly masked. Coverage is 2,301/5,925 (38.835%). It derives `H=(k1+k2)/2` (1/mm), `K=k1*k2` (1/mm²), curvedness `sqrt((k1²+k2²)/2)` (1/mm), and Koenderink shape index `(2/pi) atan2(k1+k2,k1-k2)`. Positive/negative H follows that local CAD-normal convention; `K<0` denotes saddle geometry. At sharp creases smooth curvature is undefined, so the model/viewer retains face dihedral instead. The normal baseline does not receive these channels.
