Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ vcpkg_installed/
Workspace
CLAUDE.md
.claude
docs/superpowers
79 changes: 79 additions & 0 deletions docs/vv_templates/deviation_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Deviations from DREAM3D 6.5.171: <FilterName>

This file lists every documented behavioral difference between this SIMPLNX filter and its DREAM3D 6.5.171 equivalent.

Entries are referenced by stable ID (`<FilterName>-D<N>`) from the V&V report and from public migration guidance. The ID is stable across renames; the Filter UUID field is the permanent cross-reference anchor.

---

## <FilterName>-D1

| Field | Value |
|---|---|
| **Deviation ID** | `<FilterName>-D1` |
| **Filter UUID** | `<uuid>` |
| **Status** | active *or* superseded by `<FilterName>-D<M>` *or* retired YYYY-MM-DD |

**Symptom:** *<one-sentence user-visible symptom>*

**Root cause:** *bug | precision | order of operations | library | algorithmic choice*
*One paragraph explaining the technical mechanism. Cite source files and line ranges where helpful.*

**Affected users:** *<who actually sees this — e.g., "anyone with features spanning image boundaries", "only Hex-symmetry datasets", "datasets larger than 10M voxels">*

**Recommendation:** *trust SIMPLNX | trust 6.5.171 | either acceptable within tolerance X | see quick-patch link for legacy-parity*
*One sentence justifying the recommendation.*

---

## <FilterName>-D2

| Field | Value |
|---|---|
| **Deviation ID** | `<FilterName>-D2` |
| **Filter UUID** | `<uuid>` |
| **Status** | active |

**Symptom:** ...

**Root cause:** ...

**Affected users:** ...

**Recommendation:** ...

---

## Examples (delete this section before sign-off)

### Precision example

| Field | Value |
|---|---|
| **Deviation ID** | `ComputeEulerAngles-D1` |
| **Filter UUID** | `aaaa1111-0000-0000-0000-000000000001` *(illustrative)* |
| **Status** | active |

**Symptom:** Euler-angle output differs from 6.5.171 by up to 0.003° in orientations near grain boundaries.

**Root cause:** Precision. SIMPLNX performs the internal orientation-matrix operations in `double`; 6.5.171 performed the same operations in `float`.

**Affected users:** Workflows that compute orientation statistics on features larger than ~10⁴ voxels, where accumulated float32 round-off becomes visible at the 10⁻³ degree level. Users who only visualize IPF colors will not notice.

**Recommendation:** Trust SIMPLNX. The 6.5.171 output was limited by float32 round-off and is not materially more correct for any downstream calculation.

### Legacy-bug example

| Field | Value |
|---|---|
| **Deviation ID** | `SegmentFeatures-D2` |
| **Filter UUID** | `aaaa2222-0000-0000-0000-000000000002` *(illustrative)* |
| **Status** | active |

**Symptom:** FeatureId count on a 50×50×50 block test pattern is 27 in SIMPLNX and 26 in 6.5.171.

**Root cause:** Bug in 6.5.171. The outer segmentation loop used `< dimZ` where it should have used `<= dimZ`, silently dropping features that touched the +Z boundary. Corrected in SIMPLNX.

**Affected users:** Anyone who ran `SegmentFeatures` on datasets where a feature touched the +Z volume boundary. The missing feature was always the one nearest +Z.

**Recommendation:** Trust SIMPLNX. The 6.5.171 result was mathematically incorrect.
234 changes: 234 additions & 0 deletions docs/vv_templates/oracle_classes.md

Large diffs are not rendered by default.

75 changes: 75 additions & 0 deletions docs/vv_templates/provenance_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Exemplar Archive Provenance: <archive_name>.tar.gz

This sidecar records how an exemplar archive used in unit tests was generated. It is the answer to "where did this gold-standard data come from?"

One sidecar per archive. The archive name and SHA512 must match `download_test_data()` in `src/Plugins/<P>/test/CMakeLists.txt`.

