Skip to content

Commit 69fb0ae

Browse files
committed
TEST: Add 200x200x200 benchmark for NeighborOrientationCorrelation
Adds a dedicated benchmark test case with programmatic block-based EBSD data (25^3 grain blocks, low CI at boundaries, cubic crystal structure). Benchmark results: IC 2.19s→1.74s (1.26x), OOC 67.94s→18.62s (3.65x). Signed-off-by: Joey Kleingers <joey.kleingers@bluequartz.net>
1 parent 9d730a2 commit 69fb0ae

1 file changed

Lines changed: 109 additions & 0 deletions

File tree

src/Plugins/OrientationAnalysis/test/NeighborOrientationCorrelationTest.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@
33
#include "OrientationAnalysisTestUtils.hpp"
44

55
#include "simplnx/Core/Application.hpp"
6+
#include "simplnx/DataStructure/AttributeMatrix.hpp"
7+
#include "simplnx/DataStructure/Geometry/ImageGeom.hpp"
68
#include "simplnx/Parameters/ArraySelectionParameter.hpp"
79
#include "simplnx/Parameters/BoolParameter.hpp"
810
#include "simplnx/Parameters/Dream3dImportParameter.hpp"
911
#include "simplnx/Parameters/GeometrySelectionParameter.hpp"
12+
#include "simplnx/Parameters/MultiArraySelectionParameter.hpp"
1013
#include "simplnx/UnitTest/UnitTestCommon.hpp"
1114

1215
#include <catch2/catch.hpp>
1316

17+
#include <cmath>
1418
#include <filesystem>
1519

