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