diff --git a/src/algebra/IntegrationTools.hpp b/src/algebra/IntegrationTools.hpp
index 08642b794df8470eb1e4598c75d643ccdf1de364..954924d6ec9f6db78127d3e6afe4fae28db6cb3b 100644
--- a/src/algebra/IntegrationTools.hpp
+++ b/src/algebra/IntegrationTools.hpp
@@ -8,6 +8,7 @@
 #include <utils/Types.hpp>
 
 #include <analysis/GaussLegendreQuadrature.hpp>
+#include <analysis/GaussLobattoQuadrature.hpp>
 
 #include <cmath>
 
@@ -21,337 +22,13 @@ enum class QuadratureType : int8_t
   QT__end
 };
 
-class IntegrationMethod
-{
- public:
-  PUGS_INLINE virtual SmallArray<TinyVector<1>> quadraturePoints(const double& a, const double& b) = 0;
-  PUGS_INLINE virtual size_t numberOfPoints()                                                      = 0;
-  PUGS_INLINE virtual SmallArray<double> weights()                                                 = 0;
-  // One does not use the '=default' constructor to avoid
-  // (zero-initialization) performances issues
-  PUGS_INLINE
-  constexpr IntegrationMethod() noexcept {}
-
-  PUGS_INLINE
-  constexpr IntegrationMethod(const IntegrationMethod&) noexcept = default;
-
-  PUGS_INLINE
-  constexpr IntegrationMethod(IntegrationMethod&& v) noexcept = default;
-
-  virtual PUGS_INLINE ~IntegrationMethod() noexcept = default;
-};
-
-template <size_t Order>
-class IntegrationMethodLobatto : public IntegrationMethod
-{
- private:
-  static constexpr size_t number_points = (Order < 4 ? 3 : (Order < 6 ? 4 : (Order < 8 ? 5 : Order < 10 ? 6 : 7)));
-  SmallArray<double> m_weights;
-  SmallArray<TinyVector<1>> m_quadrature_positions;
-  PUGS_INLINE SmallArray<TinyVector<1>>
-  translateQuadraturePoints(const double& a, const double& b)
-  {
-    SmallArray<TinyVector<1>> points{number_points};
-    double length = b - a;
-    for (size_t i = 0; i < m_quadrature_positions.size(); i++) {
-      TinyVector<1> qpos{m_quadrature_positions[i]};
-      points[i] = 0.5 * (length * qpos + a + b);
-    }
-    return points;
-  }
-
-  PUGS_INLINE
-  void
-  fillArrayLobatto()
-  {
-    switch (Order) {
-    case 0:
-    case 1:
-    case 2:
-    case 3: {
-      double oneov3             = 1. / 3.;
-      m_quadrature_positions[0] = -1.;
-      m_quadrature_positions[1] = 0.;
-      m_quadrature_positions[2] = 1.;
-      m_weights[0]              = oneov3;
-      m_weights[1]              = 4 * oneov3;
-      m_weights[2]              = oneov3;
-      // m_quadrature_positions.fill({-1, 0, 1});
-      // m_weights.fill({oneov3, 4 * oneov3, oneov3});
-      break;
-    }
-    case 4:
-    case 5: {
-      double oneov6             = 1. / 6.;
-      double coef               = std::sqrt(1. / 5.);
-      m_quadrature_positions[0] = -1;
-      m_quadrature_positions[1] = -coef;
-      m_quadrature_positions[2] = coef;
-      m_quadrature_positions[3] = 1;
-      m_weights[0]              = oneov6;
-      m_weights[1]              = 5 * oneov6;
-      m_weights[2]              = 5 * oneov6;
-      m_weights[3]              = oneov6;
-      break;
-    }
-    case 6:
-    case 7: {
-      double oneov90            = 1. / 90.;
-      double oneov10            = 1. / 10.;
-      double coef               = std::sqrt(3. / 7.);
-      m_quadrature_positions[0] = -1;
-      m_quadrature_positions[1] = -coef;
-      m_quadrature_positions[2] = 0;
-      m_quadrature_positions[3] = coef;
-      m_quadrature_positions[4] = 1;
-      m_weights[0]              = oneov10;
-      m_weights[1]              = 49 * oneov90;
-      m_weights[2]              = 64 * oneov90;
-      m_weights[3]              = 49 * oneov90;
-      m_weights[4]              = oneov10;
-      break;
-    }
-    case 8:
-    case 9: {
-      double oneov30            = 1. / 30.;
-      double coef1              = std::sqrt(1. / 3. - 2 * std::sqrt(7) / 21.);
-      double coef2              = std::sqrt(1. / 3. + 2 * std::sqrt(7) / 21.);
-      double coef3              = (14. + std::sqrt(7)) * oneov30;
-      double coef4              = (14. - std::sqrt(7)) * oneov30;
-      m_quadrature_positions[0] = -1;
-      m_quadrature_positions[1] = -coef2;
-      m_quadrature_positions[2] = -coef1;
-      m_quadrature_positions[3] = coef1;
-      m_quadrature_positions[4] = coef2;
-      m_quadrature_positions[5] = 1;
-      m_weights[0]              = 2. * oneov30;
-      m_weights[1]              = coef4;
-      m_weights[2]              = coef3;
-      m_weights[3]              = coef3;
-      m_weights[4]              = coef4;
-      m_weights[5]              = 2. * oneov30;
-      break;
-    }
-    case 10:
-    case 11: {
-      double oneov350           = 1. / 350.;
-      double oneov21            = 1. / 21.;
-      double coef0              = 256. / 525.;
-      double coef1              = std::sqrt((5. - 2 * std::sqrt(5. / 3.)) / 11.);
-      double coef2              = std::sqrt((5. + 2 * std::sqrt(5. / 3.)) / 11.);
-      double coef3              = (124. + 7 * std::sqrt(15.)) * oneov350;
-      double coef4              = (124. - 7 * std::sqrt(15.)) * oneov350;
-      m_quadrature_positions[0] = -1;
-      m_quadrature_positions[1] = -coef2;
-      m_quadrature_positions[2] = -coef1;
-      m_quadrature_positions[3] = 0;
-      m_quadrature_positions[4] = coef1;
-      m_quadrature_positions[5] = coef2;
-      m_quadrature_positions[6] = 1;
-      m_weights[0]              = oneov21;
-      m_weights[1]              = coef4;
-      m_weights[2]              = coef3;
-      m_weights[3]              = coef0;
-      m_weights[4]              = coef3;
-      m_weights[5]              = coef4;
-      m_weights[6]              = oneov21;
-      break;
-    }
-
-    default: {
-      throw UnexpectedError("Gauss-Lobatto quadratures handle orders up to 11.");
-      break;
-    }
-    }
-  }
-
- public:
-  // One does not use the '=default' constructor to avoid
-  // (zero-initialization) performances issues
-  PUGS_INLINE
-  constexpr IntegrationMethodLobatto() noexcept : m_weights{number_points}, m_quadrature_positions{number_points}
-  {
-    m_weights.fill(0.);
-    m_quadrature_positions.fill(TinyVector<1, double>{0});
-    fillArrayLobatto();
-  }
-
-  PUGS_INLINE
-  SmallArray<TinyVector<1>>
-  quadraturePoints(const double& a, const double& b)
-  {
-    return translateQuadraturePoints(a, b);
-  }
-
-  PUGS_INLINE size_t
-  numberOfPoints()
-  {
-    return number_points;
-  }
-  PUGS_INLINE
-  SmallArray<double>
-  weights()
-  {
-    return m_weights;
-  }
-
-  PUGS_INLINE
-  constexpr IntegrationMethodLobatto(const IntegrationMethodLobatto&) noexcept = default;
-
-  PUGS_INLINE
-  constexpr IntegrationMethodLobatto(IntegrationMethodLobatto&& v) noexcept = default;
-
-  PUGS_INLINE
-  ~IntegrationMethodLobatto() noexcept = default;
-};
-
-template <size_t Order>
-class IntegrationMethodLegendre : public IntegrationMethod
-{
- private:
-  static constexpr size_t number_points = (Order < 4 ? 2 : (Order < 6 ? 3 : (Order < 8 ? 4 : 5)));
-  SmallArray<double> m_weights;
-  SmallArray<TinyVector<1>> m_quadrature_positions;
-  PUGS_INLINE SmallArray<TinyVector<1>>
-  translateQuadraturePoints(const double& a, const double& b)
-  {
-    SmallArray<TinyVector<1>> points{number_points};
-    double length = b - a;
-    for (size_t i = 0; i < m_quadrature_positions.size(); i++) {
-      TinyVector<1> qpos{m_quadrature_positions[i]};
-      points[i] = 0.5 * (length * qpos + a + b);
-    }
-    return points;
-  }
-
-  PUGS_INLINE
-  void
-  fillArrayLegendre()
-  {
-    GaussLegendreQuadrature<1> tensorial_gauss_legendre(Order);
-    copy_to(tensorial_gauss_legendre.pointList(), m_quadrature_positions);
-    copy_to(tensorial_gauss_legendre.weightList(), m_weights);
-    // switch (Order) {
-    // case 0:
-    // case 1:
-    // case 2:
-    // case 3: {
-    //   double oneovsqrt3         = 1. / std::sqrt(3);
-    //   m_quadrature_positions[0] = -oneovsqrt3;
-    //   m_quadrature_positions[1] = oneovsqrt3;
-    //   m_weights[0]              = 1;
-    //   m_weights[1]              = 1;
-    //   // m_quadrature_positions.fill({-1, 0, 1});
-    //   // m_weights.fill({oneov3, 4 * oneov3, oneov3});
-    //   break;
-    // }
-    // case 4:
-    // case 5: {
-    //   double oneover9           = 1. / 9;
-    //   double coef               = std::sqrt(3. / 5.);
-    //   m_quadrature_positions[0] = -coef;
-    //   m_quadrature_positions[1] = 0;
-    //   m_quadrature_positions[2] = coef;
-    //   m_weights[0]              = 5 * oneover9;
-    //   m_weights[1]              = 8 * oneover9;
-    //   m_weights[2]              = 5 * oneover9;
-    //   break;
-    // }
-    // case 6:
-    // case 7: {
-    //   double oneov36 = 1. / 36.;
-    //   double coef1   = std::sqrt(3. / 7. - 2. / 7. * std::sqrt(6. / 5.));
-    //   double coef2   = std::sqrt(3. / 7. + 2. / 7. * std::sqrt(6. / 5.));
-    //   double coef3   = 18. + std::sqrt(30.);
-    //   double coef4   = 18. - std::sqrt(30.);
-
-    //   m_quadrature_positions[0] = -coef2;
-    //   m_quadrature_positions[1] = -coef1;
-    //   m_quadrature_positions[2] = coef1;
-    //   m_quadrature_positions[3] = coef2;
-    //   m_weights[0]              = coef4 * oneov36;
-    //   m_weights[1]              = coef3 * oneov36;
-    //   m_weights[2]              = coef3 * oneov36;
-    //   m_weights[3]              = coef4 * oneov36;
-    //   break;
-    // }
-    // case 8:
-    // case 9: {
-    //   double oneov900           = 1. / 900.;
-    //   double oneov225           = 1. / 225.;
-    //   double coef1              = 1. / 3. * std::sqrt(5. - 2. * std::sqrt(10. / 7.));
-    //   double coef2              = 1. / 3. * std::sqrt(5. + 2. * std::sqrt(10. / 7.));
-    //   double coef3              = 322. + 13. * std::sqrt(70.);
-    //   double coef4              = 322. - 13. * std::sqrt(70.);
-    //   m_quadrature_positions[0] = -coef2;
-    //   m_quadrature_positions[1] = -coef1;
-    //   m_quadrature_positions[2] = 0;
-    //   m_quadrature_positions[3] = coef1;
-    //   m_quadrature_positions[4] = coef2;
-    //   m_weights[0]              = coef4 * oneov900;
-    //   m_weights[1]              = coef3 * oneov900;
-    //   m_weights[2]              = 128. * oneov225;
-    //   m_weights[3]              = coef3 * oneov900;
-    //   m_weights[4]              = coef4 * oneov900;
-    //   break;
-    // }
-    // default: {
-    //   throw UnexpectedError("Gauss-Legendre quadratures handle orders up to 9.");
-    //   break;
-    // }
-    // }
-  }
-
- public:
-  // One does not use the '=default' constructor to avoid
-  // (zero-initialization) performances issues
-  PUGS_INLINE
-  constexpr IntegrationMethodLegendre() noexcept : m_weights{number_points}, m_quadrature_positions{number_points}
-  {
-    m_weights.fill(0.);
-    m_quadrature_positions.fill(TinyVector<1, double>{0});
-    fillArrayLegendre();
-  }
-
-  PUGS_INLINE
-  SmallArray<TinyVector<1>>
-  quadraturePoints(const double& a, const double& b)
-  {
-    return translateQuadraturePoints(a, b);
-  }
-
-  PUGS_INLINE size_t
-  numberOfPoints()
-  {
-    return number_points;
-  }
-  PUGS_INLINE
-  SmallArray<double>
-  weights()
-  {
-    return m_weights;
-  }
-
-  PUGS_INLINE
-  constexpr IntegrationMethodLegendre(const IntegrationMethodLegendre&) noexcept = default;
-
-  PUGS_INLINE
-  constexpr IntegrationMethodLegendre(IntegrationMethodLegendre&& v) noexcept = default;
-
-  PUGS_INLINE
-  ~IntegrationMethodLegendre() noexcept = default;
-};
-
 template <size_t Order, typename T = double>
 class IntegrationTools
 {
  public:
   using data_type = T;
-
- private:
-  //  T m_values[N];
-  std::shared_ptr<IntegrationMethod> m_integration_method;
-  size_t N;
+  SmallArray<const double> m_weights;
+  SmallArray<const TinyVector<1>> m_quadrature_positions;
 
  public:
   PUGS_INLINE T
@@ -359,7 +36,7 @@ class IntegrationTools
   {
     T result                                  = 0;
     SmallArray<const TinyVector<1>> positions = quadraturePoints(a, b);
-    SmallArray<double> weights                = m_integration_method->weights();
+    SmallArray<const double> weights          = m_weights;
     SmallArray<T> values(positions.size());
     EvaluateAtPoints<double(const TinyVector<1>)>::evaluateTo(function, positions, values);
     Assert(positions.size() == weights.size(), "Wrong number of quadrature points and/or weights in Gauss quadrature");
@@ -375,7 +52,7 @@ class IntegrationTools
     SmallArray<const TinyVector<1>> positions = quadraturePoints(vertices);
     SmallArray<T> result{vertices.size() - 1};
     SmallArray<double> interval_size{vertices.size() - 1};
-    SmallArray<double> weights = m_integration_method->weights();
+    SmallArray<const double> weights = m_weights;
     SmallArray<T> values(positions.size());
     EvaluateAtPoints<double(const TinyVector<1>)>::evaluateTo(function, positions, values);
 
@@ -393,10 +70,10 @@ class IntegrationTools
   PUGS_INLINE T
   testIntegrate(const double& a, const double& b) const
   {
-    T result                            = 0;
-    SmallArray<TinyVector<1>> positions = quadraturePoints(a, b);
+    T result                                  = 0;
+    SmallArray<const TinyVector<1>> positions = quadraturePoints(a, b);
 
-    SmallArray<double> weights = m_integration_method->weights();
+    SmallArray<const double> weights = m_weights;
 
     SmallArray<T> values(weights.size());
     for (size_t j = 0; j < weights.size(); ++j) {
@@ -414,9 +91,9 @@ class IntegrationTools
   PUGS_INLINE SmallArray<T>
   testIntegrateFunction(const SmallArray<TinyVector<1>>& vertices) const
   {
-    SmallArray<TinyVector<1>> positions = quadraturePoints(vertices);
+    SmallArray<const TinyVector<1>> positions = quadraturePoints(vertices);
     SmallArray<double> interval_size{vertices.size() - 1};
-    SmallArray<double> weights = m_integration_method->weights();
+    SmallArray<const double> weights = this->weights();
     SmallArray<T> values(positions.size());
     for (size_t j = 0; j < positions.size(); ++j) {
       values[j] = std::pow(positions[j][0], 3);
@@ -441,10 +118,16 @@ class IntegrationTools
     return result;
   }
 
-  PUGS_INLINE SmallArray<TinyVector<1>>
+  PUGS_INLINE SmallArray<const TinyVector<1>>
   quadraturePoints(const double& a, const double& b) const
   {
-    return m_integration_method->quadraturePoints(a, b);
+    SmallArray<TinyVector<1>> points{m_quadrature_positions.size()};
+    double length = b - a;
+    for (size_t i = 0; i < m_quadrature_positions.size(); i++) {
+      TinyVector<1> qpos{m_quadrature_positions[i]};
+      points[i] = 0.5 * (length * qpos + a + b);
+    }
+    return points;
   }
 
   PUGS_INLINE
@@ -452,12 +135,12 @@ class IntegrationTools
   quadraturePoints(const SmallArray<TinyVector<1>>& positions) const
   {
     size_t number_of_intervals = positions.size() - 1;
-    size_t quadrature_size     = m_integration_method->numberOfPoints();
+    size_t quadrature_size     = m_quadrature_positions.size();
     SmallArray<TinyVector<1>> quadratures{quadrature_size * number_of_intervals};
     for (size_t j = 0; j < number_of_intervals; ++j) {
       double a                    = positions[j][0];
       double b                    = positions[j + 1][0];
-      auto intermediate_positions = m_integration_method->quadraturePoints(a, b);
+      auto intermediate_positions = this->quadraturePoints(a, b);
       for (size_t k = 0; k < quadrature_size; k++) {
         quadratures[j * quadrature_size + k] = intermediate_positions[k];
       }
@@ -465,10 +148,10 @@ class IntegrationTools
     return quadratures;
   }
 
-  PUGS_INLINE SmallArray<double>
+  PUGS_INLINE SmallArray<const double>
   weights() const
   {
-    return m_integration_method->weights();
+    return m_weights;
   }
 
   PUGS_INLINE
@@ -478,15 +161,20 @@ class IntegrationTools
     return Order;
   }
 
-  PUGS_INLINE constexpr IntegrationTools(QuadratureType quadrature)
+  PUGS_INLINE
+  IntegrationTools(QuadratureType quadrature)
   {
     switch (quadrature) {
     case QuadratureType::gausslobatto: {
-      m_integration_method = std::make_shared<IntegrationMethodLobatto<Order>>();
+      GaussLobattoQuadrature<1> tensorial_gauss_lobatto(Order);
+      m_quadrature_positions = tensorial_gauss_lobatto.pointList();
+      m_weights              = tensorial_gauss_lobatto.weightList();
       break;
     }
     case QuadratureType::gausslegendre: {
-      m_integration_method = std::make_shared<IntegrationMethodLegendre<Order>>();
+      GaussLegendreQuadrature<1> tensorial_gauss_legendre(Order);
+      m_quadrature_positions = tensorial_gauss_legendre.pointList();
+      m_weights              = tensorial_gauss_legendre.weightList();
       break;
     }
     case QuadratureType::QT__end: {
diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt
index b2e2f4c2f472639c54e013de1495a33a7433d021..03c39ec899321a01c4ec569a44bd3c3791e39589 100644
--- a/src/analysis/CMakeLists.txt
+++ b/src/analysis/CMakeLists.txt
@@ -2,4 +2,5 @@
 
 add_library(
   PugsAnalysis
-  GaussLegendreQuadrature.cpp)
+  GaussLegendreQuadrature.cpp
+  GaussLobattoQuadrature.cpp)
diff --git a/src/analysis/GaussLobattoQuadrature.cpp b/src/analysis/GaussLobattoQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..267b91c36c2df63bdb812d307777eef5f47ff9d2
--- /dev/null
+++ b/src/analysis/GaussLobattoQuadrature.cpp
@@ -0,0 +1,180 @@
+#include <analysis/GaussLobattoQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+template <>
+void
+GaussLobattoQuadrature<1>::_buildPointAndWeightLists()
+{
+  const size_t nb_points = m_order / 2 + 2;
+
+  SmallArray<TinyVector<1>> point_list(nb_points);
+  SmallArray<double> weight_list(nb_points);
+
+  switch (m_order) {
+  case 0:
+  case 1: {
+    point_list[0] = -1;
+    point_list[1] = +1;
+
+    weight_list[0] = 1;
+    weight_list[1] = 1;
+    break;
+  }
+  case 2:
+  case 3: {
+    point_list[0] = -1;
+    point_list[1] = 0;
+    point_list[2] = +1;
+
+    weight_list[0] = 0.3333333333333333333333333;
+    weight_list[1] = 1.3333333333333333333333333;
+    weight_list[2] = 0.3333333333333333333333333;
+    break;
+  }
+  case 4:
+  case 5: {
+    point_list[0] = -1;
+    point_list[1] = -0.4472135954999579392818347;
+    point_list[2] = +0.4472135954999579392818347;
+    point_list[3] = +1;
+
+    weight_list[0] = 0.1666666666666666666666667;
+    weight_list[1] = 0.8333333333333333333333333;
+    weight_list[2] = 0.8333333333333333333333333;
+    weight_list[3] = 0.1666666666666666666666667;
+    break;
+  }
+  case 6:
+  case 7: {
+    point_list[0] = -1;
+    point_list[1] = -0.6546536707079771437982925;
+    point_list[2] = 0;
+    point_list[3] = +0.6546536707079771437982925;
+    point_list[4] = +1;
+
+    weight_list[0] = 0.1;
+    weight_list[1] = 0.5444444444444444444444444;
+    weight_list[2] = 0.7111111111111111111111111;
+    weight_list[3] = 0.5444444444444444444444444;
+    weight_list[4] = 0.1;
+    break;
+  }
+  case 8:
+  case 9: {
+    point_list[0] = -1;
+    point_list[1] = -0.7650553239294646928510030;
+    point_list[2] = -0.2852315164806450963141510;
+    point_list[3] = +0.2852315164806450963141510;
+    point_list[4] = +0.7650553239294646928510030;
+    point_list[5] = +1;
+
+    weight_list[0] = 0.0666666666666666666666667;
+    weight_list[1] = 0.3784749562978469803166128;
+    weight_list[2] = 0.5548583770354863530167205;
+    weight_list[3] = 0.5548583770354863530167205;
+    weight_list[4] = 0.3784749562978469803166128;
+    weight_list[5] = 0.0666666666666666666666667;
+    break;
+  }
+  case 10:
+  case 11: {
+    point_list[0] = -1;
+    point_list[1] = -0.8302238962785669298720322;
+    point_list[2] = -0.4688487934707142138037719;
+    point_list[3] = 0;
+    point_list[4] = +0.4688487934707142138037719;
+    point_list[5] = +0.8302238962785669298720322;
+    point_list[6] = +1;
+
+    weight_list[0] = 0.0476190476190476190476190;
+    weight_list[1] = 0.2768260473615659480107004;
+    weight_list[2] = 0.4317453812098626234178710;
+    weight_list[3] = 0.4876190476190476190476190;
+    weight_list[4] = 0.4317453812098626234178710;
+    weight_list[5] = 0.2768260473615659480107004;
+    weight_list[6] = 0.0476190476190476190476190;
+    break;
+  }
+  case 12:
+  case 13: {
+    point_list[0] = -1;
+    point_list[1] = -0.8717401485096066153374457;
+    point_list[2] = -0.5917001814331423021445107;
+    point_list[3] = -0.2092992179024788687686573;
+    point_list[4] = +0.2092992179024788687686573;
+    point_list[5] = +0.5917001814331423021445107;
+    point_list[6] = +0.8717401485096066153374457;
+    point_list[7] = +1;
+
+    weight_list[0] = 0.0357142857142857142857143;
+    weight_list[1] = 0.2107042271435060393829921;
+    weight_list[2] = 0.3411226924835043647642407;
+    weight_list[3] = 0.4124587946587038815670530;
+    weight_list[4] = 0.4124587946587038815670530;
+    weight_list[5] = 0.3411226924835043647642407;
+    weight_list[6] = 0.2107042271435060393829921;
+    weight_list[7] = 0.0357142857142857142857143;
+    break;
+  }
+  default: {
+    throw NormalError("Gauss-Lobatto quadrature formulae handle orders up to 13");
+  }
+  }
+
+  m_point_list  = point_list;
+  m_weight_list = weight_list;
+}
+
+template <>
+void
+GaussLobattoQuadrature<2>::_buildPointAndWeightLists()
+{
+  const size_t nb_points_1d = this->m_order / 2 + 2;
+  const size_t nb_points    = nb_points_1d * nb_points_1d;
+
+  SmallArray<TinyVector<2>> point_list(nb_points);
+  SmallArray<double> weight_list(nb_points);
+
+  GaussLobattoQuadrature<1> gauss_lobatto_1d(m_order);
+  const auto& point_list_1d  = gauss_lobatto_1d.pointList();
+  const auto& weight_list_1d = gauss_lobatto_1d.weightList();
+
+  size_t l = 0;
+  for (size_t i = 0; i < nb_points_1d; ++i) {
+    for (size_t j = 0; j < nb_points_1d; ++j, ++l) {
+      point_list[l]  = TinyVector<2>{point_list_1d[i][0], point_list_1d[j][0]};
+      weight_list[l] = weight_list_1d[i] * weight_list_1d[j];
+    }
+  }
+
+  this->m_point_list  = point_list;
+  this->m_weight_list = weight_list;
+}
+
+template <>
+void
+GaussLobattoQuadrature<3>::_buildPointAndWeightLists()
+{
+  const size_t nb_points_1d = this->m_order / 2 + 2;
+  const size_t nb_points    = nb_points_1d * nb_points_1d * nb_points_1d;
+
+  SmallArray<TinyVector<3>> point_list(nb_points);
+  SmallArray<double> weight_list(nb_points);
+
+  GaussLobattoQuadrature<1> gauss_lobatto_1d(m_order);
+  const auto& point_list_1d  = gauss_lobatto_1d.pointList();
+  const auto& weight_list_1d = gauss_lobatto_1d.weightList();
+
+  size_t l = 0;
+  for (size_t i = 0; i < nb_points_1d; ++i) {
+    for (size_t j = 0; j < nb_points_1d; ++j) {
+      for (size_t k = 0; k < nb_points_1d; ++k, ++l) {
+        point_list[l]  = TinyVector<3>{point_list_1d[i][0], point_list_1d[j][0], point_list_1d[k][0]};
+        weight_list[l] = weight_list_1d[i] * weight_list_1d[j] * weight_list_1d[k];
+      }
+    }
+  }
+
+  this->m_point_list  = point_list;
+  this->m_weight_list = weight_list;
+}
diff --git a/src/analysis/GaussLobattoQuadrature.hpp b/src/analysis/GaussLobattoQuadrature.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..89dbb2e8b11da89bc3c326fd8a39e7a3412c2e4f
--- /dev/null
+++ b/src/analysis/GaussLobattoQuadrature.hpp
@@ -0,0 +1,25 @@
+#ifndef GAUSS_LOBATTO_QUADRATURE_HPP
+#define GAUSS_LOBATTO_QUADRATURE_HPP
+
+#include <analysis/QuadratureFormula.hpp>
+
+template <size_t Dimension>
+class GaussLobattoQuadrature final : public QuadratureForumla<Dimension>
+{
+ private:
+  void _buildPointAndWeightLists();
+
+ public:
+  GaussLobattoQuadrature(GaussLobattoQuadrature&&)      = default;
+  GaussLobattoQuadrature(const GaussLobattoQuadrature&) = default;
+
+  explicit GaussLobattoQuadrature(const size_t order) : QuadratureForumla<Dimension>(order)
+  {
+    this->_buildPointAndWeightLists();
+  }
+
+  GaussLobattoQuadrature()  = delete;
+  ~GaussLobattoQuadrature() = default;
+};
+
+#endif   // GAUSS_LOBATTO_QUADRATURE_HPP
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index b0b2b73281d3c7473cdd0636bd2c1490fd21329c..b5d57ecad09204171ec829e250edecf565740e48 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -81,6 +81,7 @@ add_executable (unit_tests
   test_FunctionSymbolId.cpp
   test_FunctionTable.cpp
   test_GaussLegendreQuadrature.cpp
+  test_GaussLobattoQuadrature.cpp
   test_IfProcessor.cpp
   test_IncDecExpressionProcessor.cpp
   test_INodeProcessor.cpp
diff --git a/tests/test_FunctionEvaluation.cpp b/tests/test_FunctionEvaluation.cpp
index 2e290556eac3a8d46d76cd9fd114cfa025e6ca2f..9a8839f5aada5171b4071d7cd787c1ddece8118c 100644
--- a/tests/test_FunctionEvaluation.cpp
+++ b/tests/test_FunctionEvaluation.cpp
@@ -30,14 +30,14 @@ TEST_CASE("IntegrationTools", "[integration]")
   }
   SECTION("QuadraturePoints")
   {
-    SmallArray<TinyVector<1>> quadratures = integrationtools.quadraturePoints(a, b);
+    SmallArray<const TinyVector<1>> quadratures = integrationtools.quadraturePoints(a, b);
     REQUIRE(quadratures[0] == 1);
     REQUIRE(quadratures[1] == 1.5);
     REQUIRE(quadratures[2] == 2);
   }
   SECTION("Weights")
   {
-    SmallArray<double> weights = integrationtools.weights();
+    SmallArray<const double> weights = integrationtools.weights();
     REQUIRE(((weights[0] == 1. / 3.) and (weights[1] == 4. / 3.) and (weights[2] == 1. / 3.)));
   }
   SECTION("TestOrder3")
diff --git a/tests/test_GaussLobattoQuadrature.cpp b/tests/test_GaussLobattoQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d60a0653b240b1c45077580d74a2f56e85b8da4a
--- /dev/null
+++ b/tests/test_GaussLobattoQuadrature.cpp
@@ -0,0 +1,409 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/GaussLobattoQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("GaussLobattoQuadrature", "[analysis]")
+{
+  auto same_formulas = [](auto quadrature_formula0, auto quadrature_formula1) {
+    auto point_list0  = quadrature_formula0.pointList();
+    auto weight_list0 = quadrature_formula0.weightList();
+
+    auto point_list1  = quadrature_formula1.pointList();
+    auto weight_list1 = quadrature_formula1.weightList();
+
+    bool is_same = true;
+
+    for (size_t i = 0; i < point_list0.size(); ++i) {
+      if (point_list0[i] != point_list1[i]) {
+        return false;
+      }
+    }
+    for (size_t i = 0; i < weight_list0.size(); ++i) {
+      if (weight_list0[i] != weight_list1[i]) {
+        return false;
+      }
+    }
+
+    return is_same;
+  };
+
+  SECTION("1D")
+  {
+    auto is_symmetric_formula = [](auto quadrature_formula) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      bool is_symmetric = true;
+
+      const size_t middle_index = point_list.size() / 2;
+      for (size_t i = 0; i <= middle_index; ++i) {
+        if (point_list[i] != -point_list[point_list.size() - 1 - i]) {
+          return false;
+        }
+      }
+      for (size_t i = 0; i <= middle_index; ++i) {
+        if (weight_list[i] != weight_list[point_list.size() - 1 - i]) {
+          return false;
+        }
+      }
+
+      return is_symmetric;
+    };
+
+    auto integrate = [](auto f, auto quadrature_formula, const double a, const double b) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      double alpha = 0.5 * (b - a);
+      double beta  = 0.5 * (a + b);
+
+      auto x = [&alpha, &beta](auto x_hat) { return alpha * x_hat + beta; };
+
+      auto value = weight_list[0] * f(x(point_list[0]));
+      for (size_t i = 1; i < weight_list.size(); ++i) {
+        value += weight_list[i] * f(x(point_list[i]));
+      }
+
+      return alpha * value;
+    };
+
+    auto get_order = [&integrate](auto f, auto quadrature_formula, const double a, const double b,
+                                  const double exact_value) {
+      double int_ab          = integrate(f, quadrature_formula, a, b);
+      double int_first_half  = integrate(f, quadrature_formula, a, 0.5 * (a + b));
+      double int_second_half = integrate(f, quadrature_formula, 0.5 * (a + b), b);
+
+      return -std::log((int_first_half + int_second_half - exact_value) / (int_ab - exact_value)) / std::log(2);
+    };
+
+    auto p0 = [](const TinyVector<1>&) { return 1; };
+    auto p1 = [&p0](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 2 * x + p0(X);
+    };
+    auto p2 = [&p1](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 3 * x * x + p1(X);
+    };
+    auto p3 = [&p2](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 4 * std::pow(x, 3) + p2(X);
+    };
+    auto p4 = [&p3](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 5 * std::pow(x, 4) + p3(X);
+    };
+    auto p5 = [&p4](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 6 * std::pow(x, 5) + p4(X);
+    };
+    auto p6 = [&p5](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 7 * std::pow(x, 6) + p5(X);
+    };
+    auto p7 = [&p6](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 8 * std::pow(x, 7) + p6(X);
+    };
+    auto p8 = [&p7](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 9 * std::pow(x, 8) + p7(X);
+    };
+    auto p9 = [&p8](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 10 * std::pow(x, 9) + p8(X);
+    };
+    auto p10 = [&p9](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 11 * std::pow(x, 10) + p9(X);
+    };
+    auto p11 = [&p10](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 12 * std::pow(x, 11) + p10(X);
+    };
+    auto p12 = [&p11](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 13 * std::pow(x, 12) + p11(X);
+    };
+    auto p13 = [&p12](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 14 * std::pow(x, 13) + p12(X);
+    };
+    auto p14 = [&p13](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 15 * std::pow(x, 14) + p13(X);
+    };
+
+    SECTION("order 0 and 1")
+    {
+      GaussLobattoQuadrature<1> l0(0);
+      GaussLobattoQuadrature<1> l1(1);
+
+      REQUIRE(same_formulas(l0, l1));
+      REQUIRE(is_symmetric_formula(l1));
+
+      REQUIRE(l1.numberOfPoints() == 2);
+
+      REQUIRE(integrate(p0, l1, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l1, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l1, 0, 1) != Catch::Approx(3));
+
+      REQUIRE(get_order(p2, l1, -1, 1, 4) == Catch::Approx(2));
+    }
+
+    SECTION("order 2 and 3")
+    {
+      GaussLobattoQuadrature<1> l2(2);
+      GaussLobattoQuadrature<1> l3(3);
+
+      REQUIRE(same_formulas(l2, l3));
+      REQUIRE(is_symmetric_formula(l3));
+
+      REQUIRE(l3.numberOfPoints() == 3);
+
+      REQUIRE(integrate(p0, l3, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l3, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l3, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l3, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l3, 0, 1) != Catch::Approx(5));
+
+      REQUIRE(get_order(p4, l3, -1, 1, 6) == Catch::Approx(4));
+    }
+
+    SECTION("order 4 and 5")
+    {
+      GaussLobattoQuadrature<1> l4(4);
+      GaussLobattoQuadrature<1> l5(5);
+
+      REQUIRE(same_formulas(l4, l5));
+      REQUIRE(is_symmetric_formula(l5));
+
+      REQUIRE(l5.numberOfPoints() == 4);
+
+      REQUIRE(integrate(p0, l5, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l5, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l5, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l5, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l5, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l5, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l5, 0, 1) != Catch::Approx(7));
+
+      REQUIRE(get_order(p6, l5, -1, 1, 8) == Catch::Approx(6));
+    }
+
+    SECTION("order 6 and 7")
+    {
+      GaussLobattoQuadrature<1> l6(6);
+      GaussLobattoQuadrature<1> l7(7);
+
+      REQUIRE(same_formulas(l6, l7));
+      REQUIRE(is_symmetric_formula(l7));
+
+      REQUIRE(l7.numberOfPoints() == 5);
+
+      REQUIRE(integrate(p0, l7, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l7, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l7, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l7, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l7, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l7, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l7, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l7, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l7, 0, 1) != Catch::Approx(9));
+
+      REQUIRE(get_order(p8, l7, -1, 1, 10) == Catch::Approx(8));
+    }
+
+    SECTION("order 8 and 9")
+    {
+      GaussLobattoQuadrature<1> l8(8);
+      GaussLobattoQuadrature<1> l9(9);
+
+      REQUIRE(same_formulas(l8, l9));
+      REQUIRE(is_symmetric_formula(l9));
+
+      REQUIRE(l9.numberOfPoints() == 6);
+
+      REQUIRE(integrate(p0, l9, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l9, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l9, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l9, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l9, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l9, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l9, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l9, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l9, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l9, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l9, 0, 1) != Catch::Approx(11).epsilon(1E-13));
+
+      REQUIRE(get_order(p10, l9, -1, 1, 12) == Catch::Approx(10));
+    }
+
+    SECTION("order 10 and 11")
+    {
+      GaussLobattoQuadrature<1> l10(10);
+      GaussLobattoQuadrature<1> l11(11);
+
+      REQUIRE(same_formulas(l10, l11));
+      REQUIRE(is_symmetric_formula(l11));
+
+      REQUIRE(l11.numberOfPoints() == 7);
+
+      REQUIRE(integrate(p0, l11, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l11, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l11, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l11, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l11, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l11, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l11, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l11, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l11, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l11, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l11, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l11, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l11, 0, 1) != Catch::Approx(13).epsilon(1E-13));
+
+      REQUIRE(get_order(p12, l11, -1, 1, 14) == Catch::Approx(12));
+    }
+
+    SECTION("order 12 and 13")
+    {
+      GaussLobattoQuadrature<1> l12(12);
+      GaussLobattoQuadrature<1> l13(13);
+
+      REQUIRE(same_formulas(l12, l13));
+      REQUIRE(is_symmetric_formula(l13));
+
+      REQUIRE(l13.numberOfPoints() == 8);
+
+      REQUIRE(integrate(p0, l13, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l13, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l13, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l13, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l13, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l13, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l13, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l13, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l13, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l13, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l13, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l13, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l13, 0, 1) == Catch::Approx(13));
+      REQUIRE(integrate(p13, l13, 0, 1) == Catch::Approx(14));
+      REQUIRE(integrate(p14, l13, 0, 1) != Catch::Approx(15).epsilon(1E-13));
+
+      REQUIRE(get_order(p14, l13, -1, 1, 16) == Catch::Approx(14));
+    }
+
+    SECTION("not implemented formulae")
+    {
+      REQUIRE_THROWS_WITH(GaussLobattoQuadrature<1>(14),
+                          "error: Gauss-Lobatto quadrature formulae handle orders up to 13");
+    }
+  }
+
+  SECTION("2D")
+  {
+    auto integrate = [](auto f, auto quadrature_formula, const double xa, const double xb, const double ya,
+                        const double yb) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      const double alphax = 0.5 * (xb - xa);
+      const double betax  = 0.5 * (xa + xb);
+
+      const double alphay = 0.5 * (yb - ya);
+      const double betay  = 0.5 * (ya + yb);
+
+      auto x = [&alphax, &betax, &alphay, &betay](auto x_hat) {
+        return TinyVector<2>{alphax * x_hat[0] + betax, alphay * x_hat[1] + betay};
+      };
+
+      auto value = weight_list[0] * f(x(point_list[0]));
+      for (size_t i = 1; i < weight_list.size(); ++i) {
+        value += weight_list[i] * f(x(point_list[i]));
+      }
+
+      return alphax * alphay * value;
+    };
+
+    auto p6 = [](const double x) { return 7 * std::pow(x, 6); };
+    auto p7 = [](const double x) { return 8 * std::pow(x, 7); };
+
+    auto px7y6 = [&p6, &p7](const TinyVector<2>& X) {
+      const double x = X[0];
+      const double y = X[1];
+      return p7(x) * p6(y);
+    };
+
+    SECTION("order 6 and 7")
+    {
+      GaussLobattoQuadrature<2> l6(6);
+      GaussLobattoQuadrature<2> l7(7);
+
+      REQUIRE(same_formulas(l6, l7));
+
+      REQUIRE(l7.numberOfPoints() == 5 * 5);
+
+      REQUIRE(integrate(px7y6, l7, 0, 1, 0.2, 0.8) == Catch::Approx(std::pow(0.8, 7) - std::pow(0.2, 7)));
+    }
+  }
+
+  SECTION("3D")
+  {
+    auto integrate = [](auto f, auto quadrature_formula, const double xa, const double xb, const double ya,
+                        const double yb, const double za, const double zb) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      const double alphax = 0.5 * (xb - xa);
+      const double betax  = 0.5 * (xa + xb);
+
+      const double alphay = 0.5 * (yb - ya);
+      const double betay  = 0.5 * (ya + yb);
+
+      const double alphaz = 0.5 * (zb - za);
+      const double betaz  = 0.5 * (za + zb);
+
+      auto x = [&alphax, &betax, &alphay, &betay, &alphaz, &betaz](auto x_hat) {
+        return TinyVector<3>{alphax * x_hat[0] + betax, alphay * x_hat[1] + betay, alphaz * x_hat[2] + betaz};
+      };
+
+      auto value = weight_list[0] * f(x(point_list[0]));
+      for (size_t i = 1; i < weight_list.size(); ++i) {
+        value += weight_list[i] * f(x(point_list[i]));
+      }
+
+      return alphax * alphay * alphaz * value;
+    };
+
+    auto p6 = [](const double x) { return 7 * std::pow(x, 6); };
+    auto p7 = [](const double x) { return 8 * std::pow(x, 7); };
+
+    auto px7y6 = [&p6, &p7](const TinyVector<3>& X) {
+      const double x = X[0];
+      const double y = X[1];
+      const double z = X[2];
+      return p7(x) * p6(y) + p7(z);
+    };
+
+    SECTION("order 6 and 7")
+    {
+      GaussLobattoQuadrature<3> l6(6);
+      GaussLobattoQuadrature<3> l7(7);
+
+      REQUIRE(same_formulas(l6, l7));
+
+      REQUIRE(l7.numberOfPoints() == 5 * 5 * 5);
+
+      REQUIRE(integrate(px7y6, l7, 0, 1, 0.2, 0.8, -0.1, 0.7) ==
+              Catch::Approx((std::pow(0.8, 7) - std::pow(0.2, 7)) * (0.7 - -0.1) +
+                            (0.8 - 0.2) * (std::pow(0.7, 8) - std::pow(-0.1, 8))));
+    }
+  }
+}