diff --git a/NumLib/NumericalStability/AdvectionMatrixAssembler.h b/NumLib/NumericalStability/AdvectionMatrixAssembler.h
index 3d541b5c048d4353da8880728222db07672052ea..ed5c09964d7d2d33bb0008c8dc12b234e887ffe4 100644
--- a/NumLib/NumericalStability/AdvectionMatrixAssembler.h
+++ b/NumLib/NumericalStability/AdvectionMatrixAssembler.h
@@ -14,12 +14,35 @@
 #include <memory>
 #include <vector>
 
+#include "NumLib/Fem/ShapeMatrixCache.h"
 #include "NumericalStabilization.h"
 
 namespace NumLib
 {
 namespace detail
 {
+template <typename MeshElementType,
+          typename IPData,
+          typename FluxVectorType,
+          typename Derived>
+void assembleAdvectionMatrix(IPData const& ip_data_vector,
+                             NumLib::ShapeMatrixCache const& shape_matrix_cache,
+                             std::vector<FluxVectorType> const& ip_flux_vector,
+                             Eigen::MatrixBase<Derived>& laplacian_matrix)
+{
+    auto const& Ns = shape_matrix_cache.NsHigherOrder<MeshElementType>();
+
+    for (std::size_t ip = 0; ip < ip_flux_vector.size(); ++ip)
+    {
+        auto const& ip_data = ip_data_vector[ip];
+        auto const w = ip_data.integration_weight;
+        auto const& dNdx = ip_data.dNdx;
+        auto const& N = Ns[ip];
+        laplacian_matrix.noalias() +=
+            N.transpose() * ip_flux_vector[ip].transpose() * dNdx * w;
+    }
+}
+
 template <typename IPData, typename FluxVectorType, typename Derived>
 void assembleAdvectionMatrix(IPData const& ip_data_vector,
                              std::vector<FluxVectorType> const& ip_flux_vector,
@@ -79,9 +102,13 @@ void applyFullUpwind(IPData const& ip_data_vector,
 }
 }  // namespace detail
 
-template <typename IPData, typename FluxVectorType, typename Derived>
+template <typename MeshElementType,
+          typename IPData,
+          typename FluxVectorType,
+          typename Derived>
 void assembleAdvectionMatrix(NumericalStabilization const& stabilizer,
                              IPData const& ip_data_vector,
+                             NumLib::ShapeMatrixCache const& shape_matrix_cache,
                              std::vector<FluxVectorType> const& ip_flux_vector,
                              double const average_velocity,
                              Eigen::MatrixBase<Derived>& laplacian_matrix)
@@ -100,8 +127,36 @@ void assembleAdvectionMatrix(NumericalStabilization const& stabilizer,
                 }
             }
 
-            detail::assembleAdvectionMatrix(ip_data_vector, ip_flux_vector,
+            detail::assembleAdvectionMatrix<MeshElementType>(
+                ip_data_vector, shape_matrix_cache, ip_flux_vector,
+                laplacian_matrix);
+        },
+        stabilizer);
+}
+
+template <typename IPData, typename FluxVectorType, typename Derived>
+void assembleAdvectionMatrix(NumericalStabilization const& stabilizer,
+                             IPData const& ip_data_vector,
+                             std::vector<FluxVectorType> const& ip_flux_vector,
+                             double const average_velocity,
+                             Eigen::MatrixBase<Derived>& laplacian_matrix)
+{
+    std::visit(
+        [&](auto&& stabilizer)
+        {
+            using Stabilizer = std::decay_t<decltype(stabilizer)>;
+            if constexpr (std::is_same_v<Stabilizer, FullUpwind>)
+            {
+                if (average_velocity > stabilizer.getCutoffVelocity())
+                {
+                    detail::applyFullUpwind(ip_data_vector, ip_flux_vector,
                                             laplacian_matrix);
+                    return;
+                }
+            }
+
+            detail::assembleAdvectionMatrix(
+                ip_data_vector, ip_flux_vector, laplacian_matrix);
         },
         stabilizer);
 }
diff --git a/ProcessLib/ComponentTransport/ComponentTransportFEM.h b/ProcessLib/ComponentTransport/ComponentTransportFEM.h
index 02ff5f8abff59a800bd84de6b475f496eb909a51..d9cedbbc2f5ebc25330f4cfbaf9f6b81f955514f 100644
--- a/ProcessLib/ComponentTransport/ComponentTransportFEM.h
+++ b/ProcessLib/ComponentTransport/ComponentTransportFEM.h
@@ -25,6 +25,7 @@
 #include "NumLib/Fem/FiniteElement/TemplateIsoparametric.h"
 #include "NumLib/Fem/InitShapeMatrices.h"
 #include "NumLib/Fem/Integration/GenericIntegrationMethod.h"
+#include "NumLib/Fem/ShapeMatrixCache.h"
 #include "NumLib/Fem/ShapeMatrixPolicy.h"
 #include "NumLib/Function/Interpolation.h"
 #include "NumLib/NumericalStability/AdvectionMatrixAssembler.h"
