From ad399724d242e24c466597cabef39c0fc6ec47eb Mon Sep 17 00:00:00 2001
From: Matteo Cicuttin <datafl4sh@toxicnet.eu>
Date: Mon, 14 Mar 2022 16:08:02 +0100
Subject: [PATCH] Full support for physical entities. Mandatory with MPI.

---
 doc/lua_api.md                     |  1 +
 include/libgmshdg/gmsh_io.h        | 23 ++++++++++--
 include/maxwell/maxwell_common.h   | 18 +++++-----
 share/validation/params_twomat.lua | 56 ++++++++++++++++++------------
 share/validation/twomat.geo        |  6 ++++
 src/libgmshdg/gmsh_io.cpp          | 16 +++++----
 src/libgmshdg/param_loader.cpp     | 28 ++++-----------
 src/maxwell/maxwell_common.cpp     | 10 +++---
 src/maxwell/maxwell_cpu.cpp        |  4 +--
 src/maxwell/maxwell_gpu.cpp        |  8 ++---
 src/maxwell/maxwell_interface.cpp  | 21 +++++++++--
 src/maxwell/maxwell_postpro.cpp    |  4 +--
 src/maxwell/maxwell_solver.cpp     |  2 +-
 13 files changed, 119 insertions(+), 78 deletions(-)

diff --git a/doc/lua_api.md b/doc/lua_api.md
index decb61f..2d01e98 100644
--- a/doc/lua_api.md
+++ b/doc/lua_api.md
@@ -32,6 +32,7 @@ This document describes the API available on the version v0.4 of the solver.
 
 ### Parallel execution information (available only if MPI support is enabled)
 The parallel solver runs as separate MPI processes. As such, each process loads and executes its own instance of the configuration script. This means that you must beware of global variables or other shared state, because modifications are **not** reflected across processes. Even if the configuration script should in general not depend on the rank of the current process, the following variables are exposed:
+
 - `parallel.comm_rank` (integer): The MPI rank of the current solver process
 - `parallel.comm_size` (integer): The MPI communicator size
 
diff --git a/include/libgmshdg/gmsh_io.h b/include/libgmshdg/gmsh_io.h
index c33ac53..e9baad2 100644
--- a/include/libgmshdg/gmsh_io.h
+++ b/include/libgmshdg/gmsh_io.h
@@ -73,6 +73,11 @@ struct boundary_descriptor
 #endif /* USE_MPI */
     {}
 
+    int gmsh_tag(void) const
+    {
+        return gmsh_entity;
+    }
+
     int material_tag(void) const
     {
 #ifdef USE_MPI
@@ -95,6 +100,15 @@ struct physical_group_descriptor {
     }
 };
 
+inline std::ostream&
+operator<<(std::ostream& os, const physical_group_descriptor& pgd)
+{
+    os << "["<< pgd.dim << ", " << pgd.tag << "] ";
+    for (auto& et : pgd.entity_tags)
+        os << et << " ";
+    return os;
+}
+
 #ifdef USE_MPI
 struct comm_descriptor
 {
@@ -107,8 +121,8 @@ struct comm_descriptor
     std::vector<element_key>    fks;
 };
 
-inline
-std::ostream& operator<<(std::ostream& os, const comm_descriptor& cd)
+inline std::ostream&
+operator<<(std::ostream& os, const comm_descriptor& cd)
 {
     os << "tag: " << cd.boundary_tag << ", PM: " << cd.partition_mine << ", PO: " << cd.partition_other;
     os << ", RM: " << cd.rank_mine << ", RO: " << cd.rank_other << ", map size: " << cd.dof_mapping.size();
@@ -178,6 +192,11 @@ struct surface_descriptor
         std::sort(face_numbers.begin(), face_numbers.end(), comp);
     }
 
