Skip to content

Commit 0f46063

Browse files
committed
ENH: Add optional polar coloring output to MTRSimFilter
* Implement MTRSim::applyPolarColoring using IPFMapper with the MatLab HCP colour scheme and Z-axis reference direction * Call applyPolarColoring from operator() when generatePolarColoring is true; short-circuits on error; leaves no array when the flag is false * Add two unit tests: polar ON verifies array shape (3 components, 10000 tuples) and non-zero content; polar OFF asserts array is absent Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
1 parent 3c70392 commit 0f46063

3 files changed

Lines changed: 104 additions & 2 deletions

File tree

src/MTRSim/Filters/Algorithms/MTRSim.cpp

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
#include "simplnx/DataStructure/DataArray.hpp"
44
#include "simplnx/DataStructure/Geometry/ImageGeom.hpp"
55

6+
#include "LibMTRSim/IPFMapper.hpp"
67
#include "LibMTRSim/MTRSimDriver.hpp"
78

9+
#include <Eigen/Dense>
810
#include <fmt/format.h>
911

1012
#include <exception>
@@ -102,7 +104,38 @@ Result<> MTRSim::operator()()
102104
}
103105

104106
m_MessageHandler(IFilter::Message::Type::Info, "MTR simulation complete.");
105-
// Polar coloring (Task 8) is handled in a later task; the optional array (if
106-
// requested) is left created-but-unfilled here and populated next task.
107+
108+
if(m_InputValues->generatePolarColoring)
109+
{
110+
m_MessageHandler(IFilter::Message::Type::Info, "Computing polar coloring...");
111+
auto colorResult = applyPolarColoring(sim, cellAm);
112+
if(colorResult.invalid())
113+
{
114+
return colorResult;
115+
}
116+
}
117+
118+
return {};
119+
}
120+
121+
// -----------------------------------------------------------------------------
122+
Result<> MTRSim::applyPolarColoring(const mtrsim::MTRSimResult& sim, const DataPath& cellAttrMatPath)
123+
{
124+
const std::size_t N = sim.phi1.size();
125+
Eigen::VectorXd phi1 = Eigen::Map<const Eigen::VectorXd>(sim.phi1.data(), static_cast<Eigen::Index>(N));
126+
Eigen::VectorXd phi = Eigen::Map<const Eigen::VectorXd>(sim.phi.data(), static_cast<Eigen::Index>(N));
127+
Eigen::VectorXd phi2 = Eigen::Map<const Eigen::VectorXd>(sim.phi2.data(), static_cast<Eigen::Index>(N));
128+
129+
mtrsim::IPFMapper mapper{mtrsim::CrystalSystem::HCP};
130+
const std::vector<mtrsim::RGBColor> colors = mapper.eulerToColors(phi1, phi, phi2, {0.0, 0.0, 1.0}, mtrsim::IPFColorScheme::MatLab);
131+
132+
auto& rgb = m_DataStructure.getDataRefAs<UInt8Array>(cellAttrMatPath.createChildPath(m_InputValues->polarColorsArrayName));
133+
auto& store = rgb.getDataStoreRef();
134+
for(std::size_t i = 0; i < N; ++i)
135+
{
136+
store[i * 3 + 0] = colors[i].r;
137+
store[i * 3 + 1] = colors[i].g;
138+
store[i * 3 + 2] = colors[i].b;
139+
}
107140
return {};
108141
}

src/MTRSim/Filters/Algorithms/MTRSim.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include "simplnx/DataStructure/DataStructure.hpp"
77
#include "simplnx/Filter/IFilter.hpp"
88

9+
#include "LibMTRSim/MTRSimDriver.hpp"
10+
911
#include <vector>
1012

1113
namespace nx::core
@@ -48,6 +50,7 @@ class MTRSIM_EXPORT MTRSim
4850
Result<> operator()();
4951

