openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
test_emi_dof_numbering.cc
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) NumeriCor GmbH
2 // SPDX-License-Identifier: Apache-2.0
3 
15 #include "SF_base.h"
16 #include "basics.h"
17 
18 #include <mpi.h>
19 
20 #include <iostream>
21 #include <map>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 namespace {
27 
29 using vertex_tag = std::pair<mesh_int_t, mesh_int_t>;
30 
31 constexpr mesh_int_t MYOCYTE = 1;
32 constexpr mesh_int_t EXTRA = 2;
33 
34 int failures = 0;
35 
36 void check(bool ok, const std::string& what)
37 {
38  if (!ok) {
39  std::cerr << "FAILED: " << what << '\n';
40  failures++;
41  }
42 }
43 
44 void build_mesh(int rank, int size, MPI_Comm comm, mesh_t& mesh)
45 {
46  const std::vector<mesh_int_t> myocyte{0, 1, 2, 3}, extra{1, 2, 3, 5};
47  std::vector<std::pair<mesh_int_t, const std::vector<mesh_int_t>*>> elems;
48  if (rank == 0) elems.push_back({MYOCYTE, &myocyte});
49  if (rank == size - 1) elems.push_back({EXTRA, &extra});
50 
51  mesh.comm = comm;
53  for (const auto& [tag, nodes] : elems) {
54  for (mesh_int_t n : *nodes) mesh.con.push_back(n);
55  mesh.tag.push_back(tag);
56  mesh.type.push_back(SF::Tetra);
57  cnt.push_back(4);
58  }
59  mesh.l_numelem = elems.size();
60  mesh.g_numelem = 2;
61  SF::dsp_from_cnt(cnt, mesh.dsp);
62  SF::vector<mesh_int_t>& eref = mesh.register_numbering(SF::NBR_ELEM_REF);
63  eref.resize(mesh.l_numelem);
64  for (size_t i = 0; i < eref.size(); i++) eref[i] = (rank == 0 ? 0 : 1) + i;
65  mesh.localize(SF::NBR_REF);
66  // as emi.cc before decoupling: g_numpts counts the used points
67  mesh.generate_par_layout();
68 }
69 
70 } // namespace
71 
72 int main(int argc, char** argv)
73 {
74  MPI_Init(&argc, &argv);
75  MPI_Comm comm = MPI_COMM_WORLD;
76  int size, rank;
77  MPI_Comm_size(comm, &size);
78  MPI_Comm_rank(comm, &rank);
79 
80  mesh_t mesh;
81  build_mesh(rank, size, comm, mesh);
82  check(mesh.g_numpts == 5, "g_numpts counts the 5 used points");
83 
84  // 1 on the membrane, 0 inside a region
85  hashmap::unordered_map<mesh_int_t, mesh_int_t> ptsdata{{0, 0}, {1, 1}, {2, 1}, {3, 1}, {5, 0}};
86  hashmap::unordered_set<int> extra_tags{EXTRA};
88  SF::compute_map_vertex_to_dof(mesh, SF::NBR_REF, ptsdata, extra_tags, dofs);
89 
90  // gather (vertex, tag, dof) on rank 0
91  std::vector<mesh_int_t> local;
92  for (const auto& [key, dof] : dofs) local.insert(local.end(), {key.first, key.second, dof});
93  int n = local.size();
94  std::vector<int> counts(size), displs(size);
95  MPI_Gather(&n, 1, MPI_INT, counts.data(), 1, MPI_INT, 0, comm);
96  for (int r = 1; r < size; r++) displs[r] = displs[r - 1] + counts[r - 1];
97  std::vector<mesh_int_t> all(rank == 0 ? displs[size - 1] + counts[size - 1] : 0);
98  MPI_Datatype type = sizeof(mesh_int_t) == 8 ? MPI_LONG_LONG : MPI_INT;
99  MPI_Gatherv(local.data(), n, type, all.data(), counts.data(), displs.data(), type, 0, comm);
100 
101  if (rank == 0) {
102  std::map<vertex_tag, mesh_int_t> dof_of;
103  for (size_t i = 0; i < all.size(); i += 3) {
104  const vertex_tag key{all[i], all[i + 1]};
105  auto [it, inserted] = dof_of.insert({key, all[i + 2]});
106  check(inserted || it->second == all[i + 2], "one DOF per (vertex, tag)");
107  }
108  for (mesh_int_t v : {1, 2, 3}) {
109  check(dof_of.count({v, MYOCYTE}) && dof_of.count({v, EXTRA}),
110  "membrane vertex " + std::to_string(v) + " has a myocyte and an extracellular DOF");
111  check(dof_of[{v, MYOCYTE}] != dof_of[{v, EXTRA}],
112  "membrane vertex " + std::to_string(v) + " is decoupled");
113  }
114  std::map<mesh_int_t, mesh_int_t> vertex_of;
115  for (const auto& [key, dof] : dof_of) {
116  auto [it, inserted] = vertex_of.insert({dof, key.first});
117  check(inserted || it->second == key.first,
118  "DOF " + std::to_string(dof) + " belongs to vertices " + std::to_string(it->second) +
119  " and " + std::to_string(key.first));
120  }
121  }
122 
123  int total = 0;
124  MPI_Allreduce(&failures, &total, 1, MPI_INT, MPI_SUM, comm);
125  MPI_Finalize();
126  return total ? 1 : 0;
127 }
The base header file.
opencarp::local_index_t mesh_int_t
Definition: SF_container.h:31
Basic utility structs and functions, mostly IO related.
MPI_Comm comm
the parallel mesh is defined on a MPI world
Definition: SF_container.h:389
size_t size() const
The current size of the vector.
Definition: SF_vector.h:89
void resize(size_t n)
Resize a vector.
Definition: SF_vector.h:194
T & push_back(T val)
Definition: SF_vector.h:268
void dsp_from_cnt(const vector< T > &cnt, vector< T > &dsp)
Compute displacements from counts.
Definition: SF_vector.h:295
@ Tetra
Definition: SF_container.h:39
@ NBR_ELEM_REF
The element numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:189
@ NBR_REF
The nodal numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:186
mesh_t
The enum identifying the different meshes we might want to load.
Definition: sf_interface.h:44
int main(int argc, char **argv)