Flow123d  JS_before_hm-1623-gd361259
msh_gmshreader.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file msh_gmshreader.cc
15  * @ingroup mesh
16  * @brief
17  * @author dalibor
18  */
19 
20 #include <istream>
21 #include <string>
22 #include <limits>
23 
24 #include "msh_gmshreader.h"
26 
27 #include "system/system.hh"
28 #include "system/tokenizer.hh"
29 #include "boost/lexical_cast.hpp"
30 
31 #include "mesh/mesh.h"
32 
33 
34 
35 using namespace std;
36 
37 
39 : BaseMeshReader(file_name)
40 {
41  tok_.set_comment_pattern( "#");
42  data_section_name_ = "$ElementData";
43  has_compatible_mesh_ = false;
45 }
46 
47 
48 
49 GmshMeshReader::~GmshMeshReader() // Tokenizer close the file automatically
50 {}
51 
52 
53 
55  using namespace boost;
56  unsigned int n_nodes;
57  MessageOut() << "- Reading nodes...";
58  tok_.set_position( Tokenizer::Position() );
59 
60  if (! tok_.skip_to("$Nodes")) THROW(ExcMissingSection() << EI_Section("$Nodes") << EI_GMSHFile(tok_.f_name()) );
61  try {
62  tok_.next_line(false);
63  n_nodes = lexical_cast<unsigned int> (*tok_);
64  mesh->init_node_vector( n_nodes );
65  if (n_nodes == 0) THROW( ExcZeroNodes() << EI_Position(tok_.position_msg()) );
66  ++tok_; // end of line
67 
68  for (unsigned int i = 0; i < n_nodes; ++i) {
69  tok_.next_line();
70 
71  unsigned int id = lexical_cast<unsigned int> (*tok_); ++tok_; // node id
72  arma::vec3 coords; // node coordinates
73  coords(0) = lexical_cast<double> (*tok_); ++tok_;
74  coords(1) = lexical_cast<double> (*tok_); ++tok_;
75  coords(2) = lexical_cast<double> (*tok_); ++tok_;
76  ++tok_; // skip mesh size parameter
77 
78  mesh->add_node(id, coords);
79  }
80 
81  } catch (bad_lexical_cast &) {
82  THROW(ExcWrongFormat() << EI_Type("number") << EI_TokenizerMsg(tok_.position_msg()) << EI_MeshFile(tok_.f_name()) );
83  }
84  MessageOut().fmt("... {} nodes read. \n", n_nodes);
85 }
86 
87 
89  using namespace boost;
90  MessageOut() << "- Reading elements...";
91 
92  if (! tok_.skip_to("$Elements")) THROW(ExcMissingSection() << EI_Section("$Elements") << EI_GMSHFile(tok_.f_name()) );
93  try {
94  tok_.next_line(false);
95  unsigned int n_elements = lexical_cast<unsigned int> (*tok_);
96  if (n_elements == 0) THROW( ExcZeroElements() << EI_Position(tok_.position_msg()) );
97  ++tok_; // end of line
98 
99  std::vector<unsigned int> node_ids; //node_ids of elements
100  node_ids.resize(4); // maximal count of nodes
101 
102  mesh->init_element_vector(n_elements);
103 
104  for (unsigned int i = 0; i < n_elements; ++i) {
105  tok_.next_line();
106  unsigned int id = lexical_cast<unsigned int>(*tok_); ++tok_;
107 
108  //get element type: supported:
109  // 1 Line (2 nodes)
110  // 2 Triangle (3 nodes)
111  // 4 Tetrahedron (4 nodes)
112  // 15 Point (1 node)
113  unsigned int type = lexical_cast<unsigned int>(*tok_); ++tok_;
114  unsigned int dim;
115  switch (type) {
116  case 1:
117  dim = 1;
118  break;
119  case 2:
120  dim = 2;
121  break;
122  case 4:
123  dim = 3;
124  break;
125  case 15:
126  dim = 0;
127  break;
128  default:
129  dim = 0;
130  THROW(ExcUnsupportedType() << EI_ElementId(id) << EI_ElementType(type) << EI_GMSHFile(tok_.f_name()) );
131  break;
132  }
133 
134  //get number of tags (at least 2)
135  unsigned int n_tags = lexical_cast<unsigned int>(*tok_);
136  if (n_tags < 2) THROW( ExcTooManyElementTags() << EI_ElementId(id) << EI_Position(tok_.position_msg()) );
137  ++tok_;
138 
139  //get tags 1 and 2
140  unsigned int region_id = lexical_cast<unsigned int>(*tok_); ++tok_; // region_id
141  lexical_cast<unsigned int>(*tok_); ++tok_; // GMSH region number, we do not store this
142  //get remaining tags
143  unsigned int partition_id = 0;
144  if (n_tags > 2) { partition_id = lexical_cast<unsigned int>(*tok_); ++tok_; } // save partition number from the new GMSH format
145  for (unsigned int ti = 3; ti < n_tags; ti++) ++tok_; //skip remaining tags
146 
147  for (unsigned int ni=0; ni<dim+1; ++ni) { // read node ids
148  node_ids[ni] = lexical_cast<unsigned int>(*tok_);
149  ++tok_;
150  }
151  mesh->add_element(id, dim, region_id, partition_id, node_ids);
152  }
153 
154  } catch (bad_lexical_cast &) {
155  THROW(ExcWrongFormat() << EI_Type("number") << EI_TokenizerMsg(tok_.position_msg()) << EI_MeshFile(tok_.f_name()) );
156  }
157 
158  unsigned int n_read_boundary = mesh->create_boundary_elements();
159  MessageOut().fmt("... {} bulk elements, {} boundary elements. \n", mesh->n_elements(), n_read_boundary);
160 }
161 
162 
163 
165  ASSERT(mesh).error("Argument mesh is NULL.\n");
166 
167  using namespace boost;
168 
169  if (! tok_.skip_to("$PhysicalNames", "$Nodes") ) return;
170  try {
171  tok_.next_line(false);
172  unsigned int n_physicals = lexical_cast<unsigned int> (*tok_);
173  ++tok_; // end of line
174 
175  for (unsigned int i = 0; i < n_physicals; ++i) {
176  tok_.next_line();
177  // format of one line:
178  // dim physical-id physical-name
179 
180  unsigned int dim = lexical_cast<unsigned int>(*tok_); ++tok_;
181  unsigned int id = lexical_cast<unsigned int>(*tok_); ++tok_;
182  string name = *tok_; ++tok_;
183  mesh->add_physical_name( dim, id, name );
184  }
185 
186  } catch (bad_lexical_cast &) {
187  THROW(ExcWrongFormat() << EI_Type("number") << EI_TokenizerMsg(tok_.position_msg()) << EI_MeshFile(tok_.f_name()) );
188  }
189 
190 }
191 
192 
193 // Is assumed to be called just after tok.skip_to("..")
194 // reads the header from the tokenizer @p tok and return it as the second parameter
196  using namespace boost;
197  try {
198  // string tags
199  tok_.next_line(false);
200  unsigned int n_str = lexical_cast<unsigned int>(*tok_); ++tok_;
201  head.field_name="";
202  head.interpolation_scheme = "";
203  if (n_str > 0) {
204  tok_.next_line(); n_str--;
205  head.field_name= *tok_; ++tok_; // unquoted by tokenizer if needed
206  }
207  if (n_str > 0) {
208  tok_.next_line(); n_str--;
209  head.interpolation_scheme = *tok_; ++tok_;
210  }
211  for(;n_str>0;n_str--) tok_.next_line(false); // skip possible remaining tags
212 
213  //real tags
214  tok_.next_line();
215  unsigned int n_real = lexical_cast<unsigned int>(*tok_); ++tok_;
216  head.time=0.0;
217  if (n_real>0) {
218  tok_.next_line(); n_real--;
219  head.time=lexical_cast<double>(*tok_); ++tok_;
220  }
221  for(;n_real>0;n_real--) tok_.next_line(false);
222 
223  // int tags
224  tok_.next_line();
225  unsigned int n_int = lexical_cast<unsigned int>(*tok_); ++tok_;
226  head.time_index=0;
227  head.n_components=1;
228  head.n_entities=0;
229  head.partition_index=0;
230  if (n_int>0) {
231  tok_.next_line(); n_int--;
232  head.time_index=lexical_cast<unsigned int>(*tok_); ++tok_;
233  }
234  if (n_int>0) {
235  tok_.next_line(); n_int--;
236  head.n_components=lexical_cast<unsigned int>(*tok_); ++tok_;
237  }
238  if (n_int>0) {
239  tok_.next_line(); n_int--;
240  head.n_entities=lexical_cast<unsigned int>(*tok_); ++tok_;
241  }
242  for(;n_int>0;n_int--) tok_.next_line(false);
243  head.position = tok_.get_position();
244  head.discretization = OutputTime::DiscreteSpace::ELEM_DATA;
245  } catch (bad_lexical_cast &) {
246  THROW(ExcWrongFormat() << EI_Type("$ElementData header") << EI_TokenizerMsg(tok_.position_msg()) << EI_MeshFile(tok_.f_name()) );
247  }
248 }
249 
250 
251 
252 void GmshMeshReader::read_element_data(ElementDataCacheBase &data_cache, MeshDataHeader actual_header, unsigned int n_components,
253  bool boundary_domain) {
254  unsigned int id, i_row;
255  unsigned int n_read = 0;
256  std::vector<int> const & el_ids = this->get_element_vector(boundary_domain);
257  vector<int>::const_iterator id_iter = el_ids.begin();
258 
259  // read @p data buffer as we have correct header with already passed time
260  // we assume that @p data buffer is big enough
261  tok_.set_position(actual_header.position);
262 
263  // read data
264  for (i_row = 0; i_row < actual_header.n_entities; ++i_row)
265  try {
266  tok_.next_line();
267  id = boost::lexical_cast<unsigned int>(*tok_); ++tok_;
268  //skip_element = false;
269  while (id_iter != el_ids.end() && *id_iter < (int)id) {
270  ++id_iter; // skip initialization of some rows in data if ID is missing
271  }
272  if (id_iter == el_ids.end()) {
273  WarningOut().fmt("In file '{}', '$ElementData' section for field '{}', time: {}.\nData ID {} not found or is not in order. Skipping rest of data.\n",
274  tok_.f_name(), actual_header.field_name, actual_header.time, id);
275  break;
276  }
277  // save data from the line if ID was found
278  if (*id_iter == (int)id) {
279  data_cache.read_ascii_data(tok_, n_components, (id_iter - el_ids.begin()) );
280  n_read++;
281  }
282  // skip the line if ID on the line < actual ID in the map el_ids
283  } catch (boost::bad_lexical_cast &) {
284  THROW(ExcWrongFormat() << EI_Type("$ElementData line") << EI_TokenizerMsg(tok_.position_msg())
285  << EI_MeshFile(tok_.f_name()) );
286  }
287  // possibly skip remaining lines after break
288  while (i_row < actual_header.n_entities) tok_.next_line(false), ++i_row;
289 
290  LogOut().fmt("time: {}; {} entities of field {} read.\n",
291  actual_header.time, n_read, actual_header.field_name);
292 }
293 
294 
295 
297 {
298  header_table_.clear();
299  MeshDataHeader header;
300  while ( !tok_.eof() ) {
301  if ( tok_.skip_to("$ElementData") ) {
302  read_data_header(header);
303  HeaderTable::iterator it = header_table_.find(header.field_name);
304 
305  if (it == header_table_.end()) { // field doesn't exists, insert new vector to map
307  vec.push_back(header);
308  header_table_[header.field_name]=vec;
309  } else if ( header.time <= it->second.back().time ) { // time is in wrong order. can't be add
310  WarningOut().fmt("Wrong time order: field '{}', time '{}', file '{}'. Skipping this '$ElementData' section.\n",
311  header.field_name, header.time, tok_.f_name() );
312  } else { // add new time step
313  it->second.push_back(header);
314  }
315  }
316  }
317 
318  tok_.set_position( Tokenizer::Position() );
319 }
320 
321 
322 
324 {
325  // check discretization, only type element_data or undefined is supported
326  if (header_query.discretization != OutputTime::DiscreteSpace::ELEM_DATA) {
327  if (header_query.discretization != OutputTime::DiscreteSpace::UNDEFINED && header_query.discretization != OutputTime::DiscreteSpace::NATIVE_DATA) {
328  WarningOut().fmt(
329  "Unsupported discretization for field '{}', time: {} and GMSH format.\nType 'ELEM_DATA' of discretization will be used.\n",
330  header_query.field_name, header_query.time);
331  }
332  header_query.discretization = OutputTime::DiscreteSpace::ELEM_DATA;
333  }
334 
335  HeaderTable::iterator table_it = header_table_.find(header_query.field_name);
336 
337  if (table_it == header_table_.end()) {
338  // no data found
339  THROW( ExcFieldNameNotFound() << EI_FieldName(header_query.field_name) << EI_MeshFile(tok_.f_name()));
340  }
341 
342  auto comp = [](double t, const MeshDataHeader &a) {
343  return t * (1.0 + 2.0*numeric_limits<double>::epsilon()) < a.time;
344  };
345 
346  std::vector<MeshDataHeader>::iterator headers_it = std::upper_bound(table_it->second.begin(),
347  table_it->second.end(),
348  header_query.time,
349  comp);
350 
351  if (headers_it == table_it->second.begin()) {
352  THROW( ExcFieldNameNotFound() << EI_FieldName(header_query.field_name)
353  << EI_MeshFile(tok_.f_name()) << EI_Time(header_query.time));
354  }
355 
356  --headers_it;
357  actual_header_ = *headers_it;
358  return actual_header_;
359 }
ArmaVec< double, N > vec
Definition: armor.hh:885
void make_header_table() override
void read_nodes(Mesh *mesh)
void read_data_header(MeshDataHeader &head)
std::string interpolation_scheme
Currently d&#39;ont used.
void init_element_vector(unsigned int size)
Initialize element_vec_, set size and reset counters of boundary and bulk elements.
Definition: mesh.cc:1210
void add_node(unsigned int node_id, arma::vec3 coords)
Add new node of given id and coordinates to mesh.
Definition: mesh.cc:1150
#define MessageOut()
Macro defining &#39;message&#39; record of log.
Definition: logger.hh:267
unsigned int create_boundary_elements()
Create boundary elements from data of temporary structure, this method MUST be call after read mesh f...
Definition: mesh.cc:1349
virtual unsigned int n_elements() const
Returns count of boundary or bulk elements.
Definition: mesh.h:361
std::string field_name
Name of field.
virtual ~GmshMeshReader()
Definition: mesh.h:77
void read_physical_names(Mesh *mesh) override
std::string data_section_name_
Store name of field data section specify for type of mesh file.
GmshMeshReader(const FilePath &file_name)
#define ASSERT(expr)
Allow use shorter versions of macro names if these names is not used with external library...
Definition: asserts.hh:347
void add_physical_name(unsigned int dim, unsigned int id, std::string name)
Add new node of given id and coordinates to mesh.
Definition: mesh.cc:1145
#define LogOut()
Macro defining &#39;log&#39; record of log.
Definition: logger.hh:273
unsigned int partition_index
Currently d&#39;ont used.
unsigned int time_index
Currently d&#39;ont used.
unsigned int n_entities
Number of rows.
virtual void read_ascii_data(Tokenizer &tok, unsigned int n_components, unsigned int i_row)=0
OutputTime::DiscreteSpace discretization
Flag marks input discretization of data of VTK file.
void read_element_data(ElementDataCacheBase &data_cache, MeshDataHeader actual_header, unsigned int n_components, bool boundary_domain) override
std::string field_name
Name of field.
Dedicated class for storing path to input and output files.
Definition: file_path.hh:54
void read_elements(Mesh *mesh)
HeaderTable header_table_
Table with data of ElementData headers.
Tokenizer::Position position
Position of data in mesh file.
OutputTime::DiscreteSpace discretization
Flag determinate type of discrete of Field (typically is used for native data of VTK) ...
double time
Time of field data (used only for GMSH reader)
unsigned int n_components
Number of values on one row.
double time
Time of field data (used only for GMSH and PVD reader)
MeshDataHeader actual_header_
Header of actual loaded data.
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:270
void add_element(unsigned int elm_id, unsigned int dim, unsigned int region_id, unsigned int partition_id, std::vector< unsigned int > node_ids)
Add new element of given id to mesh.
Definition: mesh.cc:1158
MeshDataHeader & find_header(HeaderQuery &header_query) override
Tokenizer tok_
Tokenizer used for reading ASCII file format.
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:53
std::vector< int > const & get_element_vector(bool boundary_domain)
void init_node_vector(unsigned int size)
Initialize node_vec_, set size.
Definition: mesh.cc:1224