diff --git a/CMakeLists.txt b/CMakeLists.txt
index d887159..2c65f55 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,6 +27,7 @@ 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
+ ComputeArrayNormFilter
ComputeGroupingDensityFilter
ComputeLocalAverageCAxisMisalignmentsFilter
ComputeMicroTextureRegionsFilter
@@ -44,6 +45,7 @@ set(ActionList
# This should be integrated with the `create_simplnx_plugin` function call
# ------------------------------------------------------------------------------
set(AlgorithmList
+ ComputeArrayNorm
ComputeGroupingDensity
ComputeLocalAverageCAxisMisalignments
ComputeMicroTextureRegions
diff --git a/docs/ComputeArrayNormFilter.md b/docs/ComputeArrayNormFilter.md
new file mode 100644
index 0000000..86d5d13
--- /dev/null
+++ b/docs/ComputeArrayNormFilter.md
@@ -0,0 +1,32 @@
+# Compute Array Norm
+
+## Group (Subgroup)
+
+SimplnxReview (Statistics)
+
+## Description
+
+This **Filter** computes the pth norm of an **Attribute Array**. Specifically, for each tuple of the array, the following is computed:
+
+$$\left\| \mathbf{x} \right\| _p := \bigg( \sum_{i=1}^n \left| x_i \right| ^p \bigg) ^{1/p}$$
+
+where $n$ is the number of components for the **Attribute Array**.
+
+- When $p = 2$, this results in the *Euclidean norm*
+- When $p = 1$, this results in the *Manhattan norm* (also called the *taxicab norm*)
+
+The p-space value may be any real number greater than or equal to zero. When $0 \leq p < 1$, the result may not strictly be a *norm* in the exact mathematical sense. Additionally, when $p = 0$, the result is simply the number of components for the **Attribute Array**.
+
+**Note:** If the input array is a scalar array (1 component), the output array will contain the same values as the input array, but in 32-bit floating point precision.
+
+% 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/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.cpp b/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.cpp
new file mode 100644
index 0000000..ffc4a20
--- /dev/null
+++ b/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.cpp
@@ -0,0 +1,87 @@
+#include "ComputeArrayNorm.hpp"
+
+#include "simplnx/DataStructure/DataArray.hpp"
+#include "simplnx/Utilities/DataArrayUtilities.hpp"
+#include "simplnx/Utilities/FilterUtilities.hpp"
+
+#include
+
+using namespace nx::core;
+
+namespace
+{
+template
+class ComputeNormImpl
+{
+public:
+ ComputeNormImpl(const IDataArray& inputArray, Float32Array& normArray, float32 pSpace)
+ : m_InputArray(dynamic_cast&>(inputArray))
+ , m_NormArray(normArray)
+ , m_PSpace(pSpace)
+ {
+ }
+
+ void operator()() const
+ {
+ const auto& inputStore = m_InputArray.getDataStoreRef();
+ auto& normStore = m_NormArray.getDataStoreRef();
+
+ usize numTuples = m_InputArray.getNumberOfTuples();
+ usize numComponents = m_InputArray.getNumberOfComponents();
+
+ for(usize i = 0; i < numTuples; i++)
+ {
+ float32 normTmp = 0.0f;
+ for(usize j = 0; j < numComponents; j++)
+ {
+ float32 value = static_cast(inputStore[numComponents * i + j]);
+ normTmp += std::pow(std::abs(value), m_PSpace);
+ }
+ normStore[i] = std::pow(normTmp, 1.0f / m_PSpace);
+ }
+ }
+
+private:
+ const DataArray& m_InputArray;
+ Float32Array& m_NormArray;
+ float32 m_PSpace;
+};
+
+struct ComputeNormFunctor
+{
+ template
+ void operator()(const IDataArray& inputArray, Float32Array& normArray, float32 pSpace)
+ {
+ ComputeNormImpl(inputArray, normArray, pSpace)();
+ }
+};
+} // namespace
+
+// -----------------------------------------------------------------------------
+ComputeArrayNorm::ComputeArrayNorm(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, ComputeArrayNormInputValues* inputValues)
+: m_DataStructure(dataStructure)
+, m_InputValues(inputValues)
+, m_ShouldCancel(shouldCancel)
+, m_MessageHandler(mesgHandler)
+{
+}
+
+// -----------------------------------------------------------------------------
+ComputeArrayNorm::~ComputeArrayNorm() noexcept = default;
+
+// -----------------------------------------------------------------------------
+const std::atomic_bool& ComputeArrayNorm::getCancel()
+{
+ return m_ShouldCancel;
+}
+
+// -----------------------------------------------------------------------------
+Result<> ComputeArrayNorm::operator()()
+{
+ const auto& inputArray = m_DataStructure.getDataRefAs(m_InputValues->SelectedArrayPath);
+ auto& normArray = m_DataStructure.getDataRefAs(m_InputValues->NormArrayPath);
+
+ ExecuteDataFunction(ComputeNormFunctor{}, inputArray.getDataType(), inputArray, normArray, m_InputValues->PSpace);
+
+ return {};
+}
diff --git a/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.hpp b/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.hpp
new file mode 100644
index 0000000..44af109
--- /dev/null
+++ b/src/SimplnxReview/Filters/Algorithms/ComputeArrayNorm.hpp
@@ -0,0 +1,45 @@
+#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 ComputeArrayNormInputValues
+{
+ float32 PSpace;
+ DataPath SelectedArrayPath;
+ DataPath NormArrayPath;
+};
+
+/**
+ * @class ComputeArrayNorm
+ * @brief This algorithm computes the p-th norm of an Attribute Array.
+ */
+class SIMPLNXREVIEW_EXPORT ComputeArrayNorm
+{
+public:
+ ComputeArrayNorm(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, ComputeArrayNormInputValues* inputValues);
+ ~ComputeArrayNorm() noexcept;
+
+ ComputeArrayNorm(const ComputeArrayNorm&) = delete;
+ ComputeArrayNorm(ComputeArrayNorm&&) noexcept = delete;
+ ComputeArrayNorm& operator=(const ComputeArrayNorm&) = delete;
+ ComputeArrayNorm& operator=(ComputeArrayNorm&&) noexcept = delete;
+
+ Result<> operator()();
+
+ const std::atomic_bool& getCancel();
+
+private:
+ DataStructure& m_DataStructure;
+ const ComputeArrayNormInputValues* m_InputValues = nullptr;
+ const std::atomic_bool& m_ShouldCancel;
+ const IFilter::MessageHandler& m_MessageHandler;
+};
+
+} // namespace nx::core
diff --git a/src/SimplnxReview/Filters/ComputeArrayNormFilter.cpp b/src/SimplnxReview/Filters/ComputeArrayNormFilter.cpp
new file mode 100644
index 0000000..79b5157
--- /dev/null
+++ b/src/SimplnxReview/Filters/ComputeArrayNormFilter.cpp
@@ -0,0 +1,149 @@
+#include "ComputeArrayNormFilter.hpp"
+
+#include "SimplnxReview/Filters/Algorithms/ComputeArrayNorm.hpp"
+
+#include "simplnx/DataStructure/DataArray.hpp"
+#include "simplnx/DataStructure/DataPath.hpp"
+#include "simplnx/Filter/Actions/CreateArrayAction.hpp"
+#include "simplnx/Parameters/ArraySelectionParameter.hpp"
+#include "simplnx/Parameters/DataObjectNameParameter.hpp"
+#include "simplnx/Parameters/NumberParameter.hpp"
+#include "simplnx/Utilities/SIMPLConversion.hpp"
+
+using namespace nx::core;
+
+namespace nx::core
+{
+//------------------------------------------------------------------------------
+std::string ComputeArrayNormFilter::name() const
+{
+ return FilterTraits::name.str();
+}
+
+//------------------------------------------------------------------------------
+std::string ComputeArrayNormFilter::className() const
+{
+ return FilterTraits::className;
+}
+
+//------------------------------------------------------------------------------
+Uuid ComputeArrayNormFilter::uuid() const
+{
+ return FilterTraits::uuid;
+}
+
+//------------------------------------------------------------------------------
+std::string ComputeArrayNormFilter::humanName() const
+{
+ return "Compute Array Norm";
+}
+
+//------------------------------------------------------------------------------
+std::vector ComputeArrayNormFilter::defaultTags() const
+{
+ return {className(), "Statistics", "DREAM3DReview"};
+}
+
+//------------------------------------------------------------------------------
+Parameters ComputeArrayNormFilter::parameters() const
+{
+ Parameters params;
+
+ params.insertSeparator(Parameters::Separator{"Input Parameter"});
+ params.insert(std::make_unique(k_PSpace_Key, "p-Space Value", "p-Value used for computing the norm (2 = Euclidean, 1 = Manhattan)", 2.0f));
+
+ params.insertSeparator(Parameters::Separator{"Input Data"});
+ params.insert(std::make_unique(k_SelectedArrayPath_Key, "Input Attribute Array", "The input array for computing the norm", DataPath{},
+ ArraySelectionParameter::AllowedTypes{DataType::int8, DataType::uint8, DataType::int16, DataType::uint16, DataType::int32, DataType::uint32,
+ DataType::int64, DataType::uint64, DataType::float32, DataType::float64},
+ ArraySelectionParameter::AllowedComponentShapes{}));
+
+ params.insertSeparator(Parameters::Separator{"Output Data"});
+ params.insert(std::make_unique(k_NormArrayName_Key, "Norm Array Name", "The name of the output norm array", "Norm"));
+
+ return params;
+}
+
+//------------------------------------------------------------------------------
+IFilter::UniquePointer ComputeArrayNormFilter::clone() const
+{
+ return std::make_unique();
+}
+
+//------------------------------------------------------------------------------
+IFilter::VersionType ComputeArrayNormFilter::parametersVersion() const
+{
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+IFilter::PreflightResult ComputeArrayNormFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler,
+ const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const
+{
+ auto pSpaceValue = filterArgs.value(k_PSpace_Key);
+ auto pSelectedArrayPathValue = filterArgs.value(k_SelectedArrayPath_Key);
+ auto pNormArrayNameValue = filterArgs.value(k_NormArrayName_Key);
+
+ PreflightResult preflightResult;
+ nx::core::Result resultOutputActions;
+ std::vector preflightUpdatedValues;
+
+ if(pSpaceValue < 0.0f)
+ {
+ return {MakeErrorResult(-11002, "p-space value must be greater than or equal to 0")};
+ }
+
+ const auto* inputArray = dataStructure.getDataAs(pSelectedArrayPathValue);
+ if(inputArray == nullptr)
+ {
+ return {MakeErrorResult(-11003, fmt::format("Cannot find the selected input array at path '{}'", pSelectedArrayPathValue.toString()))};
+ }
+
+ DataPath normArrayPath = pSelectedArrayPathValue.replaceName(pNormArrayNameValue);
+
+ {
+ auto createArrayAction = std::make_unique(DataType::float32, inputArray->getTupleShape(), std::vector{1}, normArrayPath);
+ resultOutputActions.value().appendAction(std::move(createArrayAction));
+ }
+
+ return {std::move(resultOutputActions), std::move(preflightUpdatedValues)};
+}
+
+//------------------------------------------------------------------------------
+Result<> ComputeArrayNormFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler,
+ const std::atomic_bool& shouldCancel, const ExecutionContext& executionContext) const
+{
+ ComputeArrayNormInputValues inputValues;
+
+ inputValues.PSpace = filterArgs.value(k_PSpace_Key);
+ inputValues.SelectedArrayPath = filterArgs.value(k_SelectedArrayPath_Key);
+ inputValues.NormArrayPath = inputValues.SelectedArrayPath.replaceName(filterArgs.value(k_NormArrayName_Key));
+
+ return ComputeArrayNorm(dataStructure, messageHandler, shouldCancel, &inputValues)();
+}
+
+namespace
+{
+namespace SIMPL
+{
+constexpr StringLiteral k_SelectedArrayPathKey = "SelectedArrayPath";
+constexpr StringLiteral k_NormArrayPathKey = "NormArrayPath";
+constexpr StringLiteral k_PSpaceKey = "PSpace";
+} // namespace SIMPL
+} // namespace
+
+Result ComputeArrayNormFilter::FromSIMPLJson(const nlohmann::json& json)
+{
+ Arguments args = ComputeArrayNormFilter().getDefaultArguments();
+
+ std::vector> results;
+
+ results.push_back(SIMPLConversion::ConvertParameter>(args, json, SIMPL::k_PSpaceKey, k_PSpace_Key));
+ results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_SelectedArrayPathKey, k_SelectedArrayPath_Key));
+ results.push_back(SIMPLConversion::ConvertParameter(args, json, SIMPL::k_NormArrayPathKey, k_NormArrayName_Key));
+
+ Result<> conversionResult = MergeResults(std::move(results));
+
+ return ConvertResultTo(std::move(conversionResult), std::move(args));
+}
+} // namespace nx::core
diff --git a/src/SimplnxReview/Filters/ComputeArrayNormFilter.hpp b/src/SimplnxReview/Filters/ComputeArrayNormFilter.hpp
new file mode 100644
index 0000000..757fd66
--- /dev/null
+++ b/src/SimplnxReview/Filters/ComputeArrayNormFilter.hpp
@@ -0,0 +1,122 @@
+#pragma once
+
+#include "SimplnxReview/SimplnxReview_export.hpp"
+
+#include "simplnx/Filter/FilterTraits.hpp"
+#include "simplnx/Filter/IFilter.hpp"
+
+namespace nx::core
+{
+/**
+ * @class ComputeArrayNormFilter
+ * @brief This filter computes the p-th norm of an Attribute Array. For each tuple of the array,
+ * the norm is calculated as: ||x||_p = (sum(|x_i|^p))^(1/p) where n is the number of components.
+ * When p=2, this results in the Euclidean norm; when p=1, this results in the Manhattan norm.
+ */
+class SIMPLNXREVIEW_EXPORT ComputeArrayNormFilter : public IFilter
+{
+public:
+ ComputeArrayNormFilter() = default;
+ ~ComputeArrayNormFilter() noexcept override = default;
+
+ ComputeArrayNormFilter(const ComputeArrayNormFilter&) = delete;
+ ComputeArrayNormFilter(ComputeArrayNormFilter&&) noexcept = delete;
+
+ ComputeArrayNormFilter& operator=(const ComputeArrayNormFilter&) = delete;
+ ComputeArrayNormFilter& operator=(ComputeArrayNormFilter&&) noexcept = delete;
+
+ // Parameter Keys
+ static constexpr StringLiteral k_PSpace_Key = "p_space_value";
+ static constexpr StringLiteral k_SelectedArrayPath_Key = "selected_array_path";
+ static constexpr StringLiteral k_NormArrayName_Key = "norm_array_name";
+
+ /**
+ * @brief Reads SIMPL json and converts it simplnx Arguments.
+ * @param json
+ * @return Result
+ */
+ static Result FromSIMPLJson(const nlohmann::json& json);
+
+ /**
+ * @brief Returns the name of the filter.
+ * @return
+ */
+ std::string name() const override;
+
+ /**
+ * @brief Returns the C++ classname of this filter.
+ * @return
+ */
+ std::string className() const override;
+
+ /**
+ * @brief Returns the uuid of the filter.
+ * @return
+ */
+ Uuid uuid() const override;
+
+ /**
+ * @brief Returns the human-readable name of the filter.
+ * @return
+ */
+ std::string humanName() const override;
+
+ /**
+ * @brief Returns the default tags for this filter.
+ * @return
+ */
+ std::vector defaultTags() const override;
+
+ /**
+ * @brief Returns the parameters of the filter (i.e. its inputs)
+ * @return
+ */
+ Parameters parameters() const override;
+
+ /**
+ * @brief Returns parameters version integer.
+ * Initial version should always be 1.
+ * Should be incremented everytime the parameters change.
+ * @return VersionType
+ */
+ VersionType parametersVersion() const override;
+
+ /**
+ * @brief Returns a copy of the filter.
+ * @return
+ */
+ UniquePointer clone() const override;
+
+protected:
+ /**
+ * @brief Takes in a DataStructure and checks that the filter can be run on it with the given arguments.
+ * Returns any warnings/errors. Also returns the changes that would be applied to the DataStructure.
+ * Some parts of the actions may not be completely filled out if all the required information is not available at preflight time.
+ * @param dataStructure The input DataStructure instance
+ * @param filterArgs These are the input values for each parameter that is required for the filter
+ * @param messageHandler The MessageHandler object
+ * @param shouldCancel Atomic boolean value that can be checked to cancel the filter
+ * @param executionContext The ExecutionContext that can be used to determine the correct absolute path from a relative path
+ * @return Returns a Result object with error or warning values if any of those occurred during execution of this function
+ */
+ PreflightResult preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel,
+ const ExecutionContext& executionContext) const override;
+
+ /**
+ * @brief Applies the filter's algorithm to the DataStructure with the given arguments. Returns any warnings/errors.
+ * On failure, there is no guarantee that the DataStructure is in a correct state.
+ * @param dataStructure The input DataStructure instance
+ * @param filterArgs These are the input values for each parameter that is required for the filter
+ * @param pipelineNode The node in the pipeline that is being executed
+ * @param messageHandler The MessageHandler object
+ * @param shouldCancel Atomic boolean value that can be checked to cancel the filter
+ * @param executionContext The ExecutionContext that can be used to determine the correct absolute path from a relative path
+ * @return Returns a Result object with error or warning values if any of those occurred during execution of this function
+ */
+ Result<> executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel,
+ const ExecutionContext& executionContext) const override;
+};
+} // namespace nx::core
+
+SIMPLNX_DEF_FILTER_TRAITS(nx::core, ComputeArrayNormFilter, "a9943ec6-1276-46d1-b9a4-c1537e8e5d5f");
+/* LEGACY UUID FOR THIS FILTER 5d0cd577-3e3e-57b8-a36d-b215b834251f */
diff --git a/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp b/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp
index d56f487..b3fdbba 100644
--- a/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp
+++ b/src/SimplnxReview/SimplnxReviewLegacyUUIDMapping.hpp
@@ -5,6 +5,7 @@
#include
/* clang-format off */
+#include "SimplnxReview/Filters/ComputeArrayNormFilter.hpp"
#include "SimplnxReview/Filters/GroupMicroTextureRegionsFilter.hpp"
#include "SimplnxReview/Filters/MergeColoniesFilter.hpp"
#include "SimplnxReview/Filters/ComputeSaltykovSizesFilter.hpp"
@@ -22,6 +23,7 @@ namespace nx::core
static const AbstractPlugin::SIMPLMapType k_SIMPL_to_SimplnxReview
{
// syntax std::make_pair {Dream3d UUID , Dream3dnx UUID, {}}}, // dream3d-class-name
+ {nx::core::Uuid::FromString("5d0cd577-3e3e-57b8-a36d-b215b834251f").value(), {nx::core::FilterTraits::uuid, &ComputeArrayNormFilter::FromSIMPLJson}}, // FindNorm
{nx::core::Uuid::FromString("5e18a9e2-e342-56ac-a54e-3bd0ca8b9c53").value(), {nx::core::FilterTraits::uuid, &GroupMicroTextureRegionsFilter::FromSIMPLJson}}, // GroupMicroTextureRegions
{nx::core::Uuid::FromString("2c4a6d83-6a1b-56d8-9f65-9453b28845b9").value(), {nx::core::FilterTraits::uuid, &MergeColoniesFilter::FromSIMPLJson}}, // MergeColonies
{nx::core::Uuid::FromString("cc76cffe-81ad-5ece-be2a-ce127c5fa6d7").value(), {nx::core::FilterTraits::uuid, &ComputeSaltykovSizesFilter::FromSIMPLJson}}, // FindSaltykovSizes