Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2e190a1
ENH: ColorKey ChoiceParameter on Compute(Face)IPFColors filters
imikejackson May 12, 2026
a580d89
ENH: HexConvention ChoiceParameter on WritePoleFigure filter
imikejackson May 12, 2026
1b0dd05
ENH: Pole Figure output is now a PNG file instead of a TIFF file.
imikejackson May 12, 2026
c43ec2f
BUG: WriteImageFilter preflight did not validate tuple dimensions
imikejackson May 12, 2026
909feac
BUG: WritePoleFigure filter bug fixes for Hex convention and unit te…
imikejackson May 13, 2026
d417422
VER: Update to EbsdLib version 3.0.0
imikejackson Jun 4, 2026
c5120ff
VV: Compute Feature Neighbor Misorientations & ComputeKernelAvgMisori…
imikejackson Jun 5, 2026
7e09a6c
VV: BadDataNeighborOrientationCheck + ComputeFeatureFaceMisorientatio…
imikejackson Jun 5, 2026
b93e823
VV: Compute Feature Neighbor C-Axis Misalignments fully V&V'ed
imikejackson Jun 5, 2026
6d46624
VV: Compute Grouping Density fully V&V'ed
imikejackson Jun 5, 2026
536b4f5
VV: Compute Average C-Axis Orientations fully V&V'ed
imikejackson Jun 5, 2026
cf15afd
Marking V&V filter's status as complete
imikejackson Jun 5, 2026
fbc03e2
COMP: Change to the real EbsdLib 3.0.0 release.
imikejackson Jun 5, 2026
4193972
BUG: Fixed bug where incorrect phase was used in a conditional and ca…
imikejackson Jun 7, 2026
afd06e2
REV: PR #1631 review feedback — code cleanup across V&V'ed filters
imikejackson Jun 7, 2026
ce3d290
DOC: PR #1631 review feedback — V&V report wordiness tightening
imikejackson Jun 7, 2026
1b4f312
REV: PR #1631 review round-2 — revert IPF Phase2 fix + cleanup pass
imikejackson Jun 7, 2026
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
6 changes: 6 additions & 0 deletions cmake/Plugin.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,14 @@ function(create_simplnx_plugin_unit_test)
#------------------------------------------------------------------------------
# Require that the test plugins are built before tests because some tests
# require loading from those plugins but don't want to link to them.
#------------------------------------------------------------------------------
add_dependencies(${UNIT_TEST_TARGET} ${ARGS_PLUGIN_NAME})

#------------------------------------------------------------------------------
# Require all test files be downloaded first before running tests
#------------------------------------------------------------------------------
add_dependencies(${UNIT_TEST_TARGET} Fetch_Remote_Data_Files)

