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