diff --git a/CMakeLists.txt b/CMakeLists.txt index d887159..41bea11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,10 +27,10 @@ set(${PLUGIN_NAME}_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}) # These are all the filters in the plugin. All filters should be kept in the # SimplnxReview/src/SimplnxReview/Filters/ directory. set(FilterList - ComputeGroupingDensityFilter ComputeLocalAverageCAxisMisalignmentsFilter ComputeMicroTextureRegionsFilter ComputeSaltykovSizesFilter + EMsoftSO3SamplerFilter GroupMicroTextureRegionsFilter InterpolateValuesToUnstructuredGridFilter MergeColoniesFilter @@ -44,10 +44,10 @@ set(ActionList # This should be integrated with the `create_simplnx_plugin` function call # ------------------------------------------------------------------------------ set(AlgorithmList - ComputeGroupingDensity ComputeLocalAverageCAxisMisalignments ComputeMicroTextureRegions ComputeSaltykovSizes + EMsoftSO3Sampler GroupMicroTextureRegions InterpolateValuesToUnstructuredGrid MergeColonies diff --git a/docs/EMsoftSO3SamplerFilter.md b/docs/EMsoftSO3SamplerFilter.md new file mode 100644 index 0000000..bd7cbb4 --- /dev/null +++ b/docs/EMsoftSO3SamplerFilter.md @@ -0,0 +1,107 @@ +# EMsoftSO3Sampler # + + +## Group (Subgroup) ## + +EMsoftToolbox (EMsoftToolbox) + +## Description ## + +The EMsoftSO3Sampler filter produces several different types of uniform samples of SO(3): + +| Mode | Description | +|------|-------------| +| 0 | a uniform sampling of a Rodrigues fundamental zone (FZ) | +| 1 | a uniform sampling of orientations at a constant misorientation from a given orientation | +| 2 | a uniform sampling of orientations at less than a given misorientation from a given orientation. | + +All three sampling methods are based on the cubochoric rotation representation, which starts with a cubical grid inside the cubochoric cube. This cube represents an equal-volume mapping of the quaternion Northern hemisphere (i.e., all 3D rotations with positive scalar quaternion component). For sampling mode 0, the filter creates a uniform grid of cubochoric vectors, transforms each vector to the Rodrigues representation and determines whether or not the point lies inside the FZ for the point group symmetry set by the user. The filter then returns an array of Euler angle triplets (Bunge convention) for use in subsequent filters. The sampling grid can be offset from the center of the cube, in which case the identity orientation will not be part of the sample. + +### Sampling Mode 0 + +The filter samples the surface of a centered cube inside the cubochoric cube and converts those points to a quadratic surface (prolate spheroid, spheroidal paraboloid, or double-sheet hyperboloid, depending on the parameter choices) in Rodrigues Space; all generated points will have the same misorientation with respect to a user defined reference point. In practical terms, this mode will sample the volume of the fundamental zone defined by the user selected Laue class. Points will be generated on the surface and within the fundamental zone volume. + +![](Images/so3_sample_mode_0.png) + +### Sampling Mode 1 + +The filter will sample at a constant misorientation from a user defined orientations resulting in points on a surface. + +![](Images/so3_sample_mode_1.png) + +### Sampling Mode 2 + +Sampling mode 2 does the same as mode 1, but now the inside of the starting cube is also filled with sampling points, leading to a uniform sampling of orientations surrounding a user defined orientation with up to a maximum misorientation with respect to that orientation. + +![](Images/so3_sample_mode_2.png) + + + +## DREAM3D-NX Laue Group to Point Group Table +| EbsdLib Laue Group | EbsdLib Laue Group Name | HM Sym | Point Group | +|--------------------|-------------------------|--------|-------------| +| 0 | Hexagonal_High | 6/mmm | 27 | +| 1 | Cubic_High | m-3m | 32 | +| 2 | Hexagonal_Low | 6/m | 23 | +| 3 | Cubic_Low | m-3 | 29 | +| 4 | Triclinic | -1 | 2 | +| 5 | Monoclinic | 2/m | 5 | +| 6 | OrthoRhombic | mmm | 8 | +| 7 | Tetragonal_Low | 4/m | 11 | +| 8 | Tetragonal_High | 4/mmm | 15 | +| 9 | Trigonal_Low | -3 | 17 | +| 10 | Trigonal_High | -3m | 20 | + + +## Point group identifiers ## + +Crystallographic point groups are identified by an integer from 1 to 32 according to the International Tables for Crystallography (Volume A). The valid numbers, along with the corresponding Hermann-Mauguin point group symbols (HM Sym), are listed here, along with the point group symbol between double quotation marks, the corresponding rotation group and its order M (in bold face): + +[Table adapted from http://pd.chem.ucl.ac.uk/pdnn/symm2/group32.htm](http://pd.chem.ucl.ac.uk/pdnn/symm2/group32.htm) + +| Laue Group | ID,HM Sym,Rot.Grp(Order) | ID,HM Sym,Rot.Grp(Order) | ID,HM Sym,Rot.Grp(Order) | ID,HM Sym,Rot.Grp(Order) | ID,HM Sym,Rot.Grp(Order) | ID,HM Sym,Rot.Grp(Order) | ID,HM Sym,Rot.Grp(Order) | +|------|------|------|------|------|------|------|------| +| Triclinic | [1], "1", **1(1)** | [2], "-1", **1(1)** | | | | | | +| Monoclinic | [3], "2", **2(2)**| [4], "m", **2(2)**| [5], "2/m", **2(2)**| | | | | +| Orthorhombic | [6], "222" , **222(4)**| [7], "mm2" , **222(4)**| [8], "mmm" , **222(4)**| | | | | +| Tetragonal | [9], "4" , **4(4)** | [10], "-4", **4(4)** | [11], "4/m", **4(4)** | [12], "422", **422(8)** | [13], "4mm", **422(8)** | [14], "-42m", **422(8)** | [15], "4/mmm", **422(8)** | +| Trigonal | [16], "3", **3(3)** | [17], "-3", **3(3)** | [18], "32", **32(6)** | [19], "3m" , **32(6)** | [20], "-3m", **32(6)** | | | +| Hexagonal | [21], "6", **6(6)** | [22], "-6" , **6(6)** | [23], "6/m", **6(6)** | [24], "622", **622(12)** | [25], "6mm", **622(12)** | [26], "-6m2", **622(12)** | [27], "6/mmm", **622(12)** | +| Cubic | [28], "23", **23(12)**| [29], "m-3", **23(12)** | [30], "432", **432(24)** | [31], "-43m", **432(24)** | [32], "m-3m", **432(24)** | | | | + + + +## Number of grid points ## + +The cubo-choric space is a cube with edge length pi^(2/3) and origin (the identity rotation) at the center of the cube. The number of sampling points entered by the user represents the number of grid points **along a semi-edge of the cube**. In other words, if the user requests N=50 sampling points, and the origin is part of the grid (see next item), then there will be 2N+1 actual sampling points along each cube edge. The total number of sampling points in the grid will then be (2N+1)^3. For point groups #1 and #2, in the absence of any rotational symmetry, the Rodrigues FZ will correspond to the full cubochoric grid, with (2N+1)^3 grid points. For any other point group, the Rodrigues FZ will correspond to a portion of the cubochoric grid, and the number of points will be approximately given by (2N+1)^3 /M, where M is the order of the rotation group corresponding to the point group. In the table above, the rotation group and its order are indicated in bold face. + +## Grid offset switch ## + +For sampling mode 0, the user has the option to offset the cubochoric grid from the origin by half a grid unit. In that case, the grid will have a maximum of 8N^3 grid points, and the identity rotation will **not** be part of the sample. The total number of points inside the Rodrigues FZ will then be approximately 8N^3 /M. + +## Misorientation sampling ## + +For sampling modes 1 and 2, the user must provide a reference orientation in the form of an Euler angle triplet (Bunge convention); this orientation will be used as the reference orientation around which the misorientation sampling will be computed. The output of all three sampling modes will be in Euler angles. + +## Funding Acknowledgment ## + +This filter was developed with financial support from contract . + +% Auto generated parameter table will be inserted here + +## References + +Detailed information on the cubo-choric rotation representation can be found in the following paper: D. Rosca, A. Morawiec, and M. De Graef. **"A new method of constructing a grid in the space of 3D rotations and its applications to texture analysis,"** _Modeling and Simulations in Materials Science and Engineering **22**, 075013 (2014)._ + +Details on the misorientation sampling approach can be found in the following paper: S. Singh and M. De Graef, **"Orientation sampling for dictionary-based diffraction pattern indexing methods"** submitted to _MSMSE (2016)_. + +## Example Pipelines ## + + +## License & Copyright + +Please see the description file distributed with this **Plugin** + +## DREAM3D-NX Help + +If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues/discussions) GitHub site where the community of DREAM3D-NX users can help answer your questions. diff --git a/docs/Images/so3_sample_mode_0.png b/docs/Images/so3_sample_mode_0.png new file mode 100644 index 0000000..7c56ccd Binary files /dev/null and b/docs/Images/so3_sample_mode_0.png differ diff --git a/docs/Images/so3_sample_mode_1.png b/docs/Images/so3_sample_mode_1.png new file mode 100644 index 0000000..7cb1600 Binary files /dev/null and b/docs/Images/so3_sample_mode_1.png differ diff --git a/docs/Images/so3_sample_mode_2.png b/docs/Images/so3_sample_mode_2.png new file mode 100644 index 0000000..1a0e2c3 Binary files /dev/null and b/docs/Images/so3_sample_mode_2.png differ diff --git a/src/SimplnxReview/Filters/Algorithms/ComputeGroupingDensity.cpp b/src/SimplnxReview/Filters/Algorithms/ComputeGroupingDensity.cpp deleted file mode 100644 index 31e20a2..0000000 --- a/src/SimplnxReview/Filters/Algorithms/ComputeGroupingDensity.cpp +++ /dev/null @@ -1,224 +0,0 @@ -#include "ComputeGroupingDensity.hpp" - -#include "simplnx/DataStructure/DataArray.hpp" -#include "simplnx/DataStructure/NeighborList.hpp" -#include "simplnx/Utilities/MessageHelper.hpp" - -using namespace nx::core; - -namespace -{ -template -struct FindDensitySpecializations -{ - static inline constexpr bool UsingNonContiguousNeighbors = UseNonContiguousNeighbors; - static inline constexpr bool FindingCheckedFeatures = FindCheckedFeatures; -}; - -template > -class FindDensityGrouping -{ -public: - FindDensityGrouping(const std::atomic_bool& shouldCancel, const IFilter::MessageHandler& mesgHandler, const Int32Array& parentIds, const Float32Array& parentVolumes, const Float32Array& volumes, - const Int32NeighborList& contiguousNL, Float32Array& groupingDensities, Int32NeighborList& nonContiguousNL, Int32Array& checkedFeatures) - : m_ShouldCancel(shouldCancel) - , m_MessageHandler(mesgHandler) - , m_ParentIds(parentIds) - , m_ParentVolumes(parentVolumes) - , m_Volumes(volumes) - , m_ContiguousNL(contiguousNL) - , m_GroupingDensities(groupingDensities) - , m_NonContiguousNL(nonContiguousNL) - , m_CheckedFeatures(checkedFeatures) - { - } - ~FindDensityGrouping() noexcept = default; - - FindDensityGrouping(const FindDensityGrouping&) = delete; // Copy Constructor Default Implemented - FindDensityGrouping(FindDensityGrouping&&) = delete; // Move Constructor Not Implemented - FindDensityGrouping& operator=(const FindDensityGrouping&) = delete; // Copy Assignment Not Implemented - FindDensityGrouping& operator=(FindDensityGrouping&&) = delete; // Move Assignment Not Implemented - - Result<> operator()() - { - const auto& parentIds = m_ParentIds.getDataStoreRef(); - const auto& parentVolumes = m_ParentVolumes.getDataStoreRef(); - const auto& volumes = m_Volumes.getDataStoreRef(); - - auto& checkedFeatures = m_CheckedFeatures.getDataStoreRef(); - auto& groupingDensities = m_GroupingDensities.getDataStoreRef(); - - usize numFeatures = volumes.getNumberOfTuples(); - usize numParents = parentVolumes.getNumberOfTuples(); - - int kMax = 1; - if constexpr(FindDensitySpecializations::UsingNonContiguousNeighbors) - { - kMax = 2; - } - - int32 numNeighbors, numNeighborhoods, numCurNeighborList, neigh; - float32 totalCheckVolume, curParentVolume; - std::set totalCheckList = {}; - - std::vector checkedFeatureVolumes(1, 0.0f); - if constexpr(FindDensitySpecializations::FindingCheckedFeatures) - { - // Default value-initialized to zeroes: https://en.cppreference.com/w/cpp/named_req/DefaultInsertable - checkedFeatureVolumes.resize(numFeatures); - } - MessageHelper messageHelper(m_MessageHandler); - ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger(); - - for(usize parentIdx = 1; parentIdx < numParents; parentIdx++) - { - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("[{}%]", CalculatePercentComplete(parentIdx, numParents)); }); - - if(m_ShouldCancel) - { - return {}; - } - for(usize j = 1; j < numFeatures; j++) - { - if(parentIds[j] == parentIdx) - { - if(totalCheckList.find(j) == totalCheckList.end()) - // if(std::find(totalCheckList.begin(), totalCheckList.end(), j) == totalCheckList.end()) - { - totalCheckVolume += m_Volumes[j]; - totalCheckList.insert(static_cast(j)); - if constexpr(FindDensitySpecializations::FindingCheckedFeatures) - { - if(parentVolumes[parentIdx] > checkedFeatureVolumes[j]) - { - checkedFeatureVolumes[j] = parentVolumes[parentIdx]; - checkedFeatures[j] = static_cast(parentIdx); - } - } - } - numNeighbors = m_ContiguousNL.getListSize(static_cast(j)); - if constexpr(FindDensitySpecializations::UsingNonContiguousNeighbors) - { - numNeighborhoods = static_cast(m_NonContiguousNL[j].size()); - } - for(int k = 0; k < kMax; k++) - { - if(k == 0) - { - numCurNeighborList = numNeighbors; - } - if constexpr(FindDensitySpecializations::UsingNonContiguousNeighbors) - { - if(k == 1) - { - numCurNeighborList = numNeighborhoods; - } - } - for(int32_t l = 0; l < numCurNeighborList; l++) - { - if(k == 0) - { - bool ok = false; - neigh = m_ContiguousNL.getValue(static_cast(j), l, ok); - } - else if(k == 1) - { - neigh = m_NonContiguousNL[j][l]; - } - if(totalCheckList.find(neigh) == totalCheckList.end()) - // if(std::find(totalCheckList.begin(), totalCheckList.end(), neigh) == totalCheckList.end()) - { - totalCheckVolume += m_Volumes[neigh]; - totalCheckList.insert(neigh); - if constexpr(FindDensitySpecializations::FindingCheckedFeatures) - { - if(parentVolumes[parentIdx] > checkedFeatureVolumes[neigh]) - { - checkedFeatureVolumes[neigh] = parentVolumes[parentIdx]; - checkedFeatures[neigh] = static_cast(parentIdx); - } - } - } - } - } - } - } - curParentVolume = parentVolumes[parentIdx]; - if(totalCheckVolume == 0.0f) - { - groupingDensities[parentIdx] = -1.0f; - } - else - { - groupingDensities[parentIdx] = (curParentVolume / totalCheckVolume); - } - totalCheckList.clear(); - totalCheckVolume = 0.0f; - } - - return {}; - } - -private: - const std::atomic_bool& m_ShouldCancel; - const IFilter::MessageHandler& m_MessageHandler; - const Int32Array& m_ParentIds; - const Float32Array& m_ParentVolumes; - const Float32Array& m_Volumes; - const Int32NeighborList& m_ContiguousNL; - Float32Array& m_GroupingDensities; - Int32NeighborList& m_NonContiguousNL; - Int32Array& m_CheckedFeatures; -}; -} // namespace - -// ----------------------------------------------------------------------------- -ComputeGroupingDensity::ComputeGroupingDensity(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, - ComputeGroupingDensityInputValues* inputValues) -: m_DataStructure(dataStructure) -, m_InputValues(inputValues) -, m_ShouldCancel(shouldCancel) -, m_MessageHandler(mesgHandler) -{ -} - -// ----------------------------------------------------------------------------- -const std::atomic_bool& ComputeGroupingDensity::getCancel() -{ - return m_ShouldCancel; -} - -// ----------------------------------------------------------------------------- -Result<> ComputeGroupingDensity::operator()() -{ - auto& parentIds = m_DataStructure.getDataRefAs(m_InputValues->ParentIdsPath); - auto& parentVolumes = m_DataStructure.getDataRefAs(m_InputValues->ParentVolumesPath); - auto& volumes = m_DataStructure.getDataRefAs(m_InputValues->VolumesPath); - auto& contiguousNL = m_DataStructure.getDataRefAs>(m_InputValues->ContiguousNLPath); - auto& groupingDensities = m_DataStructure.getDataRefAs(m_InputValues->GroupingDensitiesPath); - - // These may or may not be empty depending on the parameters - auto& nonContiguousNL = m_DataStructure.getDataRefAs>(m_InputValues->NonContiguousNLPath); - auto& checkedFeatures = m_DataStructure.getDataRefAs(m_InputValues->CheckedFeaturesPath); - - if(m_InputValues->UseNonContiguousNeighbors) - { - if(m_InputValues->FindCheckedFeatures) - { - return ::FindDensityGrouping>(getCancel(), m_MessageHandler, parentIds, parentVolumes, volumes, contiguousNL, groupingDensities, nonContiguousNL, - checkedFeatures)(); - } - return ::FindDensityGrouping>(getCancel(), m_MessageHandler, parentIds, parentVolumes, volumes, contiguousNL, groupingDensities, nonContiguousNL, - checkedFeatures)(); - } - else if(m_InputValues->FindCheckedFeatures) - { - return ::FindDensityGrouping>(getCancel(), m_MessageHandler, parentIds, parentVolumes, volumes, contiguousNL, groupingDensities, nonContiguousNL, - checkedFeatures)(); - } - else - { - return ::FindDensityGrouping>(getCancel(), m_MessageHandler, parentIds, parentVolumes, volumes, contiguousNL, groupingDensities, nonContiguousNL, - checkedFeatures)(); - } -} diff --git a/src/SimplnxReview/Filters/Algorithms/ComputeGroupingDensity.hpp b/src/SimplnxReview/Filters/Algorithms/ComputeGroupingDensity.hpp deleted file mode 100644 index 3b2dc60..0000000 --- a/src/SimplnxReview/Filters/Algorithms/ComputeGroupingDensity.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "SimplnxReview/SimplnxReview_export.hpp" - -#include "simplnx/DataStructure/DataPath.hpp" -#include "simplnx/DataStructure/DataStructure.hpp" -#include "simplnx/Filter/IFilter.hpp" - -namespace nx::core -{ - -struct SIMPLNXREVIEW_EXPORT ComputeGroupingDensityInputValues -{ - DataPath VolumesPath; - DataPath ContiguousNLPath; - bool UseNonContiguousNeighbors; - DataPath NonContiguousNLPath; - DataPath ParentIdsPath; - DataPath ParentVolumesPath; - bool FindCheckedFeatures; - DataPath CheckedFeaturesPath; - DataPath GroupingDensitiesPath; -}; - -/** - * @class ComputeGroupingDensity - * @brief This filter determines the average C-axis location of each Feature. - */ - -class SIMPLNXREVIEW_EXPORT ComputeGroupingDensity -{ -public: - ComputeGroupingDensity(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, ComputeGroupingDensityInputValues* inputValues); - ~ComputeGroupingDensity() noexcept = default; - - ComputeGroupingDensity(const ComputeGroupingDensity&) = delete; - ComputeGroupingDensity(ComputeGroupingDensity&&) noexcept = delete; - ComputeGroupingDensity& operator=(const ComputeGroupingDensity&) = delete; - ComputeGroupingDensity& operator=(ComputeGroupingDensity&&) noexcept = delete; - - Result<> operator()(); - - const std::atomic_bool& getCancel(); - -private: - DataStructure& m_DataStructure; - const ComputeGroupingDensityInputValues* m_InputValues = nullptr; - const std::atomic_bool& m_ShouldCancel; - const IFilter::MessageHandler& m_MessageHandler; -}; - -} // namespace nx::core diff --git a/src/SimplnxReview/Filters/Algorithms/EMsoftSO3Sampler.cpp b/src/SimplnxReview/Filters/Algorithms/EMsoftSO3Sampler.cpp new file mode 100644 index 0000000..3c099bc --- /dev/null +++ b/src/SimplnxReview/Filters/Algorithms/EMsoftSO3Sampler.cpp @@ -0,0 +1,332 @@ +#include "EMsoftSO3Sampler.hpp" + +#include "simplnx/Common/Constants.hpp" +#include "simplnx/DataStructure/DataArray.hpp" +#include "simplnx/DataStructure/StringArray.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" + +#include "EbsdLib/Core/EbsdLibConstants.h" +#include "EbsdLib/Core/Orientation.hpp" +#include "EbsdLib/IO/TSL/AngConstants.h" +#include "EbsdLib/LaueOps/LaueOps.h" +#include "EbsdLib/Orientation/Cubochoric.hpp" +#include "EbsdLib/Orientation/Rodrigues.hpp" + +using namespace nx::core; + +namespace +{ +template +ebsdlib::Rodrigues RodriguesComposition(const ContainerType& sigma, const ebsdlib::Rodrigues& rod) +{ + ContainerType rho{-rod[0] * rod[3], -rod[1] * rod[3], -rod[2] * rod[3]}; + // perform the Rodrigues rotation composition with sigma to get rhomis + double denom = 1.0 + (sigma[0] * rho[0] + sigma[1] * rho[1] + sigma[2] * rho[2]); + if(denom == 0.0) + { + double len = sqrt(sigma[0] * sigma[0] + sigma[1] * sigma[1] + sigma[2] * sigma[2]); + return {sigma[0] / len, sigma[1] / len, sigma[2] / len, std::numeric_limits::infinity()}; + } + + ContainerType rhomis(3); + rhomis[0] = (rho[0] - sigma[0] + (rho[1] * sigma[2] - rho[2] * sigma[1])) / denom; + rhomis[1] = (rho[1] - sigma[1] + (rho[2] * sigma[0] - rho[0] * sigma[2])) / denom; + rhomis[2] = (rho[2] - sigma[2] + (rho[0] * sigma[1] - rho[1] * sigma[0])) / denom; + // revert rhomis to a four-component Rodrigues vector + double len = sqrt(rhomis[0] * rhomis[0] + rhomis[1] * rhomis[1] + rhomis[2] * rhomis[2]); + if(len != 0.0) + { + return {-rhomis[0] / len, -rhomis[1] / len, -rhomis[2] / len, len}; + } + + return {0.0, 0.0, 0.0, 0.0}; // Return something that is not-valid +} +} // namespace + +// ----------------------------------------------------------------------------- +EMsoftSO3Sampler::EMsoftSO3Sampler(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, EMsoftSO3SamplerInputValues* inputValues) +: m_DataStructure(dataStructure) +, m_InputValues(inputValues) +, m_ShouldCancel(shouldCancel) +, m_MessageHandler(mesgHandler) +{ +} + +// ----------------------------------------------------------------------------- +EMsoftSO3Sampler::~EMsoftSO3Sampler() noexcept = default; + +// ----------------------------------------------------------------------------- +Result<> EMsoftSO3Sampler::operator()() +{ + typedef std::list OrientationListArrayType; + + OrientationListArrayType FZlist; + + if(m_InputValues->sampleModeSelector == orientation_sampling::k_FZModeIndex) + { + // here we perform the actual calculation; once we have the FZlist, + // we can allocate the data array and copy all entries + double x, y, z, delta; + + // step size for sampling of grid; maximum total number of samples = pow(2*m_InputValues->Numsp+1,3) + delta = (0.50 * LPs::ap) / static_cast(m_InputValues->Numsp); + + // do we need to shift this array away from the origin? + double gridShift = 0.0; + if(m_InputValues->OffsetGrid) + { + gridShift = 0.5; + } + + std::vector ops = ebsdlib::LaueOps::GetAllOrientationOps(); + // determine which function we should call for this point group symmetry + ebsdlib::LaueOps::FZType fzType = ops[m_InputValues->CrystalStructureIndex]->getFZType(); + ebsdlib::LaueOps::AxisOrderingType fzAxisOrder = ops[m_InputValues->CrystalStructureIndex]->getAxisOrderingType(); + + // loop over the cube of volume pi^2; note that we do not want to include + // the opposite edges/facets of the cube, to avoid double counting rotations + // with a rotation angle of 180 degrees. This only affects the cyclic groups. + int Np = m_InputValues->Numsp; + int Totp = (2 * Np + 1) * (2 * Np + 1) * (2 * Np + 1); + int Dn = Totp / 10; + int Dc = Dn; + int Di = 0; + int Dg = 0; + + // eliminate points for which any of the coordinates lies outside the cube with semi-edge length "edge" + double edge = 0.5 * LPs::ap; + + for(int i = -Np + 1; i < Np + 1; i++) + { + x = (static_cast(i) + gridShift) * delta; + + if(fabs(x) <= edge) + { + + for(int j = -Np + 1; j < Np + 1; j++) + { + y = (static_cast(j) + gridShift) * delta; + + if(fabs(y) <= edge) + { + + for(int k = -Np + 1; k < Np + 1; k++) + { + z = (static_cast(k) + gridShift) * delta; + + if(fabs(z) <= edge) + { + // convert to Rodrigues representation + ebsdlib::RodriguesDType rod = ebsdlib::CubochoricDType(x, y, z).toRodrigues(); + // If insideFZ=true, then add this point to FZlist + if(ebsdlib::LaueOps::IsInsideFZ(rod, fzType, fzAxisOrder)) + { + FZlist.push_back(rod); + Dg += 1; + } + Di += 1; + } + } + } + } + } + + // report on status of computation + if(Di > Dc) + { + m_MessageHandler(IFilter::Message::Type::Info, fmt::format("Euler Angles | Tested: {} of {} | Inside RFZ: {} ", Di, Totp, Dg)); + Dc += Dn; + } + if(m_ShouldCancel) + { + break; + } + } + } + + // here are the misorientation sampling cases: + if(m_InputValues->sampleModeSelector != orientation_sampling::k_FZModeIndex) + { + // here we perform the actual calculation; once we have the FZlist, + // we can allocate the data array and copy all entries + double x, y, z, delta, omega, semi; + + // step size for sampling of grid; the edge length of the cube is (pi ( w - sin(w) ))^1/3 with w the misorientation angle + omega = m_InputValues->MisOr * Constants::k_PiOver180D; + semi = pow(Constants::k_PiD * (omega - sin(omega)), 1.0 / 3.0) * 0.5; + delta = semi / static_cast(m_InputValues->Numsp); + + // convert the reference orientation to a 3-component Rodrigues vector sigma + ebsdlib::RodriguesDType sigm = + ebsdlib::EulerDType(m_InputValues->RefOr[0] * Constants::k_PiOver180D, m_InputValues->RefOr[1] * Constants::k_PiOver180D, m_InputValues->RefOr[2] * Constants::k_PiOver180D).toRodrigues(); + + std::vector sigma = {sigm[0] * sigm[3], sigm[1] * sigm[3], sigm[2] * sigm[3]}; + + if(m_InputValues->sampleModeSelector == orientation_sampling::k_ConstantModeIndex) + { + // set counter parameters for the loop over the sub-cube surface + int Np = m_InputValues->Numsp; + int Totp = 24 * Np * Np + 2; + int Dn = Totp / 20; + int Dc = Dn; + int Dg = 0; + + // x-y bottom and top planes + for(int i = -Np; i <= Np; i++) + { + x = static_cast(i) * delta; + for(int j = -Np; j <= Np; j++) + { + y = static_cast(j) * delta; + // convert to Rodrigues representation and apply Rodrigues composition formula + { + ebsdlib::RodriguesDType rod = ebsdlib::CubochoricDType(-x, -y, -semi).toRodrigues(); + rod = RodriguesComposition(sigma, rod); + FZlist.push_back(rod); + Dg += 1; + } + { + ebsdlib::RodriguesDType rod = ebsdlib::CubochoricDType(-x, -y, semi).toRodrigues(); + rod = RodriguesComposition(sigma, rod); + FZlist.push_back(rod); + Dg += 1; + } + } + if(m_ShouldCancel) + { + break; + } + } + // y-z planes + for(int j = -Np; j <= Np; j++) + { + y = static_cast(j) * delta; + for(int k = -Np + 1; k <= Np - 1; k++) + { + z = static_cast(k) * delta; + // convert to Rodrigues representation and apply Rodrigues composition formula + { + ebsdlib::RodriguesDType rod = ebsdlib::CubochoricDType(-semi, -y, -z).toRodrigues(); + rod = RodriguesComposition(sigma, rod); + FZlist.push_back(rod); + Dg += 1; + } + { + ebsdlib::RodriguesDType rod = ebsdlib::CubochoricDType(semi, -y, -z).toRodrigues(); + rod = RodriguesComposition(sigma, rod); + FZlist.push_back(rod); + Dg += 1; + } + } + if(m_ShouldCancel) + { + break; + } + } + // finally the x-z planes + for(int i = -Np + 1; i <= Np - 1; i++) + { + x = static_cast(i) * delta; + for(int k = -Np + 1; k <= Np - 1; k++) + { + z = static_cast(k) * delta; + // convert to Rodrigues representation and apply Rodrigues composition formula + { + ebsdlib::RodriguesDType rod = ebsdlib::CubochoricDType(-x, -semi, -z).toRodrigues(); + rod = RodriguesComposition(sigma, rod); + FZlist.push_back(rod); + Dg += 1; + } + { + ebsdlib::RodriguesDType rod = ebsdlib::CubochoricDType(-x, semi, -z).toRodrigues(); + rod = RodriguesComposition(sigma, rod); + FZlist.push_back(rod); + Dg += 1; + } + } + if(m_ShouldCancel) + { + break; + } + } + + // report on status of computation + if(Dg > Dc) + { + m_MessageHandler(IFilter::Message::Type::Info, fmt::format("Euler Angles | Generated: {} / {}", Dg, Totp)); + Dc += Dn; + } + } + else + { + // set counter parameters for the loop over the sub-cube surface + int Np = m_InputValues->Numsp; + int Totp = (2 * Np + 1) * (2 * Np + 1) * (2 * Np + 1); // see misorientation sampling paper for this expression + int Dn = Totp / 20; + int Dc = Dn; + int Dg = 0; + + for(int i = -Np; i <= Np; i++) + { + x = static_cast(i) * delta; + for(int j = -Np; j <= Np; j++) + { + y = static_cast(j) * delta; + for(int k = -Np; k <= Np; k++) + { + z = static_cast(k) * delta; + // convert to Rodrigues representation and apply Rodrigues composition formula + { + ebsdlib::RodriguesDType rod = ebsdlib::CubochoricDType(-x, -y, -z).toRodrigues(); + rod = RodriguesComposition(sigma, rod); + FZlist.push_back(rod); + Dg += 1; + } + } + } + // report on status of computation + if(Dg > Dc) + { + m_MessageHandler(IFilter::Message::Type::Info, fmt::format("Euler Angles | Generated: {} / {}", Dg, Totp)); + Dc += Dn; + } + } + } + } + + // resize the EulerAngles array to the number of items in FZlist; don't forget to redefine the hard pointer + std::vector tDims = {FZlist.size()}; + + auto& m_EulerAngles = m_DataStructure.getDataRefAs(m_InputValues->EulerAnglesArrayPath); + m_EulerAngles.resizeTuples(tDims); + auto& m_EulerAnglesStoreRef = m_EulerAngles.getDataStoreRef(); + // copy the Rodrigues vectors as Euler angles into the m_EulerAngles array; convert doubles to floats along the way + int j = -1; + for(const ebsdlib::RodriguesDType& rod : FZlist) + { + j += 1; + ebsdlib::EulerDType eu = rod.toEuler(); + + m_EulerAnglesStoreRef.setValue(j * 3 + 0, static_cast(eu[0])); + m_EulerAnglesStoreRef.setValue(j * 3 + 1, static_cast(eu[1])); + m_EulerAnglesStoreRef.setValue(j * 3 + 2, static_cast(eu[2])); + } + + if(m_InputValues->sampleModeSelector == orientation_sampling::k_FZModeIndex) + { + auto ops = ebsdlib::LaueOps::GetAllOrientationOps(); + auto& crystalStructures = m_DataStructure.getDataRefAs(m_InputValues->EnsembleAttrMatrixPath.createChildPath(ebsdlib::AngFile::CrystalStructures)); + + auto& materialNames = m_DataStructure.getDataRefAs(m_InputValues->EnsembleAttrMatrixPath.createChildPath(ebsdlib::AngFile::MaterialName)); + const std::string k_InvalidPhase = "Invalid Phase"; + + // Initialize the zero'th element to unknowns. + crystalStructures.setValue(0, ebsdlib::CrystalStructure::UnknownCrystalStructure); + materialNames.setValue(0, k_InvalidPhase); + + crystalStructures.setValue(1, m_InputValues->CrystalStructureIndex); + materialNames.setValue(1, ops[m_InputValues->CrystalStructureIndex]->getSymmetryName()); + } + + return {}; +} diff --git a/src/SimplnxReview/Filters/Algorithms/EMsoftSO3Sampler.hpp b/src/SimplnxReview/Filters/Algorithms/EMsoftSO3Sampler.hpp new file mode 100644 index 0000000..dd7bb2f --- /dev/null +++ b/src/SimplnxReview/Filters/Algorithms/EMsoftSO3Sampler.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "SimplnxReview/SimplnxReview_export.hpp" + +#include "simplnx/DataStructure/DataPath.hpp" +#include "simplnx/DataStructure/DataStructure.hpp" +#include "simplnx/Filter/IFilter.hpp" +#include "simplnx/Parameters/ChoicesParameter.hpp" +#include "simplnx/Parameters/VectorParameter.hpp" + +namespace nx::core +{ + +struct SIMPLNXREVIEW_EXPORT EMsoftSO3SamplerInputValues +{ + ChoicesParameter::ValueType sampleModeSelector; + usize CrystalStructureIndex; + bool OffsetGrid; + float64 MisOr; + VectorFloat32Parameter::ValueType RefOr; + // float64 MisOrFull; + // VectorFloat32Parameter::ValueType RefOrFull; + int32 Numsp; + DataPath EulerAnglesArrayPath; + DataPath EnsembleAttrMatrixPath; +}; + +namespace orientation_sampling +{ +const std::string k_FZMode("Rodrigues fundamental zone (0)"); +const std::string k_ConstantMode("Constant misorientation (1)"); +const std::string k_LessThan("Less than given misorientation (2)"); +const ChoicesParameter::Choices k_Choices = {k_FZMode, k_ConstantMode, k_LessThan}; + +constexpr ChoicesParameter::ValueType k_FZModeIndex = 0; +constexpr ChoicesParameter::ValueType k_ConstantModeIndex = 1; +constexpr ChoicesParameter::ValueType k_LessThanModeIndex = 2; +} // namespace orientation_sampling + +/** + * @class EMsoftSO3Sampler + * @brief This algorithm implements support code for the EMsoftSO3SamplerFilter + */ + +class SIMPLNXREVIEW_EXPORT EMsoftSO3Sampler +{ +public: + EMsoftSO3Sampler(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, EMsoftSO3SamplerInputValues* inputValues); + ~EMsoftSO3Sampler() noexcept; + + EMsoftSO3Sampler(const EMsoftSO3Sampler&) = delete; + EMsoftSO3Sampler(EMsoftSO3Sampler&&) noexcept = delete; + EMsoftSO3Sampler& operator=(const EMsoftSO3Sampler&) = delete; + EMsoftSO3Sampler& operator=(EMsoftSO3Sampler&&) noexcept = delete; + + Result<> operator()(); + +private: + DataStructure& m_DataStructure; + const EMsoftSO3SamplerInputValues* m_InputValues = nullptr; + const std::atomic_bool& m_ShouldCancel; + const IFilter::MessageHandler& m_MessageHandler; +}; + +} // namespace nx::core diff --git a/src/SimplnxReview/Filters/ComputeGroupingDensityFilter.cpp b/src/SimplnxReview/Filters/ComputeGroupingDensityFilter.cpp deleted file mode 100644 index a2f79ff..0000000 --- a/src/SimplnxReview/Filters/ComputeGroupingDensityFilter.cpp +++ /dev/null @@ -1,254 +0,0 @@ -#include "ComputeGroupingDensityFilter.hpp" - -#include "SimplnxReview/Filters/Algorithms/ComputeGroupingDensity.hpp" - -#include "simplnx/DataStructure/AttributeMatrix.hpp" -#include "simplnx/DataStructure/DataPath.hpp" -#include "simplnx/Filter/Actions/CreateArrayAction.hpp" -#include "simplnx/Filter/Actions/CreateNeighborListAction.hpp" -#include "simplnx/Filter/Actions/DeleteDataAction.hpp" -#include "simplnx/Parameters/ArraySelectionParameter.hpp" -#include "simplnx/Parameters/BoolParameter.hpp" -#include "simplnx/Parameters/DataObjectNameParameter.hpp" -#include "simplnx/Parameters/NeighborListSelectionParameter.hpp" -#include "simplnx/Utilities/SIMPLConversion.hpp" - -using namespace nx::core; - -namespace -{ -const DataPath k_ThrowawayCheckedFeatures = DataPath({"HiddenTempCheckedFeatures"}); -const DataPath k_ThrowawayNonContiguous = DataPath({"HiddenContiguousNL"}); -} // namespace - -namespace nx::core -{ -//------------------------------------------------------------------------------ -std::string ComputeGroupingDensityFilter::name() const -{ - return FilterTraits::name.str(); -} - -//------------------------------------------------------------------------------ -std::string ComputeGroupingDensityFilter::className() const -{ - return FilterTraits::className; -} - -//------------------------------------------------------------------------------ -Uuid ComputeGroupingDensityFilter::uuid() const -{ - return FilterTraits::uuid; -} - -//------------------------------------------------------------------------------ -std::string ComputeGroupingDensityFilter::humanName() const -{ - return "Compute Grouping Densities"; -} - -//------------------------------------------------------------------------------ -std::vector ComputeGroupingDensityFilter::defaultTags() const -{ - return {className(), "Statistics", "Reconstruction"}; -} - -//------------------------------------------------------------------------------ -Parameters ComputeGroupingDensityFilter::parameters() const -{ - Parameters params; - // Create the parameter descriptors that are needed for this filter - params.insertSeparator(Parameters::Separator{"Input Parameter(s)"}); - params.insertLinkableParameter(std::make_unique(k_FindCheckedFeatures_Key, "Find Checked Features", "Find checked features", false)); - - params.insertSeparator(Parameters::Separator{"Non-Contiguous Neighborhood Option"}); - params.insertLinkableParameter(std::make_unique(k_UseNonContiguousNeighbors_Key, "Use Non-Contiguous Neighbors", "Use non-contiguous neighborhoods for computations", false)); - params.insert(std::make_unique(k_NonContiguousNeighborListArrayPath_Key, "Non-Contiguous Neighborhoods", "List of non-contiguous neighbors for each Feature.", - DataPath{}, NeighborListSelectionParameter::AllowedTypes{DataType::int32})); - - params.insertSeparator(Parameters::Separator{"Input Cell Data"}); - params.insert(std::make_unique(k_ParentIdsPath_Key, "Parent Ids", "Input Cell level ParentIds", DataPath{}, ArraySelectionParameter::AllowedTypes{DataType::int32}, - ArraySelectionParameter::AllowedComponentShapes{{1}})); - - params.insertSeparator(Parameters::Separator{"Input Feature Data"}); - params.insert(std::make_unique(k_VolumesArrayPath_Key, "Volumes", "The Feature Volumes Data Array", DataPath{}, - ArraySelectionParameter::AllowedTypes{nx::core::DataType::float32}, ArraySelectionParameter::AllowedComponentShapes{{1}})); - - params.insert(std::make_unique(k_ContiguousNeighborListArrayPath_Key, "Contiguous Neighbor List", "List of contiguous neighbors for each Feature.", DataPath{}, - NeighborListSelectionParameter::AllowedTypes{DataType::int32})); - - params.insert(std::make_unique(k_ParentVolumesPath_Key, "Parent Volumes", "Input feature level parent volume data array", DataPath{}, - ArraySelectionParameter::AllowedTypes{DataType::float32}, ArraySelectionParameter::AllowedComponentShapes{{1}})); - - params.insertSeparator(Parameters::Separator{"Output Feature Data"}); - - params.insert(std::make_unique(k_CheckedFeaturesName_Key, "Checked Features Name", "Output feature level data array to hold 'Checked Features' values", "Checked Features")); - params.insert( - std::make_unique(k_GroupingDensitiesName_Key, "Grouping Densities Name", "Output feature level data array to hold 'Grouping Densities' values", "Grouping Densities")); - - // Link params - params.linkParameters(k_UseNonContiguousNeighbors_Key, k_NonContiguousNeighborListArrayPath_Key, true); - params.linkParameters(k_FindCheckedFeatures_Key, k_CheckedFeaturesName_Key, true); - - return params; -} - -//------------------------------------------------------------------------------ -IFilter::UniquePointer ComputeGroupingDensityFilter::clone() const -{ - return std::make_unique(); -} - -//------------------------------------------------------------------------------ -IFilter::VersionType ComputeGroupingDensityFilter::parametersVersion() const -{ - return 1; -} - -//------------------------------------------------------------------------------ -IFilter::PreflightResult ComputeGroupingDensityFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, - const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const -{ - auto pParentIdsPath = filterArgs.value(k_ParentIdsPath_Key); - auto pParentVolumesPath = filterArgs.value(k_ParentVolumesPath_Key); - auto pContiguousNLPath = filterArgs.value(k_ContiguousNeighborListArrayPath_Key); - auto pVolumesPath = filterArgs.value(k_VolumesArrayPath_Key); - auto pGroupingDensitiesName = filterArgs.value(k_GroupingDensitiesName_Key); - - auto pUseNonContiguousNeighbors = filterArgs.value(k_UseNonContiguousNeighbors_Key); - auto pNonContiguousNLPath = filterArgs.value(k_NonContiguousNeighborListArrayPath_Key); - auto pFindCheckedFeatures = filterArgs.value(k_FindCheckedFeatures_Key); - auto pCheckedFeaturesName = filterArgs.value(k_CheckedFeaturesName_Key); - - Result resultOutputActions; - std::vector preflightUpdatedValues; - - auto* pParentAM = dataStructure.getDataAs(pParentVolumesPath.getParent()); - if(pParentAM == nullptr) - { - return MakePreflightErrorResult(-15670, fmt::format("Parent Volumes [{}] must be stored in an Attribute Matrix.", pParentVolumesPath.toString())); - } - { - DataPath groupingDataPath = pParentVolumesPath.replaceName(pGroupingDensitiesName); - auto createArrayAction = std::make_unique(nx::core::DataType::float32, pParentAM->getShape(), std::vector{1}, groupingDataPath); - resultOutputActions.value().appendAction(std::move(createArrayAction)); - } - - auto* pFeatureAM = dataStructure.getDataAs(pVolumesPath.getParent()); - if(pFeatureAM == nullptr) - { - return MakePreflightErrorResult(-15671, fmt::format("Feature Volumes [{}] must be stored in an Attribute Matrix.", pVolumesPath.toString())); - } - - if(pFindCheckedFeatures) - { - DataPath checkedFeaturesPath = pVolumesPath.replaceName(pCheckedFeaturesName); - { - auto createArrayAction = std::make_unique(nx::core::DataType::int32, pFeatureAM->getShape(), std::vector{1}, checkedFeaturesPath); - resultOutputActions.value().appendAction(std::move(createArrayAction)); - } - } - else - { - { - auto createArrayAction = std::make_unique(nx::core::DataType::int32, ShapeType{1}, ShapeType{1}, k_ThrowawayCheckedFeatures); - resultOutputActions.value().appendAction(std::move(createArrayAction)); - } - { - auto removeAction = std::make_unique(k_ThrowawayCheckedFeatures); - resultOutputActions.value().appendDeferredAction(std::move(removeAction)); - } - } - - if(!pUseNonContiguousNeighbors) - { - { - auto createArrayAction = std::make_unique(nx::core::DataType::int32, ShapeType{1}, k_ThrowawayNonContiguous); - resultOutputActions.value().appendAction(std::move(createArrayAction)); - } - { - auto removeAction = std::make_unique(k_ThrowawayNonContiguous); - resultOutputActions.value().appendDeferredAction(std::move(removeAction)); - } - } - - preflightUpdatedValues.push_back({"WARNING: This filter is experimental in nature and has not had any testing, validation or verification. Use at your own risk"}); - resultOutputActions.warnings().push_back({-65432, "WARNING: This filter is experimental in nature and has not had any testing, validation or verification. Use at your own risk"}); - - return {std::move(resultOutputActions), std::move(preflightUpdatedValues)}; -} - -//------------------------------------------------------------------------------ -Result<> ComputeGroupingDensityFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, - const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const -{ - ComputeGroupingDensityInputValues inputValues; - - inputValues.ParentIdsPath = filterArgs.value(k_ParentIdsPath_Key); - inputValues.ParentVolumesPath = filterArgs.value(k_ParentVolumesPath_Key); - inputValues.ContiguousNLPath = filterArgs.value(k_ContiguousNeighborListArrayPath_Key); - inputValues.VolumesPath = filterArgs.value(k_VolumesArrayPath_Key); - inputValues.GroupingDensitiesPath = inputValues.ParentVolumesPath.replaceName(filterArgs.value(k_GroupingDensitiesName_Key)); - - inputValues.UseNonContiguousNeighbors = filterArgs.value(k_UseNonContiguousNeighbors_Key); - if(inputValues.UseNonContiguousNeighbors) - { - inputValues.NonContiguousNLPath = filterArgs.value(k_NonContiguousNeighborListArrayPath_Key); - } - else - { - inputValues.NonContiguousNLPath = k_ThrowawayNonContiguous; - } - - inputValues.FindCheckedFeatures = filterArgs.value(k_FindCheckedFeatures_Key); - if(inputValues.FindCheckedFeatures) - { - inputValues.CheckedFeaturesPath = inputValues.VolumesPath.replaceName(filterArgs.value(k_CheckedFeaturesName_Key)); - } - else - { - inputValues.CheckedFeaturesPath = k_ThrowawayCheckedFeatures; - } - - return ComputeGroupingDensity(dataStructure, messageHandler, shouldCancel, &inputValues)(); -} - -namespace -{ -namespace SIMPL -{ -constexpr StringLiteral k_CheckedFeaturesArrayNameKey = "CheckedFeaturesArrayName"; -constexpr StringLiteral k_ContiguousNeighborListArrayPathKey = "ContiguousNeighborListArrayPath"; -constexpr StringLiteral k_FindCheckedFeaturesKey = "FindCheckedFeatures"; -constexpr StringLiteral k_NonContiguousNeighborListArrayPathKey = "NonContiguousNeighborListArrayPath"; -constexpr StringLiteral k_ParentDensitiesArrayNameKey = "ParentDensitiesArrayName"; -constexpr StringLiteral k_ParentIdsArrayPathKey = "ParentIdsArrayPath"; -constexpr StringLiteral k_ParentVolumesArrayPathKey = "ParentVolumesArrayPath"; -constexpr StringLiteral k_UseNonContiguousNeighborsKey = "UseNonContiguousNeighbors"; -constexpr StringLiteral k_VolumesArrayPathKey = "VolumesArrayPath"; -} // namespace SIMPL -} // namespace - -Result ComputeGroupingDensityFilter::FromSIMPLJson(const nlohmann::json& json) -{ - Arguments args = ComputeGroupingDensityFilter().getDefaultArguments(); - - std::vector> results; - - results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_CheckedFeaturesArrayNameKey, k_CheckedFeaturesName_Key)); - results.push_back( - SIMPLConversion::ConvertParameter(args, json, SIMPL::k_ContiguousNeighborListArrayPathKey, k_ContiguousNeighborListArrayPath_Key)); - results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_FindCheckedFeaturesKey, k_FindCheckedFeatures_Key)); - results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_NonContiguousNeighborListArrayPathKey, - k_NonContiguousNeighborListArrayPath_Key)); - results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_ParentDensitiesArrayNameKey, k_GroupingDensitiesName_Key)); - results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_ParentIdsArrayPathKey, k_ParentIdsPath_Key)); - results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_ParentVolumesArrayPathKey, k_ParentVolumesPath_Key)); - results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_UseNonContiguousNeighborsKey, k_UseNonContiguousNeighbors_Key)); - results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_VolumesArrayPathKey, k_VolumesArrayPath_Key)); - - Result<> conversionResult = MergeResults(std::move(results)); - - return ConvertResultTo(std::move(conversionResult), std::move(args)); -} -} // namespace nx::core diff --git a/src/SimplnxReview/Filters/EMsoftSO3SamplerFilter.cpp b/src/SimplnxReview/Filters/EMsoftSO3SamplerFilter.cpp new file mode 100644 index 0000000..cc27401 --- /dev/null +++ b/src/SimplnxReview/Filters/EMsoftSO3SamplerFilter.cpp @@ -0,0 +1,245 @@ +#include "EMsoftSO3SamplerFilter.hpp" + +#include "SimplnxReview/Filters/Algorithms/EMsoftSO3Sampler.hpp" + +#include "simplnx/DataStructure/DataGroup.hpp" +#include "simplnx/DataStructure/DataPath.hpp" +#include "simplnx/Filter/Actions/CreateArrayAction.hpp" +#include "simplnx/Filter/Actions/CreateAttributeMatrixAction.hpp" +#include "simplnx/Filter/Actions/CreateStringArrayAction.hpp" +#include "simplnx/Filter/Actions/EmptyAction.hpp" +#include "simplnx/Parameters/ArrayCreationParameter.hpp" +#include "simplnx/Parameters/BoolParameter.hpp" +#include "simplnx/Parameters/ChoicesParameter.hpp" +#include "simplnx/Parameters/DataGroupCreationParameter.hpp" +#include "simplnx/Parameters/DataObjectNameParameter.hpp" +#include "simplnx/Parameters/EnsembleInfoParameter.hpp" +#include "simplnx/Parameters/NumberParameter.hpp" +#include "simplnx/Parameters/VectorParameter.hpp" + +#include "EbsdLib/IO/TSL/AngConstants.h" +#include "EbsdLib/Orientation/Euler.hpp" +#include "simplnx/Common/Constants.hpp" + +using namespace nx::core; + +namespace +{ + +} + +namespace nx::core +{ +//------------------------------------------------------------------------------ +std::string EMsoftSO3SamplerFilter::name() const +{ + return FilterTraits::name.str(); +} + +//------------------------------------------------------------------------------ +std::string EMsoftSO3SamplerFilter::className() const +{ + return FilterTraits::className; +} + +//------------------------------------------------------------------------------ +Uuid EMsoftSO3SamplerFilter::uuid() const +{ + return FilterTraits::uuid; +} + +//------------------------------------------------------------------------------ +std::string EMsoftSO3SamplerFilter::humanName() const +{ + return "SO3 Orientation Sampler"; +} + +//------------------------------------------------------------------------------ +std::vector EMsoftSO3SamplerFilter::defaultTags() const +{ + return {className(), "Sampling", "Crystallography"}; +} + +//------------------------------------------------------------------------------ +Parameters EMsoftSO3SamplerFilter::parameters() const +{ + Parameters params; + // Create the parameter descriptors that are needed for this filter + params.insertSeparator(Parameters::Separator{"Input Parameter(s)"}); + params.insert(std::make_unique(k_NumberSamples_Key, "Number of sampling points along cube semi-axis", "The number of sampling points along the cube semi-axis", 101)); + params.insertLinkableParameter(std::make_unique(k_SampleModeSelector_Key, "Select the desired SO(3) sampling mode", + "This is the mode that is ued to sample the orientation space. [0-2]", orientation_sampling::k_FZModeIndex, + orientation_sampling::k_Choices)); + + params.insert(std::make_unique(k_CrystalStructure_Index, "Laue Class", "Laue Class Index. See documentation for list.", 1, EnsembleInfoParameter::k_CrystalStructures)); + + params.insert(std::make_unique(k_OffsetGrid_Key, "Offset sampling grid from origin?", "Offset the cubo-choric grid from the origin by half a grid unit.", false)); + params.insert(std::make_unique(k_Mode1Misorientation_Key, "Misorientation angle (degree)", "Reference Misorientation in Degrees", 1.0)); + params.insert(std::make_unique(k_Mode1EulerAngle_Key, "Reference orientation (Euler, °)", "Reference Orientation as a Bunge Euler Triplet in Degrees", + std::vector{0.0F, 0.0F, 0.0F}, std::vector{"phi-1", "PHI", "phi-2"})); + // params.insert(std::make_unique(k_Mode2Misorientation_Key, "Misorientation angle (degree)", "Reference Misorientation in Degrees", 1.0)); + // params.insert(std::make_unique(k_Mode2EulerAngle_Key, "Reference orientation (Euler, °)", "Reference Orientation as a Bunge Euler Triplet in Degrees", + // std::vector{0.0F, 0.0F, 0.0F}, std::vector{"phi-1", "PHI", "phi-2"})); + + params.insertSeparator(Parameters::Separator{"Output Data"}); + // params.insert(std::make_unique(k_CellAttributeMatrixName_Key, "Output Attribute Matrix", "The name of the cell data attribute matrix for the created Image Geometry", + // DataGroupCreationParameter::ValueType({"Sampling Data"}))); + params.insert(std::make_unique(k_EnsembleAttributeMatrixPath_Key, "Ensemble Attribute Matrix", "The Attribute Matrix where the phase information is stored.", + DataGroupCreationParameter::ValueType({"Ensemble Attribute Matrix"}))); + params.insert(std::make_unique(k_EulerAnglesArrayPath_Key, "Euler Angles", "Output array to hold the generated Euler Angles", DataPath({"Euler Angles"}))); + + // Associate the Linkable Parameter(s) to the children parameters that they control + params.linkParameters(k_SampleModeSelector_Key, k_CrystalStructure_Index, static_cast(0)); + params.linkParameters(k_SampleModeSelector_Key, k_OffsetGrid_Key, static_cast(0)); + // params.linkParameters(k_SampleModeSelector_Key, k_CellAttributeMatrixName_Key, static_cast(0)); + params.linkParameters(k_SampleModeSelector_Key, k_EnsembleAttributeMatrixPath_Key, static_cast(0)); + + params.linkParameters(k_SampleModeSelector_Key, k_Mode1Misorientation_Key, static_cast(1)); + params.linkParameters(k_SampleModeSelector_Key, k_Mode1EulerAngle_Key, static_cast(1)); + + params.linkParameters(k_SampleModeSelector_Key, k_Mode1Misorientation_Key, static_cast(2)); + params.linkParameters(k_SampleModeSelector_Key, k_Mode1EulerAngle_Key, static_cast(2)); + + return params; +} + +//------------------------------------------------------------------------------ +IFilter::VersionType EMsoftSO3SamplerFilter::parametersVersion() const +{ + return 1; +} + +//------------------------------------------------------------------------------ +IFilter::UniquePointer EMsoftSO3SamplerFilter::clone() const +{ + return std::make_unique(); +} + +//------------------------------------------------------------------------------ +IFilter::PreflightResult EMsoftSO3SamplerFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const +{ + auto psampleModeSelectorValue = filterArgs.value(k_SampleModeSelector_Key); + auto pCrystalStructureIndex = filterArgs.value(k_CrystalStructure_Index); + auto pOffsetGridValue = filterArgs.value(k_OffsetGrid_Key); + auto pMisOrValue = filterArgs.value(k_Mode1Misorientation_Key); + auto pRefOrValue = filterArgs.value(k_Mode1EulerAngle_Key); + // auto pMisOrFullValue = filterArgs.value(k_Mode2Misorientation_Key); + // auto pRefOrFullValue = filterArgs.value(k_Mode2EulerAngle_Key); + auto pNumspValue = filterArgs.value(k_NumberSamples_Key); + auto pEulerAnglesArrayPath = filterArgs.value(k_EulerAnglesArrayPath_Key); + // auto pCellAttributeMatrixNameValue = filterArgs.value(k_CellAttributeMatrixName_Key); + auto ensembleAttributeMatrixPath = filterArgs.value(k_EnsembleAttributeMatrixPath_Key); + + nx::core::Result resultOutputActions; + + std::vector preflightUpdatedValues; + uint64 numTuples = 1; + + // check on the point group index; must be between 1 and 32. + if(psampleModeSelectorValue == orientation_sampling::k_FZModeIndex) + { + if(pCrystalStructureIndex > 10) + { + return MakePreflightErrorResult(-78760, "Crystal Structure Index must be in range [0,10]. See documentation for table of values"); + } + } + + // make sure that the misorientation angle lies in [0,90], and the Euler angles in [0,360], [0,180], [0,360] + if(psampleModeSelectorValue == orientation_sampling::k_ConstantModeIndex || psampleModeSelectorValue == orientation_sampling::k_LessThanModeIndex) + { + numTuples = std::pow((2 * pNumspValue) + 1, 3); + if((pMisOrValue < 0.0) || (pMisOrValue > 90.0)) + { + return MakePreflightErrorResult(-70002, "Misorientation angle must fall in interval [0,90]"); + } + ebsdlib::EulerFType euler(pRefOrValue[0] * nx::core::Constants::k_PiOver180F, pRefOrValue[1] * nx::core::Constants::k_PiOver180F, pRefOrValue[2] * nx::core::Constants::k_PiOver180F); + if(auto result = euler.isValid(); result.result != 1) + { + return MakePreflightErrorResult(-70003, result.msg); + } + } + + // check on the number of sampling intervals (>1) + if(pNumspValue < 1) + { + return MakePreflightErrorResult(-70002, "Number of sampling intervals must be at least 1 "); + } + + // create the destination array for the calculated results + { + auto createArrayAction = std::make_unique(DataType::float32, std::vector{numTuples}, std::vector{3}, pEulerAnglesArrayPath); + resultOutputActions.value().appendAction(std::move(createArrayAction)); + } + + if(psampleModeSelectorValue == orientation_sampling::k_FZModeIndex) + { + // Create the Ensemble AttributeMatrix + nx::core::ShapeType tupleDims = {2}; // Always create 1 extra slot for the phases. + { + auto createAttributeMatrixAction = std::make_unique(ensembleAttributeMatrixPath, tupleDims); + resultOutputActions.value().appendAction(std::move(createAttributeMatrixAction)); + } + + // Create the Crystal Structures Array + nx::core::ShapeType cDims = {1}; + { + DataPath dataArrayPath = ensembleAttributeMatrixPath.createChildPath(ebsdlib::AngFile::CrystalStructures); + auto action = std::make_unique(nx::core::DataType::uint32, tupleDims, cDims, dataArrayPath); + resultOutputActions.value().appendAction(std::move(action)); + } + // Create the Material Names Array + { + DataPath dataArrayPath = ensembleAttributeMatrixPath.createChildPath(ebsdlib::AngFile::MaterialName); + auto action = std::make_unique(tupleDims, dataArrayPath); + resultOutputActions.value().appendAction(std::move(action)); + } + } + + // Return both the resultOutputActions and the preflightUpdatedValues via std::move() + return {std::move(resultOutputActions), std::move(preflightUpdatedValues)}; +} + +//------------------------------------------------------------------------------ +Result<> EMsoftSO3SamplerFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const +{ + + EMsoftSO3SamplerInputValues inputValues; + + inputValues.sampleModeSelector = filterArgs.value(k_SampleModeSelector_Key); + inputValues.CrystalStructureIndex = filterArgs.value(k_CrystalStructure_Index); + inputValues.OffsetGrid = filterArgs.value(k_OffsetGrid_Key); + inputValues.MisOr = filterArgs.value(k_Mode1Misorientation_Key); + inputValues.RefOr = filterArgs.value(k_Mode1EulerAngle_Key); + // inputValues.MisOrFull = filterArgs.value(k_Mode2Misorientation_Key); + // inputValues.RefOrFull = filterArgs.value(k_Mode2EulerAngle_Key); + inputValues.Numsp = filterArgs.value(k_NumberSamples_Key); + inputValues.EulerAnglesArrayPath = filterArgs.value(k_EulerAnglesArrayPath_Key); + + inputValues.EnsembleAttrMatrixPath = filterArgs.value(k_EnsembleAttributeMatrixPath_Key); + + return EMsoftSO3Sampler(dataStructure, messageHandler, shouldCancel, &inputValues)(); +} + +namespace +{ +namespace SIMPL +{ + +} // namespace SIMPL +} // namespace + +//------------------------------------------------------------------------------ +Result EMsoftSO3SamplerFilter::FromSIMPLJson(const nlohmann::json& json) +{ + Arguments args = EMsoftSO3SamplerFilter().getDefaultArguments(); + + std::vector> results; + + Result<> conversionResult = MergeResults(std::move(results)); + + return ConvertResultTo(std::move(conversionResult), std::move(args)); +} + +} // namespace nx::core diff --git a/src/SimplnxReview/Filters/ComputeGroupingDensityFilter.hpp b/src/SimplnxReview/Filters/EMsoftSO3SamplerFilter.hpp similarity index 69% rename from src/SimplnxReview/Filters/ComputeGroupingDensityFilter.hpp rename to src/SimplnxReview/Filters/EMsoftSO3SamplerFilter.hpp index 0614178..30085a5 100644 --- a/src/SimplnxReview/Filters/ComputeGroupingDensityFilter.hpp +++ b/src/SimplnxReview/Filters/EMsoftSO3SamplerFilter.hpp @@ -8,31 +8,38 @@ namespace nx::core { /** - * @class ComputeGroupingDensityFilter - * @brief This filter determines the average C-axis location of each Feature + * @class EMsoftSO3SamplerFilter + * @brief This filter will .... */ -class SIMPLNXREVIEW_EXPORT ComputeGroupingDensityFilter : public IFilter +class SIMPLNXREVIEW_EXPORT EMsoftSO3SamplerFilter : public IFilter { public: - ComputeGroupingDensityFilter() = default; - ~ComputeGroupingDensityFilter() noexcept override = default; + EMsoftSO3SamplerFilter() = default; + ~EMsoftSO3SamplerFilter() noexcept override = default; - ComputeGroupingDensityFilter(const ComputeGroupingDensityFilter&) = delete; - ComputeGroupingDensityFilter(ComputeGroupingDensityFilter&&) noexcept = delete; + EMsoftSO3SamplerFilter(const EMsoftSO3SamplerFilter&) = delete; + EMsoftSO3SamplerFilter(EMsoftSO3SamplerFilter&&) noexcept = delete; - ComputeGroupingDensityFilter& operator=(const ComputeGroupingDensityFilter&) = delete; - ComputeGroupingDensityFilter& operator=(ComputeGroupingDensityFilter&&) noexcept = delete; + EMsoftSO3SamplerFilter& operator=(const EMsoftSO3SamplerFilter&) = delete; + EMsoftSO3SamplerFilter& operator=(EMsoftSO3SamplerFilter&&) noexcept = delete; // Parameter Keys - static constexpr StringLiteral k_VolumesArrayPath_Key = "volumes_path"; - static constexpr StringLiteral k_ContiguousNeighborListArrayPath_Key = "contiguous_neighbor_list_path"; - static constexpr StringLiteral k_UseNonContiguousNeighbors_Key = "use_non_contiguous_neighbors"; - static constexpr StringLiteral k_NonContiguousNeighborListArrayPath_Key = "non_contiguous_neighbor_list_path"; - static constexpr StringLiteral k_ParentIdsPath_Key = "parent_ids_path"; - static constexpr StringLiteral k_ParentVolumesPath_Key = "parent_volumes_path"; - static constexpr StringLiteral k_FindCheckedFeatures_Key = "find_checked_features"; - static constexpr StringLiteral k_CheckedFeaturesName_Key = "checked_features_name"; - static constexpr StringLiteral k_GroupingDensitiesName_Key = "grouping_densities_name"; + static constexpr StringLiteral k_SampleModeSelector_Key = "sample_mode_index"; + static constexpr StringLiteral k_OffsetGrid_Key = "offset_grid"; + + static constexpr StringLiteral k_Mode1Misorientation_Key = "mode_1_misorientation"; + static constexpr StringLiteral k_Mode1EulerAngle_Key = "mode_1_euler_angle"; + + // static constexpr StringLiteral k_Mode2Misorientation_Key = "mode_2_misorientation"; + // static constexpr StringLiteral k_Mode2EulerAngle_Key = "mode_2_euler_angle"; + + static constexpr StringLiteral k_NumberSamples_Key = "number_of_samples"; + static constexpr StringLiteral k_EulerAnglesArrayPath_Key = "output_euler_angles_path"; + + static constexpr StringLiteral k_CrystalStructure_Index = "crystal_structure_index"; + + // static constexpr StringLiteral k_CellAttributeMatrixName_Key = "cell_attribute_matrix_name"; + static constexpr StringLiteral k_EnsembleAttributeMatrixPath_Key = "cell_ensemble_attribute_matrix_path"; /** * @brief Reads SIMPL json and converts it simplnx Arguments. @@ -122,5 +129,5 @@ class SIMPLNXREVIEW_EXPORT ComputeGroupingDensityFilter : public IFilter }; } // namespace nx::core -SIMPLNX_DEF_FILTER_TRAITS(nx::core, ComputeGroupingDensityFilter, "ff46afcf-de32-4f37-98bc-8f0fd4b3c122"); -/* LEGACY UUID FOR THIS FILTER 708be082-8b08-4db2-94be-52781ed4d53d */ +SIMPLNX_DEF_FILTER_TRAITS(nx::core, EMsoftSO3SamplerFilter, "74478e86-ce29-40b8-8c17-d20009195f91"); +/* LEGACY UUID FOR THIS FILTER b78d8825-d3ac-5351-be20-172f07fd2aec */ diff --git a/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp b/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp index d56f487..1d6b90c 100644 --- a/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp +++ b/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp @@ -11,6 +11,7 @@ #include "SimplnxReview/Filters/ComputeMicroTextureRegionsFilter.hpp" #include "SimplnxReview/Filters/ComputeLocalAverageCAxisMisalignmentsFilter.hpp" #include "SimplnxReview/Filters/ComputeGroupingDensityFilter.hpp" +#include "SimplnxReview/Filters/EMsoftSO3SamplerFilter.hpp" // @@__HEADER__TOKEN__DO__NOT__DELETE__@@ @@ -28,6 +29,7 @@ namespace nx::core {nx::core::Uuid::FromString("90f8e3b1-2460-5862-95a1-a9e06f5ee75e").value(), {nx::core::FilterTraits::uuid, &ComputeMicroTextureRegionsFilter::FromSIMPLJson}}, // FindMicroTextureRegions {nx::core::Uuid::FromString("49b2dd47-bb29-50d4-a051-5bad9b6b9f80").value(), {nx::core::FilterTraits::uuid, &ComputeLocalAverageCAxisMisalignmentsFilter::FromSIMPLJson}}, // FindLocalAverageCAxisMisalignments {nx::core::Uuid::FromString("708be082-8b08-4db2-94be-52781ed4d53d").value(), {nx::core::FilterTraits::uuid, &ComputeGroupingDensityFilter::FromSIMPLJson}}, // FindGroupingDensity + {nx::core::Uuid::FromString("b78d8825-d3ac-5351-be20-172f07fd2aec").value(), {nx::core::FilterTraits::uuid, &EMsoftSO3SamplerFilter::FromSIMPLJson}}, // EMsoftSO3Sampler // @@__MAP__UPDATE__TOKEN__DO__NOT__DELETE__@@ }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7811af1..3682954 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,6 +9,7 @@ set(${PLUGIN_NAME}UnitTest_SRCS MergeColoniesTest.cpp ComputeLocalAverageCAxisMisalignmentsTest.cpp ComputeMicroTextureRegionsTest.cpp + EMsoftSO3SamplerTest.cpp ) set(DISABLED_TESTS diff --git a/test/EMsoftSO3SamplerTest.cpp b/test/EMsoftSO3SamplerTest.cpp new file mode 100644 index 0000000..a91454c --- /dev/null +++ b/test/EMsoftSO3SamplerTest.cpp @@ -0,0 +1,64 @@ +/** + * This file is auto generated from the original OrientationAnalysis/EMsoftSO3SamplerFilter + * runtime information. These are the steps that need to be taken to utilize this + * unit test in the proper way. + * + * 1: Validate each of the default parameters that gets created. + * 2: Inspect the actual filter to determine if the filter in its default state + * would pass or fail BOTH the preflight() and execute() methods + * 3: UPDATE the ```REQUIRE(result.result.valid());``` code to have the proper + * + * 4: Add additional unit tests to actually test each code path within the filter + * + * There are some example Catch2 ```TEST_CASE``` sections for your inspiration. + * + * NOTE the format of the ```TEST_CASE``` macro. Please stick to this format to + * allow easier parsing of the unit tests. + * + * When you start working on this unit test remove "[EMsoftSO3SamplerFilter][.][UNIMPLEMENTED]" + * from the TEST_CASE macro. This will enable this unit test to be run by default + * and report errors. + */ + +#include + +#include "simplnx/Parameters/ArrayCreationParameter.hpp" +#include "simplnx/Parameters/BoolParameter.hpp" +#include "simplnx/Parameters/NumberParameter.hpp" +#include "simplnx/Parameters/VectorParameter.hpp" + +#include "SimplnxReview/Filters/EMsoftSO3SamplerFilter.hpp" +#include "SimplnxReview/SimplnxReview_test_dirs.hpp" + +using namespace nx::core; + +TEST_CASE("OrientationAnalysis::EMsoftSO3SamplerFilter: Valid Filter Execution", "[OrientationAnalysis][EMsoftSO3SamplerFilter][.][UNIMPLEMENTED][!mayfail]") +{ + // Instantiate the filter, a DataStructure object and an Arguments Object + EMsoftSO3SamplerFilter filter; + DataStructure ds; + Arguments args; + + // Create default Parameters for the filter. + args.insertOrAssign(EMsoftSO3SamplerFilter::k_CrystalStructure_Index, std::make_any(1)); + args.insertOrAssign(EMsoftSO3SamplerFilter::k_OffsetGrid_Key, std::make_any(false)); + args.insertOrAssign(EMsoftSO3SamplerFilter::k_Mode1Misorientation_Key, std::make_any(2.3456789)); + args.insertOrAssign(EMsoftSO3SamplerFilter::k_Mode1EulerAngle_Key, std::make_any(std::vector(3))); + // args.insertOrAssign(EMsoftSO3SamplerFilter::k_Mode2Misorientation_Key, std::make_any(2.3456789)); + // args.insertOrAssign(EMsoftSO3SamplerFilter::k_Mode2EulerAngle_Key, std::make_any(std::vector(3))); + args.insertOrAssign(EMsoftSO3SamplerFilter::k_NumberSamples_Key, std::make_any(1234356)); + args.insertOrAssign(EMsoftSO3SamplerFilter::k_EulerAnglesArrayPath_Key, std::make_any(DataPath{})); + + // Preflight the filter and check result + auto preflightResult = filter.preflight(ds, args); + REQUIRE(preflightResult.outputActions.valid()); + + // Execute the filter and check the result + auto executeResult = filter.execute(ds, args); + REQUIRE(executeResult.result.valid()); +} + +// TEST_CASE("OrientationAnalysis::EMsoftSO3SamplerFilter: InValid Filter Execution") +//{ +// +// }