Flow123d  intersections_paper-476-gbe68821
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/sys_profiler.hh"
28 #include "system/tokenizer.hh"
29 #include "boost/lexical_cast.hpp"
30 
31 #include "mesh/mesh.h"
32 #include "mesh/nodes.hh"
33 
34 
35 using namespace std;
36 
37 
39 : tok_(file_name)
40 {
42  tok_.set_comment_pattern( "#");
44 }
45 
46 
47 
49 : tok_(in)
50 {
52  tok_.set_comment_pattern( "#");
54 }
55 
56 
57 
58 GmshMeshReader::~GmshMeshReader() // Tokenizer close the file automatically
59 {
60  if(current_cache_ != nullptr) delete current_cache_;
61 }
62 
63 
64 
66  START_TIMER("GMSHReader - read mesh");
67 
68  OLD_ASSERT( mesh , "Argument mesh is NULL.\n");
69  tok_.set_position( Tokenizer::Position() );
70  read_nodes(mesh);
71  read_elements(mesh);
72 }
73 
74 
75 
77  using namespace boost;
78  MessageOut() << "- Reading nodes...";
79 
80  if (! tok_.skip_to("$Nodes")) THROW(ExcMissingSection() << EI_Section("$Nodes") << EI_GMSHFile(tok_.f_name()) );
81  try {
82  tok_.next_line(false);
83  unsigned int n_nodes = lexical_cast<unsigned int> (*tok_);;
84  INPUT_CHECK( n_nodes > 0, "Zero number of nodes, %s.\n", tok_.position_msg().c_str() );
85  ++tok_; // end of line
86 
87  mesh->node_vector.reserve(n_nodes);
88  for (unsigned int i = 0; i < n_nodes; ++i) {
89  tok_.next_line();
90 
91  unsigned int id = lexical_cast<unsigned int> (*tok_); ++tok_;
92  NodeFullIter node = mesh->node_vector.add_item(id);
93 
94  node->point()(0)=lexical_cast<double> (*tok_); ++tok_;
95  node->point()(1)=lexical_cast<double> (*tok_); ++tok_;
96  node->point()(2)=lexical_cast<double> (*tok_); ++tok_;
97  ++tok_; // skip mesh size parameter
98  }
99 
100  } catch (bad_lexical_cast &) {
101  THROW(ExcWrongFormat() << EI_Type("number") << EI_TokenizerMsg(tok_.position_msg()) << EI_GMSHFile(tok_.f_name()) );
102  }
103  MessageOut().fmt("... {} nodes read. \n", mesh->node_vector.size());
104 }
105 
106 
107 
109  using namespace boost;
110  MessageOut() << "- Reading elements...";
111 
112  if (! tok_.skip_to("$Elements")) THROW(ExcMissingSection() << EI_Section("$Elements") << EI_GMSHFile(tok_.f_name()) );
113  try {
114  tok_.next_line(false);
115  unsigned int n_elements = lexical_cast<unsigned int> (*tok_);
116  INPUT_CHECK( n_elements > 0, "Zero number of elements, %s.\n", tok_.position_msg().c_str());
117  ++tok_; // end of line
118 
119  mesh->element.reserve(n_elements);
120 
121  for (unsigned int i = 0; i < n_elements; ++i) {
122  tok_.next_line();
123 
124  unsigned int id = lexical_cast<unsigned int>(*tok_); ++tok_;
125 
126 
127  //get element type: supported:
128  // 1 Line (2 nodes)
129  // 2 Triangle (3 nodes)
130  // 4 Tetrahedron (4 nodes)
131  // 15 Point (1 node)
132  unsigned int type = lexical_cast<unsigned int>(*tok_); ++tok_;
133  unsigned int dim;
134  switch (type) {
135  case 1:
136  dim = 1;
137  break;
138  case 2:
139  dim = 2;
140  break;
141  case 4:
142  dim = 3;
143  break;
144  case 15:
145  dim = 0;
146  break;
147  default:
148  dim = 0;
149  THROW(ExcUnsupportedType() << EI_ElementId(id) << EI_ElementType(type) << EI_GMSHFile(tok_.f_name()) );
150  break;
151  }
152 
153  //get number of tags (at least 2)
154  unsigned int n_tags = lexical_cast<unsigned int>(*tok_);
155  INPUT_CHECK(n_tags >= 2, "At least two element tags have to be defined for element with id=%d, %s.\n",
156  id, tok_.position_msg().c_str());
157  ++tok_;
158 
159  //get tags 1 and 2
160  unsigned int region_id = lexical_cast<unsigned int>(*tok_); ++tok_;
161  lexical_cast<unsigned int>(*tok_); ++tok_; // GMSH region number, we do not store this
162  //get remaining tags
163  unsigned int partition_id=0;
164  if (n_tags > 2) { partition_id = lexical_cast<unsigned int>(*tok_); ++tok_; } // save partition number from the new GMSH format
165  for (unsigned int ti = 3; ti < n_tags; ti++) ++tok_; //skip remaining tags
166 
167  // allocate element arrays TODO: should be in mesh class
168  Element *ele=nullptr;
169  RegionIdx region_idx = mesh->region_db_.get_region( region_id, dim );
170  if ( !region_idx.is_valid() ) {
171  region_idx = mesh->region_db_.add_region( region_id, mesh->region_db_.create_label_from_id(region_id),
172  dim, "$Element" );
173  }
174  mesh->region_db_.mark_used_region(region_idx.idx());
175 
176  if (region_idx.is_boundary()) {
177  ele = mesh->bc_elements.add_item(id);
178  } else {
179  if(dim == 0 )
180  WarningOut().fmt("Bulk elements of zero size(dim=0) are not supported. Mesh file: {}, Element ID: {}.\n", tok_.f_name() ,id);
181  else
182  ele = mesh->element.add_item(id);
183  }
184  ele->init(dim, mesh, region_idx);
185  ele->pid=partition_id;
186 
187  unsigned int ni;
188  FOR_ELEMENT_NODES(ele, ni) {
189  unsigned int node_id = lexical_cast<unsigned int>(*tok_);
190  NodeIter node = mesh->node_vector.find_id( node_id );
191  INPUT_CHECK( node!=mesh->node_vector.end() ,
192  "Unknown node id %d in specification of element with id=%d, %s.\n",
193  node_id, id, tok_.position_msg().c_str());
194  ele->node[ni] = node;
195  ++tok_;
196  }
197 
198  // check that tetrahedron element is numbered correctly and is not degenerated
199  if(ele->dim() == 3)
200  {
201  double jac = ele->tetrahedron_jacobian();
202  if( ! (jac > 0) )
203  WarningOut().fmt("Tetrahedron element with id {} has wrong numbering or is degenerated (Jacobian = {}).",ele->index(),jac);
204  }
205  }
206 
207  } catch (bad_lexical_cast &) {
208  THROW(ExcWrongFormat() << EI_Type("number") << EI_TokenizerMsg(tok_.position_msg()) << EI_GMSHFile(tok_.f_name()) );
209  }
210 
211  mesh->n_all_input_elements_=mesh->element.size() + mesh->bc_elements.size();
212  MessageOut().fmt("... {} bulk elements, {} boundary elements. \n", mesh->element.size(), mesh->bc_elements.size());
213 }
214 
215 
216 
218  OLD_ASSERT( mesh , "Argument mesh is NULL.\n");
219  using namespace boost;
220 
221  if (! tok_.skip_to("$PhysicalNames", "$Nodes") ) return;
222  try {
223  tok_.next_line(false);
224  unsigned int n_physicals = lexical_cast<unsigned int> (*tok_);
225  ++tok_; // end of line
226 
227  for (unsigned int i = 0; i < n_physicals; ++i) {
228  tok_.next_line();
229  // format of one line:
230  // dim physical-id physical-name
231 
232  unsigned int dim = lexical_cast<unsigned int>(*tok_); ++tok_;
233  unsigned int id = lexical_cast<unsigned int>(*tok_); ++tok_;
234  string name = *tok_; ++tok_;
235 
236  mesh->region_db_.add_region(id, name, dim, "$PhysicalNames");
237  }
238 
239  } catch (bad_lexical_cast &) {
240  THROW(ExcWrongFormat() << EI_Type("number") << EI_TokenizerMsg(tok_.position_msg()) << EI_GMSHFile(tok_.f_name()) );
241  }
242 }
243 
244 
245 // Is assumed to be called just after tok.skip_to("..")
246 // reads the header from the tokenizer @p tok and return it as the second parameter
248  using namespace boost;
249  try {
250  // string tags
251  tok_.next_line(false);
252  unsigned int n_str = lexical_cast<unsigned int>(*tok_); ++tok_;
253  head.field_name="";
254  head.interpolation_scheme = "";
255  if (n_str > 0) {
256  tok_.next_line(); n_str--;
257  head.field_name= *tok_; ++tok_; // unquoted by tokenizer if needed
258  }
259  if (n_str > 0) {
260  tok_.next_line(); n_str--;
261  head.interpolation_scheme = *tok_; ++tok_;
262  }
263  for(;n_str>0;n_str--) tok_.next_line(false); // skip possible remaining tags
264 
265  //real tags
266  tok_.next_line();
267  unsigned int n_real = lexical_cast<unsigned int>(*tok_); ++tok_;
268  head.time=0.0;
269  if (n_real>0) {
270  tok_.next_line(); n_real--;
271  head.time=lexical_cast<double>(*tok_); ++tok_;
272  }
273  for(;n_real>0;n_real--) tok_.next_line(false);
274 
275  // int tags
276  tok_.next_line();
277  unsigned int n_int = lexical_cast<unsigned int>(*tok_); ++tok_;
278  head.time_index=0;
279  head.n_components=1;
280  head.n_entities=0;
281  head.partition_index=0;
282  if (n_int>0) {
283  tok_.next_line(); n_int--;
284  head.time_index=lexical_cast<unsigned int>(*tok_); ++tok_;
285  }
286  if (n_int>0) {
287  tok_.next_line(); n_int--;
288  head.n_components=lexical_cast<unsigned int>(*tok_); ++tok_;
289  }
290  if (n_int>0) {
291  tok_.next_line(); n_int--;
292  head.n_entities=lexical_cast<unsigned int>(*tok_); ++tok_;
293  }
294  for(;n_int>0;n_int--) tok_.next_line(false);
295  head.position = tok_.get_position();
296  } catch (bad_lexical_cast &) {
297  THROW(ExcWrongFormat() << EI_Type("$ElementData header") << EI_TokenizerMsg(tok_.position_msg()) << EI_GMSHFile(tok_.f_name()) );
298  }
299 }
300 
301 
302 
303 template<typename T>
305  std::vector<int> const & el_ids, unsigned int component_idx)
306 {
307  using namespace boost;
308 
309  GMSH_DataHeader actual_header = find_header(search_header.time, search_header.field_name);
310  if ( !current_cache_->is_actual(actual_header.time, search_header.field_name) ) {
311 
312  unsigned int id, idx, i_row;
313  unsigned int n_read = 0;
314  unsigned int size_of_cache; // count of vectors stored in cache
315  vector<int>::const_iterator id_iter = el_ids.begin();
316 
317  // check that the header is valid, try to correct
318  if (actual_header.n_entities != search_header.n_entities) {
319  WarningOut().fmt("In file '{}', '$ElementData' section for field '{}', time: {}.\nWrong number of entities: {}, using {} instead.\n",
320  tok_.f_name(), search_header.field_name, actual_header.time, actual_header.n_entities, search_header.n_entities);
321  // actual_header.n_entities=search_header.n_entities;
322  }
323 
324  if (search_header.n_components == 1) {
325  // read for MultiField to 'n_comp' vectors
326  // or for Field if ElementData contains only one value
327  size_of_cache = actual_header.n_components;
328  }
329  else {
330  // read for Field if more values is stored to one vector
331  size_of_cache = 1;
332  if (actual_header.n_components != search_header.n_components) {
333  WarningOut().fmt("In file '{}', '$ElementData' section for field '{}', time: {}.\nWrong number of components: {}, using {} instead.\n",
334  tok_.f_name(), search_header.field_name, actual_header.time, actual_header.n_components, search_header.n_components);
335  actual_header.n_components=search_header.n_components;
336  }
337  }
338 
339  // create vector of shared_ptr for cache
340  typename ElementDataCache<T>::CacheData data_cache(size_of_cache);
341  for (unsigned int i=0; i<size_of_cache; ++i) {
342  typename ElementDataCache<T>::ComponentDataPtr row_vec = std::make_shared<std::vector<T>>();
343  row_vec->resize(search_header.n_components*search_header.n_entities);
344  data_cache[i] = row_vec;
345  }
346 
347  // read @p data buffer as we have correct header with already passed time
348  // we assume that @p data buffer is big enough
349  tok_.set_position(actual_header.position);
350 
351  // read data
352  for (i_row = 0; i_row < actual_header.n_entities; ++i_row)
353  try {
354  tok_.next_line();
355  id = lexical_cast<unsigned int>(*tok_); ++tok_;
356  //skip_element = false;
357  while (id_iter != el_ids.end() && *id_iter < (int)id) {
358  ++id_iter; // skip initialization of some rows in data if ID is missing
359  }
360  if (id_iter == el_ids.end()) {
361  WarningOut().fmt("In file '{}', '$ElementData' section for field '{}', time: {}.\nData ID {} not found or is not in order. Skipping rest of data.\n",
362  tok_.f_name(), search_header.field_name, actual_header.time, id);
363  break;
364  }
365  // save data from the line if ID was found
366  if (*id_iter == (int)id) {
367  for (unsigned int i_vec=0; i_vec<size_of_cache; ++i_vec) {
368  idx = (id_iter - el_ids.begin()) * search_header.n_components;
369  std::vector<T> &vec = *( data_cache[i_vec].get() );
370  for (unsigned int i_col=0; i_col < search_header.n_components; ++i_col, ++idx) {
371  vec[idx] = lexical_cast<T>(*tok_);
372  ++tok_;
373  }
374  }
375  n_read++;
376  }
377  // skip the line if ID on the line < actual ID in the map el_ids
378  } catch (bad_lexical_cast &) {
379  THROW(ExcWrongFormat() << EI_Type("$ElementData line") << EI_TokenizerMsg(tok_.position_msg())
380  << EI_GMSHFile(tok_.f_name()) );
381  }
382  // possibly skip remaining lines after break
383  while (i_row < actual_header.n_entities) tok_.next_line(false), ++i_row;
384 
385  LogOut().fmt("time: {}; {} entities of field {} read.\n",
386  actual_header.time, n_read, actual_header.field_name);
387 
388  search_header.actual = true; // use input header to indicate modification of @p data buffer
389 
390  // set new cache
391  delete current_cache_;
392  current_cache_ = new ElementDataCache<T>(actual_header.time, actual_header.field_name, data_cache);
393  }
394 
395  if (component_idx == std::numeric_limits<unsigned int>::max()) component_idx = 0;
396  return static_cast< ElementDataCache<T> *>(current_cache_)->get_component_data(component_idx);
397 }
398 
399 
400 
402 {
403  header_table_.clear();
404  GMSH_DataHeader header;
405  while ( !tok_.eof() ) {
406  if ( tok_.skip_to("$ElementData") ) {
407  read_data_header(header);
408  HeaderTable::iterator it = header_table_.find(header.field_name);
409 
410  if (it == header_table_.end()) { // field doesn't exists, insert new vector to map
412  vec.push_back(header);
413  header_table_[header.field_name]=vec;
414  } else if ( header.time <= it->second.back().time ) { // time is in wrong order. can't be add
415  WarningOut().fmt("Wrong time order: field '{}', time '{}', file '{}'. Skipping this '$ElementData' section.\n",
416  header.field_name, header.time, tok_.f_name() );
417  } else { // add new time step
418  it->second.push_back(header);
419  }
420  }
421  }
422 
423  tok_.set_position( Tokenizer::Position() );
424 }
425 
426 
427 
428 GMSH_DataHeader & GmshMeshReader::find_header(double time, std::string field_name)
429 {
430  HeaderTable::iterator table_it = header_table_.find(field_name);
431 
432  if (table_it == header_table_.end()) {
433  // no data found
434  THROW( ExcFieldNameNotFound() << EI_FieldName(field_name) << EI_GMSHFile(tok_.f_name()));
435  }
436 
437  auto comp = [](double t, const GMSH_DataHeader &a) {
438  return t * (1.0 + 2.0*numeric_limits<double>::epsilon()) < a.time;
439  };
440 
441  std::vector<GMSH_DataHeader>::iterator headers_it = std::upper_bound(table_it->second.begin(),
442  table_it->second.end(),
443  time,
444  comp);
445 
446  if (headers_it == table_it->second.begin()) {
447  THROW( ExcFieldNameNotFound() << EI_FieldName(field_name)
448  << EI_GMSHFile(tok_.f_name()) << EI_Time(time));
449  }
450 
451  --headers_it;
452  return *headers_it;
453 }
454 
455 
456 // explicit instantiation of template methods
457 #define READER_GET_ELEMENT_DATA(TYPE) \
458 template typename ElementDataCache<TYPE>::ComponentDataPtr GmshMeshReader::get_element_data<TYPE>(GMSH_DataHeader &search_header, \
459  std::vector<int> const & el_ids, unsigned int component_idx)
460 
462 READER_GET_ELEMENT_DATA(unsigned int);
void read_elements(Mesh *)
std::string field_name
#define READER_GET_ELEMENT_DATA(TYPE)
#define FOR_ELEMENT_NODES(i, j)
Definition: elements.h:155
std::shared_ptr< std::vector< T > > ComponentDataPtr
Tokenizer::Position position
Position of data in mesh file.
bool is_boundary() const
Returns true if it is a Boundary region and false if it is a Bulk region.
Definition: region.hh:73
Definition: nodes.hh:32
Nodes of a mesh.
int pid
Definition: elements.h:87
#define MessageOut()
Macro defining &#39;message&#39; record of log.
Definition: logger.hh:241
unsigned int index() const
Definition: mesh.h:95
double tetrahedron_jacobian() const
Definition: elements.cc:115
void reserve(unsigned int size)
Reallocates the container space.
Definition: sys_vector.hh:469
string create_label_from_id(unsigned int id) const
Definition: region.cc:337
Node ** node
Definition: elements.h:90
FullIter add_item(int id)
Definition: sys_vector.hh:359
GmshMeshReader(const FilePath &file_name)
FullIter find_id(const int id)
Definition: sys_vector.hh:434
void read_data_header(GMSH_DataHeader &head)
ElementDataCacheBase * current_cache_
Cache with last read element data.
GMSH_DataHeader & find_header(double time, std::string field_name)
#define LogOut()
Macro defining &#39;log&#39; record of log.
Definition: logger.hh:247
Region get_region(unsigned int id, unsigned int dim)
Definition: region.cc:150
unsigned int dim() const
ElementDataCache< T >::ComponentDataPtr get_element_data(GMSH_DataHeader &search_header, std::vector< int > const &el_ids, unsigned int component_idx)
unsigned int size() const
Returns size of the container. This is independent of the allocated space.
Definition: sys_vector.hh:391
#define OLD_ASSERT(...)
Definition: global_defs.h:131
unsigned int n_entities
Number of rows.
void make_header_table()
#define START_TIMER(tag)
Starts a timer with specified tag.
ElementVector bc_elements
Definition: mesh.h:243
unsigned int n_components
Number of values on one row.
void read_mesh(Mesh *mesh)
#define INPUT_CHECK(i,...)
Debugging macros.
Definition: global_defs.h:51
void init(unsigned int dim, Mesh *mesh_in, RegionIdx reg)
Definition: elements.cc:60
Dedicated class for storing path to input and output files.
Definition: file_path.hh:48
void mark_used_region(unsigned int idx)
Definition: region.cc:235
const double epsilon
Definition: mathfce.h:23
Region add_region(unsigned int id, const std::string &label, unsigned int dim, const std::string &address="implicit")
Definition: region.cc:85
HeaderTable header_table_
Table with data of ElementData headers.
unsigned int n_all_input_elements_
Number of elements read from input.
Definition: mesh.h:380
void read_physical_names(Mesh *mesh)
RegionDB region_db_
Definition: mesh.h:389
void read_nodes(Mesh *)
#define WarningOut()
Macro defining &#39;warning&#39; record of log.
Definition: logger.hh:244
std::string interpolation_scheme
Currently ont used.
bool is_actual(double time, std::string quantity_name)
Check if cache stored actual data.
bool is_valid() const
Returns false if the region has undefined/invalid value.
Definition: region.hh:77
unsigned int time_index
Currently ont used.
FullIter end()
Returns FullFullIterer of the fictions past the end element.
Definition: sys_vector.hh:387
unsigned int partition_index
?? Currently ont used
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:45
Tokenizer tok_
Tokenizer used for reading ASCII GMSH file format.
NodeVector node_vector
Vector of nodes of the mesh.
Definition: mesh.h:233
ElementVector element
Vector of elements of the mesh.
Definition: mesh.h:235
unsigned int idx() const
Returns a global index of the region.
Definition: region.hh:81