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