diff --git a/ProcessLib/Reflection/ReflectionSetIPData.h b/ProcessLib/Reflection/ReflectionSetIPData.h
new file mode 100644
index 0000000000000000000000000000000000000000..40041f7d05f0f7eaadff84e78a9e6f3ce351dd84
--- /dev/null
+++ b/ProcessLib/Reflection/ReflectionSetIPData.h
@@ -0,0 +1,156 @@
+/**
+ * \file
+ * \copyright
+ * Copyright (c) 2012-2022, OpenGeoSys Community (http://www.opengeosys.org)
+ *            Distributed under a Modified BSD License.
+ *              See accompanying file LICENSE.txt or
+ *              http://www.opengeosys.org/project/license
+ *
+ */
+
+#pragma once
+
+#include <Eigen/Core>
+#include <vector>
+
+#include "MathLib/KelvinVector.h"
+#include "ReflectionIPData.h"
+
+namespace ProcessLib::Reflection
+{
+namespace detail
+{
+template <int dim, typename IPData, typename Accessor>
+void setIPData(double const* values,
+               std::vector<IPData>& ip_data_vector,
+               Accessor const& accessor)
+{
+    using AccessorResult = std::invoke_result_t<Accessor, IPData&>;
+    using AccessorResultStripped = std::remove_cvref_t<AccessorResult>;
+    constexpr auto num_comp = NumberOfComponents<AccessorResultStripped>::value;
+
+    auto constexpr kv_size =
+        MathLib::KelvinVector::kelvin_vector_dimensions(dim);
+    auto const num_int_pts = ip_data_vector.size();
+
+    if constexpr (num_comp == 1)
+    {
+        for (unsigned ip = 0; ip < num_int_pts; ++ip)
+        {
+            accessor(ip_data_vector[ip]) = values[ip];
+        }
+    }
+    else
+    {
+        auto const values_mat =
+            Eigen::Map<Eigen::Matrix<double, num_comp, Eigen::Dynamic,
+                                     Eigen::ColMajor> const>(values, num_comp,
+                                                             num_int_pts);
+
+        for (unsigned ip = 0; ip < num_int_pts; ++ip)
+        {
+            if constexpr (num_comp == kv_size)
+            {
+                accessor(ip_data_vector[ip]) =
+                    MathLib::KelvinVector::symmetricTensorToKelvinVector(
+                        values_mat.col(ip));
+            }
+            else
+            {
+                accessor(ip_data_vector[ip]) = values_mat.col(ip);
+            }
+        }
+    }
+}
+
+// Sets IP data if the passed name equals the one in refl_data.
+// Returns true if IP have been set, false otherwise.
+template <int dim, typename IPData, typename Accessor_CurrentLevelFromIPData,
+          typename Class, typename Member>
+bool setIPDataIfNameMatches(std::string const& name, double const* values,
+                            std::vector<IPData>& ip_data_vector,
+                            Accessor_CurrentLevelFromIPData const& accessor,
+                            ReflectionData<Class, Member> const& refl_data)
+{
+    auto const field = refl_data.field;
+
+    auto const accessor_field_from_ip_data = [accessor,
+                                              field](IPData& ip_data) -> Member&
+    { return accessor(ip_data).*field; };
+
+    if constexpr (detail::has_reflect<Member>)
+    {
+        return 0 != reflectSetIPData<dim>(name, values, ip_data_vector,
+                                          accessor_field_from_ip_data,
+                                          Member::reflect());
+    }
+    else
+    {
+        if (refl_data.name != name)
+        {
+            return false;
+        }
+
+        setIPData<dim>(values, ip_data_vector, accessor_field_from_ip_data);
+
+        return true;
+    }
+}
+
+template <int dim, typename IPData, typename Accessor_CurrentLevelFromIPData,
+          typename... Class, typename... Member, std::size_t... Idcs>
+std::size_t reflectSetIPData(
+    std::string const& name, double const* values,
+    std::vector<IPData>& ip_data_vector,
+    Accessor_CurrentLevelFromIPData const& accessor,
+    std::tuple<ReflectionData<Class, Member>...> const& refl_data,
+    std::index_sequence<Idcs...>)
+{
+    // uses short-circuit evaluation of the fold || ... to stop after the first
+    // match
+    bool have_consumed_data =
+        ((setIPDataIfNameMatches<dim>(name, values, ip_data_vector, accessor,
+                                      std::get<Idcs>(refl_data))) ||
+         ...);
+
+    if (have_consumed_data)
+    {
+        return ip_data_vector.size();
+    }
+
+    return 0;
+}
+
+template <int dim, typename IPData, typename Accessor_CurrentLevelFromIPData,
+          typename... Class, typename... Member>
+std::size_t reflectSetIPData(
+    std::string const& name, double const* values,
+    std::vector<IPData>& ip_data_vector,
+    Accessor_CurrentLevelFromIPData const& accessor,
+    std::tuple<ReflectionData<Class, Member>...> const& refl_data)
+{
+    return reflectSetIPData<dim>(name, values, ip_data_vector, accessor,
+                                 refl_data,
+                                 std::make_index_sequence<sizeof...(Class)>{});
+}
+}  // namespace detail
+
+/**
+ * Sets integration point data for the property with the given \c name to the
+ * passed \c values.
+ *
+ * Possible candidate properties are obtained from \c IPData via some sort of
+ * reflection.
+ *
+ * \return The number of integration points if data has been set, zero
+ * if no property with the given \c name has been found.
+ */
+template <int dim, typename IPData>
+std::size_t reflectSetIPData(std::string const& name, double const* values,
+                             std::vector<IPData>& ip_data_vector)
+{
+    return detail::reflectSetIPData<dim>(name, values, ip_data_vector,
+                                         std::identity{}, IPData::reflect());
+}
+
+}  // namespace ProcessLib::Reflection