diff --git a/Source/Filtering/imstkCleanMesh.cpp b/Source/Filtering/imstkCleanMesh.cpp
index 3c3c752a08a1d3de682177b626bc1e6898f64ddf..55a40d8955c99361617b02807d6404d807f36ead 100644
--- a/Source/Filtering/imstkCleanMesh.cpp
+++ b/Source/Filtering/imstkCleanMesh.cpp
@@ -31,9 +31,9 @@ namespace imstk
 {
 CleanMesh::CleanMesh()
 {
-    setInputPortReq<SurfaceMesh>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<SurfaceMesh>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<SurfaceMesh>());
 }
diff --git a/Source/Filtering/imstkExtractEdges.cpp b/Source/Filtering/imstkExtractEdges.cpp
index 12e94a658f1f00783b13d2ed16f4afa0582bfdee..01d76e5f6b0d60c72ea28974092ec075f88eb271 100644
--- a/Source/Filtering/imstkExtractEdges.cpp
+++ b/Source/Filtering/imstkExtractEdges.cpp
@@ -32,9 +32,9 @@ namespace imstk
 {
 ExtractEdges::ExtractEdges()
 {
-    setInputPortReq<SurfaceMesh>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<SurfaceMesh>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<LineMesh>());
 }
diff --git a/Source/Filtering/imstkImageDistanceTransform.cpp b/Source/Filtering/imstkImageDistanceTransform.cpp
index 660e8cce3aed38b826e1c6aee7f1a84bdc7290ca..cae9e0fba0646f1eaf77d139741438cb88fb688b 100644
--- a/Source/Filtering/imstkImageDistanceTransform.cpp
+++ b/Source/Filtering/imstkImageDistanceTransform.cpp
@@ -34,9 +34,9 @@ namespace imstk
 {
 ImageDistanceTransform::ImageDistanceTransform()
 {
-    setInputPortReq<ImageData>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<ImageData>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<ImageData>(), 0);
 }
diff --git a/Source/Filtering/imstkImageGradient.cpp b/Source/Filtering/imstkImageGradient.cpp
index e1719dc0adfc1a6c983a2f2fc41ff72cd29678d1..4095e2d1e749f30a0716b623d71dde61b4068aa6 100644
--- a/Source/Filtering/imstkImageGradient.cpp
+++ b/Source/Filtering/imstkImageGradient.cpp
@@ -32,9 +32,9 @@ namespace imstk
 {
 ImageGradient::ImageGradient()
 {
-    setInputPortReq<ImageData>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<ImageData>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<PointSet>());
 }
diff --git a/Source/Filtering/imstkImageResample.cpp b/Source/Filtering/imstkImageResample.cpp
index 153655af959ad438c14f552436f6bd89fe17a6a1..ab647500127c83e490e7887d97661d260b169a87 100644
--- a/Source/Filtering/imstkImageResample.cpp
+++ b/Source/Filtering/imstkImageResample.cpp
@@ -33,9 +33,9 @@ namespace imstk
 {
 ImageResample::ImageResample()
 {
-    setInputPortReq<ImageData>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<ImageData>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<ImageData>());
 }
diff --git a/Source/Filtering/imstkImageReslice.cpp b/Source/Filtering/imstkImageReslice.cpp
index ab24e5d4e45628e8a2a179f4cf745e8dad423849..97d5a5cdd7a3b31df9aa46918abf362144d6f770 100644
--- a/Source/Filtering/imstkImageReslice.cpp
+++ b/Source/Filtering/imstkImageReslice.cpp
@@ -32,9 +32,9 @@ namespace imstk
 {
 ImageReslice::ImageReslice()
 {
-    setInputPortReq<ImageData>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<ImageData>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<ImageData>());
 }
diff --git a/Source/Filtering/imstkImplicitGeometryToImageData.cpp b/Source/Filtering/imstkImplicitGeometryToImageData.cpp
index 0cc636b4c6345548d5463b132c76d3325747a64e..1f8aa74697226e021bfef7cad612dcb9631544d5 100644
--- a/Source/Filtering/imstkImplicitGeometryToImageData.cpp
+++ b/Source/Filtering/imstkImplicitGeometryToImageData.cpp
@@ -30,9 +30,9 @@ namespace imstk
 {
 ImplicitGeometryToImageData::ImplicitGeometryToImageData()
 {
-    setInputPortReq<ImplicitGeometry>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<ImplicitGeometry>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<ImageData>());
 }
