Chaste  Release::3.4
TrianglesMeshReader.cpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright notice,
15  this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright notice,
17  this list of conditions and the following disclaimer in the documentation
18  and/or other materials provided with the distribution.
19  * Neither the name of the University of Oxford nor the names of its
20  contributors may be used to endorse or promote products derived from this
21  software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 #include <cassert>
36 #include <sstream>
37 #include <iostream>
38 
39 #include "TrianglesMeshReader.hpp"
40 #include "Exception.hpp"
41 
42 static const char* NODES_FILE_EXTENSION = ".node";
43 static const char* ELEMENTS_FILE_EXTENSION = ".ele";
44 static const char* FACES_FILE_EXTENSION = ".face";
45 static const char* EDGES_FILE_EXTENSION = ".edge";
46 static const char* NCL_FILE_EXTENSION = ".ncl";
47 static const char* CABLE_FILE_EXTENSION = ".cable";
48 
50 // Implementation
52 
53 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
55  unsigned orderOfElements,
56  unsigned orderOfBoundaryElements,
57  bool readContainingElementForBoundaryElements)
58  : mFilesBaseName(pathBaseName),
59  mNodeItemWidth(0),
60  mElementItemWidth(0),
61  mFaceItemWidth(0),
62  mNumNodes(0),
63  mNumElements(0),
64  mNumFaces(0),
65  mNumCableElements(0),
66  mNodesRead(0),
67  mElementsRead(0),
68  mCableElementsRead(0),
69  mFacesRead(0),
70  mBoundaryFacesRead(0),
71  mNclItemsRead(0),
72  mNumNodeAttributes(0),
73  mNumElementAttributes(0),
74  mNumFaceAttributes(0),
75  mNumCableElementAttributes(0),
76  mOrderOfElements(orderOfElements),
77  mOrderOfBoundaryElements(orderOfBoundaryElements),
78  mEofException(false),
79  mReadContainingElementOfBoundaryElement(readContainingElementForBoundaryElements),
80  mFilesAreBinary(false),
81  mMeshIsHexahedral(false),
82  mNodeFileReadBuffer(NULL),
83  mElementFileReadBuffer(NULL),
84  mFaceFileReadBuffer(NULL),
85  mNodePermutationDefined(false)
86 {
87  // Only linear and quadratic elements
88  assert(orderOfElements==1 || orderOfElements==2);
90  {
91  EXCEPTION("Boundary element file should not have containing element info if it is quadratic");
92  }
93  if (mOrderOfElements==1)
94  {
95  mNodesPerElement = ELEMENT_DIM+1;
96  }
97  else
98  {
99  #define COVERAGE_IGNORE
100  assert(SPACE_DIM==ELEMENT_DIM);
101  #undef COVERAGE_IGNORE
102  mNodesPerElement = (ELEMENT_DIM+1)*(ELEMENT_DIM+2)/2;
103  }
104 
106  {
107  mNodesPerBoundaryElement = ELEMENT_DIM;
108  }
109  else
110  {
111  #define COVERAGE_IGNORE
112  assert(SPACE_DIM==ELEMENT_DIM);
113  #undef COVERAGE_IGNORE
114  mNodesPerBoundaryElement = ELEMENT_DIM*(ELEMENT_DIM+1)/2;
115  }
116 
117  mIndexFromZero = false; // Initially assume that nodes are not numbered from zero
118 
119  OpenFiles();
120  ReadHeaders();
121 }
122 
123 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
125 {
126  delete[] mNodeFileReadBuffer;
127  delete[] mElementFileReadBuffer;
128  delete[] mFaceFileReadBuffer;
129 }
130 
131 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
133 {
134  return mNumElements;
135 }
136 
137 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
139 {
140  return mNumNodes;
141 }
142 
143 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
145 {
146  return mNumFaces;
147 }
148 
149 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
151 {
152  return mNumCableElements;
153 }
154 
155 
156 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
158 {
159  return mNumElementAttributes;
160 }
161 
162 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
164 {
165  return mNumFaceAttributes;
166 }
167 
168 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
170 {
171  return mNumCableElementAttributes;
172 }
173 
174 
175 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
177 {
178  CloseFiles();
179 
180  mNodesRead = 0;
181  mElementsRead = 0;
182  mFacesRead = 0;
183  mBoundaryFacesRead = 0;
184  mCableElementsRead = 0;
185  mNclItemsRead = 0;
186  mEofException = false;
187 
188  OpenFiles();
189  ReadHeaders();
190 }
191 
192 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
194 {
195  std::vector<double> ret_coords(SPACE_DIM);
196 
197  mNodeAttributes.clear(); // clear attributes for this node
198  GetNextItemFromStream(mNodesFile, mNodesRead, ret_coords, mNumNodeAttributes, mNodeAttributes);
199 
200  mNodesRead++;
201  return ret_coords;
202 }
203 
204 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
206 {
207  ElementData element_data;
208  element_data.NodeIndices.resize(mNodesPerElement);
209  element_data.AttributeValue = 0.0; // If an attribute is not read this stays as zero, otherwise overwritten.
210 
211  std::vector<double> element_attributes;
212  GetNextItemFromStream(mElementsFile, mElementsRead, element_data.NodeIndices, mNumElementAttributes, element_attributes);
213 
214  if (mNumElementAttributes > 0)
215  {
216  element_data.AttributeValue = element_attributes[0];
217  }
218 
219  EnsureIndexingFromZero(element_data.NodeIndices);
220 
221  mElementsRead++;
222 
223  if (mNodePermutationDefined)
224  {
225  for (std::vector<unsigned>::iterator node_it = element_data.NodeIndices.begin();
226  node_it != element_data.NodeIndices.end();
227  ++ node_it)
228  {
229  assert(*node_it < mPermutationVector.size());
230  *node_it = mPermutationVector[*node_it];
231  }
232  }
233 
234  return element_data;
235 }
236 
237 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
239 {
240  ElementData element_data;
241  element_data.NodeIndices.resize(2u);
242  element_data.AttributeValue = 0; // If an attribute is not read this stays as zero, otherwise overwritten.
243 
244  std::vector<double> cable_element_attributes;
245  GetNextItemFromStream(mCableElementsFile, mCableElementsRead, element_data.NodeIndices, mNumCableElementAttributes, cable_element_attributes);
246  if (mNumCableElementAttributes > 0)
247  {
248  element_data.AttributeValue = cable_element_attributes[0];
249  }
250 
251  EnsureIndexingFromZero(element_data.NodeIndices);
252 
253  mCableElementsRead++;
254 
255  // Node permutation can only be done with binary data...
256 // if (mNodePermutationDefined)
257 // {
258 // for (std::vector<unsigned>::iterator node_it = element_data.NodeIndices.begin();
259 // node_it != element_data.NodeIndices.end();
260 // ++ node_it)
261 // {
262 // assert(*node_it < mPermutationVector.size());
263 // *node_it = mPermutationVector[*node_it];
264 // }
265 // }
266 
267  return element_data;
268 }
269 
270 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
272 {
273  ElementData face_data;
274  std::vector<unsigned> ret_indices;
275 
276  // In the first case there's no file, all the nodes are set as faces
277  if (ELEMENT_DIM == 1)
278  {
279  ret_indices.push_back( mOneDimBoundary[mBoundaryFacesRead] );
280  }
281  else
282  {
283  ret_indices.resize(mNodesPerBoundaryElement);
284 
285  assert(ELEMENT_DIM != 0); //Covered in earlier exception, but needed in loop guard here.
286  do
287  {
288  face_data.AttributeValue = 1.0; // If an attribute is not read this stays as one, otherwise overwritten.
289 
290  std::vector<double> face_attributes; //will store face attributes, if any
291  if (mReadContainingElementOfBoundaryElement)
292  {
293  assert(mNumFaceAttributes == 0);
294  GetNextItemFromStream(mFacesFile, mFacesRead, ret_indices, 1, face_attributes);
295 
296  if (face_attributes.size() > 0)
297  {
298  face_data.ContainingElement = (unsigned) face_attributes[0];// only one face attribute registered for the moment
299  }
300 
301  }
302  else
303  {
304  GetNextItemFromStream(mFacesFile, mFacesRead, ret_indices, mNumFaceAttributes,
305  face_attributes);
306 
307  if (mNumFaceAttributes > 0)
308  {
309  face_data.AttributeValue = face_attributes[0]; //only one face attribute registered for the moment
310  }
311  }
312 
313  EnsureIndexingFromZero(ret_indices);
314 
315  mFacesRead++;
316  }
317  while (ELEMENT_DIM==2 && face_data.AttributeValue==0.0); //In triangles format we ignore internal edges (which are marked with attribute 0)
318  }
319 
320  mBoundaryFacesRead++;
321 
322  if (mNodePermutationDefined)
323  {
324  for (std::vector<unsigned>::iterator node_it = ret_indices.begin();
325  node_it != ret_indices.end();
326  ++ node_it)
327  {
328  assert(*node_it < mPermutationVector.size());
329  *node_it = mPermutationVector[*node_it];
330  }
331  }
332 
333  face_data.NodeIndices = ret_indices;
334 
335  return face_data;
336 }
337 
338 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
339 std::vector<double> TrianglesMeshReader<ELEMENT_DIM, SPACE_DIM>::GetNode(unsigned index)
340 {
341  if (!mFilesAreBinary)
342  {
343  EXCEPTION("Random access is only implemented in mesh readers for binary mesh files.");
344  }
345  if (index >= mNumNodes)
346  {
347  EXCEPTION("Node does not exist - not enough nodes.");
348  }
349 
350  if (mNodePermutationDefined)
351  {
352  assert(index<mInversePermutationVector.size());
353  index = mInversePermutationVector[index];
354  }
355 
356  // Put the file stream pointer to the right location
357  if ( index > mNodesRead )
358  {
359  // This is a monotonic (but non-contiguous) read. Let's assume that it's more efficient
360  // to seek from the current position rather than from the start of the file
361  mNodesFile.seekg( mNodeItemWidth*(index-mNodesRead), std::ios_base::cur);
362  }
363  else if ( mNodesRead != index )
364  {
365  mNodesFile.seekg(mNodeFileDataStart + mNodeItemWidth*index, std::ios_base::beg);
366  }
367 
368  mNodesRead = index; // Allow GetNextNode() to note the position of the item after this one
369  // Read the next item.
370  return GetNextNode();
371 }
372 
373 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
375 {
376  if (!mFilesAreBinary)
377  {
378  EXCEPTION("Random access is only implemented in mesh readers for binary mesh files.");
379  }
380  if (index >= mNumElements)
381  {
382  EXCEPTION("Element " << index << " does not exist - not enough elements (only " << mNumElements << ").");
383  }
384 
385  // Put the file stream pointer to the right location
386  if ( index > mElementsRead )
387  {
388  // This is a monotonic (but non-contiguous) read. Let's assume that it's more efficient
389  // to seek from the current position rather than from the start of the file
390  mElementsFile.seekg( mElementItemWidth*(index-mElementsRead), std::ios_base::cur);
391  }
392  else if ( mElementsRead != index )
393  {
394  mElementsFile.seekg(mElementFileDataStart + mElementItemWidth*index, std::ios_base::beg);
395  }
396 
397  mElementsRead = index; // Allow GetNextElementData() to note the position of the item after this one
398  // Read the next item.
399  return GetNextElementData();
400 }
401 
402 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
404 {
405  if (!mFilesAreBinary)
406  {
407  EXCEPTION("Random access is only implemented in mesh readers for binary mesh files.");
408  }
409  if (index >=mNumFaces)
410  {
411  EXCEPTION("Face does not exist - not enough faces.");
412  }
413 
414  /*
415  *
416  if ( index > mFacesRead )
417  {
418  // This would be a monotonic (but non-contiguous) read. But we don't actually read faces with this access pattern.
420  mFacesFile.seekg( mFaceItemWidth*(index-mFacesRead), std::ios_base::cur);
421  }
422  else
423  */
424  // Put the file stream pointer to the right location
425  if ( mFacesRead != index )
426  {
427  mFacesFile.seekg(mFaceFileDataStart + mFaceItemWidth*index, std::ios_base::beg);
428  }
429  mFacesRead = index; // Allow next call to mark the position in the file stream
430 
431  // Read the next item
432  return GetNextFaceData();
433 }
434 
435 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
437 {
438  if (!mFilesAreBinary)
439  {
440  EXCEPTION("NCL file functionality is only implemented in mesh readers for binary mesh files.");
441  }
442 
443  if (!mNclFileAvailable)
444  {
445  EXCEPTION("No NCL file available for this mesh.");
446  }
447  if (index >= mNumNodes)
448  {
449  EXCEPTION("Connectivity list does not exist - not enough nodes.");
450  }
451 
452  if (mNodePermutationDefined)
453  {
454  assert(index < mInversePermutationVector.size());
455  index = mInversePermutationVector[index];
456  }
457 
458  // Put the file stream pointer to the right location
459  if ( index > mNclItemsRead )
460  {
461  // This is a monotonic (but non-contiguous) read. Let's assume that it's more efficient
462  // to seek from the current position rather than from the start of the file
463  mNclFile.seekg( mNclItemWidth*(index-mNclItemsRead), std::ios_base::cur);
464  }
465  else if ( mNclItemsRead != index )
466  {
467  mNclFile.seekg(mNclFileDataStart + mNclItemWidth*index, std::ios_base::beg);
468  }
469 
470  // Read the next item
471  std::vector<unsigned> containing_element_indices;
472  containing_element_indices.resize(mMaxContainingElements);
473 
474  std::vector<double> dummy; // unused here
475  GetNextItemFromStream(mNclFile, index, containing_element_indices, 0, dummy);
476  mNclItemsRead = index + 1; //Ready for the next call
477 
478  EnsureIndexingFromZero(containing_element_indices);
479 
480  unsigned num_containing_elements = mMaxContainingElements;
481  while ( containing_element_indices[num_containing_elements-1] == UINT_MAX )
482  {
483  num_containing_elements--;
484  }
485 
486  containing_element_indices.resize(num_containing_elements);
487 
488  return containing_element_indices;
489 }
490 
491 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
493 {
494  OpenNodeFile();
495  OpenElementsFile();
496  OpenFacesFile();
497  OpenNclFile();
498  OpenCableElementsFile();
499 }
500 
501 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
503 {
504  // Nodes definition
505  std::string file_name = mFilesBaseName + NODES_FILE_EXTENSION;
506  mNodesFile.open(file_name.c_str(), std::ios::binary);
507  if (!mNodesFile.is_open())
508  {
509  EXCEPTION("Could not open data file: " + file_name);
510  }
511 }
512 
513 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
515 {
516  // Elements definition
517  std::string file_name;
518  if (ELEMENT_DIM == SPACE_DIM)
519  {
520  file_name = mFilesBaseName + ELEMENTS_FILE_EXTENSION;
521  }
522  else
523  {
524  if (ELEMENT_DIM == 1)
525  {
526  file_name = mFilesBaseName + EDGES_FILE_EXTENSION;
527  }
528  else if (ELEMENT_DIM == 2)
529  {
530  file_name = mFilesBaseName + FACES_FILE_EXTENSION;
531  }
532  else
533  {
534  EXCEPTION("Can't have a zero-dimensional mesh in a one-dimensional space");
535  }
536  }
537 
538  mElementsFile.open(file_name.c_str(), std::ios::binary);
539  if (!mElementsFile.is_open())
540  {
541  EXCEPTION("Could not open data file: " + file_name);
542  }
543 }
544 
545 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
547 {
548  // Faces/edges definition
549  std::string file_name;
550  if (ELEMENT_DIM == 3)
551  {
552  file_name = mFilesBaseName + FACES_FILE_EXTENSION;
553  }
554  else if (ELEMENT_DIM == 2)
555  {
556  file_name = mFilesBaseName + EDGES_FILE_EXTENSION;
557  }
558  else //if (ELEMENT_DIM == 1)
559  {
560  // There is no file, data will be read from the node file (with boundaries marked)
561  return;
562  }
563 
564  mFacesFile.open(file_name.c_str(), std::ios::binary);
565  if (!mFacesFile.is_open())
566  {
567  EXCEPTION("Could not open data file: " + file_name);
568  }
569 }
570 
571 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
573 {
574  std::string file_name = mFilesBaseName + NCL_FILE_EXTENSION;
575  mNclFile.open(file_name.c_str(), std::ios::binary);
576 
577  mNclFileAvailable = mNclFile.is_open();
578 }
579 
580 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
582 {
583  std::string file_name = mFilesBaseName + CABLE_FILE_EXTENSION;
584  mCableElementsFile.open(file_name.c_str(), std::ios::binary);
585  if (!mCableElementsFile.is_open())
586  {
587  mNumCableElements = 0u;
588  mNumCableElementAttributes = 0u;
589  }
590 }
591 
592 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
594 {
595  return mNodeAttributes;
596 }
597 
598 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
600 {
601  /*
602  * Reading node file header
603  */
604  std::string buffer;
605  GetNextLineFromStream(mNodesFile, buffer);
606  std::stringstream node_header_line(buffer);
607  unsigned dimension;
608  node_header_line >> mNumNodes >> dimension >> mNumNodeAttributes >> mMaxNodeBdyMarker;
609  if (SPACE_DIM != dimension)
610  {
611  EXCEPTION("SPACE_DIM != dimension read from file ");
612  }
613 
614  // Is there anything else on the header line?
615  std::string extras;
616  node_header_line >> extras;
617  if (extras == "BIN")
618  {
619  mFilesAreBinary = true;
620  mNodeFileDataStart = mNodesFile.tellg(); // Record the position of the first byte after the header.
621  mNodeItemWidth = SPACE_DIM * sizeof(double);
622 
623  // We enforce that all binary files (written by Chaste) are indexed from zero
624  mIndexFromZero = true;
625  }
626  else
627  {
628  // #1621 - ncl files are only supported in binary read mode.
629  assert(!mNclFileAvailable);
630 
631  // Get the next line to see if it is indexed from zero or not
632  GetNextLineFromStream(mNodesFile, buffer);
633  std::stringstream node_first_line(buffer);
634  unsigned first_index;
635  node_first_line >> first_index;
636  assert(first_index == 0 || first_index == 1);
637  mIndexFromZero = (first_index == 0);
638 
639  // Close, reopen, skip header
640  mNodesFile.close();
641  OpenNodeFile();
642  GetNextLineFromStream(mNodesFile, buffer);
643  }
644 
645  /*
646  * Reading element file header
647  */
648  GetNextLineFromStream(mElementsFile, buffer);
649  std::stringstream element_header_line(buffer);
650 
651  unsigned extra_attributes = 0;
652 
653  if (ELEMENT_DIM == SPACE_DIM)
654  {
655  element_header_line >> mNumElements >> mNumElementNodes >> mNumElementAttributes;
656 
657  extra_attributes = mNumElementAttributes;
658 
659  // Is there anything else on the header line?
660  std::string element_extras;
661  element_header_line >> element_extras;
662  if (element_extras == "BIN")
663  {
664  // Double check for binaryness
665  assert (mFilesAreBinary);
666  }
667  else if (element_extras == "HEX")
668  {
669  mMeshIsHexahedral = true;
670  if ( ELEMENT_DIM == 2 )
671  {
672  mNodesPerElement = 4;
673  mNodesPerBoundaryElement = 2;
674  }
675  if ( ELEMENT_DIM == 3 )
676  {
677  mNodesPerElement = 8;
678  mNodesPerBoundaryElement = 4;
679  }
680  }
681  else
682  {
683  assert (element_extras == "");
684  }
685 
686  // The condition below allows the writer to cope with a NodesOnlyMesh
687  if (mNumElements != 0)
688  {
689  if (mNumElementNodes != mNodesPerElement)
690  {
691  EXCEPTION("Number of nodes per elem, " << mNumElementNodes << ", does not match "
692  << "expected number, " << mNodesPerElement << " (which is calculated given "
693  << "the order of elements chosen, " << mOrderOfElements << " (1=linear, 2=quadratics)");
694  }
695  }
696  }
697  else
698  {
699  // Note that .face files don't have the number of nodes in a face element in the header (its element dim +1)
700  element_header_line >> mNumElements >> mNumFaceAttributes;
701 
702  extra_attributes = mNumFaceAttributes;
703 
704  if (ELEMENT_DIM == 1 || ELEMENT_DIM == 2)
705  {
706  mNumElementAttributes = mNumFaceAttributes;
707  }
708 
709  // Is there anything else on the header line?
710  std::string element_extras;
711  element_header_line >> element_extras;
712  if (element_extras == "BIN")
713  {
714  // Double check for binaryness
715  assert (mFilesAreBinary);
716  }
717 
718  mNodesPerElement = ELEMENT_DIM+1;
719  }
720 
721  if (mFilesAreBinary)
722  {
723  mElementFileDataStart = mElementsFile.tellg(); // Record the position of the first byte after the header.
724  mElementItemWidth = mNodesPerElement*sizeof(unsigned) + extra_attributes*sizeof(double) ;
725  }
726 
727  /*
728  * Reading face/edge file header.
729  * The condition below allows the writer to cope with a NodesOnlyMesh.
730  */
731  if (mNumElements != 0)
732  {
733  if (ELEMENT_DIM == 1)
734  {
735  GetOneDimBoundary();
736  mNumFaces = mOneDimBoundary.size();
737  }
738  else
739  {
740  GetNextLineFromStream(mFacesFile, buffer);
741  std::stringstream face_header_line(buffer);
742 
743  face_header_line >> mNumFaces >> mNumFaceAttributes;
744  assert(mNumFaceAttributes==0 || mNumFaceAttributes==1);
745 
746  /*
747  * If mNumFaceAttributes=1 then loop over and set mNumFaces to be
748  * the number of faces which are marked as boundary faces.
749  * Double check for binaryness.
750  */
751  std::string face_extras;
752  face_header_line >> face_extras;
753  assert (mFilesAreBinary == (face_extras == "BIN"));
754  if (mNumFaceAttributes==1)
755  {
756  unsigned num_boundary_faces = 0;
757  bool end_of_file=false;
758  while (!end_of_file)
759  {
760  try
761  {
762  GetNextFaceData();
763  num_boundary_faces++;
764  }
765  catch(Exception& e)
766  {
767  if (mEofException)
768  {
769  end_of_file = true;
770  }
771  else
772  {
773  throw e;
774  }
775  }
776  }
777  mNumFaces = num_boundary_faces;
778 
781  // if (mNumFaces==0)
782  // {
783  // EXCEPTION("No boundary elements found. NOTE: elements in face/edge file with an attribute value of 0 are considered to be internal (non-boundary) elements");
784  // }
785 
786  // close the file, reopen, and skip the header again
787  mFacesFile.close();
788  mFacesFile.clear(); // Older versions of gcc don't explicitly reset "fail" and "eof" flags in std::ifstream after calling close()
789  OpenFacesFile();
790  GetNextLineFromStream(mFacesFile, buffer);
791  mFacesRead = 0;
792  mBoundaryFacesRead = 0;
793  }
794  }
795  }
796 
797  if (mFilesAreBinary)
798  {
799  mFaceFileDataStart = mFacesFile.tellg(); // Record the position of the first byte after the header.
800  mFaceItemWidth = ELEMENT_DIM*sizeof(unsigned) + mNumFaceAttributes*sizeof(double);
801  }
802 
803  /*
804  * Read NCL file (if one is available)
805  */
806  if (mNclFileAvailable)
807  {
808  GetNextLineFromStream(mNclFile, buffer);
809  std::stringstream ncl_header_line(buffer);
810  unsigned num_nodes_in_file;
811  ncl_header_line >> num_nodes_in_file >> mMaxContainingElements;
812 
813  if ( mNumNodes != num_nodes_in_file )
814  {
815  EXCEPTION("NCL file does not contain the correct number of nodes for mesh");
816  }
817 
818  mNclFileDataStart = mNclFile.tellg(); // Record the position of the first byte after the header
819  mNclItemWidth = mMaxContainingElements * sizeof(unsigned);
820  }
821 
822  /*
823  * Read cable file (if one is available)
824  */
825  if (mCableElementsFile.is_open())
826  {
827  GetNextLineFromStream(mCableElementsFile, buffer);
828  std::stringstream cable_header_line(buffer);
829  unsigned num_nodes_per_cable_element;
830  cable_header_line >> mNumCableElements >> num_nodes_per_cable_element >> mNumCableElementAttributes;
831  assert(num_nodes_per_cable_element == 2u);
832  mCableElementsRead = 0u;
833  }
834 }
835 
836 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
838 {
839  mNodesFile.close();
840  mElementsFile.close();
841  mFacesFile.close();
842  mNclFile.close();
843  mCableElementsFile.close();
844 }
845 
846 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
847 void TrianglesMeshReader<ELEMENT_DIM, SPACE_DIM>::GetNextLineFromStream(std::ifstream& rFileStream, std::string& rRawLine)
848 {
849  bool line_is_blank;
850  mEofException = false;
851  do
852  {
853  getline(rFileStream, rRawLine, '\n');
854  if (rFileStream.eof())
855  {
856  mEofException = true;
857  EXCEPTION("File contains incomplete data: unexpected end of file.");
858  }
859 
860  // Get rid of any comment
861  rRawLine = rRawLine.substr(0, rRawLine.find('#',0));
862 
863  line_is_blank = (rRawLine.find_first_not_of(" \t",0) == std::string::npos);
864  }
865  while (line_is_blank);
866 }
867 
868 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
869 template<class T_DATA>
870 void TrianglesMeshReader<ELEMENT_DIM, SPACE_DIM>::GetNextItemFromStream(std::ifstream& rFileStream, unsigned expectedItemNumber,
871  std::vector<T_DATA>& rDataPacket, const unsigned& rNumAttributes, std::vector<double>& rAttributes)
872 {
873  if (mFilesAreBinary)
874  {
875  if (!rDataPacket.empty()) // Avoid MSVC 10 assertion
876  {
877  rFileStream.read((char*)&rDataPacket[0], rDataPacket.size()*sizeof(T_DATA));
878  }
879  if (rNumAttributes > 0)
880  {
881  for (unsigned i = 0; i < rNumAttributes; i++)
882  {
883  double attribute;
884  rFileStream.read((char*) &attribute, sizeof(double));
885  rAttributes.push_back(attribute);
886  }
887  }
888  }
889  else
890  {
891  std::string buffer;
892  GetNextLineFromStream(rFileStream, buffer);
893  std::stringstream buffer_stream(buffer);
894 
895  unsigned item_index;
896  buffer_stream >> item_index;
897 
898  // If we are indexing from zero our expected item number is one larger
899  expectedItemNumber += mIndexFromZero ? 0 : 1;
900 
901  if (item_index != expectedItemNumber)
902  {
903  if (!mIndexFromZero)
904  {
905  // To fix the exception message to agree with file format
906  expectedItemNumber--;
907  }
908  EXCEPTION("Data for item " << expectedItemNumber << " missing");
909  }
910 
911  for (unsigned i=0; i<rDataPacket.size(); i++)
912  {
913  buffer_stream >> rDataPacket[i];
914  }
915 
916  if (rNumAttributes > 0)
917  {
918  for (unsigned i = 0; i < rNumAttributes; i++)
919  {
920  double attribute;
921  buffer_stream >> attribute;
922  if (buffer_stream.fail())
923  {
924  EXCEPTION("Error in reading attribute index " << i << " (out of " << rNumAttributes << ") in one of the files in " << mFilesBaseName);
925  }
926  rAttributes.push_back(attribute);
927  }
928  }
929  }
930 }
931 
932 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
934 {
935  return mFilesBaseName;
936 }
937 
938 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
940 {
941  assert(ELEMENT_DIM == 1);
942  mNumFaceAttributes = 0;
943  if (!mOneDimBoundary.empty())
944  {
945  // We have already read this and have reset the reader (probably from the mesh class)
946  return;
947  }
948  std::vector<unsigned> node_indices(2);
949  std::vector<double> dummy_attribute; // unused
950 
951  // Count how many times we see each node
952  std::vector<unsigned> node_count(mNumNodes); // Covers the case if it's indexed from 1
953  for (unsigned element_index=0; element_index<mNumElements;element_index++)
954  {
955  GetNextItemFromStream(mElementsFile, element_index, node_indices, mNumElementAttributes, dummy_attribute);
956  if (!mIndexFromZero)
957  {
958  // Adjust so we are indexing from zero
959  node_indices[0]--;
960  node_indices[1]--;
961  }
962  node_count[node_indices[0]]++;
963  node_count[node_indices[1]]++;
964  }
965 
966  // Find the ones which are terminals (only one mention)
967  for (unsigned node_index=0; node_index<mNumNodes;node_index++)
968  {
969  if (node_count[node_index] == 1u)
970  {
971  mOneDimBoundary.push_back(node_index);
972  }
973  }
974 
975  // Close the file, reopen, and skip the header again
976  mElementsFile.close();
977  mElementsFile.clear(); // Older versions of gcc don't explicitly reset "fail" and "eof" flags in std::ifstream after calling close()
978  OpenElementsFile();
979  std::string buffer;
980  GetNextLineFromStream(mElementsFile, buffer);
981 }
982 
983 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
985 {
986  if (!mIndexFromZero) // If node indices do not start at zero move them all down one so they do
987  {
988  for (unsigned i=0; i<rNodeIndices.size(); i++)
989  {
990  rNodeIndices[i]--;
991  }
992  }
993 }
994 
995 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
997 {
998  return mFilesAreBinary;
999 }
1000 
1001 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1003 {
1004  return mNclFileAvailable;
1005 }
1006 
1007 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1009 {
1010  mNodeFileReadBuffer = new char[bufferSize];
1011  mElementFileReadBuffer = new char[bufferSize];
1012  mFaceFileReadBuffer = new char[bufferSize];
1013 
1014  mNodesFile.rdbuf()->pubsetbuf(mNodeFileReadBuffer, bufferSize);
1015  mElementsFile.rdbuf()->pubsetbuf(mElementFileReadBuffer, bufferSize);
1016  mFacesFile.rdbuf()->pubsetbuf(mFaceFileReadBuffer, bufferSize);
1017 }
1018 
1019 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1020 void TrianglesMeshReader<ELEMENT_DIM, SPACE_DIM>::SetNodePermutation(std::vector<unsigned>& rPermutationVector)
1021 {
1022  if ( !mFilesAreBinary )
1023  {
1024  // It would be too inefficient otherwise...
1025  EXCEPTION("Permuted read can only be used with binary files since it requires random access to the node file.");
1026  }
1027 
1028  mNodePermutationDefined = true;
1029  mPermutationVector = rPermutationVector;
1030  mInversePermutationVector.resize(mPermutationVector.size());
1031  for (unsigned index=0; index<mPermutationVector.size(); index++)
1032  {
1033  mInversePermutationVector[mPermutationVector[index]]=index;
1034  }
1035 }
1036 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1038 {
1039  return(mNodePermutationDefined);
1040 }
1041 
1042 
1043 
1047 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1049 {
1050  return mPermutationVector;
1051 }
1052 
1054 // Explicit instantiation
1056 
1057 template class TrianglesMeshReader<0,1>;
1058 template class TrianglesMeshReader<1,1>;
1059 template class TrianglesMeshReader<1,2>;
1060 template class TrianglesMeshReader<1,3>;
1061 template class TrianglesMeshReader<2,2>;
1062 template class TrianglesMeshReader<2,3>;
1063 template class TrianglesMeshReader<3,3>;
1064 
1065 
1070 template void TrianglesMeshReader<0,1>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<unsigned>&, const unsigned&, std::vector<double>&);
1071 template void TrianglesMeshReader<0,1>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<double> &, const unsigned&, std::vector<double>&);
1072 template void TrianglesMeshReader<1,1>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<unsigned>&, const unsigned&, std::vector<double>&);
1073 template void TrianglesMeshReader<1,1>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<double> &, const unsigned&, std::vector<double>&);
1074 template void TrianglesMeshReader<1,2>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<unsigned>&, const unsigned&, std::vector<double>&);
1075 template void TrianglesMeshReader<1,2>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<double> &, const unsigned&, std::vector<double>&);
1076 template void TrianglesMeshReader<1,3>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<unsigned>&, const unsigned&, std::vector<double>&);
1077 template void TrianglesMeshReader<1,3>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<double> &, const unsigned&, std::vector<double>&);
1078 template void TrianglesMeshReader<2,2>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<unsigned>&, const unsigned&, std::vector<double>&);
1079 template void TrianglesMeshReader<2,2>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<double> &, const unsigned&, std::vector<double>&);
1080 template void TrianglesMeshReader<2,3>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<unsigned>&, const unsigned&, std::vector<double>&);
1081 template void TrianglesMeshReader<2,3>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<double> &, const unsigned&, std::vector<double>&);
1082 template void TrianglesMeshReader<3,3>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<unsigned>&, const unsigned&, std::vector<double>&);
1083 template void TrianglesMeshReader<3,3>::GetNextItemFromStream(std::ifstream&, unsigned, std::vector<double> &, const unsigned&, std::vector<double>&);
ElementData GetNextCableElementData()
unsigned GetNumFaceAttributes() const
unsigned GetNumNodes() const
unsigned ContainingElement
void GetNextLineFromStream(std::ifstream &rFileStream, std::string &rRawLine)
#define EXCEPTION(message)
Definition: Exception.hpp:143
TrianglesMeshReader(std::string pathBaseName, unsigned orderOfElements=1, unsigned orderOfBoundaryElements=1, bool readContainingElementsForBoundaryElements=false)
std::vector< unsigned > GetContainingElementIndices(unsigned index)
unsigned GetNumElementAttributes() const
unsigned GetNumCableElementAttributes() const
unsigned GetNumCableElements() const
unsigned GetNumElements() const
std::vector< unsigned > NodeIndices
ElementData GetNextElementData()
void SetNodePermutation(std::vector< unsigned > &rPermutationVector)
void SetReadBufferSize(unsigned bufferSize)
std::vector< double > GetNextNode()
void EnsureIndexingFromZero(std::vector< unsigned > &rNodeIndices)
ElementData GetFaceData(unsigned index)
unsigned GetNumFaces() const
const std::vector< unsigned > & rGetNodePermutation()
std::string GetMeshFileBaseName()
std::vector< double > GetNodeAttributes()
void GetNextItemFromStream(std::ifstream &rFileStream, unsigned expectedItemNumber, std::vector< T_DATA > &rDataPacket, const unsigned &rNumAttributes, std::vector< double > &rAttributes)
std::vector< double > GetNode(unsigned index)
ElementData GetElementData(unsigned index)