Flow123d
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 {
54  tok_.set_comment_pattern( "#");
55  last_header.time=-numeric_limits<double>::infinity();
56  last_header.actual=false;
57 }
58 
59 
60 
62 : tok_(in)
63 {
64  tok_.set_comment_pattern( "#");
65  last_header.time=-numeric_limits<double>::infinity();
66  last_header.actual=false;
67 }
68 
69 
70 
71 GmshMeshReader::~GmshMeshReader() // Tokenizer close the file automatically
72 {}
73 
74 
75 
77  F_ENTRY;
78 
79  START_TIMER("GMSHReader - read mesh");
80 
81  ASSERT( mesh , "Argument mesh is NULL.\n");
83  read_nodes(tok_, mesh);
84  read_elements(tok_, mesh, el_to_reg_map);
85 }
86 
87 
88 
89 void GmshMeshReader::read_nodes(Tokenizer &tok, Mesh* mesh) {
90  using namespace boost;
91  xprintf(Msg, "- Reading nodes...");
92 
93  if (! tok.skip_to("$Nodes")) xprintf(UsrErr,"Missing section '$Nodes' in the GMSH input file: %s\n",tok.f_name().c_str());
94  try {
95  tok.next_line(false);
96  unsigned int n_nodes = lexical_cast<unsigned int> (*tok);;
97  INPUT_CHECK( n_nodes > 0, "Zero number of nodes, %s.\n", tok.position_msg().c_str() );
98  ++tok; // end of line
99 
100  mesh->node_vector.reserve(n_nodes);
101  for (unsigned int i = 0; i < n_nodes; ++i) {
102  tok.next_line();
103 
104  unsigned int id = lexical_cast<unsigned int> (*tok); ++tok;
105  NodeFullIter node = mesh->node_vector.add_item(id);
106 
107  node->point()(0)=lexical_cast<double> (*tok); ++tok;
108  node->point()(1)=lexical_cast<double> (*tok); ++tok;
109  node->point()(2)=lexical_cast<double> (*tok); ++tok;
110  ++tok; // skip mesh size parameter
111  }
112 
113  } catch (bad_lexical_cast &) {
114  xprintf(UsrErr, "Wrong format of number, %s.\n", tok.position_msg().c_str());
115  }
116  xprintf(Msg, " %d nodes read. \n", mesh->node_vector.size());
117 }
118 
119 
120 
121 void GmshMeshReader::read_elements(Tokenizer &tok, Mesh * mesh, const RegionDB::MapElementIDToRegionID *el_to_reg_map) {
122  using namespace boost;
123  xprintf(Msg, "- Reading elements...");
124 
125  if (! tok.skip_to("$Elements")) xprintf(UsrErr,"Missing section '$Elements' in the GMSH input file: %s\n",tok.f_name().c_str());
126  try {
127  tok.next_line(false);
128  unsigned int n_elements = lexical_cast<unsigned int> (*tok);
129  INPUT_CHECK( n_elements > 0, "Zero number of elements, %s.\n", tok.position_msg().c_str());
130  ++tok; // end of line
131 
132  mesh->element.reserve(n_elements);
133 
134  for (unsigned int i = 0; i < n_elements; ++i) {
135  tok.next_line();
136 
137  unsigned int id = lexical_cast<unsigned int>(*tok); ++tok;
138 
139 
140  //get element type: supported:
141  // 1 Line (2 nodes)
142  // 2 Triangle (3 nodes)
143  // 4 Tetrahedron (4 nodes)
144  // 15 Point (1 node)
145  unsigned int type = lexical_cast<unsigned int>(*tok); ++tok;
146  unsigned int dim;
147  switch (type) {
148  case 1:
149  dim = 1;
150  break;
151  case 2:
152  dim = 2;
153  break;
154  case 4:
155  dim = 3;
156  break;
157  case 15:
158  dim = 0;
159  break;
160  default:
161  xprintf(UsrErr, "Element %d is of the unsupported type %d\n", id, type);
162  break;
163  }
164 
165  //get number of tags (at least 2)
166  unsigned int n_tags = lexical_cast<unsigned int>(*tok);
167  INPUT_CHECK(n_tags >= 2, "At least two element tags have to be defined for element with id=%d, %s.\n",
168  id, tok.position_msg().c_str());
169  ++tok;
170 
171  //get tags 1 and 2
172  unsigned int region_id = lexical_cast<unsigned int>(*tok); ++tok;
173  unsigned int object_id = lexical_cast<unsigned int>(*tok); ++tok; // GMSH region number, we do not store this
174  //get remaining tags
175  unsigned int partition_id=0;
176  if (n_tags > 2) { partition_id = lexical_cast<unsigned int>(*tok); ++tok; } // save partition number from the new GMSH format
177  for (unsigned int ti = 3; ti < n_tags; ti++) ++tok; //skip remaining tags
178 
179  // allocate element arrays TODO: should be in mesh class
180  Element *ele;
181  // possibly modify region id
182  if (el_to_reg_map) {
183  RegionDB::MapElementIDToRegionID::const_iterator it = el_to_reg_map->find(id);
184  if (it != el_to_reg_map->end()) region_id = it->second;
185  }
186  RegionIdx region_idx = mesh->region_db_.add_region( region_id, dim );
187 
188  if (region_idx.is_boundary()) {
189  ele = mesh->bc_elements.add_item(id);
190  } else {
191  if(dim == 0 )
192  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);
193  else
194  ele = mesh->element.add_item(id);
195  }
196  ele->init(dim, mesh, region_idx);
197  ele->pid=partition_id;
198 
199  unsigned int ni;
200  FOR_ELEMENT_NODES(ele, ni) {
201  unsigned int node_id = lexical_cast<unsigned int>(*tok);
202  NodeIter node = mesh->node_vector.find_id( node_id );
203  INPUT_CHECK( node!=mesh->node_vector.end() ,
204  "Unknown node id %d in specification of element with id=%d, %s.\n",
205  node_id, id, tok.position_msg().c_str());
206  ele->node[ni] = node;
207  ++tok;
208  }
209  }
210 
211  } catch (bad_lexical_cast &) {
212  xprintf(UsrErr, "Wrong format of number, %s.\n", tok.position_msg().c_str());
213  }
214 
215  mesh->n_all_input_elements_=mesh->element.size() + mesh->bc_elements.size();
216  xprintf(Msg, " %d bulk elements, %d boundary elements. \n", mesh->element.size(), mesh->bc_elements.size());
217 }
218 
219 
220 
221 void GmshMeshReader::read_physical_names(Tokenizer &tok, Mesh * mesh) {
222  using namespace boost;
223 
224  if (! tok.skip_to("$PhysicalNames", "$Nodes") ) return;
225  try {
226  tok.next_line(false);
227  unsigned int n_physicals = lexical_cast<unsigned int> (*tok);
228  ++tok; // end of line
229 
230  for (unsigned int i = 0; i < n_physicals; ++i) {
231  tok.next_line();
232  // format of one line:
233  // dim physical-id physical-name
234 
235  unsigned int dim = lexical_cast<unsigned int>(*tok); ++tok;
236  unsigned int id = lexical_cast<unsigned int>(*tok); ++tok;
237  string name = *tok; ++tok;
238 
239  bool boundary = ( name.size() != 0 && name[0] == '.' );
240  mesh->region_db_.add_region(id, name, dim, boundary);
241  }
242 
243  } catch (bad_lexical_cast &) {
244  xprintf(UsrErr, "Wrong format of number, %s.\n", tok.position_msg().c_str());
245  }
246 }
247 
248 
249 // Is assumed to be called just after tok.skip_to("..")
250 // reads the header from the tokenizer @p tok and return it as the second parameter
252  using namespace boost;
253  try {
254  // string tags
255  tok.next_line(false);
256  unsigned int n_str = lexical_cast<unsigned int>(*tok); ++tok;
257  head.field_name="";
258  head.interpolation_scheme = "";
259  if (n_str > 0) {
260  tok.next_line(); n_str--;
261  head.field_name= *tok; ++tok; // unquoted by tokenizer if needed
262  }
263  if (n_str > 0) {
264  tok.next_line(); n_str--;
265  head.interpolation_scheme = *tok; ++tok;
266  }
267  for(;n_str>0;n_str--) tok.next_line(false); // skip possible remaining tags
268 
269  //real tags
270  tok.next_line();
271  unsigned int n_real = lexical_cast<unsigned int>(*tok); ++tok;
272  head.time=0.0;
273  if (n_real>0) {
274  tok.next_line(); n_real--;
275  head.time=lexical_cast<double>(*tok); ++tok;
276  }
277  for(;n_real>0;n_real--) tok.next_line(false);
278 
279  // int tags
280  tok.next_line();
281  unsigned int n_int = lexical_cast<unsigned int>(*tok); ++tok;
282  head.time_index=0;
283  head.n_components=1;
284  head.n_entities=0;
285  head.partition_index=0;
286  if (n_int>0) {
287  tok.next_line(); n_int--;
288  head.time_index=lexical_cast<unsigned int>(*tok); ++tok;
289  }
290  if (n_int>0) {
291  tok.next_line(); n_int--;
292  head.n_components=lexical_cast<unsigned int>(*tok); ++tok;
293  }
294  if (n_int>0) {
295  tok.next_line(); n_int--;
296  head.n_entities=lexical_cast<unsigned int>(*tok); ++tok;
297  }
298  for(;n_int>0;n_int--) tok.next_line(false);
299  } catch (bad_lexical_cast &) {
300  xprintf(UsrErr, "Wrong format of the $ElementData header, %s.\n", tok.position_msg().c_str());
301  }
302 }
303 
304 
305 
307  double *data, std::vector<int> const & el_ids)
308 {
309 
310  using namespace boost;
311 
312  unsigned int id, idx, n_read;
314  double * data_ptr;
315 
316  while ( last_header.time <= search_header.time*(1.0 + 2.0*numeric_limits<double>::epsilon()) ) {
317  // @p data buffer is not actual anymore
318 
319  if (last_header.actual) {
320  // read @p data buffer as we have correct header with already passed time
321  // we assume that @p data buffer is big enough
322 
323  n_read = 0;
324  id_iter=el_ids.begin();
325  unsigned int i_row;
326  for (i_row = 0; i_row < last_header.n_entities; ++i_row)
327  try {
328  tok_.next_line();
329 // DBGMSG("data line: %d %d '%s'\n", i_row, last_header.n_entities, tok_.line().c_str());
330  id = lexical_cast<unsigned int>(*tok_); ++tok_;
331  while (id_iter != el_ids.end() && *id_iter < (int)id) {
332 // DBGMSG("get id: %u %d\n", id, *id_iter);
333  ++id_iter; // skip initialization of some rows in data if ID is missing
334  }
335  if (id_iter == el_ids.end()) {
336  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",
337  tok_.f_name().c_str(), search_header.field_name.c_str(), last_header.time, id);
338  break;
339  }
340  // save data from the line if ID was found
341  if (*id_iter == (int)id) {
342  idx = id_iter - el_ids.begin();
343  data_ptr = data + idx * search_header.n_components;
344  for (unsigned int i_col =0; i_col < search_header.n_components; ++i_col, ++data_ptr) {
345  *(data_ptr) = lexical_cast<double>(*tok_); ++tok_;
346  }
347  n_read++;
348  }
349  // skip the line if ID on the line < actual ID in the map el_ids
350  } catch (bad_lexical_cast &) {
351  xprintf(UsrErr, "Wrong format of $ElementData line, %s.\n", tok_.position_msg().c_str());
352  }
353  // possibly skip remaining lines after break
354  while (i_row < last_header.n_entities) tok_.next_line(false), ++i_row;
355 
356  xprintf(Msg, "time: %f; %d entities of field %s read.\n",
357  last_header.time, n_read, last_header.field_name.c_str());
358 
359  search_header.actual = true; // use input header to indicate modification of @p data buffer
360  }
361 
362  // find next the data section of corresponding field name
364  while (! tok_.eof() && last_header.field_name != search_header.field_name) {
365  if ( tok_.skip_to("$ElementData") )
367  }
368 
369  if (tok_.eof()) {
370  if (! last_header.actual) {
371  // first call of the method, no data read
372  xprintf(UsrErr, "In file '%s', missing '$ElementData' section for field '%s'.\n",
373  tok_.f_name().c_str(), search_header.field_name.c_str());
374  return;
375  } else {
376  // mark data as actual until inf
377  last_header.time=numeric_limits<double>::infinity(); //
378  return;
379  }
380  } else {
381  // check that the header is valid, try to correct
382  if (last_header.n_components != search_header.n_components) {
383  xprintf(Warn, "In file '%s', '$ElementData' section for field '%s', time: %f.\nWrong number of components: %d, using %d instead.\n",
384  tok_.f_name().c_str(), search_header.field_name.c_str(), last_header.time, last_header.n_components, search_header.n_components);
385  last_header.n_components=search_header.n_components;
386  }
387  if (last_header.n_entities != search_header.n_entities) {
388  xprintf(Warn, "In file '%s', '$ElementData' section for field '%s', time: %f.\nWrong number of entities: %d, using %d instead.\n",
389  tok_.f_name().c_str(), search_header.field_name.c_str(), last_header.time, last_header.n_entities, search_header.n_entities);
390  // last_header.n_entities=search_header.n_entities;
391  }
392 
393  }
394  last_header.actual=true;
395 
396  } // time loop
397 }
398