1 /*
2 XLiFE++ is an extended library of finite elements written in C++
3 Copyright (C) 2014 Lunéville, Eric; Kielbasiewicz, Nicolas; Lafranche, Yvon; Nguyen, Manh-Ha; Chambeyron, Colin
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 /*!
18 \file TetrahedronMesh.cpp
19 \author Y. Lafranche
20 \since 07 Dec 2007
21 \date 08 Mar 2014
22
23 \brief Implementation of xlifepp::subdivision::TetrahedronMesh class members and related functions
24 */
25
26 #include "TetrahedronMesh.hpp"
27
28 using namespace std;
29
30 namespace xlifepp {
31 namespace subdivision {
32
33 //-------------------------------------------------------------------------------
34 // I/O utilities
35 //-------------------------------------------------------------------------------
36
37 /*!
38 Prints, on stream ftex, Fig4TeX instructions to draw the numbering convention
39 for a tetrahedron mesh of order k.
40 To be called as TetrahedronMesh(1,0,k,0).printTeXNumberConvention(ftex);
41 */
printTeXNumberConvention(ostream & ftex) const42 void TetrahedronMesh::printTeXNumberConvention(ostream& ftex) const {
43 if (subdiv_level_ > 0 || listT_.size() > 1) {
44 cerr << "*** Error in printTeXNumberConvention:" << endl;
45 cerr << "*** Numbering convention output function expects only one element.";
46 cerr << endl;
47 }
48 int dim = max(5,int(order_+1));
49 ftex << "\\input fig4tex.tex" << endl;
50 ftex << "\\def\\drawFace#1#2#3{" << endl;
51 ftex << "\\psset(color=\\FaceColor, fill=yes)\\psline[#1,#2,#3]" << endl;
52 ftex << "\\psset(color=\\defaultcolor, fill=no)\\psline[#1,#2,#3,#1]}"<<endl;
53 ftex << "%" << endl;
54 ftex << "\\def\\defpts{" << endl;
55 printTeXInArea(ftex,0,boundaryArea);
56 ftex << "}" << endl;
57 ftex << "% 1. Definition of characteristic points" << endl;
58 ftex << "\\figinit{" << dim << "cm,orthogonal}" << endl;
59 ftex << "\\defpts" << endl;
60 ftex << "\\figset proj(psi=40, theta=45)" << endl;
61 ftex << "%" << endl;
62 ftex << "% 2. Creation of the graphical file" << endl;
63 ftex << "\\psbeginfig{}" << endl;
64 ftex << "\\figpt 0:(0,-1,0)\\psaxes 0(0.2)" << endl;
65 for (number_t numBoundary=1; numBoundary<=3; numBoundary++) {
66 printTeXFacesInArea(ftex,boundaryArea,numBoundary);
67 }
68 ftex << "\\psendfig" << endl;
69 ftex << "%" << endl;
70 ftex << "% 3. Writing text on the figure" << endl;
71 ftex << "\\figvisu{\\figBoxA}{Points on the background faces.}{" << endl;
72 ftex << "\\figsetmark{$\\figBullet$}\\figsetptname{{\\bf #1}}" << endl;
73 for (number_t numBoundary=1; numBoundary<=3; numBoundary++) {
74 printTeXInArea(ftex,1,boundaryArea,numBoundary);
75 }
76 ftex << "}" << endl;
77 // Last face of the tetrahedron
78 ftex << "% 1. Definition of characteristic points" << endl;
79 ftex << "\\figinit{4cm,orthogonal}" << endl;
80 ftex << "\\defpts" << endl;
81 ftex << "\\figset proj(psi=40, theta=45)" << endl;
82 ftex << "%" << endl;
83 number_t numBoundary4 = 4;
84 ftex << "% 2. Creation of the graphical file" << endl;
85 ftex << "\\psbeginfig{}" << endl;
86 printTeXFacesInArea(ftex,boundaryArea,numBoundary4);
87 ftex << "\\psendfig" << endl;
88 ftex << "%" << endl;
89 ftex << "% 3. Writing text on the figure" << endl;
90 ftex << "\\figvisu{\\figBoxB}{Points on the foreground face (smaller scale).}{" << endl;
91 ftex << "\\figsetmark{$\\figBullet$}\\figsetptname{{\\bf #1}}" << endl;
92 printTeXInArea(ftex,1,boundaryArea,numBoundary4);
93 ftex << "}" << endl;
94 ftex << "\\centerline{\\box\\figBoxA\\hfil\\box\\figBoxB}" << endl;
95 // Internal vertices
96 vector<number_t> VI = rk_verticesInside();
97 if (VI.size() > 0)
98 {
99 ftex << "% 1. Definition of characteristic points" << endl;
100 ftex << "\\figinit{" << dim << "cm,orthogonal}" << endl;
101 ftex << "\\defpts" << endl;
102 ftex << "\\figset proj(psi=40, theta=45)" << endl;
103 ftex << "%" << endl;
104 ftex << "% 2. Creation of the graphical file" << endl;
105 ftex << "\\psbeginfig{}" << endl;
106 for (number_t numBoundary=1; numBoundary<=3; numBoundary++) {
107 printTeXFacesInArea(ftex,boundaryArea,numBoundary);
108 }
109 ftex << "\\psendfig" << endl;
110 ftex << "%" << endl;
111 ftex << "% 3. Writing text on the figure" << endl;
112 ftex << "\\figvisu{\\figBoxA}{Internal points.}{" << endl;
113 ftex << "\\figsetmark{$\\figBullet$}\\figsetptname{{\\bf #1}}" << endl;
114 ftex << "% Vertices inside the boundaries" << endl;
115 printTeXfigpt(ftex,VI);
116 printTeXfigwrite(ftex,VI);
117 ftex << "}" << endl;
118 ftex << "\\smallskip\\centerline{\\box\\figBoxA}" << endl;
119 }
120 ftex << "\\medskip" << endl;
121 ftex << "\\centerline{\\bf Nodes numbering convention for the tetrahedron of order "<< order_ << ".}" << endl;
122 ftex << "%-------------------------------- End of figure --------------------------------" << endl;
123 ftex << "\\vfill\\eject" << endl;
124 }
125
126
127 //-------------------------------------------------------------------------------
128 // Protected member functions
129 //-------------------------------------------------------------------------------
130 /*!
131 Subdivision of a tetrahedron T into 8 tetrahedrons.
132
133 Input arguments :
134 \param T : tetrahedron to be subdivided
135
136 Input and output arguments (updated by this function) :
137 \param ElementNum: number of the last tetrahedron created in the mesh
138 \param VertexNum : number of the last vertex created in the mesh
139 \param listT : list of tetrahedrons in the mesh
140 \param SeenEdges : temporary map used to build the mesh (contains the edges already seen,
141 i.e. taken into account, along with the associated vertex number,
142 in order to avoid vertex duplication)
143
144 Numbering and orientation convention
145 - - - - - - - - - - - - - - - - - - -
146 The vertices, faces and edges are numbered according to a convention given in Tetrahedron.hpp.
147 The tetrahedron T=(1,2,3,4) is subdivided into 8 "sub-"tetrahedrons: 4 at each
148 vertex plus 4 in the "kernel". Thus, the subdivision algorithm involves 6 more
149 vertices. They are the midpoints of the edges of the tetrahedron T for every
150 internal edge. For boundary edges, the additional points are also the midpoints
151 if a flat subdivision is requested ; otherwise, the midpoints are projected onto
152 the boundary surface. Their numbers follow the numbering convention given below:
153 \verbatim
154 3 3
155 /.\ /.\
156 / . \ / . \
157 / . \ / . \
158 / . \ / . \
159 / . \ SUBDIVISION / 7 \
160 / . \ / . \
161 / . \ ----> / . \
162 / . \ 9 . 8
163 / . 4 . \ / . 4 . \
164 / . . \ / . . \
165 / . . \ / . . \
166 / . . \ / .5 6. \
167 / . . \ / . . \
168 / . . \ / . . \
169 /. .\ /. .\
170 /_______________________________\ /_______________10______________\
171 1 2 1 2
172 \endverbatim
173 If, for instance, the points 1, 2 and 3 lie on the boundary and if a curved mesh (non
174 flat) mesh is requested, then the points 8, 9 and 10 are projected onto the boundary
175 surface.
176
177 Each "sub-"tetrahedron is defined by a sequence of vertices that matches the above
178 numbering convention.
179 Nota: the orientation of the edges is not used in this subdivision algorithm.
180 */
algoSubdiv(const Tetrahedron & T,number_t & ElementNum,number_t & VertexNum,vector<Tetrahedron> & listT,map_pair_num & SeenEdges)181 void TetrahedronMesh::algoSubdiv(const Tetrahedron& T, number_t& ElementNum,
182 number_t& VertexNum, vector<Tetrahedron>& listT,
183 map_pair_num& SeenEdges) {
184 // Creation of at most nb_edges_by_element_ new vertices: their rank in the list listV_ is retained in rV
185 vector<number_t> rV(nb_edges_by_element_);
186 for (number_t i=0; i<nb_edges_by_element_; i++) {
187 rV[i]=createVertex(VertexNum,T.rkOfO1VeOnEdge(i+1),SeenEdges);}
188
189 // To help transmission of the curved boundary face number to the correct first 3 sub-tetrahedrons
190 // (which are defined with the same vertex ordering as T, so this boundary face number is the same):
191 number_t bdSideNo[]={0,0,0,0}, bdfanum=T.bdSideOnCP();
192 if (bdfanum > 0) {
193 for (number_t i=0; i<3; i++) {
194 bdSideNo[T.getrkFace(bdfanum-1,i)] = bdfanum;
195 }
196 }
197
198 // 1. Tetrahedrons at each vertex
199 // Vi is T.rankOfVertex(i) for i=1,...4 and is rV[i-5] for i=5,...10.
200 listT.push_back(Tetrahedron(++ElementNum,T.rankOfVertex(1),rV[5],rV[4],rV[0],bdSideNo[0])); // ( 1,10,9,5)
201 listT.push_back(Tetrahedron(++ElementNum,rV[5],T.rankOfVertex(2),rV[3],rV[1],bdSideNo[1])); // (10, 2,8,6)
202 listT.push_back(Tetrahedron(++ElementNum,rV[4],rV[3],T.rankOfVertex(3),rV[2],bdSideNo[2])); // ( 9, 8,3,7)
203 listT.push_back(Tetrahedron(++ElementNum,rV[0],rV[1],rV[2],T.rankOfVertex(4),bdSideNo[3])); // ( 5, 6,7,4)
204
205 // 2. Tetrahedrons inside the two pyramids
206 // Index arrays to create the following 4 tetrahedrons, according to 3 possible subdivisions:
207 // s[0][t[i][j]] creates ( 5,8,10,9), ( 5,8,7, 6), (8, 5,7,9), (8, 5,10, 6), separation plane (5,10,8,7) ;
208 // s[1][t[i][j]] creates ( 6,9, 5,7), ( 6,9,8,10), (9, 6,8,7), (9, 6, 5,10), separation plane (5, 6,8,9) ;
209 // s[2][t[i][j]] creates (10,7, 6,8), (10,7,9, 5), (7,10,9,8), (7,10, 6, 5), separation plane (10,6,7,9).
210 int t[4][4]={{0,1,3,2}, {0,1,5,4}, {1,0,5,2}, {1,0,3,4}};
211 int s[3][6]={{0,3,4,5,1,2}, {1,4,2,0,5,3}, {5,2,3,1,0,4}};
212 // In order to obtain the 4 tetrahedrons, we choose the internal edge to be
213 // the shortest of the 3 diagonals 5-8, 6-9 or 7-10.
214 real_t d[2]={listV_[rV[0]].squareDistance(listV_[rV[3]]),
215 listV_[rV[1]].squareDistance(listV_[rV[4]])};
216 int k = d[0] < d[1] ? 0 : 1;
217 if (listV_[rV[2]].squareDistance(listV_[rV[5]]) < d[k]) k = 2;
218 // To help transmission of the curved boundary face number to the last sub-tetrahedron: we must identify
219 // which sub-tetrahedron is concerned and which face of this sub-tetrahedron is concerned.
220 // If bdfanum = 1 (resp. 2, 3, 4), the curved boundary subface is (6,7,8) (resp. (5,7,9), (5,6,10), (8,9,10))
221 // which belongs to one of the 4 tetrahedrons to be created, according to the subdivision k used. The
222 // correspondence is stored in the following index array, so that 0 <= u[k][bdfanum-1] <= 3 defines the right
223 // sub-tetrahedron in one of the 3 lists above:
224 // subface (6,7,8) (5,7,9), (5,6,10), (8,9,10)
225 int u[3][4] = {{1, 2, 3, 0},
226 {2, 0, 3, 1},
227 {0, 1, 3, 2}};
228 // Due to the numbering used, the corresponding curved face number is 1 for all of them.
229 // Use this correspondence array to store the curved boundary face number for each tetrahedron to be created:
230 number_t bdFaceNo[3][4] = {{0,0,0,0},{0,0,0,0},{0,0,0,0}};
231 if (bdfanum > 0) {
232 for (int ik=0; ik<3; ik++) {
233 bdFaceNo[ik][u[ik][bdfanum-1]] = 1; // curved boundary face number is 1 for all
234 }
235 }
236 for (int i=0; i<4; i++){
237 listT.push_back(Tetrahedron(++ElementNum,rV[s[k][t[i][0]]],rV[s[k][t[i][1]]],
238 rV[s[k][t[i][2]]],rV[s[k][t[i][3]]],bdFaceNo[k][i]));
239 }
240 }
241
242 /*!
243 Create high order vertices inside the tetrahedron T.
244 On input, VertexNum is the previous number used. On output, it is the last one.
245 */
createHOiV(Tetrahedron & T,const number_t order,number_t & VertexNum)246 void TetrahedronMesh::createHOiV(Tetrahedron& T, const number_t order, number_t& VertexNum){
247 vector<Point> VP(nb_main_vertices_by_element_);
248 vector<refnum_t> lc(nb_main_vertices_by_element_);
249 for (number_t i=0; i<nb_main_vertices_by_element_; i++) {
250 number_t rV = T.vertices_[i];
251 VP[i] = listV_[rV].geomPt();
252 lc[i] = listV_[rV].locCode();
253 }
254 refnum_t localcod = lc[0];
255 for (number_t i=1; i<nb_main_vertices_by_element_; i++) { localcod &= lc[i]; }// localcod should be zero
256
257 Point P;
258 vector<real_t> coef(nb_main_vertices_by_element_);
259 for (number_t i1=1; i1<order; i1++) {
260 coef[0] = i1;
261 for (number_t i2=1; i2<order-i1; i2++) {
262 coef[1] = i2;
263 for (number_t i3=1; i3<order-i1-i2; i3++) {
264 coef[2] = i3;
265 coef[3] = order-i1-i2-i3;
266 P = barycenter(coef,VP);
267 // add rank of the new vertex in the tetrahedron list:
268 T.vertices_.push_back(VertexNum);
269 // store the new vertex in the general list listV_:
270 listV_.push_back(Vertex(++VertexNum,localcod,P));
271 }
272 }
273 }
274 }
275
276
277 /*!
278 Create the initial mesh (or "seed" of the mesh) to be subdivided for a geometry of revolution,
279 i.e. defined by an axis of revolution and circular sections. The general shape is a truncated
280 cone, that can be a cylinder if the radius is constant.
281 In the following, the word "object" is used to designate the geometric shape.
282
283 nbslices : number of slices of elements orthogonal to the axis of the object
284 If this input value is equal to 0, a number of slices is computed from
285 the geometrical data.
286 radius1, radius2 : radii of the object
287 CharacPts : the two end points of the axis of the object
288 VertexNum : number of the last vertex created (modified on output)
289 ElementNum : number of the last element created (modified on output)
290 vSI : vector for shape information at both ends of the object
291 */
initMesh(const number_t nbslices,const real_t radius1,const real_t radius2,const vector<Point> & CharacPts,number_t & VertexNum,number_t & ElementNum,const vector<ShapeInfo> & vSI)292 void TetrahedronMesh::initMesh(const number_t nbslices, const real_t radius1, const real_t radius2,
293 const vector<Point>& CharacPts, number_t& VertexNum,
294 number_t& ElementNum, const vector<ShapeInfo>& vSI){
295 real_t R1, R2, slice_height;
296 Point P1, P2;
297 vector<ShapeInfo> cvSI;
298 string bottomPt, topPt, shape;
299 bool iscone;
300 DefaultGeometry *DG;
301 SurfPlane *SP;
302 SurfCone *SC;
303 vector <PatchGeometry *> EBS;
304 int nb_sl;
305 initRevMesh(nbslices, radius1,radius2, CharacPts, vSI,
306 R1,R2, P1,P2, cvSI, bottomPt, topPt, iscone, shape, DG, SP, SC, EBS, nb_sl, slice_height);
307
308 number_t iPh = 0, iPhSD;
309 title_ = shape + " - Tetrahedron mesh";
310 { // Definition of 3 boundaries...
311 //vector<number_t> B_patch{1,2,3}, B_dimen{2,2,2}; Ready for C++11
312 vector<number_t> B_patch(3, 1), B_dimen(3, 2);
313 B_patch[1] = 2; B_patch[2] = 3;
314 number_t nbBound = B_patch.size();
315 // ... nbIntrf interfaces...
316 // (two orthogonal internal planes containing the axis, a plane between
317 // two successive slices, plus eventually one plane between each end slice
318 // and the corresponding "hat" end shape)
319 number_t nbIntrf = 2 + nb_sl-1, nbSbDom = nb_sl;
320 if (EBS[0]->curvedShape()) {nbIntrf++; nbSbDom++;} // if non Flat
321 if (EBS[1]->curvedShape()) {nbIntrf++; nbSbDom++;}
322 vector<number_t> I_patch(nbIntrf), I_dimen(nbIntrf);
323 number_t numPa = nbBound;
324 for (size_t i=0; i<nbIntrf; i++) { I_patch[i] = ++numPa; I_dimen[i] = 2; }
325 // ...and nbSbDom subdomains
326 // (each slice plus eventually each "hat" end shape)
327 vector<number_t> D_patch(nbSbDom), D_dimen(nbSbDom);
328 for (size_t i=0; i<nbSbDom; i++) { D_patch[i] = ++numPa; D_dimen[i] = 3; }
329
330 number_t nbBI = nbBound + nbIntrf;
331 number_t nbPatches = nbBI + nbSbDom;
332 vector<PatchGeometry *> P_geom(nbPatches);
333 P_geom[0] = EBS[0], P_geom[1] = EBS[1];
334 for (size_t i=2; i<nbBI; i++) { P_geom[i] = SP; }
335 for (size_t i=nbBI; i<nbPatches; i++) { P_geom[i] = DG; }
336
337 if (type_ == 0) {
338 // P_geom = {EBS[0],EBS[1],SP, SP,... SP, DG,... DG}
339 // \__ nbBound __/ \_nbIntrf_/ \_nbSbDom_/
340 delete SC; // not needed
341 }
342 else {
343 // P_geom = {EBS[0],EBS[1],SC, SP,... SP, DG,... DG}
344 // \__ nbBound __/ \_nbIntrf_/ \_nbSbDom_/
345 P_geom[2] = SC;
346 }
347 TG_ = TopoGeom(B_patch,B_dimen, I_patch,I_dimen, D_patch,D_dimen, P_geom);
348 iPhSD = nbBound+nbIntrf;
349 }
350 refnum_t sBEs1, sBEs2, sBS, sIIp1, sIIp2; // localization code of the following patches
351 // Description of the boundary patches
352 TG_.setDescription(++iPh) = "Boundary: End surface on the side of end point " + bottomPt; sBEs1 = TG_.sigma(iPh); // patch 1
353 TG_.setDescription(++iPh) = "Boundary: End surface on the side of end point " + topPt; sBEs2 = TG_.sigma(iPh); // patch 2
354 TG_.setDescription(++iPh) = "Boundary: Surface of the " + shape; sBS = TG_.sigma(iPh); // patch 3
355 // Description of the first two interface patches
356 TG_.setDescription(++iPh) = "Interface: Internal plane 1 containing axis"; sIIp1 = TG_.sigma(iPh); // patch 4
357 number_t iPhIIp1 = iPh;
358 TG_.setDescription(++iPh) = "Interface: Internal plane 2 containing axis"; sIIp2 = TG_.sigma(iPh); // patch 5
359
360 // Steps 1 and 2 of the construction
361 vector<Point> Pt;
362 real_t prevRitrf;
363 number_t rVaFirst, rVc;
364 ElementNum = minElementNum_ - 1;
365 VertexNum = minVertexNum_ - 1;
366 startConeTrunk(R1, P1, EBS, SC, slice_height, nb_sl, iscone, sBEs1, sBS, sIIp1, sIIp2, iPhIIp1,
367 iPh, iPhSD, VertexNum, ElementNum, Pt, prevRitrf, rVaFirst, rVc);
368
369 // 3. Set of 4 vertices on top of last slice (on patch 2) and associated tetrahedra
370 // The central point Pt[4] is inserted later.
371 Vect AxV = SC -> AxisVector();
372 for (size_t np=0; np<5; np++) { Pt[np] = translate(Pt[np],slice_height,AxV); }
373 if (iscone) {
374 Point& homcen(Pt[4]);
375 // Compute the homothety ratio between 2 consecutive slices. homcen is the homothety center.
376 real_t ratio = SC -> radiusAt(homcen) / prevRitrf;
377 // Apply homothety to all the points in the interface, except the center.
378 for (size_t np=0; np<4; np++) { Pt[np] = translate(homcen,ratio,Vect(homcen,Pt[np])); }
379 }
380 refnum_t sITF, sITFslice, sDOM;
381 ++iPhSD, sDOM = TG_.sigma(iPhSD); // Last slice
382 if (EBS[1]->curvedShape()) { // if non Flat
383 sITFslice = TG_.sigma(++iPh); // Last interface between last slice and top end subdomain
384 sDOM = sDOM | TG_.sigma(iPhSD+1); // Last slice and top end subdomain sharing this interface
385 }
386 else {
387 sITFslice = 0; // No more transversal interface
388 }
389 for (size_t np=0; np<4; np++) {
390 sITF = TG_.sigma(iPhIIp1 + np%2)|sITFslice; // one internal plane + transversal interface if any
391 listV_.push_back(Vertex(++VertexNum,sBEs2|sBS|sITF|sDOM,Pt[np]));
392 }
393 // Associated tetrahedra
394 number_t rVa = rVaFirst, rVb = rVa+1;
395 subdivPrism(rVa,rVb,rVc,rVa+5,rVb+5,rVc+5, ElementNum); rVa++; rVb++;
396 subdivPrism(rVa,rVb,rVc,rVa+5,rVb+5,rVc+5, ElementNum); rVa++; rVb++;
397 subdivPrism(rVa,rVb,rVc,rVa+5,rVb+5,rVc+5, ElementNum); rVa++; rVb=rVaFirst;
398 subdivPrism(rVa,rVb,rVc,rVa+5,rVb+5,rVc+5, ElementNum);
399
400 if (EBS[1]->curvedShape()) { // if non Flat
401 // 4.a. Add tetrahedrons on the top
402 // The central point on top of the last slice is internal.
403 sITF = sIIp1|sIIp2|sITFslice; // center belongs to 3 interfaces
404 listV_.push_back(Vertex(++VertexNum, sITF|sDOM,Pt[4])); // rank rVc
405 Point TopPt(EBS[1]->EndPt2()); // get Apex computed in endBoundaryShapes
406
407 // Insert last vertex associated to top point in the global list
408 sDOM = TG_.sigma(iPhSD+1); // Top end subdomain
409 sITF = sIIp1|sIIp2; // two internal planes
410 listV_.push_back(Vertex(++VertexNum,sBEs2 |sITF|sDOM,TopPt)); // rank rVd
411 // Insert last 4 tetrahedra in the global list
412 rVaFirst+=5; rVc+=5;
413 number_t rVa = rVaFirst, rVb = rVa+1, rVd = rVc+1;
414 /*!
415 \verbatim
416 d
417 |
418 a+2 | d is the top point
419 \ | a, b, c are in the top circular section of the object
420 \ | c is the center
421 a+3________\|_________b=a+1
422 \c
423 \
424 \
425 a
426 \endverbatim
427 */
428 listT_.push_back(Tetrahedron(++ElementNum,rVa,rVb,rVd,rVc,4)); rVa++; rVb++;
429 listT_.push_back(Tetrahedron(++ElementNum,rVa,rVb,rVd,rVc,4)); rVa++; rVb++;
430 listT_.push_back(Tetrahedron(++ElementNum,rVa,rVb,rVd,rVc,4)); rVa++; rVb=rVaFirst;
431 listT_.push_back(Tetrahedron(++ElementNum,rVa,rVb,rVd,rVc,4));
432 // Description of the interface patch
433 TG_.setDescription(iPh) = "Interface: Top section (containing end point 2)";
434 }
435 else {
436 // 4.b The central point of the top face lies on patch 2
437 sITF = sIIp1|sIIp2; // two internal planes
438 listV_.push_back(Vertex(++VertexNum,sBEs2 |sITF|sDOM,Pt[4]));
439 }
440
441 // Description of the subdomain patches
442 if (EBS[0]->curvedShape()) { TG_.setDescription(++iPh) = "End subdomain on the side of end point " + bottomPt; }
443 for (int isl=1; isl<=nb_sl; isl++) {
444 ostringstream ss;
445 ss << "Slice " << isl;
446 TG_.setDescription(++iPh) = ss.str();
447 }
448 if (EBS[1]->curvedShape()) { TG_.setDescription(++iPh) = "End subdomain on the side of end point " + topPt; }
449 }
450
451 /*!
452 This function makes the first part of the initial mesh (or "seed" of the mesh) to be subdivided
453 for a geometry of revolution,
454 i.e. defined by an axis of revolution and circular sections. The general shape is a truncated
455 cone, that can be a cylinder if the radius is constant.
456 In the following, the word "object" is used to designate the geometric shape.
457 */
startConeTrunk(real_t R1,const Point & P1,const vector<PatchGeometry * > & EBS,const SurfCone * SC,real_t slice_height,int nb_sl,bool iscone,refnum_t sBEs1,refnum_t sBS,refnum_t sIIp1,refnum_t sIIp2,number_t iPhIIp1,number_t & iPh,number_t & iPhSD,number_t & VertexNum,number_t & ElementNum,vector<Point> & Pt,real_t & prevRitrf,number_t & rVaFirst,number_t & rVc)458 void TetrahedronMesh::startConeTrunk(real_t R1, const Point& P1, const vector <PatchGeometry *>& EBS,
459 const SurfCone *SC, real_t slice_height, int nb_sl, bool iscone,
460 refnum_t sBEs1, refnum_t sBS, refnum_t sIIp1, refnum_t sIIp2, number_t iPhIIp1,
461 number_t& iPh, number_t& iPhSD, number_t& VertexNum, number_t& ElementNum,
462 vector<Point>& Pt, real_t& prevRitrf, number_t& rVaFirst, number_t& rVc) {
463 // Define 5 "reference" points on the circle centered at the origin.
464 // Then, rotate and translate them so that they lie on the bottom face of the object.
465 Pt.push_back(Point(R1,0,0)); // Pt[0]
466 Pt.push_back(Point(0,R1,0)); // Pt[1]
467 Pt.push_back(Point(-R1,0,0)); // Pt[2]
468 Pt.push_back(Point(0,-R1,0)); // Pt[3]
469 Pt.push_back(Point(0,0,0)); // Pt[4]
470 Vect Z(0,0,1);
471 Vect AxV = SC -> AxisVector();
472 Vect Nor = crossProduct(Z,AxV); // rotation axis (Z and AxV are both unitary vectors)
473 real_t st = norm(Nor);
474 if (st > theTolerance){
475 // the axis of the object is not Z: we need to rotate the points around the axis (Pt[4],Nor)
476 Nor *= (1./st); // normalize Nor
477 real_t ct = dot(Z,AxV);
478 for (size_t np=0; np<4; np++) { Pt[np] = rotInPlane(Pt[np],ct,st,Pt[4],Nor); }
479 }
480 Vect OCP1(Pt[4],P1);
481 for (size_t np=0; np<5; np++) { Pt[np] = translate(Pt[np],1.,OCP1); }
482
483 refnum_t sITF, sITFslice;
484 refnum_t sDOM = TG_.sigma(++iPhSD); // first subdomain : bottom end subdomain or first slice
485 rVaFirst = 0, rVc = 4;
486 if (EBS[0]->curvedShape()) { // if non Flat
487 // 1.a. Add tetrahedrons at bottom.
488 // Set of 5 vertices at the bottom of the first slice whose center is internal, plus the bottom point.
489 Point BottomPt(EBS[0]->EndPt2()); // get Apex computed in endBoundaryShapes
490 // Insert first 6 vertices in the global list
491 sITF = sIIp1|sIIp2; // two internal planes
492 listV_.push_back(Vertex(++VertexNum,sBEs1 |sITF|sDOM,BottomPt)); // rank 0
493 sITFslice = TG_.sigma(++iPh); // first transversal interface
494 sDOM = sDOM | TG_.sigma(iPhSD+1); // Bottom end subdomain and first slice
495 for (size_t np=0; np<4; np++) {
496 sITF = TG_.sigma(iPhIIp1 + np%2)|sITFslice; // one internal plane + transversal interface
497 listV_.push_back(Vertex(++VertexNum,sBEs1|sBS|sITF|sDOM,Pt[np])); // rank 1, 2, 3, 4
498 }
499 sITF = sIIp1|sIIp2|sITFslice; // center belongs to 3 interfaces
500 listV_.push_back(Vertex(++VertexNum, sITF|sDOM,Pt[4])); // rank 5 (rVc)
501 rVc++;
502 // Insert first 4 tetrahedra in the global list
503 /*!
504 \verbatim
505 3
506 \
507 \ 0 is the bottom point
508 4________\c_________2 1, 2, 3, 4, c are in the bottom circular section of the object
509 \ c=5 is the center
510 |\
511 | \ Internal plane 1 is (0,1,3)
512 | 1 Internal plane 2 is (0,2,4)
513 |
514 0
515 \endverbatim
516 */
517 listT_.push_back(Tetrahedron(++ElementNum,2,1,0,rVc,4));
518 listT_.push_back(Tetrahedron(++ElementNum,3,2,0,rVc,4));
519 listT_.push_back(Tetrahedron(++ElementNum,4,3,0,rVc,4));
520 listT_.push_back(Tetrahedron(++ElementNum,1,4,0,rVc,4));
521 rVaFirst++;
522 // Description of the interface patch
523 TG_.setDescription(iPh) = "Interface: Bottom section (containing end point 1)";
524 }
525 else {
526 // 1.b Set of 5 vertices at the bottom of the first slice (on patch 1)
527 for (size_t np=0; np<4; np++) {
528 sITF = TG_.sigma(iPhIIp1 + np%2); // one internal plane
529 listV_.push_back(Vertex(++VertexNum,sBEs1|sBS|sITF|sDOM,Pt[np])); // rank 0, 1, 2, 3
530 }
531 sITF = sIIp1|sIIp2; // two internal planes
532 listV_.push_back(Vertex(++VertexNum,sBEs1 |sITF|sDOM,Pt[4])); // rank 4 (rVc)
533 --iPhSD;
534 }
535 // 2. Set of 5 vertices on top of each slice and associated tetrahedra
536 // We consider the center point of the larger basis of the object (P1).
537 // The images of this point by translations along the axis will be the centers of successive
538 // homotheties in each interface plane.
539 Point& homcen(Pt[4]);// warning : homcen is a reference to a point whose coordinates change
540 prevRitrf = R1;
541 for (int isl=1; isl<nb_sl; isl++,rVaFirst+=5,rVc+=5) {
542 for (size_t np=0; np<5; np++) { Pt[np] = translate(Pt[np],slice_height,AxV); }
543 if (iscone) {
544 // Compute the homothety ratio between 2 consecutive slices. homcen is the homothety center.
545 real_t Ritrf = SC -> radiusAt(homcen), ratio = Ritrf / prevRitrf;
546 prevRitrf = Ritrf;
547 // Apply homothety to all the points in the interface, except the center.
548 for (size_t np=0; np<4; np++) { Pt[np] = translate(homcen,ratio,Vect(homcen,Pt[np])); }
549 }
550 sITFslice = TG_.sigma(++iPh); // Next transversal interface
551 ++iPhSD, sDOM = TG_.sigma(iPhSD) | TG_.sigma(iPhSD+1); // Two slices sharing this interface
552 for (size_t np=0; np<4; np++) {
553 sITF = TG_.sigma(iPhIIp1 + np%2)|sITFslice; // one internal plane + transversal interface
554 listV_.push_back(Vertex(++VertexNum,sBS|sITF|sDOM,Pt[np]));
555 }
556 sITF = sIIp1|sIIp2|sITFslice; // center belongs to 3 interfaces
557 listV_.push_back(Vertex(++VertexNum, sITF|sDOM,Pt[4]));
558 // Associated tetrahedra
559 number_t rVa = rVaFirst, rVb = rVa+1;
560 subdivPrism(rVa,rVb,rVc,rVa+5,rVb+5,rVc+5, ElementNum); rVa++; rVb++;
561 subdivPrism(rVa,rVb,rVc,rVa+5,rVb+5,rVc+5, ElementNum); rVa++; rVb++;
562 subdivPrism(rVa,rVb,rVc,rVa+5,rVb+5,rVc+5, ElementNum); rVa++; rVb=rVaFirst;
563 subdivPrism(rVa,rVb,rVc,rVa+5,rVb+5,rVc+5, ElementNum);
564 // Description of the interface patch
565 ostringstream ss;
566 ss << "Interface: Section " << isl;
567 TG_.setDescription(iPh) = ss.str();
568 }
569 }
570
571 /*!
572 Create the tetrahedrons that subdivide a prism.
573 A prism can be subdivided in 3 tetrahedrons in six ways. We must take care to
574 have compatible subdivisions in prisms put together side by side. Here, since
575 we restrict to the case where neighbour prisms share the edge (3,6), the compa-
576 tibility is achieved with 4 subdivisions among the six possible. This function
577 selects one of those.
578
579 Ui, i=1,...6 are the ranks of the vertices in the general vertex list.
580 The vertices must be ordered as shown on the following figure.
581 ElementNum is the number of the last tetrahedron created and is updated here.
582
583 The face (1,2,5,4) is on the boundary of the object and thus corresponds to
584 a curved patch. This is used to set the face number of the tetrahedrons lying
585 on this patch.
586 \verbatim
587 5 +
588 / | \
589 / | \
590 / | \ Subdivision :
591 / | \ 1 2 6 3
592 / | \ 2 4 6 5
593 6 +-----------|------+ 4 2 6 4 1
594 Z | | |
595 ^ Y | 2 + |
596 | / | / \ |
597 | / | / \ |
598 +-----> X | / \ |
599 | / \ |
600 | / \|
601 3 +------------------+ 1
602 \endverbatim
603 */
subdivPrism(const number_t U1,const number_t U2,const number_t U3,const number_t U4,const number_t U5,const number_t U6,number_t & ElementNum)604 void TetrahedronMesh::subdivPrism(const number_t U1, const number_t U2, const number_t U3,
605 const number_t U4, const number_t U5, const number_t U6,
606 number_t& ElementNum){
607 listT_.push_back(Tetrahedron(++ElementNum,U1,U2,U6,U3));// internal
608 listT_.push_back(Tetrahedron(++ElementNum,U2,U4,U6,U5,3));// face 3 of this tetrahedron on the boundary
609 listT_.push_back(Tetrahedron(++ElementNum,U2,U6,U4,U1,2));// face 2 of this tetrahedron on the boundary
610 }
611
612 /*!
613 Define some macros in the file associated to the stream ftex in order to display
614 the mesh using Fig4TeX.
615 */
printTeXHeader(ostream & ftex) const616 void TetrahedronMesh::printTeXHeader(ostream& ftex) const {
617 ftex << "\\def\\drawFace#1#2#3#4{" << endl;
618 ftex << "\\figset(color=#4, fill=yes)\\figdrawline[#1,#2,#3]" << endl;
619 ftex << "\\figset(color=default, fill=no)\\figdrawline[#1,#2,#3,#1]}" << endl;
620 ftex << "\\def\\drawElem#1#2#3#4{" << endl;
621 ftex << "\\figdrawline[#1,#2,#3,#1,#4,#2]" << endl;
622 ftex << "\\figdrawline[#4,#3]}" << endl;
623 }
624
625 } // end of namespace subdivision
626 } // end of namespace xlifepp
627