PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
mshReader.cpp
Go to the documentation of this file.
1/*
2 * -------------------------------------------
3 * Copyright (c) 2021 - 2026 Prashant K. Jha
4 * -------------------------------------------
5 * PeriDEM https://github.com/prashjha/PeriDEM
6 *
7 * Distributed under the Boost Software License, Version 1.0. (See accompanying
8 * file LICENSE)
9 */
10
11#include "mshReader.h"
12#include <stdexcept>
13#include "util/io.h"
14
15#include <fstream>
16#include <iostream>
17#include <sstream>
18
19#include "util/feElementDefs.h"
20
21namespace {
22
23void trim_inplace(std::string &s) {
24 while (!s.empty() && (s.back() == '\r' || s.back() == ' ' || s.back() == '\t'))
25 s.pop_back();
26 size_t i = 0;
27 while (i < s.size() && (s[i] == ' ' || s[i] == '\t'))
28 ++i;
29 if (i > 0)
30 s.erase(0, i);
31}
32
33} // namespace
34
35rw::reader::MshReader::MshReader(const std::string &filename)
36 : d_filename(filename){};
37
39 std::vector<util::Point> *nodes,
40 size_t &element_type, size_t &num_elems,
41 std::vector<size_t> *enc,
42 std::vector<std::vector<size_t>> *nec,
43 std::vector<double> *volumes, bool is_fd) {
44
45 (void)is_fd;
46
47 if (util::io::isFileEmpty(d_filename)) {
48 throw std::runtime_error(
50 << "Error: Filename = " << d_filename <<
51 " in MshReader is either nonexistent or empty.\n");
52 }
53
54 // open file
55 if (d_file) d_file.close();
56
57 d_file.open(d_filename);
58
59 if (!d_file) {
60 throw std::runtime_error(
62 << "Error: Can not open file = " << d_filename + ".msh"
63 << ".\n");
64 }
65
66 int format = 0;
67 int size = 0;
68 double version = 1.0;
69
70 // clear data
71 nodes->clear();
72 enc->clear();
73 nec->clear();
74 volumes->clear();
75
76 // specify type of element to read
77 if (dim != 2 and dim != 3) {
78 throw std::runtime_error(
80 << "Error: MshReader currently only supports reading of "
81 "triangle/quadrangle elements in dimension 2 and tetragonal "
82 "elements in 3.\n");
83 }
84
85 bool read_nodes = false;
86 bool read_elements = false;
87
88 std::string line;
89 while (std::getline(d_file, line)) {
90 trim_inplace(line);
91 if (line.empty())
92 continue;
93
94 if (line == "$MeshFormat") {
95 if (!std::getline(d_file, line)) {
96 throw std::runtime_error(
98 << "Error: Unexpected end of file in MshReader (after $MeshFormat).\n");
99 }
100 trim_inplace(line);
101 {
102 std::istringstream iss(line);
103 iss >> version >> format >> size;
104 }
105 if ((version != 2.0) && (version != 2.1) && (version != 2.2)) {
106 throw std::runtime_error(
108 << "Error: Unknown .msh file version " << version << "\n");
109 }
110 if (format) {
111 throw std::runtime_error(
113 << "Error: Format of .msh is possibly binary which is not"
114 " supported currently.\n ");
115 }
116 } else if (line == "$Nodes" || line == "$NOD" || line == "$NOE") {
117 read_nodes = true;
118 if (!std::getline(d_file, line)) {
119 throw std::runtime_error(
121 << "Error: Unexpected end of file in MshReader (node count).\n");
122 }
123 trim_inplace(line);
124 unsigned int num_nodes = 0;
125 {
126 std::istringstream ns(line);
127 ns >> num_nodes;
128 }
129 nodes->resize(num_nodes);
130 nec->resize(num_nodes);
131
132 for (unsigned int i = 0; i < num_nodes; ++i) {
133 if (!std::getline(d_file, line)) {
134 throw std::runtime_error(
136 << "Error: Unexpected end of file in MshReader (reading nodes).\n");
137 }
138 trim_inplace(line);
139 std::istringstream ls(line);
140 unsigned int id;
141 double x, y, z;
142 ls >> id >> x >> y >> z;
143 if (id < 1 || id > num_nodes) {
144 throw std::runtime_error(
146 << "Error: MshReader: node id out of range in file " << d_filename << "\n");
147 }
148 // 2D: planar meshes use xy; some .msh exports leave z as denormal/garbage — ignore file z.
149 if (dim == 2)
150 (*nodes)[id - 1] = util::Point(x, y, 0.);
151 else
152 (*nodes)[id - 1] = util::Point(x, y, z);
153 }
154 } else if (line == "$Elements" || line == "$ELM") {
155 read_elements = true;
156 if (!std::getline(d_file, line)) {
157 throw std::runtime_error(
159 << "Error: Unexpected end of file in MshReader (element count).\n");
160 }
161 trim_inplace(line);
162 unsigned int num_elem = 0;
163 {
164 std::istringstream es(line);
165 es >> num_elem;
166 }
167
168 size_t elem_counter = 0;
169 bool found_tri = false;
170 bool found_quad = false;
171
172 for (unsigned int iel = 0; iel < num_elem; ++iel) {
173 if (!std::getline(d_file, line)) {
174 throw std::runtime_error(
176 << "Error: Unexpected end of file in MshReader (reading elements).\n");
177 }
178 trim_inplace(line);
179 std::istringstream ls(line);
180 unsigned int id;
181 unsigned int type;
182 unsigned int ntags;
183 ls >> id >> type >> ntags;
184 int tag = 0;
185 for (unsigned int j = 0; j < ntags; j++)
186 ls >> tag;
187
188 bool read_this_element = false;
189 unsigned int num_nodes_con = 0;
190
191 if (type == util::msh_type_triangle and dim == 2) {
192 read_this_element = true;
193 found_tri = true;
194 element_type = util::vtk_type_triangle;
195 num_nodes_con =
197 } else if (type == util::msh_type_quadrangle and dim == 2) {
198 read_this_element = true;
199 found_quad = true;
200 element_type = util::vtk_type_quad;
201 num_nodes_con =
203 } else if (type == util::msh_type_tetrahedron and dim == 3) {
204 read_this_element = true;
205 element_type = util::vtk_type_tetra;
206 num_nodes_con =
208 } else if (type == util::msh_type_hexahedron and dim == 3) {
209 read_this_element = true;
210 element_type = util::vtk_type_hexahedron;
211 num_nodes_con =
213 }
214
215 if (read_this_element) {
216 for (unsigned int i = 0; i < num_nodes_con; i++) {
217 unsigned int node_id;
218 ls >> node_id;
219 enc->push_back(node_id - 1);
220 (*nec)[node_id - 1].push_back(elem_counter);
221 }
222 elem_counter++;
223 } else {
224 unsigned int n_skip = 0;
225 if (type < 16)
226 n_skip = static_cast<unsigned int>(util::msh_map_element_to_num_nodes[type]);
227 if (n_skip > 0) {
228 unsigned int dummy;
229 for (unsigned int i = 0; i < n_skip; i++)
230 ls >> dummy;
231 } else {
232 unsigned int dummy;
233 while (ls >> dummy) {
234 }
235 }
236 }
237 }
238
239 if (found_quad and found_tri) {
240 throw std::runtime_error(
242 << "Error: Check mesh file. It appears to have both "
243 "quadrangle elements and triangle elements. "
244 "Currently we only support one kind of elements.\n");
245 }
246
247 num_elems = elem_counter;
248 break;
249 }
250 }
251
252 if (!read_nodes || !read_elements) {
253 throw std::runtime_error(
255 << "Error: MshReader: incomplete .msh file (need $Nodes and $Elements): "
256 << d_filename << "\n");
257 }
258
259 // close file
260 d_file.close();
261}
262
263void rw::reader::MshReader::readNodes(std::vector<util::Point> *nodes) {
264
265 if (util::io::isFileEmpty(d_filename)) {
266 throw std::runtime_error(
268 << "Error: Filename = " << d_filename <<
269 " in MshReader is either nonexistent or empty.\n");
270 }
271
272 // open file
273 if (d_file) d_file.close();
274
275 d_file.open(d_filename);
276
277 if (!d_file) {
278 throw std::runtime_error(
280 << "Error: Can not open file = " << d_filename + ".msh.\n");
281 }
282
283 std::string line;
284 int format = 0;
285 int size = 0;
286 double version = 1.0;
287
288 // clear data
289 nodes->clear();
290 bool read_nodes = false;
291
292 while (true) {
293 std::getline(d_file, line);
294 if (d_file) {
295 // // read $MeshFormat block
296 if (line.find("$MeshFormat") == static_cast<std::string::size_type>(0)) {
297 d_file >> version >> format >> size;
298 if ((version != 2.0) && (version != 2.1) && (version != 2.2)) {
299 throw std::runtime_error(
301 << "Error: Unknown .msh file version " << version << "\n");
302 }
303
304 // we only support reading of ascii format, so issue error if this
305 // condition is not met
306 if (format) {
307 throw std::runtime_error(
309 << "Error: Format of .msh is possibly binary which is not"
310 " supported currently.\n ");
311 }
312 }
313 // read $Nodes block
314 if (line.find("$NOD") == static_cast<std::string::size_type>(0) ||
315 line.find("$NOE") == static_cast<std::string::size_type>(0) ||
316 line.find("$Nodes") == static_cast<std::string::size_type>(0)) {
317 read_nodes = true;
318 unsigned int num_nodes = 0;
319 d_file >> num_nodes;
320
321 // allocate space
322 nodes->resize(num_nodes);
323
324 // read in the nodal coordinates and form points.
325 double x, y, z;
326 unsigned int id;
327
328 // add the nodal coordinates to the d_file
329 for (unsigned int i = 0; i < num_nodes; ++i) {
330 d_file >> id >> x >> y >> z;
331 (*nodes)[id - 1] = util::Point(x, y, z);
332 }
333 // read the $ENDNOD delimiter
334 std::getline(d_file, line);
335 } // end of reading nodes
336 } // if d_file
337
338 // If !d_file, check to see if EOF was set. If so, break out
339 // of while loop.
340 if (d_file.eof()) break;
341
342 if (read_nodes) break;
343
344 // If !d_file and !d_file.eof(), stream is in a bad state!
345 // std::cerr<<"Error: Stream is bad! Perhaps the file does not exist?\n";
346 // exit(1);
347 } // while true
348
349 // close file
350 d_file.close();
351}
352
353void rw::reader::MshReader::readCells(size_t dim, size_t &element_type,
354 size_t &num_elems, std::vector<size_t> *enc,
355 std::vector<std::vector<size_t>> *nec) {
356
357 if (util::io::isFileEmpty(d_filename)) {
358 throw std::runtime_error(
360 << "Error: Filename = " << d_filename <<
361 " in MshReader is either nonexistent or empty.\n");
362 }
363
364 // open file
365 if (d_file) d_file.close();
366
367 d_file.open(d_filename);
368
369 if (!d_file) {
370 throw std::runtime_error(
372 << "Error: Can not open file = " << d_filename + ".msh"
373 << ".\n");
374 }
375
376 // specify type of element to read
377 if (dim != 2 and dim != 3) {
378 throw std::runtime_error(
380 << "Error: MshReader currently only supports reading of "
381 "triangle/quadrangle elements in dimension 2 and tetragonal "
382 "elements in 3.\n");
383 }
384
385 enc->clear();
386 nec->clear();
387
388 bool have_nodes = false;
389 bool read_elements = false;
390
391 std::string line;
392 while (std::getline(d_file, line)) {
393 trim_inplace(line);
394 if (line.empty())
395 continue;
396
397 if (!have_nodes && (line == "$Nodes" || line == "$NOD" || line == "$NOE")) {
398 if (!std::getline(d_file, line)) {
399 throw std::runtime_error(
401 << "Error: Unexpected end of file in MshReader (readCells node count).\n");
402 }
403 trim_inplace(line);
404 unsigned int num_nodes = 0;
405 {
406 std::istringstream ns(line);
407 ns >> num_nodes;
408 }
409 for (unsigned int i = 0; i < num_nodes; ++i) {
410 if (!std::getline(d_file, line)) {
411 throw std::runtime_error(
413 << "Error: Unexpected end of file in MshReader (readCells skip nodes).\n");
414 }
415 }
416 nec->assign(num_nodes, {});
417 have_nodes = true;
418 continue;
419 }
420
421 if (have_nodes && (line == "$Elements" || line == "$ELM")) {
422 read_elements = true;
423 if (!std::getline(d_file, line)) {
424 throw std::runtime_error(
426 << "Error: Unexpected end of file in MshReader (readCells element count).\n");
427 }
428 trim_inplace(line);
429 unsigned int num_elem = 0;
430 {
431 std::istringstream es(line);
432 es >> num_elem;
433 }
434
435 size_t elem_counter = 0;
436 bool found_tri = false;
437 bool found_quad = false;
438
439 for (unsigned int iel = 0; iel < num_elem; ++iel) {
440 if (!std::getline(d_file, line)) {
441 throw std::runtime_error(
443 << "Error: Unexpected end of file in MshReader (readCells elements).\n");
444 }
445 trim_inplace(line);
446 std::istringstream ls(line);
447 unsigned int id;
448 unsigned int type;
449 unsigned int ntags;
450 ls >> id >> type >> ntags;
451 int tag = 0;
452 for (unsigned int j = 0; j < ntags; j++)
453 ls >> tag;
454
455 bool read_this_element = false;
456 unsigned int num_nodes_con = 0;
457
458 if (type == util::msh_type_triangle and dim == 2) {
459 read_this_element = true;
460 found_tri = true;
461 element_type = util::vtk_type_triangle;
462 num_nodes_con =
464 } else if (type == util::msh_type_quadrangle and dim == 2) {
465 read_this_element = true;
466 found_quad = true;
467 element_type = util::vtk_type_quad;
468 num_nodes_con =
470 } else if (type == util::msh_type_tetrahedron and dim == 3) {
471 read_this_element = true;
472 element_type = util::vtk_type_tetra;
473 num_nodes_con =
475 } else if (type == util::msh_type_hexahedron and dim == 3) {
476 read_this_element = true;
477 element_type = util::vtk_type_hexahedron;
478 num_nodes_con =
480 }
481
482 if (read_this_element) {
483 for (unsigned int i = 0; i < num_nodes_con; i++) {
484 unsigned int node_id;
485 ls >> node_id;
486 enc->push_back(node_id - 1);
487 (*nec)[node_id - 1].push_back(elem_counter);
488 }
489 elem_counter++;
490 } else {
491 unsigned int n_skip = 0;
492 if (type < 16)
493 n_skip = static_cast<unsigned int>(util::msh_map_element_to_num_nodes[type]);
494 if (n_skip > 0) {
495 unsigned int dummy;
496 for (unsigned int i = 0; i < n_skip; i++)
497 ls >> dummy;
498 } else {
499 unsigned int dummy;
500 while (ls >> dummy) {
501 }
502 }
503 }
504 }
505
506 if (found_quad and found_tri) {
507 throw std::runtime_error(
509 << "Error: Check mesh file. It appears to have both "
510 "quadrangle elements and triangle elements. "
511 "Currently we only support one kind of elements.\n");
512 }
513
514 num_elems = elem_counter;
515 break;
516 }
517 }
518
519 if (!have_nodes || !read_elements) {
520 throw std::runtime_error(
522 << "Error: MshReader::readCells: incomplete .msh file: " << d_filename << "\n");
523 }
524
525 // close file
526 d_file.close();
527}
528
529bool rw::reader::MshReader::readPointData(const std::string &name,
530 std::vector<util::Point> *data) {
531 // open file
532 if (!d_file) d_file = std::ifstream(d_filename);
533
534 if (!d_file)
535 if (!d_file) {
536 throw std::runtime_error(
538 << "Error: Can not open file = " << d_filename + ".msh"
539 << ".\n");
540 }
541
542 bool found_data = false;
543 std::string line;
544 while (true) {
545 std::getline(d_file, line);
546 if (d_file) {
547 // read $Nodes block
548 if (line.find("$NodeData") == static_cast<std::string::size_type>(0)) {
549 // get name of data
550 int num_tags = 0;
551 d_file >> num_tags;
552 std::string tag[num_tags];
553 for (size_t i = 0; i < num_tags; i++) d_file >> tag[i];
554
555 // read dummy data
556 d_file >> num_tags;
557 double real_tag = 0.;
558 d_file >> real_tag;
559
560 int tag_number = 0;
561 int field_type = 0;
562 int num_data = 0;
563 d_file >> tag_number >> field_type >> num_data;
564
565 // check we found the data
566 if (tag[0] == name) {
567 // check if data is of desired field type
568 if (field_type != 3) {
569 throw std::runtime_error(
571 << "Error: Data " << tag[0] << " is of type "
572 << field_type << " but we expect it to be of type " << 3
573 << ".\n");
574 }
575
576 found_data = true;
577 data->resize(num_data);
578 }
579
580 // we read through the data irrespective of we found it or not
581 for (size_t i = 0; i < num_data; i++) {
582 double d[field_type];
583 for (size_t j = 0; j < field_type; j++) d_file >> d[j];
584
585 if (found_data) (*data)[i] = util::Point(d[0], d[1], d[2]);
586 }
587 // read the end of data block
588 std::getline(d_file, line);
589 } // end of reading nodes
590 } // if d_file
591
592 if (found_data) break;
593
594 // If !d_file, check to see if EOF was set. If so, break out
595 // of while loop.
596 if (d_file.eof()) break;
597 } // while true
598
599 d_file.close();
600 return found_data;
601}
602
603bool rw::reader::MshReader::readPointData(const std::string &name,
604 std::vector<double> *data) {
605 // open file
606 if (!d_file) d_file = std::ifstream(d_filename);
607
608 if (!d_file)
609 if (!d_file) {
610 throw std::runtime_error(
612 << "Error: Can not open file = " << d_filename + ".msh"
613 << ".\n");
614 }
615
616 bool found_data = false;
617 std::string line;
618 while (true) {
619 std::getline(d_file, line);
620 if (d_file) {
621 // read $Nodes block
622 if (line.find("$NodeData") == static_cast<std::string::size_type>(0)) {
623 // get name of data
624 int num_tags = 0;
625 d_file >> num_tags;
626 std::string tag[num_tags];
627 for (size_t i = 0; i < num_tags; i++) d_file >> tag[i];
628
629 // read dummy data
630 d_file >> num_tags;
631 double real_tag = 0.;
632 d_file >> real_tag;
633
634 int tag_number = 0;
635 int field_type = 0;
636 int num_data = 0;
637 d_file >> tag_number >> field_type >> num_data;
638
639 // check we found the data
640 if (tag[0] == name) {
641 // check if data is of desired field type
642 if (field_type != 1) {
643 throw std::runtime_error(
645 << "Error: Data " << tag[0] << " is of type "
646 << field_type << " but we expect it to be of type " << 1
647 << ".\n");
648 }
649
650 found_data = true;
651 data->resize(num_data);
652 }
653
654 // we read through the data irrespective of we found it or not
655 for (size_t i = 0; i < num_data; i++) {
656 double d[field_type];
657 for (size_t j = 0; j < field_type; j++) d_file >> d[j];
658
659 if (found_data) (*data)[i] = d[0];
660 }
661 // read the end of data block
662 std::getline(d_file, line);
663 } // end of reading nodes
664 } // if d_file
665
666 if (found_data) break;
667
668 // If !d_file, check to see if EOF was set. If so, break out
669 // of while loop.
670 if (d_file.eof()) break;
671 } // while true
672
673 d_file.close();
674 return found_data;
675}
676
void readMesh(size_t dim, std::vector< util::Point > *nodes, size_t &element_type, size_t &num_elems, std::vector< size_t > *enc, std::vector< std::vector< size_t > > *nec, std::vector< double > *volumes, bool is_fd=false)
Reads mesh data into node file and element file.
Definition mshReader.cpp:38
void readCells(size_t dim, size_t &element_type, size_t &num_elems, std::vector< size_t > *enc, std::vector< std::vector< size_t > > *nec)
Reads cell data, i.e. element-node connectivity and node-element connectivity.
bool readPointData(const std::string &name, std::vector< util::Point > *data)
reads point data from .vtu file
void close()
Close the file.
MshReader(const std::string &filename)
Constructor.
Definition mshReader.cpp:35
void readNodes(std::vector< util::Point > *nodes)
Reads nodal position.
Collects a message with stream syntax for use in an exception.
Definition io.h:52
static const int msh_type_quadrangle
Integer flag for quadrangle element.
static const int vtk_type_triangle
Integer flag for triangle element.
static const int vtk_type_quad
Integer flag for quad element.
static const int vtk_type_tetra
Integer flag for tetrahedron element.
static int msh_map_element_to_num_nodes[16]
Map from element type to number of nodes (for msh)
static const int msh_type_hexahedron
Integer flag for hexahedron element.
static const int vtk_type_hexahedron
Integer flag for hexahedron element.
static const int msh_type_triangle
Integer flag for triangle element.
static const int msh_type_tetrahedron
Integer flag for tetrahedron element.
void trim_inplace(std::string &s)
Definition mshReader.cpp:23
Definition contact.h:20
bool isFileEmpty(std::string filename)
Check if file is empty or null.
Definition io.h:393
A structure to represent 3d vectors.
Definition point.h:30