PolyFEM
Loading...
Searching...
No Matches
MshReader.cpp
Go to the documentation of this file.
1#include "MshReader.hpp"
2
5
6#include <mshio/mshio.h>
7
8#include <fstream>
9#include <string>
10#include <iostream>
11#include <vector>
12
13#include <filesystem> // filesystem
14
15namespace polyfem::io
16{
17 namespace
18 {
19 int num_corner_nodes(const int type)
20 {
21 if (type == 1 || type == 8 || type == 26 || type == 27 || type == 28) // line
22 return 2;
23 if (type == 2 || type == 9 || type == 21 || type == 23 || type == 25) // triangle
24 return 3;
25 if (type == 3 || type == 10) // quad
26 return 4;
27 return -1;
28 }
29 } // namespace
30
31 template <typename Entity>
32 void map_entity_tag_to_physical_tag(const std::vector<Entity> &entities, std::unordered_map<int, int> &entity_tag_to_physical_tag)
33 {
34 for (int i = 0; i < entities.size(); i++)
35 {
36 entity_tag_to_physical_tag[entities[i].tag] =
37 entities[i].physical_group_tags.size() > 0
38 ? entities[i].physical_group_tags.front()
39 : 0;
40 }
41 }
42
43 bool MshReader::load(const std::string &path, Eigen::MatrixXd &vertices, Eigen::MatrixXi &cells, std::vector<std::vector<int>> &elements, std::vector<std::vector<double>> &weights, std::vector<int> &body_ids)
44 {
45 std::vector<std::string> node_data_name;
46 std::vector<std::vector<double>> node_data;
47 std::vector<std::vector<int>> boundary_elements;
48 std::vector<int> boundary_ids;
49
50 return load(path, vertices, cells, elements, weights, body_ids, boundary_elements, boundary_ids, node_data_name, node_data);
51 }
52
53 bool MshReader::load(const std::string &path, Eigen::MatrixXd &vertices, Eigen::MatrixXi &cells, std::vector<std::vector<int>> &elements, std::vector<std::vector<double>> &weights, std::vector<int> &body_ids, std::vector<std::vector<int>> &boundary_elements, std::vector<int> &boundary_ids)
54 {
55 std::vector<std::string> node_data_name;
56 std::vector<std::vector<double>> node_data;
57
58 return load(path, vertices, cells, elements, weights, body_ids, boundary_elements, boundary_ids, node_data_name, node_data);
59 }
60
61 bool MshReader::load(const std::string &path, Eigen::MatrixXd &vertices, Eigen::MatrixXi &cells, std::vector<std::vector<int>> &elements, std::vector<std::vector<double>> &weights, std::vector<int> &body_ids, std::vector<std::string> &node_data_name, std::vector<std::vector<double>> &node_data)
62 {
63 std::vector<std::vector<int>> boundary_elements;
64 std::vector<int> boundary_ids;
65
66 return load(path, vertices, cells, elements, weights, body_ids, boundary_elements, boundary_ids, node_data_name, node_data);
67 }
68
69 bool MshReader::load(const std::string &path, Eigen::MatrixXd &vertices, Eigen::MatrixXi &cells, std::vector<std::vector<int>> &elements, std::vector<std::vector<double>> &weights, std::vector<int> &body_ids, std::vector<std::vector<int>> &boundary_elements, std::vector<int> &boundary_ids, std::vector<std::string> &node_data_name, std::vector<std::vector<double>> &node_data)
70 {
71 if (!std::filesystem::exists(path))
72 {
73 logger().error("Msh file does not exist: {}", path);
74 return false;
75 }
76
77 mshio::MshSpec spec;
78 try
79 {
80 spec = mshio::load_msh(path);
81 }
82 catch (const std::exception &err)
83 {
84 logger().error("{}", err.what());
85 return false;
86 }
87 catch (...)
88 {
89 logger().error("Unknown error while reading MSH file: {}", path);
90 return false;
91 }
92
93 const auto &nodes = spec.nodes;
94 const auto &els = spec.elements;
95 const int n_vertices = nodes.num_nodes;
96 const int max_tag = nodes.max_node_tag;
97 int dim = -1;
98
99 assert(els.entity_blocks.size() > 0);
100 for (const auto &e : els.entity_blocks)
101 {
102 dim = std::max(dim, e.entity_dim);
103 }
104 assert(dim == 2 || dim == 3);
105
106 vertices.resize(n_vertices, dim);
107 std::vector<int> tag_to_index = std::vector<int>(max_tag + 1, -1);
108 if (n_vertices != max_tag)
109 logger().warn("MSH file contains more node tags than nodes, condensing nodes which will break input node ordering.");
110
111 int index = 0;
112 for (const auto &n : nodes.entity_blocks)
113 {
114 for (int i = 0; i < n.num_nodes_in_block * 3; i += 3)
115 {
116 const int node_id = n_vertices != max_tag ? (index++) : (n.tags[i / 3] - 1);
117
118 if (dim == 2)
119 vertices.row(node_id) << n.data[i], n.data[i + 1];
120 if (dim == 3)
121 vertices.row(node_id) << n.data[i], n.data[i + 1], n.data[i + 2];
122
123 assert(n.tags[i / 3] < tag_to_index.size());
124 tag_to_index[n.tags[i / 3]] = node_id;
125 }
126 }
127
128 int cells_cols = -1;
129 int num_els = 0;
130 for (const auto &e : els.entity_blocks)
131 {
132 if (e.entity_dim != dim)
133 continue;
134 const int type = e.element_type;
135 // https://shipengcheng1230.github.io/GmshTools.jl/stable/element_types/
136 if (type == 2 || type == 9 || type == 21 || type == 23 || type == 25) // tri
137 {
138 cells_cols = std::max(cells_cols, 3);
139 num_els += e.num_elements_in_block;
140 }
141 else if (type == 3 || type == 10) // quad
142 {
143 cells_cols = std::max(cells_cols, 4);
144 num_els += e.num_elements_in_block;
145 }
146 else if (type == 4 || type == 11 || type == 29 || type == 30 || type == 31) // tet
147 {
148 cells_cols = std::max(cells_cols, 4);
149 num_els += e.num_elements_in_block;
150 }
151 else if (type == 5 || type == 12) // hex
152 {
153 cells_cols = std::max(cells_cols, 8);
154 num_els += e.num_elements_in_block;
155 }
156 else if (type == 6) // prism
157 {
158 cells_cols = std::max(cells_cols, 6);
159 num_els += e.num_elements_in_block;
160 }
161 else if (type == 7) // pyramid
162 {
163 cells_cols = std::max(cells_cols, 5);
164 num_els += e.num_elements_in_block;
165 }
166 }
167 assert(cells_cols > 0);
168
169 std::unordered_map<int, int> entity_tag_to_physical_tag;
170 std::unordered_map<int, int> boundary_entity_tag_to_physical_tag;
171 if (dim == 2)
172 {
173 map_entity_tag_to_physical_tag(spec.entities.surfaces, entity_tag_to_physical_tag);
174 map_entity_tag_to_physical_tag(spec.entities.curves, boundary_entity_tag_to_physical_tag);
175 }
176 else
177 {
178 map_entity_tag_to_physical_tag(spec.entities.volumes, entity_tag_to_physical_tag);
179 map_entity_tag_to_physical_tag(spec.entities.surfaces, boundary_entity_tag_to_physical_tag);
180 }
181
182 boundary_elements.clear();
183 boundary_ids.clear();
184 for (const auto &e : els.entity_blocks)
185 {
186 if (e.entity_dim != dim - 1)
187 continue;
188
189 const auto physical_tag = boundary_entity_tag_to_physical_tag.find(e.entity_tag);
190 if (physical_tag == boundary_entity_tag_to_physical_tag.end() || physical_tag->second == 0)
191 continue;
192
193 const int n_corners = num_corner_nodes(e.element_type);
194 if (n_corners < 0)
195 {
196 logger().warn("Ignoring unsupported tagged codimension-one Gmsh element type {}.", e.element_type);
197 continue;
198 }
199
200 const size_t n_nodes = mshio::nodes_per_element(e.element_type);
201 for (int i = 0; i < e.data.size(); i += n_nodes + 1)
202 {
203 std::vector<int> corners(n_corners);
204 for (int j = 0; j < n_corners; ++j)
205 {
206 const int node_tag = e.data[i + j + 1];
207 assert(node_tag >= 0 && node_tag < tag_to_index.size());
208 corners[j] = tag_to_index[node_tag];
209 assert(corners[j] >= 0 && corners[j] < n_vertices);
210 }
211 boundary_elements.emplace_back(std::move(corners));
212 boundary_ids.push_back(physical_tag->second);
213 }
214 }
215
216 cells.resize(num_els, cells_cols);
217 cells.setConstant(-1);
218 body_ids.resize(num_els);
219 elements.resize(num_els);
220 weights.resize(num_els);
221 int cell_index = 0;
222 for (const auto &e : els.entity_blocks)
223 {
224 if (e.entity_dim != dim)
225 continue;
226 const int type = e.element_type;
227 if (type == 2 || type == 9 || type == 21 || type == 23 || type == 25 || type == 3 || type == 10 || type == 4 || type == 11 || type == 29 || type == 30 || type == 31 || type == 5 || type == 12 || type == 6 || type == 7)
228 {
229 const size_t n_nodes = mshio::nodes_per_element(type);
230 int local_cells_cols = -1;
231
232 if (type == 2 || type == 9 || type == 21 || type == 23 || type == 25) // tri
233 local_cells_cols = 3;
234 else if (type == 3 || type == 10) // quad
235 local_cells_cols = 4;
236 else if (type == 4 || type == 11 || type == 29 || type == 30 || type == 31) // tet
237 local_cells_cols = 4;
238 else if (type == 5 || type == 12) // hex
239 local_cells_cols = 8;
240 else if (type == 6) // prism
241 local_cells_cols = 6;
242 else if (type == 7) // pyramid
243 local_cells_cols = 5;
244
245 for (int i = 0; i < e.data.size(); i += (n_nodes + 1))
246 {
247 int index = 0;
248 for (int j = i + 1; j <= i + local_cells_cols; ++j)
249 {
250 const int v_index = tag_to_index[e.data[j]];
251 assert(v_index < n_vertices);
252 cells(cell_index, index++) = v_index;
253 }
254
255 for (int j = i + 1; j < i + 1 + n_nodes; ++j)
256 {
257 const int v_index = tag_to_index[e.data[j]];
258 assert(v_index < n_vertices);
259 elements[cell_index].push_back(v_index);
260 }
261
262 const auto &it = entity_tag_to_physical_tag.find(e.entity_tag);
263 body_ids[cell_index] =
264 it != entity_tag_to_physical_tag.end() ? it->second : 0;
265
266 ++cell_index;
267 }
268 }
269 }
270
271 node_data.resize(spec.node_data.size());
272 int i = 0;
273 for (const auto &data : spec.node_data)
274 {
275 for (const auto &str : data.header.string_tags)
276 node_data_name.push_back(str);
277
278 for (const auto &entry : data.entries)
279 for (const auto &d : entry.data)
280 node_data[i].push_back(d);
281
282 i++;
283 }
284
285 // std::ifstream infile(path.c_str());
286
287 // std::string line;
288
289 // int phase = -1;
290 // int line_number = -1;
291 // bool size_read = false;
292
293 // int n_triangles = 0;
294 // int n_tets = 0;
295
296 // std::vector<std::vector<double>> all_elements;
297
298 // while (std::getline(infile, line))
299 // {
300 // line = StringUtils::trim(line);
301 // ++line_number;
302
303 // if (line.empty())
304 // continue;
305
306 // if (line[0] == '$')
307 // {
308 // if (line.substr(1, 3) == "End")
309 // phase = -1;
310 // else
311 // {
312 // const auto header = line.substr(1);
313
314 // if (header.find("MeshFormat") == 0)
315 // phase = 0;
316 // else if (header.find("Nodes") == 0)
317 // phase = 1;
318 // else if (header.find("Elements") == 0)
319 // phase = 2;
320 // else
321 // {
322 // logger().debug("{}: [Warning] ignoring {}", line_number, header);
323 // phase = -1;
324 // }
325 // }
326
327 // size_read = false;
328
329 // continue;
330 // }
331
332 // if (phase == -1)
333 // continue;
334
335 // std::istringstream iss(line);
336 // //header
337 // if (phase == 0)
338 // {
339 // double version_number;
340 // int file_type;
341 // int data_size;
342
343 // iss >> version_number >> file_type >> data_size;
344
345 // assert(version_number == 2.2);
346 // assert(file_type == 0);
347 // assert(data_size == 8);
348 // }
349 // //coordiantes
350 // else if (phase == 1)
351 // {
352 // if (!size_read)
353 // {
354 // int n_vertices;
355 // iss >> n_vertices;
356 // vertices.resize(n_vertices, 3);
357 // size_read = true;
358 // }
359 // else
360 // {
361 // int node_number;
362 // double x_coord, y_coord, z_coord;
363
364 // iss >> node_number >> x_coord >> y_coord >> z_coord;
365 // //node_numbers starts with 1
366 // vertices.row(node_number - 1) << x_coord, y_coord, z_coord;
367 // }
368 // }
369 // //elements
370 // else if (phase == 2)
371 // {
372 // if (!size_read)
373 // {
374 // int number_of_elements;
375 // iss >> number_of_elements;
376 // all_elements.resize(number_of_elements);
377 // size_read = true;
378 // }
379 // else
380 // {
381 // int elm_number, elm_type, number_of_tags;
382
383 // iss >> elm_number >> elm_type >> number_of_tags;
384
385 // //9-node third order incomplete triangle
386 // assert(elm_type != 20);
387
388 // //12-node fourth order incomplete triangle
389 // assert(elm_type != 22);
390
391 // //15-node fifth order incomplete triangle
392 // assert(elm_type != 24);
393
394 // //21-node fifth order complete triangle
395 // assert(elm_type != 25);
396
397 // //56-node fifth order tetrahedron
398 // assert(elm_type != 31);
399
400 // //60 is the new rational element
401 // if (elm_type == 2 || elm_type == 9 || elm_type == 21 || elm_type == 23 || elm_type == 60)
402 // ++n_triangles;
403 // else if (elm_type == 4 || elm_type == 11 || elm_type == 29 || elm_type == 30)
404 // ++n_tets;
405
406 // //skipping tags
407 // for (int i = 0; i < number_of_tags; ++i)
408 // {
409 // int tmp;
410 // iss >> tmp;
411 // }
412
413 // auto &node_list = all_elements[elm_number - 1];
414 // node_list.push_back(elm_type);
415
416 // while (iss.good())
417 // {
418 // double tmp;
419 // iss >> tmp;
420 // node_list.push_back(tmp);
421 // }
422 // }
423 // }
424 // else
425 // {
426 // assert(false);
427 // }
428 // }
429
430 // int index = 0;
431 // if (n_tets == 0)
432 // {
433 // elements.resize(n_triangles);
434 // weights.resize(n_triangles);
435 // cells.resize(n_triangles, 3);
436
437 // for (const auto &els : all_elements)
438 // {
439 // const int elm_type = els[0];
440 // if (elm_type != 2 && elm_type != 9 && elm_type != 21 && elm_type != 23 && elm_type != 60)
441 // continue;
442
443 // auto &el = elements[index];
444 // auto &wh = weights[index];
445 // for (size_t i = 1; i < (elm_type == 60 ? 7 : els.size()); ++i)
446 // el.push_back(int(els[i]) - 1);
447 // if (elm_type == 60)
448 // {
449 // for (size_t i = 7; i < els.size(); ++i)
450 // wh.push_back(els[i]);
451
452 // assert(wh.size() == el.size());
453 // assert(wh.size() == 6);
454 // }
455
456 // cells.row(index) << el[0], el[1], el[2];
457
458 // ++index;
459 // }
460 // }
461 // else
462 // {
463 // elements.resize(n_tets);
464 // weights.resize(n_tets);
465 // cells.resize(n_tets, 4);
466
467 // for (const auto &els : all_elements)
468 // {
469 // const int elm_type = els[0];
470 // if (elm_type != 4 && elm_type != 11 && elm_type != 29 && elm_type != 30)
471 // continue;
472
473 // auto &el = elements[index];
474 // auto &wh = weights[index];
475 // for (size_t i = 1; i < els.size(); ++i)
476 // el.push_back(els[i] - 1);
477
478 // cells.row(index) << el[0], el[1], el[2], el[3];
479 // ++index;
480 // }
481 // }
482
483 return true;
484 }
485} // namespace polyfem::io
static bool load(const std::string &path, Eigen::MatrixXd &vertices, Eigen::MatrixXi &cells, std::vector< std::vector< int > > &elements, std::vector< std::vector< double > > &weights, std::vector< int > &body_ids)
Definition MshReader.cpp:43
void map_entity_tag_to_physical_tag(const std::vector< Entity > &entities, std::unordered_map< int, int > &entity_tag_to_physical_tag)
Definition MshReader.cpp:32
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44