From e91aeb35f835d7e3d9cbef47838621502c2874b0 Mon Sep 17 00:00:00 2001
From: Matteo Cicuttin <datafl4sh@toxicnet.eu>
Date: Tue, 31 Aug 2021 17:06:22 +0200
Subject: [PATCH] Interprocess connectivity.

---
 include/connectivity.h | 96 ++++++++++++++++++++++++++++++++++++++++++
 include/gmsh_io.h      |  9 ++++
 src/connectivity.cpp   | 21 +++++++++
 src/gmsh_io.cpp        | 83 +++++++++++++++++++++++++++++++++---
 src/maxwell_common.cpp | 86 +++++++++++++++++++++++++------------
 5 files changed, 263 insertions(+), 32 deletions(-)

diff --git a/include/connectivity.h b/include/connectivity.h
index 275ca63..23a4fa2 100644
--- a/include/connectivity.h
+++ b/include/connectivity.h
@@ -6,8 +6,13 @@
 #include <map>
 #include <set>
 
+#include "point.hpp"
 #include "sgr.hpp"
 
+#ifdef USE_MPI
+#include "gmsh_mpi.h"
+#endif /* USE_MPI */
+
 /***************************************************************************/
 struct neighbour_descriptor
 {
@@ -22,6 +27,25 @@ struct neighbour_descriptor
 
 std::ostream& operator<<(std::ostream&, const neighbour_descriptor&);
 
+struct neighbour_descriptor_interprocess
+{
+    size_t      iE;         /* Entity offset in data structures */
+    size_t      iT;         /* Element offset in entity in data structures */
+    size_t      via_iF;     /* Element face index (i.e. 0,1,2 for triangles) */
+
+    int         parent_tag; /* Tag of the parent entity */
+    int         tag;        /* Tag of the actual entity */
+    int         partition;  /* Partition on which the element resides */
+
+    point_3d    bar;        /* Barycenter, for materials. */
+
+    neighbour_descriptor_interprocess();
+    bool operator<(const neighbour_descriptor_interprocess&) const;
+
+};
+
+std::ostream& operator<<(std::ostream&, const neighbour_descriptor_interprocess&);
+
 /***************************************************************************/
 struct cell_descriptor
 {
@@ -235,3 +259,75 @@ public:
     }
 };
 
+
+
+
+/***************************************************************************/
+#ifdef USE_MPI
+template<typename FaceKey>
+class interprocess_connectivity
+{
+public:
+    using ConnData  = std::pair<FaceKey, neighbour_descriptor_interprocess>;
+    using NeighPair = neighbour_pair<neighbour_descriptor_interprocess>;
+
+private:
+    std::map<FaceKey, NeighPair>                    face_cell_adj;
+
+public:
+    interprocess_connectivity()
+    {}
+
+    void
+    connect(const FaceKey& fk, const neighbour_descriptor_interprocess& nd)
+    {
+        /* Insert the information that one the two neighbours around
+         * face `e' is `nd'. */
+        auto& nr = face_cell_adj[fk];
+        nr.add_neighbour(nd);
+    }
+
+    /* Find the neighbour of the element (iE,iT) via face fk */
+    std::pair<neighbour_descriptor_interprocess, bool>
+    neighbour_via(size_t iE, size_t iT, const FaceKey& fk) const
+    {
+        auto itor = face_cell_adj.find(fk);
+        if (itor == face_cell_adj.end())
+            return std::make_pair(neighbour_descriptor_interprocess(), false);
+
+        auto nr = (*itor).second;
+        assert( nr.num_neighbours() == 2 );
+
+        auto fst = nr.first();
+        auto snd = nr.second();
+
+        assert( (fst.iE == iE and fst.iT == iT) or
+                (snd.iE == iE and snd.iT == iT)
+              );
+
+        if ( fst.iE == iE and fst.iT == iT)
+            return std::make_pair(snd, true);
+
+        return std::make_pair(fst, true);
+    }
+
+    void clear(void)
+    {
+        face_cell_adj.clear();
+    }
+
+    void dump(void)
+    {
+        for (auto& [key, pair] : face_cell_adj)
+        {
+            std::cout << key << ": " << pair.first() << " - " << pair.second() << std::endl;
+        }
+    }
+
+    void mpi_bcast(int root, MPI_Comm comm)
+    {
+        priv_MPI_Bcast(face_cell_adj, root, comm);
+    }
+};
+
+#endif /* USE_MPI */
\ No newline at end of file
diff --git a/include/gmsh_io.h b/include/gmsh_io.h
index fd7f5c8..10a780f 100644
--- a/include/gmsh_io.h
+++ b/include/gmsh_io.h
@@ -111,6 +111,8 @@ class model
     element_connectivity<element_key>           conn;
 
 #ifdef USE_MPI