---

## Archive identity

| Field | Value |
|---|---|
| **Archive** | `<archive_name>.tar.gz` |
| **SHA512** | `<copy from test/CMakeLists.txt>` |
| **Used by tests** | `<TestCaseName1>`, `<TestCaseName2>` |
| **Generated by** | *<engineer name>* |
| **Generated on** | *YYYY-MM-DD* |
| **Generated at commit** | *<commit hash where the generating pipeline / script lived>* |

## How it was generated

*Describe in 2–4 sentences how the archive's contents were produced. Reference any pipeline (`.d3dpipeline`) or script (`.py`, `.m`) by file path. State whether inputs were from a public dataset, synthetic generation, or hand-construction.*

Example:
> The archive contains a 100×100×100 synthetic microstructure generated by `pipelines/generate_in100_subvol.d3dpipeline` plus the expected output of `ComputeGroupingDensityFilter` on that input. Inputs were generated synthetically (no external data); expected output was computed by the Class 3 Rowenhorst 2015 §4.2 worked example.

## Canonical oracle output

*Which arrays in the archive are the canonical oracle output (the thing tests compare against)? Reference by DataPath.*

| DataPath | Source of expected values |
|---|---|
| `/<Group>/<Array>` | *Class 1 hand derivation* / *Class 2 script X.py* / *Class 3 paper ref* / *Class 4 invariant* / *Class 5 expert sign-off* |

## Oracle provenance (Classes 2, 3, 5 only)

*Classes 1 and 4 need no provenance block — the oracle lives in the test code directly.*

### Class 2 — Reference implementation

- **Library:** *<name>*
- **Exact version:** *<x.y.z (release date)>*
- **Runtime:** *<e.g., Python 3.11, Octave 8.2.0>*
- **Random seed:** *<seed, if any>*
- **Script in archive:** *<filename.py / filename.m>*
- *Optional:* **Output hash for drift detection:** *<sha256>*

### Class 3 — Paper-based

- **Citation:** *<authors, title, journal, vol/issue, pages, year>*
- **DOI:** *<doi>*
- **Edition:** *<edition #>*
- **Figure / Table / Equation #:** *<reference>*
- **Page #:** *<page>*
- **Paper PDF in archive:** *`notes/<short_name>.pdf`*
- **Reproduced figure (if any):** *`notes/<short_name>_figN.png`*

### Class 5 — Expert-visual

- **Approving expert:** *<name>*
- **Approval date:** *YYYY-MM-DD*
- **Signed-off outputs:** *`notes/expert_signoff/*.png`*
- **Class-5-only justification:** *Why no Class 1–4 oracle was feasible for this filter.*

## Second-engineer oracle review

- **Reviewer:** *<name>* OR *skipped*
- **Date:** *YYYY-MM-DD*
- **Skip reason** (if skipped): *<reason — typically "no second engineer realistically available">*

## Regenerated to fix a circular-oracle situation?

*If this archive was created to *replace* a prior archive that was regenerated from post-fix SIMPLNX output (a circular oracle), state that here with a reference to the prior archive.*

> *Example: this archive replaces `compute_grouping_density.tar.gz` (retired YYYY-MM-DD), which was regenerated from SIMPLNX output in `<PR#>` after the bug fix in `<PR#>` and therefore could not be used as an independent oracle.*
104 changes: 104 additions & 0 deletions docs/vv_templates/report_gates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# V&V Report — Section Gates

A section of `src/Plugins/<P>/vv/<FilterName>.md` is "done" when all its gates pass. Sections may be worked in any order.

**The only ordering rule:** the **Oracle** is chosen *before* any DREAM3D 6.5.171 comparison is run. 6.5.171 is never the source of truth — it is the *thing being compared against* the independently-established oracle.

---

## Header table

