diff --git a/dune/matrix-vector/CMakeLists.txt b/dune/matrix-vector/CMakeLists.txt
index 07a1c2cbffa0a361f3f64fcac212a81a3a0d2b2e..9acfb4a5f0afce3f98809d63523037308223a809 100644
--- a/dune/matrix-vector/CMakeLists.txt
+++ b/dune/matrix-vector/CMakeLists.txt
@@ -16,4 +16,5 @@ install(FILES
   singlenonzerorowmatrix.hh
   tranpose.hh
   transformmatrix.hh
+  triangularsolve.hh
   DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/matrix-vector)
\ No newline at end of file
diff --git a/dune/matrix-vector/triangularsolve.hh b/dune/matrix-vector/triangularsolve.hh
new file mode 100644
index 0000000000000000000000000000000000000000..4d7c42e8a42969ddfb86e9e82ab6928c87473a86
--- /dev/null
+++ b/dune/matrix-vector/triangularsolve.hh
@@ -0,0 +1,133 @@
+#ifndef DUNE_MATRIX_VECTOR_TRIANGULARSOLVE_HH
+#define DUNE_MATRIX_VECTOR_TRIANGULARSOLVE_HH
+
+namespace Dune {
+namespace MatrixVector {
+
+  /**
+   * \brief Solves L*x=b where L is a lower triangular matrix.
+   * \param x Solution vector. Make sure the ignored nodes are set
+   *        correctly because their respective values for x as passed to
+   *        this method may affect other lines.
+   * \param ignore Tags the lines of the system where x shall not be
+   *        computed. Note that ignored nodes might still influence the
+   *        solution of other lines if not set to zero.
+   * \param transpose Set true if L is passed as an upper triangular matrix.
+   */
+  // Note: The current assert makes sure we actually deal with triangular
+  //       matrices. It might be favorable to support matrices as well
+  //       where other entries are not required to be non-existent, e.g.
+  //       if one has a in-place decomposition and does not want to
+  //       reimplement the iterators.
+  template <class Matrix, class Vector, class BitVector>
+  static void lowerTriangularSolve(Matrix const& L, Vector b, Vector& x,
+                                   BitVector const* ignore,
+                                   bool transpose = false) {
+    if (transpose) {
+      for (auto it = L.begin(); it != L.end(); ++it) {
+        const size_t i = it.index();
+        auto cIt = it->begin();
+        assert(cIt.index() == i);
+        if (ignore == nullptr or (*ignore)[cIt.index()].none())
+          x[i] = b[i] / *cIt;
+        cIt++;
+        for (; cIt != it->end(); ++cIt) {
+          const size_t j = cIt.index();
+          assert(j > i);
+          // Note: We could drop the check for ignore nodes here bcs. b[j] will
+          // be ignored anyway due to the check above.
+          if (ignore == nullptr or (*ignore)[j].none())
+            b[j] -= x[i] * *cIt;
+        }
+      }
+    } else {
+      for (auto it = L.begin(); it != L.end(); ++it) {
+        const size_t i = it.index();
+        if (ignore != nullptr and (*ignore)[i].all())
+          continue;
+        for (auto cIt = it->begin(); cIt != it->end(); ++cIt) {
+          const size_t j = cIt.index();
+          assert(j <= i);
+          if (j < i) {
+            b[i] -= *cIt * x[j];
+            continue;
+          }
+          assert(j == i);
+          x[i] = b[i] / *cIt;
+        }
+      }
+    }
+  }
+
+  /**
+   * \brief Same as lowerTriangularSolve. Ignored nodes are set to 0.
+   */
+  template <class Matrix, class Vector, class BitVector>
+  static Vector lowerTriangularSolve(Matrix const& L, Vector b,
+                                     BitVector const* ignore,
+                                     bool transpose = false) {
+    Vector x = b;
+    x = 0;
+    lowerTriangularSolve(L, std::move(b), x, ignore, transpose);
+    return x;
+  }
+
+  /**
+   * \brief Solves U*x=b where U is an upper triangular matrix.
+   *        See @lowerTriangularSolve for details.
+   */
+  template <class Matrix, class Vector, class BitVector>
+  static void upperTriangularSolve(Matrix const& U, Vector b, Vector& x,
+                                   BitVector const* ignore,
+                                   bool transpose = false) {
+    if (transpose) {
+      for (auto it = U.beforeEnd(); it != U.beforeBegin(); --it) {
+        const size_t i = it.index();
+        auto cIt = it->beforeEnd();
+        assert(cIt.index() == i);
+        if (ignore == nullptr or (*ignore)[cIt.index()].none())
+          x[i] = b[i] / *cIt;
+        cIt--;
+        for (; cIt != it->beforeBegin(); --cIt) {
+          const size_t j = cIt.index();
+          assert(j < i);
+          // Note: We could drop the check for ignore nodes here bcs. b[j] will
+          // be ignored anyway due to the check above.
+          if (ignore == nullptr or (*ignore)[j].none())
+            b[j] -= *cIt * x[i];
+        }
+      }
+    } else {
+      for (auto it = U.beforeEnd(); it != U.beforeBegin(); --it) {
+        const size_t i = it.index();
+        if (ignore != nullptr and (*ignore)[i].all())
+          continue;
+        auto cIt = it->beforeEnd();
+        for (; cIt != it->begin(); --cIt) {
+          const size_t j = cIt.index();
+          assert(j > i);
+          b[i] -= *cIt * x[j];
+        }
+        assert(cIt.index() == i);
+        x[i] = b[i] / *cIt;
+      }
+    }
+  }
+
+  /**
+   * \brief Same as upperTriangularSolve. Ignored nodes are set to 0.
+   */
+  template <class Matrix, class Vector, class BitVector>
+  static Vector upperTriangularSolve(Matrix const& U, Vector b,
+                                     BitVector const* ignore,
+                                     bool transpose = false) {
+    Vector x = b;
+    x = 0;
+    upperTriangularSolve(U, std::move(b), x, ignore, transpose);
+    return x;
+  }
+
+} // end namespace MatrixVector
+} // end namespace Dune
+
+#endif // DUNE_MATRIX_VECTOR_TRIANGULARSOLVE_HH