+    interprocess_connectivity<element_key>      ipconn;
+
     /* Map from boundary tag to all the partitions it belongs to */
     std::map<int, std::vector<int>>             comm_map;       
 
@@ -145,6 +147,7 @@ class model
     void    import_gmsh_entities_rankN(void);
     int     my_partition(void) const;
     void    make_partition_to_entities_map(void);
+    void    map_interprocess_boundaries();
 #endif /* USE_MPI */
 
     void    make_boundary_to_faces_map(void);
@@ -206,6 +209,12 @@ public:
 
     entity&         entity_world_lookup(int);
     const entity&   entity_world_lookup(int) const;
+
+    const interprocess_connectivity<element_key>& ip_connectivity() const
+    {
+        return ipconn;
+    }
+
 #endif /* USE_MPI */
 
     const element_connectivity<element_key>& connectivity() const
diff --git a/src/connectivity.cpp b/src/connectivity.cpp
index b42f125..a79245d 100644
--- a/src/connectivity.cpp
+++ b/src/connectivity.cpp
@@ -24,6 +24,27 @@ std::ostream& operator<<(std::ostream& os, const neighbour_descriptor& nd)
     return os;
 }
 
+
+
+
+neighbour_descriptor_interprocess::neighbour_descriptor_interprocess()
+{}
+
+bool
+neighbour_descriptor_interprocess::operator<(const neighbour_descriptor_interprocess& other) const
+{
+    std::array<size_t, 3>   arr_me{iE, iT, via_iF};
+    std::array<size_t, 3>   arr_other{other.iE, other.iT, other.via_iF};
+    return arr_me < arr_other;
+}
+
+std::ostream& operator<<(std::ostream& os, const neighbour_descriptor_interprocess& nd)
+{
+    os << "(" << nd.iE << ", " << nd.iT << ", " << nd.via_iF << ", PTAG: " << nd.parent_tag;
+    os << ", TAG: " << nd.tag << ", PART: " << nd.partition << ")";
+    return os;
+}
+
 /***************************************************************************/
 cell_descriptor::cell_descriptor()
 {}
diff --git a/src/gmsh_io.cpp b/src/gmsh_io.cpp
index e403877..acc18cc 100644
--- a/src/gmsh_io.cpp
+++ b/src/gmsh_io.cpp
@@ -93,7 +93,9 @@ model::make_boundary_to_faces_map(void)
              * used to determine who communicates with who. */
             std::vector<int> parts;
             gm::getPartitions(dim, tag, parts);
-            comm_map[tag] = parts;
+            if (parts.size() == 2)
+                comm_map[tag] = parts;
+
             if (pdim == dim)
                 surface_to_parent_map[tag] = ptag;
         }
