66#include " simplnx/DataStructure/DataArray.hpp"
77#include " simplnx/DataStructure/Geometry/ImageGeom.hpp"
88#include " simplnx/Utilities/ImageRotationUtilities.hpp"
9+ #include " simplnx/Utilities/MessageHelper.hpp"
10+
11+ #include < limits>
912
1013#include < EbsdLib/Core/EbsdDataArray.hpp>
1114#include < EbsdLib/Core/Orientation.hpp>
@@ -34,29 +37,28 @@ ComputeFeatureReferenceCAxisMisorientations::~ComputeFeatureReferenceCAxisMisori
3437Result<> ComputeFeatureReferenceCAxisMisorientations::operator ()()
3538{
3639
37- /* **************************************************************************
38- * This section performs a sanity check to ensure that at least 1 phase is
39- * hexagonal.
40- */
40+ // Preflight: every ensemble index must resolve to a Hex Laue class for the filter to do anything.
41+ // We need to know both whether any phase is hex (else hard error) and whether all phases are hex
42+ // (else warn the user that non-hex phases will be skipped).
4143 const auto & crystalStructures = m_DataStructure.getDataRefAs <UInt32Array>(m_InputValues->CrystalStructuresArrayPath );
42- bool allPhasesHexagonal = true ;
43- bool noPhasesHexagonal = true ;
44+ bool anyPhaseIsHex = false ;
45+ bool allPhasesAreHex = true ;
4446 for (usize i = 1 ; i < crystalStructures.size (); ++i)
4547 {
4648 const auto crystalStructureType = crystalStructures[i];
4749 const bool isHex = crystalStructureType == ebsdlib::CrystalStructure::Hexagonal_High || crystalStructureType == ebsdlib::CrystalStructure::Hexagonal_Low;
48- allPhasesHexagonal = allPhasesHexagonal && isHex;
49- noPhasesHexagonal = noPhasesHexagonal && ! isHex;
50+ anyPhaseIsHex = anyPhaseIsHex || isHex;
51+ allPhasesAreHex = allPhasesAreHex && isHex;
5052 }
5153
52- if (noPhasesHexagonal )
54+ if (!anyPhaseIsHex )
5355 {
5456 return MakeErrorResult (
5557 -9802 , " Finding the feature reference c-axis misorientation requires at least one phase to be Hexagonal-Low 6/m or Hexagonal-High 6/mmm type crystal structures but none were found." );
5658 }
5759
5860 Result<> result;
59- if (!allPhasesHexagonal )
61+ if (!allPhasesAreHex )
6062 {
6163 result.warnings ().push_back (
6264 {-9803 ,
@@ -86,7 +88,7 @@ Result<> ComputeFeatureReferenceCAxisMisorientations::operator()()
8688
8789 const usize numQuatComps = quats.getNumberOfComponents ();
8890
89- std::vector<usize> counts (totalFeatures, 0ULL );
91+ std::vector<usize> counts (totalFeatures, 0 );
9092 std::vector<float32> avgMisorientations (totalFeatures, 0 .0f );
9193
9294 SizeVec3 uDims = m_DataStructure.getDataRefAs <ImageGeom>(m_InputValues->ImageGeometryPath ).getDimensions ();
@@ -158,26 +160,30 @@ Result<> ComputeFeatureReferenceCAxisMisorientations::operator()()
158160 }
159161 }
160162
161- // Loop over all the features from the feature attribute matrix and compute the
162- // average C Axis Misorientation for each feature
163+ // Per-feature average. Explicit NaN when no hex cells contributed (counts == 0); without this
164+ // guard, the division below would rely on IEEE 754 0/0 -> NaN, which is correct on every
165+ // platform we ship but fragile to FP-environment changes.
166+ MessageHelper messageHelper (m_MessageHandler);
167+ ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger ();
163168 for (usize featureId = 1 ; featureId < totalFeatures; featureId++)
164169 {
165- if (featureId % 1000 == 0 )
166- {
167- m_MessageHandler (IFilter::Message::Type::Info, fmt::format (" Working On Feature {} of {}" , featureId, totalFeatures));
168- }
169170 if (m_ShouldCancel)
170171 {
171172 return {};
172173 }
174+ throttledMessenger.sendThrottledMessage ([&] { return fmt::format (" Computing per-feature average {:.2f}% completed" , CalculatePercentComplete (featureId, totalFeatures)); });
173175
174- // Compute the average value of the misorientations between each feature's cell
175- // and the average C-Axis for that feature
176- featAvgCAxisMis[featureId] = avgMisorientations[featureId] / static_cast <float32>(counts[featureId]);
176+ if (counts[featureId] == 0 )
177+ {
178+ featAvgCAxisMis[featureId] = std::numeric_limits<float32>::quiet_NaN ();
179+ }
180+ else
181+ {
182+ featAvgCAxisMis[featureId] = avgMisorientations[featureId] / static_cast <float32>(counts[featureId]);
183+ }
177184 }
178185
179- // These 2 loops compute the population standard deviation of those misorientations for
180- // each feature.
186+ // Population standard deviation. Per-cell accumulate (diff^2) then per-feature sqrt(sum/count).
181187 std::vector<double > stdevs (totalFeatures, 0.0 );
182188 for (usize cellIdx = 0 ; cellIdx < totalPoints; cellIdx++)
183189 {
@@ -191,16 +197,22 @@ Result<> ComputeFeatureReferenceCAxisMisorientations::operator()()
191197 stdevs[featureId] += (diff * diff);
192198 }
193199
194- // Finish computing the standard deviation in this loop
195200 for (usize featureId = 1 ; featureId < totalFeatures; featureId++)
196201 {
197202 if (m_ShouldCancel)
198203 {
199204 return {};
200205 }
201206
202- featStdevCAxisMis[featureId] = std::sqrt (stdevs[featureId] / static_cast <double >(counts[featureId]));
207+ if (counts[featureId] == 0 )
208+ {
209+ featStdevCAxisMis[featureId] = std::numeric_limits<float32>::quiet_NaN ();
210+ }
211+ else
212+ {
213+ featStdevCAxisMis[featureId] = static_cast <float32>(std::sqrt (stdevs[featureId] / static_cast <double >(counts[featureId])));
214+ }
203215 }
204216
205- return {} ;
217+ return result ;
206218}
0 commit comments