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