diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5415ec4976b207f91f3562dd889cc7c21509a14a..b35c03eb94dd6c9b22c529c143d09655fe38a3be 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -292,10 +292,6 @@ set(LIBGMSHDG_SOURCES   src/gmsh_io.cpp
                         src/param_loader.cpp
                     )
 
-if (OPT_USE_MPI)
-    set(LIBGMSHDG_SOURCES ${LIBGMSHDG_SOURCES} src/gmsh_mpi.cpp)
-endif()
-
 if (OPT_ENABLE_GPU_SOLVER)
     set(LIBGMSHDG_SOURCES ${LIBGMSHDG_SOURCES} src/kernels_gpu_mi.cpp)
 
@@ -316,9 +312,6 @@ endif()
 
 ########## mpi testing stuff
 
-add_executable(testmpi src/testmpi.cpp)
-target_link_libraries(testmpi gmshdg)
-
 add_executable(testpart src/testpart.cpp)
 target_link_libraries(testpart gmshdg)
 
diff --git a/include/gmsh_io.h b/include/gmsh_io.h
index 5d917b6f947b36801fa2a7e2a73237948595b7d7..4894a2cbb06fc70a29b54df87d1375041e6b1315 100644
--- a/include/gmsh_io.h
+++ b/include/gmsh_io.h
@@ -44,7 +44,8 @@ enum class face_type : int
 struct boundary_descriptor
 {
     face_type   type;
-    int         gmsh_entity;
+    int         gmsh_entity;        /* Actual GMSH entity */
+    int         parent_entity;      /* Parent GMSH entity in partitioned model */
 
     //auto operator <=> (const boundary_descriptor&) const = default;
 
@@ -60,8 +61,16 @@ struct boundary_descriptor
     }
 
     boundary_descriptor()
-        : type(face_type::NONE), gmsh_entity(0)
+        : type(face_type::NONE), gmsh_entity(0), parent_entity(-1)
     {}
+
+    int material_tag(void) const
+    {
+        if (parent_entity == -1)
+            return gmsh_entity;
+
+        return parent_entity;
+    }
 };
 
 
@@ -85,6 +94,9 @@ class model
 
     /* Map from the partition number to the entities belonging to that partition */
     std::map<int, std::vector<int>>             partition_map;
+    
+    /* In a partitioned model, map from 2D entity tag to parent tag */
+    std::map<int, int>                          surface_to_parent_map;
 
     /* Map from 3D entity tag to partition */
     std::map<int, int>                          partition_inverse_map;
@@ -107,7 +119,7 @@ class model
     void    populate_from_gmsh_rankN(void);
     void    import_gmsh_entities_rankN(void);
     int     my_partition(void) const;
-#endif
+#endif /* USE_MPI */
 
     void    make_boundary_to_faces_map(void);
     void    make_partition_to_entities_map(void);
diff --git a/include/gmsh_mpi.h b/include/gmsh_mpi.h
index 6f824d1bb1145d0b7eed2184a0fa35fd82f9d9d7..b9a0ef9ee94492f738c19493b04f357846573015 100644
--- a/include/gmsh_mpi.h
+++ b/include/gmsh_mpi.h
@@ -100,6 +100,46 @@ priv_MPI_Recv(T& elem, int src, MPI_Comm comm)
     MPI_Recv(&elem, sizeof(T), MPI_PACKED, src, 0, comm, &status);
 }
 
+template<typename T1, typename T2>
+void
+priv_MPI_Send(std::map<T1, T2>& map, int dst, MPI_Comm comm)
+{
+    static_assert(std::is_trivially_copyable<T1>::value, "Type must be trivially copyable");
+    static_assert(std::is_trivially_copyable<T2>::value, "Type must be trivially copyable");
+
+    std::vector<T1> left;
+    left.reserve( map.size() );
+    std::vector<T2> right;
+    right.reserve( map.size() );
+
+    for (auto& m : map)
+    {
+        left.push_back(m.first);
+        right.push_back(m.second);
+    }
+
+    priv_MPI_Send(left, dst, comm);
+    priv_MPI_Send(right, dst, comm);
+}
+
+template<typename T1, typename T2>
+void
+priv_MPI_Recv(std::map<T1, T2>& map, int src, MPI_Comm comm)
+{
+    static_assert(std::is_trivially_copyable<T1>::value, "Type must be trivially copyable");
+    static_assert(std::is_trivially_copyable<T2>::value, "Type must be trivially copyable");
+
+    std::vector<T1> left;
+    priv_MPI_Recv(left, src, comm);
+
+    std::vector<T2> right;
+    priv_MPI_Recv(right, src, comm);
+
+    assert(left.size() == right.size());
+
+    for (size_t i = 0; i < left.size(); i++)
+        map[ left[i] ] = right[i]; 
+}
 
 template<typename T>
 void
@@ -166,3 +206,43 @@ priv_MPI_Bcast(std::map<T1, std::vector<T2>>& map, int root, MPI_Comm comm)
     }
 }
 
