Flow123d  release_2.2.0-914-gf1a3a4f
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"
25 
26 #include "system/system.hh"
27 #include "system/tokenizer.hh"
28 #include "boost/lexical_cast.hpp"
29 
30 #include "mesh/mesh.h"
31 #include "mesh/nodes.hh"
32 
33 
34 using namespace std;
35 
36 
38 : BaseMeshReader(file_name)
39 {
40  tok_.set_comment_pattern( "#");
41  data_section_name_ = "$ElementData";
42  has_compatible_mesh_ = false;
44 }
45 
46 
47 
48 GmshMeshReader::~GmshMeshReader() // Tokenizer close the file automatically
49 {}
50 
51 
52 
54  using namespace boost;
55  unsigned int n_nodes;
56  MessageOut() << "- Reading nodes...";
57  tok_.set_position( Tokenizer::Position() );
58 
59  if (! tok_.skip_to("$Nodes")) THROW(ExcMissingSection() << EI_Section("$Nodes") << EI_GMSHFile(tok_.f_name()) );
60  try {
61  tok_.next_line(false);
62  n_nodes = lexical_cast<unsigned int> (*tok_);
63  mesh->reserve_node_size( n_nodes );
64  INPUT_CHECK( n_nodes > 0, "Zero number of nodes, %s.\n", tok_.position_msg().c_str() );
65  ++tok_; // end of line
66 
67  for (unsigned int i = 0; i < n_nodes; ++i) {
68  tok_.next_line();
69 
70  unsigned int id = lexical_cast<unsigned int> (*tok_); ++tok_; // node id
71  arma::vec3 coords; // node coordinates
72  coords(0) = lexical_cast<double> (*tok_); ++tok_;
73  coords(1) = lexical_cast<double> (*tok_); ++tok_;
74  coords(2) = lexical_cast<double> (*tok_); ++tok_;
75  ++tok_; // skip mesh size parameter
76 
77  mesh->add_node(id, coords);
78  }
79 
80  } catch (bad_lexical_cast &) {
81  THROW(ExcWrongFormat() << EI_Type("number") << EI_TokenizerMsg(tok_.position_msg()) << EI_MeshFile(tok_.f_name()) );
82  }
83  MessageOut().fmt("... {} nodes read. \n", n_nodes);
84 }
85 
86 
88  using namespace boost;
89  MessageOut() << "- Reading elements...";
90 
91  if (! tok_.skip_to("$Elements")) THROW(ExcMissingSection() << EI_Section("$Elements") << EI_GMSHFile(tok_.f_name()) );
92  try {
93  tok_.next_line(false);
94  unsigned int n_elements = lexical_cast<unsigned int> (*tok_);
95  INPUT_CHECK( n_elements > 0, "Zero number of elements, %s.\n", tok_.position_msg().c_str());
96  ++tok_; // end of line
97 
98  std::vector<unsigned int> node_ids; //node_ids of elements
99  node_ids.resize(4); // maximal count of nodes
100 
101  mesh->reserve_element_size(n_elements);
102 
103  for (unsigned int i = 0; i < n_elements; ++i) {
104  tok_.next_line();
105  unsigned int id = lexical_cast<unsigned int>(*tok_); ++tok_;
106 
107  //get element type: supported:
108  // 1 Line (2 nodes)
109  // 2 Triangle (3 nodes)
110  // 4 Tetrahedron (4 nodes)
111  // 15 Point (1 node)
112  unsigned int type = lexical_cast<unsigned int>(*tok_); ++tok_;
113  unsigned int dim;
114  switch (type) {
115  case 1:
116  dim = 1;
117  break;
118  case 2:
119  dim = 2;
120  break;
121  case 4:
122  dim = 3;
123  break;
124  case 15:
125  dim = 0;
126  break;
127  default:
128  dim = 0;
129  THROW(ExcUnsupportedType() << EI_ElementId(id) << EI_ElementType(type) << EI_GMSHFile(tok_.f_name()) );
130  break;
131  }
132 
133  //get number of tags (at least 2)
134  unsigned int n_tags = lexical_cast<unsigned int>(*tok_);
135  INPUT_CHECK(n_tags >= 2, "At least two element tags have to be defined for element with id=%d, %s.\n",
136  id, tok_.position_msg().c_str());
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  mesh->n_all_input_elements_=mesh->element.size() + mesh->bc_elements.size();
159  MessageOut().fmt("... {} bulk elements, {} boundary elements. \n", mesh->element.size(), mesh->bc_elements.size());
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) {
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 }
360 
362 {
363  bulk_elements_id_.clear();
364  boundary_elements_id_.clear();
366  has_compatible_mesh_ = true;
367 }
void make_header_table() override
void read_nodes(Mesh *mesh)
void read_data_header(MeshDataHeader &head)
Nodes of a mesh.
std::string interpolation_scheme
Currently d&#39;ont used.
void add_node(unsigned int node_id, arma::vec3 coords)
Add new node of given id and coordinates to mesh.
Definition: mesh.cc:767
void check_compatible_mesh(Mesh &mesh) override
#define MessageOut()
Macro defining &#39;message&#39; record of log.
Definition: logger.hh:243
std::string field_name
Name of field.
virtual ~GmshMeshReader()
Definition: mesh.h:99
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:346
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:762
#define LogOut()
Macro defining &#39;log&#39; record of log.
Definition: logger.hh:249
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.
unsigned int size() const
Returns size of the container. This is independent of the allocated space.
Definition: sys_vector.hh:391
void elements_id_maps(vector< IdxInt > &bulk_elements_id, vector< IdxInt > &boundary_elements_id) const
Definition: mesh.cc:674
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.
ElementVector bc_elements
Definition: mesh.h:268
void reserve_element_size(unsigned int n_elements)
Reserve size of element vector.
Definition: mesh.h:180
vector< IdxInt > boundary_elements_id_
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.
#define INPUT_CHECK(i,...)
Debugging macros.
Definition: global_defs.h:51
Dedicated class for storing path to input and output files.
Definition: file_path.hh:54
const double epsilon
Definition: mathfce.h:23
void read_elements(Mesh *mesh)
HeaderTable header_table_
Table with data of ElementData headers.
unsigned int n_all_input_elements_
Number of elements read from input.
Definition: mesh.h:356
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)
vector< IdxInt > bulk_elements_id_
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:246
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:773
MeshDataHeader & find_header(HeaderQuery &header_query) override
Tokenizer tok_
Tokenizer used for reading ASCII file format.
void reserve_node_size(unsigned int n_nodes)
Reserve size of node vector.
Definition: mesh.h:175
#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)
ElementVector element
Vector of elements of the mesh.
Definition: mesh.h:260