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