@@ -37,18 +38,16 @@ namespace ProcessLib
 {
 namespace ComponentTransport
 {
-template <typename NodalRowVectorType, typename GlobalDimNodalMatrixType>
+template <typename GlobalDimNodalMatrixType>
 struct IntegrationPointData final
 {
-    IntegrationPointData(NodalRowVectorType const& N_,
-                         GlobalDimNodalMatrixType const& dNdx_,
+    IntegrationPointData(GlobalDimNodalMatrixType const& dNdx_,
                          double const& integration_weight_)
-        : N(N_), dNdx(dNdx_), integration_weight(integration_weight_)
+        : dNdx(dNdx_), integration_weight(integration_weight_)
     {
     }
 
     void pushBackState() { porosity_prev = porosity; }
-    NodalRowVectorType const N;
     GlobalDimNodalMatrixType const dNdx;
     double const integration_weight;
 
@@ -278,7 +277,7 @@ public:
         for (unsigned ip = 0; ip < n_integration_points; ip++)
         {
             _ip_data.emplace_back(
-                shape_matrices[ip].N, shape_matrices[ip].dNdx,
+                shape_matrices[ip].dNdx,
                 _integration_method.getWeightedPoint(ip).getWeight() *
                     shape_matrices[ip].integralMeasure *
                     shape_matrices[ip].detJ * aperture_size);
@@ -325,12 +324,17 @@ public:
         ParameterLib::SpatialPosition pos;
         pos.setElementID(_element.getID());
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         unsigned const n_integration_points =
             _integration_method.getNumberOfPoints();
+
         for (unsigned ip = 0; ip < n_integration_points; ip++)
         {
             auto& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
+            auto const& N = Ns[ip];
             auto const& chemical_system_id = ip_data.chemical_system_id;
 
             auto const n_component = _transport_process_variables.size();
@@ -369,12 +373,17 @@ public:
         ParameterLib::SpatialPosition pos;
         pos.setElementID(_element.getID());
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         unsigned const n_integration_points =
             _integration_method.getNumberOfPoints();
+
         for (unsigned ip = 0; ip < n_integration_points; ip++)
         {
             auto& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
+            auto const& N = Ns[ip];
             auto& porosity = ip_data.porosity;
             auto const& porosity_prev = ip_data.porosity_prev;
             auto const& chemical_system_id = ip_data.chemical_system_id;
@@ -592,13 +601,17 @@ public:
             ip_flux_vector.reserve(n_integration_points);
         }
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip(0); ip < n_integration_points; ++ip)
         {
             pos.setIntegrationPoint(ip);
 
             auto& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& dNdx = ip_data.dNdx;
+            auto const& N = Ns[ip];
             auto const& w = ip_data.integration_weight;
             auto& porosity = ip_data.porosity;
 
@@ -719,9 +732,11 @@ public:
 
         if (!_process_data.non_advective_form)
         {
-            NumLib::assembleAdvectionMatrix(
+            NumLib::assembleAdvectionMatrix<
+                typename ShapeFunction::MeshElement>(
                 _process_data.stabilizer,
                 _ip_data,
+                _process_data.shape_matrix_cache,
                 ip_flux_vector,
                 average_velocity_norm /
                     static_cast<double>(n_integration_points),
@@ -750,11 +765,15 @@ public:
         auto const& component = phase.component(
             _transport_process_variables[component_id].get().getName());
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip(0); ip < n_integration_points; ++ip)
         {
             auto& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& w = ip_data.integration_weight;
+            auto const& N = Ns[ip];
             auto& porosity = ip_data.porosity;
 
             auto const retardation_factor =
@@ -843,14 +862,18 @@ public:
         MaterialPropertyLib::VariableArray vars;
         MaterialPropertyLib::VariableArray vars_prev;
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip(0); ip < n_integration_points; ++ip)
         {
             pos.setIntegrationPoint(ip);
 
             auto& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& dNdx = ip_data.dNdx;
             auto const& w = ip_data.integration_weight;
+            auto const& N = Ns[ip];
             auto& porosity = ip_data.porosity;
             auto const& porosity_prev = ip_data.porosity_prev;
 
@@ -967,14 +990,18 @@ public:
         double average_velocity_norm = 0.0;
         ip_flux_vector.reserve(n_integration_points);
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip(0); ip < n_integration_points; ip++)
         {
             pos.setIntegrationPoint(ip);
 
             auto const& ip_data = this->_ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& dNdx = ip_data.dNdx;
             auto const& w = ip_data.integration_weight;
+            auto const& N = Ns[ip];
 
             double p_at_xi = 0.;
             NumLib::shapeFunctionInterpolate(local_p, N, p_at_xi);
@@ -1043,8 +1070,9 @@ public:
             average_velocity_norm += velocity.norm();
         }
 
-        NumLib::assembleAdvectionMatrix(
-            process_data.stabilizer, this->_ip_data, ip_flux_vector,
+        NumLib::assembleAdvectionMatrix<typename ShapeFunction::MeshElement>(
+            process_data.stabilizer, this->_ip_data,
+            _process_data.shape_matrix_cache, ip_flux_vector,
             average_velocity_norm / static_cast<double>(n_integration_points),
             local_K);
     }
@@ -1109,14 +1137,18 @@ public:
         auto const& component = phase.component(
             _transport_process_variables[component_id].get().getName());
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip(0); ip < n_integration_points; ++ip)
         {
             pos.setIntegrationPoint(ip);
 
             auto& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& dNdx = ip_data.dNdx;
             auto const& w = ip_data.integration_weight;
+            auto const& N = Ns[ip];
             auto& porosity = ip_data.porosity;
             auto const& porosity_prev = ip_data.porosity_prev;
 
@@ -1240,10 +1272,10 @@ public:
 
         if (!_process_data.non_advective_form)
         {
-            NumLib::assembleAdvectionMatrix(
-                _process_data.stabilizer,
-                _ip_data,
-                ip_flux_vector,
+            NumLib::assembleAdvectionMatrix<
+                typename ShapeFunction::MeshElement>(
+                _process_data.stabilizer, _ip_data,
+                _process_data.shape_matrix_cache, ip_flux_vector,
                 average_velocity_norm /
                     static_cast<double>(n_integration_points),
                 KCC_Laplacian);
@@ -1307,14 +1339,18 @@ public:
         MaterialPropertyLib::VariableArray vars;
         MaterialPropertyLib::VariableArray vars_prev;
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip(0); ip < n_integration_points; ++ip)
         {
             pos.setIntegrationPoint(ip);
 
             auto& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& dNdx = ip_data.dNdx;
             auto const& w = ip_data.integration_weight;
+            auto const& N = Ns[ip];
             auto& phi = ip_data.porosity;
             auto const& phi_prev = ip_data.porosity_prev;
 
@@ -1429,14 +1465,18 @@ public:
         auto const& component = phase.component(
             _transport_process_variables[component_id].get().getName());
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip(0); ip < n_integration_points; ++ip)
         {
             pos.setIntegrationPoint(ip);
 
             auto& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& dNdx = ip_data.dNdx;
             auto const& w = ip_data.integration_weight;
+            auto const& N = Ns[ip];
             auto& phi = ip_data.porosity;
             auto const& phi_prev = ip_data.porosity_prev;
 
@@ -1513,8 +1553,9 @@ public:
             average_velocity_norm += q.norm();
         }
 
-        NumLib::assembleAdvectionMatrix(
-            _process_data.stabilizer, _ip_data, ip_flux_vector,
+        NumLib::assembleAdvectionMatrix<typename ShapeFunction::MeshElement>(
+            _process_data.stabilizer, _ip_data,
+            _process_data.shape_matrix_cache, ip_flux_vector,
             average_velocity_norm / static_cast<double>(n_integration_points),
             KCC_Laplacian);
 
@@ -1552,13 +1593,18 @@ public:
         auto const& medium =
             *_process_data.media_map.getMedium(_element.getID());
         auto const component_id = transport_process_id - 1;
+
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip(0); ip < n_integration_points; ++ip)
         {
             pos.setIntegrationPoint(ip);
 
             auto& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
             auto const w = ip_data.integration_weight;
+            auto const& N = Ns[ip];
             auto& porosity = ip_data.porosity;
             auto const& porosity_prev = ip_data.porosity_prev;
             auto const chemical_system_id = ip_data.chemical_system_id;
@@ -1669,11 +1715,15 @@ public:
             *_process_data.media_map.getMedium(_element.getID());
         auto const& phase = medium.phase("AqueousLiquid");
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip = 0; ip < n_integration_points; ++ip)
         {
             auto const& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& dNdx = ip_data.dNdx;
+            auto const& N = Ns[ip];
             auto const& porosity = ip_data.porosity;
 
             pos.setIntegrationPoint(ip);
@@ -1715,7 +1765,8 @@ public:
     Eigen::Map<const Eigen::RowVectorXd> getShapeMatrix(
         const unsigned integration_point) const override
     {
-        auto const& N = _ip_data[integration_point].N;
+        auto const& N = _process_data.shape_matrix_cache.NsHigherOrder<
+            typename ShapeFunction::MeshElement>()[integration_point];
 
         // assumes N is stored contiguously in memory
         return Eigen::Map<const Eigen::RowVectorXd>(N.data(), N.size());
@@ -1896,11 +1947,15 @@ public:
         auto const& component = phase.component(
             _transport_process_variables[component_id].get().getName());
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip = 0; ip < n_integration_points; ++ip)
         {
             auto const& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& dNdx = ip_data.dNdx;
+            auto const& N = Ns[ip];
             auto const& phi = ip_data.porosity;
 
             pos.setIntegrationPoint(ip);
@@ -1969,11 +2024,7 @@ private:
     std::vector<std::reference_wrapper<ProcessVariable>> const
         _transport_process_variables;
 
-    std::vector<
-        IntegrationPointData<NodalRowVectorType, GlobalDimNodalMatrixType>,
-        Eigen::aligned_allocator<
-            IntegrationPointData<NodalRowVectorType, GlobalDimNodalMatrixType>>>
-        _ip_data;
+    std::vector<IntegrationPointData<GlobalDimNodalMatrixType>> _ip_data;
 
     double getHeatEnergyCoefficient(
         MaterialPropertyLib::VariableArray const& vars, const double porosity,
diff --git a/ProcessLib/ComponentTransport/ComponentTransportProcess.cpp b/ProcessLib/ComponentTransport/ComponentTransportProcess.cpp
index 2e81003a219197df83eb54f935964cf7b7ed1243..74cfee620f7af6b2cbb5879de56038548e53bd07 100644
--- a/ProcessLib/ComponentTransport/ComponentTransportProcess.cpp
+++ b/ProcessLib/ComponentTransport/ComponentTransportProcess.cpp
@@ -23,6 +23,7 @@
 #include "MathLib/LinAlg/LinAlg.h"
 #include "MeshLib/Utils/getOrCreateMeshProperty.h"
 #include "NumLib/DOF/ComputeSparsityPattern.h"
+#include "NumLib/Fem/ShapeMatrixCache.h"
 #include "NumLib/NumericalStability/FluxCorrectedTransport.h"
 #include "NumLib/NumericalStability/NumericalStabilization.h"
 #include "NumLib/ODESolver/NonlinearSystem.h"
diff --git a/ProcessLib/ComponentTransport/ComponentTransportProcessData.h b/ProcessLib/ComponentTransport/ComponentTransportProcessData.h
index 2ac5512eed9f6176ad3603b62eb720ce7e711ac8..121c3e7641c99d6d8de72414128a30b927670dc0 100644
--- a/ProcessLib/ComponentTransport/ComponentTransportProcessData.h
+++ b/ProcessLib/ComponentTransport/ComponentTransportProcessData.h
@@ -17,6 +17,7 @@
 #include "LookupTable.h"
 #include "MaterialLib/MPL/MaterialSpatialDistributionMap.h"
 #include "MathLib/LinAlg/Eigen/EigenMapTools.h"
+#include "NumLib/Fem/ShapeMatrixCache.h"
 #include "NumLib/NumericalStability/NumericalStabilization.h"
 #include "ParameterLib/ConstantParameter.h"
 #include "ParameterLib/Parameter.h"
@@ -84,6 +85,9 @@ struct ComponentTransportProcessData
 
     bool const isothermal;
 
+    /// caches for each mesh element type the shape matrix
+    NumLib::ShapeMatrixCache shape_matrix_cache;
+
     static const int hydraulic_process_id = 0;
     // Thermal process is optional, indicated by -1. If present, it is positive.
     const int thermal_process_id = isothermal ? -1 : 1;
diff --git a/ProcessLib/ComponentTransport/CreateComponentTransportProcess.cpp b/ProcessLib/ComponentTransport/CreateComponentTransportProcess.cpp
index bb69b4fe94eaf9f6296d9c50d96408955fc8e1de..5710f1e4d518c561b22d13102ef3f32c05786742 100644
--- a/ProcessLib/ComponentTransport/CreateComponentTransportProcess.cpp
+++ b/ProcessLib/ComponentTransport/CreateComponentTransportProcess.cpp
@@ -317,7 +317,7 @@ std::unique_ptr<Process> createComponentTransportProcess(
         mesh_space_dimension,
         *aperture_size_parameter,
         isothermal,
-    };
+        NumLib::ShapeMatrixCache{integration_order}};
 
     SecondaryVariableCollection secondary_variables;
 
diff --git a/ProcessLib/HT/CreateHTProcess.cpp b/ProcessLib/HT/CreateHTProcess.cpp
index f8cc32419791b3aec6bdbe3a1be7360ef1cf54a1..8a5a4b6d44d6007740c1491c125959a083b23185 100644
--- a/ProcessLib/HT/CreateHTProcess.cpp
+++ b/ProcessLib/HT/CreateHTProcess.cpp
@@ -218,7 +218,8 @@ std::unique_ptr<Process> createHTProcess(
                                std::move(stabilizer),
                                projected_specific_body_force_vectors,
                                mesh_space_dimension,
-                               *aperture_size_parameter};
+                               *aperture_size_parameter,
+                               NumLib::ShapeMatrixCache{integration_order}};
 
     SecondaryVariableCollection secondary_variables;
 
diff --git a/ProcessLib/HT/HTFEM.h b/ProcessLib/HT/HTFEM.h
index da14725acc227e43f14588f20cda86ea16f12ea9..f0e08207fa6139639579869f136c3952dcfe4c88 100644
--- a/ProcessLib/HT/HTFEM.h
+++ b/ProcessLib/HT/HTFEM.h
@@ -79,7 +79,7 @@ public:
         for (unsigned ip = 0; ip < n_integration_points; ip++)
         {
             _ip_data.emplace_back(
-                shape_matrices[ip].N, shape_matrices[ip].dNdx,
+                shape_matrices[ip].dNdx,
                 _integration_method.getWeightedPoint(ip).getWeight() *
                     shape_matrices[ip].integralMeasure *
                     shape_matrices[ip].detJ * aperture_size);
@@ -89,7 +89,8 @@ public:
     Eigen::Map<const Eigen::RowVectorXd> getShapeMatrix(
         const unsigned integration_point) const override
     {
-        auto const& N = _ip_data[integration_point].N;
+        auto const& N = _process_data.shape_matrix_cache.NsHigherOrder<
+            typename ShapeFunction::MeshElement>()[integration_point];
 
         // assumes N is stored contiguously in memory
         return Eigen::Map<const Eigen::RowVectorXd>(N.data(), N.size());
@@ -168,11 +169,7 @@ protected:
     HTProcessData const& _process_data;
 
     NumLib::GenericIntegrationMethod const& _integration_method;
-    std::vector<
-        IntegrationPointData<NodalRowVectorType, GlobalDimNodalMatrixType>,
-        Eigen::aligned_allocator<
-            IntegrationPointData<NodalRowVectorType, GlobalDimNodalMatrixType>>>
-        _ip_data;
+    std::vector<IntegrationPointData<GlobalDimNodalMatrixType>> _ip_data;
 
     double getHeatEnergyCoefficient(
         MaterialPropertyLib::VariableArray const& vars, const double porosity,
@@ -270,11 +267,15 @@ protected:
             *_process_data.media_map.getMedium(_element.getID());
         auto const& liquid_phase = medium.phase("AqueousLiquid");
 
+        auto const& Ns =
+            _process_data.shape_matrix_cache
+                .NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip = 0; ip < n_integration_points; ++ip)
         {
             auto const& ip_data = _ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& dNdx = ip_data.dNdx;
+            auto const& N = Ns[ip];
 
             pos.setIntegrationPoint(ip);
 
diff --git a/ProcessLib/HT/HTLocalAssemblerInterface.h b/ProcessLib/HT/HTLocalAssemblerInterface.h
index c058a23b5c1fa1fbd17f241065aba9654359b314..8fcd126c7deea126c4e659ba6f7699d19b667078 100644
--- a/ProcessLib/HT/HTLocalAssemblerInterface.h
+++ b/ProcessLib/HT/HTLocalAssemblerInterface.h
@@ -21,19 +21,15 @@ namespace ProcessLib
 
 namespace HT
 {
-template <typename NodalRowVectorType, typename GlobalDimNodalMatrixType>
+template <typename GlobalDimNodalMatrixType>
 struct IntegrationPointData final
 {
-    IntegrationPointData(NodalRowVectorType N_,
-                         GlobalDimNodalMatrixType dNdx_,
+    IntegrationPointData(GlobalDimNodalMatrixType dNdx_,
                          double const& integration_weight_)
-        : N(std::move(N_)),
-          dNdx(std::move(dNdx_)),
-          integration_weight(integration_weight_)
+        : dNdx(std::move(dNdx_)), integration_weight(integration_weight_)
     {
     }
 
-    NodalRowVectorType const N;
     GlobalDimNodalMatrixType const dNdx;
     double const integration_weight;
 
diff --git a/ProcessLib/HT/HTProcessData.h b/ProcessLib/HT/HTProcessData.h
index f12e2c81c7905b0b9f25bfc7801ca08c69026ba1..c951cf6b98911dd67f1f0a20dd9a351a06ab1efe 100644
--- a/ProcessLib/HT/HTProcessData.h
+++ b/ProcessLib/HT/HTProcessData.h
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "MaterialLib/MPL/MaterialSpatialDistributionMap.h"
+#include "NumLib/Fem/ShapeMatrixCache.h"
 #include "NumLib/NumericalStability/NumericalStabilization.h"
 #include "ParameterLib/ConstantParameter.h"
 #include "ParameterLib/Parameter.h"
@@ -43,6 +44,9 @@ struct HTProcessData final
     /// cross section area of 1D element. For 3D element, the value is set to 1.
     ParameterLib::Parameter<double> const& aperture_size =
         ParameterLib::ConstantParameter<double>("constant_one", 1.0);
+
+    /// caches for each mesh element type the shape matrix
+    NumLib::ShapeMatrixCache shape_matrix_cache;
 };
 
 }  // namespace HT
diff --git a/ProcessLib/HT/MonolithicHTFEM.h b/ProcessLib/HT/MonolithicHTFEM.h
index 902036df8d9c41f16a2f95148a2e2880ab7fdf4b..91ad82498373ee92115067293de0211fc2fda1cb 100644
--- a/ProcessLib/HT/MonolithicHTFEM.h
+++ b/ProcessLib/HT/MonolithicHTFEM.h
@@ -121,13 +121,17 @@ public:
         double average_velocity_norm = 0.0;
         ip_flux_vector.reserve(n_integration_points);
 
+        auto const& Ns =
+            process_data.shape_matrix_cache
+                .template NsHigherOrder<typename ShapeFunction::MeshElement>();
+
         for (unsigned ip(0); ip < n_integration_points; ip++)
         {
             pos.setIntegrationPoint(ip);
 
             auto const& ip_data = this->_ip_data[ip];
-            auto const& N = ip_data.N;
             auto const& dNdx = ip_data.dNdx;
+            auto const& N = Ns[ip];
             auto const& w = ip_data.integration_weight;
 
             double T_int_pt = 0.0;
@@ -210,8 +214,9 @@ public:
              * in buoyancy effects */
         }
 
-        NumLib::assembleAdvectionMatrix(
-            process_data.stabilizer, this->_ip_data, ip_flux_vector,
+        NumLib::assembleAdvectionMatrix<typename ShapeFunction::MeshElement>(
+            process_data.stabilizer, this->_ip_data,
+            process_data.shape_matrix_cache, ip_flux_vector,
             average_velocity_norm / static_cast<double>(n_integration_points),
             KTT);
     }
diff --git a/ProcessLib/HT/StaggeredHTFEM-impl.h b/ProcessLib/HT/StaggeredHTFEM-impl.h
index 65c67a68b1ebc0dbfcf86acbc8aa760b743bfdc0..7f92bddc13beca6190f8a2f1bb7fbe2891b29bdb 100644
--- a/ProcessLib/HT/StaggeredHTFEM-impl.h
+++ b/ProcessLib/HT/StaggeredHTFEM-impl.h
@@ -79,13 +79,17 @@ void StaggeredHTFEM<ShapeFunction, GlobalDim>::assembleHydraulicEquation(
     unsigned const n_integration_points =
         this->_integration_method.getNumberOfPoints();
 
+    auto const& Ns =
+        process_data.shape_matrix_cache
+            .template NsHigherOrder<typename ShapeFunction::MeshElement>();
+
     for (unsigned ip(0); ip < n_integration_points; ip++)
     {
         pos.setIntegrationPoint(ip);
 
         auto const& ip_data = this->_ip_data[ip];
-        auto const& N = ip_data.N;
         auto const& dNdx = ip_data.dNdx;
+        auto const& N = Ns[ip];
         auto const& w = ip_data.integration_weight;
 
         double p_int_pt = 0.0;
@@ -208,13 +212,17 @@ void StaggeredHTFEM<ShapeFunction, GlobalDim>::assembleHeatTransportEquation(
     double average_velocity_norm = 0.0;
     ip_flux_vector.reserve(n_integration_points);
 
+    auto const& Ns =
+        process_data.shape_matrix_cache
+            .template NsHigherOrder<typename ShapeFunction::MeshElement>();
+
     for (unsigned ip(0); ip < n_integration_points; ip++)
     {
         pos.setIntegrationPoint(ip);
 
         auto const& ip_data = this->_ip_data[ip];
-        auto const& N = ip_data.N;
         auto const& dNdx = ip_data.dNdx;
+        auto const& N = Ns[ip];
         auto const& w = ip_data.integration_weight;
 
         double p_at_xi = 0.;
@@ -279,8 +287,9 @@ void StaggeredHTFEM<ShapeFunction, GlobalDim>::assembleHeatTransportEquation(
         average_velocity_norm += velocity.norm();
     }
 
-    NumLib::assembleAdvectionMatrix(
-        process_data.stabilizer, this->_ip_data, ip_flux_vector,
+    NumLib::assembleAdvectionMatrix<typename ShapeFunction::MeshElement>(
+        process_data.stabilizer, this->_ip_data,
+        process_data.shape_matrix_cache, ip_flux_vector,
         average_velocity_norm / static_cast<double>(n_integration_points),
         local_K);
 }
diff --git a/ProcessLib/LiquidFlow/CreateLiquidFlowProcess.cpp b/ProcessLib/LiquidFlow/CreateLiquidFlowProcess.cpp
index c25acd8344f3b6e1a13709d0b20f0927cbe3a999..c494c08b93df3a97cab43d23f6432e7b9a509f38 100644
--- a/ProcessLib/LiquidFlow/CreateLiquidFlowProcess.cpp
+++ b/ProcessLib/LiquidFlow/CreateLiquidFlowProcess.cpp
@@ -152,7 +152,8 @@ std::unique_ptr<Process> createLiquidFlowProcess(
         mesh_space_dimension,
         std::move(specific_body_force),
         has_gravity,
-        *aperture_size_parameter};
+        *aperture_size_parameter,
+        NumLib::ShapeMatrixCache{integration_order}};
 
     return std::make_unique<LiquidFlowProcess>(
         std::move(name), mesh, std::move(jacobian_assembler), parameters,
diff --git a/ProcessLib/LiquidFlow/LiquidFlowData.h b/ProcessLib/LiquidFlow/LiquidFlowData.h
index b95d1dbad784f9c16a084da2adc273f5ebb6c4ad..b366ccaf2dec593f155fec1db29ebb9fff930e22 100644
--- a/ProcessLib/LiquidFlow/LiquidFlowData.h
+++ b/ProcessLib/LiquidFlow/LiquidFlowData.h
@@ -13,6 +13,7 @@
 #include <Eigen/Core>
 
 #include "MaterialLib/MPL/MaterialSpatialDistributionMap.h"
+#include "NumLib/Fem/ShapeMatrixCache.h"
 #include "ParameterLib/Parameter.h"
 
 namespace ProcessLib
@@ -34,6 +35,9 @@ struct LiquidFlowData final
     /// It stores aperture size, which is the thickness of 2D element or the
     /// cross section area of 1D element. For 3D element, the value is set to 1.
     ParameterLib::Parameter<double> const& aperture_size;
+
+    /// caches for each mesh element type the shape matrix
+    NumLib::ShapeMatrixCache shape_matrix_cache;
 };
 
 }  // namespace LiquidFlow
diff --git a/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler-impl.h b/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler-impl.h
index 4b3e41fffcc5f1655d1d2bb76dd690e035cd2e3f..99bc458dfdc0408c641f56203e39b390f2a9b4c5 100644
--- a/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler-impl.h
+++ b/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler-impl.h
@@ -143,15 +143,16 @@ void LiquidFlowLocalAssembler<ShapeFunction, GlobalDim>::
         _process_data.element_rotation_matrices[_element.getID()].transpose() *
         _process_data.specific_body_force;
 
-    auto const& N = _shape_matrix_cache
-                        .NsHigherOrder<typename ShapeFunction::MeshElement>();
+    auto const& Ns = _process_data.shape_matrix_cache
+                         .NsHigherOrder<typename ShapeFunction::MeshElement>();
 
     for (unsigned ip = 0; ip < n_integration_points; ip++)
     {
         auto const& ip_data = _ip_data[ip];
+        auto const& N = Ns[ip];
 
         double p = 0.;
-        NumLib::shapeFunctionInterpolate(local_x, N[ip], p);
+        NumLib::shapeFunctionInterpolate(local_x, N, p);
         vars.liquid_phase_pressure = p;
 
         // Compute density:
@@ -176,7 +177,7 @@ void LiquidFlowLocalAssembler<ShapeFunction, GlobalDim>::
         // Assemble mass matrix, M
         local_M.noalias() +=
             (porosity * ddensity_dpressure / fluid_density + storage) *
-            N[ip].transpose() * N[ip] * ip_data.integration_weight;
+            N.transpose() * N * ip_data.integration_weight;
 
         // Compute viscosity:
         auto const viscosity =
@@ -295,14 +296,15 @@ void LiquidFlowLocalAssembler<ShapeFunction, GlobalDim>::
         _process_data.element_rotation_matrices[_element.getID()].transpose() *
         _process_data.specific_body_force;
 
-    auto const& N = _shape_matrix_cache
-                        .NsHigherOrder<typename ShapeFunction::MeshElement>();
+    auto const& Ns = _process_data.shape_matrix_cache
+                         .NsHigherOrder<typename ShapeFunction::MeshElement>();
 
     for (unsigned ip = 0; ip < n_integration_points; ip++)
     {
         auto const& ip_data = _ip_data[ip];
+        auto const& N = Ns[ip];
         double p = 0.;
-        NumLib::shapeFunctionInterpolate(local_x, N[ip], p);
+        NumLib::shapeFunctionInterpolate(local_x, N, p);
         vars.liquid_phase_pressure = p;
 
         // Compute density:
@@ -333,8 +335,7 @@ void LiquidFlowLocalAssembler<ShapeFunction, GlobalDim>::IsotropicCalculator::
     calculateLaplacianAndGravityTerm(
         Eigen::Map<NodalMatrixType>& local_K,
         Eigen::Map<NodalVectorType>& local_b,
-        IntegrationPointData<NodalRowVectorType,
-                             GlobalDimNodalMatrixType> const& ip_data,
+        IntegrationPointData<GlobalDimNodalMatrixType> const& ip_data,
         GlobalDimMatrixType const& permeability, double const mu,
         double const rho_L, GlobalDimVectorType const& specific_body_force,
         bool const has_gravity)
@@ -355,8 +356,7 @@ Eigen::Matrix<double, GlobalDim, 1>
 LiquidFlowLocalAssembler<ShapeFunction, GlobalDim>::IsotropicCalculator::
     calculateVelocity(
         Eigen::Map<const NodalVectorType> const& local_p,
-        IntegrationPointData<NodalRowVectorType,
-                             GlobalDimNodalMatrixType> const& ip_data,
+        IntegrationPointData<GlobalDimNodalMatrixType> const& ip_data,
         GlobalDimMatrixType const& permeability, double const mu,
         double const rho_L, GlobalDimVectorType const& specific_body_force,
         bool const has_gravity)
@@ -377,8 +377,7 @@ void LiquidFlowLocalAssembler<ShapeFunction, GlobalDim>::AnisotropicCalculator::
     calculateLaplacianAndGravityTerm(
         Eigen::Map<NodalMatrixType>& local_K,
         Eigen::Map<NodalVectorType>& local_b,
-        IntegrationPointData<NodalRowVectorType,
-                             GlobalDimNodalMatrixType> const& ip_data,
+        IntegrationPointData<GlobalDimNodalMatrixType> const& ip_data,
         GlobalDimMatrixType const& permeability, double const mu,
         double const rho_L, GlobalDimVectorType const& specific_body_force,
         bool const has_gravity)
@@ -399,8 +398,7 @@ Eigen::Matrix<double, GlobalDim, 1>
 LiquidFlowLocalAssembler<ShapeFunction, GlobalDim>::AnisotropicCalculator::
     calculateVelocity(
         Eigen::Map<const NodalVectorType> const& local_p,
-        IntegrationPointData<NodalRowVectorType,
-                             GlobalDimNodalMatrixType> const& ip_data,
+        IntegrationPointData<GlobalDimNodalMatrixType> const& ip_data,
         GlobalDimMatrixType const& permeability, double const mu,
         double const rho_L, GlobalDimVectorType const& specific_body_force,
         bool const has_gravity)
diff --git a/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler.h b/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler.h
index 86ae1838af3d58f8a78f66994b12aba1bb890e03..dcdfeb934e40ab9146fadf230c05059aa2a18957 100644
--- a/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler.h
+++ b/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler.h
@@ -34,7 +34,7 @@ namespace ProcessLib
 {
 namespace LiquidFlow
 {
-template <typename NodalRowVectorType, typename GlobalDimNodalMatrixType>
+template <typename GlobalDimNodalMatrixType>
 struct IntegrationPointData final
 {
     explicit IntegrationPointData(GlobalDimNodalMatrixType const& dNdx_,
@@ -86,12 +86,10 @@ public:
         std::size_t const /*local_matrix_size*/,
         NumLib::GenericIntegrationMethod const& integration_method,
         bool const is_axially_symmetric,
-        LiquidFlowData const& process_data,
-        NumLib::ShapeMatrixCache const& shape_matrix_cache)
+        LiquidFlowData const& process_data)
         : _element(element),
           _integration_method(integration_method),
-          _process_data(process_data),
-          _shape_matrix_cache(shape_matrix_cache)
+          _process_data(process_data)
     {
         unsigned const n_integration_points =
             _integration_method.getNumberOfPoints();
@@ -137,7 +135,7 @@ public:
         const unsigned integration_point) const override
     {
         auto const& N =
-            _shape_matrix_cache
+            _process_data.shape_matrix_cache
                 .NsHigherOrder<typename ShapeFunction::MeshElement>();
 
         // assumes N is stored contiguously in memory
@@ -155,11 +153,7 @@ private:
     MeshLib::Element const& _element;
 
     NumLib::GenericIntegrationMethod const& _integration_method;
-    std::vector<
-        IntegrationPointData<NodalRowVectorType, GlobalDimNodalMatrixType>,
-        Eigen::aligned_allocator<
-            IntegrationPointData<NodalRowVectorType, GlobalDimNodalMatrixType>>>
-        _ip_data;
+    std::vector<IntegrationPointData<GlobalDimNodalMatrixType>> _ip_data;
 
     /**
      *  Calculator of the Laplacian and the gravity term for anisotropic
@@ -170,16 +164,14 @@ private:
         static void calculateLaplacianAndGravityTerm(
             Eigen::Map<NodalMatrixType>& local_K,
             Eigen::Map<NodalVectorType>& local_b,
-            IntegrationPointData<NodalRowVectorType,
-                                 GlobalDimNodalMatrixType> const& ip_data,
+            IntegrationPointData<GlobalDimNodalMatrixType> const& ip_data,
             GlobalDimMatrixType const& permeability, double const mu,
             double const rho_L, GlobalDimVectorType const& specific_body_force,
             bool const has_gravity);
 
         static Eigen::Matrix<double, GlobalDim, 1> calculateVelocity(
             Eigen::Map<const NodalVectorType> const& local_p,
-            IntegrationPointData<NodalRowVectorType,
-                                 GlobalDimNodalMatrixType> const& ip_data,
+            IntegrationPointData<GlobalDimNodalMatrixType> const& ip_data,
             GlobalDimMatrixType const& permeability, double const mu,
             double const rho_L, GlobalDimVectorType const& specific_body_force,
             bool const has_gravity);
@@ -194,16 +186,14 @@ private:
         static void calculateLaplacianAndGravityTerm(
             Eigen::Map<NodalMatrixType>& local_K,
             Eigen::Map<NodalVectorType>& local_b,
-            IntegrationPointData<NodalRowVectorType,
-                                 GlobalDimNodalMatrixType> const& ip_data,
+            IntegrationPointData<GlobalDimNodalMatrixType> const& ip_data,
             GlobalDimMatrixType const& permeability, double const mu,
             double const rho_L, GlobalDimVectorType const& specific_body_force,
             bool const has_gravity);
 
         static Eigen::Matrix<double, GlobalDim, 1> calculateVelocity(
             Eigen::Map<const NodalVectorType> const& local_p,
-            IntegrationPointData<NodalRowVectorType,
-                                 GlobalDimNodalMatrixType> const& ip_data,
+            IntegrationPointData<GlobalDimNodalMatrixType> const& ip_data,
             GlobalDimMatrixType const& permeability, double const mu,
             double const rho_L, GlobalDimVectorType const& specific_body_force,
             bool const has_gravity);
@@ -231,7 +221,6 @@ private:
                               VelocityCacheType& darcy_velocity_at_ips) const;
 
     const LiquidFlowData& _process_data;
-    NumLib::ShapeMatrixCache const& _shape_matrix_cache;
 };
 
 }  // namespace LiquidFlow
diff --git a/ProcessLib/LiquidFlow/LiquidFlowProcess.cpp b/ProcessLib/LiquidFlow/LiquidFlowProcess.cpp
index 3a5d2af3e8b1759314a97a41bc41e49386ac20cd..23f7891cc4dd511ce86612613489196445d0661a 100644
--- a/ProcessLib/LiquidFlow/LiquidFlowProcess.cpp
+++ b/ProcessLib/LiquidFlow/LiquidFlowProcess.cpp
@@ -41,7 +41,6 @@ LiquidFlowProcess::LiquidFlowProcess(
               integration_order, std::move(process_variables),
               std::move(secondary_variables)),
       _process_data(std::move(process_data)),
-      _shape_matrix_cache{integration_order},
       _surfaceflux(std::move(surfaceflux)),
       _is_linear(is_linear)
 {
@@ -60,7 +59,7 @@ void LiquidFlowProcess::initializeConcreteProcess(
     ProcessLib::createLocalAssemblers<LiquidFlowLocalAssembler>(
         mesh_space_dimension, mesh.getElements(), dof_table, _local_assemblers,
         NumLib::IntegrationOrder{integration_order}, mesh.isAxiallySymmetric(),
-        _process_data, _shape_matrix_cache);
+        _process_data);
 
     _secondary_variables.addSecondaryVariable(
         "darcy_velocity",
diff --git a/ProcessLib/LiquidFlow/LiquidFlowProcess.h b/ProcessLib/LiquidFlow/LiquidFlowProcess.h
index aac257a1e6f3284647df77de2d253459d097875e..8ac5910005eeb4ec6a5c8ea37724280b5ff5cbfa 100644
--- a/ProcessLib/LiquidFlow/LiquidFlowProcess.h
+++ b/ProcessLib/LiquidFlow/LiquidFlowProcess.h
@@ -17,7 +17,6 @@
 #include "LiquidFlowData.h"
 #include "LiquidFlowLocalAssembler.h"
 #include "NumLib/DOF/LocalToGlobalIndexMap.h"
-#include "NumLib/Fem/ShapeMatrixCache.h"
 #include "ProcessLib/Process.h"
 #include "ProcessLib/SurfaceFlux/SurfaceFluxData.h"
 
@@ -109,8 +108,6 @@ private:
     std::vector<std::unique_ptr<LiquidFlowLocalAssemblerInterface>>
         _local_assemblers;
 
-    NumLib::ShapeMatrixCache _shape_matrix_cache;
-
     std::unique_ptr<ProcessLib::SurfaceFluxData> _surfaceflux;
     MeshLib::PropertyVector<double>* _hydraulic_flow = nullptr;
     bool _is_linear = false;