From ab6990466f280b6f3dc8fc88870bda2286a892d0 Mon Sep 17 00:00:00 2001
From: Bastian <telgenb@ethz.ch>
Date: Mon, 9 Jan 2023 14:40:01 +0100
Subject: [PATCH] Merge ae108-meshing/main into
 '132-merge-ae108-meshing-into-ae108'

---
 .gitignore                                    |   3 +-
 CMakeLists.txt                                |   6 +-
 cmake/modules/FindGmsh.cmake                  |  35 ++++
 cmake/modules/FindVoro++.cmake                |  35 ++++
 docker-compose.yml                            |   8 +-
 docker/Dockerfile                             |   9 +-
 examples/CMakeLists.txt                       |   2 +
 examples/meshing/CMakeLists.txt               |  48 +++++
 examples/meshing/GradedLattice.cc             |  98 ++++++++++
 examples/meshing/PeriodicRVEMesh.cc           | 129 +++++++++++++
 examples/meshing/PhysicalGroups.cc            | 143 +++++++++++++++
 examples/meshing/SegmentMesh.cc               |  73 ++++++++
 examples/meshing/TwoPhaseCube.cc              | 102 +++++++++++
 meshing/CMakeLists.txt                        |  21 +++
 meshing/src/BoundaryRepresentation.cc         |  16 ++
 meshing/src/BoundingBox.cc                    |  16 ++
 meshing/src/CMakeLists.txt                    |  89 +++++++++
 meshing/src/bounding_box_of.cc                |  16 ++
 meshing/src/construct_periodic_point_cloud.cc |  75 ++++++++
 meshing/src/construct_rectilinear_grid.cc     | 166 +++++++++++++++++
 meshing/src/construct_voronoi_cell.cc         | 112 ++++++++++++
 meshing/src/cppgmsh/Context.cc                |  41 +++++
 meshing/src/cppgmsh/Node.cc                   |  16 ++
 meshing/src/cppgmsh/as_affine_transform.cc    |  41 +++++
 meshing/src/cppgmsh/construct_box.cc          |  33 ++++
 meshing/src/cppgmsh/construct_cylinders.cc    |  72 ++++++++
 meshing/src/cppgmsh/construct_polyhedron.cc   |  55 ++++++
 meshing/src/cppgmsh/construct_rectangle.cc    |  33 ++++
 meshing/src/cppgmsh/copy_entities.cc          |  32 ++++
 meshing/src/cppgmsh/extract_mesh.cc           |  85 +++++++++
 meshing/src/cppgmsh/extrude_entities.cc       |  42 +++++
 meshing/src/cppgmsh/extrude_surface.cc        |  64 +++++++
 meshing/src/cppgmsh/fragment_entities.cc      |  34 ++++
 meshing/src/cppgmsh/fuse_entities.cc          |  33 ++++
 meshing/src/cppgmsh/generate_mesh.cc          |  32 ++++
 meshing/src/cppgmsh/get_boundary_of.cc        |  34 ++++
 meshing/src/cppgmsh/get_centroid_of.cc        |  33 ++++
 meshing/src/cppgmsh/get_coords_of.cc          |  39 ++++
 meshing/src/cppgmsh/get_elements_in.cc        |  42 +++++
 meshing/src/cppgmsh/get_entities_in.cc        |  53 ++++++
 meshing/src/cppgmsh/get_entities_of.cc        |  32 ++++
 meshing/src/cppgmsh/get_nodes_of.cc           |  58 ++++++
 meshing/src/cppgmsh/get_normal_of.cc          |  35 ++++
 meshing/src/cppgmsh/get_periodic_nodes_of.cc  |  54 ++++++
 meshing/src/cppgmsh/get_physical_groups.cc    |  31 ++++
 meshing/src/cppgmsh/get_points_of.cc          |  42 +++++
 meshing/src/cppgmsh/heal_periodic_domains.cc  |  52 ++++++
 meshing/src/cppgmsh/heal_periodic_surfaces.cc |  71 ++++++++
 meshing/src/cppgmsh/import_shapes.cc          |  32 ++++
 meshing/src/cppgmsh/intersect_entities.cc     |  37 ++++
 meshing/src/cppgmsh/read_file.cc              |  27 +++
 meshing/src/cppgmsh/remove_entities.cc        |  30 +++
 meshing/src/cppgmsh/rotate_entities.cc        |  33 ++++
 .../cppgmsh/set_domain_entities_periodic.cc   |  54 ++++++
 meshing/src/cppgmsh/set_expert_mode.cc        |  29 +++
 meshing/src/cppgmsh/set_granularity.cc        |  30 +++
 meshing/src/cppgmsh/set_periodic.cc           |  45 +++++
 meshing/src/cppgmsh/set_physical_group_of.cc  |  42 +++++
 meshing/src/cppgmsh/synchronize.cc            |  27 +++
 meshing/src/cppgmsh/translate_entities.cc     |  39 ++++
 meshing/src/cppgmsh/write_file.cc             |  27 +++
 .../ae108/meshing/BoundaryRepresentation.h    |  84 +++++++++
 .../src/include/ae108/meshing/BoundingBox.h   |  27 +++
 .../include/ae108/meshing/bounding_box_of.h   |  45 +++++
 .../meshing/construct_periodic_point_cloud.h  |  47 +++++
 .../meshing/construct_rectilinear_grid.h      |  38 ++++
 .../ae108/meshing/construct_voronoi_cell.h    |  39 ++++
 .../include/ae108/meshing/cppgmsh/Context.h   |  57 ++++++
 .../src/include/ae108/meshing/cppgmsh/Node.h  |  43 +++++
 .../meshing/cppgmsh/as_affine_transform.h     |  35 ++++
 .../ae108/meshing/cppgmsh/construct_box.h     |  38 ++++
 .../meshing/cppgmsh/construct_cylinders.h     |  43 +++++
 .../meshing/cppgmsh/construct_polyhedron.h    |  37 ++++
 .../meshing/cppgmsh/construct_rectangle.h     |  41 +++++
 .../ae108/meshing/cppgmsh/copy_entities.h     |  35 ++++
 .../ae108/meshing/cppgmsh/extract_mesh.h      |  42 +++++
 .../ae108/meshing/cppgmsh/extrude_entities.h  |  44 +++++
 .../ae108/meshing/cppgmsh/extrude_surface.h   |  39 ++++
 .../ae108/meshing/cppgmsh/fragment_entities.h |  36 ++++
 .../ae108/meshing/cppgmsh/fuse_entities.h     |  35 ++++
 .../ae108/meshing/cppgmsh/generate_mesh.h     |  36 ++++
 .../ae108/meshing/cppgmsh/get_boundary_of.h   |  38 ++++
 .../ae108/meshing/cppgmsh/get_centroid_of.h   |  35 ++++
 .../ae108/meshing/cppgmsh/get_coords_of.h     |  42 +++++
 .../ae108/meshing/cppgmsh/get_elements_in.h   |  36 ++++
 .../ae108/meshing/cppgmsh/get_entities_in.h   |  49 +++++
 .../ae108/meshing/cppgmsh/get_entities_of.h   |  35 ++++
 .../ae108/meshing/cppgmsh/get_nodes_of.h      |  44 +++++
 .../ae108/meshing/cppgmsh/get_normal_of.h     |  34 ++++
 .../meshing/cppgmsh/get_periodic_nodes_of.h   |  41 +++++
 .../meshing/cppgmsh/get_physical_groups.h     |  36 ++++
 .../ae108/meshing/cppgmsh/get_points_of.h     |  33 ++++
 .../meshing/cppgmsh/heal_periodic_domains.h   |  41 +++++
 .../meshing/cppgmsh/heal_periodic_surfaces.h  |  41 +++++
 .../ae108/meshing/cppgmsh/import_shapes.h     |  38 ++++
 .../meshing/cppgmsh/intersect_entities.h      |  43 +++++
 .../include/ae108/meshing/cppgmsh/read_file.h |  36 ++++
 .../ae108/meshing/cppgmsh/remove_entities.h   |  37 ++++
 .../ae108/meshing/cppgmsh/rotate_entities.h   |  41 +++++
 .../cppgmsh/set_domain_entities_periodic.h    |  41 +++++
 .../ae108/meshing/cppgmsh/set_expert_mode.h   |  32 ++++
 .../ae108/meshing/cppgmsh/set_granularity.h   |  32 ++++
 .../ae108/meshing/cppgmsh/set_periodic.h      |  43 +++++
 .../meshing/cppgmsh/set_physical_group_of.h   |  36 ++++
 .../ae108/meshing/cppgmsh/synchronize.h       |  31 ++++
 .../meshing/cppgmsh/translate_entities.h      |  41 +++++
 .../ae108/meshing/cppgmsh/write_file.h        |  35 ++++
 meshing/test/BoundaryRepresentation_Test.cc   | 110 +++++++++++
 meshing/test/BoundingBox_Test.cc              |  35 ++++
 meshing/test/CMakeLists.txt                   |  74 ++++++++
 meshing/test/bounding_box_of_Test.cc          |  41 +++++
 .../construct_periodic_point_cloud_Test.cc    |  89 +++++++++
 .../test/construct_rectilinear_grid_Test.cc   |  75 ++++++++
 meshing/test/construct_voronoi_cell_Test.cc   |  74 ++++++++
 .../test/cppgmsh/as_affine_transform_Test.cc  |  41 +++++
 meshing/test/cppgmsh/construct_box_Test.cc    |  43 +++++
 .../test/cppgmsh/construct_cylinders_Test.cc  |  75 ++++++++
 .../test/cppgmsh/construct_polyhedron_Test.cc | 104 +++++++++++
 .../test/cppgmsh/construct_rectangle_Test.cc  |  55 ++++++
 meshing/test/cppgmsh/copy_entities_Test.cc    |  63 +++++++
 meshing/test/cppgmsh/extract_mesh_Test.cc     | 144 +++++++++++++++
 meshing/test/cppgmsh/extrude_entities_Test.cc | 121 ++++++++++++
 meshing/test/cppgmsh/extrude_surface_Test.cc  |  61 +++++++
 .../test/cppgmsh/fragment_entities_Test.cc    | 152 ++++++++++++++++
 meshing/test/cppgmsh/fuse_entities_Test.cc    |  69 +++++++
 meshing/test/cppgmsh/generate_mesh_Test.cc    | 172 ++++++++++++++++++
 meshing/test/cppgmsh/get_boundary_of_Test.cc  |  91 +++++++++
 meshing/test/cppgmsh/get_centroid_of_Test.cc  |  55 ++++++
 meshing/test/cppgmsh/get_coords_of_Test.cc    |  56 ++++++
 meshing/test/cppgmsh/get_elements_in_Test.cc  | 128 +++++++++++++
 meshing/test/cppgmsh/get_entities_in_Test.cc  |  86 +++++++++
 meshing/test/cppgmsh/get_entities_of_Test.cc  |  70 +++++++
 meshing/test/cppgmsh/get_nodes_of_Test.cc     |  85 +++++++++
 meshing/test/cppgmsh/get_normal_of_Test.cc    |  43 +++++
 .../cppgmsh/get_periodic_nodes_of_Test.cc     |  96 ++++++++++
 .../test/cppgmsh/get_physical_groups_Test.cc  |  82 +++++++++
 meshing/test/cppgmsh/get_points_of_Test.cc    |  69 +++++++
 .../cppgmsh/heal_periodic_domains_Test.cc     | 100 ++++++++++
 .../cppgmsh/heal_periodic_surfaces_Test.cc    |  95 ++++++++++
 meshing/test/cppgmsh/import_shapes_Test.cc    |  74 ++++++++
 .../test/cppgmsh/intersect_entities_Test.cc   |  99 ++++++++++
 meshing/test/cppgmsh/read_file_Test.cc        | 124 +++++++++++++
 meshing/test/cppgmsh/remove_entities_Test.cc  |  61 +++++++
 meshing/test/cppgmsh/rotate_entities_Test.cc  |  70 +++++++
 .../set_domain_entities_periodic_Test.cc      |  73 ++++++++
 meshing/test/cppgmsh/set_expert_mode_Test.cc  |  50 +++++
 meshing/test/cppgmsh/set_granularity_Test.cc  |  35 ++++
 meshing/test/cppgmsh/set_periodic_Test.cc     |  94 ++++++++++
 .../cppgmsh/set_physical_group_of_Test.cc     |  91 +++++++++
 meshing/test/cppgmsh/synchronize_Test.cc      |  43 +++++
 .../test/cppgmsh/translate_entities_Test.cc   |  90 +++++++++
 meshing/test/cppgmsh/write_file_Test.cc       | 124 +++++++++++++
 .../periodic_rve_mesh/definition.json         |  10 +
 .../periodic_rve_mesh/references/stdout.txt   |  21 +++
 .../examples/physical_groups/definition.json  |  10 +
 .../physical_groups/references/stdout.txt     |  80 ++++++++
 tests/examples/segment_mesh/definition.json   |  10 +
 .../segment_mesh/references/stdout.txt        |   2 +
 tests/examples/two_phase_cube/definition.json |  10 +
 .../two_phase_cube/references/stdout.txt      |  86 +++++++++
 160 files changed, 8578 insertions(+), 8 deletions(-)
 create mode 100644 cmake/modules/FindGmsh.cmake
 create mode 100644 cmake/modules/FindVoro++.cmake
 create mode 100644 examples/meshing/CMakeLists.txt
 create mode 100644 examples/meshing/GradedLattice.cc
 create mode 100644 examples/meshing/PeriodicRVEMesh.cc
 create mode 100644 examples/meshing/PhysicalGroups.cc
 create mode 100644 examples/meshing/SegmentMesh.cc
 create mode 100644 examples/meshing/TwoPhaseCube.cc
 create mode 100644 meshing/CMakeLists.txt
 create mode 100644 meshing/src/BoundaryRepresentation.cc
 create mode 100644 meshing/src/BoundingBox.cc
 create mode 100644 meshing/src/CMakeLists.txt
 create mode 100644 meshing/src/bounding_box_of.cc
 create mode 100644 meshing/src/construct_periodic_point_cloud.cc
 create mode 100644 meshing/src/construct_rectilinear_grid.cc
 create mode 100644 meshing/src/construct_voronoi_cell.cc
 create mode 100644 meshing/src/cppgmsh/Context.cc
 create mode 100644 meshing/src/cppgmsh/Node.cc
 create mode 100644 meshing/src/cppgmsh/as_affine_transform.cc
 create mode 100644 meshing/src/cppgmsh/construct_box.cc
 create mode 100644 meshing/src/cppgmsh/construct_cylinders.cc
 create mode 100644 meshing/src/cppgmsh/construct_polyhedron.cc
 create mode 100644 meshing/src/cppgmsh/construct_rectangle.cc
 create mode 100644 meshing/src/cppgmsh/copy_entities.cc
 create mode 100644 meshing/src/cppgmsh/extract_mesh.cc
 create mode 100644 meshing/src/cppgmsh/extrude_entities.cc
 create mode 100644 meshing/src/cppgmsh/extrude_surface.cc
 create mode 100644 meshing/src/cppgmsh/fragment_entities.cc
 create mode 100644 meshing/src/cppgmsh/fuse_entities.cc
 create mode 100644 meshing/src/cppgmsh/generate_mesh.cc
 create mode 100644 meshing/src/cppgmsh/get_boundary_of.cc
 create mode 100644 meshing/src/cppgmsh/get_centroid_of.cc
 create mode 100644 meshing/src/cppgmsh/get_coords_of.cc
 create mode 100644 meshing/src/cppgmsh/get_elements_in.cc
 create mode 100644 meshing/src/cppgmsh/get_entities_in.cc
 create mode 100644 meshing/src/cppgmsh/get_entities_of.cc
 create mode 100644 meshing/src/cppgmsh/get_nodes_of.cc
 create mode 100644 meshing/src/cppgmsh/get_normal_of.cc
 create mode 100644 meshing/src/cppgmsh/get_periodic_nodes_of.cc
 create mode 100644 meshing/src/cppgmsh/get_physical_groups.cc
 create mode 100644 meshing/src/cppgmsh/get_points_of.cc
 create mode 100644 meshing/src/cppgmsh/heal_periodic_domains.cc
 create mode 100644 meshing/src/cppgmsh/heal_periodic_surfaces.cc
 create mode 100644 meshing/src/cppgmsh/import_shapes.cc
 create mode 100644 meshing/src/cppgmsh/intersect_entities.cc
 create mode 100644 meshing/src/cppgmsh/read_file.cc
 create mode 100644 meshing/src/cppgmsh/remove_entities.cc
 create mode 100644 meshing/src/cppgmsh/rotate_entities.cc
 create mode 100644 meshing/src/cppgmsh/set_domain_entities_periodic.cc
 create mode 100644 meshing/src/cppgmsh/set_expert_mode.cc
 create mode 100644 meshing/src/cppgmsh/set_granularity.cc
 create mode 100644 meshing/src/cppgmsh/set_periodic.cc
 create mode 100644 meshing/src/cppgmsh/set_physical_group_of.cc
 create mode 100644 meshing/src/cppgmsh/synchronize.cc
 create mode 100644 meshing/src/cppgmsh/translate_entities.cc
 create mode 100644 meshing/src/cppgmsh/write_file.cc
 create mode 100644 meshing/src/include/ae108/meshing/BoundaryRepresentation.h
 create mode 100644 meshing/src/include/ae108/meshing/BoundingBox.h
 create mode 100644 meshing/src/include/ae108/meshing/bounding_box_of.h
 create mode 100644 meshing/src/include/ae108/meshing/construct_periodic_point_cloud.h
 create mode 100644 meshing/src/include/ae108/meshing/construct_rectilinear_grid.h
 create mode 100644 meshing/src/include/ae108/meshing/construct_voronoi_cell.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/Context.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/Node.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/as_affine_transform.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/construct_box.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/construct_cylinders.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/construct_polyhedron.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/construct_rectangle.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/copy_entities.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/extract_mesh.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/extrude_entities.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/extrude_surface.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/fragment_entities.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/fuse_entities.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/generate_mesh.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_boundary_of.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_centroid_of.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_coords_of.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_elements_in.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_entities_in.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_entities_of.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_nodes_of.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_normal_of.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_periodic_nodes_of.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_physical_groups.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/get_points_of.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/heal_periodic_domains.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/heal_periodic_surfaces.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/import_shapes.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/intersect_entities.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/read_file.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/remove_entities.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/rotate_entities.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/set_domain_entities_periodic.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/set_expert_mode.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/set_granularity.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/set_periodic.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/set_physical_group_of.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/synchronize.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/translate_entities.h
 create mode 100644 meshing/src/include/ae108/meshing/cppgmsh/write_file.h
 create mode 100644 meshing/test/BoundaryRepresentation_Test.cc
 create mode 100644 meshing/test/BoundingBox_Test.cc
 create mode 100644 meshing/test/CMakeLists.txt
 create mode 100644 meshing/test/bounding_box_of_Test.cc
 create mode 100644 meshing/test/construct_periodic_point_cloud_Test.cc
 create mode 100644 meshing/test/construct_rectilinear_grid_Test.cc
 create mode 100644 meshing/test/construct_voronoi_cell_Test.cc
 create mode 100644 meshing/test/cppgmsh/as_affine_transform_Test.cc
 create mode 100644 meshing/test/cppgmsh/construct_box_Test.cc
 create mode 100644 meshing/test/cppgmsh/construct_cylinders_Test.cc
 create mode 100644 meshing/test/cppgmsh/construct_polyhedron_Test.cc
 create mode 100644 meshing/test/cppgmsh/construct_rectangle_Test.cc
 create mode 100644 meshing/test/cppgmsh/copy_entities_Test.cc
 create mode 100644 meshing/test/cppgmsh/extract_mesh_Test.cc
 create mode 100644 meshing/test/cppgmsh/extrude_entities_Test.cc
 create mode 100644 meshing/test/cppgmsh/extrude_surface_Test.cc
 create mode 100644 meshing/test/cppgmsh/fragment_entities_Test.cc
 create mode 100644 meshing/test/cppgmsh/fuse_entities_Test.cc
 create mode 100644 meshing/test/cppgmsh/generate_mesh_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_boundary_of_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_centroid_of_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_coords_of_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_elements_in_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_entities_in_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_entities_of_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_nodes_of_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_normal_of_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_periodic_nodes_of_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_physical_groups_Test.cc
 create mode 100644 meshing/test/cppgmsh/get_points_of_Test.cc
 create mode 100644 meshing/test/cppgmsh/heal_periodic_domains_Test.cc
 create mode 100644 meshing/test/cppgmsh/heal_periodic_surfaces_Test.cc
 create mode 100644 meshing/test/cppgmsh/import_shapes_Test.cc
 create mode 100644 meshing/test/cppgmsh/intersect_entities_Test.cc
 create mode 100644 meshing/test/cppgmsh/read_file_Test.cc
 create mode 100644 meshing/test/cppgmsh/remove_entities_Test.cc
 create mode 100644 meshing/test/cppgmsh/rotate_entities_Test.cc
 create mode 100644 meshing/test/cppgmsh/set_domain_entities_periodic_Test.cc
 create mode 100644 meshing/test/cppgmsh/set_expert_mode_Test.cc
 create mode 100644 meshing/test/cppgmsh/set_granularity_Test.cc
 create mode 100644 meshing/test/cppgmsh/set_periodic_Test.cc
 create mode 100644 meshing/test/cppgmsh/set_physical_group_of_Test.cc
 create mode 100644 meshing/test/cppgmsh/synchronize_Test.cc
 create mode 100644 meshing/test/cppgmsh/translate_entities_Test.cc
 create mode 100644 meshing/test/cppgmsh/write_file_Test.cc
 create mode 100644 tests/examples/periodic_rve_mesh/definition.json
 create mode 100644 tests/examples/periodic_rve_mesh/references/stdout.txt
 create mode 100644 tests/examples/physical_groups/definition.json
 create mode 100644 tests/examples/physical_groups/references/stdout.txt
 create mode 100644 tests/examples/segment_mesh/definition.json
 create mode 100644 tests/examples/segment_mesh/references/stdout.txt
 create mode 100644 tests/examples/two_phase_cube/definition.json
 create mode 100644 tests/examples/two_phase_cube/references/stdout.txt

diff --git a/.gitignore b/.gitignore
index 7f95e654..57b3e576 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,5 @@
 # along with ae108. If not, see <https://www.gnu.org/licenses/>.
 
 build
-*.pyc
\ No newline at end of file
+*.pyc
+.vscode
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 391692f3..1c45e3c2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,7 +27,7 @@ include(cmake/ae108_install_library.cmake)
 include(cmake/ae108_add_cppptest.cmake)
 
 add_subdirectory(cppptest)
-foreach(AE108_LIBRARY elements cpppetsc cppslepc assembly solve cmdline)
+foreach(AE108_LIBRARY elements cpppetsc cppslepc assembly solve cmdline meshing)
     add_subdirectory(${AE108_LIBRARY})
 endforeach()
 add_subdirectory(examples)
@@ -50,6 +50,8 @@ install(FILES
         cmake/modules/FindAE108_PETSc.cmake
         cmake/modules/FindAE108_SLEPc.cmake
         cmake/modules/AE108_PETSc.cc
+        cmake/modules/FindVoro++.cmake
+        cmake/modules/FindGmsh.cmake
         DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/modules"
 )
 
@@ -65,7 +67,7 @@ set(CPACK_GENERATOR "DEB")
 set(CPACK_PACKAGE_NAME "libae108-${AE108_PETSC_SCALAR_TYPE}-dev")
 set(CPACK_DEBIAN_PACKAGE_SECTION "science")
 set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")
-set(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-program-options-dev,libeigen3-dev,librange-v3-dev,libxrender1,libvtk9-dev,libpetsc-${AE108_PETSC_SCALAR_TYPE}-dev,libslepc-${AE108_PETSC_SCALAR_TYPE}3.15-dev")
+set(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-program-options-dev,libeigen3-dev,librange-v3-dev,libxrender1,libvtk9-dev,libpetsc-${AE108_PETSC_SCALAR_TYPE}-dev,libslepc-${AE108_PETSC_SCALAR_TYPE}3.15-dev,libgmsh-dev,voro++-dev")
 set(CPACK_PACKAGE_DESCRIPTION "C++ foundation for computational solid mechanics simulations using a variational framework, primarily focusing on the Finite Element Method (FEM)")
 set(CPACK_PACKAGE_CONTACT "Mechanics & Materials Lab at ETH Zurich <gerhard.braeunlich@id.ethz.ch>")
 
diff --git a/cmake/modules/FindGmsh.cmake b/cmake/modules/FindGmsh.cmake
new file mode 100644
index 00000000..ae912235
--- /dev/null
+++ b/cmake/modules/FindGmsh.cmake
@@ -0,0 +1,35 @@
+# © 2021 ETH Zurich, Mechanics and Materials Lab
+#
+# Licensed under the GNU General Public License (GPL), 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
+#
+# https:#www.gnu.org/licenses/old-licenses/gpl-2.0.html
+#
+# 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.
+
+if (NOT TARGET external::gmsh)
+  include(FindPackageHandleStandardArgs)
+
+  if (NOT DEFINED ENV{GMSH_LIBRARIES} OR NOT DEFINED ENV{GMSH_INCLUDE_DIRS})
+    find_library(Gmsh_LIBRARIES NAMES gmsh)
+    find_path(Gmsh_INCLUDE_DIRS gmsh.h)
+  else()
+    find_library(Gmsh_LIBRARIES NAMES gmsh PATHS $ENV{GMSH_LIBRARIES} NO_DEFAULT_PATH)
+    find_path(Gmsh_INCLUDE_DIRS gmsh.h PATHS $ENV{GMSH_INCLUDE_DIRS} NO_DEFAULT_PATH)
+  endif()
+
+  find_package_handle_standard_args(Gmsh Gmsh_LIBRARIES Gmsh_INCLUDE_DIRS)
+
+  if (Gmsh_FOUND)
+    add_library(external::gmsh UNKNOWN IMPORTED)
+    set_property(TARGET external::gmsh PROPERTY IMPORTED_LOCATION ${Gmsh_LIBRARIES})
+    set_property(TARGET external::gmsh PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Gmsh_INCLUDE_DIRS})
+  endif()
+endif()
+
+
diff --git a/cmake/modules/FindVoro++.cmake b/cmake/modules/FindVoro++.cmake
new file mode 100644
index 00000000..fa2cfdcc
--- /dev/null
+++ b/cmake/modules/FindVoro++.cmake
@@ -0,0 +1,35 @@
+# © 2021 ETH Zurich, Mechanics and Materials Lab
+#
+# This file is part of ae108.
+#
+# ae108 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or any
+# later version.
+#
+# ae108 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+if (NOT TARGET external::voro++)
+  include(FindPackageHandleStandardArgs)
+  if (NOT DEFINED ENV{Voro++_LIBRARIES} OR NOT DEFINED ENV{Voro++_INCLUDE_DIRS})
+    find_library(Voro++_LIBRARIES NAMES voro++ PATHS $ENV{VOROPP_DIR}/lib)
+    find_path(Voro++_INCLUDE_DIRS voro++.hh PATH_SUFFIXES voro++ PATHS $ENV{VOROPP_DIR}/include/voro++)
+  else()
+    find_library(Voro++_LIBRARIES NAMES voro++ PATHS $ENV{Voro++_LIBRARIES} NO_DEFAULT_PATH)
+    find_path(Voro++_INCLUDE_DIRS voro++.hh PATH_SUFFIXES voro++ PATHS $ENV{Voro++_INCLUDE_DIRS} NO_DEFAULT_PATH)
+  endif()
+
+  find_package_handle_standard_args(Voro++ DEFAULT_MSG Voro++_LIBRARIES Voro++_INCLUDE_DIRS)
+
+  if (Voro++_FOUND)
+    add_library(external::voro++ UNKNOWN IMPORTED)
+    set_property(TARGET external::voro++ PROPERTY IMPORTED_LOCATION ${Voro++_LIBRARIES})
+    set_property(TARGET external::voro++ PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Voro++_INCLUDE_DIRS})
+  endif()
+endif()
diff --git a/docker-compose.yml b/docker-compose.yml
index 2911fc9a..1317d71b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -24,7 +24,7 @@ services:
       context: ./docker
       dockerfile: Dockerfile
     volumes:
-    - .:/mnt/io
+      - .:/mnt/io
     tty: true
     shm_size: '1gb'
     working_dir: /mnt/io
@@ -35,7 +35,7 @@ services:
       context: ./docker
       dockerfile: Dockerfile
     volumes:
-    - .:/mnt/io
+      - .:/mnt/io
     working_dir: /mnt/io
     tty: true
     shm_size: '1gb'
@@ -44,6 +44,6 @@ services:
       context: ./docker
       dockerfile: Dockerfile_docu
     volumes:
-    - .:/mnt/io
+      - .:/mnt/io
     working_dir: /mnt/io
-    tty: true
\ No newline at end of file
+    tty: true
diff --git a/docker/Dockerfile b/docker/Dockerfile
index a7c772f6..6bf11cea 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -38,7 +38,14 @@ RUN apt-get update && \
   libxrender1 \
   ninja-build \
   python3 python3-pip \
-  python3-scipy
+  python3-scipy 
+
+RUN apt-get update && \
+  DEBIAN_FRONTEND="noninteractive" \
+  TZ="Europe/Zurich" \
+  apt-get install -y \
+  libgmsh-dev \
+  voro++-dev
 
 RUN pip3 install \
   mypy==0.931 \
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 66a9d562..49ba9694 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -156,3 +156,5 @@ if (NOT ${AE108_PETSC_COMPLEX})
                           PRIVATE ae108::elements
     )
 endif()
