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 SubdivisionMesh.cpp
19   \author Y. Lafranche
20   \since 22 May 2008
21   \date 08 Mar 2014
22 
23   \brief Implementation of xlifepp::subdivision::SubdivisionMesh class members and related functions
24 */
25 
26 #include "SubdivisionMesh.hpp"
27 #include "TeXutil.hpp"
28 #include "PointUtils.hpp"
29 
30 using namespace std;
31 
32 namespace xlifepp {
33 namespace subdivision {
34 //-------------------------------------------------------------------------------
35 //  Static variables
36 //-------------------------------------------------------------------------------
37 //HOV_error SubdivisionMesh::PB;
38 
39 //-------------------------------------------------------------------------------
40 //  Constructors, Destructor
41 //-------------------------------------------------------------------------------
42 /*!
43  Main constructor.
44  */
SubdivisionMesh(const number_t nbsubdiv,const number_t order,const number_t type,const number_t minVertexNum,const number_t minElementNum)45 SubdivisionMesh::SubdivisionMesh(const number_t nbsubdiv, const number_t order,
46          const number_t type, const number_t minVertexNum, const number_t minElementNum)
47 : subdiv_level_(nbsubdiv), order_(order), type_(type),
48   minVertexNum_(minVertexNum), minElementNum_(minElementNum),
49   newVertexPt_(&SubdivisionMesh::newVertexPtDef) {
50    if (type != 0 ) { newVertexPt_ = &SubdivisionMesh::newVertexPtGen; }
51 }
52 
53 /*!
54  Destructor.
55  Free memory allocated for patch shapes taking care of duplicated pointers
56  (see IMPORTANT NOTE in TopoGeom.hpp).
57  */
~SubdivisionMesh()58 SubdivisionMesh::~SubdivisionMesh(){
59    for (number_t numPatch=1; numPatch<=TG_.numberOfPatches(); numPatch++) {
60       bool notAlreadySeen = true;
61       for (number_t j=1; j<numPatch; j++) {
62          if (TG_.shape(numPatch) == TG_.shape(j)) {
63             notAlreadySeen = false;
64             break;
65          }
66       }
67       if (notAlreadySeen) {delete TG_.shape(numPatch);}
68    }
69 }
70 //-------------------------------------------------------------------------------
71 //  Public Access functions
72 //-------------------------------------------------------------------------------
73 /*!
74  Returns the coordinates of a vertex, given its number num, num >= minVertexNum_
75  */
vertexCoord(const number_t num) const76 vector<real_t> SubdivisionMesh::vertexCoord(const number_t num) const{
77    return listV_.at(num-minVertexNum_).geomPt();
78 }
79 /*!
80  Returns the coordinates of a vertex, given its rank rk>=0 in the global list of vertices
81  */
rkvertexCoord(const number_t rk) const82 vector<real_t> SubdivisionMesh::rkvertexCoord(const number_t rk) const{
83    return listV_.at(rk).geomPt();
84 }
85 
86 int DECHOL(const vector<vector<real_t> >& A, int n, vector<vector<real_t> >& L, real_t eps);
87 void DRCHOL(const vector<vector<real_t> >&L, int n, vector<vector<real_t> >&B, int m);
IMP(const vector<vector<number_t>> & comesh,const vector<pair<short,short>> & rkEV,const vector<number_t> & rkUnk,const vector<number_t> & rkData)88 void IMP(const vector< vector<number_t> >& comesh,
89          const vector<pair<short,short> >& rkEV,
90          const vector<number_t>& rkUnk, const vector<number_t>& rkData) {
91    cout << " MAILLAGE : " << endl;
92    for (vector< vector<number_t> >::const_iterator itcm=comesh.begin(); itcm<comesh.end(); itcm++) {
93       for (vector<number_t>::const_iterator itt=itcm->begin(); itt<itcm->end(); itt++) {
94          cout << *itt << " ";
95       }
96       cout << endl;
97    }
98    cout << " rkEV :" << endl;
99    for (vector<pair<short,short> >::const_iterator itEV=rkEV.begin(); itEV<rkEV.end(); itEV++) {
100       cout << itEV->first << " " << itEV->second << endl;
101    }
102    cout << " rkUnk :" << endl;
103    for (vector<number_t>::const_iterator itt=rkUnk.begin(); itt<rkUnk.end(); itt++) { cout << *itt << " "; }
104    cout << endl;
105    cout << " rkData :" << endl;
106    for (vector<number_t>::const_iterator itt=rkData.begin(); itt<rkData.end(); itt++) { cout << *itt << " "; }
107    cout << endl;
108 }
109 // connectivity mesh, ranks of edge vertices, ranks of unknowns, ranks of data
unifMesh(const vector<vector<number_t>> & comesh,const vector<pair<short,short>> & rkEV,const vector<number_t> & rkUnk,const vector<number_t> & rkData)110 vector<Point> SubdivisionMesh::unifMesh(const vector< vector<number_t> >& comesh,
111                                         const vector<pair<short,short> >& rkEV,
112                                         const vector<number_t>& rkUnk, const vector<number_t>& rkData) {
113 
114 //IMP(comesh, rkEV, rkUnk, rkData);
115    number_t dimM=rkUnk.size(), tN=2*dimM, fN=4*dimM;
116 // Associate a row index to each unknown number and tN to all data number
117    map<number_t, number_t> row;
118    vector<number_t>::const_iterator itv;
119    number_t i=0;
120    for (itv=rkUnk.begin(); itv<rkUnk.end(); itv++) { row[*itv] = i++; }
121    for (itv=rkData.begin(); itv<rkData.end(); itv++) { row[*itv] = tN; }
122 
123 // Build the matrix M (lower triangular and initialized to 0)
124    vector<vector<real_t> > M(dimM);
125    for (number_t p=0; p<dimM; p++) { M[p].assign(p+1, 0.); }
126    vector<set<number_t> > S(dimM);
127    // for each element in the mesh
128    for (vector< vector<number_t> >::const_iterator itcm=comesh.begin(); itcm<comesh.end(); itcm++) {
129       // for each edge AiAj
130       for (vector<pair<short,short> >::const_iterator itEV=rkEV.begin(); itEV<rkEV.end(); itEV++) {
131          number_t i=(*itcm)[itEV->first], j=(*itcm)[itEV->second];
132          number_t p=row[i], q=row[j], qJ=j, ppq=p+q;
133          if (ppq < tN) { // Ai and Aj are both unknown points
134             if (p < q) { number_t r=p; p=q; q=r; qJ=i; }// swap indices
135             // Now, we have p > q and p cannot be equal to q here.
136             // Update line p of the matrix
137             if (M[p][q] >= 0) { M[p][q] = -1.; M[p][p] += 1.; M[q][q] += 1.; }
138             }
139          else {
140             if (ppq < fN) { // One point is unknown and the other data.
141                if (q < p)  { number_t r=p; p=q; q=r; qJ=i; }// swap indices
142                // Now, AqJ is a data point. Store it to compute the rhs later.
143                pair<set<number_t>::iterator,bool> res = S[p].insert(qJ); // qJ is inserted only once in the set
144                if (res.second) { M[p][p] += 1.; } // update line p of the matrix only once
145             }
146             else { continue; }// ppq == fN : Ai and Aj are both data points (AiAj boundary edge)
147          }
148       }
149    }
150 // Build the rhs
151    dimen_t dimpt=rkvertexCoord(comesh[0][0]).size(); // space dimension
152    vector<vector<real_t> > B(dimM,vector<real_t>(dimpt, 0.));
153    // Update the rhs according to S'contents. B is initialized with 0 which is important
154    // for values of p such that S[p] is empty.
155    for (number_t p=0; p<dimM; p++) {
156       for (set<number_t>::const_iterator itSp=S[p].begin(); itSp!=S[p].end(); itSp++) {
157          vector<real_t> Coor = rkvertexCoord(*itSp);
158          for (number_t k=0; k<dimpt; k++) { B[p][k] += Coor[k]; }
159       }
160    }
161 // Solve the system and return the result
162    if ( DECHOL(M,dimM,M,theTolerance) ) { error("mat_noinvert"); }
163    else { DRCHOL(M,dimM,B,dimpt); }
164    vector<Point> vPts(dimM, Point(std::vector<real_t>(dimpt)));
165    for (number_t p=0; p<dimM; p++) { vPts[p] = B[p]; }
166    return vPts;
167 }
168 
169 //-------------------------------------------------------------------------------
170 //  I/O utilities
171 //-------------------------------------------------------------------------------
172 
173 /*!
174  Prints, on stream os, the general informations about the mesh.
175  */
printInfo(ostream & os,const bool forTeX) const176 void SubdivisionMesh::printInfo(ostream& os, const bool forTeX) const {
177    string prefix = forTeX ? "\\" : "" ;
178    os << prefix << "   "; for (size_t i=0; i<title_.size(); i++) {os << "=";} os << endl;
179    string title = forTeX ? fmtTeX(title_) : title_;
180    os << prefix << "   " << title << endl;
181    os << prefix << "   "; for (size_t i=0; i<title_.size(); i++) {os << "=";} os << endl;
182    os << prefix << " Size: " << numberOfElements() << " elements, "
183       << listV_.size() << " vertices" << endl;
184    os << prefix << " Order " << order_ << ", subdivision level " << subdiv_level_ << endl;
185    if (type_ > 0) {
186       os << prefix << " Curved mesh";
187       if (order_ > 2) {
188          if (type_ == 1) {os << " with radial projection algorithm";}
189          else if (type_ == 2) {os << " with rotation algorithm";}
190       }
191       os << endl;
192    }
193    TG_.print(os,forTeX);
194 }
195 
196 //-------------------------------------------------------------------------------
197 //  Other public member functions
198 //-------------------------------------------------------------------------------
199 /*!
200  Initializes the attribute of the areas of the mesh. The areas are considered in
201  the order boundary, then interface and at last subdomain.
202  If the input data vector holds less attributes than the number of areas, the
203  data are reused cyclically starting again from the beginning.
204  */
initUserAttribute(const vector<string> & attribute)205 void SubdivisionMesh::initUserAttribute(const vector<string>& attribute) {
206    size_t  k = 0;
207    initAttribute_(boundaryArea,attribute,k);
208    initAttribute_(interfaceArea,attribute,k);
209    initAttribute_(subdomainArea,attribute,k);
210 }
211 /*!
212  Initializes the attribute of the areas of kind TA of the mesh from vector attribute.
213  Called by initUserAttribute. k is the current index in vector attribute.
214  The vector attribute is scanned cyclically until all the areas have been assigned
215  an attribute.
216  */
initAttribute_(const topologicalArea TA,const vector<string> & attribute,size_t & k)217 void SubdivisionMesh::initAttribute_(const topologicalArea TA, const vector<string>& attribute, size_t& k){
218    number_t nbAreas = TG_.numberOf(TA);
219    number_t nbAttrib = attribute.size();
220    number_t ica=0;
221 
222    while (ica<nbAreas) {
223       number_t nb1 = nbAttrib-k, nb2 = nbAreas-ica;
224       if (nb1 >= nb2) {
225          for (number_t i=0; i<nb2; i++,ica++){
226             TG_.setAttribute(TA,ica+1) = attribute[k++];
227          }
228       }
229       else {
230          for (number_t i=0; i<nb1; i++,ica++){
231             TG_.setAttribute(TA,ica+1) = attribute[k++];
232          }
233          k=0;
234       }
235    }
236 }
237 /*!
238  Returns the number of the internal vertices of any order in the mesh
239  */
numberOfVerticesInside() const240 number_t SubdivisionMesh::numberOfVerticesInside() const {
241    return rk_verticesInside().size();
242 }
243 /*!
244  Returns the number of vertices of any order in the mesh, belonging to the area
245  num (num>=1) of kind TA or belonging to any area of kind TA if num=0.
246  */
numberOfVerticesIn(const topologicalArea TA,const number_t num) const247 number_t SubdivisionMesh::numberOfVerticesIn(const topologicalArea TA, const number_t num) const {
248    return rk_verticesIn(TA,num).size();
249 }
250 /*!
251  Returns the list of vertices of any order in a tetrahedron mesh, which are
252  not on the boundaries. Thus, they are inside the domain.
253  Vertices are defined by their number (starting from 1).
254  */
verticesInside() const255 vector<number_t> SubdivisionMesh::verticesInside() const {
256    vector<number_t> VI = rk_verticesInside();
257    rankToNum(VI);
258    return VI;
259 }
260 /*!
261  Returns the list of vertices of any order in a tetrahedron mesh, belonging to
262  the area num (num>=1) of kind TA or belonging to any area of kind TA if num=0.
263  Vertices are defined by their number (starting from 1).
264  */
verticesIn(const topologicalArea TA,const number_t num) const265 vector<number_t> SubdivisionMesh::verticesIn(const topologicalArea TA, const number_t num) const {
266    vector<number_t> VonB = rk_verticesIn(TA,num);
267    rankToNum(VonB);
268    return VonB;
269 }
270 
271 /*!
272  Returns the list of vertices of order 1 in a mesh of simplices.
273  Vertices are defined by their number (starting from 1).
274  */
verticesOfOrder1() const275 vector<number_t> SubdivisionMesh::verticesOfOrder1() const {
276    vector<number_t> V(nb_main_vertices_);
277 
278    for (size_t i=0; i<nb_main_vertices_; i++) {
279     V[i] = listV_[i].number();
280    }
281    return V;
282 }
283 
284 //-------------------------------------------------------------------------------
285 //  Protected member functions
286 //-------------------------------------------------------------------------------
287 /*!
288  Create a new vertex starting from a list of vertices and insert it in the general vertex list.
289  The function returns the rank (in the general vertex list) of the new vertex created.
290  The vertices are those of an element or a face or an edge.
291  The new vertex is computed from the isobarycenter of the given vertices (it is this
292  isobarycenter or its projection onto a boundary surface).
293  Input arguments :
294  \param VertexNum   : number of the last vertex created
295  \param rkVert      : list of vertices being processed (rank of the vertices
296                        in the general vertex list)
297  Output arguments :
298  \param VertexNum   : number of the new vertex created
299  */
createVertex(number_t & VertexNum,const vector<number_t> & rkVert)300 number_t SubdivisionMesh::createVertex(number_t& VertexNum, const vector<number_t>& rkVert){
301    vector<Point> VP(rkVert.size());
302    real_t* coef = new real_t[rkVert.size()];
303    vector<number_t>::const_iterator itrV = rkVert.begin();
304    number_t ip(0);
305    Vertex& V = listV_[*itrV];
306    refnum_t localcod = V.locCode();
307    VP[ip] = V.geomPt();
308    coef[ip++] = 1.;
309    while (++itrV < rkVert.end()) {
310       Vertex& V = listV_[*itrV];
311       localcod &= V.locCode();
312       VP[ip] = V.geomPt();
313       coef[ip++] = 1.;
314    }
315    Point P = (this->*newVertexPt_)(localcod,coef,VP);
316    number_t num = listV_.size();
317    listV_.push_back(Vertex(++VertexNum,localcod,P));
318 
319    delete [] coef;
320    return num;
321 }
322 
323 /*!
324  Create a new vertex starting from the two endpoints V1 and V2 of an edge.
325  The function returns the rank (in the general vertex list) of the new vertex
326  or the rank of the corresponding vertex if it has already been created.
327  Input arguments :
328  \param VertexNum   : number of the last vertex created
329  \param Edge        : edge being processed
330  \param SeenEdges   : list of the edges already processed
331  Output arguments :
332  \param VertexNum   : if a new vertex was created, number of this vertex,
333                       unchanged otherwise
334  \param SeenEdges   : list of the edges already processed, updated with the current
335                       one if a new vertex was created
336  */
createVertex(number_t & VertexNum,const pair_nn Edge,map_pair_num & SeenEdges)337 number_t SubdivisionMesh::createVertex(number_t& VertexNum,
338                                       const pair_nn Edge, map_pair_num& SeenEdges){
339    const number_t rV1=Edge.first, rV2=Edge.second;
340    map_pair_num::iterator itSE;
341    number_t num;
342 
343    if ((itSE=SeenEdges.find(Edge)) == SeenEdges.end()) {
344 // This edge has not yet been seen: compute the corresponding vertex
345       Vertex V1=listV_[rV1];
346       Vertex V2=listV_[rV2];
347       refnum_t localcod = V1.locCode() & V2.locCode();
348       vector<Point> VP(2);
349       VP[0] = V1.geomPt();
350       VP[1] = V2.geomPt();
351       real_t coef[2] = {1., 1.};
352       Point P;
353       P = (this->*newVertexPt_)(localcod,coef,VP);
354       num = listV_.size();
355 // A new vertex is being created on this edge: the edge is stored along with
356 // the rank of the new vertex. The edge is stored in the 2 forms (V1,V2) and
357 // (V2,V1) for simplicity reason, to make its future retrieval easier.
358       SeenEdges.insert(make_pair(Edge,num));
359       SeenEdges.insert(make_pair(make_pair(rV2,rV1),num));
360       listV_.push_back(Vertex(++VertexNum,localcod,P));
361    }
362    else {
363 // Edge found: get back the rank of the corresponding vertex in the general list
364       num = itSE->second;
365    }
366    return num;
367 }
368 /*!
369  Create a new vertex starting from the vertices of a face.
370  The function returns the rank (in the general vertex list) of the new vertex
371  or the rank of the corresponding vertex if it has already been created.
372  Input arguments :
373  \param VertexNum   : number of the last vertex created
374  \param rkVFace     : list of vertices defining the face being processed (rank of the vertices
375                       in the local list of the element)
376  \param SeenFaces   : list of the faces already processed
377  Output arguments :
378  \param VertexNum   : if a new vertex was created, number of this vertex,
379                       unchanged otherwise
380  \param SeenFaces   : list of the faces already processed, updated with the current
381                       one if a new vertex was created
382  */
createVertex(number_t & VertexNum,const vector<number_t> & rkVFace,map_set_num & SeenFaces)383 number_t SubdivisionMesh::createVertex(number_t& VertexNum,
384                                        const vector<number_t>& rkVFace, map_set_num& SeenFaces){
385    set_n Face(rkVFace.begin(),rkVFace.end());
386    map_set_num::iterator itSF;
387    number_t num;
388 
389    if ((itSF=SeenFaces.find(Face)) == SeenFaces.end()) {
390 // This face has not yet been seen: compute the corresponding new vertex
391       num = createVertex(VertexNum,rkVFace);
392 // A new vertex is being created on this face: the face is stored along with
393 // the rank of the new vertex.
394       SeenFaces.insert(make_pair(Face,num));
395    }
396    else {
397 // Face found: get back the rank of the corresponding vertex in the general list
398       num = itSF->second;
399    }
400    return num;
401 }
402    //--- Utilitary functions
403 
404 /*!
405  Returns the reference number (localization code) of the area num or of the union
406  of the areas if num=0. The kind of area is given by TA ; it may be boundary,
407  interface or subdomain.
408  */
lCodeOf(const topologicalArea TA,const number_t num) const409 refnum_t SubdivisionMesh::lCodeOf(const topologicalArea TA, const number_t num) const {
410    if (num > 0) {
411       return TG_.localCodeOf(TA,num);
412    }
413    else {
414       return TG_.maskOf(TA);
415    }
416 }
417 /*!
418  Returns color associated to an area of kind TA or default color.
419  The number of the area num must be >= 1.
420  */
colorOf(const topologicalArea TA,const number_t num) const421 string SubdivisionMesh::colorOf(const topologicalArea TA, const number_t num) const {
422    string defaultColor("0.5"); // medium gray
423    string Color = defaultColor;
424    if (num <= TG_.numberOf(TA)) {
425       Color = TG_.getAttribute(TA,num);
426       if (Color.empty()) Color = defaultColor;
427    }
428    return Color;
429 }
430 
431 /*!
432  Initializes the user attribute of the geometrical areas of the mesh,
433  boundaries, interfaces and subdomains, given in this order, by a color.
434  */
initDefaultUserAttribute(void)435 void SubdivisionMesh::initDefaultUserAttribute(void){
436    vector <string> color;
437 
438    color.push_back("\\DarkOrangergb");
439    color.push_back("\\ForestGreenrgb");
440    color.push_back("\\Cyanrgb");
441    color.push_back("\\Yellowrgb");
442    color.push_back("\\Magentargb");
443    color.push_back("\\Bluergb");
444    color.push_back("\\Redrgb");
445    color.push_back("\\Firebrickrgb");
446    color.push_back("\\Pinkrgb");
447    color.push_back("\\DarkGoldenrodrgb");
448    color.push_back("\\Goldrgb");
449    color.push_back("\\HotPinkrgb");
450    color.push_back("\\Maroonrgb");
451    color.push_back("\\RoyalBluergb");
452 
453    initUserAttribute(color);
454 }
455 /*!
456    Apply rotations defined in rots and the translation of vector U to each point of Pt
457 */
458 // Returns the rotation matrix in R3 of angle theta around the absolute axis number 1, 2 or 3.
459 // The angle is to be given in degrees.
rotationMatrix(real_t theta,dimen_t axis)460 Matrix<real_t> rotationMatrix(real_t theta, dimen_t axis) {
461   dimen_t nbrows(3);
462   Matrix<real_t> rotmat(nbrows,nbrows, 0.);
463   dimen_t i(axis%nbrows), j((i+1)%nbrows), k(axis-1);
464   real_t trad(theta*pi_/180.), st(std::sin(trad)), ct(std::cos(trad));
465   rotmat[i*nbrows+i] = ct;
466   rotmat[j*nbrows+i] = st;
467   rotmat[i*nbrows+j] = -st;
468   rotmat[j*nbrows+j] = ct;
469   rotmat[k*nbrows+k] = 1.;
470   return rotmat;
471 }
rotNtrans(const vector<pair<real_t,dimen_t>> & rots,const Vect & U,vector<Point> & Pt)472 void SubdivisionMesh::rotNtrans(const vector<pair<real_t, dimen_t> >& rots, const Vect& U, vector<Point>& Pt) {
473   vector<pair<real_t, dimen_t> >::const_iterator it_rots;
474   for (it_rots=rots.begin(); it_rots!=rots.end(); it_rots++) {
475      Matrix<real_t> R(rotationMatrix(it_rots->first, it_rots->second));
476      for (size_t np=0; np<Pt.size(); np++) {
477         Vector<real_t> v(Pt[np].toVect());
478         Pt[np] = Point(R*v);
479      }
480   }
481   for (size_t np=0; np<Pt.size(); np++) { Pt[np] = translate(Pt[np],1.,U); }
482 }
483 
484 /*!
485  Return the boundary shapes to be used at both end of an object of revolution (a cylinder, a cone
486  or a truncated cone), according to the data provided by the input arguments:
487  \param vSI : information needed to define the boundary shapes and compute their
488               intersection with the axis of the object (apexes).
489  \param SC  : definition of the object of revolution
490  */
endBoundaryShapes(const vector<ShapeInfo> & vSI,const SurfCone & SC)491 vector<PatchGeometry *> SubdivisionMesh::endBoundaryShapes(const vector<ShapeInfo>& vSI,
492                                                            const SurfCone& SC){
493    vector <PatchGeometry *> EBS;
494    Vect AxV = SC.AxisVector();
495    vector<real_t> R(SC.radii());
496    vector<real_t>::const_iterator itR=R.begin();
497    vector<Point> EndPts;
498    EndPts.push_back(SC.EndPt1());
499    EndPts.push_back(SC.EndPt2());
500    vector<Point>::const_iterator itEP=EndPts.begin();
501    vector<ShapeInfo>::const_iterator itvSI;
502    int direction = 1, shape;
503    for (itvSI=vSI.begin(); itvSI != vSI.end(); itvSI++, itEP++, itR++) {
504       direction = -direction; // first translate P1 in the direction of P2P1, then P2 in the direction of P1P2
505       Point Apex=translate(*itEP,direction * itvSI->dist_, AxV);
506       if (itvSI->dist_ > theTolerance) { shape = itvSI->shapeCode_; }
507       else { shape = Flat; }// discard the shape if degenerated
508       switch (shape) {
509          case Cone:
510             EBS.push_back(new SurfCone(*itEP,Apex,*itR));
511             break;
512          case Ellipsoid:
513             EBS.push_back(new SurfEllipsoid(*itEP,Apex,*itR));
514             break;
515          case Sphere: {
516             // Compute PtOnAxis, intersection of the sphere and the axis of the object
517             Point PtOnAxis=translate(*itEP,direction * *itR, AxV);
518             EBS.push_back(new SurfSphere(*itEP,PtOnAxis,*itR));
519             }
520             break;
521          default: // Flat
522             EBS.push_back(new SurfPlane());
523             break;
524       }
525    }
526    return EBS;
527 }
528 
529 /*!
530   Defines a set of points used to build the initial mesh in a cube.
531 */
cubePoints(const real_t edLen,const vector<pair<real_t,dimen_t>> & rots,const Point & Center)532 vector<Point> SubdivisionMesh::cubePoints(const real_t edLen, const vector<pair<real_t, dimen_t> >& rots, const Point& Center) {
533 // Define the 27 characteristic points of the cube near the origin Pt[4] (corners,
534 // middle of the edges and faces) in order to define the selected octants.
535    vector<Point> Pt;
536    real_t halfEL = edLen / 2;
537    Pt.push_back(Point(      0,      0,      0)); // unused                      7
538    Pt.push_back(Point( halfEL,      0,      0)); // Pt[1]     Z>0     11+-------+-------+6
539    Pt.push_back(Point( halfEL, halfEL,      0)); // Pt[2]              /.      /.      /|
540    Pt.push_back(Point(      0, halfEL,      0)); // Pt[3]             / .    8/ .     / |
541    Pt.push_back(Point(      0,      0,      0)); // Pt[4]          12+-------+-------+5 |
542    Pt.push_back(Point( halfEL,      0, halfEL)); // Pt[5]           /.10+.../..3+.../|..+2
543    Pt.push_back(Point( halfEL, halfEL, halfEL)); // Pt[6]          / .     / .     / | /
544    Pt.push_back(Point(      0, halfEL, halfEL)); // Pt[7]       13+-------+16-----+17|/
545    Pt.push_back(Point(      0,      0, halfEL)); // Pt[8]         | 9+....|..+4...|..+1
546    Pt.push_back(Point(-halfEL,      0,      0)); // Pt[9]         | .     | .     | /
547    Pt.push_back(Point(-halfEL, halfEL,      0)); // Pt[10]        |.      |.      |/
548    Pt.push_back(Point(-halfEL, halfEL, halfEL)); // Pt[11]      14+-------+-------+18
549    Pt.push_back(Point(-halfEL,      0, halfEL)); // Pt[12]               15
550    Pt.push_back(Point(-halfEL,-halfEL, halfEL)); // Pt[13]
551    Pt.push_back(Point(-halfEL,-halfEL,      0)); // Pt[14]                      3
552    Pt.push_back(Point(      0,-halfEL,      0)); // Pt[15]    Z<0     10+-------+-------+2
553    Pt.push_back(Point(      0,-halfEL, halfEL)); // Pt[16]             /.      /.      /|
554    Pt.push_back(Point( halfEL,-halfEL, halfEL)); // Pt[17]            / .    4/ .     / |
555    Pt.push_back(Point( halfEL,-halfEL,      0)); // Pt[18]          9+-------+-------+1 |
556    Pt.push_back(Point( halfEL,      0,-halfEL)); // Pt[19]          /.24+.../.21+.../|..+20
557    Pt.push_back(Point( halfEL, halfEL,-halfEL)); // Pt[20]         / .     / .     / | /
558    Pt.push_back(Point(      0, halfEL,-halfEL)); // Pt[21]      14+-------+15-----+18|/
559    Pt.push_back(Point(      0,      0,-halfEL)); // Pt[22]        |23+....|..+22..|..+19
560    Pt.push_back(Point(-halfEL,      0,-halfEL)); // Pt[23]        | .     | .     | /
561    Pt.push_back(Point(-halfEL, halfEL,-halfEL)); // Pt[24]        |.      |.      |/
562    Pt.push_back(Point(-halfEL,-halfEL,-halfEL)); // Pt[25]      25+-------+-------+27
563    Pt.push_back(Point(      0,-halfEL,-halfEL)); // Pt[26]               26
564    Pt.push_back(Point( halfEL,-halfEL,-halfEL)); // Pt[27]
565 // Rotate the points starting from their initial position, then translate them to their final position.
566    Vect OC(Point(0,0,0),Center);
567    rotNtrans(rots, OC, Pt);
568    return Pt;
569 }
570 
571 /*!
572    Start mesh building process for revolution objects.
573 */
initRevMesh(const number_t nbsubdom,const real_t radius1,const real_t radius2,const std::vector<Point> & CharacPts,const std::vector<ShapeInfo> & vSI,real_t & R1,real_t & R2,Point & P1,Point & P2,std::vector<ShapeInfo> & cvSI,std::string & bottomPt,std::string & topPt,bool & iscone,std::string & shape,DefaultGeometry * & DG,SurfPlane * & SP,SurfCone * & SC,std::vector<PatchGeometry * > & EBS,int & nb_sl,real_t & slice_height)574 void SubdivisionMesh::initRevMesh(const number_t nbsubdom, const real_t radius1, const real_t radius2,
575                                   const std::vector<Point>& CharacPts, const std::vector<ShapeInfo>& vSI,
576                                   real_t& R1, real_t& R2, Point& P1, Point& P2, std::vector<ShapeInfo>& cvSI,
577                                   std::string& bottomPt, std::string& topPt, bool& iscone, std::string& shape,
578                                   DefaultGeometry *& DG, SurfPlane *& SP, SurfCone *& SC,
579                                   std::vector <PatchGeometry *>& EBS, int& nb_sl, real_t& slice_height) {
580    cvSI = vSI;
581    bottomPt=string("1"), topPt=string("2");
582    // Since one of the radii may be close to 0, we identify what we call the "first" basis of the
583    // object, defined by (P1,R1) with R1 the largest radius, because we need geometrically separated
584    // points to start the mesh.
585    if (radius1 < radius2) {
586       R1 = radius2; P1 = CharacPts[1];
587       R2 = radius1; P2 = CharacPts[0];
588       cvSI[0] = vSI[1]; cvSI[1] = vSI[0];
589       swap(bottomPt, topPt);
590    }
591    else {
592       R1 = radius1; P1 = CharacPts[0];
593       R2 = radius2; P2 = CharacPts[1];
594    }
595    iscone = R1 != R2;
596    shape = iscone ? "cone" : "cylinder";
597    DG = new DefaultGeometry();
598    SP = new SurfPlane();
599    SC = new SurfCone(P1,P2,R1,R2);
600    EBS = endBoundaryShapes(cvSI,*SC);
601 
602 //  Compute effective number of slices (nb_sl) and height of a slice (slice_height)
603 //  -> The number of slices is now computed to be a multiple of the number of requested
604 //     number of subdomains (nbsubdom) along the axis.
605    nb_sl = 0;// = nbslices formerly, imposed by input argument nbslices
606    slice_height = SC -> Height();
607    if (nb_sl == 0) {// force computation of number of slices of elements, which is now the default behavior
608       nb_sl = int((slice_height + 0.5*R1) / R1);
609       if (nb_sl == 0) { nb_sl = 1; }
610    }
611    number_t nbdom = nbsubdom;
612    if (nbdom == 0) { nbdom = 1; }
613    nb_sl = std::max(number_t(1), nb_sl/nbdom) * nbdom;
614    slice_height /= nb_sl;
615 }
616 
617    //--- Fig4TeX printing functions
618 
619 /*!
620  Prints, on stream ftex, fig4tex instructions to define vertices (action = 0) or
621  to display vertices (action = 1) of any order in a tetrahedron mesh.
622  The vertices belong to area num of kind TA, or to any area of kind TA if num=0.
623  */
printTeXInArea(ostream & ftex,const number_t action,const topologicalArea TA,const number_t num) const624 void SubdivisionMesh::printTeXInArea(ostream& ftex, const number_t action,
625                                     const topologicalArea TA, const number_t num) const {
626    vector<number_t> VonB = rk_verticesIn(TA,num);
627 
628    if (num > 0) {
629       ftex << "% Vertices on " << TG_.kindOf(TA) << " " << num << endl; }
630    else {
631       ftex << "% " << TG_.kindOf(TA) << " vertices" << endl; }
632 
633    switch (action) {
634    default:
635    case 0: // defines points
636       printTeXfigpt(ftex,VonB);
637       break;
638    case 1: // display points
639       printTeXfigwrite(ftex,VonB);
640       break;
641    }
642 }
643 /*!
644  Prints, on stream ftex, fig4tex instructions to define vertices of a tetrahedron
645  mesh stored in vector V.
646  */
printTeXfigpt(ostream & ftex,const vector<number_t> & V) const647 void SubdivisionMesh::printTeXfigpt(ostream& ftex, const vector<number_t>& V) const {
648    vector<number_t>::const_iterator itV;
649    for (itV=V.begin(); itV != V.end(); itV++) {
650       ftex << "\\figpt " << listV_[*itV].number() << ":";
651       listV_[*itV].printTeX(ftex);
652       ftex << endl;
653    }
654 }
655 /*!
656  Prints, on stream ftex, fig4tex instructions to display vertices of a tetrahedron
657  mesh stored in vector V.
658  */
printTeXfigwrite(ostream & ftex,const vector<number_t> & V) const659 void SubdivisionMesh::printTeXfigwrite(ostream& ftex, const vector<number_t>& V) const {
660    vector<number_t>::const_iterator itV = V.begin();
661    ftex << "\\def\\dist{4pt}\\figwriten " << listV_[*itV].number();
662    for (itV++; itV != V.end(); itV++) {
663       ftex << "," << listV_[*itV].number();
664    }
665    ftex << ":(\\dist)" << endl;
666 }
667 /*!
668  Prints, on stream ftex, Fig4TeX instructions to draw faces of a mesh
669  belonging to area num of kind TA.
670  */
printTeXFacesInArea(ostream & ftex,const topologicalArea TA,const number_t num) const671 void SubdivisionMesh::printTeXFacesInArea(ostream& ftex, const topologicalArea TA,
672                                          const number_t num) const {
673    vector< vector<number_t> > FonB = rk_facesIn(TA,num);
674    vector< vector<number_t> >::iterator itFonB;
675    vector<number_t>::iterator itF;
676 
677    ftex << "% Faces on " << TG_.kindOf(TA) << " " << num << endl;
678    ftex << "\\def\\FaceColor{"<< colorOf(TA,num) <<"}" << endl;
679 
680    for (itFonB=FonB.begin(); itFonB != FonB.end(); itFonB++) {
681       ftex << "\\drawFace";
682       for (itF=itFonB->begin(); itF != itFonB->end(); itF++) {
683          ftex << "{" << listV_[*itF].number() << "}";
684       }
685       ftex << endl;
686    }
687 }
688 
689    //--- Low level utilitary functions
690 
691 /*!
692  Utilitary function to replace the rank in the internal list by the vertex number
693  of each vertex stored in the vector V.
694  */
rankToNum(vector<number_t> & V) const695 void SubdivisionMesh::rankToNum(vector<number_t>& V) const {
696    vector<number_t>::iterator itV;
697 
698    for (itV=V.begin(); itV != V.end(); itV++) {
699       *itV = listV_[*itV].number(); // should be equivalent to (*itV)+minVertexNum_
700    }
701 }
702 /*!
703  Utilitary function to replace the rank in the internal list by the vertex number
704  of each vertex stored in the pair V.
705  */
rankToNum(pair_nn & V) const706 void SubdivisionMesh::rankToNum(pair_nn& V) const {
707    V.first  = listV_[V.first].number();  // should be equivalent to (V.first)+minVertexNum_
708    V.second = listV_[V.second].number(); // should be equivalent to (V.second)+minVertexNum_
709 }
710 
711 /*!
712  Returns the list of vertices of any order in a tetrahedron mesh, which are
713  not on the boundaries. Thus, they are inside the domain.
714  Vertices are defined by their rank in the general internal list of vertices.
715  */
rk_verticesInside() const716 vector<number_t> SubdivisionMesh::rk_verticesInside() const {
717    vector<number_t> VI;
718    refnum_t BMask = TG_.maskOf(boundaryArea);
719    number_t k;
720 
721    vector<Vertex>::const_iterator itV;
722    for (itV=listV_.begin(), k=0; itV != listV_.end(); itV++,k++) {
723       refnum_t localcod = itV->locCode() & BMask;
724       // If the vertex belongs to none of the boundaries, store its rank.
725       if (! localcod) {VI.push_back(k);}
726    }
727    return VI;
728 }
729 /*!
730  Returns the list of vertices of any order in a tetrahedron mesh, belonging to
731  the area num of kind TA or belonging to any area of kind TA if num=0.
732  Vertices are defined by their rank in the general internal list of vertices.
733  */
rk_verticesIn(const topologicalArea TA,const number_t num) const734 vector<number_t> SubdivisionMesh::rk_verticesIn(const topologicalArea TA, const number_t num) const {
735    vector<number_t> VonB;
736    number_t k;
737    refnum_t sig = lCodeOf(TA,num);
738 
739    vector<Vertex>::const_iterator itV;
740    for (itV=listV_.begin(), k=0; itV != listV_.end(); itV++,k++) {
741       refnum_t localcod = itV->locCode();
742       // If the vertex belongs to the area, store its rank.
743       if (localcod & sig) {VonB.push_back(k);}
744    }
745    return VonB;
746 }
747 
748    //--- For high order vertices generation
749 
750 /*!
751  Comparison between two vector<number_t> in lexicographical order according to the
752  first two components.
753  */
cmpvect(const vector<number_t> & U,const vector<number_t> & V)754 bool SubdivisionMesh::cmpvect(const vector<number_t>& U, const vector<number_t>& V){
755    if (U[0] < V[0]) {return true;}
756    else if (U[0] == V[0]) {return U[1] < V[1];}
757    else return false;
758 }
759 //-------------------------------------------------------------------------------
760 //  Private member functions
761 //-------------------------------------------------------------------------------
762 /*!
763  Default function that computes the geometrical point associated to a new vertex.
764  The new point is the barycenter of points in VP with coefficients coef,
765  whatever the localization code localcod is.
766  */
newVertexPtDef(const refnum_t localcod,const real_t * coef,const vector<Point> & VP) const767 Point SubdivisionMesh::newVertexPtDef(const refnum_t localcod, const real_t *coef,
768                                       const vector<Point>& VP) const{
769    return barycenter(coef,VP);
770 }
771 /*!
772  Computational routine of the geometrical point associated to a new vertex,
773  taking into account the definition of the areas of the domain by the
774  mean of the localization code localcod of the set of points in VP.
775  Let P be the barycenter of the points in VP with coefficients coef.
776  The new point is:
777   - the projection of P on a curved area, if points in VP all belong to this area,
778   - the point P otherwise.
779  If the points in VP belong to several areas, the first curved area checked is
780  considered, which is consistent since the areas are assumed to have continuous
781  connections.
782  */
newVertexPtGen(const refnum_t localcod,const real_t * coef,const vector<Point> & VP) const783 Point SubdivisionMesh::newVertexPtGen(const refnum_t localcod, const real_t *coef,
784                                       const vector<Point>& VP) const{
785 // Check whether points in VP belong to a curved patch whose list has been prepared in TG_
786    for (number_t i=0; i < TG_.numberOfCurvedPatches(); i++) {
787       pair<PatchGeometry*,refnum_t> CP = TG_.shapeNlcode(i);
788       if (localcod  &  CP.second) {
789          // points in VP all belong to this curved patch
790          return (CP.first) -> projOnBound(coef,VP);
791       }
792    }
793 
794 // Default behavior when points in VP do not belong to any curved patch
795    return barycenter(coef,VP);
796 }
797 
798 
DECHOL(const vector<vector<real_t>> & A,int n,vector<vector<real_t>> & L,real_t eps)799 int DECHOL(const vector<vector<real_t> >& A, int n, vector<vector<real_t> >& L, real_t eps) {
800       int i, j, k;
801       real_t s;
802 /*
803        ----------------------------------------------------------------
804        Decomposition de Cholesky de la matrice A sous la forme A = LLt.
805        Le calcul de la matrice L est fait colonne par colonne.
806          Arguments d'entree  : A,n,eps
807          Arguments de sortie : L
808        A      : matrice donnee d'ordre n symetrique
809        n      : ordre de la matrice
810        L      : matrice triangulaire inferieure resultat de la
811                 decomposition de A (si A n'est pas reutilisee, A et L
812                 peuvent correspondre au meme emplacement memoire)
813        eps    : reel servant a determiner la positivite de la matrice A
814        La fonction renvoie un code de retour valant 0 si le calcul a abouti,
815        1 si la matrice A n'est pas definie positive.
816 
817        --> seuls sont utilises les elements des matrices
818            triangulaires inferieures
819        --> utiliser ensuite DRCHOL pour resoudre un systeme lineaire
820        ----------------------------------------------------------------
821 */
822       for (i=0; i<n; i++) {
823     /* Pre-calcul de la ieme colonne de L */
824          for (j=i; j<n; j++) {
825             s = A[j][i];
826             for (k=0; k<i; k++) { s -= L[i][k]*L[j][k]; }
827             L[j][i] = s;
828          }
829     /* Calcul du terme diagonal et test de positivite */
830          if (L[i][i] < 0.) return 1;
831          L[i][i] = std::sqrt(L[i][i]);
832          if (L[i][i] < eps) return 1;
833     /* Division de la colonne par l'element diagonal */
834          s = L[i][i];
835          for (j=i+1; j<n; j++) { L[j][i] /= s; }
836       }
837       return 0;
838 }
839 // *********************************************************************** //
DRCHOL(const vector<vector<real_t>> & L,int n,vector<vector<real_t>> & B,int m)840 void DRCHOL(const vector<vector<real_t> >&L, int n, vector<vector<real_t> >&B, int m) {
841       int i, j, l;
842       real_t s;
843 /*
844       -----------------------------------------------------------------
845       Resolution des m systemes lineaires Mxj=Bj, j=1,m.
846       L est le resultat de la decomposition de Cholesky (cf. module
847       DECHOL) d'une matrice M = LLt, L triangulaire inferieure.
848       La resolution se fait par descente-remontee.
849          Arguments d'entree  : L,n,m
850          Arguments de sortie : B
851        L      : matrice triangulaire inferieure de Cholesky
852        n      : ordre de la matrice
853        B      : matrice (n,m) contenant les seconds membres et les solutions
854        m      : nombre de systemes a resoudre
855       --> En sortie, les solutions sont memorises a l'emplacement des
856           seconds membres.
857       -----------------------------------------------------------------
858 */
859 /*        Pour chaque second membre : */
860       for (l=0; l<m; l++) {
861 /*        Descente      */
862          for (i=0; i<n; i++) {
863             s = B[i][l];
864             for (j=0; j<i; j++) { s -= L[i][j]*B[j][l]; }
865             B[i][l] = s / L[i][i];
866           }
867 /*        Remontee      */
868          for (i=n-1; i>=0; i--) {
869             s = B[i][l];
870             for (j=i+1; j<n; j++) { s -= L[j][i]*B[j][l]; }
871             B[i][l] = s / L[i][i];
872          }
873       }
874 }
875 } // end of namespace subdivision
876 } // end of namespace xlifepp
877