1 /********************************************************************************
2 QuadTriTransfinite3D.cpp
3 
4 The code in this file was written by Dr. Trevor S. Strickler.
5 email: <trevor.strickler@gmail.com>
6 
7 This file is part of the QuadTri contribution to Gmsh. QuadTri allows the
8 conformal interface of quadrangle faces to triangle faces using pyramids and
9 other mesh elements.
10 
11 Trevor S. Strickler hereby transfers copyright of QuadTri files to Christophe
12 Geuzaine and J.-F. Remacle with the understanding that his contribution shall be
13 cited appropriately. See the README.txt file for license information.
14 ********************************************************************************/
15 
16 #include "QuadTriTransfinite3D.h"
17 
18 // Does the pair of MVertex pointers v1 and v2 exist in the set 'edges'?
edgeExists(MVertex * v1,MVertex * v2,std::set<std::pair<MVertex *,MVertex * >> & edges)19 static int edgeExists(MVertex *v1, MVertex *v2,
20                       std::set<std::pair<MVertex *, MVertex *> > &edges)
21 {
22   std::pair<MVertex *, MVertex *> p(std::min(v1, v2), std::max(v1, v2));
23   return edges.count(p);
24 }
25 
26 // Create the pair of MVertex pointers v1 and v2 exist in the set 'edges.'
createEdge(MVertex * v1,MVertex * v2,std::set<std::pair<MVertex *,MVertex * >> & edges)27 static void createEdge(MVertex *v1, MVertex *v2,
28                        std::set<std::pair<MVertex *, MVertex *> > &edges)
29 {
30   std::pair<MVertex *, MVertex *> p(std::min(v1, v2), std::max(v1, v2));
31   edges.insert(p);
32 }
33 
addTetrahedron(MVertex * v1,MVertex * v2,MVertex * v3,MVertex * v4,GRegion * to)34 static void addTetrahedron(MVertex *v1, MVertex *v2, MVertex *v3, MVertex *v4,
35                            GRegion *to)
36 {
37   MTetrahedron *newElem = new MTetrahedron(v1, v2, v3, v4);
38   to->tetrahedra.push_back(newElem);
39 }
40 
addPyramid(MVertex * v1,MVertex * v2,MVertex * v3,MVertex * v4,MVertex * v5,GRegion * to)41 static void addPyramid(MVertex *v1, MVertex *v2, MVertex *v3, MVertex *v4,
42                        MVertex *v5, GRegion *to)
43 {
44   MPyramid *newElem = new MPyramid(v1, v2, v3, v4, v5);
45   to->pyramids.push_back(newElem);
46 }
47 
addPrism(MVertex * v1,MVertex * v2,MVertex * v3,MVertex * v4,MVertex * v5,MVertex * v6,GRegion * to)48 static void addPrism(MVertex *v1, MVertex *v2, MVertex *v3, MVertex *v4,
49                      MVertex *v5, MVertex *v6, GRegion *to)
50 {
51   MPrism *newElem = new MPrism(v1, v2, v3, v4, v5, v6);
52   to->prisms.push_back(newElem);
53 }
54 
addHexahedron(MVertex * v1,MVertex * v2,MVertex * v3,MVertex * v4,MVertex * v5,MVertex * v6,MVertex * v7,MVertex * v8,GRegion * to)55 static void addHexahedron(MVertex *v1, MVertex *v2, MVertex *v3, MVertex *v4,
56                           MVertex *v5, MVertex *v6, MVertex *v7, MVertex *v8,
57                           GRegion *to)
58 {
59   MHexahedron *newElem = new MHexahedron(v1, v2, v3, v4, v5, v6, v7, v8);
60   to->hexahedra.push_back(newElem);
61 }
62 
63 // Function to get all the diagonals from external surfaces of a given
64 // Transfinite region tr and place them in boundary_diags.
getTransfiniteBoundaryDiags(GRegion * gr,std::set<std::pair<MVertex *,MVertex * >> * boundary_diags)65 int getTransfiniteBoundaryDiags(
66   GRegion *gr, std::set<std::pair<MVertex *, MVertex *> > *boundary_diags)
67 {
68   // Get list of faces
69   std::vector<GFace *> faces = gr->faces();
70 
71   // Perform some tests of the Transfinite volume
72 
73   // Is the region Transfinite?
74   if(gr->meshAttributes.method != MESH_TRANSFINITE) {
75     Msg::Error("In getTransfiniteBoundaryDiags(), region %d was not detected "
76                "to be a transfinite volume",
77                gr->tag());
78     return 0;
79   }
80 
81   // Found right number of faces?
82   if(faces.size() != 5 && faces.size() != 6) {
83     Msg::Error(
84       "In getTransfiniteBoundaryDiags(), number of faces does not equal "
85       "5 or 6 for region %d.",
86       gr->tag());
87     return 0;
88   }
89 
90   // Are all the faces Transfinite?
91   std::vector<GFace *>::iterator itf;
92   for(itf = faces.begin(); itf != faces.end(); itf++) {
93     if((*itf)->meshAttributes.method != MESH_TRANSFINITE) {
94       Msg::Error(
95         "In getTransfiniteBoundaryDiags(), surface %d was not detected "
96         "to be transfinite",
97         (*itf)->tag());
98       return 0;
99     }
100     if(!(*itf)->transfinite_vertices.size()) {
101       Msg::Error(
102         "In getTransfiniteBoundaryDiags(), no transfinite vertices found for "
103         "surface %d.",
104         (*itf)->tag());
105       return 0;
106     }
107   }
108 
109   // Now loop through all surfaces checking for unrecombined faces. On 3-sided
110   // surfaces, skip the first layer of triangles because they ALWAYS exist even
111   // for recombined faces.
112   for(itf = faces.begin(); itf != faces.end(); itf++) {
113     if((*itf)->quadrangles.size()) continue;
114     // For this face, loop through all sets of 4 vertices that could form a
115     // quadrangle if not subdivided.  Find which of the 4 vertices are on the
116     // diagonal that subdivides the four vertices.
117     std::vector<GEdge *> const &edges = (*itf)->edges();
118     int index_guess = 0;
119     int i_low = 0;
120     if(edges.size() == 3) {
121       if((*itf)->transfinite_vertices.size() <= 2) continue;
122       index_guess += (*itf)->transfinite_vertices[1].size() - 1;
123       i_low = 1;
124     }
125 
126     for(unsigned int i = i_low; i < (*itf)->transfinite_vertices.size() - 1;
127         i++) {
128       for(unsigned int j = 0; j < (*itf)->transfinite_vertices[i].size() - 1;
129           j++) {
130         std::vector<MVertex *> verts;
131         verts.resize(4);
132         verts[0] = (*itf)->transfinite_vertices[i][j];
133         verts[1] = (*itf)->transfinite_vertices[i + 1][j];
134         verts[2] = (*itf)->transfinite_vertices[i + 1][j + 1];
135         verts[3] = (*itf)->transfinite_vertices[i][j + 1];
136         std::pair<int, int> ind_pair =
137           FindDiagonalEdgeIndices(verts, (*itf), 0, index_guess);
138         createEdge(verts[ind_pair.first], verts[ind_pair.second],
139                    (*boundary_diags));
140         index_guess += 2;
141       }
142     }
143   }
144 
145   return 1;
146 
147 } // End of getTransfiniteBoundaryDiags()
148 
149 // Meshes either a prism or a hexahedral set of mesh vertices in a Transfinite
150 // Region with an internal vertex that is created here in the function.
meshTransfElemWithInternalVertex(GRegion * gr,std::vector<MVertex * > v,std::set<std::pair<MVertex *,MVertex * >> * boundary_diags)151 void meshTransfElemWithInternalVertex(
152   GRegion *gr, std::vector<MVertex *> v,
153   std::set<std::pair<MVertex *, MVertex *> > *boundary_diags)
154 {
155   int v_size = v.size();
156   int n_lat_tmp;
157   if(v_size == 6)
158     n_lat_tmp = 3;
159   else if(v_size == 8)
160     n_lat_tmp = 4;
161   else {
162     Msg::Error("In meshTransfElemWithInternalVertex(), number of element "
163                "vertices does not equal 6 or 8.");
164     return;
165   }
166 
167   const int n_lat = n_lat_tmp;
168 
169   // find vertex node indices for diagonalized faces
170   std::vector<int> n1, n2;
171   bool found_diags = false;
172   int n_size = 0;
173   if(n_lat == 3) {
174     n1.assign(n_lat, -1);
175     n2.assign(n_lat, -2);
176     n_size = 3;
177   }
178   else if(n_lat == 4) {
179     n1.assign(n_lat + 2, -1);
180     n2.assign(n_lat + 2, -1);
181     n_size = 6;
182   }
183 
184   for(int p = 0; p < n_size; p++) {
185     n1[p] = -p * p - p - 1;
186     n2[p] = -p * p - p - 2;
187     if(p < n_lat || n_lat == 3) {
188       if(v[p] == v[p + n_lat] ||
189          v[(p + 1) % n_lat] == v[(p + 1) % n_lat + n_lat])
190         continue;
191       else if(edgeExists(v[p], v[(p + 1) % n_lat + n_lat], (*boundary_diags))) {
192         n1[p] = p;
193         n2[p] = (p + 1) % n_lat + n_lat;
194         found_diags = true;
195       }
196       else if(edgeExists(v[p + n_lat], v[(p + 1) % n_lat], (*boundary_diags))) {
197         n1[p] = p + n_lat;
198         n2[p] = (p + 1) % n_lat;
199         found_diags = true;
200       }
201     }
202     else {
203       int add = (p == n_lat) ? 0 : n_lat;
204       if(edgeExists(v[add], v[add + 2], (*boundary_diags))) {
205         n1[p] = add;
206         n2[p] = add + 2;
207         found_diags = true;
208       }
209       else if(edgeExists(v[add + 1], v[add + 3], (*boundary_diags))) {
210         n1[p] = add + 1;
211         n2[p] = add + 3;
212         found_diags = true;
213       }
214     }
215   }
216 
217   if(!found_diags) {
218     if(n_lat == 3)
219       addPrism(v[0], v[1], v[2], v[3], v[4], v[5], gr);
220     else if(n_lat == 4)
221       addHexahedron(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], gr);
222     return;
223   }
224 
225   // make the internal vertex
226   std::vector<double> centroid = QtFindVertsCentroid(v);
227   MVertex *v_int = new MVertex(centroid[0], centroid[1], centroid[2], gr);
228   gr->mesh_vertices.push_back(v_int);
229 
230   // build all pyramids/tetra
231   for(int p = 0; p < n_lat; p++) {
232     int p2 = (p + 1) % n_lat;
233     if(v[p] == v[p + n_lat] && v[p2] == v[p2 + n_lat])
234       continue;
235     else if(v[p] == v[p + n_lat] || v[p2] == v[p2 + n_lat]) {
236       MVertex *v_dup = (v[p] == v[p + n_lat]) ? v[p] : v[p2];
237       MVertex *v_non_dup = (v_dup == v[p]) ? v[p2] : v[p];
238       MVertex *v_non_dup2 = (v_non_dup == v[p]) ? v[p + n_lat] : v[p2 + n_lat];
239       addTetrahedron(v_dup, v_non_dup, v_non_dup2, v_int, gr);
240     }
241     else if(n1[p] == p || n2[p] == p) {
242       addTetrahedron(v[p], v[p2], v[p2 + n_lat], v_int, gr);
243       addTetrahedron(v[p], v[p2 + n_lat], v[p + n_lat], v_int, gr);
244     }
245     else if(n1[p] == p + n_lat || n2[p] == p + n_lat) {
246       addTetrahedron(v[p], v[p2], v[p + n_lat], v_int, gr);
247       addTetrahedron(v[p2], v[p2 + n_lat], v[p + n_lat], v_int, gr);
248     }
249     else
250       addPyramid(v[p], v[p2], v[p2 + n_lat], v[p + n_lat], v_int, gr);
251   }
252 
253   if(n_lat == 3) {
254     // bottom and top
255     addTetrahedron(v[0], v[1], v[2], v_int, gr);
256     addTetrahedron(v[3], v[5], v[4], v_int, gr);
257   }
258   else if(n_lat == 4) {
259     for(int p = 4; p < 6; p++) {
260       int add = (p == 4) ? 0 : 4;
261       if(n1[p] == 0 + add || n2[p] == 0 + add) {
262         addTetrahedron(v[add], v[1 + add], v[2 + add], v_int, gr);
263         addTetrahedron(v[add], v[2 + add], v[3 + add], v_int, gr);
264       }
265       else if(n1[p] == 1 + add || n2[p] == 1 + add) {
266         addTetrahedron(v[1 + add], v[2 + add], v[3 + add], v_int, gr);
267         addTetrahedron(v[1 + add], v[3 + add], v[add], v_int, gr);
268       }
269       else
270         addPyramid(v[add], v[1 + add], v[2 + add], v[3 + add], v_int, gr);
271     }
272   }
273 
274 } // End of meshTransfiniteWithInternalVertex()
275