1620
namespace fs = std::filesystem;
@@ -196,3 +200,108 @@ TEST_CASE("OrientationAnalysis::NeighborOrientationCorrelationFilter: Small IN10
196200

197201
UnitTest::CheckArraysInheritTupleDims(dataStructure, SmallIn100::k_TupleCheckIgnoredPaths);
198202
}
203+
204+
TEST_CASE("OrientationAnalysis::NeighborOrientationCorrelationFilter: Benchmark 200x200x200", "[OrientationAnalysis][NeighborOrientationCorrelationFilter][Benchmark]")
205+
{
206+
UnitTest::LoadPlugins();
207+
// 200x200x200, Quats (float32, 4-comp) => 200*200*4*4 = 640,000 bytes/slice
208+
const UnitTest::PreferencesSentinel prefsSentinel("Zarr", 640000, true);
209+
210+
constexpr usize kDimX = 200;
211+
constexpr usize kDimY = 200;
212+
constexpr usize kDimZ = 200;
213+
constexpr usize kTotalVoxels = kDimX * kDimY * kDimZ;
214+
const ShapeType cellTupleShape = {kDimZ, kDimY, kDimX};
215+
const auto benchmarkFile = fs::path(fmt::format("{}/neighbor_orientation_correlation_benchmark.dream3d", unit_test::k_BinaryTestOutputDir));
216+
217+
// Stage 1: Build data programmatically and write to .dream3d
218+
{
219+
DataStructure buildDS;
220+
auto* imageGeom = ImageGeom::Create(buildDS, "Image Geometry");
221+
imageGeom->setDimensions({kDimX, kDimY, kDimZ});
222+
imageGeom->setSpacing({1.0f, 1.0f, 1.0f});
223+
imageGeom->setOrigin({0.0f, 0.0f, 0.0f});
224+
225+
auto* cellAM = AttributeMatrix::Create(buildDS, "Cell Data", cellTupleShape, imageGeom->getId());
226+
imageGeom->setCellData(*cellAM);
227+
228+
// Quaternions: block-based orientations with noise at boundaries
229+
auto* quatsArray = UnitTest::CreateTestDataArray<float32>(buildDS, "Quats", cellTupleShape, {4}, cellAM->getId());
230+
auto& quatsStore = quatsArray->getDataStoreRef();
231+
232+
// Phases: all phase 1 (cubic)
233+
auto* phasesArray = UnitTest::CreateTestDataArray<int32>(buildDS, "Phases", cellTupleShape, {1}, cellAM->getId());
234+
auto& phasesStore = phasesArray->getDataStoreRef();
235+
236+
// Confidence Index: low at block boundaries, high inside blocks
237+
auto* ciArray = UnitTest::CreateTestDataArray<float32>(buildDS, "Confidence Index", cellTupleShape, {1}, cellAM->getId());
238+
auto& ciStore = ciArray->getDataStoreRef();
239+
240+
constexpr usize kBlockSize = 25;
241+
constexpr usize kBlocksPerDim = kDimX / kBlockSize;
242+
for(usize z = 0; z < kDimZ; z++)
243+
{
244+
for(usize y = 0; y < kDimY; y++)
245+
{
246+
for(usize x = 0; x < kDimX; x++)
247+
{
248+
const usize idx = z * kDimX * kDimY + y * kDimX + x;
249+
phasesStore[idx] = 1;
250+
251+
// Block-based orientation: each 25^3 block gets a distinct quaternion
252+
usize bx = x / kBlockSize;
253+
usize by = y / kBlockSize;
254+
usize bz = z / kBlockSize;
255+
float32 angle = static_cast<float32>(bz * kBlocksPerDim * kBlocksPerDim + by * kBlocksPerDim + bx) * 0.1f;
256+
float32 sinHalf = std::sin(angle * 0.5f);
257+
float32 cosHalf = std::cos(angle * 0.5f);
258+
259+
const usize qIdx = idx * 4;
260+
quatsStore[qIdx] = cosHalf;
261+
quatsStore[qIdx + 1] = sinHalf * 0.577350269f; // 1/sqrt(3)
262+
quatsStore[qIdx + 2] = sinHalf * 0.577350269f;
263+
quatsStore[qIdx + 3] = sinHalf * 0.577350269f;
264+
265+
// Low CI at block boundaries (~10% of voxels), high CI inside
266+
bool isBoundary = (x % kBlockSize == 0) || (y % kBlockSize == 0) || (z % kBlockSize == 0);
267+
bool isNoisy = ((x * 7 + y * 13 + z * 29) % 10 == 0);
268+
ciStore[idx] = (isBoundary || isNoisy) ? 0.05f : 0.9f;
269+
}
270+
}
271+
}
272+
273+
// Ensemble data: Crystal Structures (1 entry for phase 0 = unknown, 1 entry for phase 1 = cubic)
274+
auto* ensembleAM = AttributeMatrix::Create(buildDS, "Ensemble Data", {2}, imageGeom->getId());
275+
auto* crystalStructuresArray = UnitTest::CreateTestDataArray<uint32>(buildDS, "CrystalStructures", {2}, {1}, ensembleAM->getId());
276+
auto& crystalStructuresStore = crystalStructuresArray->getDataStoreRef();
277+
crystalStructuresStore[0] = 999; // Unknown
278+
crystalStructuresStore[1] = 1; // Cubic-High (m-3m)
279+
280+
UnitTest::WriteTestDataStructure(buildDS, benchmarkFile);
281+
}
282+
283+
// Stage 2: Reload (arrays become ZarrStore in OOC) and run filter
284+
DataStructure dataStructure = UnitTest::LoadDataStructure(benchmarkFile);
285+
286+
{
287+
const NeighborOrientationCorrelationFilter filter;
288+
Arguments args;
289+
290+
args.insertOrAssign(NeighborOrientationCorrelationFilter::k_ImageGeometryPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry"})));
291+
args.insertOrAssign(NeighborOrientationCorrelationFilter::k_MinConfidence_Key, std::make_any<float32>(0.2f));
292+
args.insertOrAssign(NeighborOrientationCorrelationFilter::k_MisorientationTolerance_Key, std::make_any<float32>(5.0f));
293+
args.insertOrAssign(NeighborOrientationCorrelationFilter::k_Level_Key, std::make_any<int32>(2));
294+
args.insertOrAssign(NeighborOrientationCorrelationFilter::k_CorrelationArrayPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry", "Cell Data", "Confidence Index"})));
295+
args.insertOrAssign(NeighborOrientationCorrelationFilter::k_CellPhasesArrayPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry", "Cell Data", "Phases"})));
296+
args.insertOrAssign(NeighborOrientationCorrelationFilter::k_QuatsArrayPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry", "Cell Data", "Quats"})));
297+
args.insertOrAssign(NeighborOrientationCorrelationFilter::k_CrystalStructuresArrayPath_Key, std::make_any<DataPath>(DataPath({"Image Geometry", "Ensemble Data", "CrystalStructures"})));
298+
args.insertOrAssign(NeighborOrientationCorrelationFilter::k_IgnoredDataArrayPaths_Key, std::make_any<MultiArraySelectionParameter::ValueType>(MultiArraySelectionParameter::ValueType{}));
299+
300+
auto preflightResult = filter.preflight(dataStructure, args);
301+
SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions);
302+
auto executeResult = filter.execute(dataStructure, args, nullptr, IFilter::MessageHandler{[](const IFilter::Message& message) { fmt::print("{}\n", message.message); }});
303+
SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result);
304+
}
305+
306+
fs::remove(benchmarkFile);
307+
}

0 commit comments

Comments
 (0)