+
+add_subdirectory(meshing)
\ No newline at end of file
diff --git a/examples/meshing/CMakeLists.txt b/examples/meshing/CMakeLists.txt
new file mode 100644
index 00000000..e7b99ee3
--- /dev/null
+++ b/examples/meshing/CMakeLists.txt
@@ -0,0 +1,48 @@
+# © 2020 ETH Zurich, Mechanics and Materials Lab
+#
+# This file is part of ae108.
+#
+# ae108 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or any
+# later version.
+#
+# ae108 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+project(ae108-examples LANGUAGES C CXX)
+
+if (NOT TARGET ae108::meshing)
+    find_package(ae108 REQUIRED)
+endif()
+
+add_executable(${PROJECT_NAME}-SegmentMesh SegmentMesh.cc)
+target_link_libraries(${PROJECT_NAME}-SegmentMesh
+                        PRIVATE ae108::meshing
+)
+
+add_executable(${PROJECT_NAME}-PeriodicRVEMesh PeriodicRVEMesh.cc)
+target_link_libraries(${PROJECT_NAME}-PeriodicRVEMesh
+                        PRIVATE ae108::meshing
+)
+
+add_executable(${PROJECT_NAME}-TwoPhaseCube TwoPhaseCube.cc)
+target_link_libraries(${PROJECT_NAME}-TwoPhaseCube
+                        PRIVATE ae108::meshing
+)
+
+add_executable(${PROJECT_NAME}-PhysicalGroups PhysicalGroups.cc)
+target_link_libraries(${PROJECT_NAME}-PhysicalGroups
+                        PRIVATE ae108::meshing
+)
+add_executable(${PROJECT_NAME}-GradedLattice GradedLattice.cc)
+target_link_libraries(${PROJECT_NAME}-GradedLattice
+                        PRIVATE ae108::meshing
+)
diff --git a/examples/meshing/GradedLattice.cc b/examples/meshing/GradedLattice.cc
new file mode 100644
index 00000000..e00f9cf4
--- /dev/null
+++ b/examples/meshing/GradedLattice.cc
@@ -0,0 +1,98 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/construct_rectilinear_grid.h"
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/extract_mesh.h"
+#include "ae108/meshing/cppgmsh/extrude_surface.h"
+#include "ae108/meshing/cppgmsh/fuse_entities.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/remove_entities.h"
+#include "ae108/meshing/cppgmsh/rotate_entities.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include "ae108/meshing/cppgmsh/translate_entities.h"
+#include "ae108/meshing/cppgmsh/write_file.h"
+
+#include <gmsh.h>
+#include <iostream>
+#include <numeric>
+
+using namespace ae108::meshing;
+
+constexpr std::size_t dimension = 3;
+using Point = std::array<double, dimension>;
+
+int main(int argc, char **argv) {
+
+  const auto gmshContext = cppgmsh::Context(argc, argv, true, false);
+
+  const double gamma_start = 5.;
+  const double gamma_end = 10.;
+  const double length = 466.75;
+
+  const auto xcoords = [] {
+    std::vector<double> range(80);
+    std::iota(range.begin(), range.end(), -40);
+    std::transform(range.begin(), range.end(), range.begin(),
+                   [](auto &N) { return N * 5; });
+    return range;
+  }();
+
+  const auto ycoords = [&] {
+    std::vector<double> range(59);
+    std::iota(range.begin(), range.end(), -29);
+    std::transform(range.begin(), range.end(), range.begin(), [&](auto &N) {
+      return N * length * gamma_start /
+             (length - fabs(N) * gamma_end + fabs(N) * gamma_start);
+    });
+    return range;
+  }();
+
+  const auto crosssection =
+      cppgmsh::construct_rectangle({-.25, -.5, 0}, {.5, 1.});
+  cppgmsh::rotate_entities({crosssection}, {0, 0, 0}, {1, 0, 0}, M_PI_2);
+
+  std::vector<std::pair<int, int>> entities;
+  for (const auto &xcoord : xcoords)
+    entities.push_back(cppgmsh::extrude_surface(
+        crosssection.second, {xcoord, -200, 0}, {xcoord, 200, 0}, {0, 1, 0}));
+  for (const auto &ycoord : ycoords)
+    entities.push_back(cppgmsh::extrude_surface(
+        crosssection.second, {-200, ycoord, 0}, {200, ycoord, 0}, {0, 1, 0}));
+
+  entities.push_back(cppgmsh::construct_box({-225, -225, -.5}, {450, 25, 1}));
+  entities.push_back(cppgmsh::construct_box({-225, -225, -.5}, {25, 450, 1}));
+  entities.push_back(cppgmsh::construct_box({-225, 200, -.5}, {450, 25, 1}));
+  entities.push_back(cppgmsh::construct_box({200, -225, -.5}, {25, 450, 1}));
+
+  cppgmsh::remove_entities({crosssection}, true);
+
+  cppgmsh::fuse_entities(entities);
+
+  cppgmsh::synchronize();
+  cppgmsh::write_file("gradedLattice.step");
+
+  cppgmsh::generate_mesh();
+  cppgmsh::write_file("gradedLattice.vtk");
+
+  const auto [positions, connectivity, _, __] =
+      cppgmsh::extract_mesh<dimension>();
+
+  std::cout << "# points: " << positions.size() << std::endl
+            << "# elements: " << connectivity.size() << std::endl;
+}
\ No newline at end of file
diff --git a/examples/meshing/PeriodicRVEMesh.cc b/examples/meshing/PeriodicRVEMesh.cc
new file mode 100644
index 00000000..5d0b3bb7
--- /dev/null
+++ b/examples/meshing/PeriodicRVEMesh.cc
@@ -0,0 +1,129 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/bounding_box_of.h"
+#include "ae108/meshing/construct_periodic_point_cloud.h"
+#include "ae108/meshing/construct_voronoi_cell.h"
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_cylinders.h"
+#include "ae108/meshing/cppgmsh/construct_polyhedron.h"
+#include "ae108/meshing/cppgmsh/extract_mesh.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/get_entities_in.h"
+#include "ae108/meshing/cppgmsh/get_periodic_nodes_of.h"
+#include "ae108/meshing/cppgmsh/intersect_entities.h"
+#include "ae108/meshing/cppgmsh/set_domain_entities_periodic.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include "ae108/meshing/cppgmsh/write_file.h"
+
+#include <iostream>
+
+using namespace ae108::meshing;
+
+// In this example we will generate a periodic mesh of a representative volume
+// element as it is often required for homogenization or Bloch wave analysis of
+// a periodic medium.
+
+constexpr std::size_t dimension = 3;
+using Point = std::array<double, dimension>;
+
+// A 3D periodic medium with translational symmetry is commonly described as a
+// Bravais lattice by three translation vectors. Three well-known lattices are
+// the simple cubic (sc), the body centered cubic (bcc), and the face centered
+// cubic (fcc) lattice. https://en.wikipedia.org/wiki/Bravais_lattice
+
+constexpr auto lattice_vectors =
+    std::array<std::array<double, dimension>, dimension>{
+        {{0.5, 0.5, 0.5}, {0.5, -0.5, 0.5}, {0.5, 0.5, -0.5}}}; // bcc
+//      {{1., 1., 0.}, {0., 1., 1.}, {1., 0., 1.}}}; //fcc
+//      {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}}; //sc
+
+int main(int argc, char **argv) {
+
+  // In a first step, we need to initialize the meshing engine "cppgmsh"
+
+  const auto gmshContext = cppgmsh::Context(argc, argv);
+
+  // Let us now define the domain of our representative volume element (rve).
+  // For a perodic medium, like above Bravais lattice, the smallest possible
+  // unit cell is a primitive unit cell: Two frequent choices are the
+  // parallelpiped or the Wigner-Seitz (a type of Voronoi cell) primitive cell.
+  // https://en.wikipedia.org/wiki/Unit_cell
+
+  std::vector<std::pair<std::size_t, std::size_t>> periodic_faces;
+  const auto domain = construct_voronoi_cell(
+      construct_periodic_point_cloud(lattice_vectors, {0, 0, 0}),
+      &periodic_faces);
+  const auto domain_entity = cppgmsh::construct_polyhedron(domain);
+
+  // Next, we define the solid within the primitive unit cell. We choose to
+  // place cylinders on the edges of the domain. The solid may also span several
+  // unit cells like in a finite lattice.
+
+  const auto solid_entity = cppgmsh::construct_cylinders(
+      domain.vertices, domain.edges,
+      std::vector<double>(domain.edges.size(), 0.05), true);
+
+  // Finally, we construct the rve by cropping all solid outside of the
+  // domain of the primitive unit cell.
+
+  const auto rve_entity =
+      cppgmsh::intersect_entities({solid_entity}, {domain_entity});
+
+  // We sync the constructive solid geometry engine and write a CAD file.
+
+  cppgmsh::synchronize();
+  //   cppgmsh::write_file("periodic.step");
+
+  // To obtain a periodic mesh (e.g. for homogenization or Bloch wave analysis),
+  // we define the periodic domain faces.
+
+  for (const auto &face_pair : periodic_faces)
+    cppgmsh::set_domain_entities_periodic(
+        bounding_box_of(domain.vertices_of_face(face_pair.first)),
+        bounding_box_of(domain.vertices_of_face(face_pair.second)), 2);
+
+  // Finally, we generate the periodic FE mesh.
+
+  cppgmsh::set_granularity(1. / 10);
+  cppgmsh::generate_mesh();
+
+  // We save the mesh directly as "periodic.vtk". Other formats are available.
+
+  cppgmsh::write_file("periodic.vtk");
+
+  // To use the mesh in an ae108 FE application, we extract the positions and
+  // connectivities of the mesh elements.
+
+  const auto [positions, connectivity, nodeTagToIndex, elementTagToIndex] =
+      cppgmsh::extract_mesh<dimension>();
+
+  for (const auto &face_pair : periodic_faces) {
+    std::pair<int, int> numper_of_nodes;
+    for (const auto &target_surface : cppgmsh::get_entities_in(
+             bounding_box_of(domain.vertices_of_face(face_pair.second)), 2)) {
+      const auto nodes = cppgmsh::get_periodic_nodes_of(target_surface);
+      assert(nodes.first.size() == nodes.second.size());
+      numper_of_nodes.first += nodes.first.size();
+      numper_of_nodes.second += nodes.second.size();
+    }
+    std::cout << "# target face (" << face_pair.second
+              << ") nodes: " << numper_of_nodes.second << std::endl
+              << "# source face (" << face_pair.first
+              << ") nodes: " << numper_of_nodes.first << std::endl
+              << std::endl;
+  }
+}
diff --git a/examples/meshing/PhysicalGroups.cc b/examples/meshing/PhysicalGroups.cc
new file mode 100644
index 00000000..574c559a
--- /dev/null
+++ b/examples/meshing/PhysicalGroups.cc
@@ -0,0 +1,143 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/extract_mesh.h"
+#include "ae108/meshing/cppgmsh/fragment_entities.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/get_elements_in.h"
+#include "ae108/meshing/cppgmsh/get_entities_in.h"
+#include "ae108/meshing/cppgmsh/get_nodes_of.h"
+#include "ae108/meshing/cppgmsh/get_physical_groups.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/set_physical_group_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+
+#include <array>
+#include <iostream>
+
+using namespace ae108::meshing;
+
+/*
+In this example we will construct and mesh a cube with unit side lengths, which
+consists of three layers (z < 0.3 -> layer 1, 0.3 < z < 0.7 -> layer 2, z > 0.7
+-> layer 3). Layers 1 and 3 are of one material, layer 2 of another. In order to
+account for this, physical groups are defined.
+
+     ________________    _
+    |                |  |
+    |   Material 1   |  | 0.3
+    |________________|  |_
+    |                |  |
+    |   Material 2   |  | 0.4
+    |                |  |
+    |________________|  |_
+    |                |  |
+    |   Material 1   |  | 0.3
+    |________________|  |_
+     ________________
+    |       1.0      |
+
+ z
+ ^
+ |
+    --> x,y
+
+*/
+
+int main(int argc, char **argv) {
+
+  // Initialize the gmsh context:
+  const auto gmshContext = cppgmsh::Context(argc, argv, true, false);
+
+  // Construct three boxes:
+  const auto boxes = std::vector<std::pair<int, int>>{
+      cppgmsh::construct_box({0., 0., 0.}, {1., 1., 0.3}),
+      cppgmsh::construct_box({0., 0., .3}, {1., 1., 0.4}),
+      cppgmsh::construct_box({0., 0., .7}, {1., 1., 0.3})};
+
+  std::cout << "Box 1: dim = " << boxes[0].first
+            << ", tag = " << boxes[0].second << std::endl
+            << "Box 2: dim = " << boxes[1].first
+            << ", tag = " << boxes[1].second << std::endl
+            << "Box 3: dim = " << boxes[2].first
+            << ", tag = " << boxes[2].second << std::endl;
+
+  // Fuse boxes, while keeping track of the original entities:
+  std::vector<std::pair<int, int>> fused_entitites =
+      cppgmsh::fragment_entities(boxes);
+  cppgmsh::synchronize();
+
+  // Define physical groups:
+  cppgmsh::set_physical_group_of({boxes[0], boxes[2]});
+  cppgmsh::set_physical_group_of({boxes[1]});
+
+  // Show that the physical groups have been defined as desired:
+  auto groups = cppgmsh::get_physical_groups();
+  std::cout << "Groups (dim, tag): ";
+  for (const auto &group : groups)
+    std::cout << "(" << group.first << "," << group.second << ") ";
+  std::cout << std::endl;
+
+  // Get the tags of the entities attributed two both groups:
+  for (const auto &group : groups) {
+    std::cout << "Group " << group.second << ", dim: " << group.first
+              << ", entities: ";
+    const auto entities = cppgmsh::get_entities_in(group);
+    for (const auto &entity : entities)
+      std::cout << entity.second << " ";
+    std::cout << std::endl;
+  }
+
+  // Create mesh:
+  cppgmsh::set_granularity(2.);
+  cppgmsh::generate_mesh(3, 1, 6);
+
+  const auto [positions, connectivity, nodeTagToIndex, elementTagToIndex] =
+      cppgmsh::extract_mesh<3>();
+
+  // Print element tags and indides, as well as node indides for the two groups:
+  for (const auto &group : groups) {
+    std::cout << "Elements in group " << group.second << ":\n";
+    std::cout << "\ttag\tindex\tnode indides\n";
+    const auto entities = cppgmsh::get_entities_in(group);
+    for (const auto &entity : entities) {
+      const auto elements = cppgmsh::get_elements_in(entity);
+      for (const auto &element : elements) {
+        const auto index = elementTagToIndex.at(element.second);
+        std::cout << "\t" << element.second << "\t" << index << "\t";
+        for (const auto &node : connectivity[index])
+          std::cout << node << "\t";
+        std::cout << std::endl;
+      }
+    }
+  }
+
+  // Print node tags, indides and coordinates for the two boxes:
+  for (const auto &group : groups) {
+    std::cout << "Nodes in group " << group.second << ":\n";
+    std::cout << "\ttag\tindex\tx\ty\tz\n";
+    const auto entities = cppgmsh::get_entities_in(group);
+    for (const auto &entity : entities) {
+      const auto nodes = cppgmsh::get_nodes_of<3>(entity);
+      for (const auto &node : nodes) {
+        std::cout << "\t" << node.id << "\t" << nodeTagToIndex.at(node.id)
+                  << "\t" << node.position[0] << "\t" << node.position[1]
+                  << "\t" << node.position[2] << std::endl;
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/examples/meshing/SegmentMesh.cc b/examples/meshing/SegmentMesh.cc
new file mode 100644
index 00000000..428150f0
--- /dev/null
+++ b/examples/meshing/SegmentMesh.cc
@@ -0,0 +1,73 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/construct_rectilinear_grid.h"
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/extract_mesh.h"
+#include "ae108/meshing/cppgmsh/extrude_surface.h"
+#include "ae108/meshing/cppgmsh/fuse_entities.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/remove_entities.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include "ae108/meshing/cppgmsh/write_file.h"
+
+#include <gmsh.h>
+#include <iostream>
+
+using namespace ae108::meshing;
+
+constexpr std::size_t dimension = 3;
+using Point = std::array<double, dimension>;
+
+int main(int argc, char **argv) {
+
+  const auto gmshContext = cppgmsh::Context(argc, argv, true, false);
+
+  // construct a regular grid
+  //    *--*
+  //    |  |
+  //    *--*
+
+  const auto coordinates =
+      std::array<std::vector<double>, dimension>{{{0, 1}, {0, 1}, {0}}};
+  auto [nodes, segments] = construct_rectilinear_grid(coordinates);
+
+  const auto crosssection =
+      cppgmsh::construct_rectangle({-.05, -.05, 0}, {.1, .1});
+
+  std::vector<std::pair<int, int>> beams;
+  for (const auto &segment : segments)
+    beams.push_back(cppgmsh::extrude_surface(
+        crosssection.second, nodes[segment[0]], nodes[segment[1]]));
+
+  cppgmsh::remove_entities({crosssection}, true);
+
+  cppgmsh::fuse_entities(beams);
+
+  cppgmsh::synchronize();
+  //   cppgmsh::write_file("segmentMesh.step");
+
+  cppgmsh::set_granularity(0.25);
+  cppgmsh::generate_mesh();
+  cppgmsh::write_file("segmentMesh.vtk");
+
+  const auto [positions, connectivity, _, __] =
+      cppgmsh::extract_mesh<dimension>();
+
+  std::cout << "# points: " << positions.size() << std::endl
+            << "# elements: " << connectivity.size() << std::endl;
+}
\ No newline at end of file
diff --git a/examples/meshing/TwoPhaseCube.cc b/examples/meshing/TwoPhaseCube.cc
new file mode 100644
index 00000000..eafa734d
--- /dev/null
+++ b/examples/meshing/TwoPhaseCube.cc
@@ -0,0 +1,102 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/extract_mesh.h"
+#include "ae108/meshing/cppgmsh/fragment_entities.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/get_elements_in.h"
+#include "ae108/meshing/cppgmsh/get_nodes_of.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+
+#include <array>
+#include <iostream>
+
+using namespace ae108::meshing;
+
+/*
+In this example we will construct and mesh a cube with unit side lengths, which
+consists of two different phases (z < 0.5 -> phase 1,z > 0.5 -> phase 2).
+     ________________    _
+    |                |  |
+    |     Phase 2    |  | 0.5
+    |________________|  |_
+    |                |  |
+    |     Phase 1    |  | 0.5
+    |________________|  |_
+     ________________
+    |       1.0      |
+ z
+ ^
+ |
+    --> x,y
+
+*/
+
+int main(int argc, char **argv) {
+
+  // Initialize the gmsh context:
+  const auto gmshContext = cppgmsh::Context(argc, argv, true, false);
+
+  // Construct two boxes:
+  const auto boxes = std::vector<std::pair<int, int>>{
+      cppgmsh::construct_box({0., 0., 0.}, {1., 1., 0.5}),
+      cppgmsh::construct_box({0., 0., .5}, {1., 1., 0.5})};
+
+  std::cout << "Box 1: dim = " << boxes[0].first
+            << ", tag = " << boxes[0].second << std::endl
+            << "Box 2: dim = " << boxes[1].first
+            << ", tag = " << boxes[1].second << std::endl;
+
+  // Fuse boxes, while keeping track of the original entities:
+  const auto fused_entitites = cppgmsh::fragment_entities(boxes);
+  cppgmsh::synchronize();
+
+  // Create mesh:
+  cppgmsh::set_granularity(2.);
+  cppgmsh::generate_mesh(3, 1, 6);
+
+  const auto [positions, connectivity, nodeTagToIndex, elementTagToIndex] =
+      cppgmsh::extract_mesh<3>();
+
+  // Print element tags and indides, as well as node indides for the two boxes:
+  for (const auto &box : boxes) {
+    std::cout << "Elements in box " << box.second << ":\n"
+              << "\ttag\tindex\tnode indices\n";
+
+    const auto elements = cppgmsh::get_elements_in(box);
+    for (const auto &element : elements) {
+      const auto index = elementTagToIndex.at(element.second);
+      std::cout << "\t" << element.second << "\t" << index << "\t";
+      for (const auto &node : connectivity[index])
+        std::cout << node << "\t";
+      std::cout << std::endl;
+    }
+  }
+
+  // Print node tags, indides and coordinates for the two boxes:
+  for (const auto &box : boxes) {
+    std::cout << "Nodes in box " << box.second << ":\n"
+              << "\ttag\tindex\tx\ty\tz\n";
+
+    const auto nodes = cppgmsh::get_nodes_of<3>(box);
+    for (const auto &node : nodes)
+      std::cout << "\t" << node.id << "\t" << nodeTagToIndex.at(node.id) << "\t"
+                << node.position[0] << "\t" << node.position[1] << "\t"
+                << node.position[2] << std::endl;
+  }
+}
\ No newline at end of file
diff --git a/meshing/CMakeLists.txt b/meshing/CMakeLists.txt
new file mode 100644
index 00000000..78211fae
--- /dev/null
+++ b/meshing/CMakeLists.txt
@@ -0,0 +1,21 @@
+# © 2021 ETH Zurich, Mechanics and Materials Lab
+#
+# This file is part of ae108.
+#
+# ae108 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or any
+# later version.
+#
+# ae108 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+add_subdirectory(src)
+add_subdirectory(test)
\ No newline at end of file
diff --git a/meshing/src/BoundaryRepresentation.cc b/meshing/src/BoundaryRepresentation.cc
new file mode 100644
index 00000000..004a2bb4
--- /dev/null
+++ b/meshing/src/BoundaryRepresentation.cc
@@ -0,0 +1,16 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/BoundaryRepresentation.h"
\ No newline at end of file
diff --git a/meshing/src/BoundingBox.cc b/meshing/src/BoundingBox.cc
new file mode 100644
index 00000000..cdbf1412
--- /dev/null
+++ b/meshing/src/BoundingBox.cc
@@ -0,0 +1,16 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/BoundingBox.h"
\ No newline at end of file
diff --git a/meshing/src/CMakeLists.txt b/meshing/src/CMakeLists.txt
new file mode 100644
index 00000000..876ecb62
--- /dev/null
+++ b/meshing/src/CMakeLists.txt
@@ -0,0 +1,89 @@
+# © 2021 ETH Zurich, Mechanics and Materials Lab
+#
+# This file is part of ae108.
+#
+# ae108 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or any
+# later version.
+#
+# ae108 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+add_library(${PROJECT_NAME}-meshing
+    cppgmsh/Context.cc
+    cppgmsh/Node.cc
+    cppgmsh/as_affine_transform.cc
+    cppgmsh/construct_box.cc
+    cppgmsh/construct_cylinders.cc
+    cppgmsh/construct_polyhedron.cc
+    cppgmsh/construct_rectangle.cc
+    cppgmsh/copy_entities.cc
+    cppgmsh/extrude_entities.cc
+    cppgmsh/extrude_surface.cc
+    cppgmsh/fuse_entities.cc
+    cppgmsh/fragment_entities.cc
+    cppgmsh/intersect_entities.cc
+    cppgmsh/remove_entities.cc
+    cppgmsh/rotate_entities.cc
+    cppgmsh/translate_entities.cc
+    cppgmsh/get_boundary_of.cc
+    cppgmsh/get_centroid_of.cc
+    cppgmsh/get_coords_of.cc
+    cppgmsh/get_elements_in.cc
+    cppgmsh/get_entities_in.cc
+    cppgmsh/get_entities_of.cc
+    cppgmsh/get_nodes_of.cc
+    cppgmsh/get_normal_of.cc
+    cppgmsh/get_periodic_nodes_of.cc
+    cppgmsh/get_physical_groups.cc
+    cppgmsh/get_points_of.cc
+    cppgmsh/set_domain_entities_periodic.cc
+    cppgmsh/set_expert_mode.cc
+    cppgmsh/set_granularity.cc
+    cppgmsh/set_periodic.cc
+    cppgmsh/set_physical_group_of.cc
+    cppgmsh/heal_periodic_surfaces.cc
+    cppgmsh/heal_periodic_domains.cc
+    cppgmsh/import_shapes.cc
+    cppgmsh/read_file.cc
+    cppgmsh/write_file.cc
+    cppgmsh/extract_mesh.cc
+    cppgmsh/generate_mesh.cc
+    cppgmsh/synchronize.cc
+    BoundaryRepresentation.cc
+    BoundingBox.cc
+    bounding_box_of.cc
+    construct_rectilinear_grid.cc
+    construct_periodic_point_cloud.cc
+    construct_voronoi_cell.cc
+)
+
+target_include_directories(${PROJECT_NAME}-meshing PUBLIC
+                           $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+)
+
+target_compile_features(${PROJECT_NAME}-meshing
+                        PUBLIC cxx_std_11
+)
+
+find_package(Gmsh 4.8.4 REQUIRED MODULE)
+find_package(Voro++ REQUIRED MODULE)
+find_package(Eigen3 3.3 CONFIG REQUIRED)
+find_package(range-v3 0.11.0 CONFIG REQUIRED)
+target_link_libraries(${PROJECT_NAME}-meshing 
+                            PUBLIC external::gmsh
+                            PUBLIC external::voro++
+                            PUBLIC Eigen3::Eigen
+                            PUBLIC range-v3::range-v3)
+
+add_library(${PROJECT_NAME}::meshing ALIAS ${PROJECT_NAME}-meshing)
+
+ae108_install_library(meshing)
\ No newline at end of file
diff --git a/meshing/src/bounding_box_of.cc b/meshing/src/bounding_box_of.cc
new file mode 100644
index 00000000..0f2c9217
--- /dev/null
+++ b/meshing/src/bounding_box_of.cc
@@ -0,0 +1,16 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/bounding_box_of.h"
\ No newline at end of file
diff --git a/meshing/src/construct_periodic_point_cloud.cc b/meshing/src/construct_periodic_point_cloud.cc
new file mode 100644
index 00000000..cea48c8f
--- /dev/null
+++ b/meshing/src/construct_periodic_point_cloud.cc
@@ -0,0 +1,75 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/construct_periodic_point_cloud.h"
+#include <Eigen/Dense>
+#include <range/v3/view/cartesian_product.hpp>
+#include <range/v3/view/zip.hpp>
+
+namespace ae108 {
+namespace meshing {
+
+constexpr auto get_array = [](auto &&...x) {
+  return std::array{std::forward<decltype(x)>(x)...};
+};
+
+template <std::size_t Dimension>
+const auto construct_point_cloud =
+    [](const std::array<std::array<double, Dimension>, Dimension> &translations,
+       const std::array<double, Dimension> &origin, const auto &permutations) {
+      std::vector<std::array<double, Dimension>> pointCloud(
+          permutations.size());
+      for (auto &&[point, tuple] :
+           ranges::views::zip(pointCloud, permutations)) {
+        const auto permutation = std::apply(get_array, tuple);
+        Eigen::Map<Eigen::Matrix<double, Dimension, 1>>(point.data()) =
+            Eigen::Map<const Eigen::Matrix<double, Dimension, 1>>(
+                origin.data()) +
+            (Eigen::Map<const Eigen::Matrix<double, Dimension, Dimension,
+                                            Eigen::ColMajor>>(
+                 translations.front().data()) *
+             Eigen::Map<const Eigen::Matrix<double, Dimension, 1>>(
+                 permutation.data()));
+      }
+      return pointCloud;
+    };
+
+template <>
+std::vector<std::array<double, 3>> construct_periodic_point_cloud(
+    const std::array<std::array<double, 3>, 3> &translations,
+    std::array<double, 3> origin, std::vector<double> set) noexcept {
+  return construct_point_cloud<3>(
+      translations, origin, ranges::views::cartesian_product(set, set, set));
+}
+
+template <>
+std::vector<std::array<double, 2>> construct_periodic_point_cloud(
+    const std::array<std::array<double, 2>, 2> &translations,
+    std::array<double, 2> origin, std::vector<double> set) noexcept {
+
+  return construct_point_cloud<2>(translations, origin,
+                                  ranges::views::cartesian_product(set, set));
+}
+
+template <>
+std::vector<std::array<double, 1>> construct_periodic_point_cloud(
+    const std::array<std::array<double, 1>, 1> &translations,
+    std::array<double, 1> origin, std::vector<double> set) noexcept {
+  return construct_point_cloud<1>(translations, origin,
+                                  ranges::views::cartesian_product(set));
+}
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/construct_rectilinear_grid.cc b/meshing/src/construct_rectilinear_grid.cc
new file mode 100644
index 00000000..7eb8b5c1
--- /dev/null
+++ b/meshing/src/construct_rectilinear_grid.cc
@@ -0,0 +1,166 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/construct_rectilinear_grid.h"
+#include <Eigen/Dense>
+
+namespace ae108 {
+namespace meshing {
+
+template <>
+std::tuple<std::vector<std::array<double, 1>>,
+           std::vector<std::array<std::size_t, 2>>>
+construct_rectilinear_grid(
+    const std::array<std::vector<double>, 1> &instances) noexcept {
+
+  const auto generate_points = [&]() {
+    std::vector<std::array<double, 1>> points;
+    points.reserve(instances[0].size());
+
+    for (const auto &x : instances[0])
+      points.push_back({{x}});
+
+    return points;
+  };
+
+  const auto step_to_index = [&](const std::size_t step_x) { return step_x; };
+
+  const auto generate_edges = [&]() {
+    std::vector<std::array<std::size_t, 2>> edges;
+    edges.reserve(instances[0].size() - 1);
+    std::array<std::size_t, 1> step = {{0}};
+
+    // edges in x
+    for (step[0] = 0; step[0] < instances[0].size() - 1; ++step[0])
+      edges.push_back({{step_to_index(step[0]), step_to_index(step[0] + 1)}});
+
+    return edges;
+  };
+
+  return {generate_points(), generate_edges()};
+}
+
+template <>
+std::tuple<std::vector<std::array<double, 2>>,
+           std::vector<std::array<std::size_t, 2>>>
+construct_rectilinear_grid(
+    const std::array<std::vector<double>, 2> &instances) noexcept {
+
+  const auto generate_points = [&]() {
+    std::vector<std::array<double, 2>> points;
+    points.reserve(instances[0].size() * instances[1].size());
+
+    for (const auto &x : instances[0])
+      for (const auto &y : instances[1])
+        points.push_back({{x, y}});
+
+    return points;
+  };
+
+  const auto step_to_index = [&](const std::size_t step_x,
+                                 const std::size_t step_y) {
+    return step_x * instances[1].size() + step_y;
+  };
+
+  const auto generate_edges = [&]() {
+    std::vector<std::array<std::size_t, 2>> edges;
+    edges.reserve((instances[0].size() - 1) * instances[1].size() +
+                  instances[0].size() * (instances[1].size() - 1));
+
+    std::array<std::size_t, 2> step = {{0, 0}};
+
+    // edges in y
+    for (step[0] = 0; step[0] < instances[0].size(); ++step[0])
+      for (step[1] = 0; step[1] < instances[1].size() - 1; ++step[1])
+        edges.push_back({{step_to_index(step[0], step[1]),
+                          step_to_index(step[0], step[1] + 1)}});
+
+    // edges in x
+    for (step[0] = 0; step[0] < instances[0].size() - 1; ++step[0])
+      for (step[1] = 0; step[1] < instances[1].size(); ++step[1])
+        edges.push_back({{step_to_index(step[0], step[1]),
+                          step_to_index(step[0] + 1, step[1])}});
+    return edges;
+  };
+
+  return {generate_points(), generate_edges()};
+}
+
+template <>
+std::tuple<std::vector<std::array<double, 3>>,
+           std::vector<std::array<std::size_t, 2>>>
+construct_rectilinear_grid(
+    const std::array<std::vector<double>, 3> &instances) noexcept {
+
+  const auto generate_points = [&]() {
+    std::vector<std::array<double, 3>> points;
+    points.reserve(instances[0].size() * instances[1].size() *
+                   instances[2].size());
+
+    for (const auto &x : instances[0])
+      for (const auto &y : instances[1])
+        for (const auto &z : instances[2])
+          points.push_back({{x, y, z}});
+
+    return points;
+  };
+
+  const auto step_to_index = [&](const std::size_t step_x,
+                                 const std::size_t step_y,
+                                 const std::size_t step_z) {
+    return step_x * instances[1].size() * instances[2].size() +
+           step_y * instances[2].size() + step_z;
+  };
+
+  const auto generate_edges = [&]() {
+    std::vector<std::array<std::size_t, 2>> edges;
+    edges.reserve((instances[0].size() - 1) * (instances[1].size() - 1) *
+                      instances[2].size() +
+                  instances[0].size() * (instances[1].size() - 1) *
+                      (instances[2].size() - 1) +
+                  (instances[0].size() - 1) * instances[1].size() *
+                      (instances[2].size() - 1));
+
+    std::array<std::size_t, 3> step = {{0, 0, 0}};
+
+    // edges in z
+    for (step[0] = 0; step[0] < instances[0].size(); ++step[0])
+      for (step[1] = 0; step[1] < instances[1].size(); ++step[1])
+        for (step[2] = 0; step[2] < instances[2].size() - 1; ++step[2])
+          edges.push_back({{step_to_index(step[0], step[1], step[2]),
+                            step_to_index(step[0], step[1], step[2] + 1)}});
+
+    // edges in y
+    for (step[0] = 0; step[0] < instances[0].size(); ++step[0])
+      for (step[1] = 0; step[1] < instances[1].size() - 1; ++step[1])
+        for (step[2] = 0; step[2] < instances[2].size(); ++step[2])
+          edges.push_back({{step_to_index(step[0], step[1], step[2]),
+                            step_to_index(step[0], step[1] + 1, step[2])}});
+
+    // edges in x
+    for (step[0] = 0; step[0] < instances[0].size() - 1; ++step[0])
+      for (step[1] = 0; step[1] < instances[1].size(); ++step[1])
+        for (step[2] = 0; step[2] < instances[2].size(); ++step[2])
+          edges.push_back({{step_to_index(step[0], step[1], step[2]),
+                            step_to_index(step[0] + 1, step[1], step[2])}});
+
+    return edges;
+  };
+
+  return {generate_points(), generate_edges()};
+}
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/construct_voronoi_cell.cc b/meshing/src/construct_voronoi_cell.cc
new file mode 100644
index 00000000..07600544
--- /dev/null
+++ b/meshing/src/construct_voronoi_cell.cc
@@ -0,0 +1,112 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/construct_voronoi_cell.h"
+#include <Eigen/Dense>
+#include <range/v3/action/sort.hpp>
+#include <range/v3/algorithm/copy.hpp>
+#include <range/v3/view/chunk.hpp>
+#include <range/v3/view/enumerate.hpp>
+#include <voro++.hh>
+
+namespace ae108 {
+namespace meshing {
+
+template <>
+BoundaryRepresentation<std::size_t, double, 3>
+construct_voronoi_cell<std::size_t, double, 3>(
+    const std::vector<std::array<double, 3>> &point_cloud,
+    std::vector<std::pair<std::size_t, std::size_t>> *periodic_faces) noexcept {
+
+  assert(point_cloud.size());
+
+  voro::voronoicell voronoi_cell;
+  const auto bound = 1e3;
+  voronoi_cell.init(-bound, bound, -bound, bound, -bound, bound);
+  for (auto &point : point_cloud)
+    voronoi_cell.plane(point[0], point[1], point[2]);
+
+  const auto vertices = [](voro::voronoicell &voronoi_cell) {
+    std::vector<double> vertices;
+    voronoi_cell.vertices(vertices);
+    return vertices;
+  }(voronoi_cell);
+
+  const auto faces = [](voro::voronoicell &voronoi_cell) {
+    std::vector<std::vector<std::array<std::size_t, 2>>> faces;
+    std::vector<int> temp;
+    voronoi_cell.face_vertices(temp);
+    for (int i = 0; i < (int)temp.size(); i = i + temp[i] + 1) {
+      std::vector<std::array<std::size_t, 2>> edges;
+      for (int j = 1; j < temp[i]; j++) {
+        edges.push_back(
+            {std::size_t(temp[i + j]), std::size_t(temp[i + j + 1])});
+        ranges::sort(edges.back());
+      }
+      edges.push_back(
+          {std::size_t(temp[i + temp[i]]), std::size_t(temp[i + 1])});
+      ranges::sort(edges.back());
+      faces.push_back(edges);
+    }
+    return faces;
+  }(voronoi_cell);
+
+  const auto face_normals = [](voro::voronoicell &voronoi_cell) {
+    std::vector<BoundaryRepresentation<std::size_t, double, 3>::Point>
+        face_normals(voronoi_cell.number_of_faces());
+    std::vector<double> temp;
+    voronoi_cell.normals(temp);
+    for (const auto &[index, normal] :
+         ranges::views::enumerate(temp | ranges::views::chunk(3)))
+      ranges::copy(normal, face_normals[index].begin());
+    return face_normals;
+  }(voronoi_cell);
+
+  auto add_edge =
+      [](const BoundaryRepresentation<std::size_t, double, 3>::Edge &edge,
+         BoundaryRepresentation<std::size_t, double, 3>::Edges &edges)
+      -> std::size_t {
+    for (auto [index, connectivity] : ranges::views::enumerate(edges))
+      if (edge == connectivity)
+        return index;
+    edges.push_back(edge);
+    return edges.size() - 1;
+  };
+
+  BoundaryRepresentation<std::size_t, double, 3> brep;
+  brep.faces.resize(voronoi_cell.number_of_faces());
+  for (const auto &[index, face] : ranges::views::enumerate(faces))
+    for (auto &edge : face)
+      brep.faces[index].push_back(add_edge(edge, brep.edges));
+
+  brep.vertices.resize(vertices.size() / 3);
+  for (const auto &[index, vertex] :
+       ranges::views::enumerate(vertices | ranges::views::chunk(3)))
+    ranges::copy(vertex, brep.vertices[index].begin());
+
+  if (periodic_faces)
+    for (std::size_t i = 0; i < face_normals.size(); i++)
+      for (std::size_t j = i + 1; j < face_normals.size(); j++)
+        if (Eigen::Map<const Eigen::Vector3d>(face_normals[i].data())
+                .cross(
+                    Eigen::Map<const Eigen::Vector3d>(face_normals[j].data()))
+                .isZero())
+          periodic_faces->push_back({i, j});
+
+  return brep;
+}
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/Context.cc b/meshing/src/cppgmsh/Context.cc
new file mode 100644
index 00000000..0a7cfbf7
--- /dev/null
+++ b/meshing/src/cppgmsh/Context.cc
@@ -0,0 +1,41 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+Context::Context(int const argc, char **const argv, const bool readConfigFiles,
+                 const bool verbose)
+    : token_((initialize(argc, argv, readConfigFiles, verbose), this),
+             &finalize) {}
+
+void Context::initialize(int const argc, char **const argv,
+                         const bool readConfigFiles, const bool verbose) {
+  gmsh::initialize(argc, argv, readConfigFiles);
+  if (verbose)
+    gmsh::option::setNumber("General.Verbosity", 5);
+  else
+    gmsh::option::setNumber("General.Verbosity", 0);
+}
+
+void Context::finalize(void *) { gmsh::finalize(); }
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/Node.cc b/meshing/src/cppgmsh/Node.cc
new file mode 100644
index 00000000..f1a9b83a
--- /dev/null
+++ b/meshing/src/cppgmsh/Node.cc
@@ -0,0 +1,16 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Node.h"
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/as_affine_transform.cc b/meshing/src/cppgmsh/as_affine_transform.cc
new file mode 100644
index 00000000..aa043e39
--- /dev/null
+++ b/meshing/src/cppgmsh/as_affine_transform.cc
@@ -0,0 +1,41 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/as_affine_transform.h"
+#include <Eigen/Dense>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::array<double, 16>
+as_affine_transform(const std::array<double, 3> &translation) noexcept {
+
+  const auto transform =
+      Eigen::Transform<double, 3, Eigen::Affine, Eigen::RowMajor>(
+          Eigen::Translation3d(
+              Eigen::Map<const Eigen::Vector3d>(translation.data())));
+
+  return [transform]() {
+    assert(transform.matrix().size() == 16);
+    std::array<double, 16> out;
+    std::move(transform.data(), transform.data() + 16, out.begin());
+    return out;
+  }();
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/construct_box.cc b/meshing/src/cppgmsh/construct_box.cc
new file mode 100644
index 00000000..ea5c3bd6
--- /dev/null
+++ b/meshing/src/cppgmsh/construct_box.cc
@@ -0,0 +1,33 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::pair<int, int>
+construct_box(const std::array<double, 3> origin,
+              const std::array<double, 3> side_lengths) noexcept {
+  return {3, gmsh::model::occ::addBox(origin[0], origin[1], origin[2],
+                                      side_lengths[0], side_lengths[1],
+                                      side_lengths[2])};
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/construct_cylinders.cc b/meshing/src/cppgmsh/construct_cylinders.cc
new file mode 100644
index 00000000..466cf92e
--- /dev/null
+++ b/meshing/src/cppgmsh/construct_cylinders.cc
@@ -0,0 +1,72 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/construct_cylinders.h"
+#include <gmsh.h>
+#include <range/v3/view/zip.hpp>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::pair<int, int> construct_cylinder(const std::array<double, 3> &p0,
+                                       const std::array<double, 3> &p1,
+                                       const double radius) {
+  return {3,
+          gmsh::model::occ::addCylinder(p0[0], p0[1], p0[2], p1[0] - p0[0],
+                                        p1[1] - p0[1], p1[2] - p0[2], radius)};
+}
+
+std::pair<int, int> capped_cylinder(const std::array<double, 3> &p0,
+                                    const std::array<double, 3> &p1,
+                                    const double radius) {
+  std::vector<gmsh::vectorpair> temp;
+  gmsh::vectorpair cappedCylinder;
+  gmsh::model::occ::fuse(
+      {construct_cylinder(p0, p1, radius)},
+      {{3, gmsh::model::occ::addSphere(p0[0], p0[1], p0[2], radius)},
+       {3, gmsh::model::occ::addSphere(p1[0], p1[1], p1[2], radius)}},
+      cappedCylinder, temp);
+  return cappedCylinder[0];
+}
+
+std::vector<std::pair<int, int>>
+construct_cylinders(const std::vector<std::array<double, 3>> &nodes,
+                    const std::vector<std::array<std::size_t, 2>> &connectivity,
+                    const std::vector<double> &radii,
+                    const bool capped) noexcept {
+
+  assert(connectivity.size() == radii.size());
+
+  gmsh::vectorpair cylinder_entities;
+  cylinder_entities.reserve(connectivity.size());
+  for (const auto &&[segment, radius] :
+       ranges::views::zip(connectivity, radii)) {
+
+    const auto &p0 = nodes[segment[0]];
+    const auto &p1 = nodes[segment[1]];
+
+    if (capped)
+      cylinder_entities.push_back(capped_cylinder(p0, p1, radius));
+    else
+      cylinder_entities.push_back(construct_cylinder(p0, p1, radius));
+  }
+
+  return cylinder_entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/construct_polyhedron.cc b/meshing/src/cppgmsh/construct_polyhedron.cc
new file mode 100644
index 00000000..f60d3a0b
--- /dev/null
+++ b/meshing/src/cppgmsh/construct_polyhedron.cc
@@ -0,0 +1,55 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/construct_polyhedron.h"
+#include <gmsh.h>
+#include <map>
+#include <range/v3/view/enumerate.hpp>
+#include <range/v3/view/transform.hpp>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+template <>
+std::pair<int, int> construct_polyhedron(
+    const BoundaryRepresentation<std::size_t, double, 3> &brep) noexcept {
+
+  std::map<std::size_t, int> point2tag;
+  for (auto &&[index, vertex] : ranges::views::enumerate(brep.vertices))
+    point2tag[index] =
+        gmsh::model::occ::addPoint(vertex[0], vertex[1], vertex[2]);
+
+  std::map<std::size_t, int> edge2tag;
+  for (auto &&[index, edge] : ranges::views::enumerate(brep.edges))
+    edge2tag[index] =
+        gmsh::model::occ::addLine(point2tag.at(edge[0]), point2tag.at(edge[1]));
+
+  std::vector<int> surfaceTags;
+  for (const auto &line_loop : brep.faces)
+    surfaceTags.push_back(
+        gmsh::model::occ::addPlaneSurface({gmsh::model::occ::addCurveLoop(
+            line_loop | ranges::view::transform([&edge2tag](int edge) {
+              return edge2tag.at(edge);
+            }) |
+            ranges::to<std::vector>)}));
+
+  return {3, gmsh::model::occ::addVolume(
+                 {gmsh::model::occ::addSurfaceLoop(surfaceTags)})};
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/construct_rectangle.cc b/meshing/src/cppgmsh/construct_rectangle.cc
new file mode 100644
index 00000000..fde7cf65
--- /dev/null
+++ b/meshing/src/cppgmsh/construct_rectangle.cc
@@ -0,0 +1,33 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::pair<int, int>
+construct_rectangle(const std::array<double, 3> origin,
+                    const std::array<double, 2> side_lengths,
+                    const double rounded_radius) noexcept {
+  return {2, gmsh::model::occ::addRectangle(origin[0], origin[1], origin[2],
+                                            side_lengths[0], side_lengths[1],
+                                            -1, rounded_radius)};
+}
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/copy_entities.cc b/meshing/src/cppgmsh/copy_entities.cc
new file mode 100644
index 00000000..12772368
--- /dev/null
+++ b/meshing/src/cppgmsh/copy_entities.cc
@@ -0,0 +1,32 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/copy_entities.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>>
+copy_entities(const std::vector<std::pair<int, int>> &entities) noexcept {
+  std::vector<std::pair<int, int>> copied_entities;
+  gmsh::model::occ::copy(entities, copied_entities);
+  return copied_entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/extract_mesh.cc b/meshing/src/cppgmsh/extract_mesh.cc
new file mode 100644
index 00000000..148b97ea
--- /dev/null
+++ b/meshing/src/cppgmsh/extract_mesh.cc
@@ -0,0 +1,85 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/extract_mesh.h"
+#include "ae108/meshing/cppgmsh/get_elements_in.h"
+#include "ae108/meshing/cppgmsh/get_nodes_of.h"
+#include <gmsh.h>
+#include <range/v3/view/enumerate.hpp>
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+template <std::size_t coordinate_dimension>
+std::tuple<std::vector<std::array<double, coordinate_dimension>>,
+           std::vector<std::vector<std::size_t>>,
+           std::map<std::size_t, std::size_t>,
+           std::map<std::size_t, std::size_t>>
+extract_mesh(const std::size_t element_dimension) noexcept {
+
+  const auto [nodeTagToIndex,
+              positions] = [](const std::size_t element_dimension) {
+    auto nodes = get_nodes_of<coordinate_dimension>({element_dimension, -1});
+
+    auto nodeTagToIndex = std::map<std::size_t, std::size_t>();
+    auto positions =
+        std::vector<std::array<double, coordinate_dimension>>(nodes.size());
+    for (const auto &&[index, node] : ranges::views::enumerate(nodes)) {
+      positions[index] = node.position;
+      nodeTagToIndex[node.id] = index;
+    }
+
+    return std::make_tuple(nodeTagToIndex, positions);
+  }(element_dimension);
+
+  const auto [elementTagToIndex, connectivity] =
+      [&nodeTagToIndex](const std::size_t element_dimension) {
+        const auto elements = get_elements_in({element_dimension, -1});
+
+        auto elementTagToIndex = std::map<std::size_t, std::size_t>();
+        auto connectivity = std::vector<std::vector<std::size_t>>();
+        connectivity.reserve(elements.size());
+        for (auto element : elements) {
+          std::vector<std::size_t> nodeTags;
+          gmsh::model::mesh::getElement(element.second, element.first,
+                                        nodeTags);
+
+          std::vector<std::size_t> nodeIndices(nodeTags.size());
+          for (auto &&[index, nodeTag] : ranges::views::enumerate(nodeTags))
+            nodeIndices[index] = nodeTagToIndex.at(nodeTag);
+
+          elementTagToIndex[element.second] = connectivity.size();
+          connectivity.push_back(nodeIndices);
+        }
+
+        return std::make_tuple(elementTagToIndex, connectivity);
+      }(element_dimension);
+
+  return {positions, connectivity, nodeTagToIndex, elementTagToIndex};
+}
+
+template std::tuple<
+    std::vector<std::array<double, 3>>, std::vector<std::vector<std::size_t>>,
+    std::map<std::size_t, std::size_t>, std::map<std::size_t, std::size_t>>
+extract_mesh<3>(const std::size_t element_dimension) noexcept;
+
+template std::tuple<
+    std::vector<std::array<double, 2>>, std::vector<std::vector<std::size_t>>,
+    std::map<std::size_t, std::size_t>, std::map<std::size_t, std::size_t>>
+extract_mesh<2>(const std::size_t element_dimension) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/extrude_entities.cc b/meshing/src/cppgmsh/extrude_entities.cc
new file mode 100644
index 00000000..c5dc1573
--- /dev/null
+++ b/meshing/src/cppgmsh/extrude_entities.cc
@@ -0,0 +1,42 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/extrude_entities.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>>
+extrude_entities(const std::vector<std::pair<int, int>> &entities,
+                 const std::array<double, 3> &translation,
+                 const bool extrude_mesh, const int number_of_layers) noexcept {
+  std::vector<std::pair<int, int>> extruded_entities;
+  std::vector<int> numElements = {};
+  std::vector<double> heights = {};
+  if (extrude_mesh) {
+    numElements = {number_of_layers};
+    heights = {1};
+  }
+  gmsh::model::occ::extrude(entities, translation[0], translation[1],
+                            translation[2], extruded_entities, numElements,
+                            heights);
+  return extruded_entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/extrude_surface.cc b/meshing/src/cppgmsh/extrude_surface.cc
new file mode 100644
index 00000000..56c61483
--- /dev/null
+++ b/meshing/src/cppgmsh/extrude_surface.cc
@@ -0,0 +1,64 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/extrude_surface.h"
+#include "ae108/meshing/cppgmsh/copy_entities.h"
+#include "ae108/meshing/cppgmsh/extrude_entities.h"
+#include "ae108/meshing/cppgmsh/get_centroid_of.h"
+#include "ae108/meshing/cppgmsh/rotate_entities.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include "ae108/meshing/cppgmsh/translate_entities.h"
+#include <Eigen/Dense>
+#include <Eigen/Geometry>
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::pair<int, int>
+extrude_surface(const int surface_tag, const std::array<double, 3> &from,
+                const std::array<double, 3> &to,
+                const std::array<double, 3> &surface_normal) noexcept {
+
+  auto crosssection = copy_entities({{2, surface_tag}});
+
+  const auto translation = [&] {
+    std::array<double, 3> from_to;
+    Eigen::Map<Eigen::Matrix<double, 3, 1>>(from_to.data()) =
+        (Eigen::Map<const Eigen::Vector3d>(to.data()) -
+         Eigen::Map<const Eigen::Vector3d>(from.data()));
+    return from_to;
+  }();
+
+  std::array<double, 3> axis;
+  Eigen::AngleAxisd angleAxis;
+  angleAxis = Eigen::Quaterniond().setFromTwoVectors(
+      Eigen::Map<const Eigen::Matrix<double, 3, 1>>(surface_normal.data()),
+      Eigen::Map<const Eigen::Matrix<double, 3, 1>>(translation.data()));
+  Eigen::Map<Eigen::Matrix<double, 3, 1>>(axis.data()) = angleAxis.axis();
+
+  rotate_entities(crosssection, {0, 0, 0}, axis, angleAxis.angle());
+
+  translate_entities(crosssection, from, false);
+
+  const auto extruded_entities = extrude_entities(crosssection, translation);
+
+  return extruded_entities[1];
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/fragment_entities.cc b/meshing/src/cppgmsh/fragment_entities.cc
new file mode 100644
index 00000000..8e5ac406
--- /dev/null
+++ b/meshing/src/cppgmsh/fragment_entities.cc
@@ -0,0 +1,34 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/fragment_entities.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>>
+fragment_entities(const std::vector<std::pair<int, int>> &entities) noexcept {
+  std::vector<std::pair<int, int>> fragmented_entities;
+  std::vector<gmsh::vectorpair> outDimTagsMap;
+  gmsh::model::occ::fragment(entities, entities, fragmented_entities,
+                             outDimTagsMap, -1, true, true);
+  return fragmented_entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/fuse_entities.cc b/meshing/src/cppgmsh/fuse_entities.cc
new file mode 100644
index 00000000..5a883fa5
--- /dev/null
+++ b/meshing/src/cppgmsh/fuse_entities.cc
@@ -0,0 +1,33 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/fuse_entities.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>>
+fuse_entities(const std::vector<std::pair<int, int>> &entities) noexcept {
+  std::vector<gmsh::vectorpair> temp;
+  gmsh::vectorpair fused_entities;
+  gmsh::model::occ::fuse(entities, entities, fused_entities, temp);
+  return fused_entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/generate_mesh.cc b/meshing/src/cppgmsh/generate_mesh.cc
new file mode 100644
index 00000000..d1e8cb2e
--- /dev/null
+++ b/meshing/src/cppgmsh/generate_mesh.cc
@@ -0,0 +1,32 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void generate_mesh(const int dimension, const int order,
+                   const int algorithm) noexcept {
+
+  gmsh::option::setNumber("Mesh.Algorithm", algorithm);
+  gmsh::model::mesh::generate(dimension);
+  gmsh::model::mesh::setOrder(order);
+}
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_boundary_of.cc b/meshing/src/cppgmsh/get_boundary_of.cc
new file mode 100644
index 00000000..f96d9899
--- /dev/null
+++ b/meshing/src/cppgmsh/get_boundary_of.cc
@@ -0,0 +1,34 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_boundary_of.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>>
+get_boundary_of(const std::vector<std::pair<int, int>> &entities,
+                const bool recursive) noexcept {
+  gmsh::model::occ::synchronize();
+  std::vector<std::pair<int, int>> boundary_entities;
+  gmsh::model::getBoundary(entities, boundary_entities, true, true, recursive);
+  return boundary_entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_centroid_of.cc b/meshing/src/cppgmsh/get_centroid_of.cc
new file mode 100644
index 00000000..82c71c58
--- /dev/null
+++ b/meshing/src/cppgmsh/get_centroid_of.cc
@@ -0,0 +1,33 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_centroid_of.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::array<double, 3>
+get_centroid_of(const std::pair<int, int> &entity) noexcept {
+  std::array<double, 3> centroid;
+  gmsh::model::occ::getCenterOfMass(entity.first, entity.second, centroid[0],
+                                    centroid[1], centroid[2]);
+  return centroid;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_coords_of.cc b/meshing/src/cppgmsh/get_coords_of.cc
new file mode 100644
index 00000000..c302d16f
--- /dev/null
+++ b/meshing/src/cppgmsh/get_coords_of.cc
@@ -0,0 +1,39 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_coords_of.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::array<double, 3> get_coords_of(const int point_tag) noexcept {
+  std::vector<double> _, coord;
+  gmsh::model::getValue(0, point_tag, _, coord);
+  return {coord[0], coord[1], coord[2]};
+}
+
+std::vector<std::array<double, 3>>
+get_coords_of(const std::vector<int> &point_tags) noexcept {
+  std::vector<std::array<double, 3>> coords(point_tags.size());
+  for (std::size_t i = 0; i < point_tags.size(); i++)
+    coords[i] = get_coords_of(point_tags[i]);
+  return coords;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_elements_in.cc b/meshing/src/cppgmsh/get_elements_in.cc
new file mode 100644
index 00000000..3fe575d8
--- /dev/null
+++ b/meshing/src/cppgmsh/get_elements_in.cc
@@ -0,0 +1,42 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_elements_in.h"
+#include <gmsh.h>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, std::size_t>>
+get_elements_in(const std::pair<int, int> &entity) noexcept {
+  std::vector<int> elementTypes;
+  std::vector<std::vector<std::size_t>> elementTags;
+  std::vector<std::vector<std::size_t>> nodeTags;
+  gmsh::model::mesh::getElements(elementTypes, elementTags, nodeTags,
+                                 entity.first, entity.second);
+
+  std::vector<std::pair<int, std::size_t>> elements;
+  for (std::size_t i = 0; i < elementTypes.size(); i++)
+    for (std::size_t j = 0; j < elementTags[i].size(); j++)
+      elements.push_back({elementTypes[i], elementTags[i][j]});
+
+  return elements;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_entities_in.cc b/meshing/src/cppgmsh/get_entities_in.cc
new file mode 100644
index 00000000..e09a46fa
--- /dev/null
+++ b/meshing/src/cppgmsh/get_entities_in.cc
@@ -0,0 +1,53 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_entities_in.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>>
+get_entities_in(const BoundingBox<std::array<double, 3>> &bounding_box,
+                const int dimension, const double tol) noexcept {
+
+  gmsh::vectorpair entities;
+  gmsh::model::getEntitiesInBoundingBox(
+      bounding_box.min[0] - tol, bounding_box.min[1] - tol,
+      bounding_box.min[2] - tol, bounding_box.max[0] + tol,
+      bounding_box.max[1] + tol, bounding_box.max[2] + tol, entities,
+      dimension);
+  return entities;
+}
+
+std::vector<std::pair<int, int>>
+get_entities_in(const std::pair<int, int> &physical_group) noexcept {
+
+  std::vector<int> tags;
+  gmsh::model::getEntitiesForPhysicalGroup(physical_group.first,
+                                           physical_group.second, tags);
+
+  std::vector<std::pair<int, int>> entities;
+  entities.reserve(tags.size());
+  for (const auto &tag : tags)
+    entities.push_back({physical_group.first, tag});
+
+  return entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_entities_of.cc b/meshing/src/cppgmsh/get_entities_of.cc
new file mode 100644
index 00000000..1c95c0c2
--- /dev/null
+++ b/meshing/src/cppgmsh/get_entities_of.cc
@@ -0,0 +1,32 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_entities_of.h"
+#include <gmsh.h>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>> get_entities_of(const int dim) noexcept {
+  std::vector<std::pair<int, int>> entities;
+  gmsh::model::getEntities(entities, dim);
+  return entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_nodes_of.cc b/meshing/src/cppgmsh/get_nodes_of.cc
new file mode 100644
index 00000000..05ede05c
--- /dev/null
+++ b/meshing/src/cppgmsh/get_nodes_of.cc
@@ -0,0 +1,58 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_nodes_of.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+template <std::size_t Dimension>
+std::vector<Node<Dimension>>
+get_nodes_of(const std::pair<int, int> &entity,
+             const bool remove_duplicates) noexcept {
+
+  std::vector<std::size_t> nodeTags;
+  std::vector<double> coord;
+  std::vector<double> _;
+  gmsh::model::mesh::getNodes(nodeTags, coord, _, entity.first, entity.second,
+                              true, false);
+
+  std::vector<Node<Dimension>> nodes(nodeTags.size());
+  for (std::size_t i = 0; i < nodeTags.size(); i++) {
+    nodes[i].id = nodeTags[i];
+    std::copy_n(coord.begin() + i * 3, Dimension, nodes[i].position.begin());
+  }
+
+  if (remove_duplicates) {
+    std::sort(nodes.begin(), nodes.end());
+    nodes.erase(std::unique(nodes.begin(), nodes.end()), nodes.end());
+  }
+
+  return nodes;
+}
+
+template std::vector<Node<3>>
+get_nodes_of<3>(const std::pair<int, int> &entity,
+                const bool keep_duplicates) noexcept;
+
+template std::vector<Node<2>>
+get_nodes_of<2>(const std::pair<int, int> &entity,
+                const bool keep_duplicates) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_normal_of.cc b/meshing/src/cppgmsh/get_normal_of.cc
new file mode 100644
index 00000000..bb9de420
--- /dev/null
+++ b/meshing/src/cppgmsh/get_normal_of.cc
@@ -0,0 +1,35 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_normal_of.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::array<double, 3> get_normal_of(const int surface_tag) noexcept {
+
+  std::vector<double> normals;
+  gmsh::model::getNormal(surface_tag, {0, 0}, normals);
+
+  std::array<double, 3> normal;
+  std::copy_n(normals.begin(), 3, normal.begin());
+  return normal;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_periodic_nodes_of.cc b/meshing/src/cppgmsh/get_periodic_nodes_of.cc
new file mode 100644
index 00000000..e1ea84f3
--- /dev/null
+++ b/meshing/src/cppgmsh/get_periodic_nodes_of.cc
@@ -0,0 +1,54 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_periodic_nodes_of.h"
+#include <cassert>
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::pair<std::vector<std::size_t>, std::vector<std::size_t>>
+get_periodic_nodes_of(const std::pair<int, int> &target_entity,
+                      std::pair<int, int> *source_entity,
+                      std::array<double, 16> *affine_transform) noexcept {
+
+  assert(target_entity.first == 1 || target_entity.first == 2);
+
+  std::vector<size_t> targetNodeTags, sourceNodeTags;
+  std::vector<double> _;
+
+  int source_tag;
+  std::vector<double> transform;
+  std::pair<std::vector<std::size_t>, std::vector<std::size_t>> node_tags;
+  gmsh::model::mesh::getPeriodicNodes(target_entity.first, target_entity.second,
+                                      source_tag, node_tags.first,
+                                      node_tags.second, transform, true);
+  if (source_entity) {
+    source_entity->first = target_entity.first;
+    source_entity->second = source_tag;
+  }
+
+  if (affine_transform)
+    std::move(transform.data(), transform.data() + 16,
+              affine_transform->begin());
+
+  return node_tags;
+};
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_physical_groups.cc b/meshing/src/cppgmsh/get_physical_groups.cc
new file mode 100644
index 00000000..5aeb167e
--- /dev/null
+++ b/meshing/src/cppgmsh/get_physical_groups.cc
@@ -0,0 +1,31 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_physical_groups.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>> get_physical_groups(const int dim) noexcept {
+  std::vector<std::pair<int, int>> entities;
+  gmsh::model::getPhysicalGroups(entities, dim);
+  return entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/get_points_of.cc b/meshing/src/cppgmsh/get_points_of.cc
new file mode 100644
index 00000000..0cfe8431
--- /dev/null
+++ b/meshing/src/cppgmsh/get_points_of.cc
@@ -0,0 +1,42 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/get_points_of.h"
+#include <gmsh.h>
+#include <set>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<int> get_points_of(const std::pair<int, int> &entity) noexcept {
+
+  std::set<int> tags = {entity.second};
+  for (int dim = entity.first; dim > 0; dim--) {
+    std::set<int> dim_downward;
+    for (const auto &tag : tags) {
+      std::vector<int> upward, downward;
+      gmsh::model::getAdjacencies(dim, tag, upward, downward);
+      dim_downward.insert(downward.begin(), downward.end());
+    }
+    tags = dim_downward;
+  }
+
+  return std::vector(tags.begin(), tags.end());
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/heal_periodic_domains.cc b/meshing/src/cppgmsh/heal_periodic_domains.cc
new file mode 100644
index 00000000..7489e276
--- /dev/null
+++ b/meshing/src/cppgmsh/heal_periodic_domains.cc
@@ -0,0 +1,52 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/heal_periodic_domains.h"
+#include "ae108/meshing/cppgmsh/get_centroid_of.h"
+#include "ae108/meshing/cppgmsh/get_entities_in.h"
+#include "ae108/meshing/cppgmsh/heal_periodic_surfaces.h"
+#include <Eigen/Dense>
+#include <array>
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void heal_periodic_domains(const BoundingBox<std::array<double, 3>> &source,
+                           const BoundingBox<std::array<double, 3>> &target,
+                           const double tol) noexcept {
+  std::array<double, 3> translation;
+  Eigen::Map<Eigen::Vector3d>(translation.data()) =
+      Eigen::Map<const Eigen::Vector3d>(target.min.data()) -
+      Eigen::Map<const Eigen::Vector3d>(source.min.data());
+  for (const auto &source_surface : get_entities_in(source, 2)) {
+    const auto source_centroid = get_centroid_of(source_surface);
+    for (const auto &target_surface : get_entities_in(target, 2)) {
+      const auto target_centroid = get_centroid_of(target_surface);
+      if (Eigen::Map<const Eigen::Vector3d>(target_centroid.data())
+              .isApprox(
+                  Eigen::Map<const Eigen::Vector3d>(source_centroid.data()) +
+                      Eigen::Map<const Eigen::Vector3d>(translation.data()),
+                  tol))
+        heal_periodic_surfaces(source_surface.second, target_surface.second,
+                               translation);
+    }
+  }
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/heal_periodic_surfaces.cc b/meshing/src/cppgmsh/heal_periodic_surfaces.cc
new file mode 100644
index 00000000..44b561d5
--- /dev/null
+++ b/meshing/src/cppgmsh/heal_periodic_surfaces.cc
@@ -0,0 +1,71 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/heal_periodic_surfaces.h"
+#include "ae108/meshing/cppgmsh/get_coords_of.h"
+#include "ae108/meshing/cppgmsh/get_points_of.h"
+#include <Eigen/Dense>
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void heal_periodic_surfaces(const int source_surface, const int target_surface,
+                            const std::array<double, 3> &translation,
+                            const double tol) noexcept {
+
+  for (const auto &source : get_points_of({2, source_surface})) {
+
+    std::array<double, 3> query;
+    Eigen::Map<Eigen::Vector3d>(query.data()) =
+        (Eigen::Map<const Eigen::Vector3d>(get_coords_of(source).data()) +
+         Eigen::Map<const Eigen::Vector3d>(translation.data()))
+            .eval();
+
+    bool target_found = false;
+    for (const auto &target : get_points_of({2, target_surface}))
+      target_found +=
+          Eigen::Map<const Eigen::Vector3d>(get_coords_of(target).data())
+              .isApprox(Eigen::Map<const Eigen::Vector3d>(query.data()), tol);
+
+    if (!target_found) {
+      const auto missing_point =
+          gmsh::model::occ::addPoint(query[0], query[1], query[2]);
+
+      std::vector<int> volumes, lines;
+      gmsh::model::getAdjacencies(2, target_surface, volumes, lines);
+
+      gmsh::vectorpair ov;
+      std::vector<gmsh::vectorpair> ovv;
+      if (volumes.size())
+        gmsh::model::occ::fragment(
+            [](const std::vector<int> &volumes) {
+              std::vector<std::pair<int, int>> volume_entities;
+              for (const auto &tag : volumes)
+                volume_entities.push_back({3, tag});
+              return volume_entities;
+            }(volumes),
+            {{0, missing_point}}, ov, ovv);
+      else
+        gmsh::model::occ::fragment({{2, target_surface}}, {{0, missing_point}},
+                                   ov, ovv);
+    }
+  }
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/import_shapes.cc b/meshing/src/cppgmsh/import_shapes.cc
new file mode 100644
index 00000000..1232c5f5
--- /dev/null
+++ b/meshing/src/cppgmsh/import_shapes.cc
@@ -0,0 +1,32 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/import_shapes.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>>
+import_shapes(const std::string &fileName, const bool highestDimOnly) noexcept {
+  std::vector<std::pair<int, int>> outDimTags;
+  gmsh::model::occ::importShapes(fileName, outDimTags, highestDimOnly);
+  return outDimTags;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/intersect_entities.cc b/meshing/src/cppgmsh/intersect_entities.cc
new file mode 100644
index 00000000..2c9ea746
--- /dev/null
+++ b/meshing/src/cppgmsh/intersect_entities.cc
@@ -0,0 +1,37 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/intersect_entities.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>>
+intersect_entities(const std::vector<std::pair<int, int>> &object_entities,
+                   const std::vector<std::pair<int, int>> &tool_entities,
+                   const bool remove_object, const bool remove_tool) noexcept {
+  std::vector<gmsh::vectorpair> temp;
+  gmsh::vectorpair intersected_entities;
+  gmsh::model::occ::intersect(object_entities, tool_entities,
+                              intersected_entities, temp, -1, remove_object,
+                              remove_tool);
+  return intersected_entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/read_file.cc b/meshing/src/cppgmsh/read_file.cc
new file mode 100644
index 00000000..357b5078
--- /dev/null
+++ b/meshing/src/cppgmsh/read_file.cc
@@ -0,0 +1,27 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/read_file.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void read_file(const std::string &fname) noexcept { gmsh::open(fname); }
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/remove_entities.cc b/meshing/src/cppgmsh/remove_entities.cc
new file mode 100644
index 00000000..6c2ad72c
--- /dev/null
+++ b/meshing/src/cppgmsh/remove_entities.cc
@@ -0,0 +1,30 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/remove_entities.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void remove_entities(const std::vector<std::pair<int, int>> &entities,
+                     const bool recursive) noexcept {
+  gmsh::model::occ::remove(entities, recursive);
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/rotate_entities.cc b/meshing/src/cppgmsh/rotate_entities.cc
new file mode 100644
index 00000000..cdd6b5fe
--- /dev/null
+++ b/meshing/src/cppgmsh/rotate_entities.cc
@@ -0,0 +1,33 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/rotate_entities.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void rotate_entities(const std::vector<std::pair<int, int>> &entities,
+                     const std::array<double, 3> &center,
+                     const std::array<double, 3> &direction,
+                     double angle) noexcept {
+  gmsh::model::occ::rotate(entities, center[0], center[1], center[2],
+                           direction[0], direction[1], direction[2], angle);
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/set_domain_entities_periodic.cc b/meshing/src/cppgmsh/set_domain_entities_periodic.cc
new file mode 100644
index 00000000..efe8fb83
--- /dev/null
+++ b/meshing/src/cppgmsh/set_domain_entities_periodic.cc
@@ -0,0 +1,54 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/set_domain_entities_periodic.h"
+#include "ae108/meshing/cppgmsh/as_affine_transform.h"
+#include "ae108/meshing/cppgmsh/get_centroid_of.h"
+#include "ae108/meshing/cppgmsh/get_entities_in.h"
+#include "ae108/meshing/cppgmsh/set_periodic.h"
+#include <Eigen/Dense>
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void set_domain_entities_periodic(
+    const BoundingBox<std::array<double, 3>> &source,
+    const BoundingBox<std::array<double, 3>> &target, const int dim,
+    const double tol) noexcept {
+
+  assert(dim == 1 || dim == 2);
+
+  std::array<double, 3> translation;
+  Eigen::Map<Eigen::Vector3d>(translation.data()) =
+      Eigen::Map<const Eigen::Vector3d>(target.min.data()) -
+      Eigen::Map<const Eigen::Vector3d>(source.min.data());
+
+  for (const auto &source_surface : get_entities_in(source, dim))
+    for (const auto &target_surface : get_entities_in(target, dim))
+      if (Eigen::Map<const Eigen::Vector3d>(
+              get_centroid_of(target_surface).data())
+              .isApprox(
+                  Eigen::Map<const Eigen::Vector3d>(
+                      get_centroid_of(source_surface).data()) +
+                      Eigen::Map<const Eigen::Vector3d>(translation.data()),
+                  tol))
+        set_periodic({{2, target_surface.second}}, {2, source_surface.second},
+                     as_affine_transform(translation));
+}
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/set_expert_mode.cc b/meshing/src/cppgmsh/set_expert_mode.cc
new file mode 100644
index 00000000..02939312
--- /dev/null
+++ b/meshing/src/cppgmsh/set_expert_mode.cc
@@ -0,0 +1,29 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/set_expert_mode.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void set_expert_mode(const bool enable) noexcept {
+  gmsh::option::setNumber("General.ExpertMode", double(enable));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/set_granularity.cc b/meshing/src/cppgmsh/set_granularity.cc
new file mode 100644
index 00000000..a22d8b28
--- /dev/null
+++ b/meshing/src/cppgmsh/set_granularity.cc
@@ -0,0 +1,30 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void set_granularity(const double granularity) noexcept {
+  gmsh::vectorpair point_entitites;
+  gmsh::model::getEntities(point_entitites, 0);
+  gmsh::model::mesh::setSize(point_entitites, granularity);
+}
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/set_periodic.cc b/meshing/src/cppgmsh/set_periodic.cc
new file mode 100644
index 00000000..e1a053ce
--- /dev/null
+++ b/meshing/src/cppgmsh/set_periodic.cc
@@ -0,0 +1,45 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/set_periodic.h"
+#include <cassert>
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void set_periodic(const std::vector<std::pair<int, int>> &target_entities,
+                  const std::pair<int, int> &source_entity,
+                  const std::array<double, 16> &affine_transform) noexcept {
+
+  assert(source_entity.first == 1 || source_entity.first == 2);
+
+  std::vector<int> target_tags;
+  for (const auto &target_entity : target_entities) {
+    assert(source_entity.first == target_entity.first);
+    target_tags.push_back(target_entity.second);
+  }
+
+  std::vector<double> transform(affine_transform.begin(),
+                                affine_transform.end());
+
+  gmsh::model::mesh::setPeriodic(source_entity.first, target_tags,
+                                 {source_entity.second}, transform);
+};
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/set_physical_group_of.cc b/meshing/src/cppgmsh/set_physical_group_of.cc
new file mode 100644
index 00000000..1786a8d9
--- /dev/null
+++ b/meshing/src/cppgmsh/set_physical_group_of.cc
@@ -0,0 +1,42 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/set_physical_group_of.h"
+#include <cassert>
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::pair<int, int> set_physical_group_of(
+    const std::vector<std::pair<int, int>> &entities) noexcept {
+  assert(entities.size());
+
+  int dim = entities[0].first;
+  std::vector<int> tags;
+  tags.reserve(entities.size());
+  for (const auto &entity : entities) {
+    assert(dim == entity.first);
+    tags.push_back(entity.second);
+  };
+
+  return {dim, gmsh::model::addPhysicalGroup(dim, tags)};
+  ;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/synchronize.cc b/meshing/src/cppgmsh/synchronize.cc
new file mode 100644
index 00000000..55d7a464
--- /dev/null
+++ b/meshing/src/cppgmsh/synchronize.cc
@@ -0,0 +1,27 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void synchronize() noexcept { gmsh::model::occ::synchronize(); }
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/translate_entities.cc b/meshing/src/cppgmsh/translate_entities.cc
new file mode 100644
index 00000000..bd6f7921
--- /dev/null
+++ b/meshing/src/cppgmsh/translate_entities.cc
@@ -0,0 +1,39 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/translate_entities.h"
+#include "ae108/meshing/cppgmsh/copy_entities.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+std::vector<std::pair<int, int>>
+translate_entities(std::vector<std::pair<int, int>> entities,
+                   const std::array<double, 3> &translation,
+                   const bool copy) noexcept {
+  if (copy)
+    entities = copy_entities(entities);
+
+  gmsh::model::occ::translate(entities, translation[0], translation[1],
+                              translation[2]);
+
+  return entities;
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/cppgmsh/write_file.cc b/meshing/src/cppgmsh/write_file.cc
new file mode 100644
index 00000000..c19a40a5
--- /dev/null
+++ b/meshing/src/cppgmsh/write_file.cc
@@ -0,0 +1,27 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/write_file.h"
+#include <gmsh.h>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+void write_file(const std::string &fname) noexcept { gmsh::write(fname); }
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/BoundaryRepresentation.h b/meshing/src/include/ae108/meshing/BoundaryRepresentation.h
new file mode 100644
index 00000000..cb0c1fe3
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/BoundaryRepresentation.h
@@ -0,0 +1,84 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+#include <array>
+#include <set>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+template <class SizeType_, class ValueType_, SizeType_ Dimension_>
+struct BoundaryRepresentation {
+  using Point = std::array<ValueType_, Dimension_>;
+
+  /**
+   * @brief The position per vertex.
+   */
+  using Vertices = std::vector<Point>;
+
+  /**
+   * @brief Indicies of two vertices that form an edge.
+   */
+  using Edge = std::array<SizeType_, 2>;
+
+  /**
+   * @brief Vector of all edges.
+   */
+  using Edges = std::vector<Edge>;
+
+  /**
+   * @brief Edges that enclose a face.
+   */
+  using Faces = std::vector<std::vector<SizeType_>>;
+
+  /**
+   * @brief Returns the dimension.
+   */
+  static constexpr SizeType_ dimension() noexcept { return Dimension_; }
+
+  /**
+   * @brief Returns all vertices of an edge.
+   */
+  std::vector<Point> vertices_of_edge(SizeType_ edge) const noexcept {
+    std::vector<Point> edge_vertices;
+    for (const auto &vertex : edges[edge])
+      edge_vertices.push_back(vertices[vertex]);
+    return edge_vertices;
+  };
+
+  /**
+   * @brief Returns all vertices of a face.
+   */
+  std::vector<Point> vertices_of_face(SizeType_ index) const noexcept {
+    std::set<SizeType_> face_vertex_indices;
+    for (const auto &edge : faces[index])
+      for (const auto &vertex : edges[edge])
+        face_vertex_indices.insert(vertex);
+
+    std::vector<Point> face_vertices;
+    face_vertices.reserve(face_vertex_indices.size());
+    for (const auto &vertex_index : face_vertex_indices)
+      face_vertices.push_back(vertices[vertex_index]);
+
+    return face_vertices;
+  };
+
+  Vertices vertices;
+  Edges edges;
+  Faces faces;
+};
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/BoundingBox.h b/meshing/src/include/ae108/meshing/BoundingBox.h
new file mode 100644
index 00000000..595d77a5
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/BoundingBox.h
@@ -0,0 +1,27 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+namespace ae108 {
+namespace meshing {
+
+template <class Point> struct BoundingBox {
+  Point min;
+  Point max;
+};
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/bounding_box_of.h b/meshing/src/include/ae108/meshing/bounding_box_of.h
new file mode 100644
index 00000000..241098ef
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/bounding_box_of.h
@@ -0,0 +1,45 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include "ae108/meshing/BoundingBox.h"
+#include <cassert>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+
+/**
+ * @brief Returns the bounding box of points
+ *
+ * @param points A vector of points.
+ */
+
+template <class Point>
+BoundingBox<Point> bounding_box_of(const std::vector<Point> &points) noexcept {
+  assert(points.size());
+  Point min(points[0]);
+  Point max(points[0]);
+  for (const auto &point : points)
+    for (std::size_t i = 0; i < point.size(); i++) {
+      min[i] = std::min(point[i], min[i]);
+      max[i] = std::max(point[i], max[i]);
+    }
+  return {min, max};
+}
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/construct_periodic_point_cloud.h b/meshing/src/include/ae108/meshing/construct_periodic_point_cloud.h
new file mode 100644
index 00000000..1f1aaa74
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/construct_periodic_point_cloud.h
@@ -0,0 +1,47 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+
+/**
+ * @brief Returns a vector of points that is spanned by a set of discrete
+ * translations around the origin. It is also known as Bravais lattice.
+ *
+ * In 3D, each point is defined by
+ *
+ *          R = n1 x a1 + n2 x a2 + n3 x a3,
+ *
+ * where ai is a translation vector and ni assumes any of the integers in set.
+ *
+ * @param translations An array containing the translation vectors.
+ * @param origin Location of the origin.
+ * @param set A set of integer permutations
+ * @note https://en.wikipedia.org/wiki/Bravais_lattice
+ */
+
+template <std::size_t Dimension>
+std::vector<std::array<double, Dimension>> construct_periodic_point_cloud(
+    const std::array<std::array<double, Dimension>, Dimension> &translations,
+    std::array<double, Dimension> origin,
+    std::vector<double> set = {-1, 0, 1}) noexcept;
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/construct_rectilinear_grid.h b/meshing/src/include/ae108/meshing/construct_rectilinear_grid.h
new file mode 100644
index 00000000..7f8dc9bf
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/construct_rectilinear_grid.h
@@ -0,0 +1,38 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+
+/**
+ * @brief Constructs a d-dimensional rectilinear grid based on the (non-)uniform
+ * input coordinates. Returns points and edges.
+ *
+ * @param coordinates d sets of coordinates that span the grid.
+ * @note https://en.wikipedia.org/wiki/Regular_grid
+ */
+
+template <std::size_t Dimension>
+std::tuple<std::vector<std::array<double, Dimension>>,
+           std::vector<std::array<std::size_t, 2>>>
+construct_rectilinear_grid(
+    const std::array<std::vector<double>, Dimension> &coordinates) noexcept;
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/construct_voronoi_cell.h b/meshing/src/include/ae108/meshing/construct_voronoi_cell.h
new file mode 100644
index 00000000..b4e65486
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/construct_voronoi_cell.h
@@ -0,0 +1,39 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+#include "ae108/meshing/BoundaryRepresentation.h"
+
+namespace ae108 {
+namespace meshing {
+
+/**
+ * @brief Returns the boundary representation of the Voronoi cell spanned by a
+ * point cloud around the origin. The Voronoi cell is defined as the locus of
+ * all points closer to the origin than to any other point in the point cloud.
+ *
+ * @param point_cloud A vector of points, a.k.a. sites, forming the point cloud.
+ * @param periodic_faces A vector containing all periodic face pairs.
+ * @note https://en.wikipedia.org/wiki/Voronoi_diagram
+ */
+
+template <class SizeType, class ValueType, SizeType Dimension>
+BoundaryRepresentation<SizeType, ValueType, Dimension> construct_voronoi_cell(
+    const std::vector<std::array<ValueType, Dimension>> &point_cloud,
+    std::vector<std::pair<SizeType, SizeType>> *periodic_faces =
+        nullptr) noexcept;
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/Context.h b/meshing/src/include/ae108/meshing/cppgmsh/Context.h
new file mode 100644
index 00000000..7c9b83fd
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/Context.h
@@ -0,0 +1,57 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <memory>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+class Context {
+public:
+  /**
+   * @brief Initializes Gmsh. Gmsh is finalized automatically when the
+   * instance of Context is destroyed.
+   *
+   * @param argc `argc` as provided to main().
+   * @param argv `argv` as provided to main().
+   * @param readConfigFiles Option to read config file.
+   * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L63
+   */
+  explicit Context(int const argc, char **const argv,
+                   const bool readConfigFiles = true,
+                   const bool verbose = false);
+
+private:
+  /**
+   * @brief Initializes Gmsh.
+   */
+  static void initialize(int const argc, char **const argv,
+                         const bool readConfigFiles, const bool verbose);
+
+  /**
+   * @brief Finalizes Gmsh.
+   */
+  static void finalize(void *);
+
+  using Token = std::unique_ptr<Context, decltype(&finalize)>;
+  Token token_;
+};
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/Node.h b/meshing/src/include/ae108/meshing/cppgmsh/Node.h
new file mode 100644
index 00000000..78467423
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/Node.h
@@ -0,0 +1,43 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+template <std::size_t Dimension> struct Node {
+
+  using Index = std::size_t;
+  using Position = std::array<double, Dimension>;
+
+  Index id;
+  Position position;
+
+  bool operator<(const Node<Dimension> &another) const {
+    return id < another.id;
+  }
+
+  bool operator==(const Node<Dimension> &another) const {
+    return id == another.id;
+  }
+};
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/as_affine_transform.h b/meshing/src/include/ae108/meshing/cppgmsh/as_affine_transform.h
new file mode 100644
index 00000000..2a210c08
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/as_affine_transform.h
@@ -0,0 +1,35 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns affine transform (16 entries of a 4x4 matrix, by row) based on
+ * translation.
+ *
+ * @param translation The translation.
+ */
+std::array<double, 16>
+as_affine_transform(const std::array<double, 3> &translation) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/construct_box.h b/meshing/src/include/ae108/meshing/cppgmsh/construct_box.h
new file mode 100644
index 00000000..8370430b
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/construct_box.h
@@ -0,0 +1,38 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Constructs a parallelepipedic box using gmsh. Returns the entity.
+ *
+ * @param origin Position of the box origin
+ * @param side_lengths Side lengths of x-, y-, and z-side.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2398
+ */
+std::pair<int, int>
+construct_box(const std::array<double, 3> origin,
+              const std::array<double, 3> side_lengths) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/construct_cylinders.h b/meshing/src/include/ae108/meshing/cppgmsh/construct_cylinders.h
new file mode 100644
index 00000000..bddafea9
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/construct_cylinders.h
@@ -0,0 +1,43 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Constructs cylinders of differing radius from each segment using gmsh.
+ * Returns the entity.
+ *
+ * @param nodes Node positions.
+ * @param connectivity Nodal connectivity of each segment.
+ * @param radii Radii of all segments.
+ * @param capped Option to add a cap on each cylinder end.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2414
+ */
+std::vector<std::pair<int, int>>
+construct_cylinders(const std::vector<std::array<double, 3>> &nodes,
+                    const std::vector<std::array<std::size_t, 2>> &connectivity,
+                    const std::vector<double> &radii,
+                    const bool capped = true) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/construct_polyhedron.h b/meshing/src/include/ae108/meshing/cppgmsh/construct_polyhedron.h
new file mode 100644
index 00000000..6125490d
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/construct_polyhedron.h
@@ -0,0 +1,37 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include "ae108/meshing/BoundaryRepresentation.h"
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Constructs a solid polyhedron from a BoundaryRepresentation using
+ * gmsh. Returns the gmsh entity.
+ *
+ * @param brep A boundary representation containing the polyhedron.
+ *
+ */
+template <class SizeType, class ValueType>
+std::pair<int, int> construct_polyhedron(
+    const BoundaryRepresentation<SizeType, ValueType, 3> &brep) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/construct_rectangle.h b/meshing/src/include/ae108/meshing/cppgmsh/construct_rectangle.h
new file mode 100644
index 00000000..9e9a981b
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/construct_rectangle.h
@@ -0,0 +1,41 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Constructs a rectangle, with lower left corner at {x, y, z} and
+ * upper right corner at {x+dx,y+dy,z}. Returns the gmsh entity.
+ *
+ * @param origin Position of the rectangle origin {x,y,z}.
+ * @param side_lengths Side lengths: {dx,dy}, {dy,dz} or {dx,dz}, according to
+ * plane.
+ * @param rounded_radius If > 0 the corners are rounded.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2229
+ */
+std::pair<int, int>
+construct_rectangle(const std::array<double, 3> origin,
+                    const std::array<double, 2> side_lengths,
+                    const double rounded_radius = 0.) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/copy_entities.h b/meshing/src/include/ae108/meshing/cppgmsh/copy_entities.h
new file mode 100644
index 00000000..1c3ee77c
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/copy_entities.h
@@ -0,0 +1,35 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Copies the given entities and returns the copies.
+ *
+ * @param entities Original entities.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2729
+ */
+std::vector<std::pair<int, int>>
+copy_entities(const std::vector<std::pair<int, int>> &entities) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/extract_mesh.h b/meshing/src/include/ae108/meshing/cppgmsh/extract_mesh.h
new file mode 100644
index 00000000..9aa4766d
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/extract_mesh.h
@@ -0,0 +1,42 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+#include <map>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Extract mesh from Gmsh.
+ *
+ * @param element_dimension dimension of the elements of interest.
+ */
+template <std::size_t coordinate_dimension>
+
+std::tuple<std::vector<std::array<double, coordinate_dimension>>,
+           std::vector<std::vector<std::size_t>>,
+           std::map<std::size_t, std::size_t>,
+           std::map<std::size_t, std::size_t>>
+extract_mesh(
+    const std::size_t element_dimension = coordinate_dimension) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/extrude_entities.h b/meshing/src/include/ae108/meshing/cppgmsh/extrude_entities.h
new file mode 100644
index 00000000..60344385
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/extrude_entities.h
@@ -0,0 +1,44 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Extrudes the given entities along a translation vector. Returns the
+ * extruded entities.
+ * @param entities Entities to extrude_entities.
+ * @param translation Extrusion vector.
+ * @param extrude_mesh If true, the mesh of the base area of the extruded bodies
+ * will be extruded as well, i.e. the same layer of 3D elements will be repeated
+ * several times. Otherwise, The bodies are meshed as general 3D bodies.
+ * @param number_of_layers Number of element layers (if extrude_mesh is true).
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2514
+ */
+std::vector<std::pair<int, int>>
+extrude_entities(const std::vector<std::pair<int, int>> &entities,
+                 const std::array<double, 3> &translation,
+                 const bool extrude_mesh = false,
+                 const int number_of_layers = 1) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/extrude_surface.h b/meshing/src/include/ae108/meshing/cppgmsh/extrude_surface.h
new file mode 100644
index 00000000..048ab3bf
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/extrude_surface.h
@@ -0,0 +1,39 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Extrudes the surface entity from origin to target.
+ *
+ * @param surface_tag Tag of the surface defining the cross-section in xy-plane.
+ * @param from Position of the origin.
+ * @param to Position of the target.
+ * @param surface_normal Normal of the surface.
+ */
+std::pair<int, int> extrude_surface(
+    const int surface_tag, const std::array<double, 3> &from,
+    const std::array<double, 3> &to,
+    const std::array<double, 3> &surface_normal = {{0, 0, 1}}) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/fragment_entities.h b/meshing/src/include/ae108/meshing/cppgmsh/fragment_entities.h
new file mode 100644
index 00000000..62e9e57f
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/fragment_entities.h
@@ -0,0 +1,36 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Fuses the given entities, while keeping/creating a separate entity for
+ * each boolean fragment. All interfaces among the fragments are made conformal.
+ *
+ * @param entities Separate entities.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2651
+ */
+std::vector<std::pair<int, int>>
+fragment_entities(const std::vector<std::pair<int, int>> &entities) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/fuse_entities.h b/meshing/src/include/ae108/meshing/cppgmsh/fuse_entities.h
new file mode 100644
index 00000000..070177cb
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/fuse_entities.h
@@ -0,0 +1,35 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns vector of fused entities.
+ *
+ * @param entities Seperate entities.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2600
+ */
+std::vector<std::pair<int, int>>
+fuse_entities(const std::vector<std::pair<int, int>> &entities) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/generate_mesh.h b/meshing/src/include/ae108/meshing/cppgmsh/generate_mesh.h
new file mode 100644
index 00000000..a10fcd16
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/generate_mesh.h
@@ -0,0 +1,36 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Generate mesh. The mesh can be exported with extract_mesh.
+ *
+ * @param dimension The desired meshing dimension (e.g. volume (3) or surface
+ * (2) mesh).
+ * @param order The desired element order. See gmsh docs.
+ * @param algorithm The desired meshing algorithm. See gmsh docs.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L585
+ */
+void generate_mesh(const int dimension = 3, const int order = 1,
+                   const int algorithm = 6) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_boundary_of.h b/meshing/src/include/ae108/meshing/cppgmsh/get_boundary_of.h
new file mode 100644
index 00000000..398f05e3
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_boundary_of.h
@@ -0,0 +1,38 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns boundary entities (dim-1) of the specified entities, or
+ * optionally the boundary points, if recursive = true.
+ *
+ * @param entities Entities of interest.
+ * @param recursive If true, returns boundary points (dim=0).
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L284
+ */
+std::vector<std::pair<int, int>>
+get_boundary_of(const std::vector<std::pair<int, int>> &entities,
+                const bool recursive = false) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_centroid_of.h b/meshing/src/include/ae108/meshing/cppgmsh/get_centroid_of.h
new file mode 100644
index 00000000..21c34e51
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_centroid_of.h
@@ -0,0 +1,35 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns the centroid coordinates of the entity.
+ *
+ * @param entity The point tag.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2834
+ */
+std::array<double, 3>
+get_centroid_of(const std::pair<int, int> &entity) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_coords_of.h b/meshing/src/include/ae108/meshing/cppgmsh/get_coords_of.h
new file mode 100644
index 00000000..76b48c85
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_coords_of.h
@@ -0,0 +1,42 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns coordinates of point
+ *
+ * @param point_tag The point tag.
+ */
+std::array<double, 3> get_coords_of(const int point_tag) noexcept;
+
+/**
+ * @brief Returns coordinates of points
+ *
+ * @param point_tags The point tags.
+ */
+std::vector<std::array<double, 3>>
+get_coords_of(const std::vector<int> &point_tags) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_elements_in.h b/meshing/src/include/ae108/meshing/cppgmsh/get_elements_in.h
new file mode 100644
index 00000000..9197b77d
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_elements_in.h
@@ -0,0 +1,36 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns the element types and tags of the elements in the
+ * specified entity.
+ *
+ * @param entity The entity {dim, tag}.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L771
+ */
+std::vector<std::pair<int, std::size_t>>
+get_elements_in(const std::pair<int, int> &entity) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_entities_in.h b/meshing/src/include/ae108/meshing/cppgmsh/get_entities_in.h
new file mode 100644
index 00000000..82132438
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_entities_in.h
@@ -0,0 +1,49 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include "ae108/meshing/BoundingBox.h"
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns all entities of dimension inside of a bounding box
+ *
+ * @param bounding_box The bounding box or search domain.
+ * @param dimension Entity dimension, i.e 3-volume, 2-surface, 1-line, 0-point
+ * @param tol Spatial tolerance.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L307
+ */
+std::vector<std::pair<int, int>>
+get_entities_in(const BoundingBox<std::array<double, 3>> &bounding_box,
+                const int dimension, const double tol = 1e-6) noexcept;
+
+/**
+ * @brief Returns all entities in the specified physical group.
+ *
+ * @param physical_group The physical group {dim, tag}.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L230
+ */
+std::vector<std::pair<int, int>>
+get_entities_in(const std::pair<int, int> &physical_group) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_entities_of.h b/meshing/src/include/ae108/meshing/cppgmsh/get_entities_of.h
new file mode 100644
index 00000000..1dd2c496
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_entities_of.h
@@ -0,0 +1,35 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns all geometric entities of all or just the specified dimension.
+ *
+ * @param dim The dimension of interest. If < 0, the entities of all dimensions
+ * are returned.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2792
+ */
+std::vector<std::pair<int, int>> get_entities_of(const int dim = -1) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_nodes_of.h b/meshing/src/include/ae108/meshing/cppgmsh/get_nodes_of.h
new file mode 100644
index 00000000..9da697f0
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_nodes_of.h
@@ -0,0 +1,44 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include "ae108/meshing/cppgmsh/Node.h"
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Get the nodes of one or more entities. Optionally avoids
+ * duplicate nodes (e.g. on entity boundaries). Returns a vector of Nodes.
+ *
+ * @param dim The dimension of the model entities of interest. Set to < 0 to get
+ * all nodes of the mesh.
+ * @param tag The tag of the model entity of interest. Set to < 0 to get the
+ * nodes of all entities of dimension dim.
+ * @param remove_duplicates If true, node duplicates are removed.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L662
+ */
+
+template <std::size_t Dimension>
+std::vector<Node<Dimension>>
+get_nodes_of(const std::pair<int, int> &entity,
+             const bool remove_duplicates = true) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_normal_of.h b/meshing/src/include/ae108/meshing/cppgmsh/get_normal_of.h
new file mode 100644
index 00000000..59f39d97
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_normal_of.h
@@ -0,0 +1,34 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns the normal of a surface.
+ *
+ * @param surface_tag The surface tag.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L462
+ */
+std::array<double, 3> get_normal_of(const int surface_tag) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_periodic_nodes_of.h b/meshing/src/include/ae108/meshing/cppgmsh/get_periodic_nodes_of.h
new file mode 100644
index 00000000..476fa505
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_periodic_nodes_of.h
@@ -0,0 +1,41 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns the corresponding node tags of the target entity (out.first)
+ * and the source entity (out.second)
+ *
+ * @param target_entity Target entity.
+ * @param source_entity Optional output of the source entity.
+ * @param affine_transform Optional output of the affine transformation.
+ */
+std::pair<std::vector<std::size_t>, std::vector<std::size_t>>
+get_periodic_nodes_of(
+    const std::pair<int, int> &target_entity,
+    std::pair<int, int> *source_entity = nullptr,
+    std::array<double, 16> *affine_transform = nullptr) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_physical_groups.h b/meshing/src/include/ae108/meshing/cppgmsh/get_physical_groups.h
new file mode 100644
index 00000000..5a3cfbca
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_physical_groups.h
@@ -0,0 +1,36 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns the physical groups in the model.
+ *
+ * @param dim The dimension of the physical groups to return, if < 0, the groups
+ * of all dimensions are returned.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L223
+ */
+std::vector<std::pair<int, int>>
+get_physical_groups(const int dim = -1) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/get_points_of.h b/meshing/src/include/ae108/meshing/cppgmsh/get_points_of.h
new file mode 100644
index 00000000..d9f769d6
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/get_points_of.h
@@ -0,0 +1,33 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns tags of all points of an entity.
+ *
+ * @param entity The entity.
+ */
+std::vector<int> get_points_of(const std::pair<int, int> &entity) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/heal_periodic_domains.h b/meshing/src/include/ae108/meshing/cppgmsh/heal_periodic_domains.h
new file mode 100644
index 00000000..e4de776c
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/heal_periodic_domains.h
@@ -0,0 +1,41 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include "ae108/meshing/BoundingBox.h"
+#include <array>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Matches points between all periodic surfaces in the source and target
+ * domain. If a point on the source surface is missing on the target surface,
+ * it will be added to the target surface. Assumption: Source and target domain
+ * are translational periodic.
+ *
+ * @param source Source domain spanned by a bounding box.
+ * @param target Target domain spanned by a bounding box.
+ * @param tol Tolerance for point matching.
+ */
+void heal_periodic_domains(const BoundingBox<std::array<double, 3>> &source,
+                           const BoundingBox<std::array<double, 3>> &target,
+                           const double tol = 1e-9) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/heal_periodic_surfaces.h b/meshing/src/include/ae108/meshing/cppgmsh/heal_periodic_surfaces.h
new file mode 100644
index 00000000..eca339f8
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/heal_periodic_surfaces.h
@@ -0,0 +1,41 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Matches points between source and target surface. If a point on the
+ * source surface is missing on the target surface, it will be added to the
+ * target surface. Source and target surface are translational periodic.
+ *
+ * @param source Source surface tag.
+ * @param target Target surface tag.
+ * @param translation Translation vector from source to target.
+ * @param tol Tolerance for point matching.
+ *
+ */
+void heal_periodic_surfaces(const int source, const int target,
+                            const std::array<double, 3> &translation,
+                            const double tol = 1e-9) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/import_shapes.h b/meshing/src/include/ae108/meshing/cppgmsh/import_shapes.h
new file mode 100644
index 00000000..568ac8ec
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/import_shapes.h
@@ -0,0 +1,38 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Imports shapes from a BREP, STEP or IGES file.
+ * @param fileName Name of the imported file.
+ * @param highestDimOnly If true, only the highest dimensional entities are
+ * imported.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2770
+ */
+std::vector<std::pair<int, int>>
+import_shapes(const std::string &fileName,
+              const bool highestDimOnly = true) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/intersect_entities.h b/meshing/src/include/ae108/meshing/cppgmsh/intersect_entities.h
new file mode 100644
index 00000000..0b84f654
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/intersect_entities.h
@@ -0,0 +1,43 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Returns vector of intersected entities.
+ *
+ * @param object_entities Object entities.
+ * @param tool_entities Tool entities.
+ * @param remove_object If true, the object entities will be removed after the
+ * intersection was performed.
+ * @param remove_tool If true, the tool entities will be removed after the
+ * intersection was performed.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2616
+ */
+std::vector<std::pair<int, int>>
+intersect_entities(const std::vector<std::pair<int, int>> &object_entities,
+                   const std::vector<std::pair<int, int>> &tool_entities,
+                   const bool remove_object = true,
+                   const bool remove_tool = true) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/read_file.h b/meshing/src/include/ae108/meshing/cppgmsh/read_file.h
new file mode 100644
index 00000000..d134002c
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/read_file.h
@@ -0,0 +1,36 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <string>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Reads a file. The export format is determined by the file
+ * extension (e.g. *.vtk).
+ *
+ * @param file_name The file name.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L77
+ */
+
+void read_file(const std::string &file_name) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/remove_entities.h b/meshing/src/include/ae108/meshing/cppgmsh/remove_entities.h
new file mode 100644
index 00000000..b1e83707
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/remove_entities.h
@@ -0,0 +1,37 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Removes the given entities.
+ *
+ * @param entities Entities to remove.
+ * @param recursive If true, all entities on the boundaries of the given entity
+ * are removed as well, down to dimension 0 (points).
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2737
+ */
+void remove_entities(const std::vector<std::pair<int, int>> &entities,
+                     const bool recursive = false) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/rotate_entities.h b/meshing/src/include/ae108/meshing/cppgmsh/rotate_entities.h
new file mode 100644
index 00000000..be15b35a
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/rotate_entities.h
@@ -0,0 +1,41 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Rotates the given entities.
+ *
+ * @param entities Entities to rotate.
+ * @param center Center of rotation.
+ * @param direction Direction of the axis of rotation.
+ * @param angle Angle of rotation measured in radians.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2673
+ */
+void rotate_entities(const std::vector<std::pair<int, int>> &entities,
+                     const std::array<double, 3> &center,
+                     const std::array<double, 3> &direction,
+                     double angle) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/set_domain_entities_periodic.h b/meshing/src/include/ae108/meshing/cppgmsh/set_domain_entities_periodic.h
new file mode 100644
index 00000000..28b8fc8b
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/set_domain_entities_periodic.h
@@ -0,0 +1,41 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include "ae108/meshing/BoundingBox.h"
+#include <array>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Set the meshes of the entities in domain target as
+ * periodic copies of the meshes of entities in domain source
+ *
+ * @param source Source domain.
+ * @param target Target domain.
+ * @param dim Dimension of the entities (1 or 2).
+ * @param tol Spatial tolerance for entity matching.
+ */
+void set_domain_entities_periodic(
+    const BoundingBox<std::array<double, 3>> &source,
+    const BoundingBox<std::array<double, 3>> &target, const int dim,
+    const double tol = 1e-9) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/set_expert_mode.h b/meshing/src/include/ae108/meshing/cppgmsh/set_expert_mode.h
new file mode 100644
index 00000000..59fc067b
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/set_expert_mode.h
@@ -0,0 +1,32 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Enables/disables expert mode (to disable all the messages and user
+ * input requests meant for inexperienced users)
+ * @param enable Specifies if the expert mode should be enabled (true) or
+ * disabled (false).
+ */
+void set_expert_mode(const bool enable = true) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/set_granularity.h b/meshing/src/include/ae108/meshing/cppgmsh/set_granularity.h
new file mode 100644
index 00000000..17ca0cad
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/set_granularity.h
@@ -0,0 +1,32 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Sets the mesh element length scale
+ *
+ * @param size The desired maximum mesh size for the volume mesh.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L1225
+ */
+void set_granularity(const double size) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/set_periodic.h b/meshing/src/include/ae108/meshing/cppgmsh/set_periodic.h
new file mode 100644
index 00000000..0074219b
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/set_periodic.h
@@ -0,0 +1,43 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Sets the mesh of all target entities as periodic copies of the source
+ * entity, using the affine transformation.
+ *
+ * The function supportes line (dim=1) and surface (dim=2) entities. Target and
+ * surface entity need to have the same dimension.
+ *
+ * @param target_entities Target entities.
+ * @param source_entity Source entity.
+ * @param affine_transform Affine transformation matrix (4x4 matrix, by row)
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L1423
+ */
+void set_periodic(const std::vector<std::pair<int, int>> &target_entities,
+                  const std::pair<int, int> &source_entity,
+                  const std::array<double, 16> &affine_transform) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/set_physical_group_of.h b/meshing/src/include/ae108/meshing/cppgmsh/set_physical_group_of.h
new file mode 100644
index 00000000..21f3e4ff
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/set_physical_group_of.h
@@ -0,0 +1,36 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Adds entities to a physical group. Returns the tag of the added group.
+ *
+ * @param dim The dimension of the model entities to be grouped.
+ * @param tags The tags of the model entities to be grouped.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L247
+ */
+std::pair<int, int> set_physical_group_of(
+    const std::vector<std::pair<int, int>> &entities) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/synchronize.h b/meshing/src/include/ae108/meshing/cppgmsh/synchronize.h
new file mode 100644
index 00000000..54e798a7
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/synchronize.h
@@ -0,0 +1,31 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Synchronize the built-in CAD representation with the current Gmsh
+ * model.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L1983
+ */
+void synchronize() noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/translate_entities.h b/meshing/src/include/ae108/meshing/cppgmsh/translate_entities.h
new file mode 100644
index 00000000..deeacd3b
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/translate_entities.h
@@ -0,0 +1,41 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Translates the given entities by a vector or makes a translated copy
+ * of the entities. Returns the translated entity.
+ *
+ * @param entities Entities to translate.
+ * @param translation translation vector.
+ * @param copy It true, a copy of the entities is translated.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L2663
+ */
+std::vector<std::pair<int, int>>
+translate_entities(std::vector<std::pair<int, int>> entities,
+                   const std::array<double, 3> &translation,
+                   const bool copy = false) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/src/include/ae108/meshing/cppgmsh/write_file.h b/meshing/src/include/ae108/meshing/cppgmsh/write_file.h
new file mode 100644
index 00000000..9bb87f5e
--- /dev/null
+++ b/meshing/src/include/ae108/meshing/cppgmsh/write_file.h
@@ -0,0 +1,35 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#pragma once
+
+#include <string>
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+/**
+ * @brief Writes a file.
+ *
+ * @param file_name The bounding box or search domain.
+ * @note https://gitlab.onelab.info/gmsh/gmsh/-/blob/gmsh_4_8_4/api/gmsh.h#L89
+ */
+
+void write_file(const std::string &file_name) noexcept;
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/BoundaryRepresentation_Test.cc b/meshing/test/BoundaryRepresentation_Test.cc
new file mode 100644
index 00000000..859bf9c8
--- /dev/null
+++ b/meshing/test/BoundaryRepresentation_Test.cc
@@ -0,0 +1,110 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/BoundaryRepresentation.h"
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+struct BoundaryRepresentation3D_Test : Test {
+
+  using brep_type = BoundaryRepresentation<int, double, 3>;
+  using vertices_type = typename brep_type::Vertices;
+  using edges_type = typename brep_type::Edges;
+  using faces_type = typename brep_type::Faces;
+
+  vertices_type vertices = {{
+      {{0, 0, 0}},
+      {{1, 0, 0}},
+      {{0, 1, 0}},
+      {{0, 0, 1}},
+  }};
+  edges_type edges = {{
+      {{0, 1}},
+      {{0, 2}},
+      {{0, 3}},
+      {{1, 3}},
+      {{2, 3}},
+      {{2, 1}},
+  }};
+  faces_type faces = {{
+      {{0, 1, 5}},
+      {{0, 1, 3}},
+      {{0, 2, 3}},
+      {{1, 2, 3}},
+  }};
+
+  brep_type brep{vertices, edges, faces};
+};
+
+struct BoundaryRepresentation2D_Test : Test {
+
+  using brep_type = BoundaryRepresentation<int, double, 2>;
+  using vertices_type = typename brep_type::Vertices;
+  using edges_type = typename brep_type::Edges;
+  using faces_type = typename brep_type::Faces;
+
+  vertices_type vertices = {{
+      {{0, 0}},
+      {{1, 0}},
+      {{0, 1}},
+  }};
+  edges_type edges = {{
+      {{0, 1}},
+      {{0, 2}},
+      {{2, 1}},
+  }};
+  faces_type faces = {{
+      {{0, 1, 2}},
+  }};
+
+  brep_type brep{vertices, edges, faces};
+};
+
+TEST_F(BoundaryRepresentation3D_Test, returns_correct_dimension) {
+  EXPECT_THAT(brep.dimension(), Eq(3));
+};
+
+TEST_F(BoundaryRepresentation3D_Test, returns_correct_vertices_of_edge) {
+  EXPECT_THAT(brep.vertices_of_edge(3)[0], Eq(vertices.at(1)));
+  EXPECT_THAT(brep.vertices_of_edge(3)[1], Eq(vertices.at(3)));
+};
+
+TEST_F(BoundaryRepresentation3D_Test, returns_correct_vertices_of_face) {
+  EXPECT_THAT(brep.vertices_of_face(0)[0], Eq(vertices.at(0)));
+  EXPECT_THAT(brep.vertices_of_face(0)[1], Eq(vertices.at(1)));
+  EXPECT_THAT(brep.vertices_of_face(0)[2], Eq(vertices.at(2)));
+};
+
+TEST_F(BoundaryRepresentation2D_Test, returns_correct_dimension) {
+  EXPECT_THAT(brep.dimension(), Eq(2));
+};
+
+TEST_F(BoundaryRepresentation2D_Test, returns_correct_vertices_of_edge) {
+  EXPECT_THAT(brep.vertices_of_edge(2)[0], Eq(vertices.at(2)));
+  EXPECT_THAT(brep.vertices_of_edge(2)[1], Eq(vertices.at(1)));
+};
+
+TEST_F(BoundaryRepresentation2D_Test, returns_correct_vertices_of_face) {
+  EXPECT_THAT(brep.vertices_of_face(0)[0], Eq(vertices.at(0)));
+  EXPECT_THAT(brep.vertices_of_face(0)[1], Eq(vertices.at(1)));
+  EXPECT_THAT(brep.vertices_of_face(0)[2], Eq(vertices.at(2)));
+};
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/BoundingBox_Test.cc b/meshing/test/BoundingBox_Test.cc
new file mode 100644
index 00000000..c06d530a
--- /dev/null
+++ b/meshing/test/BoundingBox_Test.cc
@@ -0,0 +1,35 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/BoundingBox.h"
+#include <array>
+#include <gmock/gmock.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+struct BoundingBox_Test : Test {};
+
+TEST_F(BoundingBox_Test, returns_correct_bbox) {
+  const auto bbox = BoundingBox<std::array<double, 3>>({{0, 0, 0}, {1, 1, 1}});
+  EXPECT_THAT(bbox.min, ElementsAre(DoubleEq(0.), DoubleEq(0.), DoubleEq(0.)));
+  EXPECT_THAT(bbox.max, ElementsAre(DoubleEq(1.), DoubleEq(1.), DoubleEq(1.)));
+};
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/CMakeLists.txt b/meshing/test/CMakeLists.txt
new file mode 100644
index 00000000..8363235b
--- /dev/null
+++ b/meshing/test/CMakeLists.txt
@@ -0,0 +1,74 @@
+# © 2021 ETH Zurich, Mechanics and Materials Lab
+#
+# This file is part of ae108.
+#
+# ae108 is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or any
+# later version.
+#
+# ae108 is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
+
+add_executable(${PROJECT_NAME}-MeshingTests
+        cppgmsh/construct_cylinders_Test.cc
+        cppgmsh/construct_polyhedron_Test.cc
+        cppgmsh/construct_rectangle_Test.cc
+        cppgmsh/construct_box_Test.cc
+        cppgmsh/copy_entities_Test.cc
+        cppgmsh/extrude_entities_Test.cc
+        cppgmsh/extrude_surface_Test.cc
+        cppgmsh/fragment_entities_Test.cc
+        cppgmsh/fuse_entities_Test.cc
+        cppgmsh/intersect_entities_Test.cc
+        cppgmsh/remove_entities_Test.cc
+        cppgmsh/rotate_entities_Test.cc
+        cppgmsh/translate_entities_Test.cc
+        cppgmsh/generate_mesh_Test.cc
+        cppgmsh/extract_mesh_Test.cc
+        # cppgmsh/heal_periodic_surfaces_Test.cc
+        # cppgmsh/heal_periodic_domains_Test.cc
+        cppgmsh/as_affine_transform_Test.cc
+        cppgmsh/get_boundary_of_Test.cc
+        cppgmsh/get_centroid_of_Test.cc
+        cppgmsh/get_coords_of_Test.cc
+        cppgmsh/get_elements_in_Test.cc
+        cppgmsh/get_entities_in_Test.cc
+        cppgmsh/get_entities_of_Test.cc
+        cppgmsh/get_nodes_of_Test.cc
+        cppgmsh/get_normal_of_Test.cc
+        cppgmsh/get_periodic_nodes_of_Test.cc
+        cppgmsh/get_physical_groups_Test.cc
+        cppgmsh/get_points_of_Test.cc
+        cppgmsh/set_domain_entities_periodic_Test.cc
+        cppgmsh/set_expert_mode_Test.cc
+        cppgmsh/set_granularity_Test.cc
+        cppgmsh/set_periodic_Test.cc
+        cppgmsh/set_physical_group_of_Test.cc
+        cppgmsh/synchronize_Test.cc
+        cppgmsh/import_shapes_Test.cc
+        cppgmsh/read_file_Test.cc
+        cppgmsh/write_file_Test.cc
+        BoundaryRepresentation_Test.cc
+        BoundingBox_Test.cc
+        bounding_box_of_Test.cc
+        construct_rectilinear_grid_Test.cc
+        construct_periodic_point_cloud_Test.cc
+        construct_voronoi_cell_Test.cc
+)
+
+add_test(NAME ${PROJECT_NAME}-MeshingTests
+         COMMAND $<TARGET_FILE:${PROJECT_NAME}-MeshingTests>)
+
+find_package(GTest 1.8.1 CONFIG REQUIRED)
+target_link_libraries(${PROJECT_NAME}-MeshingTests
+                      PRIVATE ${PROJECT_NAME}::meshing
+                      PRIVATE GTest::gmock_main
+)
diff --git a/meshing/test/bounding_box_of_Test.cc b/meshing/test/bounding_box_of_Test.cc
new file mode 100644
index 00000000..b8956041
--- /dev/null
+++ b/meshing/test/bounding_box_of_Test.cc
@@ -0,0 +1,41 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/bounding_box_of.h"
+#include <gmock/gmock.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+struct bounding_box_of_Test : Test {};
+
+TEST_F(bounding_box_of_Test, returns_correct_bounding_box_of_single_point) {
+  const auto box = bounding_box_of<std::array<double, 2>>({{1, 2}});
+  ASSERT_THAT(box.min, ElementsAre(DoubleEq(1), DoubleEq(2)));
+  ASSERT_THAT(box.max, ElementsAre(DoubleEq(1), DoubleEq(2)));
+};
+
+TEST_F(bounding_box_of_Test, returns_correct_bounding_box_for_multiple_point) {
+  const auto box = bounding_box_of<std::array<double, 3>>(
+      {{1, 2, 3}, {-1, 2, -3}, {3, 2, 1}});
+  ASSERT_THAT(box.min, ElementsAre(DoubleEq(-1), DoubleEq(2), DoubleEq(-3)));
+  ASSERT_THAT(box.max, ElementsAre(DoubleEq(3), DoubleEq(2), DoubleEq(3)));
+};
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/construct_periodic_point_cloud_Test.cc b/meshing/test/construct_periodic_point_cloud_Test.cc
new file mode 100644
index 00000000..5657743d
--- /dev/null
+++ b/meshing/test/construct_periodic_point_cloud_Test.cc
@@ -0,0 +1,89 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/construct_periodic_point_cloud.h"
+#include <gmock/gmock.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+struct construct_periodic_point_cloud_Test : Test {};
+
+TEST_F(construct_periodic_point_cloud_Test,
+       returns_correct_nonsymmetric_3D_point_cloud) {
+
+  const auto point_cloud = construct_periodic_point_cloud<3>(
+      {{{1, 0, 0}, {0, 2, 0}, {0, 0, 3}}}, {0, 0, 0}, {1, 0});
+
+  ASSERT_THAT(point_cloud.size(), Eq(8));
+  ASSERT_THAT(point_cloud[0],
+              ElementsAre(DoubleEq(1), DoubleEq(2), DoubleEq(3)));
+  ASSERT_THAT(point_cloud[7],
+              ElementsAre(DoubleEq(0), DoubleEq(0), DoubleEq(0)));
+};
+
+TEST_F(construct_periodic_point_cloud_Test,
+       returns_correct_3D_point_cloud_around_zero_origin) {
+
+  const auto point_cloud = construct_periodic_point_cloud<3>(
+      {{{1, 0, 0}, {0, 2, 0}, {0, 0, 3}}}, {0, 0, 0});
+
+  ASSERT_THAT(point_cloud.size(), Eq(27));
+  ASSERT_THAT(point_cloud[0],
+              ElementsAre(DoubleEq(-1), DoubleEq(-2), DoubleEq(-3)));
+  ASSERT_THAT(point_cloud[26],
+              ElementsAre(DoubleEq(1), DoubleEq(2), DoubleEq(3)));
+};
+
+TEST_F(construct_periodic_point_cloud_Test,
+       returns_correct_3D_point_cloud_around_nonzero_origin) {
+
+  const auto point_cloud = construct_periodic_point_cloud<3>(
+      {{{1, 0, 0}, {0, 2, 0}, {0, 0, 3}}}, {1, 2, 3});
+
+  ASSERT_THAT(point_cloud.size(), Eq(27));
+  ASSERT_THAT(point_cloud[0],
+              ElementsAre(DoubleEq(0), DoubleEq(0), DoubleEq(0)));
+  ASSERT_THAT(point_cloud[26],
+              ElementsAre(DoubleEq(2), DoubleEq(4), DoubleEq(6)));
+};
+
+TEST_F(construct_periodic_point_cloud_Test,
+       returns_correct_2D_point_cloud_around_zero_origin) {
+
+  const auto point_cloud =
+      construct_periodic_point_cloud<2>({{{1, 0}, {0, 2}}}, {0, 0});
+
+  ASSERT_THAT(point_cloud.size(), Eq(9));
+  ASSERT_THAT(point_cloud[0], ElementsAre(DoubleEq(-1), DoubleEq(-2)));
+  ASSERT_THAT(point_cloud[8], ElementsAre(DoubleEq(1), DoubleEq(2)));
+};
+
+TEST_F(construct_periodic_point_cloud_Test,
+       returns_correct_1D_point_cloud_around_zero_origin) {
+
+  const auto point_cloud = construct_periodic_point_cloud<1>({{{2}}}, {0});
+
+  ASSERT_THAT(point_cloud.size(), Eq(3));
+  ASSERT_THAT(point_cloud[0], ElementsAre(DoubleEq(-2)));
+  ASSERT_THAT(point_cloud[2], ElementsAre(DoubleEq(2)));
+};
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/construct_rectilinear_grid_Test.cc b/meshing/test/construct_rectilinear_grid_Test.cc
new file mode 100644
index 00000000..ed769bbd
--- /dev/null
+++ b/meshing/test/construct_rectilinear_grid_Test.cc
@@ -0,0 +1,75 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/construct_rectilinear_grid.h"
+#include <gmock/gmock.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+struct construct_rectilinear_grid_Test : Test {};
+
+TEST_F(construct_rectilinear_grid_Test, returns_correct_1D_grid) {
+
+  std::array<std::vector<double>, 1> instances = {{{1, 2}}};
+  const auto [nodes, segments] = construct_rectilinear_grid(instances);
+
+  ASSERT_THAT(nodes.size(), Eq(2));
+  ASSERT_THAT(segments.size(), Eq(1));
+  ASSERT_THAT(nodes[0], ElementsAre(DoubleEq(1)));
+  ASSERT_THAT(nodes[1], ElementsAre(DoubleEq(2)));
+  ASSERT_THAT(segments[0], ElementsAre(Eq(0), Eq(1)));
+};
+
+TEST_F(construct_rectilinear_grid_Test, returns_correct_2D_grid) {
+
+  std::array<std::vector<double>, 2> instances = {{{1, 2}, {3, 4}}};
+  const auto [nodes, segments] = construct_rectilinear_grid(instances);
+
+  ASSERT_THAT(nodes.size(), Eq(4));
+  ASSERT_THAT(segments.size(), Eq(4));
+  ASSERT_THAT(nodes[0], ElementsAre(DoubleEq(1), DoubleEq(3)));
+  ASSERT_THAT(nodes[1], ElementsAre(DoubleEq(1), DoubleEq(4)));
+  ASSERT_THAT(nodes[2], ElementsAre(DoubleEq(2), DoubleEq(3)));
+  ASSERT_THAT(nodes[3], ElementsAre(DoubleEq(2), DoubleEq(4)));
+  ASSERT_THAT(segments[0], ElementsAre(Eq(0), Eq(1)));
+  ASSERT_THAT(segments[1], ElementsAre(Eq(2), Eq(3)));
+  ASSERT_THAT(segments[2], ElementsAre(Eq(0), Eq(2)));
+  ASSERT_THAT(segments[3], ElementsAre(Eq(1), Eq(3)));
+};
+
+TEST_F(construct_rectilinear_grid_Test, returns_correct_3D_grid) {
+
+  std::array<std::vector<double>, 3> instances = {{{1, 2}, {3, 4}, {0}}};
+  const auto [nodes, segments] = construct_rectilinear_grid(instances);
+
+  ASSERT_THAT(nodes.size(), Eq(4));
+  ASSERT_THAT(segments.size(), Eq(4));
+  ASSERT_THAT(nodes[0], ElementsAre(DoubleEq(1), DoubleEq(3), DoubleEq(0)));
+  ASSERT_THAT(nodes[1], ElementsAre(DoubleEq(1), DoubleEq(4), DoubleEq(0)));
+  ASSERT_THAT(nodes[2], ElementsAre(DoubleEq(2), DoubleEq(3), DoubleEq(0)));
+  ASSERT_THAT(nodes[3], ElementsAre(DoubleEq(2), DoubleEq(4), DoubleEq(0)));
+  ASSERT_THAT(segments[0], ElementsAre(Eq(0), Eq(1)));
+  ASSERT_THAT(segments[1], ElementsAre(Eq(2), Eq(3)));
+  ASSERT_THAT(segments[2], ElementsAre(Eq(0), Eq(2)));
+  ASSERT_THAT(segments[3], ElementsAre(Eq(1), Eq(3)));
+};
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/construct_voronoi_cell_Test.cc b/meshing/test/construct_voronoi_cell_Test.cc
new file mode 100644
index 00000000..086547d4
--- /dev/null
+++ b/meshing/test/construct_voronoi_cell_Test.cc
@@ -0,0 +1,74 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/construct_periodic_point_cloud.h"
+#include "ae108/meshing/construct_voronoi_cell.h"
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+struct construct_voronoi_cell_Test : Test {};
+
+TEST_F(construct_voronoi_cell_Test, returns_correct_cube) {
+
+  const auto point_cloud = construct_periodic_point_cloud<3>(
+      {{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}}, {0, 0, 0});
+
+  std::vector<std::pair<std::size_t, std::size_t>> periodic_faces;
+  const auto voronoi_cell =
+      construct_voronoi_cell(point_cloud, &periodic_faces);
+
+  ASSERT_THAT(voronoi_cell.vertices.size(), Eq(8));
+  ASSERT_THAT(voronoi_cell.edges.size(), Eq(12));
+  ASSERT_THAT(voronoi_cell.faces.size(), Eq(6));
+  ASSERT_THAT(periodic_faces.size(), Eq(3));
+};
+
+TEST_F(construct_voronoi_cell_Test, returns_correct_hexagonal_prism) {
+
+  const auto point_cloud = construct_periodic_point_cloud<3>(
+      {{{sqrt(3.) / 2, 0.5, 0}, {sqrt(3.) / 2, -0.5, 0}, {0, 0, 1}}},
+      {0, 0, 0});
+
+  std::vector<std::pair<std::size_t, std::size_t>> periodic_faces;
+  const auto voronoi_cell =
+      construct_voronoi_cell(point_cloud, &periodic_faces);
+
+  ASSERT_THAT(voronoi_cell.vertices.size(), Eq(12));
+  ASSERT_THAT(voronoi_cell.edges.size(), Eq(18));
+  ASSERT_THAT(voronoi_cell.faces.size(), Eq(8));
+  ASSERT_THAT(periodic_faces.size(), Eq(4));
+};
+
+TEST_F(construct_voronoi_cell_Test, returns_correct_bitruncated_octahedron) {
+
+  const auto point_cloud = construct_periodic_point_cloud<3>(
+      {{{0.5, 0.5, 0.5}, {0.5, -0.5, 0.5}, {0.5, 0.5, -0.5}}}, {0, 0, 0});
+
+  std::vector<std::pair<std::size_t, std::size_t>> periodic_faces;
+  const auto voronoi_cell =
+      construct_voronoi_cell(point_cloud, &periodic_faces);
+
+  ASSERT_THAT(voronoi_cell.vertices.size(), Eq(24));
+  ASSERT_THAT(voronoi_cell.edges.size(), Eq(36));
+  ASSERT_THAT(voronoi_cell.faces.size(), Eq(14));
+  ASSERT_THAT(periodic_faces.size(), Eq(7));
+};
+
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/as_affine_transform_Test.cc b/meshing/test/cppgmsh/as_affine_transform_Test.cc
new file mode 100644
index 00000000..eb56904e
--- /dev/null
+++ b/meshing/test/cppgmsh/as_affine_transform_Test.cc
@@ -0,0 +1,41 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/as_affine_transform.h"
+#include <gmock/gmock.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct as_affine_transform_Test : Test {};
+
+TEST_F(as_affine_transform_Test, returns_correct_translation_transform) {
+  const auto transform = as_affine_transform({1., 2., 3.});
+  ASSERT_THAT(transform, ElementsAre(DoubleEq(1.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(1.), DoubleEq(0.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(2.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(1.), DoubleEq(3.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(1.)));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/construct_box_Test.cc b/meshing/test/cppgmsh/construct_box_Test.cc
new file mode 100644
index 00000000..f60e81fb
--- /dev/null
+++ b/meshing/test/cppgmsh/construct_box_Test.cc
@@ -0,0 +1,43 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct construct_box_Test : Test {};
+
+TEST_F(construct_box_Test, constructs_box) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto origin = std::array<double, 3>{0, 0, 0};
+  const auto side_lengths = std::array<double, 3>{1, 2, 3};
+
+  const auto box = construct_box(origin, side_lengths);
+
+  ASSERT_THAT(box.first, Eq(3));
+  ASSERT_THAT(box.second, Eq(1));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/construct_cylinders_Test.cc b/meshing/test/cppgmsh/construct_cylinders_Test.cc
new file mode 100644
index 00000000..6afd12e2
--- /dev/null
+++ b/meshing/test/cppgmsh/construct_cylinders_Test.cc
@@ -0,0 +1,75 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_cylinders.h"
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct construct_cylinders_Test : Test {};
+
+TEST_F(construct_cylinders_Test, constructs_empty) {
+  const auto gmshContext = Context(0, 0);
+  std::vector<std::array<double, 3>> nodes{{0, 0, 0}, {1, 0, 0}};
+  std::vector<std::array<std::size_t, 2>> connectivity{};
+  std::vector<double> radii{};
+  bool capped = false;
+
+  const auto result = construct_cylinders(nodes, connectivity, radii, capped);
+  ASSERT_THAT(result.size(), Eq(0));
+}
+
+TEST_F(construct_cylinders_Test, constructs_single_cylinder) {
+  const auto gmshContext = Context(0, 0);
+  std::vector<std::array<double, 3>> nodes{{0, 0, 0}, {1, 0, 0}};
+  std::vector<std::array<std::size_t, 2>> connectivity{{0, 1}};
+  std::vector<double> radii{1.};
+  bool capped = false;
+
+  const auto result = construct_cylinders(nodes, connectivity, radii, capped);
+  ASSERT_THAT(result.size(), Eq(1));
+}
+
+TEST_F(construct_cylinders_Test, constructs_two_cylinders) {
+  const auto gmshContext = Context(0, 0);
+  std::vector<std::array<double, 3>> nodes{
+      {0, 0, 0}, {1, 0, 0}, {0, 0, 1}, {1, 0, 1}};
+  std::vector<std::array<std::size_t, 2>> connectivity{{0, 1}, {2, 3}};
+  std::vector<double> radii{0.1, 0.1};
+  bool capped = false;
+
+  const auto result = construct_cylinders(nodes, connectivity, radii, capped);
+  ASSERT_THAT(result.size(), Eq(2));
+}
+
+TEST_F(construct_cylinders_Test, constructs_capped_cylinder) {
+  const auto gmshContext = Context(0, 0);
+  std::vector<std::array<double, 3>> nodes{{0, 0, 0}, {1, 0, 0}};
+  std::vector<std::array<std::size_t, 2>> connectivity{{0, 1}};
+  std::vector<double> radii{1.};
+  bool capped = true;
+
+  const auto result = construct_cylinders(nodes, connectivity, radii, capped);
+  ASSERT_THAT(result.size(), Eq(1));
+}
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/construct_polyhedron_Test.cc b/meshing/test/cppgmsh/construct_polyhedron_Test.cc
new file mode 100644
index 00000000..b31856da
--- /dev/null
+++ b/meshing/test/cppgmsh/construct_polyhedron_Test.cc
@@ -0,0 +1,104 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_polyhedron.h"
+#include "ae108/meshing/cppgmsh/get_entities_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct construct_polyhedron_Test : Test {
+
+  using brep_type = BoundaryRepresentation<std::size_t, double, 3>;
+  using vertices_type = typename brep_type::Vertices;
+  using edges_type = typename brep_type::Edges;
+  using faces_type = typename brep_type::Faces;
+
+  vertices_type vertices = {{
+      {{0, 0, 0}},
+      {{1, 0, 0}},
+      {{0, 1, 0}},
+      {{0, 0, 1}},
+  }};
+  edges_type edges = {{
+      {{0, 1}},
+      {{0, 2}},
+      {{0, 3}},
+      {{1, 2}},
+      {{1, 3}},
+      {{2, 3}},
+  }};
+  faces_type faces = {{
+      {{0, 1, 3}},
+      {{0, 2, 4}},
+      {{1, 2, 5}},
+      {{3, 4, 5}},
+  }};
+
+  brep_type brep{vertices, edges, faces};
+};
+
+TEST_F(construct_polyhedron_Test, constructs_tetrahedron) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto polyhedron = construct_polyhedron(brep);
+
+  ASSERT_THAT(polyhedron.first, Eq(3));
+  ASSERT_THAT(polyhedron.second, Eq(1));
+}
+
+TEST_F(construct_polyhedron_Test, returns_correct_number_of_volumes) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  construct_polyhedron(brep);
+  synchronize();
+
+  const auto surfaces = get_entities_of(3);
+
+  ASSERT_THAT(surfaces.size(), Eq(1));
+}
+
+TEST_F(construct_polyhedron_Test, returns_correct_number_of_surfaces) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  construct_polyhedron(brep);
+  synchronize();
+
+  const auto surfaces = get_entities_of(2);
+
+  ASSERT_THAT(surfaces.size(), Eq(4));
+}
+
+TEST_F(construct_polyhedron_Test, returns_correct_number_of_lines) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  construct_polyhedron(brep);
+  synchronize();
+
+  const auto surfaces = get_entities_of(1);
+
+  ASSERT_THAT(surfaces.size(), Eq(6));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/construct_rectangle_Test.cc b/meshing/test/cppgmsh/construct_rectangle_Test.cc
new file mode 100644
index 00000000..2a6c86d5
--- /dev/null
+++ b/meshing/test/cppgmsh/construct_rectangle_Test.cc
@@ -0,0 +1,55 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/get_coords_of.h"
+#include "ae108/meshing/cppgmsh/get_points_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct construct_rectangle_Test : Test {};
+
+TEST_F(construct_rectangle_Test, constructs_rectangle) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto origin = std::array<double, 3>{0, 0, 0};
+  const auto side_lengths = std::array<double, 2>{1, 2};
+
+  const auto rectangle = construct_rectangle(origin, side_lengths);
+
+  ASSERT_THAT(rectangle.first, Eq(2));
+  ASSERT_THAT(rectangle.second, Eq(1));
+
+  synchronize();
+  const auto points = get_coords_of(get_points_of(rectangle));
+  ASSERT_THAT(points[0], ElementsAre(DoubleEq(0.), DoubleEq(0.), DoubleEq(0.)));
+  ASSERT_THAT(points[1], ElementsAre(DoubleEq(1.), DoubleEq(0.), DoubleEq(0.)));
+  ASSERT_THAT(points[2], ElementsAre(DoubleEq(1.), DoubleEq(2.), DoubleEq(0.)));
+  ASSERT_THAT(points[3], ElementsAre(DoubleEq(0.), DoubleEq(2.), DoubleEq(0.)));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/copy_entities_Test.cc b/meshing/test/cppgmsh/copy_entities_Test.cc
new file mode 100644
index 00000000..e90694eb
--- /dev/null
+++ b/meshing/test/cppgmsh/copy_entities_Test.cc
@@ -0,0 +1,63 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/copy_entities.h"
+#include "ae108/meshing/cppgmsh/get_coords_of.h"
+#include "ae108/meshing/cppgmsh/get_entities_of.h"
+#include "ae108/meshing/cppgmsh/get_points_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+
+#include <gmock/gmock.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct copy_entities_Test : Test {};
+
+TEST_F(copy_entities_Test, copies_box) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  auto box = construct_box(std::array<double, 3>{0., 0., 0.},
+                           std::array<double, 3>{1., 1., 1.});
+
+  copy_entities({box});
+
+  synchronize();
+  const auto entities = get_entities_of(3);
+
+  ASSERT_THAT(entities.size(), Eq(2));
+  auto points_0 = get_points_of(entities[0]);
+  auto points_1 = get_points_of(entities[1]);
+  ASSERT_THAT(points_0.size(), Eq(points_1.size()));
+  for (std::size_t i = 0; i < points_0.size(); i++) {
+    auto coords_0 = get_coords_of(points_0[i]);
+    auto coords_1 = get_coords_of(points_1[i]);
+    for (std::size_t d = 0; d < 3; d++) {
+      ASSERT_THAT(coords_0[d], DoubleEq(coords_1[d]));
+    }
+  }
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/extract_mesh_Test.cc b/meshing/test/cppgmsh/extract_mesh_Test.cc
new file mode 100644
index 00000000..486529db
--- /dev/null
+++ b/meshing/test/cppgmsh/extract_mesh_Test.cc
@@ -0,0 +1,144 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/extract_mesh.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct extract_mesh_Test : Test {};
+
+TEST_F(extract_mesh_Test, extract_Tet4_mesh) {
+  const auto gmshContext = Context(0, 0);
+  construct_box({0, 0, 0}, {1, 1, 1});
+  synchronize();
+  set_granularity(1.);
+
+  const int dimension = 3;
+  const int order = 1;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<dimension>();
+
+  ASSERT_THAT(positions.size(), Eq(14));
+  ASSERT_THAT(connectivity.size(), Eq(24));
+  ASSERT_THAT(connectivity[0].size(), Eq(4));
+}
+
+TEST_F(extract_mesh_Test, extract_Tet10_mesh) {
+  const auto gmshContext = Context(0, 0);
+  construct_box({0, 0, 0}, {1, 1, 1});
+  synchronize();
+  set_granularity(1.);
+
+  const int dimension = 3;
+  const int order = 2;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<dimension>();
+
+  ASSERT_THAT(positions.size(), Eq(63));
+  ASSERT_THAT(connectivity.size(), Eq(24));
+  ASSERT_THAT(connectivity[0].size(), Eq(10));
+}
+
+TEST_F(extract_mesh_Test, extract_Tri3_mesh) {
+  const auto gmshContext = Context(0, 0);
+  construct_rectangle({0, 0, 0}, {1, 1});
+  synchronize();
+  set_granularity(1.);
+
+  const int dimension = 2;
+  const int order = 1;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<dimension>();
+
+  ASSERT_THAT(positions.size(), Eq(5));
+  ASSERT_THAT(connectivity.size(), Eq(4));
+  ASSERT_THAT(connectivity[0].size(), Eq(3));
+}
+
+TEST_F(extract_mesh_Test, extract_Tri6_mesh) {
+  const auto gmshContext = Context(0, 0);
+  construct_rectangle({0, 0, 0}, {1, 1});
+  synchronize();
+  set_granularity(1.);
+
+  const int dimension = 2;
+  const int order = 2;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<dimension>();
+
+  ASSERT_THAT(positions.size(), Eq(13));
+  ASSERT_THAT(connectivity.size(), Eq(4));
+  ASSERT_THAT(connectivity[0].size(), Eq(6));
+}
+
+TEST_F(extract_mesh_Test, extract_Quad4_mesh) {
+  const auto gmshContext = Context(0, 0);
+  construct_rectangle({0, 0, 0}, {1, 1});
+  synchronize();
+  set_granularity(1.);
+  gmsh::option::setNumber("Mesh.RecombineAll", 1);
+  gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 2);
+
+  const int dimension = 2;
+  const int order = 1;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<dimension>();
+
+  ASSERT_THAT(positions.size(), Eq(9));
+  ASSERT_THAT(connectivity.size(), Eq(4));
+  ASSERT_THAT(connectivity[0].size(), Eq(4));
+}
+
+TEST_F(extract_mesh_Test, extract_Quad9_mesh) {
+  const auto gmshContext = Context(0, 0);
+  construct_rectangle({0, 0, 0}, {1, 1});
+  synchronize();
+  set_granularity(1.);
+  gmsh::option::setNumber("Mesh.RecombineAll", 1);
+  gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 2);
+
+  const int dimension = 2;
+  const int order = 2;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<dimension>();
+
+  ASSERT_THAT(positions.size(), Eq(25));
+  ASSERT_THAT(connectivity.size(), Eq(4));
+  ASSERT_THAT(connectivity[0].size(), Eq(9));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/extrude_entities_Test.cc b/meshing/test/cppgmsh/extrude_entities_Test.cc
new file mode 100644
index 00000000..9b50f790
--- /dev/null
+++ b/meshing/test/cppgmsh/extrude_entities_Test.cc
@@ -0,0 +1,121 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/extrude_entities.h"
+#include "ae108/meshing/cppgmsh/fragment_entities.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/get_coords_of.h"
+#include "ae108/meshing/cppgmsh/get_entities_of.h"
+#include "ae108/meshing/cppgmsh/get_nodes_of.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+
+#include <cmath>
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::DoubleEq;
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct extrude_Test : Test {};
+
+TEST_F(extrude_Test, extrudes_rectangle) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto rectangle = construct_rectangle({0., 0., 0.}, {1., 1.});
+  const std::array<double, 3> dr = {0.1, 0.2, 0.3};
+  extrude_entities({rectangle}, dr);
+  synchronize();
+
+  const auto points = get_entities_of(0);
+  ASSERT_THAT(points.size(), Eq(8));
+  const auto edges = get_entities_of(1);
+  ASSERT_THAT(edges.size(), Eq(12));
+  const auto surfaces = get_entities_of(2);
+  ASSERT_THAT(surfaces.size(), Eq(6));
+  const auto volumes = get_entities_of(3);
+  ASSERT_THAT(volumes.size(), Eq(1));
+
+  for (std::size_t i = 0; i < 4; i++) {
+    const auto coords_0 = get_coords_of(points[i].second);
+    const auto coords_1 = get_coords_of(points[i + 4].second);
+    for (std::size_t d = 0; d < 3; d++) {
+      ASSERT_THAT(coords_0[d] + dr[d], DoubleEq(coords_1[d]));
+    }
+  }
+}
+
+TEST_F(extrude_Test, extrudes_mesh) {
+  const auto gmshContext = Context(0, 0);
+  const auto rectangle_1 = construct_rectangle({0., 0., 0.}, {1., 1.});
+  const auto rectangle_2 = construct_rectangle({2., 0., 0.}, {1., 1.});
+  const std::array<double, 3> dr = {0, 0, 1};
+  auto extruded_entities_1 = extrude_entities({rectangle_1}, dr);
+  std::pair<int, int> box_1 = {-1, -1};
+  for (std::size_t i = 0; i < extruded_entities_1.size(); i++) {
+    if (extruded_entities_1[i].first == 3) {
+      box_1 = extruded_entities_1[i];
+      break;
+    }
+  }
+  const int number_of_layers = 3;
+  auto extruded_entities_2 =
+      extrude_entities({rectangle_2}, dr, true, number_of_layers);
+  std::pair<int, int> box_2 = {-1, -1};
+  for (std::size_t i = 0; i < extruded_entities_2.size(); i++) {
+    if (extruded_entities_2[i].first == 3) {
+      box_2 = extruded_entities_2[i];
+      break;
+    }
+  }
+
+  synchronize();
+
+  set_granularity(0.5);
+  generate_mesh(3, 1, 6);
+
+  const auto nodes_1 = get_nodes_of<3>(box_1);
+  bool at_least_one_node_not_on_plane = false;
+  for (std::size_t i = 0; i < nodes_1.size(); i++) {
+    if (fabs(double(int(nodes_1[i].position[2] /
+                            (dr[2] / double(number_of_layers)) +
+                        1e-10)) -
+             (nodes_1[i].position[2] / (dr[2] / double(number_of_layers)))) >
+        1e-10) {
+      at_least_one_node_not_on_plane = true;
+      break;
+    }
+  }
+  ASSERT_THAT(at_least_one_node_not_on_plane, Eq(true));
+
+  const auto nodes_2 = get_nodes_of<3>(box_2);
+  for (std::size_t i = 0; i < nodes_2.size(); i++) {
+    ASSERT_THAT(
+        double(int(nodes_2[i].position[2] / (dr[2] / double(number_of_layers)) +
+                   1e-10)),
+        DoubleEq(nodes_2[i].position[2] / (dr[2] / double(number_of_layers))));
+  }
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/extrude_surface_Test.cc b/meshing/test/cppgmsh/extrude_surface_Test.cc
new file mode 100644
index 00000000..5331f571
--- /dev/null
+++ b/meshing/test/cppgmsh/extrude_surface_Test.cc
@@ -0,0 +1,61 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/extrude_surface.h"
+#include "ae108/meshing/cppgmsh/get_entities_of.h"
+#include "ae108/meshing/cppgmsh/remove_entities.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include "ae108/meshing/cppgmsh/write_file.h"
+
+#include <cmath>
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::DoubleEq;
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct extrude_Test : Test {};
+
+TEST_F(extrude_Test, extrude_rectangle_along_axis) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto rectangle = construct_rectangle({-.5, -.5, 0}, {1., 1.});
+  const std::array<double, 3> from = {0, 0, 0};
+  const std::array<double, 3> to = {0, 0, 10};
+
+  extrude_surface(rectangle.second, from, to);
+  remove_entities({rectangle}, true);
+  synchronize();
+
+  const auto points = get_entities_of(0);
+  ASSERT_THAT(points.size(), Eq(8));
+  const auto edges = get_entities_of(1);
+  ASSERT_THAT(edges.size(), Eq(12));
+  const auto surfaces = get_entities_of(2);
+  ASSERT_THAT(surfaces.size(), Eq(6));
+  const auto volumes = get_entities_of(3);
+  ASSERT_THAT(volumes.size(), Eq(1));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/fragment_entities_Test.cc b/meshing/test/cppgmsh/fragment_entities_Test.cc
new file mode 100644
index 00000000..363fc882
--- /dev/null
+++ b/meshing/test/cppgmsh/fragment_entities_Test.cc
@@ -0,0 +1,152 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/fragment_entities.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/get_boundary_of.h"
+#include "ae108/meshing/cppgmsh/get_elements_in.h"
+#include "ae108/meshing/cppgmsh/get_nodes_of.h"
+#include "ae108/meshing/cppgmsh/intersect_entities.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct fragment_Test : Test {};
+
+TEST_F(fragment_Test, returns_the_correct_entities) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  const auto box_2 = construct_box({0., 0., 1.}, {1., 1., 1.});
+  const auto fragmented_entities = fragment_entities({box_1, box_2});
+
+  ASSERT_THAT(fragmented_entities.size(), Eq(2));
+  ASSERT_THAT(fragmented_entities[0], Eq(box_1));
+  ASSERT_THAT(fragmented_entities[1], Eq(box_2));
+}
+
+TEST_F(fragment_Test, no_elements_crossing_entity_interface) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 6. / 7.});
+  const auto box_2 = construct_box({0., 0., 6. / 7.}, {1., 1., 1. / 7.});
+  const auto fragmented_entities = fragment_entities({box_1, box_2});
+  synchronize();
+
+  set_granularity(0.5);
+  generate_mesh(3, 1, 6);
+
+  const auto elements = get_elements_in({3, -1});
+  const auto elements_1 = get_elements_in(box_1);
+  const auto elements_2 = get_elements_in(box_2);
+  ASSERT_THAT(elements_1.size() + elements_2.size(), Eq(elements.size()));
+
+  const auto nodes_1 = get_nodes_of<3>(box_1);
+  for (const auto &element_1 : elements_1) {
+
+    int elementType;
+    std::vector<std::size_t> nodes;
+    gmsh::model::mesh::getElement(element_1.second, elementType, nodes);
+
+    bool is_in_nodes_1 = false;
+    for (const auto &node_1 : nodes_1)
+      is_in_nodes_1 += std::count(nodes.begin(), nodes.end(), node_1.id);
+
+    ASSERT_THAT(is_in_nodes_1, Eq(true));
+  }
+
+  auto nodes_2 = get_nodes_of<3>(box_2);
+  for (const auto &element_2 : elements_2) {
+
+    int elementType;
+    std::vector<std::size_t> nodes;
+    gmsh::model::mesh::getElement(element_2.second, elementType, nodes);
+
+    bool is_in_nodes_2 = false;
+    for (const auto &node_2 : nodes_2)
+      is_in_nodes_2 += std::count(nodes.begin(), nodes.end(), node_2.id);
+
+    ASSERT_THAT(is_in_nodes_2, Eq(true));
+  }
+}
+
+TEST_F(fragment_Test, conformal_mesh_at_interface) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 6. / 7.});
+  const auto box_2 =
+      construct_box({1. / 3., 1. / 3., 6. / 7.}, {1. / 3., 1. / 3., 1. / 7.});
+  synchronize();
+
+  const auto boundary_1 = get_boundary_of({box_1});
+  const auto boundary_2 = get_boundary_of({box_2});
+
+  const auto common_boundary = intersect_entities(boundary_1, boundary_2);
+  const auto fragmented_entities = fragment_entities({box_1, box_2});
+  synchronize();
+
+  set_granularity(0.5);
+  generate_mesh(3, 1, 6);
+
+  const auto nodes_1 = get_nodes_of<3>(box_1);
+  const auto nodes_2 = get_nodes_of<3>(box_2);
+  const auto nodes_interface = get_nodes_of<3>(common_boundary[0]);
+
+  bool is_in_nodes_1 = false;
+  for (const auto &node_1 : nodes_1)
+    is_in_nodes_1 +=
+        std::count(nodes_interface.begin(), nodes_interface.end(), node_1);
+  ASSERT_THAT(is_in_nodes_1, Eq(true));
+
+  bool is_in_nodes_2 = false;
+  for (const auto &node_2 : nodes_2)
+    is_in_nodes_2 +=
+        std::count(nodes_interface.begin(), nodes_interface.end(), node_2);
+  ASSERT_THAT(is_in_nodes_2, Eq(true));
+}
+
+TEST_F(fragment_Test, handles_overlapping_entities) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  const auto box_2 = construct_box({0.5, 0.5, 0.5}, {1., 1., 1.});
+  const auto fragmented_entities = fragment_entities({box_1, box_2});
+
+  ASSERT_THAT(fragmented_entities.size(), Eq(3));
+}
+
+TEST_F(fragment_Test, handles_unconnected_entities) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  const auto box_2 = construct_box({2., 2., 2.}, {1., 1., 1.});
+  auto fragmented_entities = fragment_entities({box_1, box_2});
+
+  ASSERT_THAT(fragmented_entities.size(), Eq(2));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/fuse_entities_Test.cc b/meshing/test/cppgmsh/fuse_entities_Test.cc
new file mode 100644
index 00000000..b5d9564e
--- /dev/null
+++ b/meshing/test/cppgmsh/fuse_entities_Test.cc
@@ -0,0 +1,69 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_cylinders.h"
+#include "ae108/meshing/cppgmsh/fuse_entities.h"
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct fuse_entities_Test : Test {};
+
+TEST_F(fuse_entities_Test, fuse_single_entity) {
+  const auto gmshContext = Context(0, 0);
+  std::vector<std::array<double, 3>> nodes{{0, 0, 0}, {1, 0, 0}};
+  std::vector<std::array<std::size_t, 2>> connectivity{{0, 1}};
+  std::vector<double> radii{1.};
+  bool capped = false;
+
+  const auto result =
+      fuse_entities(construct_cylinders(nodes, connectivity, radii, capped));
+  ASSERT_THAT(result.size(), Eq(1));
+}
+
+TEST_F(fuse_entities_Test, fuse_two_entitities) {
+  const auto gmshContext = Context(0, 0);
+  std::vector<std::array<double, 3>> nodes{{0, 0, 0}, {1, 0, 0}, {2, 0, 0}};
+  std::vector<std::array<std::size_t, 2>> connectivity{{0, 1}, {1, 2}};
+  std::vector<double> radii{0.1, 0.1};
+  bool capped = false;
+
+  const auto result =
+      fuse_entities(construct_cylinders(nodes, connectivity, radii, capped));
+  ASSERT_THAT(result.size(), Eq(1));
+}
+
+TEST_F(fuse_entities_Test, fuse_unconnected_entitities) {
+  const auto gmshContext = Context(0, 0);
+  std::vector<std::array<double, 3>> nodes{
+      {0, 0, 0}, {1, 0, 0}, {0, 0, 1}, {1, 0, 1}};
+  std::vector<std::array<std::size_t, 2>> connectivity{{0, 1}, {2, 3}};
+  std::vector<double> radii{0.1, 0.1};
+  bool capped = false;
+
+  const auto result =
+      fuse_entities(construct_cylinders(nodes, connectivity, radii, capped));
+  ASSERT_THAT(result.size(), Eq(2));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/generate_mesh_Test.cc b/meshing/test/cppgmsh/generate_mesh_Test.cc
new file mode 100644
index 00000000..ba1ecc75
--- /dev/null
+++ b/meshing/test/cppgmsh/generate_mesh_Test.cc
@@ -0,0 +1,172 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct generate_mesh_Test : Test {
+  void add_box() {
+    gmsh::model::occ::addBox(0, 0, 0, 1, 1, 1, 1);
+    gmsh::model::occ::synchronize();
+  }
+  void add_rectangle() {
+    gmsh::model::occ::addRectangle(0, 0, 0, 1, 1, 1);
+    gmsh::model::occ::synchronize();
+  }
+
+  std::size_t number_of_nodes(const int dimension) {
+    std::vector<std::size_t> nodeTags;
+    std::vector<double> nodeCoordinates, parametricNodeCoordinates;
+    gmsh::model::mesh::getNodes(nodeTags, nodeCoordinates,
+                                parametricNodeCoordinates, dimension, -1, true,
+                                false);
+    return nodeTags.size();
+  }
+
+  std::size_t number_of_elements(const int dimension, const int elementType) {
+    std::vector<int> elementTypes{elementType};
+
+    std::vector<std::vector<std::size_t>> elementTypeTags, elementNodeTags;
+    gmsh::model::mesh::getElements(elementTypes, elementTypeTags,
+                                   elementNodeTags, dimension);
+
+    return [&elementTypeTags]() {
+      auto numberOfElements = std::size_t{0};
+      for (const auto &elementTags : elementTypeTags)
+        numberOfElements += elementTags.size();
+      return numberOfElements;
+    }();
+  }
+};
+
+TEST_F(generate_mesh_Test, generate_Tet4_mesh) {
+
+  const auto gmshContext = Context(0, 0);
+  generate_mesh_Test::add_box();
+  set_granularity(1.);
+
+  const int dimension = 3;
+  const int order = 1;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+
+  const auto element_type = int(4); // 4-node tetrahedron.
+  ASSERT_THAT(generate_mesh_Test::number_of_elements(dimension, element_type),
+              Eq(24));
+  ASSERT_THAT(generate_mesh_Test::number_of_nodes(dimension), Eq(14));
+}
+
+TEST_F(generate_mesh_Test, generate_Tet10_mesh) {
+
+  const auto gmshContext = Context(0, 0);
+  generate_mesh_Test::add_box();
+  set_granularity(1.);
+
+  const int dimension = 3;
+  const int order = 2;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+
+  const auto element_type = int(11); // 10-node second order tetrahedron
+  ASSERT_THAT(generate_mesh_Test::number_of_elements(dimension, element_type),
+              Eq(24));
+  ASSERT_THAT(generate_mesh_Test::number_of_nodes(dimension), Eq(63));
+}
+
+TEST_F(generate_mesh_Test, generate_Tri3_mesh) {
+  const auto gmshContext = Context(0, 0);
+
+  generate_mesh_Test::add_rectangle();
+  set_granularity(1.);
+
+  const int dimension = 2;
+  const int order = 1;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+
+  const auto element_type = int(2); // 3-node triangle.
+  ASSERT_THAT(generate_mesh_Test::number_of_elements(dimension, element_type),
+              Eq(4));
+  ASSERT_THAT(generate_mesh_Test::number_of_nodes(dimension), Eq(5));
+}
+
+TEST_F(generate_mesh_Test, generate_Tri6_mesh) {
+
+  const auto gmshContext = Context(0, 0);
+  generate_mesh_Test::add_rectangle();
+  set_granularity(1.);
+
+  const int dimension = 2;
+  const int order = 2;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+
+  const auto element_type = int(9); // 6-node second order triangle
+  ASSERT_THAT(generate_mesh_Test::number_of_elements(dimension, element_type),
+              Eq(4));
+  ASSERT_THAT(generate_mesh_Test::number_of_nodes(dimension), Eq(13));
+}
+
+TEST_F(generate_mesh_Test, generate_Quad4_mesh) {
+
+  const auto gmshContext = Context(0, 0);
+  generate_mesh_Test::add_rectangle();
+  set_granularity(1.);
+  gmsh::option::setNumber("Mesh.RecombineAll", 1);
+  gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 2);
+
+  const int dimension = 2;
+  const int order = 1;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+
+  const auto element_type = int(3); // 4-node quadrangle.
+  ASSERT_THAT(generate_mesh_Test::number_of_elements(dimension, element_type),
+              Eq(4));
+  ASSERT_THAT(generate_mesh_Test::number_of_nodes(dimension), Eq(9));
+}
+
+TEST_F(generate_mesh_Test, generate_Quad9_mesh) {
+
+  const auto gmshContext = Context(0, 0);
+  generate_mesh_Test::add_rectangle();
+  set_granularity(1.);
+  gmsh::option::setNumber("Mesh.RecombineAll", 1);
+  gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 2);
+
+  const int dimension = 2;
+  const int order = 2;
+  const int algorithm = 6;
+  generate_mesh(dimension, order, algorithm);
+
+  const auto element_type = int(10); // 9-node quadrangle.
+  ASSERT_THAT(generate_mesh_Test::number_of_elements(dimension, element_type),
+              Eq(4));
+  ASSERT_THAT(generate_mesh_Test::number_of_nodes(dimension), Eq(25));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_boundary_of_Test.cc b/meshing/test/cppgmsh/get_boundary_of_Test.cc
new file mode 100644
index 00000000..7c285b1a
--- /dev/null
+++ b/meshing/test/cppgmsh/get_boundary_of_Test.cc
@@ -0,0 +1,91 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/get_boundary_of.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct get_boundary_of_Test : Test {};
+
+TEST_F(get_boundary_of_Test, box_correctly) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto box = construct_box({0., 0., 0.}, {1., 1., 1.});
+
+  const auto boundary = get_boundary_of({box});
+
+  ASSERT_THAT(boundary.size(), Eq(6));
+  for (std::size_t i = 0; i < boundary.size(); i++) {
+    ASSERT_THAT(boundary[i].first, Eq(2));
+  }
+}
+
+TEST_F(get_boundary_of_Test, rectangle_correctly) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto rectangle = construct_rectangle({0., 0., 0.}, {1., 1.});
+
+  const auto boundary = get_boundary_of({rectangle});
+
+  ASSERT_THAT(boundary.size(), Eq(4));
+  for (std::size_t i = 0; i < boundary.size(); i++) {
+    ASSERT_THAT(boundary[i].first, Eq(1));
+  }
+}
+
+TEST_F(get_boundary_of_Test, line_correctly) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto point_1 = gmsh::model::occ::addPoint(0., 0., 0.);
+  const auto point_2 = gmsh::model::occ::addPoint(1., 1., 1.);
+
+  const auto line = gmsh::model::occ::addLine(point_1, point_2);
+
+  const auto boundary = get_boundary_of({{1, line}});
+
+  ASSERT_THAT(boundary.size(), Eq(2));
+  for (std::size_t i = 0; i < boundary.size(); i++) {
+    ASSERT_THAT(boundary[i].first, Eq(0));
+  }
+  ASSERT_THAT(boundary[0].second, Eq(point_1));
+  ASSERT_THAT(boundary[1].second, Eq(point_2));
+}
+
+TEST_F(get_boundary_of_Test, recursive_rectangle_correctly) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto rectangle = construct_rectangle({0., 0., 0.}, {1., 1.});
+
+  const auto boundary = get_boundary_of({rectangle}, true);
+
+  ASSERT_THAT(boundary.size(), Eq(4));
+  for (std::size_t i = 0; i < boundary.size(); i++) {
+    ASSERT_THAT(boundary[i].first, Eq(0));
+  }
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_centroid_of_Test.cc b/meshing/test/cppgmsh/get_centroid_of_Test.cc
new file mode 100644
index 00000000..6479179e
--- /dev/null
+++ b/meshing/test/cppgmsh/get_centroid_of_Test.cc
@@ -0,0 +1,55 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/get_centroid_of.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct get_centroid_of_Test : Test {};
+
+TEST_F(get_centroid_of_Test, returns_correct_get_centroid_of_line) {
+  const auto gmshContext = Context(0, 0);
+  const auto centroid = get_centroid_of(
+      {1, gmsh::model::occ::addLine(gmsh::model::occ::addPoint(0, 0, 0),
+                                    gmsh::model::occ::addPoint(1, 0, 0))});
+  ASSERT_THAT(centroid, ElementsAre(DoubleEq(0.5), DoubleEq(0), DoubleEq(0)));
+}
+
+TEST_F(get_centroid_of_Test, returns_correct_get_centroid_of_rectangle) {
+  const auto gmshContext = Context(0, 0);
+  const auto centroid = get_centroid_of(construct_rectangle({0, 0, 0}, {1, 1}));
+  ASSERT_THAT(centroid, ElementsAre(DoubleEq(.5), DoubleEq(.5), DoubleEq(0)));
+}
+
+TEST_F(get_centroid_of_Test, returns_correct_get_centroid_of_box) {
+  const auto gmshContext = Context(0, 0);
+  const auto centroid = get_centroid_of(construct_box({0, 0, 0}, {1, 1, 1}));
+  ASSERT_THAT(centroid, ElementsAre(DoubleEq(.5), DoubleEq(.5), DoubleEq(.5)));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_coords_of_Test.cc b/meshing/test/cppgmsh/get_coords_of_Test.cc
new file mode 100644
index 00000000..3dc5c4e7
--- /dev/null
+++ b/meshing/test/cppgmsh/get_coords_of_Test.cc
@@ -0,0 +1,56 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/get_coords_of.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct get_coords_of_Test : Test {};
+
+TEST_F(get_coords_of_Test, returns_correct_coords_of_point) {
+  const auto gmshContext = Context(0, 0);
+  const auto tag = gmsh::model::occ::addPoint(1, 2, 3);
+  gmsh::model::occ::synchronize();
+  const auto coords = get_coords_of(tag);
+  ASSERT_THAT(coords[0], Eq(1));
+  ASSERT_THAT(coords[1], Eq(2));
+  ASSERT_THAT(coords[2], Eq(3));
+}
+
+TEST_F(get_coords_of_Test, returns_correct_coords_of_points) {
+  const auto gmshContext = Context(0, 0);
+  const auto tag_1 = gmsh::model::occ::addPoint(1, 2, 3);
+  const auto tag_2 = gmsh::model::occ::addPoint(4, 5, 6);
+  gmsh::model::occ::synchronize();
+  const auto coords = get_coords_of({tag_1, tag_2});
+  ASSERT_THAT(coords[0][0], Eq(1));
+  ASSERT_THAT(coords[0][1], Eq(2));
+  ASSERT_THAT(coords[0][2], Eq(3));
+  ASSERT_THAT(coords[1][0], Eq(4));
+  ASSERT_THAT(coords[1][1], Eq(5));
+  ASSERT_THAT(coords[1][2], Eq(6));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_elements_in_Test.cc b/meshing/test/cppgmsh/get_elements_in_Test.cc
new file mode 100644
index 00000000..cea33c53
--- /dev/null
+++ b/meshing/test/cppgmsh/get_elements_in_Test.cc
@@ -0,0 +1,128 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/get_elements_in.h"
+#include "ae108/meshing/cppgmsh/get_nodes_of.h"
+#include "ae108/meshing/cppgmsh/get_points_of.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Ge;
+using testing::Le;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct get_elements_in_Test : Test {};
+
+TEST_F(get_elements_in_Test, returns_correct_number_of_tets_for_simple_box) {
+  const auto gmshContext = Context(0, 0);
+
+  construct_box({0., 0., 0.}, {1., 1., 1.});
+  gmsh::model::occ::synchronize();
+
+  set_granularity(1);
+  generate_mesh(3, 1, 6);
+
+  auto elements = get_elements_in({3, -1});
+  ASSERT_THAT(elements.size(), Eq(24));
+  ASSERT_THAT(elements[0].first, Eq(4));
+}
+
+TEST_F(get_elements_in_Test, returns_the_correct_elements) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  const auto box_2 = construct_box({10., 10., 10.}, {1., 1., 1.});
+  gmsh::model::occ::synchronize();
+
+  set_granularity(1.);
+  generate_mesh(3, 1, 6);
+
+  auto elements = get_elements_in({3, -1});
+  std::vector<bool> foundElement(elements.size(), false);
+
+  auto elements_1 = get_elements_in({3, box_1.second});
+  auto elements_2 = get_elements_in({3, box_2.second});
+
+  const auto all_nodes = get_nodes_of<3>({3, -1});
+  for (std::size_t i = 0; i < elements_1.size(); i++) {
+    int type;
+    std::vector<std::size_t> nodes;
+    gmsh::model::mesh::getElement(elements_1[i].second, type, nodes);
+    ASSERT_THAT(elements_1[i].first, Eq(type));
+    for (std::size_t j = 0; j < nodes.size(); j++) {
+      bool foundNode = false;
+      for (std::size_t k = 0; k < all_nodes.size(); k++) {
+        if (nodes[j] == all_nodes[k].id) {
+          foundNode = true;
+          ASSERT_THAT(all_nodes[k].position,
+                      ElementsAre(Ge(0.), Ge(0.), Ge(0.)));
+          ASSERT_THAT(all_nodes[k].position,
+                      ElementsAre(Le(1.), Le(1.), Le(1.)));
+          break;
+        }
+      }
+      ASSERT_THAT(foundNode, Eq(true));
+    }
+    for (std::size_t j = 0; j < elements.size(); j++) {
+      if (elements_1[i].second == elements[j].second) {
+        ASSERT_THAT(foundElement[j], Eq(false));
+        foundElement[j] = true;
+      }
+    }
+  }
+
+  for (std::size_t i = 0; i < elements_2.size(); i++) {
+    int type;
+    std::vector<std::size_t> nodes;
+    gmsh::model::mesh::getElement(elements_2[i].second, type, nodes);
+    for (std::size_t j = 0; j < nodes.size(); j++) {
+      bool foundNode = false;
+      for (std::size_t k = 0; k < all_nodes.size(); k++) {
+        if (nodes[j] == all_nodes[k].id) {
+          foundNode = true;
+          ASSERT_THAT(all_nodes[k].position,
+                      ElementsAre(Ge(10.), Ge(10.), Ge(10.)));
+          ASSERT_THAT(all_nodes[k].position,
+                      ElementsAre(Le(11.), Le(11.), Le(11.)));
+          break;
+        }
+      }
+      ASSERT_THAT(foundNode, Eq(true));
+    }
+    for (std::size_t j = 0; j < elements.size(); j++) {
+      if (elements_2[i].second == elements[j].second) {
+        ASSERT_THAT(foundElement[j], Eq(false));
+        foundElement[j] = true;
+      }
+    }
+  }
+  for (std::size_t i = 0; i < elements.size(); i++) {
+    ASSERT_THAT(foundElement[i], Eq(true));
+  }
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_entities_in_Test.cc b/meshing/test/cppgmsh/get_entities_in_Test.cc
new file mode 100644
index 00000000..a8d89595
--- /dev/null
+++ b/meshing/test/cppgmsh/get_entities_in_Test.cc
@@ -0,0 +1,86 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/get_entities_in.h"
+#include "ae108/meshing/cppgmsh/set_physical_group_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct get_entities_in_Test : Test {};
+
+TEST_F(get_entities_in_Test, returns_correct_vertices_in_box) {
+  const auto gmshContext = Context(0, 0);
+  gmsh::model::occ::addBox(0, 0, 0, 1, 1, 1);
+  gmsh::model::occ::synchronize();
+  const auto points = get_entities_in({{0, 0, 0}, {1, 1, 1}}, 0);
+  ASSERT_THAT(points[0].first, Eq(0));
+  ASSERT_THAT(points.size(), Eq(8));
+}
+
+TEST_F(get_entities_in_Test, returns_correct_lines_in_box) {
+  const auto gmshContext = Context(0, 0);
+  gmsh::model::occ::addBox(0, 0, 0, 1, 1, 1);
+  gmsh::model::occ::synchronize();
+  const auto lines = get_entities_in({{0, 0, 0}, {1, 1, 1}}, 1);
+  ASSERT_THAT(lines[0].first, Eq(1));
+  ASSERT_THAT(lines.size(), Eq(12));
+}
+
+TEST_F(get_entities_in_Test, returns_correct_surfaces_in_box) {
+  const auto gmshContext = Context(0, 0);
+  gmsh::model::occ::addBox(0, 0, 0, 1, 1, 1);
+  gmsh::model::occ::synchronize();
+  const auto surfaces = get_entities_in({{0, 0, 0}, {1, 1, 1}}, 2);
+  ASSERT_THAT(surfaces[0].first, Eq(2));
+  ASSERT_THAT(surfaces.size(), Eq(6));
+}
+
+TEST_F(get_entities_in_Test, returns_correct_volumes_in_box) {
+  const auto gmshContext = Context(0, 0);
+  gmsh::model::occ::addBox(0, 0, 0, 1, 1, 1);
+  gmsh::model::occ::synchronize();
+  const auto volumes = get_entities_in({{0, 0, 0}, {1, 1, 1}}, 3);
+  ASSERT_THAT(volumes[0].first, Eq(3));
+  ASSERT_THAT(volumes.size(), Eq(1));
+}
+
+TEST_F(get_entities_in_Test, returns_the_correct_entities) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  const auto box_2 = construct_box({10., 10., 10.}, {1., 1., 1.});
+  synchronize();
+
+  const auto group = set_physical_group_of({box_1, box_2});
+  const auto entities = get_entities_in(group);
+
+  ASSERT_THAT(entities.size(), Eq(2));
+  ASSERT_THAT(entities[0], Eq(box_1));
+  ASSERT_THAT(entities[1], Eq(box_2));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_entities_of_Test.cc b/meshing/test/cppgmsh/get_entities_of_Test.cc
new file mode 100644
index 00000000..587fdebb
--- /dev/null
+++ b/meshing/test/cppgmsh/get_entities_of_Test.cc
@@ -0,0 +1,70 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/get_entities_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct get_entities_of_Test : Test {};
+
+TEST_F(get_entities_of_Test, gets_enities_of_dim3) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  std::pair<int, int> box = construct_box(std::array<double, 3>{0., 0., 0.},
+                                          std::array<double, 3>{1., 1., 1.});
+  synchronize();
+
+  const auto entities = get_entities_of(3);
+  ASSERT_THAT(entities.size(), Eq(1));
+  ASSERT_THAT(entities[0].first, Eq(3));
+  ASSERT_THAT(entities[0].second, Eq(box.second));
+}
+
+TEST_F(get_entities_of_Test, gets_enities_of_dim2) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  construct_box(std::array<double, 3>{0., 0., 0.},
+                std::array<double, 3>{1., 1., 1.});
+  synchronize();
+
+  const auto entities = get_entities_of(2);
+  ASSERT_THAT(entities.size(), Eq(6));
+  ASSERT_THAT(entities[0].first, Eq(2));
+}
+
+TEST_F(get_entities_of_Test, gets_enities_of_all_dimensions) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  construct_box(std::array<double, 3>{0., 0., 0.},
+                std::array<double, 3>{1., 1., 1.});
+  synchronize();
+
+  const auto entities = get_entities_of();
+  ASSERT_THAT(entities.size(),
+              Eq(1 /*volume*/ + 6 /*surfaces*/ + 12 /*edges*/ + 8 /*points*/));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_nodes_of_Test.cc b/meshing/test/cppgmsh/get_nodes_of_Test.cc
new file mode 100644
index 00000000..e4b81e2a
--- /dev/null
+++ b/meshing/test/cppgmsh/get_nodes_of_Test.cc
@@ -0,0 +1,85 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/fragment_entities.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/get_nodes_of.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Ne;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct get_nodes_Test : Test {};
+
+TEST_F(get_nodes_Test, returns_correct_nodes) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto box = construct_box({0., 0., 0.}, {1., 1., 1.});
+  synchronize();
+
+  set_granularity(1.);
+  generate_mesh();
+
+  const auto nodes = get_nodes_of<3>(box);
+  ASSERT_THAT(nodes.size(), Eq(14));
+  ASSERT_THAT(nodes[0].position,
+              ElementsAre(DoubleEq(0), DoubleEq(0), DoubleEq(1)));
+  ASSERT_THAT(nodes[1].position,
+              ElementsAre(DoubleEq(0), DoubleEq(0), DoubleEq(0)));
+  ASSERT_THAT(nodes[2].position,
+              ElementsAre(DoubleEq(0), DoubleEq(1), DoubleEq(1)));
+  ASSERT_THAT(nodes[3].position,
+              ElementsAre(DoubleEq(0), DoubleEq(1), DoubleEq(0)));
+  ASSERT_THAT(nodes[4].position,
+              ElementsAre(DoubleEq(1), DoubleEq(0), DoubleEq(1)));
+  ASSERT_THAT(nodes[5].position,
+              ElementsAre(DoubleEq(1), DoubleEq(0), DoubleEq(0)));
+  ASSERT_THAT(nodes[6].position,
+              ElementsAre(DoubleEq(1), DoubleEq(1), DoubleEq(1)));
+  ASSERT_THAT(nodes[7].position,
+              ElementsAre(DoubleEq(1), DoubleEq(1), DoubleEq(0)));
+}
+
+TEST_F(get_nodes_Test, removes_duplicates) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  const auto box_2 = construct_box({0., 0., 1.}, {1., 1., 1.});
+  const auto fused_entities = fragment_entities({box_1, box_2});
+  synchronize();
+
+  set_granularity(1);
+  generate_mesh(3, 1, 6);
+
+  const auto duplicate_nodes = get_nodes_of<3>({3, -1}, false);
+  const auto nodes = get_nodes_of<3>({3, -1}, true);
+
+  ASSERT_THAT(duplicate_nodes.size(), Ne(nodes.size()));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_normal_of_Test.cc b/meshing/test/cppgmsh/get_normal_of_Test.cc
new file mode 100644
index 00000000..baa96c61
--- /dev/null
+++ b/meshing/test/cppgmsh/get_normal_of_Test.cc
@@ -0,0 +1,43 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/get_normal_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct get_normal_of_Test : Test {};
+
+TEST_F(get_normal_of_Test, returns_correct_normal_of_rectangle) {
+  const auto gmshContext = Context(0, 0);
+  const auto rectangle = construct_rectangle({0, 0, 0}, {1, 1});
+  synchronize();
+  const auto normal = get_normal_of(rectangle.second);
+  ASSERT_THAT(normal, ElementsAre(DoubleEq(0), DoubleEq(0), DoubleEq(1)));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_periodic_nodes_of_Test.cc b/meshing/test/cppgmsh/get_periodic_nodes_of_Test.cc
new file mode 100644
index 00000000..3b0be723
--- /dev/null
+++ b/meshing/test/cppgmsh/get_periodic_nodes_of_Test.cc
@@ -0,0 +1,96 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/get_periodic_nodes_of.h"
+#include "ae108/meshing/cppgmsh/set_periodic.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Gt;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct get_periodic_nodes_of_Test : Test {};
+
+TEST_F(get_periodic_nodes_of_Test, returns_correct_mesh_of_periodic_lines) {
+  const auto gmshContext = Context(0, 0);
+  const auto source_line = gmsh::model::occ::addLine(
+      gmsh::model::occ::addPoint(0, 0, 0), gmsh::model::occ::addPoint(1, 0, 0));
+  const auto target_line = gmsh::model::occ::addLine(
+      gmsh::model::occ::addPoint(0, 0, 1), gmsh::model::occ::addPoint(1, 0, 1));
+  gmsh::model::occ::synchronize();
+
+  set_periodic({{1, target_line}}, {1, source_line},
+               {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1});
+  generate_mesh(1);
+
+  std::array<double, 16> transform;
+  std::pair<int, int> source_entity;
+  const auto node_tags =
+      get_periodic_nodes_of({1, target_line}, &source_entity, &transform);
+
+  ASSERT_THAT(source_entity.first, Eq(1));
+  ASSERT_THAT(source_entity.second, Eq(source_line));
+  ASSERT_THAT(node_tags.first.size(), Gt(0));
+  ASSERT_THAT(node_tags.second.size(), Gt(0));
+  ASSERT_THAT(node_tags.second.size(), Eq(node_tags.first.size()));
+  ASSERT_THAT(transform, ElementsAre(DoubleEq(1.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(1.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(1.)));
+}
+
+TEST_F(get_periodic_nodes_of_Test,
+       returns_correct_mesh_of_periodic_rectangles) {
+  const auto gmshContext = Context(0, 0);
+  const auto source_rect = gmsh::model::occ::addRectangle(0, 0, 0, 1, 1);
+  const auto target_rect = gmsh::model::occ::addRectangle(0, 0, 1, 1, 1);
+  gmsh::model::occ::synchronize();
+
+  set_periodic({{2, target_rect}}, {2, source_rect},
+               {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1});
+  generate_mesh(2);
+
+  std::array<double, 16> transform;
+  std::pair<int, int> source_entity;
+  const auto node_tags =
+      get_periodic_nodes_of({2, target_rect}, &source_entity, &transform);
+
+  ASSERT_THAT(source_entity.first, Eq(2));
+  ASSERT_THAT(source_entity.second, Eq(source_rect));
+  ASSERT_THAT(node_tags.first.size(), Gt(0));
+  ASSERT_THAT(node_tags.second.size(), Gt(0));
+  ASSERT_THAT(node_tags.second.size(), Eq(node_tags.first.size()));
+  ASSERT_THAT(transform, ElementsAre(DoubleEq(1.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(1.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(1.)));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_physical_groups_Test.cc b/meshing/test/cppgmsh/get_physical_groups_Test.cc
new file mode 100644
index 00000000..06a89055
--- /dev/null
+++ b/meshing/test/cppgmsh/get_physical_groups_Test.cc
@@ -0,0 +1,82 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/get_physical_groups.h"
+#include "ae108/meshing/cppgmsh/set_physical_group_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct get_physical_groups_Test : Test {};
+
+TEST_F(get_physical_groups_Test, gets_all_groups) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto rectangle_1 = construct_rectangle({0, 0, 0}, {1, 1});
+  const auto rectangle_2 = construct_rectangle({0, 1, 0}, {.5, .5});
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  const auto box_2 = construct_box({10., 10., 10.}, {1., 1., 1.});
+  const auto box_3 = construct_box({5., 10., 10.}, {1., 1., 1.});
+  synchronize();
+
+  const auto group_1 = set_physical_group_of({rectangle_1, rectangle_2});
+  const auto group_2 = set_physical_group_of({box_1, box_2});
+  const auto group_3 = set_physical_group_of({box_3});
+
+  auto groups = get_physical_groups();
+  ASSERT_THAT(groups.size(), Eq(3));
+  ASSERT_THAT(groups[0], Eq(group_1));
+  ASSERT_THAT(groups[1], Eq(group_2));
+  ASSERT_THAT(groups[2], Eq(group_3));
+}
+
+TEST_F(get_physical_groups_Test, gets_groups_of_specified_dimension) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto rectangle_1 = construct_rectangle({0, 0, 0}, {1, 1});
+  const auto rectangle_2 = construct_rectangle({0, 1, 0}, {.5, .5});
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  const auto box_2 = construct_box({10., 10., 10.}, {1., 1., 1.});
+  const auto box_3 = construct_box({5., 10., 10.}, {1., 1., 1.});
+  synchronize();
+
+  const auto group_1 = set_physical_group_of({rectangle_1, rectangle_2});
+  const auto group_2 = set_physical_group_of({box_1, box_2});
+  const auto group_3 = set_physical_group_of({box_3});
+
+  auto groups_2D = get_physical_groups(2);
+  ASSERT_THAT(groups_2D.size(), Eq(1));
+  ASSERT_THAT(groups_2D[0], Eq(group_1));
+
+  auto groups_3D = get_physical_groups(3);
+  ASSERT_THAT(groups_3D[0], Eq(group_2));
+  ASSERT_THAT(groups_3D[1], Eq(group_3));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/get_points_of_Test.cc b/meshing/test/cppgmsh/get_points_of_Test.cc
new file mode 100644
index 00000000..c7238bb7
--- /dev/null
+++ b/meshing/test/cppgmsh/get_points_of_Test.cc
@@ -0,0 +1,69 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/get_points_of.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct points_of_Test : Test {};
+
+TEST_F(points_of_Test, returns_correct_points_of_point) {
+  const auto gmshContext = Context(0, 0);
+  const auto tag = gmsh::model::occ::addPoint(0, 0, 0);
+  gmsh::model::occ::synchronize();
+  const auto points = get_points_of({0, tag});
+  ASSERT_THAT(points.size(), Eq(1));
+  ASSERT_THAT(points[0], Eq(tag));
+}
+
+TEST_F(points_of_Test, returns_correct_points_of_line) {
+  const auto gmshContext = Context(0, 0);
+  const auto A = gmsh::model::occ::addPoint(0, 0, 0);
+  const auto B = gmsh::model::occ::addPoint(1, 0, 0);
+  const auto tag = gmsh::model::occ::addLine(A, B);
+  gmsh::model::occ::synchronize();
+  const auto points = get_points_of({1, tag});
+  ASSERT_THAT(points.size(), Eq(2));
+  ASSERT_THAT(points[0], Eq(A));
+  ASSERT_THAT(points[1], Eq(B));
+}
+
+TEST_F(points_of_Test, returns_correct_points_of_rectangle) {
+  const auto gmshContext = Context(0, 0);
+  const auto tag = gmsh::model::occ::addRectangle(0, 0, 0, 1, 1);
+  gmsh::model::occ::synchronize();
+  const auto points = get_points_of({2, tag});
+  ASSERT_THAT(points.size(), Eq(4));
+}
+
+TEST_F(points_of_Test, returns_correct_points_of_box) {
+  const auto gmshContext = Context(0, 0);
+  const auto tag = gmsh::model::occ::addBox(0, 0, 0, 1, 1, 1);
+  gmsh::model::occ::synchronize();
+  const auto points = get_points_of({3, tag});
+  ASSERT_THAT(points.size(), Eq(8));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/heal_periodic_domains_Test.cc b/meshing/test/cppgmsh/heal_periodic_domains_Test.cc
new file mode 100644
index 00000000..e86faff1
--- /dev/null
+++ b/meshing/test/cppgmsh/heal_periodic_domains_Test.cc
@@ -0,0 +1,100 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/BoundingBox.h"
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/get_entities_in.h"
+#include "ae108/meshing/cppgmsh/get_points_of.h"
+#include "ae108/meshing/cppgmsh/heal_periodic_domains.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct heal_periodic_domains_Test : Test {};
+
+TEST_F(heal_periodic_domains_Test, heal_periodic_box_surfaces) {
+  const auto gmshContext = Context(0, 0);
+  const auto box = construct_box({0, 0, 0}, {1, 1, 1});
+
+  gmsh::vectorpair ov;
+  std::vector<gmsh::vectorpair> ovv;
+  gmsh::model::occ::fragment(
+      {box}, {{0, gmsh::model::occ::addPoint(0.5, 0, 0)}}, ov, ovv);
+  gmsh::model::occ::synchronize();
+
+  const auto source_bbox =
+      BoundingBox<std::array<double, 3>>{{0, 0, 0}, {1, 1, 0}};
+  const auto target_bbox =
+      BoundingBox<std::array<double, 3>>{{0, 0, 1}, {1, 1, 1}};
+
+  heal_periodic_domains(source_bbox, target_bbox);
+  gmsh::model::occ::synchronize();
+
+  const auto source_surface = get_entities_in(source_bbox, 2);
+  const auto target_surface = get_entities_in(target_bbox, 2);
+  const auto source_points = get_points_of(source_surface[0]);
+  const auto target_points = get_points_of(target_surface[0]);
+
+  gmsh::vectorpair points;
+  gmsh::model::getEntities(points, 0);
+
+  ASSERT_THAT(points.size(), Eq(10));
+  ASSERT_THAT(source_points.size(), Eq(5));
+  ASSERT_THAT(target_points.size(), Eq(5));
+}
+
+TEST_F(heal_periodic_domains_Test, heal_two_periodic_box_surfaces) {
+  const auto gmshContext = Context(0, 0);
+  const auto boxA = construct_box({0, 0, 0}, {.4, .4, 1});
+  construct_box({.5, .5, 0}, {.4, .4, 1});
+
+  gmsh::vectorpair ov;
+  std::vector<gmsh::vectorpair> ovv;
+  gmsh::model::occ::fragment(
+      {boxA}, {{0, gmsh::model::occ::addPoint(0.25, 0, 0)}}, ov, ovv);
+  gmsh::model::occ::synchronize();
+
+  const auto source_bbox =
+      BoundingBox<std::array<double, 3>>{{0, 0, 0}, {1, 1, 0}};
+  const auto target_bbox =
+      BoundingBox<std::array<double, 3>>{{0, 0, 1}, {1, 1, 1}};
+
+  heal_periodic_domains(source_bbox, target_bbox);
+  gmsh::model::occ::synchronize();
+
+  const auto source_surface = get_entities_in(source_bbox, 2);
+  const auto target_surface = get_entities_in(target_bbox, 2);
+  const auto source_points = get_points_of(source_surface[0]).size() +
+                             get_points_of(source_surface[1]).size();
+  const auto target_points = get_points_of(target_surface[0]).size() +
+                             get_points_of(target_surface[1]).size();
+
+  gmsh::vectorpair points;
+  gmsh::model::getEntities(points, 0);
+
+  ASSERT_THAT(points.size(), Eq(18));
+  ASSERT_THAT(source_points, Eq(9));
+  ASSERT_THAT(target_points, Eq(9));
+}
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/heal_periodic_surfaces_Test.cc b/meshing/test/cppgmsh/heal_periodic_surfaces_Test.cc
new file mode 100644
index 00000000..2ee3fb2d
--- /dev/null
+++ b/meshing/test/cppgmsh/heal_periodic_surfaces_Test.cc
@@ -0,0 +1,95 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/BoundingBox.h"
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/get_entities_in.h"
+#include "ae108/meshing/cppgmsh/get_points_of.h"
+#include "ae108/meshing/cppgmsh/heal_periodic_surfaces.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct heal_periodic_surfaces_Test : Test {};
+
+TEST_F(heal_periodic_surfaces_Test, heal_periodic_rectangles) {
+  const auto gmshContext = Context(0, 0);
+  const auto source_rect = construct_rectangle({0, 0, 0}, {1, 1});
+  const auto target_rect = construct_rectangle({0, 0, 1}, {1, 1});
+
+  gmsh::vectorpair ov;
+  std::vector<gmsh::vectorpair> ovv;
+  gmsh::model::occ::fragment(
+      {source_rect}, {{0, gmsh::model::occ::addPoint(0.5, 0, 0)}}, ov, ovv);
+  gmsh::model::occ::synchronize();
+
+  heal_periodic_surfaces(source_rect.second, target_rect.second, {0, 0, 1});
+  gmsh::model::occ::synchronize();
+
+  const auto source_points = get_points_of({2, source_rect});
+  const auto target_points = get_points_of({2, target_rect});
+
+  gmsh::vectorpair points;
+  gmsh::model::getEntities(points, 0);
+
+  ASSERT_THAT(points.size(), Eq(10));
+  ASSERT_THAT(source_points.size(), Eq(5));
+  ASSERT_THAT(target_points.size(), Eq(5));
+}
+
+TEST_F(heal_periodic_surfaces_Test, heal_periodic_box_surfaces) {
+  const auto gmshContext = Context(0, 0);
+  const auto box = construct_box({0, 0, 0}, {1, 1, 1});
+
+  gmsh::vectorpair ov;
+  std::vector<gmsh::vectorpair> ovv;
+  gmsh::model::occ::fragment(
+      {box}, {{0, gmsh::model::occ::addPoint(0.5, 0, 0)}}, ov, ovv);
+  gmsh::model::occ::synchronize();
+
+  const auto source_bbox =
+      BoundingBox<std::array<double, 3>>{{0, 0, 0}, {1, 1, 0}};
+  const auto target_bbox =
+      BoundingBox<std::array<double, 3>>{{0, 0, 1}, {1, 1, 1}};
+
+  const auto source_surface = get_entities_in(source_bbox, 2);
+  const auto target_surface = get_entities_in(target_bbox, 2);
+
+  heal_periodic_surfaces(source_surface[0].second, target_surface[0].second,
+                         {0, 0, 1});
+  gmsh::model::occ::synchronize();
+
+  const auto source_points = get_points_of(source_surface[0]);
+  const auto target_points = get_points_of(target_surface[0]);
+
+  gmsh::vectorpair points;
+  gmsh::model::getEntities(points, 0);
+
+  ASSERT_THAT(points.size(), Eq(10));
+  ASSERT_THAT(source_points.size(), Eq(5));
+  ASSERT_THAT(target_points.size(), Eq(5));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/import_shapes_Test.cc b/meshing/test/cppgmsh/import_shapes_Test.cc
new file mode 100644
index 00000000..fc13d1e4
--- /dev/null
+++ b/meshing/test/cppgmsh/import_shapes_Test.cc
@@ -0,0 +1,74 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/fragment_entities.h"
+#include "ae108/meshing/cppgmsh/import_shapes.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include "ae108/meshing/cppgmsh/write_file.h"
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct import_shapes_Test : Test {};
+
+TEST_F(import_shapes_Test, imports_box) {
+  const auto gmshContext = Context(0, 0, true, false);
+
+  const auto origin = std::array<double, 3>{0, 0, 0};
+  const auto side_lengths = std::array<double, 3>{1, 2, 3};
+
+  const auto box_0 = construct_box(origin, side_lengths);
+  synchronize();
+  write_file("box.stp");
+
+  const auto imported_shapes = import_shapes("box.stp");
+  ASSERT_THAT(imported_shapes.size(), Eq(1));
+  const auto box_1 = imported_shapes[0];
+
+  const auto fragments = fragment_entities({box_0, box_1});
+  ASSERT_THAT(fragments.size(), Eq(1));
+}
+
+TEST_F(import_shapes_Test, imports_box_with_lower_dimensional_entities) {
+  const auto gmshContext = Context(0, 0, true, false);
+
+  const auto origin = std::array<double, 3>{0, 0, 0};
+  const auto side_lengths = std::array<double, 3>{1, 2, 3};
+
+  construct_box(origin, side_lengths);
+  synchronize();
+  write_file("box.stp");
+
+  const auto imported_shapes = import_shapes("box.stp", false);
+  std::array<bool, 4> includes_iD_entities = {false, false, false, false};
+  for (std::size_t i = 0; i < imported_shapes.size(); i++) {
+    for (int d = 0; d < 4; d++)
+      if (imported_shapes[i].first == d)
+        includes_iD_entities[d] = true;
+  }
+  for (std::size_t d = 0; d < 4; d++)
+    ASSERT_THAT(includes_iD_entities[d], Eq(true));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/intersect_entities_Test.cc b/meshing/test/cppgmsh/intersect_entities_Test.cc
new file mode 100644
index 00000000..20a3fcc5
--- /dev/null
+++ b/meshing/test/cppgmsh/intersect_entities_Test.cc
@@ -0,0 +1,99 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/get_entities_of.h"
+#include "ae108/meshing/cppgmsh/intersect_entities.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct intersect_entities_Test : Test {};
+
+TEST_F(intersect_entities_Test, self_intersect_single_entitity) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto object = construct_box({0, 0, 0}, {1, 1, 1});
+
+  const auto result = intersect_entities({object}, {object});
+  ASSERT_THAT(result.size(), Eq(1));
+}
+
+TEST_F(intersect_entities_Test, intersect_two_entitities) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto object = construct_box({0, 0, 0}, {1, 1, 1});
+  const auto tool = construct_box({.5, .5, .5}, {1, 1, 1});
+
+  const auto result = intersect_entities({object}, {tool});
+  ASSERT_THAT(result.size(), Eq(1));
+}
+
+TEST_F(intersect_entities_Test, intersect_unconnected_entitities) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto object = construct_box({0, 0, 0}, {1, 1, 1});
+  const auto tool = construct_box({2, 2, 2}, {1, 1, 1});
+
+  const auto result = intersect_entities({object}, {tool});
+  ASSERT_THAT(result.size(), Eq(0));
+}
+
+TEST_F(intersect_entities_Test, keep_object) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto object = construct_box({0, 0, 0}, {1, 1, 1});
+  const auto tool = construct_box({.5, .5, .5}, {1, 1, 1});
+
+  intersect_entities({object}, {tool}, false, true);
+  synchronize();
+  const auto entities = get_entities_of(3);
+  ASSERT_THAT(entities.size(), Eq(2));
+}
+
+TEST_F(intersect_entities_Test, keep_tool) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto object = construct_box({0, 0, 0}, {1, 1, 1});
+  const auto tool = construct_box({.5, .5, .5}, {1, 1, 1});
+
+  intersect_entities({object}, {tool}, true, false);
+  synchronize();
+  const auto entities = get_entities_of(3);
+  ASSERT_THAT(entities.size(), Eq(2));
+}
+
+TEST_F(intersect_entities_Test, keep_object_and_tool) {
+  const auto gmshContext = Context(0, 0);
+
+  const auto object = construct_box({0, 0, 0}, {1, 1, 1});
+  const auto tool = construct_box({.5, .5, .5}, {1, 1, 1});
+
+  intersect_entities({object}, {tool}, false, false);
+  synchronize();
+  const auto entities = get_entities_of(3);
+  ASSERT_THAT(entities.size(), Eq(3));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/read_file_Test.cc b/meshing/test/cppgmsh/read_file_Test.cc
new file mode 100644
index 00000000..32a1aa30
--- /dev/null
+++ b/meshing/test/cppgmsh/read_file_Test.cc
@@ -0,0 +1,124 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/extract_mesh.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/read_file.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/write_file.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct read_file_Test : Test {};
+
+TEST_F(read_file_Test, reads_step_file) {
+
+  {
+    const auto gmshContext = Context(0, 0);
+    construct_box({0, 0, 0}, {1, 2, 3});
+    gmsh::model::occ::synchronize();
+    write_file("test.stp");
+  }
+
+  const auto gmshContext = Context(0, 0);
+
+  read_file("test.stp");
+  gmsh::vectorpair dimTags;
+  gmsh::model::getEntities(dimTags, 3);
+
+  ASSERT_THAT(dimTags.size(), Eq(1));
+  ASSERT_THAT(dimTags[0].first, Eq(3));
+  ASSERT_THAT(dimTags[0].second, Eq(1));
+}
+
+TEST_F(read_file_Test, reads_stl_file) {
+
+  {
+    const auto gmshContext = Context(0, 0);
+    construct_box({0, 0, 0}, {1, 1, 1});
+    gmsh::model::occ::synchronize();
+    set_granularity(1.);
+    generate_mesh(2, 1, 6);
+    write_file("test.stl");
+  }
+
+  const auto gmshContext = Context(0, 0);
+
+  read_file("test.stl");
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<2>();
+
+  ASSERT_THAT(positions.size(), Eq(14));
+  ASSERT_THAT(connectivity.size(), Eq(24));
+  ASSERT_THAT(connectivity[0].size(), Eq(3));
+}
+
+TEST_F(read_file_Test, reads_vtk_file) {
+
+  {
+    const auto gmshContext = Context(0, 0);
+    construct_box({0, 0, 0}, {1, 1, 1});
+    gmsh::model::occ::synchronize();
+    set_granularity(1.);
+    generate_mesh(3, 1, 6);
+    write_file("test.vtk");
+  }
+
+  const auto gmshContext = Context(0, 0);
+
+  read_file("test.vtk");
+  gmsh::model::mesh::createTopology();
+  gmsh::model::mesh::classifySurfaces(0.1, true, false, 0.1);
+  gmsh::model::mesh::createGeometry();
+
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<3>();
+
+  ASSERT_THAT(positions.size(), Eq(14));
+  ASSERT_THAT(connectivity.size(), Eq(24));
+  ASSERT_THAT(connectivity[0].size(), Eq(4));
+}
+
+TEST_F(read_file_Test, reads_msh_file) {
+
+  {
+    const auto gmshContext = Context(0, 0);
+    construct_box({0, 0, 0}, {1, 1, 1});
+    gmsh::model::occ::synchronize();
+    set_granularity(1.);
+    generate_mesh(3, 1, 6);
+    write_file("test.msh");
+  }
+
+  const auto gmshContext = Context(0, 0);
+
+  read_file("test.msh");
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<3>();
+
+  ASSERT_THAT(positions.size(), Eq(14));
+  ASSERT_THAT(connectivity.size(), Eq(24));
+  ASSERT_THAT(connectivity[0].size(), Eq(4));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/remove_entities_Test.cc b/meshing/test/cppgmsh/remove_entities_Test.cc
new file mode 100644
index 00000000..1f11a5b7
--- /dev/null
+++ b/meshing/test/cppgmsh/remove_entities_Test.cc
@@ -0,0 +1,61 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/get_entities_of.h"
+#include "ae108/meshing/cppgmsh/remove_entities.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Gt;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct remove_entities_Test : Test {};
+
+TEST_F(remove_entities_Test, remove_box_keep_boundaries) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto box = construct_box({0., 0., 0.}, {1., 1., 1.});
+
+  remove_entities({box});
+
+  synchronize();
+  auto entities = get_entities_of(3);
+  ASSERT_THAT(entities.size(), Eq(0));
+  entities = get_entities_of();
+  ASSERT_THAT(entities.size(), Gt(0));
+}
+
+TEST_F(remove_entities_Test, remove_box_and_boundaries) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto box = construct_box({0., 0., 0.}, {1., 1., 1.});
+
+  remove_entities({box}, true);
+
+  const auto entities = get_entities_of();
+  ASSERT_THAT(entities.size(), Eq(0));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/rotate_entities_Test.cc b/meshing/test/cppgmsh/rotate_entities_Test.cc
new file mode 100644
index 00000000..5e1b3562
--- /dev/null
+++ b/meshing/test/cppgmsh/rotate_entities_Test.cc
@@ -0,0 +1,70 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/get_coords_of.h"
+#include "ae108/meshing/cppgmsh/get_points_of.h"
+#include "ae108/meshing/cppgmsh/rotate_entities.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct rotate_entities_Test : Test {};
+
+TEST_F(rotate_entities_Test, rotates_rectangle_by_30_degrees) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto box = construct_rectangle({0, 0, 0}, {1, 1});
+  rotate_entities({box}, {0., 0., 0.}, {0., 0., 1.}, 30. * M_PI / 180.);
+  synchronize();
+
+  const auto points = get_points_of(box);
+  std::vector<std::array<double, 3>> coords;
+  for (const auto &point : points)
+    coords.push_back(get_coords_of(point));
+
+  ASSERT_THAT(coords[0], ElementsAre(DoubleEq(0.), DoubleEq(0.), DoubleEq(0.)));
+  ASSERT_THAT(coords[1][1], DoubleEq(1. / 2.));
+  ASSERT_THAT(coords[3][0], DoubleEq(-1. / 2.));
+}
+
+TEST_F(rotate_entities_Test, rotates_rectangle_by_pi_sixths) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto box = construct_rectangle({0, 0, 0}, {1, 1});
+  rotate_entities({box}, {0., 0., 0.}, {0., 0., 1.}, M_PI / 6.);
+  synchronize();
+
+  const auto points = get_points_of(box);
+  std::vector<std::array<double, 3>> coords;
+  for (const auto &point : points)
+    coords.push_back(get_coords_of(point));
+
+  ASSERT_THAT(coords[0], ElementsAre(DoubleEq(0.), DoubleEq(0.), DoubleEq(0.)));
+  ASSERT_THAT(coords[1][1], DoubleEq(1. / 2.));
+  ASSERT_THAT(coords[3][0], DoubleEq(-1. / 2.));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/set_domain_entities_periodic_Test.cc b/meshing/test/cppgmsh/set_domain_entities_periodic_Test.cc
new file mode 100644
index 00000000..4009ec87
--- /dev/null
+++ b/meshing/test/cppgmsh/set_domain_entities_periodic_Test.cc
@@ -0,0 +1,73 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/get_periodic_nodes_of.h"
+#include "ae108/meshing/cppgmsh/set_domain_entities_periodic.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Gt;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct set_domain_entities_periodic_Test : Test {};
+
+TEST_F(set_domain_entities_periodic_Test, fuse_single_entity) {
+  const auto gmshContext = Context(0, 0);
+}
+
+TEST_F(set_domain_entities_periodic_Test,
+       returns_correct_mesh_of_periodic_rectangles) {
+  const auto gmshContext = Context(0, 0);
+  const auto source_rect = construct_rectangle({0, 0, 0}, {1, 1});
+  const auto target_rect = construct_rectangle({0, 0, 1}, {1, 1});
+  synchronize();
+
+  const auto source_bbox =
+      BoundingBox<std::array<double, 3>>{{0, 0, 0}, {1, 1, 0}};
+  const auto target_bbox =
+      BoundingBox<std::array<double, 3>>{{0, 0, 1}, {1, 1, 1}};
+
+  set_domain_entities_periodic(source_bbox, target_bbox, 2);
+  generate_mesh(2);
+
+  std::pair<int, int> source;
+  std::array<double, 16> transform;
+  const auto [targetNodeTags, sourceNodeTags] =
+      get_periodic_nodes_of(target_rect, &source, &transform);
+
+  ASSERT_THAT(source, Eq(source_rect));
+  ASSERT_THAT(targetNodeTags.size(), Gt(0));
+  ASSERT_THAT(sourceNodeTags.size(), Eq(targetNodeTags.size()));
+  ASSERT_THAT(transform, ElementsAre(DoubleEq(1.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(1.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(1.)));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/set_expert_mode_Test.cc b/meshing/test/cppgmsh/set_expert_mode_Test.cc
new file mode 100644
index 00000000..c5077cfc
--- /dev/null
+++ b/meshing/test/cppgmsh/set_expert_mode_Test.cc
@@ -0,0 +1,50 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/set_expert_mode.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::DoubleEq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct set_expert_mode_Test : Test {};
+
+TEST_F(set_expert_mode_Test, enables_expert_mode) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  set_expert_mode();
+  double value;
+  gmsh::option::getNumber("General.ExpertMode", value);
+  ASSERT_THAT(value, DoubleEq(1.0));
+}
+
+TEST_F(set_expert_mode_Test, disables_expert_mode) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  set_expert_mode(false);
+  double value;
+  gmsh::option::getNumber("General.ExpertMode", value);
+  ASSERT_THAT(value, DoubleEq(0.0));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/set_granularity_Test.cc b/meshing/test/cppgmsh/set_granularity_Test.cc
new file mode 100644
index 00000000..d7789896
--- /dev/null
+++ b/meshing/test/cppgmsh/set_granularity_Test.cc
@@ -0,0 +1,35 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include <gmock/gmock.h>
+
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct set_granularity_Test : Test {};
+
+TEST_F(set_granularity_Test, fuse_single_entity) {
+  const auto gmshContext = Context(0, 0);
+  set_granularity(0.1);
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/set_periodic_Test.cc b/meshing/test/cppgmsh/set_periodic_Test.cc
new file mode 100644
index 00000000..d8cfc8b0
--- /dev/null
+++ b/meshing/test/cppgmsh/set_periodic_Test.cc
@@ -0,0 +1,94 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/get_periodic_nodes_of.h"
+#include "ae108/meshing/cppgmsh/set_periodic.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Gt;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct set_periodic_Test : Test {};
+
+TEST_F(set_periodic_Test, returns_correct_mesh_of_periodic_lines) {
+  const auto gmshContext = Context(0, 0);
+  const auto source_line = gmsh::model::occ::addLine(
+      gmsh::model::occ::addPoint(0, 0, 0), gmsh::model::occ::addPoint(1, 0, 0));
+  const auto target_line = gmsh::model::occ::addLine(
+      gmsh::model::occ::addPoint(0, 0, 1), gmsh::model::occ::addPoint(1, 0, 1));
+  synchronize();
+
+  set_periodic({{1, target_line}}, {1, source_line},
+               {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1});
+  generate_mesh(1);
+
+  std::pair<int, int> master;
+  std::array<double, 16> transform;
+  const auto nodes =
+      get_periodic_nodes_of({1, target_line}, &master, &transform);
+
+  ASSERT_THAT(master.second, Eq(source_line));
+  ASSERT_THAT(nodes.first.size(), Gt(0));
+  ASSERT_THAT(nodes.second.size(), Gt(0));
+  ASSERT_THAT(nodes.first.size(), nodes.second.size());
+  ASSERT_THAT(transform, ElementsAre(DoubleEq(1.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(1.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(1.)));
+}
+
+TEST_F(set_periodic_Test, returns_correct_mesh_of_periodic_rectangles) {
+  const auto gmshContext = Context(0, 0);
+  const auto source_rect = construct_rectangle({0, 0, 0}, {1, 1});
+  const auto target_rect = construct_rectangle({0, 0, 1}, {1, 1});
+  synchronize();
+
+  set_periodic({target_rect}, source_rect,
+               {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1});
+  generate_mesh(2);
+
+  std::pair<int, int> master;
+  std::array<double, 16> transform;
+  const auto nodes = get_periodic_nodes_of(target_rect, &master, &transform);
+
+  ASSERT_THAT(master.second, Eq(source_rect.second));
+  ASSERT_THAT(nodes.first.size(), Gt(0));
+  ASSERT_THAT(nodes.second.size(), Gt(0));
+  ASSERT_THAT(nodes.first.size(), nodes.second.size());
+  ASSERT_THAT(transform, ElementsAre(DoubleEq(1.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(0.), DoubleEq(1.), DoubleEq(1.),
+                                     DoubleEq(0.), DoubleEq(0.), DoubleEq(0.),
+                                     DoubleEq(1.)));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/set_physical_group_of_Test.cc b/meshing/test/cppgmsh/set_physical_group_of_Test.cc
new file mode 100644
index 00000000..d7a84dea
--- /dev/null
+++ b/meshing/test/cppgmsh/set_physical_group_of_Test.cc
@@ -0,0 +1,91 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/set_physical_group_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct set_physical_group_of_Test : Test {};
+
+TEST_F(set_physical_group_of_Test, adds_1D_group) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto line_1 = gmsh::model::occ::addLine(
+      gmsh::model::occ::addPoint(0, 0, 0), gmsh::model::occ::addPoint(1, 0, 0));
+  const auto line_2 = gmsh::model::occ::addLine(
+      gmsh::model::occ::addPoint(1, 0, 0), gmsh::model::occ::addPoint(1, 1, 0));
+  synchronize();
+
+  const auto group = set_physical_group_of({{1, line_1}, {1, line_2}});
+  ASSERT_THAT(group, Eq(std::pair<int, int>{1, 1}));
+}
+
+TEST_F(set_physical_group_of_Test, adds_2D_group) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto rectangle_1 = construct_rectangle({0, 0, 0}, {1, 1});
+  const auto rectangle_2 = construct_rectangle({0, 1, 0}, {0.5, 0.5});
+  synchronize();
+
+  const auto group = set_physical_group_of({rectangle_1, rectangle_2});
+  ASSERT_THAT(group, Eq(std::pair<int, int>{2, 1}));
+}
+
+TEST_F(set_physical_group_of_Test, adds_3D_group) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  const auto box_2 = construct_box({10., 10., 10.}, {1., 1., 1.});
+  synchronize();
+
+  const auto group = set_physical_group_of({box_1, box_2});
+  ASSERT_THAT(group, Eq(std::pair<int, int>{3, 1}));
+}
+
+TEST_F(set_physical_group_of_Test, adds_group_with_one_member) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  synchronize();
+
+  const auto group = set_physical_group_of({box_1});
+  ASSERT_THAT(group, Eq(std::pair<int, int>{3, 1}));
+}
+
+TEST_F(set_physical_group_of_Test, adds_group_with_three_members) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto box_1 = construct_box({0., 0., 0.}, {1., 1., 1.});
+  const auto box_2 = construct_box({10., 10., 10.}, {1., 1., 1.});
+  const auto box_3 = construct_box({5., 10., 10.}, {1., 1., 1.});
+  synchronize();
+
+  const auto group = set_physical_group_of({box_1, box_2, box_3});
+  ASSERT_THAT(group, Eq(std::pair<int, int>{3, 1}));
+}
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/synchronize_Test.cc b/meshing/test/cppgmsh/synchronize_Test.cc
new file mode 100644
index 00000000..c1253b0f
--- /dev/null
+++ b/meshing/test/cppgmsh/synchronize_Test.cc
@@ -0,0 +1,43 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/get_entities_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include <gmock/gmock.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct synchronize_Test : Test {};
+
+TEST_F(synchronize_Test, synchronizes) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  construct_box({0., 0., 0.}, {1., 1., 1.});
+
+  ASSERT_THAT(get_entities_of(3).size(), Eq(0));
+  synchronize();
+  ASSERT_THAT(get_entities_of(3).size(), Eq(1));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/translate_entities_Test.cc b/meshing/test/cppgmsh/translate_entities_Test.cc
new file mode 100644
index 00000000..681d2bb4
--- /dev/null
+++ b/meshing/test/cppgmsh/translate_entities_Test.cc
@@ -0,0 +1,90 @@
+// © 2022 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_rectangle.h"
+#include "ae108/meshing/cppgmsh/get_coords_of.h"
+#include "ae108/meshing/cppgmsh/get_entities_of.h"
+#include "ae108/meshing/cppgmsh/get_points_of.h"
+#include "ae108/meshing/cppgmsh/synchronize.h"
+#include "ae108/meshing/cppgmsh/translate_entities.h"
+#include <gmock/gmock.h>
+#include <math.h>
+
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct translate_entities_Test : Test {};
+
+TEST_F(translate_entities_Test, translates_rectangle) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto rectangle = construct_rectangle({0, 0, 0}, {1, 1});
+
+  translate_entities({rectangle}, {0.1, 0.2, 0.3});
+
+  synchronize();
+  const auto points = get_points_of(rectangle);
+  std::vector<std::array<double, 3>> coords;
+  for (const auto &point : points)
+    coords.push_back(get_coords_of(point));
+
+  ASSERT_THAT(coords[0],
+              ElementsAre(DoubleEq(0.1), DoubleEq(0.2), DoubleEq(0.3)));
+  ASSERT_THAT(coords[1],
+              ElementsAre(DoubleEq(1.1), DoubleEq(0.2), DoubleEq(0.3)));
+  ASSERT_THAT(coords[2],
+              ElementsAre(DoubleEq(1.1), DoubleEq(1.2), DoubleEq(0.3)));
+  ASSERT_THAT(coords[3],
+              ElementsAre(DoubleEq(0.1), DoubleEq(1.2), DoubleEq(0.3)));
+}
+
+TEST_F(translate_entities_Test, makes_translated_copy_of_rectangle) {
+  const auto gmshContext = Context(0, 0, true, true);
+
+  const auto rectangle = construct_rectangle({0, 0, 0}, {1, 1});
+
+  const auto translated_rectangle =
+      translate_entities({rectangle}, {0.1, 0.2, 0.3}, true)[0];
+
+  synchronize();
+
+  const auto rectangles = get_entities_of(2);
+  ASSERT_THAT(rectangles.size(), Eq(2));
+
+  const auto points = get_points_of(translated_rectangle);
+  std::vector<std::array<double, 3>> coords;
+  for (const auto &point : points)
+    coords.push_back(get_coords_of(point));
+
+  ASSERT_THAT(coords[0],
+              ElementsAre(DoubleEq(0.1), DoubleEq(0.2), DoubleEq(0.3)));
+  ASSERT_THAT(coords[1],
+              ElementsAre(DoubleEq(1.1), DoubleEq(0.2), DoubleEq(0.3)));
+  ASSERT_THAT(coords[2],
+              ElementsAre(DoubleEq(1.1), DoubleEq(1.2), DoubleEq(0.3)));
+  ASSERT_THAT(coords[3],
+              ElementsAre(DoubleEq(0.1), DoubleEq(1.2), DoubleEq(0.3)));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/meshing/test/cppgmsh/write_file_Test.cc b/meshing/test/cppgmsh/write_file_Test.cc
new file mode 100644
index 00000000..5c90d0a5
--- /dev/null
+++ b/meshing/test/cppgmsh/write_file_Test.cc
@@ -0,0 +1,124 @@
+// © 2021 ETH Zurich, Mechanics and Materials Lab
+//
+// ae108 is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or any
+// later version.
+//
+// ae108 is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ae108. If not, see <https://www.gnu.org/licenses/>.
+
+#include "ae108/meshing/cppgmsh/Context.h"
+#include "ae108/meshing/cppgmsh/construct_box.h"
+#include "ae108/meshing/cppgmsh/extract_mesh.h"
+#include "ae108/meshing/cppgmsh/generate_mesh.h"
+#include "ae108/meshing/cppgmsh/read_file.h"
+#include "ae108/meshing/cppgmsh/set_granularity.h"
+#include "ae108/meshing/cppgmsh/write_file.h"
+#include <gmock/gmock.h>
+#include <gmsh.h>
+
+using testing::Eq;
+using testing::Test;
+
+namespace ae108 {
+namespace meshing {
+namespace cppgmsh {
+
+struct write_file_Test : Test {};
+
+TEST_F(write_file_Test, writes_step_file) {
+
+  {
+    const auto gmshContext = Context(0, 0);
+    construct_box({0, 0, 0}, {1, 2, 3});
+    gmsh::model::occ::synchronize();
+    write_file("test.stp");
+  }
+
+  const auto gmshContext = Context(0, 0);
+
+  read_file("test.stp");
+  gmsh::vectorpair dimTags;
+  gmsh::model::getEntities(dimTags, 3);
+
+  ASSERT_THAT(dimTags.size(), Eq(1));
+  ASSERT_THAT(dimTags[0].first, Eq(3));
+  ASSERT_THAT(dimTags[0].second, Eq(1));
+}
+
+TEST_F(write_file_Test, writes_stl_file) {
+
+  {
+    const auto gmshContext = Context(0, 0);
+    construct_box({0, 0, 0}, {1, 1, 1});
+    gmsh::model::occ::synchronize();
+    set_granularity(1.);
+    generate_mesh(2, 1, 6);
+    write_file("test.stl");
+  }
+
+  const auto gmshContext = Context(0, 0);
+
+  read_file("test.stl");
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<2>();
+
+  ASSERT_THAT(positions.size(), Eq(14));
+  ASSERT_THAT(connectivity.size(), Eq(24));
+  ASSERT_THAT(connectivity[0].size(), Eq(3));
+}
+
+TEST_F(write_file_Test, writes_vtk_file) {
+
+  {
+    const auto gmshContext = Context(0, 0);
+    construct_box({0, 0, 0}, {1, 1, 1});
+    gmsh::model::occ::synchronize();
+    set_granularity(1.);
+    generate_mesh(3, 1, 6);
+    write_file("test.vtk");
+  }
+
+  const auto gmshContext = Context(0, 0);
+
+  read_file("test.vtk");
+  gmsh::model::mesh::createTopology();
+  gmsh::model::mesh::classifySurfaces(0.1, true, false, 0.1);
+  gmsh::model::mesh::createGeometry();
+
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<3>();
+
+  ASSERT_THAT(positions.size(), Eq(14));
+  ASSERT_THAT(connectivity.size(), Eq(24));
+  ASSERT_THAT(connectivity[0].size(), Eq(4));
+}
+
+TEST_F(write_file_Test, writes_msh_file) {
+
+  {
+    const auto gmshContext = Context(0, 0);
+    construct_box({0, 0, 0}, {1, 1, 1});
+    gmsh::model::occ::synchronize();
+    set_granularity(1.);
+    generate_mesh(3, 1, 6);
+    write_file("test.msh");
+  }
+
+  const auto gmshContext = Context(0, 0);
+
+  read_file("test.msh");
+  const auto [positions, connectivity, mapA, mapB] = extract_mesh<3>();
+
+  ASSERT_THAT(positions.size(), Eq(14));
+  ASSERT_THAT(connectivity.size(), Eq(24));
+  ASSERT_THAT(connectivity[0].size(), Eq(4));
+}
+
+} // namespace cppgmsh
+} // namespace meshing
+} // namespace ae108
\ No newline at end of file
diff --git a/tests/examples/periodic_rve_mesh/definition.json b/tests/examples/periodic_rve_mesh/definition.json
new file mode 100644
index 00000000..1eeebdd0
--- /dev/null
+++ b/tests/examples/periodic_rve_mesh/definition.json
@@ -0,0 +1,10 @@
+{
+    "executable": [
+        "examples/meshing",
+        "ae108-examples-PeriodicRVEMesh"
+    ],
+    "compare_stdout": "numeric",
+    "mpi_processes": [
+        1
+    ]
+}
\ No newline at end of file
diff --git a/tests/examples/periodic_rve_mesh/references/stdout.txt b/tests/examples/periodic_rve_mesh/references/stdout.txt
new file mode 100644
index 00000000..0ffd4bc5
--- /dev/null
+++ b/tests/examples/periodic_rve_mesh/references/stdout.txt
@@ -0,0 +1,21 @@
+# target face (9) nodes: 150
+# source face (0) nodes: 150
+
+# target face (11) nodes: 139
+# source face (1) nodes: 139
+
+# target face (7) nodes: 76
+# source face (2) nodes: 76
+
+# target face (6) nodes: 150
+# source face (3) nodes: 150
+
+# target face (10) nodes: 76
+# source face (4) nodes: 76
+
+# target face (8) nodes: 161
+# source face (5) nodes: 161
+
+# target face (13) nodes: 76
+# source face (12) nodes: 76
+
diff --git a/tests/examples/physical_groups/definition.json b/tests/examples/physical_groups/definition.json
new file mode 100644
index 00000000..391bfe1b
--- /dev/null
+++ b/tests/examples/physical_groups/definition.json
@@ -0,0 +1,10 @@
+{
+    "executable": [
+        "examples/meshing",
+        "ae108-examples-PhysicalGroups"
+    ],
+    "compare_stdout": "numeric",
+    "mpi_processes": [
+        1
+    ]
+}
\ No newline at end of file
diff --git a/tests/examples/physical_groups/references/stdout.txt b/tests/examples/physical_groups/references/stdout.txt
new file mode 100644
index 00000000..ad936f6f
--- /dev/null
+++ b/tests/examples/physical_groups/references/stdout.txt
@@ -0,0 +1,80 @@
+Box 1: dim = 3, tag = 1
+Box 2: dim = 3, tag = 2
+Box 3: dim = 3, tag = 3
+Groups (dim, tag): (3,1) (3,2) 
+Group 1, dim: 3, entities: 1 3 
+Group 2, dim: 3, entities: 2 
+Elements in group 1:
+	tag	index	node indides
+	1	0	2	16	6	17	
+	2	1	6	16	5	17	
+	3	2	1	16	2	17	
+	4	3	5	16	1	17	
+	5	4	0	1	16	3	
+	6	5	7	2	16	6	
+	7	6	1	16	4	0	
+	8	7	16	6	5	7	
+	9	8	2	7	16	3	
+	10	9	16	7	5	4	
+	11	10	1	16	3	2	
+	12	11	5	16	4	1	
+	25	24	18	13	8	19	
+	26	25	19	11	18	13	
+	27	26	8	19	10	18	
+	28	27	11	10	19	18	
+	29	28	19	11	13	15	
+	30	29	8	19	13	12	
+	31	30	9	13	18	11	
+	32	31	8	14	19	12	
+	33	32	13	18	8	9	
+	34	33	14	11	19	15	
+	35	34	11	14	19	10	
+	36	35	14	8	19	10	
+Elements in group 2:
+	tag	index	node indides
+	13	12	11	10	18	17	
+	14	13	2	18	17	11	
+	15	14	1	18	10	17	
+	16	15	18	1	2	17	
+	17	16	17	6	11	5	
+	18	17	2	11	17	6	
+	19	18	2	18	11	9	
+	20	19	2	8	18	9	
+	21	20	11	10	17	5	
+	22	21	18	1	10	8	
+	23	22	17	10	1	5	
+	24	23	8	2	18	1	
+Nodes in group 1:
+	tag	index	x	y	z
+	1	0	0	0	0
+	2	1	0	0	0.3
+	3	2	0	1	0.3
+	4	3	0	1	0
+	5	4	1	0	0
+	6	5	1	0	0.3
+	7	6	1	1	0.3
+	8	7	1	1	0
+	17	16	0.5	0.5	0
+	18	17	0.5	0.5	0.3
+	9	8	0	0	0.7
+	10	9	0	1	0.7
+	11	10	1	0	0.7
+	12	11	1	1	0.7
+	13	12	0	0	1
+	14	13	0	1	1
+	15	14	1	0	1
+	16	15	1	1	1
+	19	18	0.5	0.5	0.7
+	20	19	0.5	0.5	1
+Nodes in group 2:
+	tag	index	x	y	z
+	2	1	0	0	0.3
+	3	2	0	1	0.3
+	6	5	1	0	0.3
+	7	6	1	1	0.3
+	9	8	0	0	0.7
+	10	9	0	1	0.7
+	11	10	1	0	0.7
+	12	11	1	1	0.7
+	18	17	0.5	0.5	0.3
+	19	18	0.5	0.5	0.7
diff --git a/tests/examples/segment_mesh/definition.json b/tests/examples/segment_mesh/definition.json
new file mode 100644
index 00000000..ded8a81a
--- /dev/null
+++ b/tests/examples/segment_mesh/definition.json
@@ -0,0 +1,10 @@
+{
+    "executable": [
+        "examples/meshing",
+        "ae108-examples-SegmentMesh"
+    ],
+    "compare_stdout": "numeric",
+    "mpi_processes": [
+        1
+    ]
+}
\ No newline at end of file
diff --git a/tests/examples/segment_mesh/references/stdout.txt b/tests/examples/segment_mesh/references/stdout.txt
new file mode 100644
index 00000000..284c37ec
--- /dev/null
+++ b/tests/examples/segment_mesh/references/stdout.txt
@@ -0,0 +1,2 @@
+# points: 121
+# elements: 260
diff --git a/tests/examples/two_phase_cube/definition.json b/tests/examples/two_phase_cube/definition.json
new file mode 100644
index 00000000..1cc5b6bb
--- /dev/null
+++ b/tests/examples/two_phase_cube/definition.json
@@ -0,0 +1,10 @@
+{
+    "executable": [
+        "examples/meshing",
+        "ae108-examples-TwoPhaseCube"
+    ],
+    "compare_stdout": "numeric",
+    "mpi_processes": [
+        1
+    ]
+}
\ No newline at end of file
diff --git a/tests/examples/two_phase_cube/references/stdout.txt b/tests/examples/two_phase_cube/references/stdout.txt
new file mode 100644
index 00000000..a3c9fcc0
--- /dev/null
+++ b/tests/examples/two_phase_cube/references/stdout.txt
@@ -0,0 +1,86 @@
+Box 1: dim = 3, tag = 1
+Box 2: dim = 3, tag = 2
+Elements in box 1:
+	tag	index	node indices
+	77	0	3	0	16	12	
+	78	1	1	2	17	12	
+	79	2	5	1	17	14	
+	80	3	6	5	17	13	
+	81	4	16	3	15	7	
+	82	5	16	14	0	4	
+	83	6	7	13	16	4	
+	84	7	2	6	17	15	
+	85	8	14	16	12	17	
+	86	9	12	16	15	17	
+	87	10	14	16	17	13	
+	88	11	16	15	17	13	
+	89	12	3	16	15	12	
+	90	13	12	15	2	17	
+	91	14	6	17	15	13	
+	92	15	5	14	17	13	
+	93	16	16	15	13	7	
+	94	17	14	16	13	4	
+	95	18	0	14	16	12	
+	96	19	1	14	12	17	
+	97	20	15	6	13	7	
+	98	21	15	2	3	12	
+	99	22	0	14	12	1	
+	100	23	13	5	14	4	
+Elements in box 2:
+	tag	index	node indices
+	101	24	11	10	22	19	
+	102	25	2	1	17	18	
+	103	26	9	8	18	22	
+	104	27	1	5	17	20	
+	105	28	20	22	8	10	
+	106	29	6	2	17	21	
+	107	30	11	22	9	21	
+	108	31	5	6	17	19	
+	109	32	18	22	17	21	
+	110	33	18	22	20	17	
+	111	34	22	20	17	19	
+	112	35	21	22	17	19	
+	113	36	17	2	18	21	
+	114	37	22	8	18	20	
+	115	38	22	18	9	21	
+	116	39	1	18	20	17	
+	117	40	5	17	20	19	
+	118	41	21	11	22	19	
+	119	42	21	17	6	19	
+	120	43	20	22	10	19	
+	121	44	8	1	18	20	
+	122	45	2	9	18	21	
+	123	46	10	5	20	19	
+	124	47	21	6	11	19	
+Nodes in box 1:
+	tag	index	x	y	z
+	1	0	0	0	0
+	2	1	0	0	0.5
+	3	2	0	1	0.5
+	4	3	0	1	0
+	5	4	1	0	0
+	6	5	1	0	0.5
+	7	6	1	1	0.5
+	8	7	1	1	0
+	13	12	0	0.5	0.25
+	14	13	1	0.5	0.25
+	15	14	0.5	0	0.25
+	16	15	0.5	1	0.25
+	17	16	0.5	0.5	0
+	18	17	0.5	0.5	0.5
+Nodes in box 2:
+	tag	index	x	y	z
+	2	1	0	0	0.5
+	3	2	0	1	0.5
+	6	5	1	0	0.5
+	7	6	1	1	0.5
+	9	8	0	0	1
+	10	9	0	1	1
+	11	10	1	0	1
+	12	11	1	1	1
+	18	17	0.5	0.5	0.5
+	19	18	0	0.5	0.75
+	20	19	1	0.5	0.75
+	21	20	0.5	0	0.75
+	22	21	0.5	1	0.75
+	23	22	0.5	0.5	1
-- 
GitLab