diff --git a/CMakeLists.txt b/CMakeLists.txt
index 80681c5e74e6e790604bd271e4285099dcac5bab..5415ec4976b207f91f3562dd889cc7c21509a14a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -71,6 +71,13 @@ set(SOL2_BUILD_LUA OFF CACHE BOOL "Override sol2 build lua option")
 add_subdirectory(contrib/sol2)
 include_directories(contrib/sol2/include)
 
+option(OPT_USE_MPI "Use MPI if available" OFF)
+find_package(MPI)
+if (OPT_USE_MPI AND MPI_FOUND)
+    add_definitions(-DUSE_MPI)
+    include_directories("${MPI_CXX_INCLUDE_DIRS}")
+    set(LINK_LIBS ${LINK_LIBS} ${MPI_CXX_LIBRARIES})
+endif()
 ######################################################################
 ## Use or not use GPU solvers
 option(OPT_ENABLE_GPU_SOLVER "Enable GPU solvers" OFF)
@@ -285,6 +292,10 @@ 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)
 
@@ -303,6 +314,16 @@ else()
     target_link_libraries(gmshdg ${LINK_LIBS})
 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)
+
+add_executable(testmod src/testmod.cpp)
+target_link_libraries(testmod gmshdg)
 
 ########## maxwell solver
 set(MAXWELL_SOURCES     src/maxwell_interface.cpp
diff --git a/doc/changelog.md b/doc/changelog.md
index d3defac113566debd48c73ef83f93087fa36730b..a35cb5cc01ff7f2cc38ea75383b7f622f0ada7ac 100644
--- a/doc/changelog.md
+++ b/doc/changelog.md
@@ -23,5 +23,10 @@ Unavailable features:
 
 ## Version v0.3 (NOT RELEASED YET)
 - Volumetric sources on CPU & GPU (2ed2e9e7).
+- Fixed issues that prevented compilation on CECI clusters.
+- Minor performance optimizations
+- Line integration
+- Preliminary MPI support
+
 
 
diff --git a/include/gmsh_io.h b/include/gmsh_io.h
index 0824294ad5f41df66b74ac9dd2fd91aacb61348b..8d32796a4be30b0230a54b18f343e721233a7ab2 100644
--- a/include/gmsh_io.h
+++ b/include/gmsh_io.h
@@ -58,11 +58,13 @@ struct boundary_descriptor
 
 class model
 {
-    int                             geometric_order;
-    int                             approximation_order;
+    int                                         geometric_order;
+    int                                         approximation_order;
+    int                                         num_partitions;
 
     std::vector<entity>                         entities;
     std::map<size_t, std::vector<element_key>>  boundary_map;
+    std::map<size_t, std::vector<int>>          comm_map;
     std::vector<boundary_descriptor>            bnd_descriptors;
     element_connectivity<element_key>           conn;
 
@@ -73,7 +75,13 @@ class model
     void    import_gmsh_entities(void);
     void    update_connectivity(const entity&, size_t);
 
-    void    build_priv(void);
+    void    populate_from_gmsh_rank0(void);
+
+#ifdef HAVE_MPI
+    void    populate_from_gmsh_rankN(void);
+#endif
+
+    void    make_boundary_to_faces_map(void);
 
 public:
     model();
@@ -85,6 +93,11 @@ public:
     void build(void);
     void build(double);
 
+    void generate_mesh(void);
+    void generate_mesh(double);
+    void partition_mesh(int);
+    void populate_from_gmsh(void);
+
     const element_connectivity<element_key>& connectivity() const
     {
         return conn;
diff --git a/include/gmsh_mpi.h b/include/gmsh_mpi.h
new file mode 100644
index 0000000000000000000000000000000000000000..c428de0fa77eab2de162603701b2e44202708e97
--- /dev/null
+++ b/include/gmsh_mpi.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <mpi/mpi.h>
+#include "gmsh.h"
+
+namespace g     = gmsh;
+namespace go    = gmsh::option;
+namespace gm    = gmsh::model;
+namespace gmm   = gmsh::model::mesh;
+namespace gmo   = gmsh::model::occ;
+namespace gmg   = gmsh::model::geo;
+namespace gv    = gmsh::view;
+
+using gvp_t = gmsh::vectorpair;
+
+/* Macros used to annotate GMSH integer inputs */
+#define DIMENSION(x) x
+
+void MPIGMSH_getEntities(gvp_t&, int, MPI_Comm);
+
diff --git a/src/gmsh_io.cpp b/src/gmsh_io.cpp
index bdddb6e911c5067c8cb38389327dacfd209962a6..7d8eebee3f68cd0597ef6410246627ced6bd6ba9 100644
--- a/src/gmsh_io.cpp
+++ b/src/gmsh_io.cpp
@@ -5,6 +5,10 @@
 #include "gmsh_io.h"
 #include "entity.h"
 
+#ifdef HAVE_MPI
+#include <mpi/mpi.h>
+#endif /* HAVE_MPI */
+
 std::string
 quadrature_name(int order)
 {
@@ -34,40 +38,26 @@ basis_grad_name(int order)
 }
 
 model::model()
-    : geometric_order(1), approximation_order(1)
+    : geometric_order(1), approximation_order(1), num_partitions(1)
 {}
 
 model::model(int pgo, int pao)
-    : geometric_order(pgo), approximation_order(pao)
+    : geometric_order(pgo), approximation_order(pao), num_partitions(1)
 {
     assert(geometric_order > 0);
     assert(approximation_order > 0);
 }
 
-model::model(const char *filename)
-    : geometric_order(1), approximation_order(1)
-{
-    gmsh::open( std::string(filename) );
-}
-
-model::model(const char *filename, int pgo, int pao)
-    : geometric_order(pgo), approximation_order(pao)
-{
-    assert(geometric_order > 0);
-    assert(approximation_order > 0);
-
-    gmsh::open( std::string(filename) );
-}
-
 model::~model()
 {
-    gmsh::clear();
+    //gmsh::clear();
 }
 
+
 void
-model::build_priv()
+model::make_boundary_to_faces_map(void)
 {
-    gmm::setOrder( geometric_order );
+    assert(num_partitions > 1);
 
     gvp_t gmsh_entities;
 
@@ -76,124 +66,38 @@ model::build_priv()
      * because otherwise you get spurious 2D entities (and this in
      * turn because the constructor of entity calls addDiscreteEntity()
      * to get all the element faces) .*/
-    std::map<size_t, std::vector<face_key>>   boundaries_elemTags;
     gm::getEntities(gmsh_entities, DIMENSION(2));
     for (auto [dim, tag] : gmsh_entities)
     {
+        if (num_partitions > 1)
+        {
+            /* If the model is partitioned and entity has no parents,
+             * it means that there are no elements in the entity on which
+             * we will compute. Skip it. */
+            int pdim, ptag;
+            gmsh::model::getParent(dim, tag, pdim, ptag);
+
+            if (ptag == -1)
+                continue;
+
+            /* This stores which partitions share a boundary. It will be
+             * used to determine who communicates with who. */
+            std::vector<int> parts;
+            gm::getPartitions(dim, tag, parts);
+            comm_map[tag] = parts;
+        }
+
         std::vector<int> elemTypes;
         gmm::getElementTypes(elemTypes, dim, tag);
         assert(elemTypes.size() == 1);
+
+        /* Get all the face keys for a given boundary. */
         for (auto& elemType : elemTypes)
         {
             element_key_factory ekf(DIMENSION(2), tag, elemType);
             boundary_map[tag] = ekf.get_keys();
         }
     }
-
-    entities.clear();
-    import_gmsh_entities();
-    map_boundaries(); /* This must happen after import_gmsh_entities(). */
-}
-
-void
-model::build()
-{
-    gmm::generate( DIMENSION(3) );
-    build_priv();
-}
-
-void
-model::build(double sf)
-{
-    gmm::generate( DIMENSION(3) );
-
-    std::vector<double> tr = {sf,  0,  0,  0,
-                               0, sf,  0,  0,
-                               0,  0, sf,  0 };
-    gmm::affineTransform(tr);
-    build_priv();
-}
-
-std::vector<entity>::const_iterator
-model::begin() const
-{
-    return entities.begin();
-}
-
-std::vector<entity>::const_iterator
-model::end() const
-{
-    return entities.end();
-}
-
-std::vector<entity>::iterator
-model::begin()
-{
-    return entities.begin();
-}
-
-std::vector<entity>::iterator
-model::end()
-{
-    return entities.end();
-}
-
-entity&
-model::entity_at(size_t pos)
-{
-    return entities.at(pos);
-}
-
-const entity&
-model::entity_at(size_t pos) const
-{
-    return entities.at(pos);
-}
-
-size_t
-model::num_dofs(void) const
-{
-    size_t ret = 0;
-    for (const auto& e : entities)
-        ret += e.num_dofs();
-
-    return ret;
-}
-
-size_t
-model::num_fluxes(void) const
-{
-    size_t ret = 0;
-    for (const auto& e : entities)
-        ret += e.num_fluxes();
-
-    return ret;
-}
-
-size_t
-model::num_cells(void) const
-{
-    size_t ret = 0;
-    for (const auto& e : entities)
-        ret += e.num_cells();
-
-    return ret;
-}
-
-size_t
-model::num_faces(void) const
-{
-    size_t ret = 0;
-    for (const auto& e : entities)
-        ret += e.num_faces();
-
-    return ret;
-}
-
-size_t
-model::num_entities(void) const
-{
-    return entities.size();
 }
 
 void
@@ -223,6 +127,18 @@ model::import_gmsh_entities(void)
     size_t index_base = 0;
     for (auto [dim, tag] : gmsh_entities)
     {
+        if (num_partitions > 1)
+        {
+            /* If the model is partitioned and entity has no parents,
+             * it means that there are no elements in the entity on which
+             * we will compute. Skip it. */
+            int pdim, ptag;
+            gmsh::model::getParent(dim, tag, pdim, ptag);
+
+            if (ptag == -1)
+                continue;
+        }
+
         std::vector<int> elemTypes;
         gmm::getElementTypes(elemTypes, dim, tag);
         
@@ -330,6 +246,171 @@ model::map_boundaries(void)
     */
 }
 
+void
+model::populate_from_gmsh_rank0()
+{
+    gmm::setOrder( geometric_order );
+
+    make_boundary_to_faces_map();
+
+    for (auto& [tag, parts] : comm_map)
+    {
+        std::cout << tag << ": ";
+        for (auto& part : parts)
+            std::cout << part << " ";
+        std::cout << std::endl;
+    }
+
+    entities.clear();
+    import_gmsh_entities();
+    map_boundaries(); /* This must happen after import_gmsh_entities(). */
+}
+
+#ifdef HAVE_MPI
+void
+model::populate_from_gmsh_rankN()
+{}
+#endif /* HAVE_MPI */
+
+void
+model::populate_from_gmsh()
+{
+#ifdef HAVE_MPI
+    int rank;
+    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+    if (rank == 0)
+        populate_from_gmsh_rank0();
+    else
+        populate_from_gmsh_rankN();
+#else /* HAVE_MPI */
+    populate_from_gmsh_rank0();
+#endif /* HAVE_MPI */
+}
+
+void
+model::build()
+{
+    generate_mesh();
+    make_boundary_to_faces_map();
+    populate_from_gmsh_rank0();
+}
+
+void
+model::build(double sf)
+{
+    generate_mesh(sf);
+    make_boundary_to_faces_map();
+    populate_from_gmsh_rank0();
+}
+
+void
+model::generate_mesh()
+{
+    gmm::generate( DIMENSION(3) );
+}
+
+void
+model::generate_mesh(double sf)
+{
+    gmm::generate( DIMENSION(3) );
+
+    std::vector<double> tr = {sf,  0,  0,  0,
+                               0, sf,  0,  0,
+                               0,  0, sf,  0 };
+    gmm::affineTransform(tr);
+}
+
+void
+model::partition_mesh(int parts)
+{
+    gmm::partition(parts);
+    num_partitions = parts;
+}
+
+std::vector<entity>::const_iterator
+model::begin() const
+{
+    return entities.begin();
+}
+
+std::vector<entity>::const_iterator
+model::end() const
+{
+    return entities.end();
+}
+
+std::vector<entity>::iterator
+model::begin()
+{
+    return entities.begin();
+}
+
+std::vector<entity>::iterator
+model::end()
+{
+    return entities.end();
+}
+
+entity&
+model::entity_at(size_t pos)
+{
+    return entities.at(pos);
+}
+
+const entity&
+model::entity_at(size_t pos) const
+{
+    return entities.at(pos);
+}
+
+size_t
+model::num_dofs(void) const
+{
+    size_t ret = 0;
+    for (const auto& e : entities)
+        ret += e.num_dofs();
+
+    return ret;
+}
+
+size_t
+model::num_fluxes(void) const
+{
+    size_t ret = 0;
+    for (const auto& e : entities)
+        ret += e.num_fluxes();
+
+    return ret;
+}
+
+size_t
+model::num_cells(void) const
+{
+    size_t ret = 0;
+    for (const auto& e : entities)
+        ret += e.num_cells();
+
+    return ret;
+}
+
+size_t
+model::num_faces(void) const
+{
+    size_t ret = 0;
+    for (const auto& e : entities)
+        ret += e.num_faces();
+
+    return ret;
+}
+
+size_t
+model::num_entities(void) const
+{
+    return entities.size();
+}
+
+
+
 model::entofs_pair
 model::lookup_tag(size_t tag) const
 {
diff --git a/src/gmsh_mpi.cpp b/src/gmsh_mpi.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f644e3af0450807d511dc70507f5ee3482a9f8fc
--- /dev/null
+++ b/src/gmsh_mpi.cpp
@@ -0,0 +1,92 @@
+#include "gmsh_mpi.h"
+
+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);
+    }
+}
\ No newline at end of file
diff --git a/src/maxwell_solver.cpp b/src/maxwell_solver.cpp
index 72ac3c5b636055881a8f57b437afcc12a013c5d0..ce3cd72d56c1847014b5f553b6a7aedf712ad9f0 100644
--- a/src/maxwell_solver.cpp
+++ b/src/maxwell_solver.cpp
@@ -9,6 +9,10 @@
 #include <omp.h>
 #endif
 
+#ifdef USE_MPI
+#include <mpi/mpi.h>
+#endif
+
 #include <fstream>
 
 #include "gmsh.h"
@@ -275,10 +279,14 @@ register_lua_usertypes_bystate(maxwell::parameter_loader& mpl,
 
 int main(int argc, const char *argv[])
 {
-    gmsh::initialize();
-    //make_geometry(1, 0.1);
+#ifdef USE_MPI
+    MPI_Init(&argc, &argv);
 
-    gmsh::option::setNumber("General.Terminal", 0);
+    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;
+#endif
 
     if (argc != 2)
     {
@@ -301,6 +309,8 @@ int main(int argc, const char *argv[])
     std::filesystem::create_directory( mpl.sim_name() );;
 #endif
 
+    gmsh::initialize();
+    gmsh::option::setNumber("General.Terminal", 0);
     gmsh::open( mpl.sim_gmshmodel() );
 
     model mod( mpl.sim_geomorder(), mpl.sim_approxorder() );
@@ -336,5 +346,9 @@ int main(int argc, const char *argv[])
 
     //gmsh::finalize();
 
+#ifdef USE_MPI
+    MPI_Finalize();
+#endif
+
     return 0;
 }
diff --git a/src/testmod.cpp b/src/testmod.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..74f9ce12ca4520abdb7ac33977c9b4c55a26f745
--- /dev/null
+++ b/src/testmod.cpp
@@ -0,0 +1,74 @@
+#include <iostream>
+
+#ifdef USE_MPI
+#include <mpi/mpi.h>
+#endif
+
+#include "gmsh_io.h"
+
+std::pair<int, int>
+priv_MPI_Procinfo()
+{
+    int rank, size;
+    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+    MPI_Comm_size(MPI_COMM_WORLD, &size);
+    return std::make_pair(rank, size);
+}
+
+void
+init_model_rank0(model& mod)
+{
+    std::cout << "Rank 0 init" << std::endl;
+
+    auto [comm_rank, comm_size] = priv_MPI_Procinfo();
+
+    mod.generate_mesh();
+    if (comm_size > 1)
+        mod.partition_mesh(comm_size);
+
+    mod.populate_from_gmsh();
+
+}
+
+void
+init_model_rankN(model& mod)
+{
+    std::cout << "Rank N init" << std::endl;
+    mod.populate_from_gmsh();
+}
+
+int main(int argc, char **argv)
+{
+    if (argc < 2)
+    {
+        std::cout << "Please specify .geo" << std::endl;
+        return 1;
+    }
+
+    MPI_Init(&argc, &argv);
+
+    auto [rank, size] = priv_MPI_Procinfo();
+
+    model mod;
+
+    if (rank == 0)
+    {
+        gmsh::initialize();
+        gmsh::open(argv[1]);
+        init_model_rank0(mod);
+    }
+    else
+    {
+        init_model_rankN(mod);
+    }
+
+
+
+    if (rank == 0)
+    {
+        gmsh::finalize();
+    }
+
+    MPI_Finalize();
+    return 0;
+}
\ No newline at end of file
diff --git a/src/testmpi.cpp b/src/testmpi.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bcfe50692465a49c88c220b98214dfa47032000d
--- /dev/null
+++ b/src/testmpi.cpp
@@ -0,0 +1,39 @@
+#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;
+}
diff --git a/src/testpart.cpp b/src/testpart.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ff467d3b0c0ba75cc38e36d040b9b7cfcc97a74d
--- /dev/null
+++ b/src/testpart.cpp
@@ -0,0 +1,44 @@
+#include <iostream>
+
+#include "gmsh.h"
+
+int main(int argc, char **argv)
+{
+    if (argc < 2)
+    {
+        std::cout << "Specify .geo" << std::endl;
+        return 1;
+    }
+
+    gmsh::initialize();
+    gmsh::open(argv[1]);
+
+    gmsh::vectorpair gmsh_entities;
+
+    gmsh::model::getEntities(gmsh_entities, 2);
+
+    for (auto [dim, tag] : gmsh_entities)
+    {
+        //std::vector<int> elemTypes;
+        //gmm::getElementTypes(elemTypes, dim, tag);
+
+        std::string etype;
+        gmsh::model::getType(dim, tag, etype);
+        std::cout << etype << ", tag: " << tag << std::endl;
+
+        int pdim, ptag;
+        gmsh::model::getParent(dim, tag, pdim, ptag);
+        std::cout << "  Parent: " << ptag << std::endl;
+
+        std::vector<int> parts;
+        gmsh::model::getPartitions(dim, tag, parts);
+        std::cout << "  Partitions: ";
+        for (const auto& part : parts)
+            std::cout << part << " ";
+        std::cout << std::endl;
+    }
+
+    gmsh::finalize();
+
+    return 0;
+}
\ No newline at end of file