From e6148a2d6ddaa84bb06b6c87c8a311217312cc76 Mon Sep 17 00:00:00 2001 From: Joey Kleingers Date: Fri, 27 Sep 2024 08:24:26 -0400 Subject: [PATCH] FILTER: Concatenate Data Arrays (#1072) * Add Concatenate Data Arrays filter with unit test and documentation. Signed-off-by: Joey Kleingers --- src/Plugins/SimplnxCore/CMakeLists.txt | 2 + .../docs/CombineAttributeArraysFilter.md | 2 + .../docs/ConcatenateDataArraysFilter.md | 27 + .../Algorithms/ConcatenateDataArrays.cpp | 51 ++ .../Algorithms/ConcatenateDataArrays.hpp | 169 +++++++ .../Filters/ConcatenateDataArraysFilter.cpp | 173 +++++++ .../Filters/ConcatenateDataArraysFilter.hpp | 91 ++++ src/Plugins/SimplnxCore/test/CMakeLists.txt | 1 + .../test/ConcatenateDataArraysTest.cpp | 462 ++++++++++++++++++ 9 files changed, 978 insertions(+) create mode 100644 src/Plugins/SimplnxCore/docs/ConcatenateDataArraysFilter.md create mode 100644 src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ConcatenateDataArrays.cpp create mode 100644 src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ConcatenateDataArrays.hpp create mode 100644 src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ConcatenateDataArraysFilter.cpp create mode 100644 src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ConcatenateDataArraysFilter.hpp create mode 100644 src/Plugins/SimplnxCore/test/ConcatenateDataArraysTest.cpp diff --git a/src/Plugins/SimplnxCore/CMakeLists.txt b/src/Plugins/SimplnxCore/CMakeLists.txt index 0b9c01bf98..703db545ed 100644 --- a/src/Plugins/SimplnxCore/CMakeLists.txt +++ b/src/Plugins/SimplnxCore/CMakeLists.txt @@ -44,6 +44,7 @@ set(FilterList ComputeVectorColorsFilter ComputeVertexToTriangleDistancesFilter ComputeVolumeFractionsFilter + ConcatenateDataArraysFilter ConditionalSetValueFilter ConvertColorToGrayScaleFilter ConvertDataFilter @@ -168,6 +169,7 @@ set(AlgorithmList ComputeTriangleGeomCentroids ComputeVectorColors ComputeVertexToTriangleDistances + ConcatenateDataArrays ConvertColorToGrayScale ConvertData CreatePythonSkeleton diff --git a/src/Plugins/SimplnxCore/docs/CombineAttributeArraysFilter.md b/src/Plugins/SimplnxCore/docs/CombineAttributeArraysFilter.md index cd42100752..7284e2f57c 100644 --- a/src/Plugins/SimplnxCore/docs/CombineAttributeArraysFilter.md +++ b/src/Plugins/SimplnxCore/docs/CombineAttributeArraysFilter.md @@ -30,6 +30,8 @@ _Combined (vector first)_: tuple 1 - { v1 v2 v3 s1 } ; tuple 2 - { v1 v2 v3 s1 } The user may also select to normalize the resulting combined array. The normalization procedure will enforce a range of [0, 1] for all values in the combined array. This may be useful for combining two arrays that have different order of magnitude data. Note that this option will fundamentally change the underlying data in the combined array from the original incoming arrays. +**NOTE:** If you are wanting to instead concatenate/append data arrays together into a longer array, please see the [Concatenate Data Arrays](ConcatenateDataArraysFilter.md) filter. + % Auto generated parameter table will be inserted here ## Example Pipelines diff --git a/src/Plugins/SimplnxCore/docs/ConcatenateDataArraysFilter.md b/src/Plugins/SimplnxCore/docs/ConcatenateDataArraysFilter.md new file mode 100644 index 0000000000..edabd3f966 --- /dev/null +++ b/src/Plugins/SimplnxCore/docs/ConcatenateDataArraysFilter.md @@ -0,0 +1,27 @@ +# Concatenate Data Arrays + +## Group (Subgroup) + +Core (Generation) + +## Description + +This **Filter** concatenates multiple input arrays by taking a list of input arrays and appending their data sequentially into a single output array. The concatenation process involves combining the arrays such that the order of the input arrays directly affects the structure of the output. For example, if the first input array contains 5 tuples and the second contains 7 tuples, the resulting output array will have 12 tuples, with the tuples from the second array appended directly after those from the first array. + +This filter will always output an array with tuple dimensions that are 1-dimensional. + +This filter is designed to handle arrays of matching array types and component dimensions. If the arrays have different array types or component dimensions, the filter will raise an error. + +**NOTE:** If you are wanting to instead combine data arrays into a multi-component array, please see the [Combine Attribute Arrays](CombineAttributeArraysFilter.md) filter. + +% Auto generated parameter table will be inserted here + +## 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/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ConcatenateDataArrays.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ConcatenateDataArrays.cpp new file mode 100644 index 0000000000..59d4d8f37f --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ConcatenateDataArrays.cpp @@ -0,0 +1,51 @@ +#include "ConcatenateDataArrays.hpp" + +#include "simplnx/DataStructure/IArray.hpp" + +using namespace nx::core; + +// ----------------------------------------------------------------------------- +ConcatenateDataArrays::ConcatenateDataArrays(DataStructure& dataStructure, const IFilter::MessageHandler& msgHandler, const std::atomic_bool& shouldCancel, + ConcatenateDataArraysInputValues* inputValues) +: m_DataStructure(dataStructure) +, m_InputValues(inputValues) +, m_ShouldCancel(shouldCancel) +, m_MessageHandler(msgHandler) +{ +} + +// ----------------------------------------------------------------------------- +ConcatenateDataArrays::~ConcatenateDataArrays() noexcept = default; + +// ----------------------------------------------------------------------------- +const std::atomic_bool& ConcatenateDataArrays::getCancel() +{ + return m_ShouldCancel; +} + +// ----------------------------------------------------------------------------- +Result<> ConcatenateDataArrays::operator()() +{ + const auto& outputDataArray = m_DataStructure.getDataRefAs(m_InputValues->OutputArrayPath); + std::string arrayTypeName = outputDataArray.getTypeName(); + switch(outputDataArray.getArrayType()) + { + case IArray::ArrayType::DataArray: { + return ConcatenateArrays(m_DataStructure, m_InputValues->InputArrayPaths, m_InputValues->OutputArrayPath, m_MessageHandler, m_ShouldCancel); + } + case IArray::ArrayType::StringArray: { + return ConcatenateArraysImpl(m_DataStructure, m_InputValues->InputArrayPaths, m_InputValues->OutputArrayPath, m_MessageHandler, m_ShouldCancel); + } + case IArray::ArrayType::NeighborListArray: { + return ConcatenateNeighborLists(m_DataStructure, m_InputValues->InputArrayPaths, m_InputValues->OutputArrayPath, m_MessageHandler, m_ShouldCancel); + } + case IArray::ArrayType::Any: { + return MakeErrorResult(to_underlying(ConcatenateDataArrays::ErrorCodes::InputArraysEqualAny), + "Every array in the input arrays list has array type 'Any'. This SHOULD NOT be possible, so please contact the developers."); + } + default: { + return MakeErrorResult(to_underlying(ConcatenateDataArrays::ErrorCodes::InputArraysUnsupported), + "Every array in the input arrays list has array type '{}'. This is an array type that is currently not supported by this filter, so please contact the developers."); + } + } +} diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ConcatenateDataArrays.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ConcatenateDataArrays.hpp new file mode 100644 index 0000000000..c1d12966df --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ConcatenateDataArrays.hpp @@ -0,0 +1,169 @@ +#pragma once + +#include "SimplnxCore/SimplnxCore_export.hpp" + +#include "simplnx/DataStructure/DataArray.hpp" +#include "simplnx/DataStructure/DataStructure.hpp" +#include "simplnx/DataStructure/NeighborList.hpp" +#include "simplnx/DataStructure/StringArray.hpp" +#include "simplnx/Filter/IFilter.hpp" +#include "simplnx/Utilities/DataArrayUtilities.hpp" +#include "simplnx/Utilities/FilterUtilities.hpp" + +namespace nx::core +{ +template +struct is_allowed_array_type : std::false_type +{ +}; + +template +struct is_allowed_array_type> : std::true_type +{ +}; + +template <> +struct is_allowed_array_type : std::true_type +{ +}; + +template +typename std::enable_if::value, Result<>>::type ConcatenateArraysImpl(DataStructure& dataStructure, const std::vector& inputArrayPaths, + const DataPath& outputArrayPath, const IFilter::MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) +{ + auto& outputDataArray = dataStructure.getDataRefAs(outputArrayPath); + usize destTupleOffset = 0; + for(const auto& inputArrayPath : inputArrayPaths) + { + if(shouldCancel) + { + return {}; + } + + messageHandler({IFilter::Message::Type::Info, fmt::format("Concatenating array '{}'...", inputArrayPath.toString())}); + const auto& inputDataArray = dataStructure.getDataRefAs(inputArrayPath); + auto result = CopyFromArray::CopyData(inputDataArray, outputDataArray, destTupleOffset, 0, inputDataArray.getNumberOfTuples()); + if(result.invalid()) + { + return result; + } + destTupleOffset += inputDataArray.getNumberOfTuples(); + } + + return {}; +} + +template +Result<> ConcatenateNeighborListsImpl(DataStructure& dataStructure, const std::vector& inputArrayPaths, const DataPath& outputArrayPath, const IFilter::MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) +{ + auto& outputNeighborList = dataStructure.getDataRefAs>(outputArrayPath); + int32 currentOutputTuple = 0; + for(const auto& inputNeighborListPath : inputArrayPaths) + { + if(shouldCancel) + { + return {}; + } + + messageHandler({IFilter::Message::Type::Info, fmt::format("Concatenating neighbor list '{}'...", inputNeighborListPath.toString())}); + const auto& inputNeighborList = dataStructure.getDataRefAs>(inputNeighborListPath); + for(int32 listIdx = 0; listIdx < inputNeighborList.getNumberOfLists(); ++listIdx) + { + outputNeighborList.setList(currentOutputTuple, inputNeighborList.getList(listIdx)); + currentOutputTuple++; + } + } + + return {}; +} + +struct SIMPLNXCORE_EXPORT ConcatenateDataArraysInputValues +{ + std::vector InputArrayPaths; + DataPath OutputArrayPath; +}; + +/** + * @class + */ +class SIMPLNXCORE_EXPORT ConcatenateDataArrays +{ +public: + ConcatenateDataArrays(DataStructure& dataStructure, const IFilter::MessageHandler& msgHandler, const std::atomic_bool& shouldCancel, ConcatenateDataArraysInputValues* inputValues); + ~ConcatenateDataArrays() noexcept; + + ConcatenateDataArrays(const ConcatenateDataArrays&) = delete; + ConcatenateDataArrays(ConcatenateDataArrays&&) noexcept = delete; + ConcatenateDataArrays& operator=(const ConcatenateDataArrays&) = delete; + ConcatenateDataArrays& operator=(ConcatenateDataArrays&&) noexcept = delete; + + // Error Codes + enum class ErrorCodes : int32 + { + EmptyInputArrays = -2300, + OneInputArray = -2301, + NonPositiveTupleDimValue = -2302, + TypeNameMismatch = -2303, + ComponentShapeMismatch = -2304, + InputArraysEqualAny = -2305, + InputArraysUnsupported = -2306 + }; + + // Warning Codes + enum class WarningCodes : int32 + { + MultipleTupleDimsNotSupported = -100 + }; + + Result<> operator()(); + + const std::atomic_bool& getCancel(); + +private: + DataStructure& m_DataStructure; + const ConcatenateDataArraysInputValues* m_InputValues = nullptr; + const std::atomic_bool& m_ShouldCancel; + const IFilter::MessageHandler& m_MessageHandler; + + struct ConcatenateDataArraysTemplateImpl + { + template + void operator()(DataStructure& dataStructure, const std::vector& inputArrayPaths, const DataPath& outputArrayPath, const IFilter::MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel, Result<>& result) + { + result = ConcatenateArraysImpl>(dataStructure, inputArrayPaths, outputArrayPath, messageHandler, shouldCancel); + } + }; + + struct ConcatenateNeighborListsTemplateImpl + { + template + void operator()(DataStructure& dataStructure, const std::vector& inputArrayPaths, const DataPath& outputArrayPath, const IFilter::MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel, Result<>& result) + { + result = ConcatenateNeighborListsImpl(dataStructure, inputArrayPaths, outputArrayPath, messageHandler, shouldCancel); + } + }; + + static Result<> ConcatenateArrays(DataStructure& dataStructure, const std::vector& inputArrayPaths, const DataPath& outputArrayPath, const IFilter::MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) + { + const auto& outputDataArray = dataStructure.getDataRefAs(outputArrayPath); + Result<> result; + ExecuteDataFunction(ConcatenateDataArraysTemplateImpl{}, outputDataArray.getDataType(), dataStructure, inputArrayPaths, outputArrayPath, messageHandler, shouldCancel, result); + return result; + } + + static Result<> ConcatenateNeighborLists(DataStructure& dataStructure, const std::vector& inputArrayPaths, const DataPath& outputArrayPath, const IFilter::MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) + { + const auto& outputNeighborList = dataStructure.getDataRefAs(outputArrayPath); + Result<> result; + ExecuteNeighborFunction(ConcatenateNeighborListsTemplateImpl{}, outputNeighborList.getDataType(), dataStructure, inputArrayPaths, outputArrayPath, messageHandler, shouldCancel, result); + return result; + } +}; + +} // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ConcatenateDataArraysFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ConcatenateDataArraysFilter.cpp new file mode 100644 index 0000000000..5182404f05 --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ConcatenateDataArraysFilter.cpp @@ -0,0 +1,173 @@ +#include "ConcatenateDataArraysFilter.hpp" + +#include "simplnx/DataStructure/DataPath.hpp" +#include "simplnx/DataStructure/IDataArray.hpp" +#include "simplnx/Filter/Actions/CreateArrayAction.hpp" +#include "simplnx/Filter/Actions/CreateNeighborListAction.hpp" +#include "simplnx/Filter/Actions/CreateStringArrayAction.hpp" +#include "simplnx/Parameters/ArrayCreationParameter.hpp" +#include "simplnx/Parameters/DynamicTableParameter.hpp" +#include "simplnx/Parameters/MultiArraySelectionParameter.hpp" +#include "simplnx/Utilities/DataArrayUtilities.hpp" + +#include "SimplnxCore/Filters/Algorithms/ConcatenateDataArrays.hpp" + +using namespace nx::core; + +namespace nx::core +{ +//------------------------------------------------------------------------------ +std::string ConcatenateDataArraysFilter::name() const +{ + return FilterTraits::name; +} + +//------------------------------------------------------------------------------ +std::string ConcatenateDataArraysFilter::className() const +{ + return FilterTraits::className; +} + +//------------------------------------------------------------------------------ +Uuid ConcatenateDataArraysFilter::uuid() const +{ + return FilterTraits::uuid; +} + +//------------------------------------------------------------------------------ +std::string ConcatenateDataArraysFilter::humanName() const +{ + return "Concatenate Data Arrays"; +} + +//------------------------------------------------------------------------------ +std::vector ConcatenateDataArraysFilter::defaultTags() const +{ + return {className(), "Concatenate", "Data Array", "Join", "Make", "Append", "Push", "Pushback"}; +} + +//------------------------------------------------------------------------------ +Parameters ConcatenateDataArraysFilter::parameters() const +{ + Parameters params; + + params.insertSeparator(Parameters::Separator{"Input Parameters"}); + params.insert(std::make_unique(k_InputArrays_Key, "Arrays To Concatenate", + "Select the arrays that will be concatenated together. The arrays will be concatenated in the order they are listed here.", + std::vector{}, MultiArraySelectionParameter::AllowedTypes{IArray::ArrayType::Any}, GetAllDataTypes())); + + params.insertSeparator(Parameters::Separator{"Output Parameters"}); + params.insert(std::make_unique(k_OutputArray_Key, "Output Array", "The output array that contains the concatenated arrays.", DataPath({"Concatenated Array"}))); + + return params; +} + +//------------------------------------------------------------------------------ +IFilter::UniquePointer ConcatenateDataArraysFilter::clone() const +{ + return std::make_unique(); +} + +//------------------------------------------------------------------------------ +IFilter::PreflightResult ConcatenateDataArraysFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + auto inputArrayPaths = filterArgs.value(k_InputArrays_Key); + auto outputArrayPath = filterArgs.value(k_OutputArray_Key); + + if(inputArrayPaths.empty()) + { + return MakePreflightErrorResult(to_underlying(ConcatenateDataArrays::ErrorCodes::EmptyInputArrays), "No input arrays have been selected. Please select at least 2 input arrays."); + } + + if(inputArrayPaths.size() == 1) + { + return MakePreflightErrorResult(to_underlying(ConcatenateDataArrays::ErrorCodes::OneInputArray), "Only one input array has been selected. Please select at least 2 input arrays."); + } + + // Check for unequal array types, data types, and component dimensions + std::vector cDims; + IArray::ArrayType arrayType; + std::string arrayTypeName; + usize numTuples = 0; + for(usize i = 0; i < inputArrayPaths.size(); ++i) + { + const auto& inputDataArray = dataStructure.getDataRefAs(inputArrayPaths[i]); + for(usize j = i + 1; j < inputArrayPaths.size(); ++j) + { + const auto& inputDataArray2 = dataStructure.getDataRefAs(inputArrayPaths[j]); + + if(inputDataArray.getTypeName() != inputDataArray2.getTypeName()) + { + return MakePreflightErrorResult(to_underlying(ConcatenateDataArrays::ErrorCodes::TypeNameMismatch), + fmt::format("Input array '{}' has array type '{}', but input array '{}' has array type '{}'. The array types must match.", inputArrayPaths[i].toString(), + inputDataArray.getTypeName(), inputArrayPaths[j].toString(), inputDataArray2.getTypeName())); + } + + if(inputDataArray.getComponentShape() != inputDataArray2.getComponentShape()) + { + return MakePreflightErrorResult(to_underlying(ConcatenateDataArrays::ErrorCodes::ComponentShapeMismatch), + fmt::format("Input array '{}' has component shape '{}', but input array '{}' has component shape '{}'. The component shapes must match.", + inputArrayPaths[i].toString(), fmt::join(inputDataArray.getComponentShape(), ","), inputArrayPaths[j].toString(), + fmt::join(inputDataArray2.getComponentShape(), ","))); + } + + cDims = inputDataArray.getComponentShape(); + arrayType = inputDataArray.getArrayType(); + arrayTypeName = inputDataArray.getTypeName(); + } + + auto tupleShape = inputDataArray.getTupleShape(); + numTuples += std::accumulate(tupleShape.begin(), tupleShape.end(), static_cast(1), std::multiplies<>()); + } + + std::vector tDims = {numTuples}; + + // Create the output array + nx::core::Result resultOutputActions; + + switch(arrayType) + { + case IArray::ArrayType::DataArray: { + const auto& inputDataArray = dataStructure.getDataRefAs(inputArrayPaths[0]); + auto action = std::make_unique(inputDataArray.getDataType(), tDims, cDims, outputArrayPath); + resultOutputActions.value().appendAction(std::move(action)); + break; + } + case IArray::ArrayType::StringArray: { + auto action = std::make_unique(tDims, outputArrayPath); + resultOutputActions.value().appendAction(std::move(action)); + break; + } + case IArray::ArrayType::NeighborListArray: { + const auto& inputNeighborList = dataStructure.getDataRefAs(inputArrayPaths[0]); + auto action = std::make_unique(inputNeighborList.getDataType(), numTuples, outputArrayPath); + resultOutputActions.value().appendAction(std::move(action)); + break; + } + case IArray::ArrayType::Any: { + return MakePreflightErrorResult(to_underlying(ConcatenateDataArrays::ErrorCodes::InputArraysEqualAny), + "Every array in the input arrays list has array type 'Any'. This SHOULD NOT be possible, so please contact the developers."); + } + default: + return MakePreflightErrorResult( + to_underlying(ConcatenateDataArrays::ErrorCodes::InputArraysUnsupported), + fmt::format("Every array in the input arrays list has array type '{}'. This is an array type that is currently not supported by this filter, so please contact the developers.", + arrayTypeName)); + } + + return {std::move(resultOutputActions)}; +} + +//------------------------------------------------------------------------------ +Result<> ConcatenateDataArraysFilter::executeImpl(DataStructure& dataStructure, const Arguments& args, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const +{ + ConcatenateDataArraysInputValues inputValues; + + inputValues.InputArrayPaths = args.value(k_InputArrays_Key); + inputValues.OutputArrayPath = args.value(k_OutputArray_Key); + + return ConcatenateDataArrays(dataStructure, messageHandler, shouldCancel, &inputValues)(); +} +} // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ConcatenateDataArraysFilter.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ConcatenateDataArraysFilter.hpp new file mode 100644 index 0000000000..604c373bea --- /dev/null +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ConcatenateDataArraysFilter.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include "SimplnxCore/SimplnxCore_export.hpp" + +#include "simplnx/Filter/FilterTraits.hpp" +#include "simplnx/Filter/IFilter.hpp" + +namespace nx::core +{ +class SIMPLNXCORE_EXPORT ConcatenateDataArraysFilter : public IFilter +{ +public: + ConcatenateDataArraysFilter() = default; + ~ConcatenateDataArraysFilter() noexcept override = default; + + ConcatenateDataArraysFilter(const ConcatenateDataArraysFilter&) = delete; + ConcatenateDataArraysFilter(ConcatenateDataArraysFilter&&) noexcept = delete; + + ConcatenateDataArraysFilter& operator=(const ConcatenateDataArraysFilter&) = delete; + ConcatenateDataArraysFilter& operator=(ConcatenateDataArraysFilter&&) noexcept = delete; + + // Parameter Keys + static inline constexpr StringLiteral k_InputArrays_Key = "input_arrays"; + static inline constexpr StringLiteral k_OutputArray_Key = "output_array_path"; + + /** + * @brief Returns the filter's name. + * @return std::string + */ + std::string name() const override; + + /** + * @brief Returns the C++ classname of this filter. + * @return std::string + */ + std::string className() const override; + + /** + * @brief Returns the filter's UUID. + * @return Uuid + */ + Uuid uuid() const override; + + /** + * @brief Returns the filter name as a human-readable string. + * @return std::string + */ + std::string humanName() const override; + + /** + * @brief Returns the default tags for this filter. + * @return + */ + std::vector defaultTags() const override; + + /** + * @brief Returns a collection of parameters required to execute the filter. + * @return Parameters + */ + Parameters parameters() const override; + + /** + * @brief Creates and returns a copy of the filter. + * @return UniquePointer + */ + UniquePointer clone() const override; + +protected: + /** + * @brief + * @param data + * @param filterArgs + * @param messageHandler + * @return Result + */ + PreflightResult preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const override; + + /** + * @brief + * @param dataStructure + * @param args + * @param pipelineNode + * @param messageHandler + * @return Result<> + */ + Result<> executeImpl(DataStructure& dataStructure, const Arguments& args, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, + const std::atomic_bool& shouldCancel) const override; +}; +} // namespace nx::core + +SIMPLNX_DEF_FILTER_TRAITS(nx::core, ConcatenateDataArraysFilter, "e547e382-4928-4ec6-a311-d26c16e9f675"); diff --git a/src/Plugins/SimplnxCore/test/CMakeLists.txt b/src/Plugins/SimplnxCore/test/CMakeLists.txt index 691dc537d1..f1b21d67cd 100644 --- a/src/Plugins/SimplnxCore/test/CMakeLists.txt +++ b/src/Plugins/SimplnxCore/test/CMakeLists.txt @@ -44,6 +44,7 @@ set(${PLUGIN_NAME}UnitTest_SRCS ComputeVectorColorsTest.cpp ComputeVertexToTriangleDistancesTest.cpp ComputeVolumeFractionsTest.cpp + ConcatenateDataArraysTest.cpp ConditionalSetValueTest.cpp ConvertColorToGrayScaleTest.cpp ConvertDataTest.cpp diff --git a/src/Plugins/SimplnxCore/test/ConcatenateDataArraysTest.cpp b/src/Plugins/SimplnxCore/test/ConcatenateDataArraysTest.cpp new file mode 100644 index 0000000000..06026e0cec --- /dev/null +++ b/src/Plugins/SimplnxCore/test/ConcatenateDataArraysTest.cpp @@ -0,0 +1,462 @@ +#include "SimplnxCore/Filters/Algorithms/ConcatenateDataArrays.hpp" +#include "SimplnxCore/Filters/ConcatenateDataArraysFilter.hpp" +#include "SimplnxCore/SimplnxCore_test_dirs.hpp" + +#include "simplnx/Parameters/DynamicTableParameter.hpp" +#include "simplnx/UnitTest/UnitTestCommon.hpp" + +#include + +using namespace nx::core; + +namespace +{ +const std::string k_TestArray1Name = "TestArray1"; +const std::string k_TestArray2Name = "TestArray2"; +const std::string k_TestResultArrayName = "TestResultArray"; +const std::vector k_InputArrayPaths = std::vector{DataPath({k_TestArray1Name}), DataPath({k_TestArray2Name})}; +const DataPath k_OutputArrayPath = DataPath({k_TestResultArrayName}); +} // namespace + +TEMPLATE_TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: DataArrays Valid - 1 Tuple", "[SimplnxCore][ConcatenateDataArraysFilter]", bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64, + float32, float64) +{ + using T = TestType; + + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + auto array1 = DataArray::template CreateWithStore>(dataStructure, k_TestArray1Name, std::vector{1}, std::vector{3}); + array1->setComponent(0, 0, 0); + array1->setComponent(0, 1, 1); + array1->setComponent(0, 2, 0); + auto array2 = DataArray::template CreateWithStore>(dataStructure, k_TestArray2Name, std::vector{1}, std::vector{3}); + array2->setComponent(0, 0, 1); + array2->setComponent(0, 1, 1); + array2->setComponent(0, 2, 1); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(k_InputArrayPaths)); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); + + REQUIRE_NOTHROW(dataStructure.getDataRefAs>(k_OutputArrayPath)); + auto outputArray = dataStructure.getDataRefAs>(k_OutputArrayPath); + + REQUIRE(outputArray.getTupleShape() == std::vector{2}); + REQUIRE(outputArray.getNumberOfComponents() == 3); + REQUIRE(outputArray[0] == 0); + REQUIRE(outputArray[1] == 1); + REQUIRE(outputArray[2] == 0); + REQUIRE(outputArray[3] == 1); + REQUIRE(outputArray[4] == 1); + REQUIRE(outputArray[5] == 1); +} + +TEMPLATE_TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: DataArrays Valid - 2 Tuples", "[SimplnxCore][ConcatenateDataArraysFilter]", bool, int8, int16, int32, int64, uint8, uint16, uint32, + uint64, float32, float64) +{ + using T = TestType; + + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + auto array1 = DataArray::template CreateWithStore>(dataStructure, k_TestArray1Name, std::vector{2}, std::vector{2}); + array1->setComponent(0, 0, 0); + array1->setComponent(0, 1, 1); + array1->setComponent(1, 0, 0); + array1->setComponent(1, 1, 0); + auto array2 = DataArray::template CreateWithStore>(dataStructure, k_TestArray2Name, std::vector{2}, std::vector{2}); + array2->setComponent(0, 0, 1); + array2->setComponent(0, 1, 1); + array2->setComponent(1, 0, 1); + array2->setComponent(1, 1, 0); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(k_InputArrayPaths)); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); + + REQUIRE_NOTHROW(dataStructure.getDataRefAs>(k_OutputArrayPath)); + auto outputArray = dataStructure.getDataRefAs>(k_OutputArrayPath); + + REQUIRE(outputArray.getTupleShape() == std::vector{4}); + REQUIRE(outputArray.getNumberOfComponents() == 2); + REQUIRE(outputArray[0] == 0); + REQUIRE(outputArray[1] == 1); + REQUIRE(outputArray[2] == 0); + REQUIRE(outputArray[3] == 0); + REQUIRE(outputArray[4] == 1); + REQUIRE(outputArray[5] == 1); + REQUIRE(outputArray[6] == 1); + REQUIRE(outputArray[7] == 0); +} + +TEMPLATE_TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: DataArrays Valid - 3 Tuples", "[SimplnxCore][ConcatenateDataArraysFilter]", bool, int8, int16, int32, int64, uint8, uint16, uint32, + uint64, float32, float64) +{ + using T = TestType; + + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + auto array1 = DataArray::template CreateWithStore>(dataStructure, k_TestArray1Name, std::vector{3}, std::vector{1}); + array1->setValue(0, 0); + array1->setValue(1, 1); + array1->setValue(2, 0); + auto array2 = DataArray::template CreateWithStore>(dataStructure, k_TestArray2Name, std::vector{3}, std::vector{1}); + array2->setValue(0, 1); + array2->setValue(1, 1); + array2->setValue(2, 1); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(k_InputArrayPaths)); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); + + REQUIRE_NOTHROW(dataStructure.getDataRefAs>(k_OutputArrayPath)); + auto outputArray = dataStructure.getDataRefAs>(k_OutputArrayPath); + + REQUIRE(outputArray.getTupleShape() == std::vector{6}); + REQUIRE(outputArray.getNumberOfComponents() == 1); + REQUIRE(outputArray[0] == 0); + REQUIRE(outputArray[1] == 1); + REQUIRE(outputArray[2] == 0); + REQUIRE(outputArray[3] == 1); + REQUIRE(outputArray[4] == 1); + REQUIRE(outputArray[5] == 1); +} + +TEMPLATE_TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: DataArrays Valid - 8 Tuples", "[SimplnxCore][ConcatenateDataArraysFilter]", bool, int8, int16, int32, int64, uint8, uint16, uint32, + uint64, float32, float64) +{ + using T = TestType; + + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + auto array1 = DataArray::template CreateWithStore>(dataStructure, k_TestArray1Name, std::vector{2, 2}, std::vector{1}); + array1->setValue(0, 0); + array1->setValue(1, 1); + array1->setValue(2, 0); + array1->setValue(3, 0); + auto array2 = DataArray::template CreateWithStore>(dataStructure, k_TestArray2Name, std::vector{2, 2}, std::vector{1}); + array2->setValue(0, 1); + array2->setValue(1, 1); + array2->setValue(2, 1); + array2->setValue(3, 0); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(k_InputArrayPaths)); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); + + REQUIRE_NOTHROW(dataStructure.getDataRefAs>(k_OutputArrayPath)); + auto outputArray = dataStructure.getDataRefAs>(k_OutputArrayPath); + + REQUIRE(outputArray.getTupleShape() == std::vector{8}); + REQUIRE(outputArray.getNumberOfComponents() == 1); + REQUIRE(outputArray[0] == 0); + REQUIRE(outputArray[1] == 1); + REQUIRE(outputArray[2] == 0); + REQUIRE(outputArray[3] == 0); + REQUIRE(outputArray[4] == 1); + REQUIRE(outputArray[5] == 1); + REQUIRE(outputArray[6] == 1); + REQUIRE(outputArray[7] == 0); +} + +TEMPLATE_TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: NeighborLists Valid - 1 List", "[SimplnxCore][ConcatenateDataArraysFilter]", int8, int16, int32, int64, uint8, uint16, uint32, uint64, + float32, float64) +{ + using T = TestType; + + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + auto inputNeighborList1 = NeighborList::Create(dataStructure, k_TestArray1Name, 1); + typename NeighborList::SharedVectorType inputList1(new std::vector({0, 1, 0})); + inputNeighborList1->setList(0, inputList1); + auto inputNeighborList2 = NeighborList::Create(dataStructure, k_TestArray2Name, 1); + typename NeighborList::SharedVectorType inputList2(new std::vector({1, 1, 1})); + inputNeighborList2->setList(0, inputList2); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(k_InputArrayPaths)); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); + + REQUIRE_NOTHROW(dataStructure.getDataRefAs>(k_OutputArrayPath)); + auto outputNeighborList = dataStructure.getDataRefAs>(k_OutputArrayPath); + + REQUIRE(outputNeighborList.getTupleShape() == std::vector{2}); + auto outputList1 = outputNeighborList.getList(0); + auto outputList2 = outputNeighborList.getList(1); + + REQUIRE(outputList1 == inputList1); + REQUIRE(outputList2 == inputList2); +} + +TEMPLATE_TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: NeighborLists Valid - 2 Lists", "[SimplnxCore][ConcatenateDataArraysFilter]", int8, int16, int32, int64, uint8, uint16, uint32, uint64, + float32, float64) +{ + using T = TestType; + + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + auto inputNeighborList1 = NeighborList::Create(dataStructure, k_TestArray1Name, 2); + typename NeighborList::SharedVectorType inputList1(new std::vector({0, 1, 0})); + inputNeighborList1->setList(0, inputList1); + typename NeighborList::SharedVectorType inputList2(new std::vector({1, 0, 0})); + inputNeighborList1->setList(1, inputList2); + auto inputNeighborList2 = NeighborList::Create(dataStructure, k_TestArray2Name, 2); + typename NeighborList::SharedVectorType inputList3(new std::vector({1, 1, 1})); + inputNeighborList2->setList(0, inputList3); + typename NeighborList::SharedVectorType inputList4(new std::vector({0, 0, 1})); + inputNeighborList2->setList(1, inputList4); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(k_InputArrayPaths)); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); + + REQUIRE_NOTHROW(dataStructure.getDataRefAs>(k_OutputArrayPath)); + auto outputNeighborList = dataStructure.getDataRefAs>(k_OutputArrayPath); + + REQUIRE(outputNeighborList.getTupleShape() == std::vector{4}); + auto outputList1 = outputNeighborList.getList(0); + auto outputList2 = outputNeighborList.getList(1); + auto outputList3 = outputNeighborList.getList(2); + auto outputList4 = outputNeighborList.getList(3); + + REQUIRE(outputList1 == inputList1); + REQUIRE(outputList2 == inputList2); + REQUIRE(outputList3 == inputList3); + REQUIRE(outputList4 == inputList4); +} + +TEMPLATE_TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: NeighborLists Valid - 3 Lists", "[SimplnxCore][ConcatenateDataArraysFilter]", int8, int16, int32, int64, uint8, uint16, uint32, uint64, + float32, float64) +{ + using T = TestType; + + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + auto inputNeighborList1 = NeighborList::Create(dataStructure, k_TestArray1Name, 3); + typename NeighborList::SharedVectorType inputList1(new std::vector({0, 1, 0})); + inputNeighborList1->setList(0, inputList1); + typename NeighborList::SharedVectorType inputList2(new std::vector({1, 0, 0})); + inputNeighborList1->setList(1, inputList2); + typename NeighborList::SharedVectorType inputList3(new std::vector({2, 2, 1})); + inputNeighborList1->setList(2, inputList3); + auto inputNeighborList2 = NeighborList::Create(dataStructure, k_TestArray2Name, 3); + typename NeighborList::SharedVectorType inputList4(new std::vector({1, 1, 1})); + inputNeighborList2->setList(0, inputList4); + typename NeighborList::SharedVectorType inputList5(new std::vector({0, 0, 1})); + inputNeighborList2->setList(1, inputList5); + typename NeighborList::SharedVectorType inputList6(new std::vector({4, 5, 6})); + inputNeighborList2->setList(2, inputList6); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(k_InputArrayPaths)); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); + + REQUIRE_NOTHROW(dataStructure.getDataRefAs>(k_OutputArrayPath)); + auto outputNeighborList = dataStructure.getDataRefAs>(k_OutputArrayPath); + + REQUIRE(outputNeighborList.getTupleShape() == std::vector{6}); + auto outputList1 = outputNeighborList.getList(0); + auto outputList2 = outputNeighborList.getList(1); + auto outputList3 = outputNeighborList.getList(2); + auto outputList4 = outputNeighborList.getList(3); + auto outputList5 = outputNeighborList.getList(4); + auto outputList6 = outputNeighborList.getList(5); + + REQUIRE(outputList1 == inputList1); + REQUIRE(outputList2 == inputList2); + REQUIRE(outputList3 == inputList3); + REQUIRE(outputList4 == inputList4); + REQUIRE(outputList5 == inputList5); + REQUIRE(outputList6 == inputList6); +} + +TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: StringArray Valid - 1 Tuple", "[SimplnxCore][ConcatenateDataArraysFilter]") +{ + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + std::string value1 = "Foo"; + std::string value2 = "Bar"; + + StringArray::CreateWithValues(dataStructure, k_TestArray1Name, {value1}); + StringArray::CreateWithValues(dataStructure, k_TestArray2Name, {value2}); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(k_InputArrayPaths)); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); + + REQUIRE_NOTHROW(dataStructure.getDataRefAs(k_OutputArrayPath)); + auto outputArray = dataStructure.getDataRefAs(k_OutputArrayPath); + + REQUIRE(outputArray.getTupleShape() == std::vector{2}); + REQUIRE(outputArray[0] == value1); + REQUIRE(outputArray[1] == value2); +} + +TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: StringArray Valid - 2 Tuples", "[SimplnxCore][ConcatenateDataArraysFilter]") +{ + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + std::string value1 = "Foo"; + std::string value2 = "Bar"; + std::string value3 = "Baz"; + std::string value4 = "Fizzle"; + + StringArray::CreateWithValues(dataStructure, k_TestArray1Name, {value1, value2}); + StringArray::CreateWithValues(dataStructure, k_TestArray2Name, {value3, value4}); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(k_InputArrayPaths)); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); + + REQUIRE_NOTHROW(dataStructure.getDataRefAs(k_OutputArrayPath)); + auto outputArray = dataStructure.getDataRefAs(k_OutputArrayPath); + + REQUIRE(outputArray.getTupleShape() == std::vector{4}); + REQUIRE(outputArray[0] == value1); + REQUIRE(outputArray[1] == value2); + REQUIRE(outputArray[2] == value3); + REQUIRE(outputArray[3] == value4); +} + +TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: StringArray Valid - 3 Tuples", "[SimplnxCore][ConcatenateDataArraysFilter]") +{ + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + std::string value1 = "Foo"; + std::string value2 = "Bar"; + std::string value3 = "Baz"; + std::string value4 = "Fizzle"; + std::string value5 = "Sizzle"; + std::string value6 = "Twizzler"; + + StringArray::CreateWithValues(dataStructure, k_TestArray1Name, {value1, value2, value3}); + StringArray::CreateWithValues(dataStructure, k_TestArray2Name, {value4, value5, value6}); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(k_InputArrayPaths)); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_VALID(result.result); + + REQUIRE_NOTHROW(dataStructure.getDataRefAs(k_OutputArrayPath)); + auto outputArray = dataStructure.getDataRefAs(k_OutputArrayPath); + + REQUIRE(outputArray.getTupleShape() == std::vector{6}); + REQUIRE(outputArray[0] == value1); + REQUIRE(outputArray[1] == value2); + REQUIRE(outputArray[2] == value3); + REQUIRE(outputArray[3] == value4); + REQUIRE(outputArray[4] == value5); + REQUIRE(outputArray[5] == value6); +} + +TEST_CASE("SimplnxCore::ConcatenateDataArraysFilter: Invalid Parameters", "[SimplnxCore][ConcatenateDataArraysFilter]") +{ + ConcatenateDataArraysFilter filter; + DataStructure dataStructure; + Arguments args; + + SECTION("Empty Input Arrays") + { + Int8Array::CreateWithStore(dataStructure, k_TestArray1Name, std::vector{1}, std::vector{3}); + Int8Array::CreateWithStore(dataStructure, k_TestArray2Name, std::vector{1}, std::vector{3}); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(std::vector{})); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + REQUIRE(result.result.errors().size() == 1); + REQUIRE(result.result.errors()[0].code == to_underlying(ConcatenateDataArrays::ErrorCodes::EmptyInputArrays)); + } + SECTION("One Input Array") + { + Int8Array::CreateWithStore(dataStructure, k_TestArray1Name, std::vector{1}, std::vector{3}); + Int8Array::CreateWithStore(dataStructure, k_TestArray2Name, std::vector{1}, std::vector{3}); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(std::vector{DataPath({k_TestArray1Name})})); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + REQUIRE(result.result.errors().size() == 1); + REQUIRE(result.result.errors()[0].code == to_underlying(ConcatenateDataArrays::ErrorCodes::OneInputArray)); + } + SECTION("Mismatching Type Names 1") + { + Int8Array::CreateWithStore(dataStructure, k_TestArray1Name, std::vector{1}, std::vector{3}); + Int32Array::CreateWithStore(dataStructure, k_TestArray2Name, std::vector{1}, std::vector{3}); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(std::vector{k_InputArrayPaths})); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + REQUIRE(result.result.errors().size() == 1); + REQUIRE(result.result.errors()[0].code == to_underlying(ConcatenateDataArrays::ErrorCodes::TypeNameMismatch)); + } + + SECTION("Mismatching Type Names 2") + { + Int8Array::CreateWithStore(dataStructure, k_TestArray1Name, std::vector{1}, std::vector{3}); + StringArray::CreateWithValues(dataStructure, k_TestArray2Name, {"Foo"}); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(std::vector{k_InputArrayPaths})); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + REQUIRE(result.result.errors().size() == 1); + REQUIRE(result.result.errors()[0].code == to_underlying(ConcatenateDataArrays::ErrorCodes::TypeNameMismatch)); + } + SECTION("Mismatching Component Dimensions") + { + Int8Array::CreateWithStore(dataStructure, k_TestArray1Name, std::vector{1}, std::vector{3, 4}); + Int8Array::CreateWithStore(dataStructure, k_TestArray2Name, std::vector{1}, std::vector{4, 3}); + + args.insert(ConcatenateDataArraysFilter::k_InputArrays_Key, std::make_any>(std::vector{k_InputArrayPaths})); + args.insert(ConcatenateDataArraysFilter::k_OutputArray_Key, std::make_any(k_OutputArrayPath)); + + auto result = filter.execute(dataStructure, args); + SIMPLNX_RESULT_REQUIRE_INVALID(result.result); + REQUIRE(result.result.errors().size() == 1); + REQUIRE(result.result.errors()[0].code == to_underlying(ConcatenateDataArrays::ErrorCodes::ComponentShapeMismatch)); + } +}