OGS
GocadAsciiReader.cpp
Go to the documentation of this file.
1 
10 #include "GocadAsciiReader.h"
11 
12 #include <iosfwd>
13 
15 #include "BaseLib/FileTools.h"
16 #include "BaseLib/Logging.h"
17 #include "BaseLib/StringTools.h"
18 #include "MeshLib/Elements/Line.h"
19 #include "MeshLib/Elements/Tri.h"
20 #include "MeshLib/Mesh.h"
21 #include "MeshLib/Node.h"
22 #include "MeshLib/Properties.h"
23 
24 namespace FileIO
25 {
26 namespace Gocad
27 {
28 namespace GocadAsciiReader
29 {
30 enum class NodeType
31 {
33  VRTX,
34  PVRTX
35 };
36 
37 const std::string mat_id_name = "MaterialIDs";
38 const std::string eof_error = "Error: Unexpected end of file.";
39 
43 void checkMeshNames(std::vector<std::unique_ptr<MeshLib::Mesh>> const& meshes)
44 {
45  std::size_t const n_meshes = meshes.size();
46  for (std::size_t i = 0; i < n_meshes; ++i)
47  {
48  std::string const& name = meshes[i]->getName();
49  for (std::size_t j = i + 1; j < n_meshes; ++j)
50  {
51  if (meshes[j]->getName() == name)
52  {
53  std::string const id_str = std::to_string(meshes[j]->getID());
54  meshes[i]->setName(name + "--importID-" + id_str);
55  break;
56  }
57  }
58  }
59 }
60 
62 bool isCommentLine(std::string const& str)
63 {
64  return (str.substr(0, 1) == "#");
65 }
66 
68 bool skipToEND(std::ifstream& in)
69 {
70  std::string line;
71  while (std::getline(in, line))
72  {
73  if (line == "END")
74  {
75  return true;
76  }
77  }
78  ERR("{:s}", eof_error);
79  return false;
80 }
81 
83 bool isKeyword(DataType const t, std::string const& line)
84 {
85  std::size_t str_length = dataType2String(t).length();
86  return (line.substr(0, str_length) == dataType2String(t));
87 }
88 
90 DataType datasetFound(std::ifstream& in)
91 {
92  std::string line;
93  while (std::getline(in, line))
94  {
95  if (line.empty() || isCommentLine(line))
96  {
97  continue;
98  }
99 
100  if (isKeyword(DataType::VSET, line))
101  {
102  return DataType::VSET;
103  }
104  if (isKeyword(DataType::PLINE, line))
105  {
106  return DataType::PLINE;
107  }
108  if (isKeyword(DataType::TSURF, line))
109  {
110  return DataType::TSURF;
111  }
112  if (isKeyword(DataType::MODEL3D, line))
113  {
114  return DataType::MODEL3D;
115  }
116  ERR("No known identifier found...");
117  return DataType::UNDEFINED;
118  }
119  return DataType::UNDEFINED;
120 }
122 void checkLineEndings(std::string const& file_name)
123 {
124 #ifndef _WIN32
125  std::ifstream in(file_name);
126  if (in.is_open())
127  {
128  std::string line;
129  std::getline(in, line);
130  if (line.back() == '\r')
131  {
132  OGS_FATAL(
133  "Error in input file: {:s}. The line endings are in windows "
134  "format. To read this file under UNIX, transform the input "
135  "file to unix style line endings (e.g. dos2unix).",
136  file_name);
137  }
138  }
139 #endif
140 }
141 
143 bool parseHeader(std::ifstream& in, std::string& mesh_name)
144 {
145  std::string line;
146  while (std::getline(in, line))
147  {
148  if (line.substr(0, 5) == "name:")
149  {
150  mesh_name = line.substr(5, line.length() - 5);
151  BaseLib::trim(mesh_name, ' ');
152  }
153  else if (line.substr(0, 1) == "}")
154  {
155  return true;
156  }
157  // ignore all other header parameters
158  }
159  ERR("{:s}", eof_error);
160  return false;
161 }
162 
165 bool parsePropertyClass(std::ifstream& in)
166 {
167  std::string line;
168  while (std::getline(in, line))
169  {
170  if (line.substr(0, 1) == "}")
171  {
172  return true;
173  }
174  }
175  ERR("{:s}", eof_error);
176  return false;
177 }
178 
180 std::string propertyCheck(std::string const& string)
181 {
182  std::array<std::string, 7> const property_keywords = {
183  {"PROPERTY_CLASSES", "PROP_LEGAL_RANGES", "NO_DATA_VALUES",
184  "PROPERTY_KINDS", "PROPERTY_SUBCLASSES", "UNITS", "ESIZES"}};
185 
186  std::string const str = BaseLib::splitString(string)[0];
187  auto res =
188  std::find(property_keywords.begin(), property_keywords.end(), str);
189  if (res != property_keywords.end())
190  {
191  return *res;
192  }
193  return std::string("");
194 }
195 
198 bool parseProperties(std::ifstream& in,
199  std::vector<std::string> const& names,
200  MeshLib::Properties& mesh_prop)
201 {
202  // Because properties have no end-tag, the position of the last line is
203  // stored, so the stream can be set back if none of the allowed property-
204  // related keywords is found.
205  std::streampos pos = in.tellg();
206  std::string line;
207  while (getline(in, line))
208  {
209  std::string const key = propertyCheck(line);
210  // This is the intended way to exit this method:
211  // No property-related keyword has been found, so the stream is set
212  // back one line and the (unrelated) keyword can be read again in the
213  // parent method.
214  if (key.empty())
215  {
216  in.seekg(pos);
217  return true;
218  }
219 
220  // Currently all property parameters except array name and size are
221  // ignored.
222  if (key == "ESIZES")
223  {
224  std::vector<std::string> prop_size = BaseLib::splitString(line);
225 
226  if (names.size() != prop_size.size())
227  {
228  ERR("Error: Number of PROPERTY-names ({:d}) does not match "
229  "number of ESIZES ({:d})",
230  names.size(), prop_size.size());
231  return false;
232  }
233  std::size_t const n_names(names.size());
234  for (std::size_t i = 1; i < n_names; ++i)
235  {
236  mesh_prop.createNewPropertyVector<double>(
237  names[i],
239  BaseLib::str2number<std::size_t>(prop_size[i]));
240  }
241  }
242  // Remember current position in case the properties black ends now.
243  pos = in.tellg();
244  }
245  ERR("{:s}", eof_error);
246  return false;
247 }
248 
249 MeshLib::Node* createNode(std::stringstream& sstr)
250 {
251  std::string keyword;
252  std::size_t id;
253  std::array<double, 3> data{};
254  sstr >> keyword >> id >> data[0] >> data[1] >> data[2];
255  return new MeshLib::Node(data, id);
256 }
257 
260 bool parseAtomRegionIndicators(std::ifstream& in)
261 {
262  std::string line;
263  while (std::getline(in, line))
264  {
265  if (line.substr(0, 26) == "END_ATOM_REGION_INDICATORS")
266  {
267  return true;
268  }
269  }
270  return false;
271 }
272 
274 bool parseNodes(std::ifstream& in,
275  std::vector<MeshLib::Node*>& nodes,
276  std::map<std::size_t, std::size_t>& node_id_map,
277  MeshLib::Properties const& mesh_prop)
278 {
280  std::streampos pos = in.tellg();
281  std::string line;
282  while (std::getline(in, line))
283  {
284  std::vector<std::string> str = BaseLib::splitString(line);
285  if (line.substr(0, 3) == "SEG" || line.substr(0, 4) == "TRGL")
286  {
287  in.seekg(pos);
288  return true;
289  }
290 
291  if (line.substr(0, 28) == "BEGIN_ATOM_REGION_INDICATORS")
292  {
293  if (!parseAtomRegionIndicators(in))
294  {
295  ERR("File ended while parsing Atom Region Indicators...");
296  return false;
297  }
298  return true;
299  }
300 
301  if (line.empty() || isCommentLine(line))
302  {
303  continue;
304  }
305  if (!(line.substr(0, 4) == "VRTX" || line.substr(0, 5) == "PVRTX" ||
306  line.substr(0, 4) == "ATOM"))
307  {
308  WARN("GocadAsciiReader::parseNodes() - Unknown keyword found: {:s}",
309  line);
310  continue;
311  }
312 
313  std::stringstream sstr(line);
314  if (line.substr(0, 4) == "VRTX" && t != NodeType::PVRTX)
315  {
316  t = NodeType::VRTX;
317  nodes.push_back(createNode(sstr));
318  }
319  else if (line.substr(0, 5) == "PVRTX" && t != NodeType::VRTX)
320  {
321  t = NodeType::PVRTX;
322  nodes.push_back(createNode(sstr));
323  for (auto [name, property] : mesh_prop)
324  {
325  if (name == mat_id_name)
326  {
327  continue;
328  }
329  if (auto p = dynamic_cast<MeshLib::PropertyVector<double>*>(
330  property))
331  {
332  double value;
333  sstr >> value;
334  p->push_back(value);
335  }
336  }
337  }
338  else if (line.substr(0, 4) == "ATOM")
339  {
340  std::size_t new_id;
341  std::size_t ref_id;
342  std::string keyword;
343  sstr >> keyword >> new_id >> ref_id;
344  nodes.push_back(
345  new MeshLib::Node(nodes[ref_id]->getCoords(), new_id));
346  }
347  node_id_map[nodes.back()->getID()] = nodes.size() - 1;
348  pos = in.tellg();
349  }
350  ERR("{:s}", eof_error);
351  return false;
352 }
353 
355 bool parseLineSegments(std::ifstream& in,
356  std::vector<MeshLib::Node*>& nodes,
357  std::vector<MeshLib::Element*>& elems,
358  std::map<std::size_t, std::size_t> const& node_id_map,
359  MeshLib::Properties& mesh_prop)
360 {
362  *mesh_prop.getPropertyVector<int>(mat_id_name);
363  int current_mat_id(0);
364  if (!mat_ids.empty())
365  {
366  current_mat_id = (*std::max_element(mat_ids.begin(), mat_ids.end()))++;
367  }
368  std::streampos pos = in.tellg();
369  std::size_t id(0);
370  std::string line;
371  while (std::getline(in, line))
372  {
373  if (line.empty() || isCommentLine(line))
374  {
375  continue;
376  }
377  if (line.substr(0, 3) == "SEG")
378  {
379  std::stringstream sstr(line);
380  std::string keyword;
381  std::array<std::size_t, 2> data{};
382  sstr >> keyword >> data[0] >> data[1];
383  std::array<MeshLib::Node*, 2> elem_nodes{};
384  for (std::size_t i = 0; i < 2; ++i)
385  {
386  auto const it = node_id_map.find(data[i]);
387  if (it == node_id_map.end() || it->second >= nodes.size())
388  {
389  ERR("Error: Node ID ({:d}) out of range (0, {:d}).",
390  data[i], nodes.back()->getID());
391  return false;
392  }
393  elem_nodes[i] = nodes[it->second];
394  }
395  elems.push_back(new MeshLib::Line(elem_nodes, id++));
396  mat_ids.push_back(current_mat_id);
397  }
398  else
399  {
400  in.seekg(pos);
401  return true;
402  }
403  pos = in.tellg();
404  }
405  ERR("{:s}", eof_error);
406  return false;
407 }
408 
410 bool parseLine(std::ifstream& in,
411  std::vector<MeshLib::Node*>& nodes,
412  std::vector<MeshLib::Element*>& elems,
413  std::map<std::size_t, std::size_t>& node_id_map,
414  MeshLib::Properties& mesh_prop)
415 {
416  if (!parseNodes(in, nodes, node_id_map, mesh_prop))
417  {
418  return false;
419  }
420  if (!parseLineSegments(in, nodes, elems, node_id_map, mesh_prop))
421  {
422  return false;
423  }
424 
425  std::string line;
426  while (std::getline(in, line))
427  {
428  std::vector<std::string> str = BaseLib::splitString(line);
429  if (str[0] == "ILINE")
430  {
431  parseLine(in, nodes, elems, node_id_map, mesh_prop);
432  return true;
433  }
434  if (line == "END")
435  {
436  return true;
437  }
438  WARN("GocadAsciiReader::parseLine() - Unknown keyword found: {:s}",
439  line);
440  }
441  ERR("{:s}", eof_error);
442  return false;
443 }
444 
446 bool parseElements(std::ifstream& in,
447  std::vector<MeshLib::Node*>& nodes,
448  std::vector<MeshLib::Element*>& elems,
449  std::map<std::size_t, std::size_t> const& node_id_map,
450  MeshLib::Properties& mesh_prop)
451 {
453  *mesh_prop.getPropertyVector<int>(mat_id_name);
454  int current_mat_id(0);
455  if (!mat_ids.empty())
456  {
457  current_mat_id = (*std::max_element(mat_ids.begin(), mat_ids.end()))++;
458  }
459  std::streampos pos = in.tellg();
460  std::size_t id(0);
461  std::string line;
462  while (std::getline(in, line))
463  {
464  if (line.empty() || isCommentLine(line))
465  {
466  continue;
467  }
468  if (line.substr(0, 4) == "TRGL")
469  {
470  std::stringstream sstr(line);
471  std::string keyword;
472  std::array<std::size_t, 3> data{};
473  sstr >> keyword >> data[0] >> data[1] >> data[2];
474  std::array<MeshLib::Node*, 3> elem_nodes{};
475  for (std::size_t i = 0; i < 3; ++i)
476  {
477  auto const it = node_id_map.find(data[i]);
478  if (it == node_id_map.end() || it->second >= nodes.size())
479  {
480  ERR("Error: Node ID ({:d}) out of range (0, {:d}).",
481  data[i], nodes.back()->getID());
482  return false;
483  }
484  elem_nodes[i] = nodes[it->second];
485  }
486  elems.push_back(new MeshLib::Tri(elem_nodes, id++));
487  mat_ids.push_back(current_mat_id);
488  }
489  else
490  {
491  in.seekg(pos);
492  return true;
493  }
494  pos = in.tellg();
495  }
496  ERR("{:s}", eof_error);
497  return false;
498 }
499 
501 bool parseSurface(std::ifstream& in,
502  std::vector<MeshLib::Node*>& nodes,
503  std::vector<MeshLib::Element*>& elems,
504  std::map<std::size_t, std::size_t>& node_id_map,
505  MeshLib::Properties& mesh_prop)
506 {
507  if (!parseNodes(in, nodes, node_id_map, mesh_prop))
508  {
509  return false;
510  }
511  if (!parseElements(in, nodes, elems, node_id_map, mesh_prop))
512  {
513  return false;
514  }
515 
516  std::string line;
517  while (std::getline(in, line))
518  {
519  std::vector<std::string> str = BaseLib::splitString(line);
520  if (str[0] == "TFACE")
521  {
522  parseSurface(in, nodes, elems, node_id_map, mesh_prop);
523  return true;
524  }
525  if (str[0] == "BSTONE")
526  {
527  // borderstone definition - currently ignored
528  }
529  else if (str[0] == "BORDER")
530  {
531  // border tracking direction - currently ignored
532  }
533  else if (line == "END")
534  {
535  return true;
536  }
537  else
538  {
539  WARN(
540  "GocadAsciiReader::parseSurface() - Unknown keyword found: "
541  "{:s}",
542  line);
543  }
544  }
545  ERR("{:s}", eof_error);
546  return false;
547 }
548 
550 template <typename T>
551 MeshLib::Mesh* createMesh(std::ifstream& in, DataType type,
552  std::string& mesh_name,
553  MeshLib::Properties& mesh_prop, T parser)
554 {
555  std::vector<MeshLib::Node*> nodes;
556  std::vector<MeshLib::Element*> elems;
557  std::map<std::size_t, std::size_t> node_id_map;
558  INFO("Parsing {:s} {:s}.", dataType2ShortString(type), mesh_name);
559  bool return_val;
560  return_val = parser(in, nodes, elems, node_id_map, mesh_prop);
561 
562  if (return_val)
563  {
564  return new MeshLib::Mesh(mesh_name, nodes, elems, mesh_prop);
565  }
566  ERR("Error parsing {:s} {:s}.", dataType2ShortString(type), mesh_name);
567  BaseLib::cleanupVectorElements(nodes, elems);
568  return nullptr;
569 }
570 
572 MeshLib::Mesh* readData(std::ifstream& in,
573  DataType const& type,
574  std::string& mesh_name)
575 {
576  if (!parseHeader(in, mesh_name))
577  {
578  return nullptr;
579  }
580 
581  MeshLib::Properties mesh_prop;
582  mesh_prop.createNewPropertyVector<int>(mat_id_name,
584  std::string line;
585  while (std::getline(in, line))
586  {
587  std::vector<std::string> str = BaseLib::splitString(line);
588  if (line.empty() || isCommentLine(line))
589  {
590  continue;
591  }
592  if (str[0] == "GOCAD_ORIGINAL_COORDINATE_SYSTEM")
593  {
594  Gocad::CoordinateSystem coordinate_system;
595  if (!coordinate_system.parse(in))
596  {
597  ERR("Error parsing coordinate system.");
598  return nullptr;
599  }
600  }
601  else if (str[0] == "GEOLOGICAL_FEATURE" ||
602  str[0] == "GEOLOGICAL_TYPE" ||
603  str[0] == "STRATIGRAPHIC_POSITION")
604  {
605  // geological and stratigraphic information - currently ignored
606  }
607  else if (str[0] == "PROPERTY_CLASS_HEADER")
608  {
609  if (!parsePropertyClass(in))
610  {
611  ERR("Error parsing PROPERTY_CLASS_HEADER.");
612  return nullptr;
613  }
614  }
615  else if (str[0] == "PROPERTIES")
616  {
617  if (!parseProperties(in, str, mesh_prop))
618  {
619  ERR("Error parsing PROPERTIES");
620  return nullptr;
621  }
622  }
623  else if (type == DataType::PLINE && str[0] == "ILINE")
624  {
625  return createMesh(in, type, mesh_name, mesh_prop, parseLine);
626  }
627  else if (type == DataType::TSURF && str[0] == "TFACE")
628  {
629  return createMesh(in, type, mesh_name, mesh_prop, parseSurface);
630  }
631  else
632  {
633  WARN("GocadAsciiReader::readData() - Unknown keyword found: {:s}",
634  line);
635  }
636  }
637  ERR("{:s}", eof_error);
638  return nullptr;
639 }
640 
641 bool readFile(std::string const& file_name,
642  std::vector<std::unique_ptr<MeshLib::Mesh>>& meshes,
643  DataType const export_type)
644 {
645  std::ifstream in(file_name);
646  if (!in.is_open())
647  {
648  ERR("GocadAsciiReader::readFile(): Could not open file {:s}.",
649  file_name);
650  return false;
651  }
652 
653  checkLineEndings(file_name);
654 
655  DataType type;
656  while ((type = datasetFound(in)) != DataType::UNDEFINED)
657  {
658  if (export_type != DataType::ALL && type != export_type)
659  {
660  skipToEND(in);
661  continue;
662  }
663 
664  if (type == DataType::VSET || type == DataType::MODEL3D)
665  {
666  if (!skipToEND(in))
667  {
668  ERR("Parsing of type {:s} is not implemented. Skipping "
669  "section.",
670  dataType2String(type));
671  return false;
672  }
673  continue;
674  }
675 
676  std::string mesh_name = BaseLib::dropFileExtension(file_name) +
677  std::to_string(meshes.size() + 1);
678  std::unique_ptr<MeshLib::Mesh> mesh(readData(in, type, mesh_name));
679  if (mesh == nullptr)
680  {
681  ERR("File parsing aborted...");
682  return false;
683  }
684  meshes.push_back(std::move(mesh));
685  }
686  checkMeshNames(meshes);
687  return true;
688 }
689 
690 } // namespace GocadAsciiReader
691 } // end namespace Gocad
692 } // end namespace FileIO
#define OGS_FATAL(...)
Definition: Error.h:26
Filename manipulation routines.
Definition of the Line class.
void INFO(char const *fmt, Args const &... args)
Definition: Logging.h:32
void ERR(char const *fmt, Args const &... args)
Definition: Logging.h:42
void WARN(char const *fmt, Args const &... args)
Definition: Logging.h:37
Definition of the class Properties that implements a container of properties.
Definition of the Mesh class.
Definition of the Node class.
Definition of string helper functions.
std::string getName(std::string const &line)
Returns the name/title from the "Zone"-description.
Definition of the Tri class.
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
Definition: Properties.h:36
PropertyVector< T > const * getPropertyVector(std::string const &name) const
PropertyVector< T > * createNewPropertyVector(std::string const &name, MeshItemType mesh_item_type, std::size_t n_components=1)
void trim(std::string &str, char ch)
Definition: StringTools.cpp:58
std::string dropFileExtension(std::string const &filename)
Definition: FileTools.cpp:169
std::vector< std::string > splitString(std::string const &str)
Definition: StringTools.cpp:28
void cleanupVectorElements(std::vector< T1 * > const &items, std::vector< T2 * > const &dependent_items)
Definition: Algorithm.h:300
bool parseLineSegments(std::ifstream &in, std::vector< MeshLib::Node * > &nodes, std::vector< MeshLib::Element * > &elems, std::map< std::size_t, std::size_t > const &node_id_map, MeshLib::Properties &mesh_prop)
Parses the segments of the current line.
bool readFile(std::string const &file_name, std::vector< std::unique_ptr< MeshLib::Mesh >> &meshes, DataType const export_type)
Reads the specified file and writes data into internal mesh vector.
void checkLineEndings(std::string const &file_name)
Checks if current line is a designated keyword for a GoCAD data set.
MeshLib::Mesh * readData(std::ifstream &in, DataType const &type, std::string &mesh_name)
Reads one mesh contained in the file (there may be more than one!)
bool parseHeader(std::ifstream &in, std::string &mesh_name)
Parses the HEADER section (everything except the name is ignored right now)
MeshLib::Node * createNode(std::stringstream &sstr)
bool parseLine(std::ifstream &in, std::vector< MeshLib::Node * > &nodes, std::vector< MeshLib::Element * > &elems, std::map< std::size_t, std::size_t > &node_id_map, MeshLib::Properties &mesh_prop)
Parses line information (nodes, segments, properties)
bool parsePropertyClass(std::ifstream &in)
bool isCommentLine(std::string const &str)
Checks if the current line is a comment.
bool skipToEND(std::ifstream &in)
Parses current section until END-tag is reached.
bool isKeyword(DataType const t, std::string const &line)
Checks if current line is a designated keyword for a GoCAD data set.
MeshLib::Mesh * createMesh(std::ifstream &in, DataType type, std::string &mesh_name, MeshLib::Properties &mesh_prop, T parser)
Converts parsed data into mesh.
void checkMeshNames(std::vector< std::unique_ptr< MeshLib::Mesh >> const &meshes)
bool parseNodes(std::ifstream &in, std::vector< MeshLib::Node * > &nodes, std::map< std::size_t, std::size_t > &node_id_map, MeshLib::Properties const &mesh_prop)
Parses the node data for the current mesh.
bool parseAtomRegionIndicators(std::ifstream &in)
bool parseElements(std::ifstream &in, std::vector< MeshLib::Node * > &nodes, std::vector< MeshLib::Element * > &elems, std::map< std::size_t, std::size_t > const &node_id_map, MeshLib::Properties &mesh_prop)
Parses the element data for the current mesh.
std::string propertyCheck(std::string const &string)
Checks if the current line starts with one of the allowed keywords.
bool parseProperties(std::ifstream &in, std::vector< std::string > const &names, MeshLib::Properties &mesh_prop)
DataType datasetFound(std::ifstream &in)
Checks if a GoCAD data set begins at the current stream position.
bool parseSurface(std::ifstream &in, std::vector< MeshLib::Node * > &nodes, std::vector< MeshLib::Element * > &elems, std::map< std::size_t, std::size_t > &node_id_map, MeshLib::Properties &mesh_prop)
Parses the surface information (nodes, triangles, properties)
std::string dataType2String(DataType const t)
Given a Gocad DataType this returns the appropriate string.
Definition: GocadEnums.cpp:16
std::string dataType2ShortString(DataType const t)
Given a Gocad DataType this returns the appropriate short form.
Definition: GocadEnums.cpp:41