tetgen_scaffold_mesh.cc
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 #include "mesh.h"
31 #include "Telements.h"
32 #include "tetgen_scaffold_mesh.h"
33 
34 namespace oomph
35 {
36 
37 //======================================================================
38 /// Constructor: Pass the filename of the tetrahedra file
39 /// The assumptions are that the nodes have been assigned boundary
40 /// information which is used in the nodal construction to make sure
41 /// that BoundaryNodes are constructed. Any additional boundaries are
42 /// determined from the face boundary information.
43 //======================================================================
45  const std::string& element_file_name,
46  const std::string& face_file_name)
47 {
48 
49  // Process the element file
50  // --------------------------
51  std::ifstream element_file(element_file_name.c_str(),std::ios_base::in);
52 
53  // Check that the file actually opened correctly
54 #ifdef PARANOID
55  if(!element_file.is_open())
56  {
57  std::string error_msg("Failed to open element file: ");
58  error_msg += "\"" + element_file_name + "\".";
59  throw OomphLibError(error_msg, OOMPH_CURRENT_FUNCTION,
60  OOMPH_EXCEPTION_LOCATION);
61  }
62 #endif
63 
64 
65  //Read in number of elements
66  unsigned n_element;
67  element_file>>n_element;
68 
69  //Read in number of nodes per element
70  unsigned n_local_node;
71  element_file>>n_local_node;
72 
73  //Throw an error if we have anything but linear simplices
74  if (n_local_node!=4)
75  {
76  std::ostringstream error_stream;
77  error_stream
78  << "Tetgen should only be used to generate 4-noded tetrahedra!\n"
79  << "Your tetgen input file, contains data for "
80  << n_local_node << "-noded tetrahedra" << std::endl;
81 
82  throw OomphLibError(error_stream.str(),
83  OOMPH_CURRENT_FUNCTION,
84  OOMPH_EXCEPTION_LOCATION);
85  }
86 
87  // Dummy nodal attributes
88  unsigned dummy_attribute;
89 
90  // Element attributes may be used to distinguish internal regions
91  // NOTE: This stores doubles because tetgen forces us to!
92  Element_attribute.resize(n_element,0.0);
93 
94  // Dummy storage for element numbers
95  unsigned dummy_element_number;
96 
97  // Resize storage for the global node numbers listed element-by-element
98  Global_node.resize(n_element*n_local_node);
99 
100  //Initialise (global) node counter
101  unsigned k=0;
102  //Are there attributes?
103  unsigned attribute_flag;
104  element_file >> attribute_flag;
105 
106  //If no attributes
107  if(attribute_flag==0)
108  {
109  //Read in global node numbers
110  for(unsigned i=0;i<n_element;i++)
111  {
112  element_file>>dummy_element_number;
113  for(unsigned j=0;j<n_local_node;j++)
114  {
115  element_file >> Global_node[k];
116  k++;
117  }
118  }
119  }
120  //Otherwise read in the attributes as well
121  else
122  {
123  for(unsigned i=0;i<n_element;i++)
124  {
125  element_file>>dummy_element_number;
126  for(unsigned j=0;j<n_local_node;j++)
127  {
128  element_file >> Global_node[k];
129  k++;
130  }
131  element_file >> Element_attribute[i];
132  }
133  }
134  element_file.close();
135 
136  // Resize the Element vector
137  Element_pt.resize(n_element);
138 
139  //Process node file
140  //--------------------
141  std::ifstream node_file(node_file_name.c_str(),std::ios_base::in);
142 
143  // Check that the file actually opened correctly
144 #ifdef PARANOID
145  if(!node_file.is_open())
146  {
147  std::string error_msg("Failed to open node file: ");
148  error_msg += "\"" + node_file_name + "\".";
149  throw OomphLibError(error_msg, OOMPH_CURRENT_FUNCTION,
150  OOMPH_EXCEPTION_LOCATION);
151  }
152 #endif
153 
154 
155  //Read in the number of nodes
156  unsigned n_node;
157  node_file>>n_node;
158 
159  // Create a vector of boolean so as not to create the same node twice
160  std::vector<bool> done (n_node,false);
161 
162  // Resize the Node vector
163  Node_pt.resize(n_node);
164 
165  //Set the spatial dimension of the nodes
166  unsigned dimension;
167  node_file>>dimension;
168 
169 #ifdef PARANOID
170  if(dimension!=3)
171  {
172  throw OomphLibError("The dimesion of the nodes must be 3\n",
173  OOMPH_CURRENT_FUNCTION,
174  OOMPH_EXCEPTION_LOCATION);
175  }
176 #endif
177 
178  // Flag for attributes
179  node_file>>attribute_flag;
180 
181  //Flag for boundary markers
182  unsigned boundary_markers_flag;
183  node_file>>boundary_markers_flag;
184 
185  // Dummy storage for the node number
186  unsigned dummy_node_number;
187 
188  // Create storage for nodal positions and boundary markers
189  Vector<double> x_node(n_node);
190  Vector<double> y_node(n_node);
191  Vector<double> z_node(n_node);
192  Vector<unsigned> bound(n_node);
193 
194  // Check if there are attributes
195  //If not
196  if(attribute_flag==0)
197  {
198  //Are there boundary markers
199  if(boundary_markers_flag==1)
200  {
201  for(unsigned i=0;i<n_node;i++)
202  {
203  node_file>>dummy_node_number;
204  node_file>>x_node[i];
205  node_file>>y_node[i];
206  node_file>>z_node[i];
207  node_file>>bound[i];
208  }
209  node_file.close();
210  }
211  //Otherwise no boundary markers
212  else
213  {
214  for(unsigned i=0;i<n_node;i++)
215  {
216  node_file>>dummy_node_number;
217  node_file>>x_node[i];
218  node_file>>y_node[i];
219  node_file>>z_node[i];
220  bound[i] = 0;
221  }
222  node_file.close();
223  }
224  }
225  //Otherwise there are attributes
226  else
227  {
228  if(boundary_markers_flag==1)
229  {
230  for(unsigned i=0;i<n_node;i++)
231  {
232  node_file>>dummy_node_number;
233  node_file>>x_node[i];
234  node_file>>y_node[i];
235  node_file>>z_node[i];
236  node_file>>dummy_attribute;
237  node_file>>bound[i];
238  }
239  node_file.close();
240  }
241  else
242  {
243  for(unsigned i=0;i<n_node;i++)
244  {
245  node_file>>dummy_node_number;
246  node_file>>x_node[i];
247  node_file>>y_node[i];
248  node_file>>z_node[i];
249  node_file>>dummy_attribute;
250  bound[i] = 0;
251  }
252  node_file.close();
253  }
254  }
255 
256  // Determine highest boundary index
257  //------------------------------------
258  unsigned n_bound=0;
259  if(boundary_markers_flag==1)
260  {
261  n_bound=bound[0];
262  for(unsigned i=1;i<n_node;i++)
263  {
264  if(bound[i] > n_bound)
265  {
266  n_bound = bound[i];
267  }
268  }
269  }
270 
271  // Process face file to extract boundary faces
272  //--------------------------------------------
273 
274  // Open face file
275  std::ifstream face_file(face_file_name.c_str(),std::ios_base::in);
276 
277  // Check that the file actually opened correctly
278 #ifdef PARANOID
279  if(!face_file.is_open())
280  {
281  std::string error_msg("Failed to open face file: ");
282  error_msg += "\"" + face_file_name + "\".";
283  throw OomphLibError(error_msg, OOMPH_CURRENT_FUNCTION,
284  OOMPH_EXCEPTION_LOCATION);
285  }
286 #endif
287 
288  // Number of faces in face file
289  unsigned n_face;
290  face_file>>n_face;
291 
292  // Boundary markers flag
293  face_file>>boundary_markers_flag;
294 
295  // Storage for the global node numbers (in the tetgen 1-based
296  // numbering scheme!) of the first, second and third node in
297  // each segment
298  Vector<unsigned> first_node(n_face);
299  Vector<unsigned> second_node(n_face);
300  Vector<unsigned> third_node(n_face);
301 
302  // Storage for the boundary marker for each face
304 
305  // Dummy for global face number
306  unsigned dummy_face_number;
307 
308  // Storage for the (boundary) faces associated with each node.
309  // Nodes are indexed using Tetgen's 1-based scheme, which is why
310  // there is a +1 here
311  Vector<std::set<unsigned> > node_on_faces(n_node+1);
312 
313  // Extract information for each segment
314  for(unsigned i=0;i<n_face;i++)
315  {
316  face_file >> dummy_face_number;
317  face_file >> first_node[i];
318  face_file >> second_node[i];
319  face_file >> third_node[i];
320  face_file >> face_boundary[i];
321  if (face_boundary[i]>n_bound) {n_bound=face_boundary[i];}
322  //Add the face index to each node
323  node_on_faces[first_node[i]].insert(i);
324  node_on_faces[second_node[i]].insert(i);
325  node_on_faces[third_node[i]].insert(i);
326  }
327  face_file.close();
328 
329  // Set number of boundaries
330  if(n_bound > 0)
331  {
332  this->set_nboundary(n_bound);
333  }
334 
335  // Create the elements
336  unsigned counter=0;
337  for(unsigned e=0;e<n_element;e++)
338  {
340  unsigned global_node_number = Global_node[counter];
341  if(done[global_node_number-1]==false)
342  // ... -1 because node number begins at 1 in tetgen
343  {
344  //If the node is on a boundary, construct a boundary node
345  if((boundary_markers_flag==1) &&
346  (bound[global_node_number-1] > 0))
347  {
348  //Construct the boundary ndoe
349  Node_pt[global_node_number-1] =
351 
352  //Add to the boundary lookup scheme
353  add_boundary_node(bound[global_node_number-1]-1,
354  Node_pt[global_node_number-1]);
355  }
356  //Otherwise just construct a normal node
357  else
358  {
359  Node_pt[global_node_number-1] =
361  }
362 
363  done[global_node_number-1]=true;
364  Node_pt[global_node_number-1]->x(0)=x_node[global_node_number-1];
365  Node_pt[global_node_number-1]->x(1)=y_node[global_node_number-1];
366  Node_pt[global_node_number-1]->x(2)=z_node[global_node_number-1];
367  }
368  //Otherwise just copy the node numbr accross
369  else
370  {
371  finite_element_pt(e)->node_pt(3) = Node_pt[global_node_number-1];
372  }
373  counter++;
374 
375  //Loop over the other nodes
376  for(unsigned j=0;j<(n_local_node-1);j++)
377  {
378  global_node_number=Global_node[counter];
379  if(done[global_node_number-1]==false)
380  // ... -1 because node number begins at 1 in tetgen
381  {
382  //If we're on a boundary
383  if((boundary_markers_flag==1) &&
384  (bound[global_node_number-1] > 0))
385  {
386  //Construct the boundary ndoe
387  Node_pt[global_node_number-1] =
389 
390  //Add to the boundary lookup scheme
391  add_boundary_node(bound[global_node_number-1]-1,
392  Node_pt[global_node_number-1]);
393  }
394  else
395  {
396  Node_pt[global_node_number-1]=finite_element_pt(e)->construct_node(j);
397  }
398  done[global_node_number-1]=true;
399  Node_pt[global_node_number-1]->x(0)=x_node[global_node_number-1];
400  Node_pt[global_node_number-1]->x(1)=y_node[global_node_number-1];
401  Node_pt[global_node_number-1]->x(2)=z_node[global_node_number-1];
402  }
403  //Otherwise copy the pointer over
404  else
405  {
406  finite_element_pt(e)->node_pt(j) = Node_pt[global_node_number-1];
407  }
408  counter++;
409  }
410  }
411 
412 
413  // Resize the "matrix" that stores the boundary id for each
414  // face in each element.
415  Face_boundary.resize(n_element);
416  Face_index.resize(n_element);
417  Edge_index.resize(n_element);
418 
419 
420  //0-based index scheme used to construct a global lookup for
421  //each face that will be used to uniquely construct interior
422  //face nodes.
423  //The faces that lie on boundaries will have already been allocated so
424  //we can start from there
425  Nglobal_face = n_face;
426 
427  // Storage for the edges associated with each node.
428  // Nodes are indexed using Tetgen's 1-based scheme, which is why
429  // there is a +1 here
430  Vector<std::set<unsigned> > node_on_edges(n_node+1);
431 
432  //0-based index scheme used to construct a global lookup for each
433  //edge that will be used to uniquely construct interior edge nodes
434  Nglobal_edge = 0;
435 
436  // Conversion from the edge numbers to the nodes at the end
437  // of each each edge
438  const unsigned first_local_edge_node[6] = {0,0,0,1,2,1};
439  const unsigned second_local_edge_node[6] = {1,2,3,2,3,3};
440 
441  // Loop over the elements
442  for(unsigned e=0;e<n_element;e++)
443  {
444  // Each element has four faces
445  Face_boundary[e].resize(4);
446  Face_index[e].resize(4);
447  // By default each face is NOT on a boundary
448  for(unsigned i=0;i<4;i++)
449  {
450  Face_boundary[e][i]=0;
451  }
452 
453  Edge_index[e].resize(6);
454 
455  // Read out the global node numbers of the nodes from
456  // tetgen's 1-based numbering.
457  // The offset is to match the offset used above
458  const unsigned element_offset = e*n_local_node;
459  unsigned glob_num[4] = {0,0,0,0};
460  for(unsigned i=0;i<4;++i)
461  {
462  glob_num[i] = Global_node[element_offset + ((i+1)%4)];
463  }
464 
465  // Now we know the global node numbers of the elements' four nodes
466  // in tetgen's 1-based numbering.
467 
468  //Determine whether any of the element faces have already been allocated an
469  //index. This may be because they are located on boundaries, or
470  //have already been visited
471  //The global index for the i-th face will be stored in Face_index[i]
472 
473  //Loop over the local faces in the element
474  for(unsigned i=0;i<4;++i)
475  {
476  //On the i-th face, our numbering convention is such that
477  //it is the (3-i)th node of the element that is omitted
478  const unsigned omitted_node = 3-i;
479 
480  //Start with the set of global faces associated with the i-th node
481  //which is always on the i-th face
482  std::set<unsigned> input = node_on_faces[glob_num[i]];
483 
484  //If there is no data yet, not point doing intersections
485  //the face has not been set up
486  if(input.size() > 0)
487  {
488  //Loop over the other nodes on the face
489  unsigned local_node = i;
490  for(unsigned i2=0;i2<3;++i2)
491  {
492  //Create the next node index (mod 4)
493  local_node = (local_node+1)%4;
494  //Only take the intersection of nodes on the face
495  //i.e. not 3-i
496  if(local_node != omitted_node)
497  {
498  //Local storage for the intersection
499  std::set<unsigned> intersection_result;
500  //Calculate the intersection of the current input
501  //and next node
502  std::set_intersection(input.begin(),input.end(),
503  node_on_faces[glob_num[local_node]].begin(),
504  node_on_faces[glob_num[local_node]].end(),
505  std::insert_iterator<std::set<unsigned> >(
506  intersection_result,
507  intersection_result.begin()));
508  //Let the result be the next input
509  input = intersection_result;
510  }
511  }
512  }
513 
514  //If the nodes share more than one global face index, then
515  //we have a problem
516  if(input.size() > 1)
517  {
518  throw OomphLibError(
519  "Nodes in scaffold mesh share more than one global face",
520  OOMPH_CURRENT_FUNCTION,
521  OOMPH_EXCEPTION_LOCATION);
522  }
523 
524  //If the element's face is not already allocated, the intersection
525  //will be empty
526  if(input.size()==0)
527  {
528  //Allocate the next global index
530  //Associate the new face index with the nodes
531  for(unsigned i2=0;i2<4;++i2)
532  {
533  //Don't add the omitted node
534  if(i2 != omitted_node)
535  {
536  node_on_faces[glob_num[i2]].insert(Nglobal_face);
537  }
538  }
539  //Increment the global face index
540  ++Nglobal_face;
541  }
542  //Otherwise we already have a face
543  else if(input.size()==1)
544  {
545  const unsigned global_face_index = *input.begin();
546  //Set the face index
547  Face_index[e][i] = global_face_index;
548  //Allocate the boundary index, if it's a boundary
549  if(global_face_index < n_face)
550  {
551  Face_boundary[e][i] = face_boundary[global_face_index];
552  //Add the nodes to the boundary look-up scheme in
553  //oomph-lib (0-based) index
554  for(unsigned i2=0;i2<4;++i2)
555  {
556  //Don't add the omitted node
557  if(i2 != omitted_node)
558  {
559  add_boundary_node(face_boundary[global_face_index]-1,
560  Node_pt[glob_num[i2]-1]);
561  }
562  }
563  }
564  }
565  } //end of loop over the faces
566 
567 
568  //Loop over the element edges and assign global edge numbers
569  for(unsigned i=0;i<6;++i)
570  {
571  //Storage for the result of the intersection
572  std::vector<unsigned> local_edge_index;
573 
574  const unsigned first_global_num = glob_num[first_local_edge_node[i]];
575  const unsigned second_global_num = glob_num[second_local_edge_node[i]];
576 
577  //Find the common global edge index for the nodes on
578  //the i-th element edge
579  std::set_intersection(node_on_edges[first_global_num].begin(),
580  node_on_edges[first_global_num].end(),
581  node_on_edges[second_global_num].begin(),
582  node_on_edges[second_global_num].end(),
583  std::insert_iterator<std::vector<unsigned> >(
584  local_edge_index,local_edge_index.begin()));
585 
586  //If the nodes share more than one global edge index, then
587  //we have a problem
588  if(local_edge_index.size() > 1)
589  {
590  throw OomphLibError(
591  "Nodes in scaffold mesh share more than one global edge",
592  OOMPH_CURRENT_FUNCTION,
593  OOMPH_EXCEPTION_LOCATION);
594  }
595 
596  //If the element's edge is not already allocated, the intersection
597  //will be empty
598  if(local_edge_index.size()==0)
599  {
600  //Allocate the next global index
602  //Associate the new edge index with the nodes
603  node_on_edges[first_global_num].insert(Nglobal_edge);
604  node_on_edges[second_global_num].insert(Nglobal_edge);
605  //Increment the global edge index
606  ++Nglobal_edge;
607  }
608  //Otherwise we already have an edge
609  else if(local_edge_index.size()==1)
610  {
611  //Set the edge index from the result of the intersection
612  Edge_index[e][i] = local_edge_index[0];
613  }
614  }
615 
616  } // end for e
617 
618 
619 
620  //Now determine whether any edges lie on boundaries by using the
621  //face boundary scheme and
622 
623  //Resize the storage
624  Edge_boundary.resize(Nglobal_edge,false);
625 
626  //Now loop over all the boundary faces and mark that all edges
627  //must also lie on the bounadry
628  for(unsigned i=0;i<n_face;++i)
629  {
630  {
631  //Storage for the result of the intersection
632  std::vector<unsigned> local_edge_index;
633 
634  //Find the common global edge index for first edge of the face
635  std::set_intersection(node_on_edges[first_node[i]].begin(),
636  node_on_edges[first_node[i]].end(),
637  node_on_edges[second_node[i]].begin(),
638  node_on_edges[second_node[i]].end(),
639  std::insert_iterator<std::vector<unsigned> >(
640  local_edge_index,local_edge_index.begin()));
641 
642  //If the nodes do not share exactly one global edge index, then
643  //we have a problem
644  if(local_edge_index.size() != 1)
645  {
646  throw OomphLibError(
647  "Nodes in scaffold mesh face do not share exactly one global edge",
648  OOMPH_CURRENT_FUNCTION,
649  OOMPH_EXCEPTION_LOCATION);
650  }
651  else
652  {
653  Edge_boundary[local_edge_index[0]] = true;
654  }
655  }
656 
657  {
658  //Storage for the result of the intersection
659  std::vector<unsigned> local_edge_index;
660 
661  //Find the common global edge index for second edge of the face
662  std::set_intersection(node_on_edges[second_node[i]].begin(),
663  node_on_edges[second_node[i]].end(),
664  node_on_edges[third_node[i]].begin(),
665  node_on_edges[third_node[i]].end(),
666  std::insert_iterator<std::vector<unsigned> >(
667  local_edge_index,local_edge_index.begin()));
668 
669  //If the nodes do not share exactly one global edge index, then
670  //we have a problem
671  if(local_edge_index.size() != 1)
672  {
673  throw OomphLibError(
674  "Nodes in scaffold mesh face do not share exactly one global edge",
675  OOMPH_CURRENT_FUNCTION,
676  OOMPH_EXCEPTION_LOCATION);
677  }
678  else
679  {
680  Edge_boundary[local_edge_index[0]] = true;
681  }
682  }
683 
684  {
685  //Storage for the result of the intersection
686  std::vector<unsigned> local_edge_index;
687 
688  //Find the common global edge index for third edge of the face
689  std::set_intersection(node_on_edges[first_node[i]].begin(),
690  node_on_edges[first_node[i]].end(),
691  node_on_edges[third_node[i]].begin(),
692  node_on_edges[third_node[i]].end(),
693  std::insert_iterator<std::vector<unsigned> >(
694  local_edge_index,local_edge_index.begin()));
695 
696  //If the nodes do not share exactly one global edge index, then
697  //we have a problem
698  if(local_edge_index.size() != 1)
699  {
700  throw OomphLibError(
701  "Nodes in scaffold mesh face do not share exactly one global edge",
702  OOMPH_CURRENT_FUNCTION,
703  OOMPH_EXCEPTION_LOCATION);
704  }
705  else
706  {
707  Edge_boundary[local_edge_index[0]] = true;
708  }
709  }
710  }
711 
712 
713 } //end of constructor
714 
715 
716 //======================================================================
717 /// Constructor: Pass a tetgenio data structure that represents
718 /// the input data of the mesh.
719 /// The assumptions are that the nodes have been assigned boundary
720 /// information which is used in the nodal construction to make sure
721 /// that BoundaryNodes are constructed. Any additional boundaries are
722 /// determined from the face boundary information.
723 //======================================================================
725 {
726  //Find the number of elements
727  unsigned n_element = static_cast<unsigned>(tetgen_data.numberoftetrahedra);
728 
729  //Read in number of nodes per element
730  unsigned n_local_node = static_cast<unsigned>(tetgen_data.numberofcorners);
731 
732  //Throw an error if we have anything but linear simplices
733  if (n_local_node!=4)
734  {
735  std::ostringstream error_stream;
736  error_stream
737  << "Tetgen should only be used to generate 4-noded tetrahedra!\n"
738  << "Your tetgen input data, contains data for "
739  << n_local_node << "-noded tetrahedra" << std::endl;
740 
741  throw OomphLibError(error_stream.str(),
742  OOMPH_CURRENT_FUNCTION,
743  OOMPH_EXCEPTION_LOCATION);
744  }
745 
746  // Element attributes may be used to distinguish internal regions
747  // NOTE: This stores doubles because tetgen forces us to!
748  Element_attribute.resize(n_element,0.0);
749 
750  // Resize storage for the global node numbers listed element-by-element
751  Global_node.resize(n_element*n_local_node);
752 
753  //Initialise (global) node counter
754  unsigned k=0;
755  //Are there attributes?
756  unsigned attribute_flag =
757  static_cast<unsigned>(tetgen_data.numberoftetrahedronattributes);
758 
759  //Read global node numbers for all elements
760  if(attribute_flag==0)
761  {
762  for(unsigned i=0;i<n_element;i++)
763  {
764  for(unsigned j=0;j<n_local_node;j++)
765  {
766  Global_node[k] = static_cast<unsigned>(tetgen_data.tetrahedronlist[k]);
767  k++;
768  }
769  }
770  }
771  //Otherwise read in the attributes as well
772  else
773  {
774  for(unsigned i=0;i<n_element;i++)
775  {
776  for(unsigned j=0;j<n_local_node;j++)
777  {
778  Global_node[k] = static_cast<unsigned>(tetgen_data.tetrahedronlist[k]);
779  k++;
780  }
781  Element_attribute[i] = tetgen_data.tetrahedronattributelist[i];
782  }
783  }
784 
785  // Resize the Element vector
786  Element_pt.resize(n_element);
787 
788  //Read in the number of nodes
789  unsigned n_node = tetgen_data.numberofpoints;
790 
791  // Create a vector of boolean so as not to create the same node twice
792  std::vector<bool> done (n_node,false);
793 
794  // Resize the Node vector
795  Node_pt.resize(n_node);
796 
797  //Flag for boundary markers
798  unsigned boundary_markers_flag = 0;
799  if(tetgen_data.pointmarkerlist!=0) {boundary_markers_flag=1;}
800 
801  // Create storage for nodal positions and boundary markers
802  Vector<double> x_node(n_node);
803  Vector<double> y_node(n_node);
804  Vector<double> z_node(n_node);
805  Vector<unsigned> bound(n_node);
806 
807  //We shall ignore all point attributes
808  if(boundary_markers_flag==1)
809  {
810  for(unsigned i=0;i<n_node;i++)
811  {
812  x_node[i] = tetgen_data.pointlist[3*i];
813  y_node[i] = tetgen_data.pointlist[3*i+1];
814  z_node[i] = tetgen_data.pointlist[3*i+2];
815  bound[i] = static_cast<unsigned>(tetgen_data.pointmarkerlist[i]);
816  }
817  }
818  //Otherwise no boundary markers
819  else
820  {
821  for(unsigned i=0;i<n_node;i++)
822  {
823  x_node[i] = tetgen_data.pointlist[3*i];
824  y_node[i] = tetgen_data.pointlist[3*i+1];
825  z_node[i] = tetgen_data.pointlist[3*i+2];
826  bound[i] = 0;
827  }
828  }
829 
830 
831  // Determine highest boundary index
832  //------------------------------------
833  unsigned n_bound=0;
834  if(boundary_markers_flag==1)
835  {
836  n_bound=bound[0];
837  for(unsigned i=1;i<n_node;i++)
838  {
839  if(bound[i] > n_bound)
840  {
841  n_bound = bound[i];
842  }
843  }
844  }
845 
846  //Now extract face information
847  //---------------------------------
848 
849  // Number of faces in face file
850  unsigned n_face = tetgen_data.numberoftrifaces;
851 
852  // Storage for the global node numbers (in the tetgen 1-based
853  // numbering scheme!) of the first, second and third node in
854  // each segment
855  Vector<unsigned> first_node(n_face);
856  Vector<unsigned> second_node(n_face);
857  Vector<unsigned> third_node(n_face);
858 
859  // Storage for the boundary marker for each face
861 
862  // Storage for the (boundary) faces associated with each node.
863  // Nodes are indexed using Tetgen's 1-based scheme, which is why
864  // there is a +1 here
865  Vector<std::set<unsigned> > node_on_faces(n_node+1);
866 
867  // Extract information for each segment
868  for(unsigned i=0;i<n_face;i++)
869  {
870  first_node[i] = static_cast<unsigned>(tetgen_data.trifacelist[3*i]);
871  second_node[i] = static_cast<unsigned>(tetgen_data.trifacelist[3*i+1]);
872  third_node[i] = static_cast<unsigned>(tetgen_data.trifacelist[3*i+2]);
873  face_boundary[i] =
874  static_cast<unsigned>(tetgen_data.trifacemarkerlist[i]);
875  if (face_boundary[i]>n_bound) {n_bound=face_boundary[i];}
876  //Add the face index to each node
877  node_on_faces[first_node[i]].insert(i);
878  node_on_faces[second_node[i]].insert(i);
879  node_on_faces[third_node[i]].insert(i);
880  }
881 
882  // Extract hole center information
883  //unsigned nhole=tetgen_data.numberofholes;
884  /*if(nhole!=0)
885  {
886  Hole_centre.resize(nhole);
887 
888  // Coords counter
889  unsigned count_coords=0;
890 
891  // Loop over the holes to get centre coords
892  for(unsigned ihole=0;ihole<nhole;ihole++)
893  {
894  Hole_centre[ihole].resize(3);
895 
896  // Read the centre value
897  Hole_centre[ihole][0]=triangle_data.holelist[count_coords];
898  Hole_centre[ihole][1]=triangle_data.holelist[count_coords+1];
899  Hole_centre[ihole][2]=triangle_data.holelist[count_coords+2];
900 
901  // Increment coords counter
902  count_coords+=3;
903  }
904  }
905  else
906  {
907  Hole_centre.resize(0);
908  }*/
909 
910 
911  // Set number of boundaries
912  if(n_bound > 0)
913  {
914  this->set_nboundary(n_bound);
915  }
916 
917  // Create the elements
918  unsigned counter=0;
919  for(unsigned e=0;e<n_element;e++)
920  {
922  unsigned global_node_number = Global_node[counter];
923  if(done[global_node_number-1]==false)
924  // ... -1 because node number begins at 1 in tetgen
925  {
926  //If the node is on a boundary, construct a boundary node
927  if((boundary_markers_flag==1) &&
928  (bound[global_node_number-1] > 0))
929  {
930  //Construct the boundary ndoe
931  Node_pt[global_node_number-1] =
933 
934  //Add to the boundary lookup scheme
935  add_boundary_node(bound[global_node_number-1]-1,
936  Node_pt[global_node_number-1]);
937  }
938  //Otherwise just construct a normal node
939  else
940  {
941  Node_pt[global_node_number-1] =
943  }
944 
945  done[global_node_number-1]=true;
946  Node_pt[global_node_number-1]->x(0)=x_node[global_node_number-1];
947  Node_pt[global_node_number-1]->x(1)=y_node[global_node_number-1];
948  Node_pt[global_node_number-1]->x(2)=z_node[global_node_number-1];
949  }
950  //Otherwise just copy the node numbr accross
951  else
952  {
953  finite_element_pt(e)->node_pt(3) = Node_pt[global_node_number-1];
954  }
955  counter++;
956 
957  //Loop over the other nodes
958  for(unsigned j=0;j<(n_local_node-1);j++)
959  {
960  global_node_number=Global_node[counter];
961  if(done[global_node_number-1]==false)
962  // ... -1 because node number begins at 1 in tetgen
963  {
964  //If we're on a boundary
965  if((boundary_markers_flag==1) &&
966  (bound[global_node_number-1] > 0))
967  {
968  //Construct the boundary ndoe
969  Node_pt[global_node_number-1] =
971 
972  //Add to the boundary lookup scheme
973  add_boundary_node(bound[global_node_number-1]-1,
974  Node_pt[global_node_number-1]);
975  }
976  else
977  {
978  Node_pt[global_node_number-1]=finite_element_pt(e)->construct_node(j);
979  }
980  done[global_node_number-1]=true;
981  Node_pt[global_node_number-1]->x(0)=x_node[global_node_number-1];
982  Node_pt[global_node_number-1]->x(1)=y_node[global_node_number-1];
983  Node_pt[global_node_number-1]->x(2)=z_node[global_node_number-1];
984  }
985  //Otherwise copy the pointer over
986  else
987  {
988  finite_element_pt(e)->node_pt(j) = Node_pt[global_node_number-1];
989  }
990  counter++;
991  }
992  }
993 
994 
995  // Resize the "matrix" that stores the boundary id for each
996  // face in each element.
997  Face_boundary.resize(n_element);
998  Face_index.resize(n_element);
999  Edge_index.resize(n_element);
1000 
1001 
1002  //0-based index scheme used to construct a global lookup for
1003  //each face that will be used to uniquely construct interior
1004  //face nodes.
1005  //The faces that lie on boundaries will have already been allocated so
1006  //we can start from there
1007  Nglobal_face = n_face;
1008 
1009  // Storage for the edges associated with each node.
1010  // Nodes are indexed using Tetgen's 1-based scheme, which is why
1011  // there is a +1 here
1012  Vector<std::set<unsigned> > node_on_edges(n_node+1);
1013 
1014  //0-based index scheme used to construct a global lookup for each
1015  //edge that will be used to uniquely construct interior edge nodes
1016  Nglobal_edge = 0;
1017 
1018  // Conversion from the edge numbers to the nodes at the end
1019  // of each each edge
1020  const unsigned first_local_edge_node[6] = {0,0,0,1,2,1};
1021  const unsigned second_local_edge_node[6] = {1,2,3,2,3,3};
1022 
1023  // Loop over the elements
1024  for(unsigned e=0;e<n_element;e++)
1025  {
1026  // Each element has four faces
1027  Face_boundary[e].resize(4);
1028  Face_index[e].resize(4);
1029  // By default each face is NOT on a boundary
1030  for(unsigned i=0;i<4;i++)
1031  {
1032  Face_boundary[e][i]=0;
1033  }
1034 
1035  Edge_index[e].resize(6);
1036 
1037  // Read out the global node numbers of the nodes from
1038  // tetgen's 1-based numbering.
1039  // The offset is to match the offset used above
1040  const unsigned element_offset = e*n_local_node;
1041  unsigned glob_num[4] = {0,0,0,0};
1042  for(unsigned i=0;i<4;++i)
1043  {
1044  glob_num[i] = Global_node[element_offset + ((i+1)%4)];
1045  }
1046 
1047  // Now we know the global node numbers of the elements' four nodes
1048  // in tetgen's 1-based numbering.
1049 
1050  //Determine whether any of the element faces have already been allocated an
1051  //index. This may be because they are located on boundaries, or
1052  //have already been visited
1053  //The global index for the i-th face will be stored in Face_index[i]
1054 
1055  //Loop over the local faces in the element
1056  for(unsigned i=0;i<4;++i)
1057  {
1058  //On the i-th face, our numbering convention is such that
1059  //it is the (3-i)th node of the element that is omitted
1060  const unsigned omitted_node = 3-i;
1061 
1062  //Start with the set of global faces associated with the i-th node
1063  //which is always on the i-th face
1064  std::set<unsigned> input = node_on_faces[glob_num[i]];
1065 
1066  //If there is no data yet, not point doing intersections
1067  //the face has not been set up
1068  if(input.size() > 0)
1069  {
1070  //Loop over the other nodes on the face
1071  unsigned local_node = i;
1072  for(unsigned i2=0;i2<3;++i2)
1073  {
1074  //Create the next node index (mod 4)
1075  local_node = (local_node+1)%4;
1076  //Only take the intersection of nodes on the face
1077  //i.e. not 3-i
1078  if(local_node != omitted_node)
1079  {
1080  //Local storage for the intersection
1081  std::set<unsigned> intersection_result;
1082  //Calculate the intersection of the current input
1083  //and next node
1084  std::set_intersection(input.begin(),input.end(),
1085  node_on_faces[glob_num[local_node]].begin(),
1086  node_on_faces[glob_num[local_node]].end(),
1087  std::insert_iterator<std::set<unsigned> >(
1088  intersection_result,
1089  intersection_result.begin()));
1090  //Let the result be the next input
1091  input = intersection_result;
1092  }
1093  }
1094  }
1095 
1096  //If the nodes share more than one global face index, then
1097  //we have a problem
1098  if(input.size() > 1)
1099  {
1100  throw OomphLibError(
1101  "Nodes in scaffold mesh share more than one global face",
1102  OOMPH_CURRENT_FUNCTION,
1103  OOMPH_EXCEPTION_LOCATION);
1104  }
1105 
1106  //If the element's face is not already allocated, the intersection
1107  //will be empty
1108  if(input.size()==0)
1109  {
1110  //Allocate the next global index
1111  Face_index[e][i] = Nglobal_face;
1112  //Associate the new face index with the nodes
1113  for(unsigned i2=0;i2<4;++i2)
1114  {
1115  //Don't add the omitted node
1116  if(i2 != omitted_node)
1117  {
1118  node_on_faces[glob_num[i2]].insert(Nglobal_face);
1119  }
1120  }
1121  //Increment the global face index
1122  ++Nglobal_face;
1123  }
1124  //Otherwise we already have a face
1125  else if(input.size()==1)
1126  {
1127  const unsigned global_face_index = *input.begin();
1128  //Set the face index
1129  Face_index[e][i] = global_face_index;
1130  //Allocate the boundary index, if it's a boundary
1131  if(global_face_index < n_face)
1132  {
1133  Face_boundary[e][i] = face_boundary[global_face_index];
1134  //Add the nodes to the boundary look-up scheme in
1135  //oomph-lib (0-based) index
1136  for(unsigned i2=0;i2<4;++i2)
1137  {
1138  //Don't add the omitted node
1139  if(i2 != omitted_node)
1140  {
1141  add_boundary_node(face_boundary[global_face_index]-1,
1142  Node_pt[glob_num[i2]-1]);
1143  }
1144  }
1145  }
1146  }
1147  } //end of loop over the faces
1148 
1149 
1150  //Loop over the element edges and assign global edge numbers
1151  for(unsigned i=0;i<6;++i)
1152  {
1153  //Storage for the result of the intersection
1154  std::vector<unsigned> local_edge_index;
1155 
1156  const unsigned first_global_num = glob_num[first_local_edge_node[i]];
1157  const unsigned second_global_num = glob_num[second_local_edge_node[i]];
1158 
1159  //Find the common global edge index for the nodes on
1160  //the i-th element edge
1161  std::set_intersection(node_on_edges[first_global_num].begin(),
1162  node_on_edges[first_global_num].end(),
1163  node_on_edges[second_global_num].begin(),
1164  node_on_edges[second_global_num].end(),
1165  std::insert_iterator<std::vector<unsigned> >(
1166  local_edge_index,local_edge_index.begin()));
1167 
1168  //If the nodes share more than one global edge index, then
1169  //we have a problem
1170  if(local_edge_index.size() > 1)
1171  {
1172  throw OomphLibError(
1173  "Nodes in scaffold mesh share more than one global edge",
1174  OOMPH_CURRENT_FUNCTION,
1175  OOMPH_EXCEPTION_LOCATION);
1176  }
1177 
1178  //If the element's edge is not already allocated, the intersection
1179  //will be empty
1180  if(local_edge_index.size()==0)
1181  {
1182  //Allocate the next global index
1183  Edge_index[e][i] = Nglobal_edge;
1184  //Associate the new edge index with the nodes
1185  node_on_edges[first_global_num].insert(Nglobal_edge);
1186  node_on_edges[second_global_num].insert(Nglobal_edge);
1187  //Increment the global edge index
1188  ++Nglobal_edge;
1189  }
1190  //Otherwise we already have an edge
1191  else if(local_edge_index.size()==1)
1192  {
1193  //Set the edge index from the result of the intersection
1194  Edge_index[e][i] = local_edge_index[0];
1195  }
1196  }
1197 
1198  } // end for e
1199 
1200 
1201  //Now determine whether any edges lie on boundaries by using the
1202  //face boundary scheme and
1203 
1204  //Resize the storage
1205  Edge_boundary.resize(Nglobal_edge,false);
1206 
1207  //Now loop over all the boundary faces and mark that all edges
1208  //must also lie on the bounadry
1209  for(unsigned i=0;i<n_face;++i)
1210  {
1211  {
1212  //Storage for the result of the intersection
1213  std::vector<unsigned> local_edge_index;
1214 
1215  //Find the common global edge index for first edge of the face
1216  std::set_intersection(node_on_edges[first_node[i]].begin(),
1217  node_on_edges[first_node[i]].end(),
1218  node_on_edges[second_node[i]].begin(),
1219  node_on_edges[second_node[i]].end(),
1220  std::insert_iterator<std::vector<unsigned> >(
1221  local_edge_index,local_edge_index.begin()));
1222 
1223  //If the nodes do not share exactly one global edge index, then
1224  //we have a problem
1225  if(local_edge_index.size() != 1)
1226  {
1227  throw OomphLibError(
1228  "Nodes in scaffold mesh face do not share exactly one global edge",
1229  OOMPH_CURRENT_FUNCTION,
1230  OOMPH_EXCEPTION_LOCATION);
1231  }
1232  else
1233  {
1234  Edge_boundary[local_edge_index[0]] = true;
1235  }
1236  }
1237 
1238  {
1239  //Storage for the result of the intersection
1240  std::vector<unsigned> local_edge_index;
1241 
1242  //Find the common global edge index for second edge of the face
1243  std::set_intersection(node_on_edges[second_node[i]].begin(),
1244  node_on_edges[second_node[i]].end(),
1245  node_on_edges[third_node[i]].begin(),
1246  node_on_edges[third_node[i]].end(),
1247  std::insert_iterator<std::vector<unsigned> >(
1248  local_edge_index,local_edge_index.begin()));
1249 
1250  //If the nodes do not share exactly one global edge index, then
1251  //we have a problem
1252  if(local_edge_index.size() != 1)
1253  {
1254  throw OomphLibError(
1255  "Nodes in scaffold mesh face do not share exactly one global edge",
1256  OOMPH_CURRENT_FUNCTION,
1257  OOMPH_EXCEPTION_LOCATION);
1258  }
1259  else
1260  {
1261  Edge_boundary[local_edge_index[0]] = true;
1262  }
1263  }
1264 
1265  {
1266  //Storage for the result of the intersection
1267  std::vector<unsigned> local_edge_index;
1268 
1269  //Find the common global edge index for third edge of the face
1270  std::set_intersection(node_on_edges[first_node[i]].begin(),
1271  node_on_edges[first_node[i]].end(),
1272  node_on_edges[third_node[i]].begin(),
1273  node_on_edges[third_node[i]].end(),
1274  std::insert_iterator<std::vector<unsigned> >(
1275  local_edge_index,local_edge_index.begin()));
1276 
1277  //If the nodes do not share exactly one global edge index, then
1278  //we have a problem
1279  if(local_edge_index.size() != 1)
1280  {
1281  throw OomphLibError(
1282  "Nodes in scaffold mesh face do not share exactly one global edge",
1283  OOMPH_CURRENT_FUNCTION,
1284  OOMPH_EXCEPTION_LOCATION);
1285  }
1286  else
1287  {
1288  Edge_boundary[local_edge_index[0]] = true;
1289  }
1290  }
1291  }
1292 
1293 
1294 } //end of constructor
1295 
1296 
1297 }
1298 
1299 
unsigned global_node_number(const unsigned &i)
Return the global node of each local node listed element-by-element e*n_local_node + n_local Note tha...
Vector< Node * > Node_pt
Vector of pointers to nodes.
Definition: mesh.h:194
void add_boundary_node(const unsigned &b, Node *const &node_pt)
Add a (pointer to) a node to the b-th boundary.
Definition: mesh.cc:246
unsigned Nglobal_face
Storage for the number of global faces.
Vector< Vector< unsigned > > Face_boundary
Vector of vectors containing the boundary ids of the elements&#39; faces.
cstr elem_len * i
Definition: cfortran.h:607
Vector< Vector< unsigned > > Edge_index
Vector of vectors containing the global edge index of.
virtual Node * construct_boundary_node(const unsigned &n)
Construct the local node n as a boundary node; that is a node that MAY be placed on a mesh boundary a...
Definition: elements.h:2420
e
Definition: cfortran.h:575
TetgenScaffoldMesh()
Empty constructor.
unsigned Nglobal_edge
Storage for the number of global edges.
virtual Node * construct_node(const unsigned &n)
Construct the local node n and return a pointer to the newly created node object. ...
Definition: elements.h:2392
void set_nboundary(const unsigned &nbound)
Set the number of boundaries in the mesh.
Definition: mesh.h:505
Vector< GeneralisedElement * > Element_pt
Vector of pointers to generalised elements.
Definition: mesh.h:197
Vector< double > Element_attribute
Vector of double attributes for each element. NOTE: This stores doubles because tetgen forces us to! ...
Vector< unsigned > Global_node
Storage for global node numbers listed element-by-element.
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2109
unsigned face_boundary(const unsigned &e, const unsigned &i) const
Return the boundary id of the i-th face in the e-th element: This is zero-based as in tetgen...
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition: mesh.h:477
Vector< Vector< unsigned > > Face_index
Vector of vectors containing the global edge index of.
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn&#39;t been defined.
std::vector< bool > Edge_boundary
Vector of booleans to indicate whether a global edge lies on a boundary.