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-2025 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"
29
30namespace 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(
55 }
56#endif
57
58
59 // Read in number of elements
60 unsigned n_element;
62
63 // Read in number of nodes per element
64 unsigned 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;
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(
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
92
93 // Initialise (global) node counter
94 unsigned k = 0;
95 // Are there attributes?
96 unsigned 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 {
106 for (unsigned j = 0; j < n_local_node; j++)
107 {
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 {
119 for (unsigned j = 0; j < n_local_node; j++)
120 {
122 k++;
123 }
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(
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;
161
162#ifdef PARANOID
163 if (dimension != 3)
164 {
165 throw OomphLibError("The dimesion of the nodes must be 3\n",
168 }
169#endif
170
171 // Flag for attributes
173
174 // Flag for boundary markers
175 unsigned 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
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 {
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 {
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 {
226 node_file >> x_node[i];
227 node_file >> y_node[i];
228 node_file >> z_node[i];
230 node_file >> bound[i];
231 }
232 node_file.close();
233 }
234 else
235 {
236 for (unsigned i = 0; i < n_node; i++)
237 {
239 node_file >> x_node[i];
240 node_file >> y_node[i];
241 node_file >> z_node[i];
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(
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
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
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
305
306 // Extract information for each segment
307 for (unsigned i = 0; i < n_face; i++)
308 {
314 if (face_boundary[i] > n_bound)
315 {
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 {
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
350 }
351 // Otherwise just construct a normal node
352 else
353 {
356 }
357
358 done[global_node_number - 1] = true;
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 {
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
388 }
389 else
390 {
393 }
394 done[global_node_number - 1] = true;
395 Node_pt[global_node_number - 1]->x(0) =
397 Node_pt[global_node_number - 1]->x(1) =
399 Node_pt[global_node_number - 1]->x(2) =
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
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
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
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(),
506 std::insert_iterator<std::set<unsigned>>(
508 // Let the result be the next input
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",
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 {
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
548 // Allocate the boundary index, if it's a boundary
550 {
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 {
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
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(),
583 std::insert_iterator<std::vector<unsigned>>(
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",
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
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
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(),
638 std::insert_iterator<std::vector<unsigned>>(
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",
649 }
650 else
651 {
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(),
665 std::insert_iterator<std::vector<unsigned>>(
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",
676 }
677 else
678 {
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(),
692 std::insert_iterator<std::vector<unsigned>>(
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",
703 }
704 else
705 {
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;
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(
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
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 {
802 }
803
804 // Create storage for nodal positions and boundary markers
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
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
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]);
878 static_cast<unsigned>(tetgen_data.trifacemarkerlist[i]);
879 if (face_boundary[i] > n_bound)
880 {
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 {
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
943 }
944 // Otherwise just construct a normal node
945 else
946 {
949 }
950
951 done[global_node_number - 1] = true;
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 {
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
981 }
982 else
983 {
986 }
987 done[global_node_number - 1] = true;
988 Node_pt[global_node_number - 1]->x(0) =
990 Node_pt[global_node_number - 1]->x(1) =
992 Node_pt[global_node_number - 1]->x(2) =
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
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
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(),
1099 std::insert_iterator<std::set<unsigned>>(
1101 // Let the result be the next input
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",
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
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 {
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
1141 // Allocate the boundary index, if it's a boundary
1143 {
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 {
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
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(),
1176 std::insert_iterator<std::vector<unsigned>>(
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",
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
1195 // Associate the new edge index with the nodes
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
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(),
1231 std::insert_iterator<std::vector<unsigned>>(
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",
1242 }
1243 else
1244 {
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(),
1258 std::insert_iterator<std::vector<unsigned>>(
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",
1269 }
1270 else
1271 {
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(),
1285 std::insert_iterator<std::vector<unsigned>>(
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",
1296 }
1297 else
1298 {
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
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
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:2513
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:2542
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
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:477
void set_nboundary(const unsigned &nbound)
Set the number of boundaries in the mesh.
Definition mesh.h:509
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....
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
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.
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).