+template<typename T1, typename T2>
+void
+priv_MPI_Bcast(std::map<T1, T2>& map, int root, MPI_Comm comm)
+{
+    static_assert(std::is_trivially_copyable<T1>::value, "Type must be trivially copyable");
+    static_assert(std::is_trivially_copyable<T2>::value, "Type must be trivially copyable");
+
+    int rank;
+    MPI_Comm_rank(comm, &rank);
+
+    if (rank == root)
+    {
+        std::vector<T1> left;
+        left.reserve( map.size() );
+        std::vector<T2> right;
+        right.reserve( map.size() );
+
+        for (auto& m : map)
+        {
+            left.push_back(m.first);
+            right.push_back(m.second);
+        }
+
+        priv_MPI_Bcast(left, root, comm);
+        priv_MPI_Bcast(right, root, comm);
+    }
+    else
+    {
+        std::vector<T1> left;
+        priv_MPI_Bcast(left, root, comm);
+
+        std::vector<T2> right;
+        priv_MPI_Bcast(right, root, comm);
+
+        assert(left.size() == right.size());
+
+        for (size_t i = 0; i < left.size(); i++)
+            map[ left[i] ] = right[i]; 
+    }
+}
\ No newline at end of file
diff --git a/src/gmsh_io.cpp b/src/gmsh_io.cpp
index 9e8142119024ca9c283a299b8c45a0ed488dc395..e2f3f7fbfb43b6ae43bdb74dd1f716854b359a52 100644
--- a/src/gmsh_io.cpp
+++ b/src/gmsh_io.cpp
@@ -87,6 +87,7 @@ model::make_boundary_to_faces_map(void)
             std::vector<int> parts;
             gm::getPartitions(dim, tag, parts);
             comm_map[tag] = parts;
+            surface_to_parent_map[tag] = ptag;
         }
 
         std::vector<int> elemTypes;
@@ -300,7 +301,10 @@ model::import_gmsh_entities_rankN(void)
 bool
 model::is_interprocess_boundary(int tag)
 {
-    return comm_map.at(tag).size() == 2;
+    if (num_partitions > 1)
+        return comm_map.at(tag).size() == 2;
+    
+    return false;
 }
 
 #endif /* USE_MPI */
@@ -361,6 +365,8 @@ model::map_boundaries(void)
             }
             /* and store the gmsh tag in the descriptor. */
             bd.gmsh_entity = (*itor).second;
+            if (num_partitions > 1)
+                bd.parent_entity = surface_to_parent_map.at( (*itor).second );
 
             /* Finally, put the descriptor in the global array of faces. */
             bnd_descriptors.at(fbase + iF) = bd;
@@ -416,6 +422,7 @@ model::populate_from_gmsh_rank0()
     priv_MPI_Bcast(boundary_map, 0, MPI_COMM_WORLD);
     priv_MPI_Bcast(comm_map, 0, MPI_COMM_WORLD);
     priv_MPI_Bcast(partition_map, 0, MPI_COMM_WORLD);