@@ -157,10 +159,6 @@ model::make_partition_to_entities_map()
 void
 model::update_connectivity(const entity& e, size_t entity_index)
 {
-#ifdef USE_MPI
-    //ASSERT_MPI_RANK_0;
-#endif /* USE_MPI */
-
     for (size_t iT = 0; iT < e.num_cells(); iT++)
     {
         for (size_t iF = 0; iF < e.num_faces_per_elem(); iF++)
@@ -368,7 +366,7 @@ bool
 model::is_interprocess_boundary(int tag)
 {
     if (num_partitions > 1)
-        return comm_map.at(tag).size() == 2;
+        return comm_map.find(tag) != comm_map.end();
     
     return false;
 }
@@ -468,6 +466,73 @@ model::map_boundaries(void)
 #endif
 }
 
+#ifdef USE_MPI
+void
+model::map_interprocess_boundaries(void)
+{
+    /* Make a vector mapping element_key to entity tag */
+    using bfk_t = std::pair<element_key, int>;
+    std::vector<bfk_t> bfk;
+    for (auto& [tag, keys] : boundary_map)
+    {
+        if ( not is_interprocess_boundary(tag) )
+            continue;
+        
+        for (auto& k : keys)
+            bfk.push_back( std::make_pair(k, tag) );
+    }
+    /* Sort it for lookups */
+    auto comp = [](const bfk_t& a, const bfk_t& b) -> bool {
+        return a.first < b.first;
+    };
+    std::sort(bfk.begin(), bfk.end(), comp);
+
+    auto lbcomp = [](const bfk_t& a, const element_key& b) -> bool {
+        return a.first < b;
+    };
+
+    auto connect = [&](const entity& e, int part) {
+        for (size_t iT = 0; iT < e.num_cells(); iT++)
+        {
+            auto nfpe = e.num_faces_per_elem();
+            for (size_t iF = 0; iF < nfpe; iF++)
+            {
+                auto fnum = iT*nfpe + iF;
+                element_key fk(e.face(fnum));
+
+                auto itor = std::lower_bound(bfk.begin(), bfk.end(), fk, lbcomp);
+                if ( (itor == bfk.end()) or not ((*itor).first == fk) )
+                    continue;
+            
+                neighbour_descriptor_interprocess ndi;
+                ndi.iE = e.number();
+                ndi.iT = iT;
+                ndi.via_iF = iF;
+                ndi.parent_tag = e.material_tag();
+                ndi.tag = e.gmsh_tag();
+                ndi.partition = part;
+                ndi.bar = e.cell(iT).barycenter();
+                ipconn.connect(fk, ndi);
+            }
+        }
+    };
+
+    for (auto& e : entities)
+    {
+        auto part = my_partition();
+        assert(part == 1);
+        connect(e, part);
+    }
+
+    for (auto& [part, res] : remote_entities)
+    {
+        assert(part != 1);
+        for (auto& e : res)
+            connect(e, part);
+    }
+}
+#endif
+
 void
 model::populate_from_gmsh_rank0()
 {
@@ -512,6 +577,11 @@ model::populate_from_gmsh_rank0()
     entities.clear();
     import_gmsh_entities_rank0();
     map_boundaries(); /* This must happen after import_gmsh_entities(). */
+    
+#ifdef USE_MPI
+    map_interprocess_boundaries();
+    ipconn.mpi_bcast(0, MPI_COMM_WORLD);
+#endif /* USE_MPI */
 }
 
 #ifdef USE_MPI
@@ -539,6 +609,7 @@ model::populate_from_gmsh_rankN()
     entities.clear();
     import_gmsh_entities_rankN();
     map_boundaries();
+    ipconn.mpi_bcast(0, MPI_COMM_WORLD);
 }
 #endif /* USE_MPI */
 
diff --git a/src/maxwell_common.cpp b/src/maxwell_common.cpp
index ba1e8db..e6a0feb 100644
--- a/src/maxwell_common.cpp
+++ b/src/maxwell_common.cpp
@@ -2,9 +2,10 @@
 #include "maxwell_interface.h"
 #include "maxwell_common.h"
 