- [ ] Plugin, SIMPLNX UUID, legacy DREAM3D equivalent (or "None") filled in
- [ ] Status reflects current state: `DRAFT` | `READY FOR REVIEW` | `COMPLETE`
- [ ] Verified-commit field present (filled in at SBIR deliverable assembly)
- [ ] Sign-off line has named engineer(s) and date at sign-off

## At a glance

The dashboard a reviewer reads first. Lets a reviewer decide in 30 seconds whether they need to dig into the long-form sections below.

- [ ] All 8 rows present: Algorithm Relationship, Oracle (confirmed), Code paths enumerated, Tests today, Exemplar archive, Legacy comparison, Bug flags, V&V phase
- [ ] Each cell is one sentence to one short paragraph — not a single word, not a full subsection. If a row needs more than ~3 sentences, that detail belongs in the long-form section and the dashboard summarizes it
- [ ] **Algorithm Relationship** row names the legacy equivalent (or "no legacy equivalent") and the classification — must agree with the long-form `## Algorithm Relationship` section
- [ ] **Oracle (confirmed)** row names the Class number(s) and the encoded test fixture(s). Use "confirmed" only when the oracle has been applied and the test passes; otherwise write "tentative" or "in progress"
- [ ] **Code paths enumerated** row states `N of M exercised` — agrees with the long-form `## Code path coverage` count
- [ ] **Tests today** row gives the test-case count and a one-phrase shape of coverage (parameter sweep, positive/negative/conversion, etc.)
- [ ] **Exemplar archive** row names the archive and flags retired/replaced archives (cross-reference the long-form `## Exemplar archive` SHA512)
- [ ] **Legacy comparison** row is `Run` / `Not run` / `Three-way (SIMPLNX vs 6.5.171 vs 6.5.172)` plus a one-sentence headline. "Not run" must include a brief reason ("design-by-inspection — pure port", "legacy binary unavailable", "deferred to Phase 9")
- [ ] **Bug flags** row is `None` or a list of deviation IDs flagged as suspected bugs (not all deviations are bugs; only those classified as bug under the root-cause taxonomy)
- [ ] **V&V phase** row lists which phases of the workflow are complete and what is outstanding — drives the Status field in the header table

## Summary

- [ ] 2–3 sentences only
- [ ] States what the filter does
- [ ] States the verification approach (one phrase, e.g., "Class 3 paper-based vs Rowenhorst 2015")
- [ ] States the headline result (e.g., "1 deviation, all tests pass")

## Algorithm Relationship

- [ ] One classification: Port | Minor changes | Rewrite | New filter
- [ ] One-line evidence (UUID inheritance, PR history, line-count diff with legacy)
- [ ] **Rewrite + outputs diverge from legacy** → explicit defense required in the Deviations file (same UUID is a claim of functional equivalence)
- [ ] *Optional but encouraged for Port / Minor changes:* numbered list of **Port-time deltas** — API swaps, library version differences, progress-reporting changes, normalization steps added. Each delta gets one sentence justifying why it does or does not change output. Forces the engineer to *enumerate* the structural diff instead of asserting "it's a port" with no evidence.
- [ ] *Optional:* **Material PRs since baseline** line — `(none identified for this filter)` is a valid answer. The discipline of looking is what matters; it surfaces drift introduced after the last V&V pass.

## Oracle

For detailed explanations of each class — with examples, strengths and weaknesses, drift-risk analysis, and a decision tree for picking the right class — see [`oracle_classes.md`](./oracle_classes.md). The summary below is the gate checklist.

- [ ] Class named (1–5)
- 1 = Analytical (closed-form expected output on toy input)
- 2 = Reference implementation (NumPy / SciPy / MTEX / EbsdLib upstream)
- 3 = Paper-based (published figure / table / equation)
- 4 = Invariant-based (derivable property the output must satisfy)
- 5 = Expert-visual (last resort, requires justification)
- [ ] If Class 5: justification block stating why no Class 1–4 oracle was feasible
- [ ] One-line description of how oracle was applied
- [ ] Encoded test reference: `<file>::<TEST_CASE>` exists and is greppable
- [ ] N fixtures stated; all pass at the verified commit
- [ ] Second-engineer review of oracle design, OR documented skip reason