5052
private:
53+
Result<> applyPolarColoring(const mtrsim::MTRSimResult& sim, const DataPath& cellAttrMatPath);
5154
DataStructure& m_DataStructure;
5255
const MTRSimInputValues* m_InputValues = nullptr;
5356
const std::atomic_bool& m_ShouldCancel;

test/MTRSimTest.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,72 @@ TEST_CASE("MTRSim::MTRSimFilter: Rejects Volume Fraction not summing to 1.0", "[
248248
SIMPLNX_RESULT_REQUIRE_INVALID(preflightResult.outputActions);
249249
}
250250

251+
TEST_CASE("MTRSim::MTRSimFilter: Execute with polar coloring ON fills Polar Colors array", "[MTRSim][MTRSimFilter]")
252+
{
253+
UnitTest::LoadPlugins();
254+
255+
DataStructure dataStructure;
256+
const std::vector<DataPath> compPaths = BuildOdfDataStructure(dataStructure, 3);
257+
for(const auto& path : compPaths)
258+
{
259+
auto& arr = dataStructure.getDataRefAs<Float64Array>(path);
260+
arr.fill(1.0);
261+
}
262+
263+
MTRSimFilter filter;
264+
Arguments args = MakeValidArgs(compPaths);
265+
args.insertOrAssign(MTRSimFilter::k_GeneratePolarColoring_Key, true);
266+
args.insertOrAssign(MTRSimFilter::k_UseSeed_Key, true);
267+
args.insertOrAssign(MTRSimFilter::k_SeedValue_Key, static_cast<uint64>(42));
268+
269+
auto preflightResult = filter.preflight(dataStructure, args);
270+
SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions);
271+
272+
auto executeResult = filter.execute(dataStructure, args);
273+
SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result);
274+
275+
const DataPath cellAm = DataPath({"MTR Microstructure"}).createChildPath("Cell Data");
276+
constexpr usize expectedTuples = 100 * 100;
277+
278+
auto& rgb = dataStructure.getDataRefAs<UInt8Array>(cellAm.createChildPath("Polar Colors"));
279+
REQUIRE(rgb.getNumberOfComponents() == 3);
280+
REQUIRE(rgb.getNumberOfTuples() == expectedTuples);
281+
uint64 sum = 0;
282+
for(usize i = 0; i < rgb.getSize(); ++i)
283+
{
284+
sum += rgb[i];
285+
}
286+
REQUIRE(sum > 0);
287+
}
288+
289+
TEST_CASE("MTRSim::MTRSimFilter: Execute with polar coloring OFF omits Polar Colors array", "[MTRSim][MTRSimFilter]")
290+
{
291+
UnitTest::LoadPlugins();
292+
293+
DataStructure dataStructure;
294+
const std::vector<DataPath> compPaths = BuildOdfDataStructure(dataStructure, 3);
295+
for(const auto& path : compPaths)
296+
{
297+
auto& arr = dataStructure.getDataRefAs<Float64Array>(path);
298+
arr.fill(1.0);
299+
}
300+
301+
MTRSimFilter filter;
302+
Arguments args = MakeValidArgs(compPaths);
303+
// k_GeneratePolarColoring_Key is false by default in MakeValidArgs
304+
args.insertOrAssign(MTRSimFilter::k_UseSeed_Key, true);
305+
args.insertOrAssign(MTRSimFilter::k_SeedValue_Key, static_cast<uint64>(42));
306+
307+
auto preflightResult = filter.preflight(dataStructure, args);
308+
SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions);
309+
310+
auto executeResult = filter.execute(dataStructure, args);
311+
SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result);
312+
313+
const DataPath cellAm = DataPath({"MTR Microstructure"}).createChildPath("Cell Data");
314+
REQUIRE(dataStructure.getDataAs<UInt8Array>(cellAm.createChildPath("Polar Colors")) == nullptr);
315+
}
316+
251317
TEST_CASE("MTRSim::MTRSimFilter: Rejects Theta List rows with wrong column count", "[MTRSim][MTRSimFilter][ErrorPath]")
252318
{
253319
UnitTest::LoadPlugins();

0 commit comments

Comments
 (0)