-#define DIRICHLET   2.0
-#define ROBIN       1.0
-#define NEUMANN     0.0
+#define DIRICHLET       2.0
+#define ROBIN           1.0
+#define INTERPROCESS    1.0
+#define NEUMANN         0.0
 
 namespace maxwell {
 
@@ -29,25 +30,32 @@ init_boundary_conditions(const model& mod, const parameter_loader& mpl,
                 continue;
 
             double bc_coeff = DIRICHLET;
-            switch ( mpl.boundary_type(bd.material_tag()) )
+            if (bd.type == face_type::INTERPROCESS_BOUNDARY)
             {
-                case boundary_condition::UNSPECIFIED:
-                case boundary_condition::PEC:
-                case boundary_condition::E_FIELD:
-                    bc_coeff = DIRICHLET;
-                    break;
-
-                case boundary_condition::IMPEDANCE:
-                case boundary_condition::PLANE_WAVE_E:
-                case boundary_condition::PLANE_WAVE_H:
-                    bc_coeff = ROBIN;
-                    break;
-
-                case boundary_condition::PMC:
-                case boundary_condition::SURFACE_CURRENT:
-                case boundary_condition::H_FIELD:
-                    bc_coeff = NEUMANN;
-                    break;
+                bc_coeff = INTERPROCESS;
+            }
+            else
+            {
+                switch ( mpl.boundary_type(bd.material_tag()) )
+                {
+                    case boundary_condition::UNSPECIFIED:
+                    case boundary_condition::PEC:
+                    case boundary_condition::E_FIELD:
+                        bc_coeff = DIRICHLET;
+                        break;
+
+                    case boundary_condition::IMPEDANCE:
+                    case boundary_condition::PLANE_WAVE_E:
+                    case boundary_condition::PLANE_WAVE_H:
+                        bc_coeff = ROBIN;
+                        break;
+
+                    case boundary_condition::PMC:
+                    case boundary_condition::SURFACE_CURRENT:
+                    case boundary_condition::H_FIELD:
+                        bc_coeff = NEUMANN;
+                        break;
+               }
             }
 
             auto& pf = e.face(iF);
@@ -73,6 +81,9 @@ init_lax_milgram(const model& mod, const parameter_loader& mpl,
     pbH = vecxd::Zero( mod.num_fluxes() );
 
     auto& conn = mod.connectivity();
+#ifdef USE_MPI
+    auto& ipconn = mod.ip_connectivity();
+#endif /* USE_MPI */
     for (auto& e : mod)
     {
         for (size_t iT = 0; iT < e.num_cells(); iT++)
@@ -120,16 +131,39 @@ init_lax_milgram(const model& mod, const parameter_loader& mpl,
                         paH(glob_ofs+iD) = aH;
                         pbH(glob_ofs+iD) = bH;
                     }
+                    continue;
                 }
-                else
+#ifdef USE_MPI
+                auto [ip_neigh, has_ip_neigh] = ipconn.neighbour_via(e.number(), iT, fk);
+                if (has_ip_neigh)
                 {
+                    auto ne_eps = mpl.epsilon(ip_neigh.parent_tag, ip_neigh.bar);
+                    auto ne_mu = mpl.mu(ip_neigh.parent_tag, ip_neigh.bar);
+
+                    auto ne_Z = std::sqrt(ne_mu/ne_eps);
+                    auto ne_Y = 1./ne_Z;
+
+                    auto aE = ne_Z/(my_Z + ne_Z);
+                    auto bE = 1./(my_Z + ne_Z);
+                    auto aH = ne_Y/(my_Y + ne_Y);
+                    auto bH = 1./(my_Y + ne_Y);
+
                     for (size_t iD = 0; iD < rf.num_basis_functions(); iD++)
                     {
-                        paE(glob_ofs+iD) = 0.5;
-                        pbE(glob_ofs+iD) = 0.5/my_Z;
-                        paH(glob_ofs+iD) = 0.5;
-                        pbH(glob_ofs+iD) = 0.5/my_Y;
+                        paE(glob_ofs+iD) = aE;
+                        pbE(glob_ofs+iD) = bE;
+                        paH(glob_ofs+iD) = aH;
+                        pbH(glob_ofs+iD) = bH;
                     }
+                    continue;
+                }
+#endif /* USE_MPI */
+                for (size_t iD = 0; iD < rf.num_basis_functions(); iD++)
+                {
+                    paE(glob_ofs+iD) = 0.5;
+                    pbE(glob_ofs+iD) = 0.5/my_Z;
+                    paH(glob_ofs+iD) = 0.5;
+                    pbH(glob_ofs+iD) = 0.5/my_Y;
                 }
             }
         }
-- 
GitLab