+    priv_MPI_Bcast(surface_to_parent_map, 0, MPI_COMM_WORLD);
 
     for (auto& [tag, vec] : partition_map)
     {
@@ -443,6 +450,7 @@ model::populate_from_gmsh_rankN()
     priv_MPI_Bcast(boundary_map, 0, MPI_COMM_WORLD);
     priv_MPI_Bcast(comm_map, 0, MPI_COMM_WORLD);
     priv_MPI_Bcast(partition_map, 0, MPI_COMM_WORLD);
+    priv_MPI_Bcast(surface_to_parent_map, 0, MPI_COMM_WORLD);
 
     for (auto& [tag, vec] : partition_map)
     {
diff --git a/src/gmsh_mpi.cpp b/src/gmsh_mpi.cpp
deleted file mode 100644
index 07f9c3c5de0cb865a98d1ec37767f8dfe83a6838..0000000000000000000000000000000000000000
--- a/src/gmsh_mpi.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-#include "gmsh_mpi.h"
-
-#if 0
-template<typename T1, typename T2>
-void zip(const std::vector<T1>& firsts, const std::vector<T2>& seconds,
-    std::vector<std::pair<T1, T2>>& pairs)
-{
-    pairs.clear();
-    auto len = std::min(firsts.size(), seconds.size());
-    for(size_t i = 0; i < len; i++)
-        pairs.push_back( std::make_pair(firsts[i], seconds[i]) );
-}
-
-template<typename T1, typename T2>
-void unzip(const std::vector<std::pair<T1, T2>>& pairs,
-    std::vector<T1>& firsts, std::vector<T2>& seconds)
-{
-    firsts.clear();
-    seconds.clear();
-    for (const auto& [fst, snd] : pairs)
-    {
-        firsts.push_back(fst);
-        seconds.push_back(snd);
-    }
-}
-
-void
-MPIGMSH_getEntities(gvp_t& gmsh_entities, int dim, MPI_Comm comm)
-{
-    int rank, size;
-    MPI_Comm_rank(comm, &rank);
-    MPI_Comm_size(comm, &size);
-
-    if (rank == 0)
-    {
-        gm::getEntities(gmsh_entities, dim);
-        size_t num_entities = gmsh_entities.size();
-
-        if (size > 1)
-        {
-            MPI_Bcast(&num_entities, 1, MPI_UNSIGNED_LONG_LONG, 0, comm);
-            std::vector<int> dims;
-            std::vector<int> tags;
-            unzip(gmsh_entities, dims, tags);
-            MPI_Bcast(dims.data(), num_entities, MPI_INT, 0, comm);
-            MPI_Bcast(tags.data(), num_entities, MPI_INT, 0, comm);
-        }
-    }
-    else
-    {
-        size_t num_entities;
-        MPI_Bcast(&num_entities, 1, MPI_UNSIGNED_LONG_LONG, 0, comm);
-        
-        std::vector<int> dims;
-        dims.resize(num_entities);
-        MPI_Bcast(dims.data(), num_entities, MPI_INT, 0, comm);
-
-        std::vector<int> tags;
-        tags.resize(num_entities);
-        MPI_Bcast(tags.data(), num_entities, MPI_INT, 0, comm);
-
-        zip(dims, tags, gmsh_entities);
-    }
-
-    return;
-}
-
-void
-MPIGMSH_getElementTypes(std::vector<int>& elemTypes, int dim, int tag, MPI_Comm comm)
-{
-    int rank, size;
-    MPI_Comm_rank(comm, &rank);
-    MPI_Comm_size(comm, &size);
-
-    if (rank == 0)
-    {
-        //gm::getEntities(elemTypes, dim, tag);
-        size_t num_eT = elemTypes.size();
-
-        if (size > 1)
-        {
-            MPI_Bcast(&num_eT, 1, MPI_UNSIGNED_LONG_LONG, 0, comm);
-            MPI_Bcast(elemTypes.data(), num_eT, MPI_INT, 0, comm);
-        }
-    }
-    else
-    {
-        size_t num_eT;
-        MPI_Bcast(&num_eT, 1, MPI_UNSIGNED_LONG_LONG, 0, comm);
-        elemTypes.resize(num_eT);
-        MPI_Bcast(elemTypes.data(), num_eT, MPI_INT, 0, comm);
-    }
-}
-
-#endif
\ No newline at end of file
diff --git a/src/maxwell_solver.cpp b/src/maxwell_solver.cpp
index ce3cd72d56c1847014b5f553b6a7aedf712ad9f0..eb2d0b4755ca52085d5276f158884eeddd77ceb8 100644
--- a/src/maxwell_solver.cpp
+++ b/src/maxwell_solver.cpp
@@ -5,10 +5,6 @@
 #include <filesystem>
 #endif
 
-#ifdef _OPENMP
-#include <omp.h>
-#endif
-
 #ifdef USE_MPI
 #include <mpi/mpi.h>
 #endif
@@ -203,10 +199,6 @@ void test_it(const model& mod, State& state, maxwell::parameter_loader& mpl)
     auto cycle_print_rate = mpl.postpro_cyclePrintRate();
     auto ti = mpl.sim_timeIntegrator();
 
-#ifdef _OPENMP
-    omp_set_num_threads(4);
-#endif
-
     std::cout << "    BEGINNING SIMULATION" << std::endl;
     std::cout << "I will do " << num_timesteps << " of " << state.delta_t;
     std::cout << "s each." << std::endl;
diff --git a/src/testmpi.cpp b/src/testmpi.cpp
deleted file mode 100644
index bcfe50692465a49c88c220b98214dfa47032000d..0000000000000000000000000000000000000000
--- a/src/testmpi.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <iostream>
-#include <mpi/mpi.h>
-
-#include "gmsh_mpi.h"
-
-int main(int argc, char **argv)
-{
-    MPI_Init(&argc, &argv);
-
-    int rank, size;
-    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
-    MPI_Comm_size(MPI_COMM_WORLD, &size);
-    std::cout << "Running with MPI: " << rank+1 << "/" << size << std::endl;
-
-    gvp_t gmsh_entities;
-
-    if (rank == 0)
-    {
-        gmsh::initialize();
-        gmsh::open("yagi_prof.geo");
-        gmm::generate(3);
-        gmm::setOrder(1);
-    }
-
-    MPIGMSH_getEntities(gmsh_entities, 3, MPI_COMM_WORLD);
-
-    std::cout << "Rank: " << rank << " -> ";
-    for (auto& [dim, tag] : gmsh_entities)
-        std::cout << "(" << dim << ", " << tag << ") ";
-    std::cout << std::endl;
-
-    if (rank == 0)
-    {
-        //gmsh::finalize();
-    }
-
-    MPI_Finalize();
-    return 0;
-}