+    int gmsh_tag(void) const
+    {
+        return surface_entity;
+    }
+
     int material_tag(void) const
     {
 #ifdef USE_MPI
diff --git a/include/maxwell/maxwell_common.h b/include/maxwell/maxwell_common.h
index 1e902e1..1997435 100644
--- a/include/maxwell/maxwell_common.h
+++ b/include/maxwell/maxwell_common.h
@@ -25,7 +25,7 @@ eval_boundary_sources(const model& mod, const parameter_loader& mpl,
     size_t face_num_base = 0;
     for (auto& e : mod)
     {
-        auto etag = e.material_tag();
+        auto etag = e.gmsh_tag();
         for (size_t iF = 0; iF < e.num_faces(); iF++)
         {
             auto gfnum = face_num_base + iF;
@@ -45,7 +45,7 @@ eval_boundary_sources(const model& mod, const parameter_loader& mpl,
 
             vec3d n = state.eds[e.number()].normals.row(iF);
 
-            switch ( mpl.boundary_type(bd.material_tag()) )
+            switch ( mpl.boundary_type(bd.gmsh_tag()) )
             {
                 case boundary_condition::UNSPECIFIED:
                     break;
@@ -67,10 +67,10 @@ eval_boundary_sources(const model& mod, const parameter_loader& mpl,
 
                 case boundary_condition::PLANE_WAVE_E: {
                     auto fE = [&](const point_3d& pt) -> vec3d {
-                        return mpl.eval_boundary_source(bd.material_tag(), pt, state.curr_time);
+                        return mpl.eval_boundary_source(bd.gmsh_tag(), pt, state.curr_time);
                     };
                     auto fH = [&](const point_3d& pt) -> vec3d {
-                        vec3d H = mpl.eval_boundary_source(bd.material_tag(), pt, state.curr_time);
+                        vec3d H = mpl.eval_boundary_source(bd.gmsh_tag(), pt, state.curr_time);
                         return n.cross(H)/Z;
                     };
                     matxd prjE = e.project_on_face(iF, fE);
@@ -149,9 +149,9 @@ eval_boundary_sources_new(const model& mod, const parameter_loader& mpl,
     for (auto& [si, sd] : bds)
     {
         const auto& e = mod.entity_at(sd.adjacent_volume_entity);
-        auto etag = e.material_tag();
+        auto etag = e.gmsh_tag();
 
-        auto mtag = sd.material_tag();
+        auto mtag = sd.gmsh_tag();
         switch ( mpl.boundary_type( mtag ) )
         {
             case boundary_condition::UNSPECIFIED:
@@ -219,7 +219,7 @@ eval_interface_sources(const model& mod, const parameter_loader& mpl,
 
             vec3d n = state.eds[e.number()].normals.row(iF);
 
-            switch ( mpl.interface_type(bd.material_tag()) )
+            switch ( mpl.interface_type(bd.gmsh_tag()) )
             {
                 case interface_condition::UNSPECIFIED:
                     break;
@@ -227,7 +227,7 @@ eval_interface_sources(const model& mod, const parameter_loader& mpl,
                 case interface_condition::E_FIELD: {
                     /* This is BS actually... */
                     auto fE = [&](const point_3d& pt) -> vec3d {
-                        return mpl.eval_interface_source(bd.material_tag(), pt, state.curr_time);
+                        return mpl.eval_interface_source(bd.gmsh_tag(), pt, state.curr_time);
                     };
 
                     matxd prjE = e.project_on_face(iF, fE);
@@ -242,7 +242,7 @@ eval_interface_sources(const model& mod, const parameter_loader& mpl,
 
                 case interface_condition::SURFACE_CURRENT: {
                     auto fJ = [&](const point_3d& pt) -> vec3d {
-                        vec3d J = mpl.eval_interface_source(bd.material_tag(), pt, state.curr_time);
+                        vec3d J = mpl.eval_interface_source(bd.gmsh_tag(), pt, state.curr_time);
                         return n.cross(J);
                     };
 
diff --git a/share/validation/params_twomat.lua b/share/validation/params_twomat.lua
index 67b0160..8be05f6 100644
--- a/share/validation/params_twomat.lua
+++ b/share/validation/params_twomat.lua
@@ -13,35 +13,47 @@ postpro.cycle_print_rate = 100                  -- console print rate
 
 postpro["E"].mode = "nodal"
 
-materials[1] = {}
-materials[1].epsilon = 1
-materials[1].mu = 1
-materials[1].sigma = 0
-
-materials[2] = {}
-materials[2].epsilon = 4
-materials[2].mu = 1
-materials[2].sigma = 0
-
--- ** Boundary conditions **
-local PMC_bnds = { 3, 4, 8, 9 }
-for i,v in ipairs(PMC_bnds) do
-    bndconds[v] = {}
-    bndconds[v].kind = "pmc"
+function setup_materials()
+    local bulk = volume_physical_group_entities(1);
+    for i,v in ipairs(bulk) do
+        materials[v] = {}
+        materials[v].epsilon = 1
+        materials[v].mu = 1
+        materials[v].sigma = 0
+    end
 end
 
-bndconds[7] = {}
-bndconds[7].kind = "impedance"
-
 local freq = 3e8
-function source(tag, x, y, z, t)
+function plane_wave(tag, x, y, z, t)
     local Ex = 0
     local Ey = 0
     local Ez = math.sin(2*math.pi*t*freq)
     return Ex, Ey, Ez
 end
 
-bndconds[1] = {}
-bndconds[1].kind = "plane_wave_E"
-bndconds[1].source = source
+function setup_boundary_conditions()
+    local source = surface_physical_group_entities(10);
+    for i,v in ipairs(source) do
+        bndconds[v] = {};
+        bndconds[v].kind = "plane_wave_E";
+        bndconds[v].source = plane_wave;
+    end
+
+    local pmc = surface_physical_group_entities(11);
+    for i,v in ipairs(pmc) do
+        bndconds[v] = {};
+        bndconds[v].kind = "pmc";
+    end
+
+    local abc = surface_physical_group_entities(13);
+    for i,v in ipairs(abc) do
+        bndconds[v] = {};
+        bndconds[v].kind = "impedance";
+    end
+end
+
+function on_mesh_loaded()
+    setup_materials();
+    setup_boundary_conditions();
+end
 
diff --git a/share/validation/twomat.geo b/share/validation/twomat.geo
index 6545506..5ca38ad 100644
--- a/share/validation/twomat.geo
+++ b/share/validation/twomat.geo
@@ -3,4 +3,10 @@ Box(1) = {0, 0, 0, 1, 1, 0.1};
 Box(2) = {1, 0, 0, 1, 1, 0.1};
 Coherence;
 
+Physical Volume("bulk", 1) = {1,2};
+Physical Surface("source", 10) = {1};
+Physical Surface("pmc", 11) = {3,4,8,9};
+Physical Surface("pec", 12) = {5,6,10,11};
+Physical Surface("abc", 13) = {7};
+
 MeshSize{:} = 0.075;
diff --git a/src/libgmshdg/gmsh_io.cpp b/src/libgmshdg/gmsh_io.cpp
index b67a6b4..7fcedca 100644
--- a/src/libgmshdg/gmsh_io.cpp
+++ b/src/libgmshdg/gmsh_io.cpp
@@ -423,7 +423,7 @@ model::import_physical_groups(void)
         for (auto& pg : physical_groups)
         {
             MPI_Bcast(&pg.dim, 1, MPI_INT, 0, MPI_COMM_WORLD);
-            MPI_Bcast(&pg.dim, 1, MPI_INT, 0, MPI_COMM_WORLD);
+            MPI_Bcast(&pg.tag, 1, MPI_INT, 0, MPI_COMM_WORLD);
             size_t esize = pg.entity_tags.size();
             MPI_Bcast(&esize, 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD);
             MPI_Bcast(pg.entity_tags.data(), esize, MPI_INT, 0, MPI_COMM_WORLD);
@@ -435,7 +435,9 @@ model::import_physical_groups(void)
         for (auto& pg : physical_groups)
         {
             MPI_Bcast(&pg.dim, 1, MPI_INT, 0, MPI_COMM_WORLD);
-            MPI_Bcast(&pg.dim, 1, MPI_INT, 0, MPI_COMM_WORLD);
+            assert(pg.dim != 0);
+            MPI_Bcast(&pg.tag, 1, MPI_INT, 0, MPI_COMM_WORLD);
+            assert(pg.tag != 0);
             size_t esize;
             MPI_Bcast(&esize, 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD);
             pg.entity_tags.resize(esize);
@@ -1114,10 +1116,10 @@ model::lookup_physical_group_entities(int dim, int tag)
     physical_group_descriptor pg;
     pg.dim = dim;
     pg.tag = tag;
-    auto elem = std::lower_bound(physical_groups.begin() , physical_groups.end(), pg);
-    if (!(elem == physical_groups.end()) && !(pg < *physical_groups.begin()))
-        return elem->entity_tags;
-
+    auto pg_begin = physical_groups.begin();
+    auto pg_end = physical_groups.end();
+    auto itor = std::lower_bound(pg_begin , pg_end, pg);
+    if (!(itor == pg_end) && !(pg < *pg_begin))
+        return itor->entity_tags;
     return std::vector<int>{};
 }
-
diff --git a/src/libgmshdg/param_loader.cpp b/src/libgmshdg/param_loader.cpp
index 569a959..0adc570 100644
--- a/src/libgmshdg/param_loader.cpp
+++ b/src/libgmshdg/param_loader.cpp
@@ -16,25 +16,6 @@
 
 #include "libgmshdg/param_loader.h"
 
-static auto
-lua_getEntitiesForPhysicalGroup(int dim, int tag)
-{
-    std::vector<int> ret;
-#ifdef USE_MPI
-    int rank;
-    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
-    if (rank != 0)
-    {
-        std::cout << "[WARNING] getEntitiesForPhysicalGroup() ";
-        std::cout << "can be called only on MPI rank 0. On the other ";
-        std::cout << "ranks you will get an empty list." << std::endl;
-        return sol::as_table(ret);
-    }
-#endif
-    gmsh::model::getEntitiesForPhysicalGroup(dim, tag, ret);
-    return sol::as_table(ret);
-}
-
 parameter_loader_base::parameter_loader_base()
     : m_bnd_sources_active(true), m_ifc_sources_active(true),
       m_vol_sources_active(true)
@@ -86,8 +67,13 @@ parameter_loader_base::init(void)
                      &parameter_loader_base::enable_volume_sources,
                      this);
     
-    lua.set_function("getEntitiesForPhysicalGroup",
-        &lua_getEntitiesForPhysicalGroup);
+    auto exit_abc = [](){
+#ifdef USE_MPI
+        MPI_Finalize();
+#endif /* USE_MPI */
+        exit(0);
+    };
+    lua["exit"] = exit_abc;
 
 #ifdef USE_MPI
     int comm_rank, comm_size;
diff --git a/src/maxwell/maxwell_common.cpp b/src/maxwell/maxwell_common.cpp
index bda7f6d..eec2b36 100644
--- a/src/maxwell/maxwell_common.cpp
+++ b/src/maxwell/maxwell_common.cpp
@@ -48,7 +48,7 @@ init_boundary_conditions(const model& mod, const parameter_loader& mpl,
             else
             {
 #endif /* USE_MPI */
-                switch ( mpl.boundary_type(bd.material_tag()) )
+                switch ( mpl.boundary_type(bd.gmsh_tag()) )
                 {
                     case boundary_condition::UNSPECIFIED:
                     case boundary_condition::PEC:
@@ -103,7 +103,7 @@ init_lax_milgram(const model& mod, const parameter_loader& mpl,
         {
             auto& pe = e.cell(iT);
             auto bar = pe.barycenter();
-            auto tag = e.material_tag();
+            auto tag = e.gmsh_tag();
             auto my_eps = mpl.epsilon(tag, bar);
             auto my_mu = mpl.mu(tag, bar);
 
@@ -125,7 +125,7 @@ init_lax_milgram(const model& mod, const parameter_loader& mpl,
                     auto& ne_e = mod.entity_at(neigh.iE);
                     auto& ne_pe = ne_e.cell(neigh.iT);
                     auto ne_bar = ne_pe.barycenter();
-                    auto ne_tag = ne_e.material_tag();
+                    auto ne_tag = ne_e.gmsh_tag();
                     auto ne_eps = mpl.epsilon(ne_tag, ne_bar);
                     auto ne_mu = mpl.mu(ne_tag, ne_bar);
 
@@ -228,7 +228,7 @@ export_vector_field_nodal(const model& mod, silo& sdb, const vecxd& Fx,
             auto& pe = e.cell(iT);
             auto cgofs = e.cell_world_dof_offset(iT);
             auto nodetags = pe.node_tags();
-            auto mp = mf(e.material_tag(), pe.barycenter());
+            auto mp = mf(e.gmsh_tag(), pe.barycenter());
             for (size_t iD = 0; iD < 4; iD++)
             {
                 size_t tag = nodetags[iD];
@@ -282,7 +282,7 @@ export_vector_field_zonal(const model& mod, silo& sdb, const vecxd& Fx,
             auto& pe = e.cell(iT);
             auto& re = e.cell_refelem(pe);
             vecxd phi_bar = re.basis_functions({1./3., 1./3., 1./3.});
-            auto mp = mf(e.material_tag(), pe.barycenter());
+            auto mp = mf(e.gmsh_tag(), pe.barycenter());
             auto num_bf = re.num_basis_functions();
             auto ofs = e.cell_world_dof_offset(iT);
             auto gi = e.cell_world_index_by_gmsh(iT);
diff --git a/src/maxwell/maxwell_cpu.cpp b/src/maxwell/maxwell_cpu.cpp
index b54294b..e6d7782 100644
--- a/src/maxwell/maxwell_cpu.cpp
+++ b/src/maxwell/maxwell_cpu.cpp
@@ -33,7 +33,7 @@ init_matparams(const model& mod, solver_state& state,
 
     for (auto& e : mod)
     {
-        auto etag = e.material_tag();
+        auto etag = e.gmsh_tag();
         for (size_t iT = 0; iT < e.num_cells(); iT++)
         {
             auto& pe = e.cell(iT);
@@ -787,7 +787,7 @@ eval_volume_sources(const model& mod, const parameter_loader& mpl, solver_state&
 {
     for (auto& e : mod)
     {
-        auto etag = e.material_tag();
+        auto etag = e.gmsh_tag();
         if ( not mpl.volume_has_source(etag) )
             continue;
 
diff --git a/src/maxwell/maxwell_gpu.cpp b/src/maxwell/maxwell_gpu.cpp
index 5f95498..e13aff2 100644
--- a/src/maxwell/maxwell_gpu.cpp
+++ b/src/maxwell/maxwell_gpu.cpp
@@ -32,7 +32,7 @@ init_matparams(const model& mod, solver_state_gpu& state,
 
     for (auto& e : mod)
     {
-        auto etag = e.material_tag();
+        auto etag = e.gmsh_tag();
         for (size_t iT = 0; iT < e.num_cells(); iT++)
         {
             auto& pe = e.cell(iT);
@@ -87,7 +87,7 @@ build_source_compression_tables(const model& mod, solver_state_gpu& state, const
             auto bd = bds[gfnum];
             if (bd.type == face_type::INTERFACE)
             {
-                switch ( mpl.interface_type(bd.material_tag()) )
+                switch ( mpl.interface_type(bd.gmsh_tag()) )
                 {
                     case interface_condition::E_FIELD:
                     case interface_condition::SURFACE_CURRENT:
@@ -102,7 +102,7 @@ build_source_compression_tables(const model& mod, solver_state_gpu& state, const
 
             if (bd.type == face_type::BOUNDARY)
             {
-                switch ( mpl.boundary_type(bd.material_tag()) )
+                switch ( mpl.boundary_type(bd.gmsh_tag()) )
                 {
                     case boundary_condition::PLANE_WAVE_E:
                         for (size_t i = 0; i < num_fluxes; i++)
@@ -594,7 +594,7 @@ eval_volume_sources(const model& mod, const parameter_loader& mpl, solver_state_
 {
     for (auto& e : mod)
     {
-        auto etag = e.material_tag();
+        auto etag = e.gmsh_tag();
         if ( not mpl.volume_has_source(etag) )
             continue;
 
diff --git a/src/maxwell/maxwell_interface.cpp b/src/maxwell/maxwell_interface.cpp
index ab6c81e..32899c4 100644
--- a/src/maxwell/maxwell_interface.cpp
+++ b/src/maxwell/maxwell_interface.cpp
@@ -524,6 +524,20 @@ parameter_loader::parameter_loader()
 bool
 parameter_loader::validate_materials(const std::string& mpn, int tag) const
 {
+#ifdef USE_MPI
+    int rank;
+    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+    if (rank == 0) {
+#endif /* USE_MPI */
+        int pdim, ptag;
+        gm::getParent(3,tag,pdim,ptag);
+        if (ptag == -1)
+            return true; /* This is not a computational entity, only the parent of a partition. */
+#ifdef USE_MPI
+    } else {
+        return true; /* The script is the same for all ranks, do validation only on rank 0. */
+    }
+#endif /* USE_MPI */
     auto mfun = lua[SEC_MATERIALS][mpn];
     if ( mfun.valid() )
         return true;
@@ -636,12 +650,12 @@ parameter_loader::validate_conditions(const model& mod) const
                 break;
 
             case face_type::BOUNDARY:
-                if (not validate_boundary_condition(mod, bd.material_tag()) )
+                if (not validate_boundary_condition(mod, bd.gmsh_tag()) )
                     success = false;
                 break;
 
             case face_type::INTERFACE:
-                if (not validate_interface_condition(mod, bd.material_tag()) )
+                if (not validate_interface_condition(mod, bd.gmsh_tag()) )
                     success = false;
                 break;
 
@@ -659,6 +673,7 @@ bool
 parameter_loader::validate_model_params(const model& mod) const
 {
     bool success = true;
+    
     for (auto& e : mod)
     {
         auto tag = e.material_tag();
@@ -671,7 +686,7 @@ parameter_loader::validate_model_params(const model& mod) const
 
     if ( not validate_conditions(mod) )
         success = false;
-
+    
     return success;
 }
 
diff --git a/src/maxwell/maxwell_postpro.cpp b/src/maxwell/maxwell_postpro.cpp
index 8490a59..c84baf1 100644
--- a/src/maxwell/maxwell_postpro.cpp
+++ b/src/maxwell/maxwell_postpro.cpp
@@ -92,8 +92,8 @@ compute_energy(const model& mod, const solver_state& state, const parameter_load
             const auto cbs = re.num_basis_functions();
 
             const auto bar = pe.barycenter();
-            auto eps = mpl.epsilon( e.material_tag(), bar );
-            auto mu = mpl.mu( e.material_tag(), bar );
+            auto eps = mpl.epsilon( e.gmsh_tag(), bar );
+            auto mu = mpl.mu( e.gmsh_tag(), bar );
 
             field_values mat(eps, eps, eps, mu, mu, mu);
 
diff --git a/src/maxwell/maxwell_solver.cpp b/src/maxwell/maxwell_solver.cpp
index 600713f..7c17ab6 100644
--- a/src/maxwell/maxwell_solver.cpp
+++ b/src/maxwell/maxwell_solver.cpp
@@ -157,7 +157,7 @@ reinit_materials(const model& mod, maxwell::solver_state& state, maxwell::parame
 {
     for (auto& e : mod)
     {
-        auto tag = e.material_tag();
+        auto tag = e.gmsh_tag();
         for (size_t iT = 0; iT < e.num_cells(); iT++)
         {
             auto& pe = e.cell(iT);
-- 
GitLab