## Code path coverage

- [ ] `Source:` line cites the algorithm `.cpp` with line count (e.g., `Source: src/Plugins/<P>/.../Algorithms/<Name>.cpp (181 lines).`) — anchors the reader to the file being audited
- [ ] *Optional 1–2 sentences* naming the algorithm's logical phases when it has staged structure (e.g., "(a) preflight scan, (b) per-cell accumulation, (c) per-feature finalize") so the `Phase` column reads in context
- [ ] All algorithm code paths enumerated — kernel choices, mask on/off, edge cases, error paths, cancel paths, background/sentinel branches
- [ ] Paths numbered via the `#` column so they have stable IDs for referencing from Test inventory, Deviations, and review comments
- [ ] *Recommended:* `Phase` (or `Pass` / `Stage`) column groups paths when the algorithm has distinct stages; drop the column when the algorithm is flat
- [ ] `N of M paths exercised.` count stated at the top of the section
- [ ] If `N < M`: each uncovered path appears as its own table row with `*Not directly tested. <one-line reason>*` in the Test case cell — paths are **never silently omitted**. Acceptable reasons include: low-value loop-guard, exercised implicitly by shipping pipelines (name one), requires cancel-signal injection, deferred to integration test
- [ ] Each covered path maps to ≥1 named test case (`TEST_CASE` name or `DYNAMIC_SECTION` label as it appears in the test source)
- [ ] Parameter-dependent paths: every combination of interest represented (don't trust a single test case to cover the parameter cube)

## Test inventory

- [ ] Every `TEST_CASE` in the filter's test file listed (including `DYNAMIC_SECTION` variants that show up as separate ctest entries — list each one)
- [ ] Each marked: `kept` | `new-for-V&V` | `retired` (with one-line reason for retired)
- [ ] **Notes** column states what each test actually verifies (number of arrays compared, number of assertions, exemplar archive consumed, any expected-failure status). Don't leave Notes blank — "Validates 80 element-wise assertions against bundled exemplar" beats no entry
- [ ] If a test was modified for this V&V cycle (e.g., inline expected-array updates, exemplar bump), the Notes column records what changed and why (one line; cite the deviation ID if the change is traceable to one)
- [ ] All non-retired tests pass at the verified commit in **both** in-core and OOC builds

## Exemplar archive

- [ ] Archive name matches `download_test_data()` entry in `test/CMakeLists.txt`
- [ ] SHA512 in report matches SHA512 in `test/CMakeLists.txt`
- [ ] Provenance sidecar exists at `src/Plugins/<P>/vv/provenance/<archive>.md` and documents:
- who generated the archive
- when
- with what pipeline / script
- what oracle output was canonical
- [ ] If the archive was regenerated during V&V to fix a circular-oracle situation → documented in the sidecar

## Deviations from DREAM3D 6.5.171

- [ ] Comparison was run on at least one fixture (named in the report)
- [ ] If no deviations: a one-line confirmation that 6.5.171 and SIMPLNX outputs matched within tolerance
- [ ] If deviations: each ID referenced in the report points to a fleshed-out entry in `src/Plugins/<P>/vv/deviations/<FilterName>.md`
- [ ] Each deviation entry has:
- stable ID (`<FilterName>-D<N>`)
- filter UUID (permanent cross-reference anchor)
- symptom (user-visible)
- root cause: `bug` | `precision` | `order of operations` | `library` | `algorithmic choice`
- affected users
- recommendation: `trust SIMPLNX` | `trust 6.5.171` | `either acceptable within tolerance` | `see quick-patch link`
Loading
Loading