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 std::size_t num_physical_names = 0;
68
69 bool in_nodes = false;
70 bool in_elements = false;
71 bool in_physical_names = false;
72
73 std::map<Index, Index> surface_to_num_elems;
74 std::map<Index, Index> physical_to_num_elems;
75 std::vector<std::string> physical_names;
76
77 uint8_t elm_map = 2;
78
79 while (std::getline(file, line))
80 {
81 if (line == "$Nodes")
82 {
83 in_nodes = true;
84 std::getline(file, line); // Read number of vertices
85 num_vertices = std::stoull(line);
86 continue;
87 }
88 if (line == "$EndNodes")
89 {
90 in_nodes = false;
91 continue;
92 }
93 if (line == "$Elements")
94 {
95 in_elements = true;
96 std::getline(file, line); // Skip number of elements line
97 continue;
98 }
99 if (line == "$EndElements")
100 {
101 in_elements = false;
102 continue;
103 }
104 if (line == "$PhysicalNames")
105 {
106 in_physical_names = true;
107 std::getline(file, line); // Read number of physical names
108 num_physical_names = std::stoull(line);
109 continue;
110 }
111 if (line == "$EndPhysicalNames")
112 {
113 in_physical_names = false;
114 continue;
115 }
116
117 if (in_physical_names)
118 {
119 std::istringstream iss(line);
120 Int n1, n2;
121 std::string name;
122 iss >> n1 >> n2 >> name;
123 name.erase(std::remove(name.begin(), name.end(), '\"' ), name.end());
124 physical_names.push_back(name);
125 continue;
126 }
127
128 if (in_elements)
129 {
130 std::istringstream iss(line);
131 std::size_t elm_id, elm_type, num_tags, phys_tag, surf_tag;
132 iss >> elm_id >> elm_type >> num_tags >> phys_tag >> surf_tag;
133 phys_tag -= 1;
134 surf_tag -= 1;
135
136 // Only count faces of the given type
137 if (elm_type == elm_map)
138 {
139 num_faces++;
140
141 bool inserted_surface = surface_to_num_elems.insert(std::make_pair(surf_tag, 1)).second;
142 if (!inserted_surface)
143 surface_to_num_elems[surf_tag]++;
144
145 bool inserted_physical = physical_to_num_elems.insert(std::make_pair(phys_tag, 1)).second;
146 if (!inserted_physical)
147 physical_to_num_elems[phys_tag]++;
148 }
149 }
150 }
151
152 EigMatNX<Float, 3> verts = EigMatNX<Float, 3>::Zero(3, num_vertices);
153 EigMatNX<Index, 3> elems = EigMatNX<Index, 3>::Zero(3, num_faces);
154 EigRowVec<Index> elem_tags = EigRowVec<Index>::Zero(1, num_faces);
155
156 std::map<Index, EigRowVec<Index>> surface_elems;
157 std::map<Index, Index> surface_elem_counters;
158 for (auto& [key, value]: surface_to_num_elems)
159 {
160 surface_elems[key] = EigRowVec<Index>::Zero(1, value);
161 surface_elem_counters[key] = 0;
162 }
163
164 std::map<Index, EigRowVec<Index>> physical_elems;
165 std::map<Index, Index> physical_elem_counters;
166 for (auto& [key, value]: physical_to_num_elems)
167 {
168 physical_elems[key] = EigRowVec<Index>::Zero(1, value);
169 physical_elem_counters[key] = 0;
170 }
171
172 // Second pass: read actual data
173 file.clear();
174 file.seekg(0);
175
176 // Skip to $Nodes section
177 while (std::getline(file, line) && line != "$Nodes") {}
178 std::getline(file, line); // Skip number of vertices line
179
180 // Read vertex coordinates
181 std::size_t vertex_idx = 0;
182 while (std::getline(file, line) && line != "$EndNodes")
183 {
184 std::istringstream iss(line);
185 std::size_t vertex_id;
186 Float x, y, z;
187 iss >> vertex_id >> x >> y >> z;
188 verts.col(vertex_idx++) << x, y, z;
189 }
190
191 // Skip to $Elements section
192 while (std::getline(file, line) && line != "$Elements") {}
193 std::getline(file, line); // Skip number of elements line
194
195 // Read faces
196 std::size_t face_idx = 0;
197 while (std::getline(file, line) && line != "$EndElements")
198 {
199 std::istringstream iss(line);
200 std::size_t elm_id, elm_type, num_tags;
201 iss >> elm_id >> elm_type >> num_tags;
202
203 if (num_tags != 2)
204 throw std::runtime_error(
205 "Unexpected number of tags in file " + msh_filename + " for element " + std::to_string(elm_id)
206 );
207
208 std::vector<std::size_t> tags (num_tags);
209 for (std::size_t ii = 0; ii < num_tags; ++ii)
210 iss >> tags[ii];
211
212 std::size_t ptag = tags[0] - 1;
213 std::size_t stag = tags[1] - 1;
214
215 // Only process faces of the given type
216 if (elm_type == elm_map)
217 {
218 physical_elems[ptag][physical_elem_counters[ptag]++] = face_idx;
219 surface_elems[stag][surface_elem_counters[stag]++] = face_idx;
220
221 for (std::size_t ii = 0; ii < 3; ++ii)
222 {
223 std::size_t v;
224 iss >> v;
225 // Convert from 1-based to 0-based indexing
226 elems(ii, face_idx) = v - 1;
227 }
228 // Convert from 1-based to 0-based indexing
229 elem_tags[face_idx] = ptag;
230 face_idx++;
231 }
232 }
233
234 structure.mesh().set_data(verts, elems, elem_tags, decoupled_edges);
235
236 for (const auto& [key, value]: surface_elems)
237 {
238 MeshView view (structure.mesh(), value, "surface_id_" + std::to_string(key));
239 Component metacomp (view, PerfectDielectricMaterial(1, 1), "surface_id_" + std::to_string(key));
240 structure.add_metacomponent(metacomp);
241 }
242 for (const auto& [key, value]: physical_elems)
243 {
244 std::string name = physical_names[key] + "_physical_id_" + std::to_string(key);
245 MeshView view (structure.mesh(), value, name);
246 Component comp (view, PerfectDielectricMaterial(1, 1), name);
247 structure.add_component(comp);
248 }
249
250 return;
251
252};
253
254
256 const Structure<TriangleMesh<3>>& structure,
257 const std::string msh_filename,
258 const std::string extension
259 )
260{
261
262 std::ofstream file(msh_filename + "." + extension);
263 if (!file.is_open())
264 throw std::runtime_error("Could not open mesh file: " + msh_filename + "." + extension);
265
266 // Write header
267 file << "$MeshFormat\n";
268 file << "2.2 0 8\n"; // Gmsh version 2.2, ASCII format, data size 8 bytes
269 file << "$EndMeshFormat\n";
270
271 // Write physical names
272 file << "$PhysicalNames\n";
273 file << structure.components().size() << "\n";
274 for (Index ii = 0; ii < structure.components().size(); ++ii)
275 file << "2 " << ii + 1 << " \"" << structure.components()[ii].name() << "\"\n";
276 file << "$EndPhysicalNames\n";
277
278 // Nodes
279 file << "$Nodes\n";
280 file << structure.mesh().num_verts() << "\n";
281 for (Index ii = 0; ii < structure.mesh().num_verts(); ++ii)
282 {
283 const EigColVecN<Float, 3>& v = structure.mesh().verts(ii);
284 file << (ii + 1) << " " << v[0] << " " << v[1] << " " << v[2] << "\n";
285 }
286 file << "$EndNodes\n";
287
288 // Elements
289 Index num_elems = 0;
290 for (Index jj = 0; jj < structure.components().size(); ++jj)
291 num_elems += structure.components()[jj].mesh_view().elem_inds().size();
292
293 file << "$Elements\n";
294 file << num_elems << "\n";
295 Index elem_idx = 1;
296 for (Index jj = 0; jj < structure.components().size(); ++jj)
297 {
298 for (Index ii = 0; ii < structure.components()[jj].mesh_view().elem_inds().size(); ++ii)
299 {
300 const EigColVecN<Index, 3>& fv = structure.mesh().elems(
301 structure.components()[jj].mesh_view().elem_inds()[ii]
302 );
303 file << elem_idx++ << " 2 2 ";
304 file << jj + 1 << " " << jj + 1 << " ";
305 file << (fv[0] + 1) << " " << (fv[1] + 1) << " " << (fv[2] + 1) << "\n";
306 }
307 }
308 file << "$EndElements\n";
309
310 return;
311
312};
313
314
316 const Structure<TriangleMesh<3>>& structure,
317 const std::string msh_filename,
319 std::string field_name
320 )
321{
322
323 MeshTransfer::write_gmsh_v2(structure, msh_filename, "pos");
324
325 if (field.size() != structure.mesh().num_elems())
326 throw std::invalid_argument("Field size must match number of faces in the mesh.");
327
328 std::ofstream file(msh_filename + ".pos", std::ios_base::app);
329 if (!file.is_open())
330 throw std::runtime_error("Could not open mesh file: " + msh_filename + ".pos");
331
332 file << "$ElementData\n";
333 file << "1\n" << field_name << "\n";
334 file << "1\n" << 0 << "\n";
335 file << "4\n" << 0 << "\n" << 1 << "\n" << structure.mesh().num_elems() << "\n" << "0\n";
336 for (Index ii = 0; ii < structure.mesh().num_elems(); ++ii)
337 file << (ii + 1) << " " << field(ii) << "\n";
338 file << "$EndElementData\n";
339
340 return;
341
342};
343
344
346 const Structure<TriangleMesh<3>>& structure,
347 const std::string msh_filename,
349 std::string field_name
350 )
351{
352
353 MeshTransfer::write_gmsh_v2_scalar_field(structure, msh_filename, field.colwise().norm());
354
355 if (field.cols() != structure.mesh().num_elems())
356 throw std::invalid_argument("Field size must match number of faces in the mesh.");
357
358 std::ofstream file(msh_filename + ".pos", std::ios_base::app);
359 if (!file.is_open())
360 throw std::runtime_error("Could not open mesh file: " + msh_filename + ".pos");
361
362 file << "$ElementData\n";
363 file << "1\n" << field_name << "\n";
364 file << "1\n" << 0 << "\n";
365 file << "4\n" << 0 << "\n" << 3 << "\n" << structure.mesh().num_elems() << "\n" << "0\n";
366 for (Index ii = 0; ii < structure.mesh().num_elems(); ++ii)
367 file << (ii + 1) << " " << field(0, ii) << " " << field(1, ii) << " " << field(2, ii) << "\n";
368 file << "$EndElementData\n";
369
370 return;
371
372};
373
374}
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:185
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