diff --git a/Source/Filtering/imstkLocalMarchingCubes.cpp b/Source/Filtering/imstkLocalMarchingCubes.cpp
index 331dc08084ff5abcffb468c999c4f222a645546f..97bed7704edab9f12b10f0337018427fc64570b6 100644
--- a/Source/Filtering/imstkLocalMarchingCubes.cpp
+++ b/Source/Filtering/imstkLocalMarchingCubes.cpp
@@ -360,9 +360,9 @@ lerp(double val1, double val2, double isovalue, double spacing)
 
 LocalMarchingCubes::LocalMarchingCubes()
 {
-    setInputPortReq<ImageData>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<ImageData>(0);
+
     setNumberOfOutputPorts(0);
 }
 
diff --git a/Source/Filtering/imstkQuadricDecimate.cpp b/Source/Filtering/imstkQuadricDecimate.cpp
index 230ab092ae24b8b67c254f25ec2741e31118f00c..bf09650906bd939411624cdcf48d7953b7c10a85 100644
--- a/Source/Filtering/imstkQuadricDecimate.cpp
+++ b/Source/Filtering/imstkQuadricDecimate.cpp
@@ -31,7 +31,7 @@ namespace imstk
 QuadricDecimate::QuadricDecimate() :
     m_VolumePreserving(true), m_TargetReduction(0.6)
 {
-    setInputPortReq<SurfaceMesh>(0);
+    setRequiredInputType<SurfaceMesh>(0);
 
     setNumberOfInputPorts(1);
     setNumberOfOutputPorts(1);
diff --git a/Source/Filtering/imstkSelectEnclosedPoints.cpp b/Source/Filtering/imstkSelectEnclosedPoints.cpp
index 9ca99062980b2fb19a51a2cbf5124f3260f40f62..4d708db960939c50c189f2084e7d043c01fda130 100644
--- a/Source/Filtering/imstkSelectEnclosedPoints.cpp
+++ b/Source/Filtering/imstkSelectEnclosedPoints.cpp
@@ -32,10 +32,10 @@ namespace imstk
 {
 SelectEnclosedPoints::SelectEnclosedPoints()
 {
-    setInputPortReq<SurfaceMesh>(0);
-    setInputPortReq<PointSet>(1);
-
     setNumberOfInputPorts(2);
+    setRequiredInputType<SurfaceMesh>(0);
+    setRequiredInputType<PointSet>(1);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<PointSet>());
 }
diff --git a/Source/Filtering/imstkSurfaceMeshCut.cpp b/Source/Filtering/imstkSurfaceMeshCut.cpp
index 583bc1f39a9777189b40911273aa13e70801ad31..03c0b96899756dc2627ac12b74db16192b117fc9 100644
--- a/Source/Filtering/imstkSurfaceMeshCut.cpp
+++ b/Source/Filtering/imstkSurfaceMeshCut.cpp
@@ -32,9 +32,9 @@ namespace imstk
 {
 SurfaceMeshCut::SurfaceMeshCut()
 {
-    setInputPortReq<SurfaceMesh>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<SurfaceMesh>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<SurfaceMesh>());
 
diff --git a/Source/Filtering/imstkSurfaceMeshDistanceTransform.cpp b/Source/Filtering/imstkSurfaceMeshDistanceTransform.cpp
index 204651aaa18e5f592fb99a0096e841f097c235b9..a209904b149bec00f19a2819de792f23452c1a75 100644
--- a/Source/Filtering/imstkSurfaceMeshDistanceTransform.cpp
+++ b/Source/Filtering/imstkSurfaceMeshDistanceTransform.cpp
@@ -202,9 +202,9 @@ computeFullDT(std::shared_ptr<ImageData> imageData, std::shared_ptr<SurfaceMesh>
 
 SurfaceMeshDistanceTransform::SurfaceMeshDistanceTransform()
 {
-    setInputPortReq<SurfaceMesh>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<SurfaceMesh>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<ImageData>(), 0);
 }
diff --git a/Source/Filtering/imstkSurfaceMeshFlyingEdges.cpp b/Source/Filtering/imstkSurfaceMeshFlyingEdges.cpp
index b3127d94f7022215a37a1d44f99fc5bde031d3de..0151b05c6434b086afc1172c9233ce78cc384b47 100644
--- a/Source/Filtering/imstkSurfaceMeshFlyingEdges.cpp
+++ b/Source/Filtering/imstkSurfaceMeshFlyingEdges.cpp
@@ -32,9 +32,9 @@ namespace imstk
 {
 SurfaceMeshFlyingEdges::SurfaceMeshFlyingEdges()
 {
-    setInputPortReq<ImageData>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<ImageData>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<SurfaceMesh>());
 }
diff --git a/Source/Filtering/imstkSurfaceMeshImageMask.cpp b/Source/Filtering/imstkSurfaceMeshImageMask.cpp
index d40be31dec8afb8f7dcef4cd3ea916c92e576438..251cc4e353f77ac9b78168e6aae0fca2c86eb709 100644
--- a/Source/Filtering/imstkSurfaceMeshImageMask.cpp
+++ b/Source/Filtering/imstkSurfaceMeshImageMask.cpp
@@ -35,10 +35,10 @@ namespace imstk
 {
 SurfaceMeshImageMask::SurfaceMeshImageMask()
 {
-    setInputPortReq<SurfaceMesh>(0);
-    setInputPortReq<ImageData>(1);
-
     setNumberOfInputPorts(2);
+    setRequiredInputType<SurfaceMesh>(0);
+    setOptionalInputType<ImageData>(1);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<ImageData>(), 0);
 }
diff --git a/Source/Filtering/imstkSurfaceMeshSmoothen.cpp b/Source/Filtering/imstkSurfaceMeshSmoothen.cpp
index 35432eb3b724a06274913fcc855e7ce89c72eece..48d9cafc8261581e388e1d0c368125983897b404 100644
--- a/Source/Filtering/imstkSurfaceMeshSmoothen.cpp
+++ b/Source/Filtering/imstkSurfaceMeshSmoothen.cpp
@@ -30,9 +30,9 @@ namespace imstk
 {
 SurfaceMeshSmoothen::SurfaceMeshSmoothen()
 {
-    setInputPortReq<SurfaceMesh>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<SurfaceMesh>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<SurfaceMesh>());
 }
diff --git a/Source/Filtering/imstkSurfaceMeshSubdivide.cpp b/Source/Filtering/imstkSurfaceMeshSubdivide.cpp
index 6e15d9e3efdbf143a8c980d7814f0e104d2cb5ce..393540127bbfcea085de8007ae76602f7f5cb1a6 100644
--- a/Source/Filtering/imstkSurfaceMeshSubdivide.cpp
+++ b/Source/Filtering/imstkSurfaceMeshSubdivide.cpp
@@ -32,9 +32,9 @@ namespace imstk
 {
 SurfaceMeshSubdivide::SurfaceMeshSubdivide()
 {
-    setInputPortReq<SurfaceMesh>(0);
-
     setNumberOfInputPorts(1);
+    setRequiredInputType<SurfaceMesh>(0);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<SurfaceMesh>());
 }
diff --git a/Source/Filtering/imstkSurfaceMeshTextureProject.cpp b/Source/Filtering/imstkSurfaceMeshTextureProject.cpp
index e728a21130a49dca496114d12ad1fa2ecd874488..720f2fdc12b2e70746d1b061e9e87ff4ba6ae464 100644
--- a/Source/Filtering/imstkSurfaceMeshTextureProject.cpp
+++ b/Source/Filtering/imstkSurfaceMeshTextureProject.cpp
@@ -105,10 +105,10 @@ baryInterpolate(T v1, T v2, T v3, Vec3d uvw)
 
 SurfaceMeshTextureProject::SurfaceMeshTextureProject()
 {
-    setInputPortReq<SurfaceMesh>(0);
-    setInputPortReq<SurfaceMesh>(1);
-
     setNumberOfInputPorts(2);
+    setRequiredInputType<SurfaceMesh>(0);
+    setRequiredInputType<SurfaceMesh>(1);
+
     setNumberOfOutputPorts(1);
     setOutput(std::make_shared<SurfaceMesh>(), 0);
 }
diff --git a/Source/FilteringCore/CMakeLists.txt b/Source/FilteringCore/CMakeLists.txt
index 8b9c7d72792dc9745ea2d76392f1e7bc04ee084c..3ca75375a0a2136bed648cbf16ba75641bcffd4c 100644
--- a/Source/FilteringCore/CMakeLists.txt
+++ b/Source/FilteringCore/CMakeLists.txt
@@ -14,3 +14,10 @@ imstk_add_library( FilteringCore
     Geometry
     DataStructures
     ${VTK_LIBRARIES})
+
+#-----------------------------------------------------------------------------
+# Testing
+#-----------------------------------------------------------------------------
+if( ${PROJECT_NAME}_BUILD_TESTING )
+  add_subdirectory(Testing)
+endif()
\ No newline at end of file
diff --git a/Source/FilteringCore/Testing/CMakeLists.txt b/Source/FilteringCore/Testing/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6e2536781a7fba5c9e009fef7f9775965dbc8a74
--- /dev/null
+++ b/Source/FilteringCore/Testing/CMakeLists.txt
@@ -0,0 +1,2 @@
+include(imstkAddTest)
+imstk_add_test( FilteringCore )
\ No newline at end of file
diff --git a/Source/FilteringCore/Testing/GeometryAlgorithmTest.cpp b/Source/FilteringCore/Testing/GeometryAlgorithmTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d365a5dfee3229a1a1a03fe1a3963c212a41b5fe
--- /dev/null
+++ b/Source/FilteringCore/Testing/GeometryAlgorithmTest.cpp
@@ -0,0 +1,154 @@
+/*=========================================================================
+
+   Library: iMSTK
+
+   Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+   & Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+=========================================================================*/
+
+#include <gtest/gtest.h>
+
+#include <imstkGeometryAlgorithm.h>
+#include <imstkSurfaceMesh.h>
+#include <imstkSphere.h>
+
+using namespace imstk;
+namespace
+{
+class MockAlgorithm : public GeometryAlgorithm
+{
+public:
+
+    bool areInputsValid() const override
+    {
+        return GeometryAlgorithm::areInputsValid();
+    }
+
+    void requestUpdate() override
+    {
+    }
+};
+
+class EmptyAlgorithm : public MockAlgorithm
+{
+public:
+    EmptyAlgorithm()
+    {
+        setNumberOfInputPorts(4);
+    }
+};
+
+class ExpectingAlgorithm : public MockAlgorithm
+{
+public:
+    ExpectingAlgorithm()
+    {
+        setNumberOfInputPorts(4);
+        setRequiredInputType<SurfaceMesh>(1);
+    }
+};
+
+class ExpectingOptional : public MockAlgorithm
+{
+public:
+    ExpectingOptional()
+    {
+        setNumberOfInputPorts(4);
+        setOptionalInputType<SurfaceMesh>(1);
+    }
+};
+
+class ExpectingAllKinds : public MockAlgorithm
+{
+public:
+    ExpectingAllKinds()
+    {
+        setNumberOfInputPorts(5);
+        setRequiredInputType<SurfaceMesh>(1);
+        setRequiredInputType<Sphere>(2);
+        setOptionalInputType<SurfaceMesh>(3);
+        setOptionalInputType<Sphere>(4);
+    }
+};
+}
+
+TEST(imstkGeometryAlgorithmTest, no_expectations)
+{
+    EmptyAlgorithm a;
+
+    EXPECT_TRUE(a.areInputsValid());
+
+    auto mesh = std::make_shared<SurfaceMesh>();
+    a.setInput(mesh, 0);
+    EXPECT_TRUE(a.areInputsValid());
+}
+
+TEST(imstkGeometryAlgorithmTest, required_only)
+{
+    ExpectingAlgorithm a;
+
+    EXPECT_FALSE(a.areInputsValid());
+
+    auto mesh = std::make_shared<SurfaceMesh>();
+    a.setInput(mesh, 1);
+    EXPECT_TRUE(a.areInputsValid());
+
+    auto sphere = std::make_shared<Sphere>();
+    a.setInput(sphere, 1);
+    EXPECT_FALSE(a.areInputsValid());
+}
+
+TEST(imstkGeometryAlgorithmTest, optional_only)
+{
+    ExpectingOptional a;
+
+    // optional is true when there isn't a value
+    EXPECT_TRUE(a.areInputsValid());
+
+    // should be true, expected is mesh
+    auto mesh = std::make_shared<SurfaceMesh>();
+    a.setInput(mesh, 1);
+    EXPECT_TRUE(a.areInputsValid());
+
+    // should be false ... mesh is expected
+    auto sphere = std::make_shared<Sphere>();
+    a.setInput(sphere, 1);
+    EXPECT_FALSE(a.areInputsValid());
+}
+
+TEST(imstkGeometryAlgorithmTest, all_kinds)
+{
+    ExpectingAllKinds a;
+
+    EXPECT_FALSE(a.areInputsValid());
+
+    auto mesh   = std::make_shared<SurfaceMesh>();
+    auto sphere = std::make_shared<Sphere>();
+    a.setInput(mesh, 1);
+    EXPECT_FALSE(a.areInputsValid());
+    a.setInput(sphere, 2);
+    EXPECT_TRUE(a.areInputsValid());
+
+    a.setInput(mesh, 3);
+    EXPECT_TRUE(a.areInputsValid());
+    a.setInput(sphere, 4);
+    EXPECT_TRUE(a.areInputsValid());
+
+    a.setInput(sphere, 3);
+    EXPECT_FALSE(a.areInputsValid());
+    a.setInput(mesh, 4);
+    EXPECT_FALSE(a.areInputsValid());
+}
\ No newline at end of file
diff --git a/Source/FilteringCore/imstkGeometryAlgorithm.cpp b/Source/FilteringCore/imstkGeometryAlgorithm.cpp
index 8f11baac28f1a9dec888555bbd84d8047851928a..2f6392b11ef2987d1f89caee5b6b242204c73cd3 100644
--- a/Source/FilteringCore/imstkGeometryAlgorithm.cpp
+++ b/Source/FilteringCore/imstkGeometryAlgorithm.cpp
@@ -17,7 +17,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 
-=========================================================================*/
+   =========================================================================*/
 
 #include "imstkGeometryAlgorithm.h"
 #include "imstkGeometry.h"
@@ -32,7 +32,9 @@ GeometryAlgorithm::setInput(std::shared_ptr<Geometry> inputGeometry, size_t port
     {
         LOG(WARNING) << "Tried to set input " << port << " on filter with " << m_NumberOfInputPorts << " ports";
     }
+
     m_inputs[port] = inputGeometry;
+    areInputsValid();
 }
 
 void
@@ -74,30 +76,39 @@ GeometryAlgorithm::setNumberOfOutputPorts(size_t numPorts)
 }
 
 bool
-GeometryAlgorithm::checkInputRequirements(
-    const std::unordered_map<size_t, std::shared_ptr<Geometry>>& inputs,
-    const std::unordered_map<size_t, PortReq>& inputPortReqs)
+GeometryAlgorithm::areInputsValid() const
 {
     // Check input types
-    for (auto i : inputs)
+    for (const auto& port : m_inputs)
     {
-        const size_t portId = i.first;
-        if (i.second == nullptr)
+        const size_t portId = port.first;
+        Geometry*    input  = port.second.get();
+        auto         found  = m_requiredTypeChecks.find(portId);
+        if (found != m_requiredTypeChecks.cend())
         {
-            LOG(WARNING) << "GeometryAlgorithm input " << portId << " missing!";
-            return false;
+            // Require Input: can't be null has to succeed type check
+            if (port.second == nullptr)
+            {
+                LOG(WARNING) << "GeometryAlgorithm input " << portId << " missing!";
+                return false;
+            }
+            else if (!found->second(input))
+            {
+                LOG(WARNING) << "GeometryAlgorithm received invalid geometry type \"" <<
+                    m_inputs.at(portId)->getTypeName() << "\" in port " << portId;
+                return false;
+            }
+            continue;
         }
 
-        // If the user added a requirement for this port
-        if (inputPortReqs.count(portId) != 0)
+        found = m_optionalTypeChecks.find(portId);
+        if (found != m_optionalTypeChecks.cend())
         {
-            // Check if it's valid
-            if (!inputPortReqs.at(portId).isValid(i.second))
+            // Require Input: may be null, if not has succeed type check
+            if (input != nullptr && !found->second(input))
             {
-                LOG(WARNING) << "GeometryAlgorithm recieved invalid geometry type \"" <<
-                    inputs.at(portId)->getTypeName() << "\" in port " << portId;
-                /*LOG(WARNING) << "GeometryAlgorithm required port " << portId; <<
-                    " to be of type " << inputPortReqs.at(portId).validGeomName();*/
+                LOG(WARNING) << "GeometryAlgorithm received invalid geometry type \"" <<
+                    m_inputs.at(portId)->getTypeName() << "\" in port " << portId;
                 return false;
             }
         }
diff --git a/Source/FilteringCore/imstkGeometryAlgorithm.h b/Source/FilteringCore/imstkGeometryAlgorithm.h
index a2bc40ca71e9420420ccc20650265d09a2e1d635..e3ebb04bbed48f6dca84c95f7614113eba4b65d2 100644
--- a/Source/FilteringCore/imstkGeometryAlgorithm.h
+++ b/Source/FilteringCore/imstkGeometryAlgorithm.h
@@ -30,81 +30,71 @@ namespace imstk
 {
 class Geometry;
 
+/// Returns a function that for instances with common Base classes determines
+/// whether it is of the Target type
+template<class Base, class Target>
+std::function<bool(Base*)>
+makeTypeCheck()
+{
+    return [](Base* p) {
+               return (dynamic_cast<Target*>(p) != nullptr);
+    };
+}
+
 ///
 /// \class GeometryAlgorithm
 ///
-/// \brief Base abstract class for geometry algorithms. GeometryAlgorithms take N input
-/// geometries and produce N output geometries. Sublcasses should implement requestUpdate
+/// \brief Abstract base class for geometry algorithms. GeometryAlgorithms take N input
+/// geometries and produce N output geometries. Subclasses should implement requestUpdate
 /// to do algorithm logic. Subclasses may also setInputPortReq to require an input to be
 /// a certain type.
 ///
 class GeometryAlgorithm
 {
-public:
-    ///
-    /// \brief Used for type erasure of the port requirements
-    /// \todo: Type names can't be deduced for abstract classes.
-    /// Would be nice to have static type name as well
-    ///
-    class PortReq
-    {
-    public:
-        struct BaseReq
-        {
-            virtual ~BaseReq() = default;
-            virtual bool isValid(std::shared_ptr<Geometry> geom) const = 0;
-            //virtual std::string name() const = 0;
-        };
-
-        template<typename T>
-        struct Requirement : public BaseReq
-        {
-            virtual ~Requirement() override = default;
-            bool isValid(std::shared_ptr<Geometry> geom) const override
-            {
-                return std::dynamic_pointer_cast<T>(geom) != nullptr;
-            }
-
-            //std::string name() const override
-            //{
-            //    T t; // Can't use
-            //    return t.getTypeName();
-            //}
-        };
-
-        std::shared_ptr<BaseReq> req = nullptr;
-
-    public:
-        PortReq() = default;
-
-        template<typename T>
-        PortReq(T*) : req(std::make_shared<Requirement<T>>()) { }
-
-        bool isValid(std::shared_ptr<Geometry> geom) const { return req->isValid(geom); }
-        //std::string validGeomName() const { return req->name(); }
-    };
-
 protected:
     GeometryAlgorithm() = default;
 
 public:
     virtual ~GeometryAlgorithm() = default;
 
-public:
     ///
     /// \brief Returns input geometry given port, returns nullptr if doesn't exist
     ///
-    std::shared_ptr<Geometry> getInput(size_t port = 0) const { return (m_inputs.count(port) == 0) ? nullptr : m_inputs.at(port); }
+    std::shared_ptr<Geometry> getInput(size_t port = 0) const
+    {
+        return (m_inputs.count(port) == 0) ? nullptr : m_inputs.at(port);
+    }
+
     ///
     /// \brief Returns output geometry given port, returns nullptr if doesn't exist
     ///
-    std::shared_ptr<Geometry> getOutput(size_t port = 0) const { return m_outputs.count(port) == 0 ? nullptr : m_outputs.at(port); }
+    std::shared_ptr<Geometry> getOutput(size_t port = 0) const
+    {
+        return m_outputs.count(port) == 0 ? nullptr : m_outputs.at(port);
+    }
 
     ///
     /// \brief Set the input at the port
     ///
     void setInput(std::shared_ptr<Geometry> inputGeometry, size_t port = 0);
 
+    ///
+    /// \brief Do the actual algorithm
+    ///
+    void update()
+    {
+        if (!areInputsValid())
+        {
+            LOG(WARNING) << "GeometryAlgorithm failed to run, inputs not satisfied";
+            return;
+        }
+        //if (m_modified)
+        //{
+        requestUpdate();
+        //}
+        //m_modified = false;
+    }
+
 protected:
     ///
     /// \brief Set the output at the port
@@ -125,54 +115,50 @@ protected:
     void setNumberOfOutputPorts(size_t numPorts);
 
     ///
-    /// \brief Set a type requirement on the inputs, it will check when running
-    /// the algorithm and warn at runtime
+    /// \brief Declares the type for the port with the given number, also defines that
+    /// the give port is required for the filter to run correctly
     ///
     template<typename T>
-    void setInputPortReq(size_t port)
+    void setRequiredInputType(size_t port)
     {
-        T* ptr = nullptr;
-        m_inputPortTypeReqs[port] = PortReq(ptr);
+        CHECK(m_optionalTypeChecks.find(port) == m_optionalTypeChecks.end())
+            << "There is already an optional type for this port " << port << ", can't assign another one.";
+        m_requiredTypeChecks[port] = makeTypeCheck<Geometry, T>();
     }
 
-public:
-    //void modified() { this->m_modified = true; }
-
     ///
-    /// \brief Do the actual algorithm
+    /// \brief Declares the type for the port with the given number, the data
+    /// for this port is optional and may be omitted
     ///
-    void update()
+    template<typename T>
+    void setOptionalInputType(size_t port)
     {
-        if (!checkInputRequirements(m_inputs, m_inputPortTypeReqs))
-        {
-            LOG(WARNING) << "GeometryAlgorithm failed to run, inputs not satisfied";
-            return;
-        }
-        //if (m_modified)
-        //{
-        requestUpdate();
-        //}
-        //m_modified = false;
+        CHECK(m_requiredTypeChecks.find(port) == m_requiredTypeChecks.end())
+            << "There is already a required type for port " << port << " , can't assign another one.";
+        m_optionalTypeChecks[port] = makeTypeCheck<Geometry, T>();
     }
 
-protected:
     ///
-    /// \brief Check inputs are correct
-    /// \return true if all inputs match the requirements, false if not
+    /// \brief Users can implement this for the logic to be run
     ///
-    virtual bool checkInputRequirements(
-        const std::unordered_map<size_t, std::shared_ptr<Geometry>>& inputs,
-        const std::unordered_map<size_t, PortReq>& inputPortReqs);
+    virtual void requestUpdate() = 0;
 
     ///
-    /// \brief Users can implement this for the logic to be run
+    /// \brief Check inputs are correct
+    /// \return true if all inputs match the requirements, false if not
     ///
-    virtual void requestUpdate() = 0;
+    virtual bool areInputsValid() const;
 
 private:
-    std::unordered_map<size_t, PortReq> m_inputPortTypeReqs; // The desired types of the input
+    using GeometryCheck      = std::function<bool (Geometry*)>;
+    using TypeCheckContainer = std::unordered_map<size_t, GeometryCheck>;
+
+    std::unordered_map<size_t, GeometryCheck> m_requiredTypeChecks;
+    std::unordered_map<size_t, GeometryCheck> m_optionalTypeChecks;
+
     std::unordered_map<size_t, std::shared_ptr<Geometry>> m_inputs;
     std::unordered_map<size_t, std::shared_ptr<Geometry>> m_outputs;
+
     //bool m_modified = true;
     size_t m_NumberOfInputPorts  = 1;
     size_t m_NumberOfOutputPorts = 1;