diff --git a/README.md b/README.md
index 14584344979a0456c667555791e3b5904fb22721..b8d84c7be5fbf2e0659a0142cab4cb0c15f458de 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,7 @@ The project uses [CMake](https://cmake.org) as its build system generator. The f
 - [HDF5](https://www.hdfgroup.org/solutions/hdf5/): version 1.10
 - [MPI](https://cmake.org/cmake/help/latest/module/FindMPI.html): version 3.1
 - [PETSc](https://www.mcs.anl.gov/petsc/): version 3.12
+- [Range-v3](https://github.com/ericniebler/range-v3): version 0.10
 
 Of course, these libraries are covered by their own license terms. Since PETSc does not provide a CMake configuration file, PETSc is found using the provided find module in ```cmake/modules/```, which in turn is based on ```pkg-config```.
 
diff --git a/assembly/src/include/ae108/assembly/Assembler.h b/assembly/src/include/ae108/assembly/Assembler.h
index 87cea4d6f0de2632746f0dafd23d54c79419476f..ebaf5885cd183af7a424c267d8a59ada4b636cd2 100644
--- a/assembly/src/include/ae108/assembly/Assembler.h
+++ b/assembly/src/include/ae108/assembly/Assembler.h
@@ -19,7 +19,7 @@
 #include "ae108/assembly/DefaultFeaturePlugins.h"
 #include "ae108/assembly/FeaturePlugin.h"
 #include "ae108/cpppetsc/IteratorRange.h"
-#include "ae108/cpppetsc/LocalElementIterator.h"
+#include "ae108/cpppetsc/LocalElementView.h"
 #include "ae108/cpppetsc/Mesh_fwd.h"
 #include "ae108/cpppetsc/SequentialComputePolicy.h"
 #include <deque>
@@ -68,8 +68,7 @@ public:
    */
   template <class... Args> explicit Assembler(Args &&... args);
 
-  using ElementView =
-      typename cpppetsc::LocalElementIterator<mesh_type>::value_type;
+  using ElementView = typename cpppetsc::LocalElementView<mesh_type>;
 
   /**
    * @brief Emplace (i.e. construct directly into the internal container without
diff --git a/cpppetsc/src/CMakeLists.txt b/cpppetsc/src/CMakeLists.txt
index 950115511bf8e0af7ed1b9a3c8514102024f3e92..f992a5ce3a28eddd62a4bf6600ffc4d2dd9ee5a7 100644
--- a/cpppetsc/src/CMakeLists.txt
+++ b/cpppetsc/src/CMakeLists.txt
@@ -24,8 +24,6 @@ add_library(${PROJECT_NAME}-cpppetsc
             ./IteratorRange.cc
             ./LinearSolver.cc
             ./LinearSolverDivergedException.cc
-            ./LocalElementIterator.cc
-            ./LocalVertexIterator.cc
             ./LocalVertexView.cc
             ./Matrix.cc
             ./Matrix_fwd.cc
@@ -71,8 +69,10 @@ target_include_directories(${PROJECT_NAME}-cpppetsc PUBLIC
 )
 
 find_package(AE108_PETSc 3.12 MODULE REQUIRED)
+find_package(range-v3 0.10.0 CONFIG REQUIRED)
 target_link_libraries(${PROJECT_NAME}-cpppetsc
                       PUBLIC ${PROJECT_NAME}::external::petsc
+                      PUBLIC range-v3::range-v3
 )
 
 target_compile_features(${PROJECT_NAME}-cpppetsc PUBLIC cxx_std_11)
diff --git a/cpppetsc/src/LocalElementIterator.cc b/cpppetsc/src/LocalElementIterator.cc
deleted file mode 100644
index a79ed7ed6ab5035ad3572f86d36de032efbfb3cf..0000000000000000000000000000000000000000
--- a/cpppetsc/src/LocalElementIterator.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-// © 2020 ETH Zurich, Mechanics and Materials Lab
-// © 2020 California Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "ae108/cpppetsc/LocalElementIterator.h"
diff --git a/cpppetsc/src/LocalVertexIterator.cc b/cpppetsc/src/LocalVertexIterator.cc
deleted file mode 100644
index f5d93cab6d4367bfa2e1d5e08a36a2d968b345fe..0000000000000000000000000000000000000000
--- a/cpppetsc/src/LocalVertexIterator.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-// © 2020 ETH Zurich, Mechanics and Materials Lab
-// © 2020 California Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "ae108/cpppetsc/LocalVertexIterator.h"
diff --git a/cpppetsc/src/include/ae108/cpppetsc/GeneralizedMeshBoundaryCondition.h b/cpppetsc/src/include/ae108/cpppetsc/GeneralizedMeshBoundaryCondition.h
index 9d2e1a80ebd1958f2dab57396ba4b6c502754818..7a1dbe2496f58c8054009c163b9483df102ed09e 100644
--- a/cpppetsc/src/include/ae108/cpppetsc/GeneralizedMeshBoundaryCondition.h
+++ b/cpppetsc/src/include/ae108/cpppetsc/GeneralizedMeshBoundaryCondition.h
@@ -15,7 +15,7 @@
 
 #pragma once
 
-#include "ae108/cpppetsc/LocalVertexIterator.h"
+#include <vector>
 
 namespace ae108 {
 namespace cpppetsc {
diff --git a/cpppetsc/src/include/ae108/cpppetsc/LocalElementIterator.h b/cpppetsc/src/include/ae108/cpppetsc/LocalElementIterator.h
deleted file mode 100644
index 9730ff276eda4b0f27ff30158ffdee32dda65ae9..0000000000000000000000000000000000000000
--- a/cpppetsc/src/include/ae108/cpppetsc/LocalElementIterator.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// © 2020 ETH Zurich, Mechanics and Materials Lab
-// © 2020 California Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include "ae108/cpppetsc/LocalElementView.h"
-#include "ae108/cpppetsc/LocalEntityIterator.h"
-
-namespace ae108 {
-namespace cpppetsc {
-
-/**
- * @brief A bidirectional iterator to traverse the local elements of a mesh.
- * Dereferencing the iterator yields a view of a local element. See
- * LocalElementView.h for a list of the methods that it supports.
- *
- * @remark Changes to the mesh invalidate the iterator.
- */
-template <class MeshType>
-using LocalElementIterator = LocalEntityIterator<MeshType, LocalElementView>;
-
-} // namespace cpppetsc
-} // namespace ae108
\ No newline at end of file
diff --git a/cpppetsc/src/include/ae108/cpppetsc/LocalElementView.h b/cpppetsc/src/include/ae108/cpppetsc/LocalElementView.h
index f21c6b054702521987d8d729dd5f6e4f76ad352e..e548592ad7c87e8925fdd5fcefd230551c09f44a 100644
--- a/cpppetsc/src/include/ae108/cpppetsc/LocalElementView.h
+++ b/cpppetsc/src/include/ae108/cpppetsc/LocalElementView.h
@@ -21,11 +21,9 @@
 namespace ae108 {
 namespace cpppetsc {
 
-template <class IteratorType> class LocalElementView {
-  friend IteratorType;
-
+template <class MeshType_> class LocalElementView {
 public:
-  using mesh_type = typename IteratorType::mesh_type;
+  using mesh_type = MeshType_;
 
   using size_type = typename mesh_type::size_type;
   using value_type = typename mesh_type::value_type;
diff --git a/cpppetsc/src/include/ae108/cpppetsc/LocalEntityIterator.h b/cpppetsc/src/include/ae108/cpppetsc/LocalEntityIterator.h
deleted file mode 100644
index d6f8c8e9dbd867e8f27396025240912f6a686e43..0000000000000000000000000000000000000000
--- a/cpppetsc/src/include/ae108/cpppetsc/LocalEntityIterator.h
+++ /dev/null
@@ -1,131 +0,0 @@
-// © 2020 ETH Zurich, Mechanics and Materials Lab
-// © 2020 California Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <iterator>
-#include <vector>
-
-namespace ae108 {
-namespace cpppetsc {
-
-/**
- * @brief A bidirectional iterator to traverse the local entities of a mesh.
- * Dereferencing the iterator yields a view of a local entity.
- *
- * @remark Changes to the mesh invalidate the iterator.
- */
-template <class MeshType, template <class> class ViewTemplate>
-class LocalEntityIterator {
-public:
-  using mesh_type = MeshType;
-
-  using View = ViewTemplate<LocalEntityIterator>;
-
-  using value_type = View;
-  using iterator_category = std::bidirectional_iterator_tag;
-  using difference_type = typename mesh_type::size_type;
-  using reference = const View &;
-  using pointer = const View *;
-
-  LocalEntityIterator() = default;
-  explicit LocalEntityIterator(
-      const MeshType *const mesh,
-      const typename mesh_type::size_type elementPointIndex);
-
-  LocalEntityIterator &operator++();
-
-  LocalEntityIterator operator++(int);
-
-  LocalEntityIterator &operator--();
-
-  LocalEntityIterator operator--(int);
-
-  reference operator*() const;
-
-  pointer operator->() const;
-
-  friend inline bool operator==(const LocalEntityIterator &lhs,
-                                const LocalEntityIterator &rhs) {
-    return lhs._view == rhs._view;
-  }
-
-  friend inline bool operator!=(const LocalEntityIterator &lhs,
-                                const LocalEntityIterator &rhs) {
-    return !(lhs == rhs);
-  }
-
-private:
-  View _view;
-};
-} // namespace cpppetsc
-} // namespace ae108
-
-/********************************************************************
- *  implementations
- *******************************************************************/
-
-namespace ae108 {
-namespace cpppetsc {
-
-template <class MeshType, template <class> class ViewTemplate>
-LocalEntityIterator<MeshType, ViewTemplate>::LocalEntityIterator(
-    const MeshType *const mesh,
-    const typename mesh_type::size_type elementPointIndex)
-    : _view(mesh, elementPointIndex) {}
-
-template <class MeshType, template <class> class ViewTemplate>
-LocalEntityIterator<MeshType, ViewTemplate> &
-LocalEntityIterator<MeshType, ViewTemplate>::operator++() {
-  ++_view._entityIndex;
-  return *this;
-}
-
-template <class MeshType, template <class> class ViewTemplate>
-LocalEntityIterator<MeshType, ViewTemplate>
-LocalEntityIterator<MeshType, ViewTemplate>::operator++(int) {
-  auto copy = *this;
-  ++(*this);
-  return copy;
-}
-
-template <class MeshType, template <class> class ViewTemplate>
-LocalEntityIterator<MeshType, ViewTemplate> &
-LocalEntityIterator<MeshType, ViewTemplate>::operator--() {
-  --_view._entityIndex;
-  return *this;
-}
-
-template <class MeshType, template <class> class ViewTemplate>
-LocalEntityIterator<MeshType, ViewTemplate>
-LocalEntityIterator<MeshType, ViewTemplate>::operator--(int) {
-  auto copy = *this;
-  --(*this);
-  return copy;
-}
-
-template <class MeshType, template <class> class ViewTemplate>
-typename LocalEntityIterator<MeshType, ViewTemplate>::reference
-LocalEntityIterator<MeshType, ViewTemplate>::operator*() const {
-  return _view;
-}
-
-template <class MeshType, template <class> class ViewTemplate>
-typename LocalEntityIterator<MeshType, ViewTemplate>::pointer
-LocalEntityIterator<MeshType, ViewTemplate>::operator->() const {
-  return &_view;
-}
-} // namespace cpppetsc
-} // namespace ae108
\ No newline at end of file
diff --git a/cpppetsc/src/include/ae108/cpppetsc/LocalVertexIterator.h b/cpppetsc/src/include/ae108/cpppetsc/LocalVertexIterator.h
deleted file mode 100644
index b9667f0388fc0c2d57d5878d2fd8c2ad7d17d2fe..0000000000000000000000000000000000000000
--- a/cpppetsc/src/include/ae108/cpppetsc/LocalVertexIterator.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// © 2020 ETH Zurich, Mechanics and Materials Lab
-// © 2020 California Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include "ae108/cpppetsc/LocalEntityIterator.h"
-#include "ae108/cpppetsc/LocalVertexView.h"
-
-namespace ae108 {
-namespace cpppetsc {
-
-/**
- * @brief A bidirectional iterator to traverse the local vertices of a mesh.
- * Dereferencing the iterator yields a view of a local vertex. See
- * LocalVertexView.h for a list of the methods that it supports.
- *
- * @remark Changes to the mesh invalidate the iterator.
- */
-template <class MeshType>
-using LocalVertexIterator = LocalEntityIterator<MeshType, LocalVertexView>;
-
-} // namespace cpppetsc
-} // namespace ae108
\ No newline at end of file
diff --git a/cpppetsc/src/include/ae108/cpppetsc/LocalVertexView.h b/cpppetsc/src/include/ae108/cpppetsc/LocalVertexView.h
index 34b7ec17d4b0c5c106d100fa71e33145f7196934..7331ad83f57c5f9986a2f7011aa01e97e72cebcb 100644
--- a/cpppetsc/src/include/ae108/cpppetsc/LocalVertexView.h
+++ b/cpppetsc/src/include/ae108/cpppetsc/LocalVertexView.h
@@ -22,11 +22,9 @@
 namespace ae108 {
 namespace cpppetsc {
 
-template <class IteratorType> class LocalVertexView {
-  friend IteratorType;
-
+template <class MeshType_> class LocalVertexView {
 public:
-  using mesh_type = typename IteratorType::mesh_type;
+  using mesh_type = MeshType_;
 
   using size_type = typename mesh_type::size_type;
   using value_type = typename mesh_type::value_type;
diff --git a/cpppetsc/src/include/ae108/cpppetsc/Mesh.h b/cpppetsc/src/include/ae108/cpppetsc/Mesh.h
index a60618281d9caa214c0e855d92f3c1deb08f3126..5dbecaac0af2e0419fd51fb998af0d28ba18225a 100644
--- a/cpppetsc/src/include/ae108/cpppetsc/Mesh.h
+++ b/cpppetsc/src/include/ae108/cpppetsc/Mesh.h
@@ -15,10 +15,7 @@
 
 #pragma once
 
-#include "ae108/cpppetsc/IteratorRange.h"
-#include "ae108/cpppetsc/LocalElementIterator.h"
 #include "ae108/cpppetsc/LocalElementView.h"
-#include "ae108/cpppetsc/LocalVertexIterator.h"
 #include "ae108/cpppetsc/LocalVertexView.h"
 #include "ae108/cpppetsc/Matrix_fwd.h"
 #include "ae108/cpppetsc/Mesh_fwd.h"
@@ -37,6 +34,8 @@
 #include <petscmath.h>
 #include <petscsf.h>
 #include <petscsys.h>
+#include <range/v3/view/iota.hpp>
+#include <range/v3/view/transform.hpp>
 #include <utility>
 #include <vector>
 
@@ -44,8 +43,8 @@ namespace ae108 {
 namespace cpppetsc {
 
 template <class Policy> class Mesh {
-  friend LocalElementView<LocalElementIterator<Mesh>>;
-  friend LocalVertexView<LocalVertexIterator<Mesh>>;
+  friend LocalElementView<Mesh>;
+  friend LocalVertexView<Mesh>;
 
 public:
   using size_type = PetscInt;
@@ -92,9 +91,6 @@ public:
                                const size_type dofPerVertex,
                                const size_type dofPerElement);
 
-  using const_element_iterator = LocalElementIterator<Mesh>;
-  using const_iterator = const_element_iterator;
-
   /**
    * @brief Clone the mesh with a different default section.
    */
@@ -104,13 +100,12 @@ public:
   /**
    * @brief Makes it possible to iterate over (views of) the local elements.
    */
-  IteratorRange<const_iterator> localElements() const;
+  auto localElements() const;
 
-  using const_vertex_iterator = LocalVertexIterator<Mesh>;
   /**
    * @brief Makes it possible to iterate over (views of) the local vertices.
    */
-  IteratorRange<const_vertex_iterator> localVertices() const;
+  auto localVertices() const;
 
   /**
    * @brief Returns the total number of elements in the mesh.
@@ -364,24 +359,26 @@ extern template class Mesh<ParallelComputePolicy>;
 namespace ae108 {
 namespace cpppetsc {
 
-template <class Policy>
-IteratorRange<typename Mesh<Policy>::const_iterator>
-Mesh<Policy>::localElements() const {
+template <class Policy> auto Mesh<Policy>::localElements() const {
   auto start = size_type{0};
   auto stop = size_type{0};
   Policy::handleError(DMPlexGetHeightStratum(_mesh.get(), 0, &start, &stop));
-  return IteratorRange<const_iterator>{const_iterator{this, start},
-                                       const_iterator{this, stop}};
+
+  namespace rv = ranges::cpp20::views;
+  return rv::iota(start, stop) | rv::transform([&](const size_type id) {
+           return LocalElementView<Mesh>(this, id);
+         });
 }
 
-template <class Policy>
-IteratorRange<typename Mesh<Policy>::const_vertex_iterator>
-Mesh<Policy>::localVertices() const {
+template <class Policy> auto Mesh<Policy>::localVertices() const {
   auto start = size_type{0};
   auto stop = size_type{0};
   Policy::handleError(DMPlexGetDepthStratum(_mesh.get(), 0, &start, &stop));
-  return IteratorRange<const_vertex_iterator>{
-      const_vertex_iterator{this, start}, const_vertex_iterator{this, stop}};
+
+  namespace rv = ranges::cpp20::views;
+  return rv::iota(start, stop) | rv::transform([&](const size_type id) {
+           return LocalVertexView<Mesh>(this, id);
+         });
 }
 
 template <class Policy>
diff --git a/cpppetsc/src/include/ae108/cpppetsc/MeshBoundaryCondition.h b/cpppetsc/src/include/ae108/cpppetsc/MeshBoundaryCondition.h
index 7443587ebb1d15a984f76d5492689b7f033cf1bc..4561b156af1486ffd0ad549d7ea57218f41080d5 100644
--- a/cpppetsc/src/include/ae108/cpppetsc/MeshBoundaryCondition.h
+++ b/cpppetsc/src/include/ae108/cpppetsc/MeshBoundaryCondition.h
@@ -15,13 +15,13 @@
 
 #pragma once
 
-#include "ae108/cpppetsc/LocalVertexIterator.h"
+#include "ae108/cpppetsc/LocalVertexView.h"
 
 namespace ae108 {
 namespace cpppetsc {
 
 template <class MeshType> struct MeshBoundaryCondition {
-  typename LocalVertexIterator<MeshType>::value_type node;
+  LocalVertexView<MeshType> node;
   typename MeshType::size_type dof;
   typename MeshType::value_type value;
 };
diff --git a/cpppetsc/test/CMakeLists.txt b/cpppetsc/test/CMakeLists.txt
index 7caf51a4062693c5a6dc0aaebaceaab0861b4580..b251decdfe321800982574c7f358262c4d28f1e3 100644
--- a/cpppetsc/test/CMakeLists.txt
+++ b/cpppetsc/test/CMakeLists.txt
@@ -21,8 +21,6 @@ add_executable(${PROJECT_NAME}-CppPetscTests
                ./IteratorRange_Test.cc
                ./LinearSolverDivergedException_Test.cc
                ./LinearSolver_Test.cc
-               ./LocalElementIterator_Test.cc
-               ./LocalVertexIterator_Test.cc
                ./Matrix_Test.cc
                ./Mesh_Test.cc
                ./NonlinearSolverDivergedException_Test.cc
diff --git a/cpppetsc/test/LocalElementIterator_Test.cc b/cpppetsc/test/LocalElementIterator_Test.cc
deleted file mode 100644
index 8ee39df11de9c212ce9755094c169d15dad64927..0000000000000000000000000000000000000000
--- a/cpppetsc/test/LocalElementIterator_Test.cc
+++ /dev/null
@@ -1,132 +0,0 @@
-// © 2020 ETH Zurich, Mechanics and Materials Lab
-// © 2020 California Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "ae108/cpppetsc/LocalElementIterator.h"
-#include "ae108/cpppetsc/test/LocalEntityIterator_Test.h"
-#include "ae108/cppptest/Matchers.h"
-#include <gmock/gmock.h>
-
-using ae108::cppptest::AddressEq;
-using testing::Eq;
-using testing::Return;
-using testing::Types;
-
-namespace ae108 {
-namespace cpppetsc {
-namespace test {
-namespace {
-
-using IteratorsToTest = Types<LocalElementIterator<Mesh_Mock>>;
-INSTANTIATE_TYPED_TEST_CASE_P(LocalElementIteratorTest,
-                              LocalEntityIterator_Test, IteratorsToTest);
-} // namespace
-} // namespace test
-
-namespace {
-
-struct LocalElementIterator_Test
-    : test::LocalEntityIterator_Test<LocalElementIterator<test::Mesh_Mock>> {
-  using Iterator = LocalElementIterator<test::Mesh_Mock>;
-};
-
-TEST_F(LocalElementIterator_Test, accessing_index_works) {
-  const auto value = Iterator::mesh_type::size_type{77};
-  EXPECT_CALL(mesh, elementPointIndexToGlobalIndex(begin_index))
-      .WillOnce(Return(value));
-
-  EXPECT_THAT(begin->index(), Eq(value));
-}
-
-TEST_F(LocalElementIterator_Test, accessing_vertices_works) {
-  const std::vector<Iterator::mesh_type::size_type> vertices = {3, 4, 5};
-  EXPECT_CALL(mesh, vertices(begin_index)).WillOnce(Return(vertices));
-
-  EXPECT_THAT(begin->vertexIndices(), Eq(vertices));
-}
-
-TEST_F(LocalElementIterator_Test, accessing_number_of_vertices_works) {
-  const auto value = Iterator::mesh_type::size_type{77};
-  EXPECT_CALL(mesh, numberOfVertices(begin_index)).WillOnce(Return(value));
-
-  EXPECT_THAT(begin->numberOfVertices(), Eq(value));
-}
-
-TEST_F(LocalElementIterator_Test, dereferencing_works) {
-  const auto value = Iterator::mesh_type::size_type{77};
-  EXPECT_CALL(mesh, numberOfVertices(begin_index)).WillOnce(Return(value));
-
-  EXPECT_THAT((*begin).numberOfVertices(), Eq(value));
-}
-
-TEST_F(LocalElementIterator_Test, copy_element_data_works) {
-  TaggedEntity<test::Mesh_Mock::vector_type, LocalTag> from;
-  std::vector<double> to;
-
-  EXPECT_CALL(mesh, copyEntityData(begin_index, AddressEq(&from), &to))
-      .Times(1);
-
-  begin->copyElementData(from, &to);
-}
-
-TEST_F(LocalElementIterator_Test, add_element_data_works) {
-  TaggedEntity<test::Mesh_Mock::vector_type, LocalTag> to;
-  std::vector<double> from;
-
-  EXPECT_CALL(mesh, addEntityData(begin_index, from, &to)).Times(1);
-
-  begin->addElementData(from, &to);
-}
-
-TEST_F(LocalElementIterator_Test, add_element_matrix_works) {
-  test::Mesh_Mock::matrix_type to;
-  std::vector<double> from;
-
-  EXPECT_CALL(mesh, addEntityMatrix(begin_index, from, &to)).Times(1);
-
-  begin->addElementMatrix(from, &to);
-}
-
-TEST_F(LocalElementIterator_Test, number_of_dofs_works) {
-  const test::Mesh_Mock::size_type dofs = 77;
-  EXPECT_CALL(mesh, numberOfDofs(begin_index)).WillOnce(Return(dofs));
-
-  EXPECT_THAT(begin->numberOfDofs(), Eq(dofs));
-}
-
-TEST_F(LocalElementIterator_Test, forClonedMesh_redirects_to_cloned_mesh) {
-  const test::Mesh_Mock::size_type dofs = 77;
-
-  test::Mesh_Mock newMesh;
-  EXPECT_CALL(newMesh, numberOfDofs(begin_index)).WillOnce(Return(dofs));
-  EXPECT_THAT(begin->forClonedMesh(&newMesh).numberOfDofs(), Eq(dofs));
-}
-
-TEST_F(LocalElementIterator_Test, local_dof_range_works) {
-  const auto reference = std::make_pair(77, 777);
-
-  EXPECT_CALL(mesh, localDofLineRange(begin_index)).WillOnce(Return(reference));
-  EXPECT_THAT(begin->localDofLineRange(), Eq(reference));
-}
-
-TEST_F(LocalElementIterator_Test, global_dof_range_works) {
-  const auto reference = std::make_pair(77, 777);
-
-  EXPECT_CALL(mesh, globalDofLineRange(begin_index))
-      .WillOnce(Return(reference));
-  EXPECT_THAT(begin->globalDofLineRange(), Eq(reference));
-}
-} // namespace
-} // namespace cpppetsc
-} // namespace ae108
diff --git a/cpppetsc/test/LocalVertexIterator_Test.cc b/cpppetsc/test/LocalVertexIterator_Test.cc
deleted file mode 100644
index 039c180a70a936580c1211c26d79e20bd72e4604..0000000000000000000000000000000000000000
--- a/cpppetsc/test/LocalVertexIterator_Test.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// © 2020 ETH Zurich, Mechanics and Materials Lab
-// © 2020 California Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "ae108/cpppetsc/LocalVertexIterator.h"
-#include "ae108/cpppetsc/test/LocalEntityIterator_Test.h"
-#include "ae108/cppptest/Matchers.h"
-#include <gmock/gmock.h>
-
-using ae108::cppptest::AddressEq;
-using testing::Eq;
-using testing::Return;
-using testing::Types;
-
-namespace ae108 {
-namespace cpppetsc {
-namespace test {
-namespace {
-
-using IteratorsToTest = Types<LocalVertexIterator<Mesh_Mock>>;
-INSTANTIATE_TYPED_TEST_CASE_P(LocalVertexIteratorTest, LocalEntityIterator_Test,
-                              IteratorsToTest);
-} // namespace
-} // namespace test
-
-namespace {
-
-struct LocalVertexIterator_Test
-    : test::LocalEntityIterator_Test<LocalVertexIterator<test::Mesh_Mock>> {
-  using Iterator = LocalVertexIterator<test::Mesh_Mock>;
-};
-
-TEST_F(LocalVertexIterator_Test, accessing_index_works) {
-  const auto value = Iterator::mesh_type::size_type{77};
-  EXPECT_CALL(mesh, vertexPointIndexToGlobalIndex(begin_index))
-      .WillOnce(Return(value));
-
-  EXPECT_THAT(begin->index(), Eq(value));
-}
-
-TEST_F(LocalVertexIterator_Test, accessing_number_of_dofs_works) {
-  const auto value = Iterator::mesh_type::size_type{77};
-  EXPECT_CALL(mesh, numberOfDofs(begin_index)).WillOnce(Return(value));
-
-  EXPECT_THAT(begin->numberOfDofs(), Eq(value));
-}
-
-TEST_F(LocalVertexIterator_Test, accessing_local_dof_line_range_works) {
-  const auto value = std::make_pair(Iterator::mesh_type::size_type{77},
-                                    Iterator::mesh_type::size_type{77});
-  EXPECT_CALL(mesh, localDofLineRange(begin_index)).WillOnce(Return(value));
-
-  EXPECT_THAT(begin->localDofLineRange(), Eq(value));
-}
-
-TEST_F(LocalVertexIterator_Test, accessing_global_dof_line_range_works) {
-  const auto value = std::make_pair(Iterator::mesh_type::size_type{77},
-                                    Iterator::mesh_type::size_type{77});
-  EXPECT_CALL(mesh, globalDofLineRange(begin_index)).WillOnce(Return(value));
-
-  EXPECT_THAT(begin->globalDofLineRange(), Eq(value));
-}
-
-TEST_F(LocalVertexIterator_Test, forClonedMesh_redirects_to_cloned_mesh) {
-  const test::Mesh_Mock::size_type dofs = 77;
-
-  test::Mesh_Mock newMesh;
-  EXPECT_CALL(newMesh, numberOfDofs(begin_index)).WillOnce(Return(dofs));
-  EXPECT_THAT(begin->forClonedMesh(&newMesh).numberOfDofs(), Eq(dofs));
-}
-
-TEST_F(LocalVertexIterator_Test, copy_vertex_data_works) {
-  TaggedEntity<test::Mesh_Mock::vector_type, LocalTag> from;
-  std::vector<double> to;
-
-  EXPECT_CALL(mesh, copyEntityData(begin_index, AddressEq(&from), &to))
-      .Times(1);
-
-  begin->copyVertexData(from, &to);
-}
-
-TEST_F(LocalVertexIterator_Test, add_vertex_data_works) {
-  TaggedEntity<test::Mesh_Mock::vector_type, LocalTag> to;
-  std::vector<double> from;
-
-  EXPECT_CALL(mesh, addEntityData(begin_index, from, &to)).Times(1);
-
-  begin->addVertexData(from, &to);
-}
-
-TEST_F(LocalVertexIterator_Test, add_vertex_matrix_works) {
-  test::Mesh_Mock::matrix_type to;
-  std::vector<double> from;
-
-  EXPECT_CALL(mesh, addEntityMatrix(begin_index, from, &to)).Times(1);
-
-  begin->addVertexMatrix(from, &to);
-}
-} // namespace
-} // namespace cpppetsc
-} // namespace ae108
diff --git a/cpppetsc/test/Mesh_Test.cc b/cpppetsc/test/Mesh_Test.cc
index 1e45e8ee612c6c341af31d31e22163b7d1219ad0..7cae66a7455eeb6b2e8ff8ad86fcf648a251d552 100644
--- a/cpppetsc/test/Mesh_Test.cc
+++ b/cpppetsc/test/Mesh_Test.cc
@@ -183,8 +183,8 @@ TYPED_TEST(Mesh_Test, cloned_mesh_has_correct_connectivity) {
   for (const auto &element : this->mesh.localElements()) {
     ASSERT_THAT(iterator, Not(Eq(clonedRange.end())));
 
-    EXPECT_THAT(iterator->index(), Eq(element.index()));
-    EXPECT_THAT(iterator->vertexIndices(), Eq(element.vertexIndices()));
+    EXPECT_THAT((*iterator).index(), Eq(element.index()));
+    EXPECT_THAT((*iterator).vertexIndices(), Eq(element.vertexIndices()));
 
     ++iterator;
   }
@@ -247,7 +247,8 @@ TYPED_TEST(Mesh_Test, correct_number_of_local_elements_are_iterated) {
   using size_type = typename TestFixture::size_type;
 
   const auto range = this->mesh.localElements();
-  auto elements = size_type{std::distance(range.begin(), range.end())};
+  auto elements =
+      static_cast<size_type>(std::distance(range.begin(), range.end()));
 
   ASSERT_THAT(MPI_Allreduce(MPI_IN_PLACE, &elements, 1, MPI_INT, MPI_SUM,
                             TestFixture::policy_type::communicator()),
@@ -259,7 +260,8 @@ TYPED_TEST(Mesh_Test, plausible_number_of_local_vertices_are_iterated) {
   using size_type = typename TestFixture::size_type;
 
   const auto range = this->mesh.localVertices();
-  auto vertices = size_type{std::distance(range.begin(), range.end())};
+  auto vertices =
+      static_cast<size_type>(std::distance(range.begin(), range.end()));
 
   ASSERT_THAT(MPI_Allreduce(MPI_IN_PLACE, &vertices, 1, MPI_INT, MPI_SUM,
                             TestFixture::policy_type::communicator()),
@@ -496,7 +498,7 @@ TYPED_TEST(Mesh_Test, begin_element_iterator_starts_at_index_0) {
   auto result = std::numeric_limits<size_type>::max();
 
   if (iterator != this->mesh.localElements().end()) {
-    result = iterator->index();
+    result = (*iterator).index();
   }
   ASSERT_THAT(MPI_Allreduce(MPI_IN_PLACE, &result, 1, MPI_INT, MPI_MIN,
                             TestFixture::policy_type::communicator()),
@@ -512,7 +514,7 @@ TYPED_TEST(Mesh_Test, end_element_iterator_is_past_final_index) {
   auto result = std::numeric_limits<size_type>::min();
 
   if (iterator != this->mesh.localElements().begin()) {
-    result = (--iterator)->index();
+    result = (*(--iterator)).index();
   }
   ASSERT_THAT(MPI_Allreduce(MPI_IN_PLACE, &result, 1, MPI_INT, MPI_MAX,
                             TestFixture::policy_type::communicator()),
@@ -528,7 +530,7 @@ TYPED_TEST(Mesh_Test, begin_vertex_iterator_starts_at_index_0) {
   auto result = std::numeric_limits<size_type>::max();
 
   if (iterator != this->mesh.localVertices().end()) {
-    result = iterator->index();
+    result = (*iterator).index();
   }
   ASSERT_THAT(MPI_Allreduce(MPI_IN_PLACE, &result, 1, MPI_INT, MPI_MIN,
                             TestFixture::policy_type::communicator()),
@@ -544,7 +546,7 @@ TYPED_TEST(Mesh_Test, end_vertex_iterator_is_past_final_index) {
   auto result = std::numeric_limits<size_type>::min();
 
   if (iterator != this->mesh.localVertices().begin()) {
-    result = (--iterator)->index();
+    result = (*(--iterator)).index();
   }
   ASSERT_THAT(MPI_Allreduce(MPI_IN_PLACE, &result, 1, MPI_INT, MPI_MAX,
                             TestFixture::policy_type::communicator()),
diff --git a/cpppetsc/test/include/ae108/cpppetsc/test/LocalEntityIterator_Test.h b/cpppetsc/test/include/ae108/cpppetsc/test/LocalEntityIterator_Test.h
deleted file mode 100644
index 4d63af462861ebd45049782095f9cea702e28dc3..0000000000000000000000000000000000000000
--- a/cpppetsc/test/include/ae108/cpppetsc/test/LocalEntityIterator_Test.h
+++ /dev/null
@@ -1,180 +0,0 @@
-// © 2020 ETH Zurich, Mechanics and Materials Lab
-// © 2020 California Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "ae108/cpppetsc/LocalEntityIterator.h"
-#include "ae108/cpppetsc/TaggedVector.h"
-#include <gmock/gmock.h>
-
-namespace ae108 {
-namespace cpppetsc {
-namespace test {
-namespace {
-
-struct Mesh_Mock {
-  using size_type = int;
-  using value_type = double;
-  using vector_type = std::vector<float>;
-  using matrix_type = std::vector<std::vector<float>>;
-
-  MOCK_CONST_METHOD1(vertices, std::vector<size_type>(size_type));
-  MOCK_CONST_METHOD1(elementPointIndexToGlobalIndex, size_type(size_type));
-  MOCK_CONST_METHOD1(vertexPointIndexToGlobalIndex, size_type(size_type));
-  MOCK_CONST_METHOD1(numberOfVertices, size_type(size_type));
-
-  MOCK_CONST_METHOD1(numberOfDofs, size_type(size_type));
-
-  using Range = std::pair<size_type, size_type>;
-  MOCK_CONST_METHOD1(localDofLineRange, Range(size_type));
-  MOCK_CONST_METHOD1(globalDofLineRange, Range(size_type));
-
-  MOCK_CONST_METHOD3(copyEntityData, void(size_type, const local<vector_type> &,
-                                          std::vector<value_type> *));
-  MOCK_CONST_METHOD3(addEntityData,
-                     void(size_type, const std::vector<value_type> &,
-                          local<vector_type> *));
-
-  MOCK_CONST_METHOD3(addEntityMatrix,
-                     void(size_type, const std::vector<value_type> &,
-                          matrix_type *));
-};
-
-template <class Iterator> struct LocalEntityIterator_Test : ::testing::Test {
-  Mesh_Mock mesh;
-
-  typename Iterator::mesh_type::size_type begin_index = 1;
-  Iterator begin{&mesh, begin_index};
-
-  typename Iterator::mesh_type::size_type end_index = 7;
-  Iterator end{&mesh, end_index};
-};
-
-TYPED_TEST_CASE_P(LocalEntityIterator_Test);
-
-TYPED_TEST_P(LocalEntityIterator_Test, is_default_constructible) {
-  EXPECT_NO_THROW(TypeParam());
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test,
-             eq_returns_false_for_different_iterators) {
-  EXPECT_FALSE(this->begin == this->end);
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, eq_returns_true_for_same_iterator) {
-  EXPECT_TRUE(this->begin == this->begin);
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test,
-             neq_returns_true_for_different_iterators) {
-  EXPECT_TRUE(this->begin != this->end);
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, neq_returns_false_for_same_iterator) {
-  EXPECT_FALSE(this->begin != this->begin);
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, is_copyconstrubtible) {
-  TypeParam x(this->begin);
-  EXPECT_THAT(x, ::testing::Eq(this->begin));
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, is_copyassignable) {
-  TypeParam x;
-
-  x = this->begin;
-
-  EXPECT_THAT(x, ::testing::Eq(this->begin));
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, is_swappable) {
-  using std::swap;
-
-  TypeParam x(this->begin);
-  TypeParam y(this->end);
-
-  swap(x, y);
-
-  EXPECT_THAT(x, ::testing::Eq(this->end));
-  EXPECT_THAT(y, ::testing::Eq(this->begin));
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, is_dereferenceable) {
-  EXPECT_NO_THROW(*this->begin);
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, is_preincrementable) {
-  for (typename TypeParam::mesh_type::size_type i = this->begin_index;
-       i < this->end_index; ++i)
-    ++this->begin;
-
-  EXPECT_THAT(this->begin, ::testing::Eq(this->end));
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, is_postincrementable) {
-  for (typename TypeParam::mesh_type::size_type i = this->begin_index;
-       i < this->end_index; ++i)
-    this->begin++;
-
-  EXPECT_THAT(this->begin, ::testing::Eq(this->end));
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, is_predecrementable) {
-  for (typename TypeParam::mesh_type::size_type i = this->begin_index;
-       i < this->end_index; ++i)
-    --this->end;
-
-  EXPECT_THAT(this->begin, ::testing::Eq(this->end));
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, is_postdecrementable) {
-  for (typename TypeParam::mesh_type::size_type i = this->begin_index;
-       i < this->end_index; ++i)
-    this->end--;
-
-  EXPECT_THAT(this->begin, ::testing::Eq(this->end));
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, preincrement_returns_new_iterator) {
-  auto iter = this->begin;
-  EXPECT_THAT(this->begin, ::testing::Not(::testing::Eq(++iter)));
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, postincrement_returns_old_iterator) {
-  auto iter = this->begin;
-  EXPECT_THAT(this->begin, ::testing::Eq(iter++));
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, predecrement_returns_new_iterator) {
-  auto iter = this->end;
-  EXPECT_THAT(this->end, ::testing::Not(::testing::Eq(--iter)));
-}
-
-TYPED_TEST_P(LocalEntityIterator_Test, postdecrement_returns_old_iterator) {
-  auto iter = this->end;
-  EXPECT_THAT(this->end, ::testing::Eq(iter--));
-}
-
-REGISTER_TYPED_TEST_CASE_P(
-    LocalEntityIterator_Test, is_default_constructible,
-    eq_returns_false_for_different_iterators, eq_returns_true_for_same_iterator,
-    neq_returns_true_for_different_iterators,
-    neq_returns_false_for_same_iterator, is_copyconstrubtible,
-    is_copyassignable, is_swappable, is_dereferenceable, is_preincrementable,
-    is_postincrementable, is_predecrementable, is_postdecrementable,
-    preincrement_returns_new_iterator, postincrement_returns_old_iterator,
-    predecrement_returns_new_iterator, postdecrement_returns_old_iterator);
-} // namespace
-} // namespace test
-} // namespace cpppetsc
-} // namespace ae108
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 5aa298270d3e45ec4d30174c1f9061d9afd18f4d..27f8357e9729a1b009bd02d361cf1f5e76f564ce 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -27,6 +27,7 @@ RUN apt-get update && \
         googletest \
         libboost-program-options-dev \
         libeigen3-dev \
+        librange-v3-dev \
         ninja-build \
         petsc-dev \
         python3 python3-pip \