set_target_properties(${UNIT_TEST_TARGET}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY $<TARGET_FILE_DIR:simplnx>
Expand Down
66 changes: 66 additions & 0 deletions docs/vv_templates/commit_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# V&V Commit Message Template

This is the standard commit message format for landing a completed V&V cycle on a SIMPLNX filter. It is used by the engineer at Phase 13 (status flip DRAFT → READY FOR REVIEW / COMPLETE), and is the commit that opens the PR.

## Format

```
VV: <Filter Human Name> fully V&V'ed

Summary:
- Found and fixed <N> bug(s) (<one-line per bug, or "Confirmed no bugs">);
- documented <M> deviation(s) from DREAM3D 6.5.171 (<D1 …, D2 …>);
- retired <P> tests (<reason: circular oracle / UNIMPLEMENTED stub / etc., or "none">);
- unit tests replaced with <Q> inlined *<Class X (Name) + Class Y (Name)>* test fixtures;
- added 3 V&V source-tree deliverables (report, deviations, provenance);
- <optional: doc/pipeline-name fixes, when applicable>.
```

## Title rules

- Must start with `VV:` (matches existing precedent — e.g., commit `99f3a9865` for `Compute Feature Reference Misorientations`).
- Use the filter's `humanName()` from the `.cpp` (with spaces, not the class name).
- End with `fully V&V'ed`. If the cycle is partial (rare — e.g., second-engineer oracle review still pending), end with `— READY FOR REVIEW` instead.

## Body rules

- One bullet per category, even when the count is zero. **Don't drop bullets** — write "Confirmed no bugs" or "no tests retired" so the reader knows the question was asked.
- Past-tense verbs at the start of each bullet ("Found and fixed", "documented", "retired", "replaced", "added", "fixed").
- Semicolons at the end of each bullet except the last (period on the last).
- Inline the deviation IDs (`D1`, `D2`, …) with a 2-3 word reminder of what each one was. The full text lives in `vv/deviations/<FilterName>.md`; the commit message just needs to be greppable.
- Oracle classes get the parenthetical name (`Class 1 (Analytical)`, `Class 4 (Invariant)`). See `oracle_classes.md` for the canonical class list.
- The "3 V&V source-tree deliverables" line is invariant across all V&V commits (every cycle produces exactly those 3). Don't rewrite it per-filter.

## Worked example

This is what the F#2 (`ComputeFeatureNeighborMisorientations`) V&V cycle's commit looks like:

```
VV: Compute Feature Neighbor Misorientations fully V&V'ed

Summary:
- Found and fixed 1 bug (divisor clobbered inside inner j-loop of algorithm.cpp);
- documented 2 deviations from DREAM3D 6.5.171 (D1 divisor bug, D2 EbsdLib 2.4.1 precision);
- retired 2 tests (circular-oracle exemplar consumer + UNIMPLEMENTED stub);
- unit tests replaced with 4 inlined *Class 1 (Analytical) + Class 4 (Invariant)* test fixtures;
- added 3 V&V source-tree deliverables (report, deviations, provenance);
- fixed pipeline-name typo in user-facing doc.
```

## When a category is zero or N/A

| Situation | Bullet phrasing |
|----------------------------------------------------|-----------------------------------------------------------------------|
| Clean Port, no bugs found | `Confirmed no bugs (clean Port);` |
| No deviations from legacy | `Confirmed no deviations from DREAM3D 6.5.171;` |
| Fresh V&V, no prior tests to retire | `No prior tests retired (fresh V&V cycle);` |
| Existing tests kept, only new ones added | `Augmented existing tests with <Q> inlined …;` |
| Doc was already correct | *omit the last bullet entirely (it's the only optional one)* |
| New filter, no legacy equivalent | `documented 0 deviations (no legacy equivalent — new filter);` |

## Why this format

- **Scannable.** A reviewer skimming `git log` should be able to decide whether a V&V commit is worth opening from the title and one bullet pass.
- **Greppable.** `git log --grep='^VV:'` returns every V&V commit. `git log --grep='D2 EbsdLib'` returns every commit that mentions the EbsdLib precision deviation. The deviation IDs are stable across commits (per the `<FilterName>-D<N>` rule in `deviation_template.md`).
- **Constrained.** The five fixed categories (bugs / deviations / retired tests / new fixtures / V&V deliverables) match what every V&V cycle produces under the v2 policy. The engineer fills slots rather than designing prose.
- **Cross-references the source-tree deliverables.** The commit is the "shortest legible record" of the V&V; the long-form analysis lives at `src/Plugins/<Plugin>/vv/{,deviations/,provenance/}<FilterName>.md`. A reader who wants more clicks through.
18 changes: 14 additions & 4 deletions docs/vv_templates/oracle_classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The five classes differ in **what they trust** (math, a library, a paper, proper

## Class 1 — Analytical

**What it is:** You can write down, on paper, the *exact* expected output as a closed-form function of the input. The math is small enough to do by hand or in a spreadsheet on a toy dataset.
**What it is:** You can write down, on paper, the *exact* expected output as a closed-form function of the input. The math is small enough to do by hand or in a spreadsheet on a hand-built fixture.

**Where the trust comes from:** Mathematics itself. Arithmetic doesn't drift. `2 + 2 == 4` today, tomorrow, and forever.

Expand All @@ -32,10 +32,20 @@ The five classes differ in **what they trust** (math, a library, a paper, proper
**Weaknesses:**

- Only works when the algorithm has a tractable closed form on small inputs. Many real algorithms don't (e.g., segmentation, optimization, iterative convergence).
- Toy datasets may not exercise all code paths in a complex filter.
- Hand-built fixtures may not exercise all code paths in a complex filter.

**How it's encoded in tests:** Usually inline `REQUIRE(result == 0.6428571)` with a comment showing the hand derivation. Or a tiny exemplar `.dream3d` whose values were hand-computed and the `.dream3d` is just a cache of the computation.

### Naming convention: `AnalyticalFixtures`

The C++ namespace used to hold Class 1 test scaffolding (helpers, struct, fixture builders) is `AnalyticalFixtures` — e.g., `AnalyticalFixtures::CreateScaffold`, `AnalyticalFixtures::FixtureData`.

**Why not `ToyFixtures`?** In CS / ML / testing terminology, "toy example" is the established term for a minimal hand-crafted dataset with a closed-form expected output. It is a precise technical signal — not dismissive — meaning *"this is an analytical oracle, not a regression-on-real-data circular oracle."* The original V&V scaffolding used `ToyFixtures` on those grounds.

**Why `AnalyticalFixtures` then?** SIMPLNX V&V deliverables read to a non-CS-academic audience (SBIR program managers, MTR reviewers, materials engineers). To that audience, "toy" can read as informal or unrigorous — the exact perception V&V is meant to dispel. `AnalyticalFixtures` preserves the load-bearing signal that "Toy" was carrying (*Class 1 closed-form oracle data*) without the dismissive connotation.

The rename was applied 2026-06-10 across all test files, V&V reports, deviations, provenance docs, and templates. The decision is documented here so it doesn't get re-litigated.

## Class 2 — Reference implementation

**What it is:** A trusted external library — one with broader user base, more testing, and longer history than your filter — computes the same operation on the same input. Its output becomes your expected output.
Expand All @@ -50,7 +60,7 @@ The five classes differ in **what they trust** (math, a library, a paper, proper
- Eigen for linear algebra in C++ tests.
- A reference Python implementation of an algorithm published as a research paper companion.

**Workflow:** Write a small Python (or other) script that takes the toy input, runs the reference library, and saves the expected output. That script + the library version + any random seed used become part of the V&V archive. Future tests load the saved expected output and compare bit-for-bit (or with appropriate float tolerance).
**Workflow:** Write a small Python (or other) script that takes the hand-built input, runs the reference library, and saves the expected output. That script + the library version + any random seed used become part of the V&V archive. Future tests load the saved expected output and compare bit-for-bit (or with appropriate float tolerance).

**Strengths:**

Expand Down Expand Up @@ -216,7 +226,7 @@ If the existing exemplar in the data archive was generated from one of the above

| Class | Name | Best for | Default choice when |
|---|---|---|---|
| 1 | Analytical | Tight algorithms with closed-form output, hand-pickable toy inputs, small outputs | The math is short enough to do on paper |
| 1 | Analytical | Tight algorithms with closed-form output, hand-pickable hand-built inputs, small outputs | The math is short enough to do on paper |
| 2 | Reference impl | Algorithms where a trusted library exists and you trust it more than your own implementation | You're porting an algorithm that has a well-known library implementation elsewhere |
| 3 | Paper-based | Implementations of published algorithms (orientation math, classical algorithms with named authors) | The filter's header cites a paper |
| 4 | Invariant | Anything with conservation laws, range bounds, or structural constraints | Always — add Class 4 alongside whatever other class you pick |
Expand Down
2 changes: 1 addition & 1 deletion docs/vv_templates/report_gates.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The dashboard a reviewer reads first. Lets a reviewer decide in 30 seconds wheth
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)
- 1 = Analytical (closed-form expected output on hand-built 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)
Expand Down
5 changes: 3 additions & 2 deletions docs/vv_templates/vv_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Every filter must be verified against at least one of the following. Classes 1

| # | Class | What it is | Drift risk |
|---|---|---|---|
| 1 | Analytical | Closed-form expected output on toy input (threshold, crop, rotate, color conversion, array arithmetic) | None |
| 1 | Analytical | Closed-form expected output on hand-built input (threshold, crop, rotate, color conversion, array arithmetic) | None |
| 2 | Reference implementation | Trusted external library produces the expected output (NumPy / SciPy / MTEX / EbsdLib upstream / Eigen) | **High** — library version drift |
| 3 | Paper-based | Filter reproduces a published figure, table, or equation from a named reference | Low–Medium |
| 4 | Invariant | Derivable properties the output must satisfy (FeatureIds start at 1 and contiguous; sum of phase fractions = 1; mass conservation) | None |
Expand Down Expand Up @@ -56,6 +56,7 @@ The verified state is pinned by **(commit hash, archive SHA512)**. The commit ca
| [`report_gates.md`](./report_gates.md) | Per-section "Done when:" checklists — reference while filling in the report |
| [`deviation_template.md`](./deviation_template.md) | Empty deviation file — copy into `src/Plugins/<P>/vv/deviations/<FilterName>.md` |
| [`provenance_template.md`](./provenance_template.md) | Empty exemplar-provenance sidecar — copy per exemplar archive |
| [`commit_template.md`](./commit_template.md) | Standard commit message format for landing a completed V&V cycle — use at step 6 of the engineer workflow below |

## Engineer workflow

Expand All @@ -64,7 +65,7 @@ The verified state is pinned by **(commit hash, archive SHA512)**. The commit ca
3. Run `python scripts/vv_init.py <FilterName>` to scaffold the report and deviation files in the plugin tree.
4. Open `report_gates.md` in a second tab.
5. Work each section in any order. A section is "done" when all its gates pass.
6. When all gates green, set `Status: READY FOR REVIEW`, push a `vv/<FilterName>` branch.
6. When all gates green, set `Status: READY FOR REVIEW`, push a `vv/<FilterName>` branch with a commit following [`commit_template.md`](./commit_template.md).
7. After sign-off, set `Status: COMPLETE`. Verified commit hash is filled in at SBIR deliverable assembly.

## Status tracking across filters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,18 @@ changing voxels.
### 2D Versus 3D Note

If the user is processing a 2D data set, **none** of the voxels can have 6 neighbors
since there are no neighbors is the +-Z directions.
since there are no neighbors in the +/-Z directions.

### Warning - Data Modification

Only the *Mask* value defining the cell as *good* or *bad* is changed. No other cell level array is modified.

### Memory Considerations

The filter allocates a temporary `int32` neighbor-count array sized to the total voxel count
(4 bytes per voxel). For a 1-billion-voxel dataset, that is approximately 4 GB of additional
working memory during execution. This memory is released when the filter finishes.

## Example Data

| Example Input Image | Example Output Image |
Expand All @@ -55,8 +61,12 @@ From the above before and after images you can see that this filter can help mod
## Example Pipelines

+ (02) Small IN100 Full Reconstruction
+ INL Export
+ 04_Steiner Compact

## Related Filters

- [Fill Bad Data](../SimplnxCore/FillBadDataFilter.md) — fills voxels still marked bad after this filter runs (or as a standalone alternative when no orientation data is available).
- [Multi-Threshold Objects](../SimplnxCore/MultiThresholdObjectsFilter.md) — typical upstream filter that generates the initial *Mask* array (e.g., from `Confidence Index` and `Image Quality`).
- [Replace Element Attributes with Neighbor Values](../SimplnxCore/ReplaceElementAttributesWithNeighborValuesFilter.md) — alternative cleanup approach that copies attribute values from neighboring cells rather than flipping a mask.

## License & Copyright

Expand Down
43 changes: 29 additions & 14 deletions src/Plugins/OrientationAnalysis/docs/ComputeAvgCAxesFilter.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,44 @@ Statistics (Crystallography)

## Description

This **Filter** determines the average C-axis location of each **Feature** by the following algorithm:
This **Filter** computes the average C-axis direction for each **Feature** (grain) in hexagonal materials. The result is a unit vector per **Feature** that indicates where the grain's C-axis points in the sample reference frame.

1. Gather all **Elements** that belong to the **Feature**
2. Determine the location of the c-axis in the sample *reference frame* for the rotated quaternions for all **Elements**. This is achieved by converting the input quaternion to
an orientation matrix (which represents a passive transform matrix), taking the transpose of the matrix to convert it from passive to active, and then multiplying the
transposed matrix by the crystallographic C-Axis direction vector <001>.
3. Average the locations and store the average for the **Feature**
### What is the C-Axis?

*Note:* This **Filter** will only work properly for *Hexagonal* materials. The **Filter** does not apply any symmetry
operators because there is only one c-axis (<001>) in *Hexagonal* materials and thus all symmetry operators will leave
the c-axis in the same position in the sample *reference frame*. However, in *Cubic* materials, for example, the {100}
family of directions are all equivalent and the <001> direction will change location in the sample *reference frame* when
symmetry operators are applied.
In hexagonal crystal structures (such as titanium, magnesium, and zinc), the *C-axis* is the unique crystallographic direction that runs along the long axis of the hexagonal unit cell (the [001] direction). This axis is important because many mechanical and physical properties of hexagonal materials vary depending on whether they are measured along or perpendicular to the C-axis.

This filter will error out if **ALL** phases are non-hexagonal. Any non-hexagonal phases will have their computed values set to NaN value.
![Fig. 1: The C-axis in a hexagonal unit cell.](Images/ComputeAvgCAxes_HexagonalCAxis.png)

The output is a direction vector for each feature.
### How This Filter Works

Each **Cell** (voxel) in a grain has its own measured orientation. This filter determines where each cell's C-axis points in the physical sample coordinate system, then averages those directions across all cells belonging to the same **Feature**:

1. For each **Cell**, the filter uses the cell's orientation (quaternion) to rotate the crystal [001] direction into the sample reference frame.
2. The rotated C-axis directions are accumulated for each **Feature**, ensuring all directions point into the same hemisphere for a consistent average.
3. The accumulated directions are normalized to produce a unit vector for each **Feature**.

### Hexagonal Materials Only

This filter only produces valid results for hexagonal phases (6/mmm or 6/m symmetry). In hexagonal materials, the C-axis is unique -- there is only one [001] direction, so crystal symmetry does not create ambiguity.

In cubic materials, the [001], [010], and [100] directions are all crystallographically equivalent. Applying symmetry operators would move the [001] direction to different positions in the sample frame, making a simple average meaningless. For this reason, the filter **silently skips non-hexagonal cells** during accumulation. A **Feature** whose contributing cells are *all* non-hexagonal (or which has no cells assigned to it at all) will have its output set to **NaN**.

The filter will produce an error if no hexagonal phases are present in the data (`-76402`: "No phases that have a crystal symmetry of Hexagonal (6/mmm or 6/m) were found.").

### Required Input Sources

This filter operates on previously segmented data and requires that several prior operations have already been run:

- **Cell Quaternions** -- typically read from EBSD data via [Read H5EBSD](ReadH5EbsdFilter.md), [Read CTF Data](ReadCtfDataFilter.md), or [Read ANG Data](ReadAngDataFilter.md); can also be produced from Euler angles by [Convert Orientations](ConvertOrientationsFilter.md).
- **Cell Feature Ids** -- produced by a segmentation filter such as [Segment Features (Misorientation)](EBSDSegmentFeaturesFilter.md) or [Segment Features (C-Axis Misalignment)](CAxisSegmentFeaturesFilter.md).
- **Cell Phases** -- typically read from EBSD data alongside the quaternions.
- **Crystal Structures** -- ensemble-level array read from EBSD data or created by [Create Ensemble Info](CreateEnsembleInfoFilter.md).

% Auto generated parameter table will be inserted here

## Example Pipelines

EBSD_Hexagonal_Data_Analysis
+ `EBSD_Hexagonal_Data_Analysis`

## License & Copyright

Expand Down
Loading
Loading