OpenBEM
Open-source framework for electromagnetic simulation with the boundary element method.
Loading...
Searching...
No Matches
mesh_transfer.cpp
Go to the documentation of this file.
1// OpenBEM - Copyright (C) 2026 Shashwat Sharma
2
3// This file is part of OpenBEM.
4
5// OpenBEM is free software: you can redistribute it and/or modify it under the terms of the
6// GNU General Public License as published by the Free Software Foundation, either version 3
7// of the License, or (at your option) any later version.
8
9// You should have received a copy of the GNU General Public License along with OpenBEM.
10// If not, see <https://www.gnu.org/licenses/>.
11
12
19
20#include <fstream>
21#include <sstream>
22#include <stdexcept>
23#include <vector>
24#include <algorithm>
25#include <string>
26#include <map>
27
28#include "types.hpp"
31
32
33namespace bem
34{
35
37 Structure<TriangleMesh<3>>& structure,
38 const std::string msh_filename,
39 const bool decoupled_edges
40 )
41{
42
43 std::ifstream file(msh_filename);
44 if (!file.is_open())
45 throw std::runtime_error("Could not open mesh file: " + msh_filename);
46
47 // Check version and format
48 std::string line;
49 std::getline(file, line);
50 if (line != "$MeshFormat")
51 throw std::runtime_error("Invalid Gmsh file format: missing $MeshFormat header");
52
53 std::getline(file, line); // version file-type data-size
54 std::istringstream iss (line);
55 float version;
56 int file_type, data_size;
57 iss >> version >> file_type >> data_size;
58
59 if (version < 2.0 || version >= 3.0)
60 throw std::runtime_error(".msh file version must be 2.x");
61
62 std::getline(file, line); // $EndMeshFormat
63
64 // First pass: count vertices, elements, surfaces, and physical names
65 std::size_t num_vertices = 0;
66 std::size_t num_faces = 0;
67
68 bool in_elements = false;
69 bool in_physical_names = false;
70
71 std::map<Index, Index> surface_to_num_elems;
72 std::map<Index, Index> physical_to_num_elems;
73 std::vector<std::string> physical_names;
74
75 uint8_t elm_map = 2;
76
77 while (std::getline(file, line))
78 {
79 if (line == "$Nodes")
80 {
81 std::getline(file, line); // Read number of vertices
82 num_vertices = std::stoull(line);
83 continue;
84 }
85 if (line == "$EndNodes")
86 {
87 continue;
88 }
89 if (line == "$Elements")
90 {
91 in_elements = true;
92 std::getline(file, line); // Skip number of elements line
93 continue;
94 }
95 if (line == "$EndElements")
96 {
97 in_elements = false;
98 continue;
99 }
100 if (line == "$PhysicalNames")
101 {
102 in_physical_names = true;
103 std::getline(file, line); // Read number of physical names
104 continue;
105 }
106 if (line == "$EndPhysicalNames")
107 {
108 in_physical_names = false;
109 continue;
110 }
111
112 if (in_physical_names)
113 {
114 std::istringstream iss(line);
115 Int n1, n2;
116 std::string name;
117 iss >> n1 >> n2 >> name;
118 name.erase(std::remove(name.begin(), name.end(), '\"' ), name.end());
119 physical_names.push_back(name);
120 continue;
121 }
122
123 if (in_elements)
124 {
125 std::istringstream iss(line);
126 std::size_t elm_id, elm_type, num_tags, phys_tag, surf_tag;
127 iss >> elm_id >> elm_type >> num_tags >> phys_tag >> surf_tag;
128 phys_tag -= 1;
129 surf_tag -= 1;
130
131 // Only count faces of the given type
132 if (elm_type == elm_map)
133 {
134 num_faces++;
135
136 bool inserted_surface = surface_to_num_elems.insert(std::make_pair(surf_tag, 1)).second;
137 if (!inserted_surface)
138 surface_to_num_elems[surf_tag]++;
139
140 bool inserted_physical = physical_to_num_elems.insert(std::make_pair(phys_tag, 1)).second;
141 if (!inserted_physical)
142 physical_to_num_elems[phys_tag]++;
143 }
144 }
145 }
146
147 EigMatNX<Float, 3> verts = EigMatNX<Float, 3>::Zero(3, num_vertices);
148 EigMatNX<Index, 3> elems = EigMatNX<Index, 3>::Zero(3, num_faces);
149 EigRowVec<Index> elem_tags = EigRowVec<Index>::Zero(1, num_faces);
150
151 std::map<Index, EigRowVec<Index>> surface_elems;
152 std::map<Index, Index> surface_elem_counters;
153 for (auto& [key, value]: surface_to_num_elems)
154 {
155 surface_elems[key] = EigRowVec<Index>::Zero(1, value);
156 surface_elem_counters[key] = 0;
157 }
158
159 std::map<Index, EigRowVec<Index>> physical_elems;
160 std::map<Index, Index> physical_elem_counters;
161 for (auto& [key, value]: physical_to_num_elems)
162 {
163 physical_elems[key] = EigRowVec<Index>::Zero(1, value);
164 physical_elem_counters[key] = 0;
165 }
166
167 // Second pass: read actual data
168 file.clear();
169 file.seekg(0);
170
171 // Skip to $Nodes section
172 while (std::getline(file, line) && line != "$Nodes") {}
173 std::getline(file, line); // Skip number of vertices line
174
175 // Read vertex coordinates
176 std::size_t vertex_idx = 0;
177 while (std::getline(file, line) && line != "$EndNodes")
178 {
179 std::istringstream iss(line);
180 std::size_t vertex_id;
181 Float x, y, z;
182 iss >> vertex_id >> x >> y >> z;
183 verts.col(vertex_idx++) << x, y, z;
184 }
185
186 // Skip to $Elements section
187 while (std::getline(file, line) && line != "$Elements") {}
188 std::getline(file, line); // Skip number of elements line
189
190 // Read faces
191 std::size_t face_idx = 0;
192 while (std::getline(file, line) && line != "$EndElements")
193 {
194 std::istringstream iss(line);
195 std::size_t elm_id, elm_type, num_tags;
196 iss >> elm_id >> elm_type >> num_tags;
197
198 if (num_tags != 2)
199 throw std::runtime_error(
200 "Unexpected number of tags in file " + msh_filename + " for element " + std::to_string(elm_id)
201 );
202
203 std::vector<std::size_t> tags (num_tags);
204 for (std::size_t ii = 0; ii < num_tags; ++ii)
205 iss >> tags[ii];
206
207 std::size_t ptag = tags[0] - 1;
208 std::size_t stag = tags[1] - 1;
209
210 // Only process faces of the given type
211 if (elm_type == elm_map)
212 {
213 physical_elems[ptag][physical_elem_counters[ptag]++] = face_idx;
214 surface_elems[stag][surface_elem_counters[stag]++] = face_idx;
215
216 for (std::size_t ii = 0; ii < 3; ++ii)
217 {
218 std::size_t v;
219 iss >> v;
220 // Convert from 1-based to 0-based indexing
221 elems(ii, face_idx) = v - 1;
222 }
223 // Convert from 1-based to 0-based indexing
224 elem_tags[face_idx] = ptag;
225 face_idx++;
226 }
227 }
228
229 structure.mesh().set_data(verts, elems, elem_tags, decoupled_edges);
230
231 for (const auto& [key, value]: surface_elems)
232 {
233 MeshView view (structure.mesh(), value, "surface_id_" + std::to_string(key));
234 Component metacomp (view, PerfectDielectricMaterial(1, 1), "surface_id_" + std::to_string(key));
235 structure.add_metacomponent(metacomp);
236 }
237 for (const auto& [key, value]: physical_elems)
238 {
239 std::string name = physical_names[key] + "_physical_id_" + std::to_string(key);
240 MeshView view (structure.mesh(), value, name);
241 Component comp (view, PerfectDielectricMaterial(1, 1), name);
242 structure.add_component(comp);
243 }
244
245 return;
246
247};
248
249
251 const Structure<TriangleMesh<3>>& structure,
252 const std::string msh_filename,
253 const std::string extension
254 )
255{
256
257 std::ofstream file(msh_filename + "." + extension);
258 if (!file.is_open())
259 throw std::runtime_error("Could not open mesh file: " + msh_filename + "." + extension);
260
261 // Write header
262 file << "$MeshFormat\n";
263 file << "2.2 0 8\n"; // Gmsh version 2.2, ASCII format, data size 8 bytes
264 file << "$EndMeshFormat\n";
265
266 // Write physical names
267 file << "$PhysicalNames\n";
268 file << structure.components().size() << "\n";
269 for (Index ii = 0; ii < structure.components().size(); ++ii)
270 file << "2 " << ii + 1 << " \"" << structure.components()[ii].name() << "\"\n";
271 file << "$EndPhysicalNames\n";
272
273 // Nodes
274 file << "$Nodes\n";
275 file << structure.mesh().num_verts() << "\n";
276 for (Index ii = 0; ii < structure.mesh().num_verts(); ++ii)
277 {
278 const EigColVecN<Float, 3>& v = structure.mesh().verts(ii);
279 file << (ii + 1) << " " << v[0] << " " << v[1] << " " << v[2] << "\n";
280 }
281 file << "$EndNodes\n";
282
283 // Elements
284 Index num_elems = 0;
285 for (Index jj = 0; jj < structure.components().size(); ++jj)
286 num_elems += structure.components()[jj].mesh_view().elem_inds().size();
287
288 file << "$Elements\n";
289 file << num_elems << "\n";
290 Index elem_idx = 1;
291 for (Index jj = 0; jj < structure.components().size(); ++jj)
292 {
293 for (Index ii = 0; ii < structure.components()[jj].mesh_view().elem_inds().size(); ++ii)
294 {
295 const EigColVecN<Index, 3>& fv = structure.mesh().elems(
296 structure.components()[jj].mesh_view().elem_inds()[ii]
297 );
298 file << elem_idx++ << " 2 2 ";
299 file << jj + 1 << " " << jj + 1 << " ";
300 file << (fv[0] + 1) << " " << (fv[1] + 1) << " " << (fv[2] + 1) << "\n";
301 }
302 }
303 file << "$EndElements\n";
304
305 return;
306
307};
308
309
311 const Structure<TriangleMesh<3>>& structure,
312 const std::string msh_filename,
314 std::string field_name
315 )
316{
317
318 MeshTransfer::write_gmsh_v2(structure, msh_filename, "pos");
319
320 if (field.size() != structure.mesh().num_elems())
321 throw std::invalid_argument("Field size must match number of faces in the mesh.");
322
323 std::ofstream file(msh_filename + ".pos", std::ios_base::app);
324 if (!file.is_open())
325 throw std::runtime_error("Could not open mesh file: " + msh_filename + ".pos");
326
327 file << "$ElementData\n";
328 file << "1\n" << field_name << "\n";
329 file << "1\n" << 0 << "\n";
330 file << "4\n" << 0 << "\n" << 1 << "\n" << structure.mesh().num_elems() << "\n" << "0\n";
331 for (Index ii = 0; ii < structure.mesh().num_elems(); ++ii)
332 file << (ii + 1) << " " << field(ii) << "\n";
333 file << "$EndElementData\n";
334
335 return;
336
337};
338
339
341 const Structure<TriangleMesh<3>>& structure,
342 const std::string msh_filename,
344 std::string field_name
345 )
346{
347
348 MeshTransfer::write_gmsh_v2_scalar_field(structure, msh_filename, field.colwise().norm());
349
350 if (field.cols() != structure.mesh().num_elems())
351 throw std::invalid_argument("Field size must match number of faces in the mesh.");
352
353 std::ofstream file(msh_filename + ".pos", std::ios_base::app);
354 if (!file.is_open())
355 throw std::runtime_error("Could not open mesh file: " + msh_filename + ".pos");
356
357 file << "$ElementData\n";
358 file << "1\n" << field_name << "\n";
359 file << "1\n" << 0 << "\n";
360 file << "4\n" << 0 << "\n" << 3 << "\n" << structure.mesh().num_elems() << "\n" << "0\n";
361 for (Index ii = 0; ii < structure.mesh().num_elems(); ++ii)
362 file << (ii + 1) << " " << field(0, ii) << " " << field(1, ii) << " " << field(2, ii) << "\n";
363 file << "$EndElementData\n";
364
365 return;
366
367};
368
369}
Class that defines a component in a structure.
Definition component.hpp:43
static void read_gmsh_v2(Structure< TriangleMesh< 3 > > &structure, const std::string msh_filename, const bool decoupled_edges=false)
Reads a GMSH v2 mesh file and populates a TriangleMesh in a Structure.
static void write_gmsh_v2_scalar_field(const Structure< TriangleMesh< 3 > > &structure, const std::string msh_filename, ConstEigRef< EigRowVec< Float > > field, std::string field_name="scalar_field")
Writes a GMSH v2 mesh file from a TriangleMesh in a Structure with a superimposed scalar field.
static void write_gmsh_v2_vector_field(const Structure< TriangleMesh< 3 > > &structure, const std::string msh_filename, ConstEigRef< EigMatNX< Float, 3 > > field, std::string field_name="vector_field")
Writes a GMSH v2 mesh file from a TriangleMesh in a Structure with a superimposed vector field.
static void write_gmsh_v2(const Structure< TriangleMesh< 3 > > &structure, const std::string msh_filename, const std::string extension="msh")
Writes a GMSH v2 mesh file from a TriangleMesh in a Structure.
Class that provides a lightweight view into a MeshBase object.
Definition base.hpp:191
Class defining a perfect lossless dielectric material.
Class that defines a structure.
Definition structure.hpp:45
Class defining a mesh with triangle elements.
const Eigen::Ref< const EigObj > ConstEigRef
Read-only reference to an Eigen object.
Definition types.hpp:98
Eigen::Matrix< T, 1, Eigen::Dynamic > EigRowVec
Dynamic-size row vector containing type T.
Definition types.hpp:90
double Float
Floating point number.
Definition types.hpp:47
Eigen::Matrix< T, N, 1 > EigColVecN
Fixed-size column vector of size N containing type T.
Definition types.hpp:86
int Int
Signed integer type.
Definition types.hpp:57
Eigen::Matrix< T, N, Eigen::Dynamic > EigMatNX
Fixed-height matrix with N rows containing type T.
Definition types.hpp:78
std::size_t Index
Unsigned integer type for indices and container sizes.
Definition types.hpp:54
Primary namespace for the OpenBEM library.
Definition constants.hpp:31