1 // Gmsh - Copyright (C) 1997-2021 C. Geuzaine, J.-F. Remacle
2 //
3 // See the LICENSE.txt file in the Gmsh root directory for license information.
4 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
5 //
6 // Contributor(s):
7 //   Koen Hillewaert
8 //
9 
10 #include <sstream>
11 #include <vector>
12 #include "GmshConfig.h"
13 #include "GModel.h"
14 #include "HighOrder.h"
15 #include "MLine.h"
16 #include "MTriangle.h"
17 #include "MQuadrangle.h"
18 #include "MTetrahedron.h"
19 #include "MHexahedron.h"
20 #include "MPrism.h"
21 #include "MPyramid.h"
22 #include "GmshMessage.h"
23 #include "OS.h"
24 #include "fullMatrix.h"
25 #include "BasisFactory.h"
26 #include "nodalBasis.h"
27 #include "InnerVertexPlacement.h"
28 #include "Context.h"
29 #include "MFace.h"
30 #include "ExtrudeParams.h"
31 
32 // for each pair of vertices (an edge), we build a list of vertices that are the
33 // high order representation of the edge. The ordering of vertices in the list
34 // is supposed to be (by construction) consistent with the ordering of the pair.
35 // FIXME: replace this by std::map<MEdge, std::vector<MVertex *>,
36 // MEdgeLessThan>!
37 typedef std::map<std::pair<MVertex *, MVertex *>, std::vector<MVertex *> >
38   edgeContainer;
39 
40 // for each face (a list of vertices) we build a list of vertices that are the
41 // high order representation of the face
42 typedef std::map<MFace, std::vector<MVertex *>, MFaceLessThan> faceContainer;
43 
44 // Functions that help optimizing placement of points on geometry
45 
46 // The aim here is to build a polynomial representation that consist
47 // in polynomial segments of equal length
48 
mylength(GEdge * ge,int i,double * u)49 static double mylength(GEdge *ge, int i, double *u)
50 {
51   return ge->length(u[i], u[i + 1], 10);
52 }
53 
myresid(int N,GEdge * ge,double * u,fullVector<double> & r,double * weight=nullptr)54 static void myresid(int N, GEdge *ge, double *u, fullVector<double> &r,
55                     double *weight = nullptr)
56 {
57   double L[100];
58   for(int i = 0; i < N - 1; i++) L[i] = mylength(ge, i, u);
59   if(weight)
60     for(int i = 0; i < N - 2; i++)
61       r(i) = L[i + 1] / weight[i + 1] - L[i] / weight[i];
62   else
63     for(int i = 0; i < N - 2; i++) r(i) = L[i + 1] - L[i];
64 }
65 
computeEquidistantParameters(GEdge * ge,double u0,double uN,int N,double * u,double underRelax)66 static bool computeEquidistantParameters(GEdge *ge, double u0, double uN, int N,
67                                          double *u, double underRelax)
68 {
69   const double PRECISION = 1.e-6;
70   const int MAX_ITER = 50;
71   const double eps = (uN - u0) * 1.e-5;
72 
73   // newton algorithm
74   // N is the total number of points (3 for quadratic, 4 for cubic ...)
75   // u[0] = u0;
76   // u[N-1] = uN;
77   // initialize as equidistant in parameter space
78   u[0] = u0;
79   double du = (uN - u0) / (N - 1);
80   for(int i = 1; i < N; i++) { u[i] = u[i - 1] + du; }
81 
82   // return true;
83 
84   // create the tangent matrix
85   const int M = N - 2;
86   fullMatrix<double> J(M, M);
87   fullVector<double> DU(M);
88   fullVector<double> R(M);
89   fullVector<double> Rp(M);
90 
91   int iter = 1;
92 
93   while(iter < MAX_ITER) {
94     iter++;
95     myresid(N, ge, u, R);
96 
97     for(int i = 0; i < M; i++) {
98       u[i + 1] += eps;
99       myresid(N, ge, u, Rp);
100       for(int j = 0; j < M; j++) { J(i, j) = (Rp(j) - R(j)) / eps; }
101       u[i + 1] -= eps;
102     }
103 
104     if(M == 1)
105       DU(0) = R(0) / J(0, 0);
106     else
107       J.luSolve(R, DU);
108 
109     for(int i = 0; i < M; i++) { u[i + 1] -= underRelax * DU(i); }
110 
111     if(u[1] < u0) break;
112     if(u[N - 2] > uN) break;
113 
114     double newt_norm = DU.norm();
115     if(newt_norm < PRECISION) { return true; }
116   }
117   return false;
118 }
119 
computeGLLParametersP6(GEdge * ge,double u0,double uN,int N,double * u,double underRelax)120 static bool computeGLLParametersP6(GEdge *ge, double u0, double uN, int N,
121                                    double *u, double underRelax)
122 {
123   static const double GLLQL[7] = {
124     -1.000000000000000, -0.830223896278567, -0.468848793470714, 0,
125     0.468848793470714,  0.830223896278567,  1.000000000000000};
126   double weight[6];
127   for(int i = 0; i < 6; ++i) { weight[i] = GLLQL[i + 1] - GLLQL[i]; }
128 
129   const double PRECISION = 1.e-6;
130   const int MAX_ITER = 50;
131   const double eps = (uN - u0) * 1.e-5;
132 
133   // newton algorithm
134   // N is the total number of points (3 for quadratic, 4 for cubic ...)
135   // u[0] = u0;
136   // u[N-1] = uN;
137   // initialize as equidistant in parameter space
138   u[0] = u0;
139   u[N - 1] = uN;
140   double uMiddle = .5 * (u0 + uN);
141   double du = .5 * (uN - u0);
142   for(int i = 1; i < N - 1; i++) { u[i] = uMiddle + GLLQL[i] * du; }
143 
144   // create the tangent matrix
145   const int M = N - 2;
146   fullMatrix<double> J(M, M);
147   fullVector<double> DU(M);
148   fullVector<double> R(M);
149   fullVector<double> Rp(M);
150 
151   int iter = 1;
152 
153   while(iter < MAX_ITER) {
154     iter++;
155     myresid(N, ge, u, R, weight);
156 
157     for(int i = 0; i < M; i++) {
158       u[i + 1] += eps;
159       myresid(N, ge, u, Rp, weight);
160       for(int j = 0; j < M; j++) { J(i, j) = (Rp(j) - R(j)) / eps; }
161       u[i + 1] -= eps;
162     }
163 
164     if(M == 1)
165       DU(0) = R(0) / J(0, 0);
166     else
167       J.luSolve(R, DU);
168 
169     for(int i = 0; i < M; i++) { u[i + 1] -= underRelax * DU(i); }
170 
171     if(u[1] < u0) break;
172     if(u[N - 2] > uN) break;
173 
174     double newt_norm = DU.norm();
175     if(newt_norm < PRECISION) { return true; }
176   }
177   return false;
178 }
179 
computeEquidistantParameters(GFace * gf,double u0,double uN,double v0,double vN,SPoint3 & p0,SPoint3 & pN,int N,bool geodesic,double * u,double * v)180 static bool computeEquidistantParameters(GFace *gf, double u0, double uN,
181                                          double v0, double vN, SPoint3 &p0,
182                                          SPoint3 &pN, int N, bool geodesic,
183                                          double *u, double *v)
184 {
185   const int NI = N - 1;
186   u[0] = u0;
187   v[0] = v0;
188   u[NI] = uN;
189   v[NI] = vN;
190   const double fact = 1. / (double)NI;
191   for(int i = 1; i < NI; i++) {
192     const double t = i * fact;
193     u[i] = u0 + (uN - u0) * t;
194     v[i] = v0 + (vN - v0) * t;
195     // only use closestPoint for non-plane surfaces (for performance reasons -
196     // it's very slow in OpenCASCADE), and not with the built-in representation
197     // (since it does not support complex surfaces anyway)
198     if(geodesic &&
199        gf->getNativeType() != GEntity::GmshModel &&
200        gf->getNativeType() != GEntity::UnknownModel &&
201        gf->geomType() != GEntity::Plane) {
202       SPoint3 pc(t * pN + (1. - t) * p0);
203       double guess[2] = {u[i], v[i]};
204       GPoint gp = gf->closestPoint(pc, guess);
205       if(gp.succeeded()) {
206         u[i] = gp.u();
207         v[i] = gp.v();
208       }
209     }
210   }
211   return true;
212 }
213 
214 static fullMatrix<double> *lob2lagP6 = nullptr;
215 
createMatLob2LagP6()216 void createMatLob2LagP6()
217 {
218   const double lobPt[7] = {
219     -1.000000000000000, -0.830223896278567, -0.468848793470714, 0,
220     0.468848793470714,  0.830223896278567,  1.000000000000000};
221   const double lagPt[7] = {
222     -1.000000000000000, -0.666666666666666, -0.333333333333333, 0,
223     0.333333333333333,  0.666666666666666,  1.000000000000000};
224   const int ndofs = 7;
225 
226   const double monomial[7] = {0, 1, 2, 3, 4, 5, 6};
227 
228   fullMatrix<double> Vandermonde(ndofs, ndofs);
229   for(int i = 0; i < ndofs; i++) {
230     for(int j = 0; j < ndofs; j++) {
231       Vandermonde(i, j) = pow_int(lobPt[i], monomial[j]);
232     }
233   }
234 
235   fullMatrix<double> coefficient(ndofs, ndofs);
236   Vandermonde.invert(coefficient);
237 
238   lob2lagP6 = new fullMatrix<double>(ndofs, ndofs);
239 
240   for(int i = 0; i < ndofs; i++) {
241     for(int j = 0; j < ndofs; j++) {
242       Vandermonde(i, j) = pow_int(lagPt[i], monomial[j]);
243     }
244   }
245 
246   Vandermonde.mult(coefficient, (*lob2lagP6));
247 }
248 
249 // Creation of high-order edge vertices
250 
getEdgeVerticesOnGeo(GEdge * ge,MVertex * v0,MVertex * v1,std::vector<MVertex * > & ve,int nPts=1)251 static bool getEdgeVerticesOnGeo(GEdge *ge, MVertex *v0, MVertex *v1,
252                                  std::vector<MVertex *> &ve, int nPts = 1)
253 {
254   static bool GLLquad = false;
255   static const double relaxFail = 1e-2;
256   double u0 = 0., u1 = 0., US[100];
257   bool reparamOK = reparamMeshVertexOnEdge(v0, ge, u0);
258   if(ge->periodic(0) && ge->getEndVertex() &&
259      ge->getEndVertex()->getNumMeshVertices() > 0 &&
260      v1 == ge->getEndVertex()->mesh_vertices[0])
261     u1 = ge->parBounds(0).high();
262   else
263     reparamOK &= reparamMeshVertexOnEdge(v1, ge, u1);
264 
265   if(reparamOK) {
266     double uMin = std::min(u0, u1), uMax = std::max(u0, u1);
267     bool failed = true;
268     double relax = 1.;
269     while(failed && (relax > relaxFail)) {
270       if(GLLquad)
271         failed = !computeGLLParametersP6(ge, uMin, uMax, nPts + 2, US, relax);
272       else
273         failed =
274           !computeEquidistantParameters(ge, uMin, uMax, nPts + 2, US, relax);
275       relax *= 0.5;
276     }
277     if(failed) {
278       Msg::Warning(
279         "Failed to compute equidistant parameters (relax = %g, value = %g) "
280         "for edge %d-%d parametrized with %g %g on curve %d",
281         relax, US[1], v0->getNum(), v1->getNum(), u0, u1, ge->tag());
282       US[0] = uMin;
283       const double du = (uMax - uMin) / (nPts + 1);
284       for(int i = 1; i <= nPts; i++) US[i] = US[i - 1] + du;
285     }
286   }
287   else
288     Msg::Error("Cannot reparametrize a mesh node in high order meshing");
289 
290   if(!reparamOK) return false;
291 
292   if(GLLquad) {
293     fullMatrix<double> M(7, 3);
294     M(0, 0) = u0 < u1 ? v0->x() : v1->x();
295     M(0, 1) = u0 < u1 ? v0->y() : v1->y();
296     M(0, 2) = u0 < u1 ? v0->z() : v1->z();
297     M(6, 0) = u0 < u1 ? v1->x() : v0->x();
298     M(6, 1) = u0 < u1 ? v1->y() : v0->y();
299     M(6, 2) = u0 < u1 ? v1->z() : v0->z();
300     for(int j = 0; j < nPts; j++) {
301       int count = u0 < u1 ? j + 1 : nPts + 1 - (j + 1);
302       GPoint pc = ge->point(US[count]);
303       M(j + 1, 0) = pc.x();
304       M(j + 1, 1) = pc.y();
305       M(j + 1, 2) = pc.z();
306     }
307     fullMatrix<double> Mlag(7, 3);
308     if(!lob2lagP6) createMatLob2LagP6();
309     lob2lagP6->mult(M, Mlag);
310 
311     for(int j = 0; j < nPts; j++) {
312       MVertex *v;
313       int count = u0 < u1 ? j + 1 : nPts + 1 - (j + 1);
314       // FIXME US[count] false!!!
315       v = new MEdgeVertex(Mlag(count, 0), Mlag(count, 1), Mlag(count, 2), ge,
316                           US[count]);
317       // this destroys the ordering of the mesh vertices on the edge
318       ve.push_back(v);
319     }
320   }
321   else {
322     for(int j = 0; j < nPts; j++) {
323       MVertex *v;
324       int count = u0 < u1 ? j + 1 : nPts + 1 - (j + 1);
325       GPoint pc = ge->point(US[count]);
326       v = new MEdgeVertex(pc.x(), pc.y(), pc.z(), ge, US[count]);
327       // this destroys the ordering of the mesh vertices on the edge
328       ve.push_back(v);
329     }
330   }
331 
332   //  GLLquad = false;
333   return true;
334 }
335 
getEdgeVerticesOnGeo(GFace * gf,MVertex * v0,MVertex * v1,std::vector<MVertex * > & ve,int nPts=1)336 static bool getEdgeVerticesOnGeo(GFace *gf, MVertex *v0, MVertex *v1,
337                                  std::vector<MVertex *> &ve, int nPts = 1)
338 {
339   SPoint2 p0, p1;
340   double US[100], VS[100];
341   bool reparamOK = reparamMeshEdgeOnFace(v0, v1, gf, p0, p1);
342   if(reparamOK) {
343     SPoint3 pnt0, pnt1;
344     if(nPts >= 30)
345       computeEquidistantParameters(gf, p0[0], p1[0], p0[1], p1[1], pnt0, pnt1,
346                                    nPts + 2, false, US, VS);
347     else {
348       pnt0 = v0->point();
349       pnt1 = v1->point();
350       // warning: using the geodesic is sometimes a bad idea when the edge is
351       // "far away" from the surface (e.g. on the diameter of a circle!);
352       // however removing this can cause failures on surfaces with singular
353       // parametrizations like spheroids (see #1271)
354       computeEquidistantParameters(gf, p0[0], p1[0], p0[1], p1[1], pnt0, pnt1,
355                                    nPts + 2, true, US, VS);
356     }
357   }
358   else {
359     Msg::Error("Cannot reparametrize mesh edge %lu-%lu on surface %d",
360                v0->getNum(), v1->getNum(), gf->tag());
361     return false;
362   }
363 
364   for(int j = 0; j < nPts; j++) {
365     GPoint pc = gf->point(US[j + 1], VS[j + 1]);
366     MVertex *v =
367       new MFaceVertex(pc.x(), pc.y(), pc.z(), gf, US[j + 1], VS[j + 1]);
368     ve.push_back(v);
369   }
370 
371   return true;
372 }
373 
interpVerticesInExistingEdge(GEntity * ge,const MElement * edgeEl,std::vector<MVertex * > & veEdge,int nPts)374 static void interpVerticesInExistingEdge(GEntity *ge, const MElement *edgeEl,
375                                          std::vector<MVertex *> &veEdge,
376                                          int nPts)
377 {
378   fullMatrix<double> points;
379   points = edgeEl->getFunctionSpace(nPts + 1)->points;
380   for(int k = 2; k < nPts + 2; k++) {
381     SPoint3 pos;
382     edgeEl->pnt(points(k, 0), 0., 0., pos);
383     MVertex *v = new MVertex(pos.x(), pos.y(), pos.z(), ge);
384     veEdge.push_back(v);
385   }
386 }
387 
getMinMaxVert(MVertex * v0,MVertex * v1,MVertex * & vMin,MVertex * & vMax)388 inline static bool getMinMaxVert(MVertex *v0, MVertex *v1, MVertex *&vMin,
389                                  MVertex *&vMax)
390 {
391   const bool increasing = (v0->getNum() < v1->getNum());
392   if(increasing) {
393     vMin = v0;
394     vMax = v1;
395   }
396   else {
397     vMin = v1;
398     vMax = v0;
399   }
400   return increasing;
401 }
402 
403 // Get new interior vertices for a 1D element
getEdgeVertices(GEdge * ge,MElement * ele,std::vector<MVertex * > & ve,edgeContainer & edgeVertices,bool linear,int nPts=1)404 static void getEdgeVertices(GEdge *ge, MElement *ele,
405                             std::vector<MVertex *> &ve,
406                             edgeContainer &edgeVertices, bool linear,
407                             int nPts = 1)
408 {
409   if(!ge->haveParametrization()) linear = true;
410 
411   std::vector<MVertex *> veOld;
412   ele->getVertices(veOld);
413   MVertex *vMin, *vMax;
414   const bool increasing = getMinMaxVert(veOld[0], veOld[1], vMin, vMax);
415   std::pair<MVertex *, MVertex *> p(vMin, vMax);
416 
417   std::vector<MVertex *> veEdge;
418   // Get vertices on geometry if asked
419   bool gotVertOnGeo =
420     linear ? false : getEdgeVerticesOnGeo(ge, veOld[0], veOld[1], veEdge, nPts);
421   // If not on geometry, create from mesh interpolation
422   if(!gotVertOnGeo) interpVerticesInExistingEdge(ge, ele, veEdge, nPts);
423   if(edgeVertices.count(p) == 0) {
424     if(increasing) // Add newly created vertices to list
425       edgeVertices[p].insert(edgeVertices[p].end(), veEdge.begin(),
426                              veEdge.end());
427     else
428       edgeVertices[p].insert(edgeVertices[p].end(), veEdge.rbegin(),
429                              veEdge.rend());
430   }
431   else if(p.first != p.second) {
432     // Vertices already exist and edge is not a degenerated edge
433     Msg::Error(
434       "Mesh edges from different curves share nodes: create a finer mesh "
435       "(curve involved: %d)",
436       ge->tag());
437   }
438   ve.insert(ve.end(), veEdge.begin(), veEdge.end());
439 }
440 
441 // Get new interior vertices for an edge in a 2D element
getEdgeVertices(GFace * gf,MElement * ele,std::vector<MVertex * > & ve,edgeContainer & edgeVertices,bool linear,int nPts=1)442 static void getEdgeVertices(GFace *gf, MElement *ele,
443                             std::vector<MVertex *> &ve,
444                             edgeContainer &edgeVertices, bool linear,
445                             int nPts = 1)
446 {
447   if(!gf->haveParametrization()) linear = true;
448 
449   for(int i = 0; i < ele->getNumEdges(); i++) {
450     std::vector<MVertex *> veOld;
451     ele->getEdgeVertices(i, veOld);
452     MVertex *vMin, *vMax;
453     const bool increasing = getMinMaxVert(veOld[0], veOld[1], vMin, vMax);
454     std::pair<MVertex *, MVertex *> p(vMin, vMax);
455     std::vector<MVertex *> veEdge;
456 
457     auto eIter = edgeVertices.find(p);
458 
459     if(eIter != edgeVertices.end()) { // Vertices already exist
460       std::vector<MVertex *> &eVtcs = eIter->second;
461       if(increasing)
462         veEdge.assign(eVtcs.begin(), eVtcs.end());
463       else
464         veEdge.assign(eVtcs.rbegin(), eVtcs.rend());
465     }
466     else { // Vertices do not exist, create them
467       // Get vertices on geometry if asked
468       bool gotVertOnGeo =
469         linear ? false :
470                  getEdgeVerticesOnGeo(gf, veOld[0], veOld[1], veEdge, nPts);
471       if(!gotVertOnGeo) {
472         // If not on geometry, create from mesh interpolation
473         const MLineN edgeEl(veOld, ele->getPolynomialOrder());
474         interpVerticesInExistingEdge(gf, &edgeEl, veEdge, nPts);
475       }
476 
477       std::vector<MVertex *> &eVtcs = edgeVertices[p];
478 
479       if(increasing) // Add newly created vertices to list
480         eVtcs.insert(eVtcs.end(), veEdge.begin(), veEdge.end());
481       else
482         eVtcs.insert(eVtcs.end(), veEdge.rbegin(), veEdge.rend());
483     }
484     ve.insert(ve.end(), veEdge.begin(), veEdge.end());
485   }
486 }
487 
488 // Get new interior vertices for an edge in a 3D element
getEdgeVertices(GRegion * gr,MElement * ele,std::vector<MVertex * > & ve,edgeContainer & edgeVertices,int nPts=1)489 static void getEdgeVertices(GRegion *gr, MElement *ele,
490                             std::vector<MVertex *> &ve,
491                             edgeContainer &edgeVertices, int nPts = 1)
492 {
493   for(int i = 0; i < ele->getNumEdges(); i++) {
494     std::vector<MVertex *> veOld;
495     ele->getEdgeVertices(i, veOld);
496     MVertex *vMin, *vMax;
497     const bool increasing = getMinMaxVert(veOld[0], veOld[1], vMin, vMax);
498     std::pair<MVertex *, MVertex *> p(vMin, vMax);
499     std::vector<MVertex *> veEdge;
500     if(edgeVertices.count(p)) { // Vertices already exist
501       if(increasing)
502         veEdge.assign(edgeVertices[p].begin(), edgeVertices[p].end());
503       else
504         veEdge.assign(edgeVertices[p].rbegin(), edgeVertices[p].rend());
505     }
506     else { // Vertices do not exist, create them
507       const MLineN edgeEl(veOld, ele->getPolynomialOrder());
508       interpVerticesInExistingEdge(gr, &edgeEl, veEdge, nPts);
509       if(increasing) // Add newly created vertices to list
510         edgeVertices[p].insert(edgeVertices[p].end(), veEdge.begin(),
511                                veEdge.end());
512       else
513         edgeVertices[p].insert(edgeVertices[p].end(), veEdge.rbegin(),
514                                veEdge.rend());
515     }
516     ve.insert(ve.end(), veEdge.begin(), veEdge.end());
517   }
518 }
519 
520 // Creation of high-order face vertices
521 
reorientTrianglePoints(std::vector<MVertex * > & vtcs,int orientation,bool swap)522 static void reorientTrianglePoints(std::vector<MVertex *> &vtcs,
523                                    int orientation, bool swap)
524 {
525   int nbPts = vtcs.size();
526   if(nbPts <= 1) return;
527   std::vector<MVertex *> tmp(nbPts);
528   int interiorOrder = (int)((sqrt(1. + 8. * nbPts) - 3) / 2);
529   int pos = 0;
530   for(int o = interiorOrder; o > 0; o -= 3) {
531     if(swap) {
532       tmp[pos] = vtcs[pos];
533       tmp[pos + 1] = vtcs[pos + 2];
534       tmp[pos + 2] = vtcs[pos + 1];
535       for(int i = 0; i < 3 * (o - 1); i++)
536         tmp[pos + 3 + i] = vtcs[pos + 3 * o - i - 1];
537     }
538     else {
539       for(int i = 0; i < 3 * o; i++) tmp[pos + i] = vtcs[pos + i];
540     }
541     for(int i = 0; i < 3; i++) {
542       int ri = (i + orientation) % 3;
543       vtcs[pos + ri] = tmp[pos + i];
544       for(int j = 0; j < o - 1; j++)
545         vtcs[pos + 3 + (o - 1) * ri + j] = tmp[pos + 3 + (o - 1) * i + j];
546     }
547     pos += 3 * o;
548   }
549 }
550 
reorientQuadPoints(std::vector<MVertex * > & vtcs,int orientation,bool swap,int order)551 static void reorientQuadPoints(std::vector<MVertex *> &vtcs, int orientation,
552                                bool swap, int order)
553 {
554   int nbPts = vtcs.size();
555   if(nbPts <= 1) return;
556   std::vector<MVertex *> tmp(nbPts);
557 
558   int start = 0;
559   while(1) {
560     // CORNERS
561     int index = 0;
562     if(order == 0) { start++; }
563     else {
564       int i1(0), i2(0), i3(0), i4(0);
565       if(!swap) {
566         if(orientation == 0) {
567           i1 = 0;
568           i2 = 1;
569           i3 = 2;
570           i4 = 3;
571         }
572         else if(orientation == 1) {
573           i1 = 3;
574           i2 = 0;
575           i3 = 1;
576           i4 = 2;
577         }
578         else if(orientation == 2) {
579           i1 = 2;
580           i2 = 3;
581           i3 = 0;
582           i4 = 1;
583         }
584         else if(orientation == 3) {
585           i1 = 1;
586           i2 = 2;
587           i3 = 3;
588           i4 = 0;
589         }
590       }
591       else {
592         if(orientation == 0) {
593           i1 = 0;
594           i2 = 3;
595           i3 = 2;
596           i4 = 1;
597         }
598         else if(orientation == 3) {
599           i1 = 3;
600           i2 = 2;
601           i3 = 1;
602           i4 = 0;
603         }
604         else if(orientation == 2) {
605           i1 = 2;
606           i2 = 1;
607           i3 = 0;
608           i4 = 3;
609         }
610         else if(orientation == 1) {
611           i1 = 1;
612           i2 = 0;
613           i3 = 3;
614           i4 = 2;
615         }
616       }
617 
618       int indices[4] = {i1, i2, i3, i4};
619       for(int i = 0; i < 4; i++) tmp[i] = vtcs[start + indices[i]];
620       for(int i = 0; i < 4; i++) vtcs[start + i] = tmp[i];
621 
622       // EDGES
623       start += 4;
624       for(int iEdge = 0; iEdge < 4; iEdge++) {
625         int p1 = indices[iEdge];
626         int p2 = indices[(iEdge + 1) % 4];
627         int nbP = order - 1;
628         if(p1 == 0 && p2 == 1) {
629           for(int i = start + 0 * nbP; i < start + 1 * nbP; i++)
630             tmp[index++] = vtcs[i];
631         }
632         else if(p1 == 1 && p2 == 2) {
633           for(int i = start + 1 * nbP; i < start + 2 * nbP; i++)
634             tmp[index++] = vtcs[i];
635         }
636         else if(p1 == 2 && p2 == 3) {
637           for(int i = start + 2 * nbP; i < start + 3 * nbP; i++)
638             tmp[index++] = vtcs[i];
639         }
640         else if(p1 == 3 && p2 == 0) {
641           for(int i = start + 3 * nbP; i < start + 4 * nbP; i++)
642             tmp[index++] = vtcs[i];
643         }
644         else if(p1 == 1 && p2 == 0) {
645           for(int i = start + 1 * nbP - 1; i >= start + 0 * nbP; i--)
646             tmp[index++] = vtcs[i];
647         }
648         else if(p1 == 2 && p2 == 1) {
649           for(int i = start + 2 * nbP - 1; i >= start + 1 * nbP; i--)
650             tmp[index++] = vtcs[i];
651         }
652         else if(p1 == 3 && p2 == 2) {
653           for(int i = start + 3 * nbP - 1; i >= start + 2 * nbP; i--)
654             tmp[index++] = vtcs[i];
655         }
656         else if(p1 == 0 && p2 == 3) {
657           for(int i = start + 4 * nbP - 1; i >= start + 3 * nbP; i--)
658             tmp[index++] = vtcs[i];
659         }
660         else
661           Msg::Error("Something wrong in reorientQuadPoints");
662       }
663       for(int i = 0; i < index; i++) vtcs[start + i] = tmp[i];
664       start += index;
665     }
666 
667     order -= 2;
668     if(start >= (int)vtcs.size()) break;
669   }
670 }
671 
interpVerticesInExistingFace(GEntity * ge,const fullMatrix<double> & coefficients,const std::vector<MVertex * > & vertices,std::vector<MVertex * > & vFace)672 static void interpVerticesInExistingFace(GEntity *ge,
673                                          const fullMatrix<double> &coefficients,
674                                          const std::vector<MVertex *> &vertices,
675                                          std::vector<MVertex *> &vFace)
676 {
677   for(int k = 0; k < coefficients.size1(); k++) {
678     double x(0), y(0), z(0);
679     for(int j = 0; j < coefficients.size2(); j++) {
680       MVertex *v = vertices[j];
681       x += coefficients(k, j) * v->x();
682       y += coefficients(k, j) * v->y();
683       z += coefficients(k, j) * v->z();
684     }
685     vFace.push_back(new MVertex(x, y, z, ge));
686   }
687 }
688 
getFaceVerticesOnExtrudedGeo(GFace * gf,const fullMatrix<double> & coefficients,const std::vector<MVertex * > & vertices,std::vector<MVertex * > & vf)689 static bool getFaceVerticesOnExtrudedGeo(GFace *gf,
690                                          const fullMatrix<double> &coefficients,
691                                          const std::vector<MVertex *> &vertices,
692                                          std::vector<MVertex *> &vf)
693 {
694   // special case for 9 node quads...
695   if(coefficients.size1() != 1 || vertices.size() != 8) return false;
696 
697   // ...on surfaces with recombined extruded meshes, generated by translation...
698   ExtrudeParams *ep = gf->meshAttributes.extrude;
699   if(!ep || !ep->mesh.ExtrudeMesh || !ep->mesh.Recombine ||
700      ep->geo.Mode != EXTRUDED_ENTITY || ep->geo.Type != TRANSLATE)
701     return false;
702 
703   // ...then this is exact
704   interpVerticesInExistingFace(gf, coefficients, vertices, vf);
705   return true;
706 }
707 
getFaceVerticesOnGeo(GFace * gf,const fullMatrix<double> & coefficients,const std::vector<MVertex * > & vertices,std::vector<MVertex * > & vf)708 static void getFaceVerticesOnGeo(GFace *gf,
709                                  const fullMatrix<double> &coefficients,
710                                  const std::vector<MVertex *> &vertices,
711                                  std::vector<MVertex *> &vf)
712 {
713   SPoint2 pts[1000];
714   bool reparamOK = true;
715   for(std::size_t k = 0; k < vertices.size(); ++k)
716     reparamOK &= reparamMeshVertexOnFace(vertices[k], gf, pts[k]);
717   for(int k = 0; k < coefficients.size1(); k++) {
718     double X(0), Y(0), Z(0), GUESS[2] = {0, 0};
719     for(int j = 0; j < coefficients.size2(); j++) {
720       MVertex *vt = vertices[j];
721       X += coefficients(k, j) * vt->x();
722       Y += coefficients(k, j) * vt->y();
723       Z += coefficients(k, j) * vt->z();
724       if(reparamOK) {
725         GUESS[0] += coefficients(k, j) * pts[j][0];
726         GUESS[1] += coefficients(k, j) * pts[j][1];
727       }
728     }
729     MVertex *v;
730     if(reparamOK) {
731       // closestPoint is absolutely necessary when the parameterization is
732       // degenerate
733       GPoint gp;
734       if(gf->geomType() == GEntity::Plane) {
735         gp = gf->point(SPoint2(GUESS[0], GUESS[1]));
736       }
737       else {
738         gp = gf->closestPoint(SPoint3(X, Y, Z), GUESS);
739       }
740       if(gp.g()) {
741         v = new MFaceVertex(gp.x(), gp.y(), gp.z(), gf, gp.u(), gp.v());
742       }
743       else {
744         v = new MVertex(X, Y, Z, gf);
745       }
746     }
747     else {
748       GPoint gp = gf->closestPoint(SPoint3(X, Y, Z), GUESS);
749       if(gp.succeeded())
750         v = new MVertex(gp.x(), gp.y(), gp.z(), gf);
751       else
752         v = new MVertex(X, Y, Z, gf);
753     }
754     vf.push_back(v);
755   }
756 }
757 
758 // Get new interior vertices for a 2D element
getFaceVertices(GFace * gf,MElement * ele,std::vector<MVertex * > & newVertices,faceContainer & faceVertices,bool linear,int nPts=1)759 static void getFaceVertices(GFace *gf, MElement *ele,
760                             std::vector<MVertex *> &newVertices,
761                             faceContainer &faceVertices, bool linear,
762                             int nPts = 1)
763 {
764   if(!gf->haveParametrization()) linear = true;
765 
766   std::vector<MVertex *> boundaryVertices;
767   {
768     std::size_t nCorner = ele->getNumPrimaryVertices();
769     boundaryVertices.reserve(nCorner + newVertices.size());
770     ele->getVertices(boundaryVertices);
771     boundaryVertices.resize(nCorner);
772     boundaryVertices.insert(boundaryVertices.end(), newVertices.begin(),
773                             newVertices.end());
774   }
775   int type = ele->getType();
776   fullMatrix<double> *coefficients = getInnerVertexPlacement(type, nPts + 1);
777   std::vector<MVertex *> vFace;
778   if(!linear) { // Get vertices on geometry if asked...
779     // special case for 2nd order quads on surfaces generated by extrusion
780     // (translation): this allows to speed up a common case (extruded OCC
781     // models) by orders of magnitudes, where OCC closestPoint() is atrociously
782     // slow
783     if(!getFaceVerticesOnExtrudedGeo(gf, *coefficients, boundaryVertices,
784                                      vFace))
785       getFaceVerticesOnGeo(gf, *coefficients, boundaryVertices, vFace);
786   }
787   else { // ... otherwise, create from mesh interpolation
788     interpVerticesInExistingFace(gf, *coefficients, boundaryVertices, vFace);
789   }
790 
791   MFace face = ele->getFace(0);
792   faceVertices[face].insert(faceVertices[face].end(), vFace.begin(),
793                             vFace.end());
794   newVertices.insert(newVertices.end(), vFace.begin(), vFace.end());
795 }
796 
retrieveFaceBoundaryVertices(int k,int type,int nPts,const std::vector<MVertex * > & vCorner,const std::vector<MVertex * > & vEdges,std::vector<MVertex * > & v)797 static int retrieveFaceBoundaryVertices(int k, int type, int nPts,
798                                         const std::vector<MVertex *> &vCorner,
799                                         const std::vector<MVertex *> &vEdges,
800                                         std::vector<MVertex *> &v)
801 {
802   v.clear();
803   int nCorner;
804   switch(type) {
805   case TYPE_TET:
806     v.push_back(vCorner[MTetrahedron::faces_tetra(k, 0)]);
807     v.push_back(vCorner[MTetrahedron::faces_tetra(k, 1)]);
808     v.push_back(vCorner[MTetrahedron::faces_tetra(k, 2)]);
809     for(int i = 0; i < 3; ++i) {
810       int edge = MTetrahedron::faces2edge_tetra(k, i);
811       int n = std::abs(edge) - 1;
812       v.insert(v.end(), vEdges.begin() + n * nPts,
813                vEdges.begin() + (n + 1) * nPts);
814       if(edge < 0) std::reverse(v.end() - nPts, v.end());
815     }
816     return TYPE_TRI;
817   case TYPE_HEX:
818     v.push_back(vCorner[MHexahedron::faces_hexa(k, 0)]);
819     v.push_back(vCorner[MHexahedron::faces_hexa(k, 1)]);
820     v.push_back(vCorner[MHexahedron::faces_hexa(k, 2)]);
821     v.push_back(vCorner[MHexahedron::faces_hexa(k, 3)]);
822     for(int i = 0; i < 4; ++i) {
823       int edge = MHexahedron::faces2edge_hexa(k, i);
824       int n = std::abs(edge) - 1;
825       v.insert(v.end(), vEdges.begin() + n * nPts,
826                vEdges.begin() + (n + 1) * nPts);
827       if(edge < 0) std::reverse(v.end() - nPts, v.end());
828     }
829     return TYPE_QUA;
830   case TYPE_PRI:
831     nCorner = k < 2 ? 3 : 4;
832     v.push_back(vCorner[MPrism::faces_prism(k, 0)]);
833     v.push_back(vCorner[MPrism::faces_prism(k, 1)]);
834     v.push_back(vCorner[MPrism::faces_prism(k, 2)]);
835     if(nCorner == 4) v.push_back(vCorner[MPrism::faces_prism(k, 3)]);
836     for(int i = 0; i < nCorner; ++i) {
837       int edge = MPrism::faces2edge_prism(k, i);
838       int n = std::abs(edge) - 1;
839       v.insert(v.end(), vEdges.begin() + n * nPts,
840                vEdges.begin() + (n + 1) * nPts);
841       if(edge < 0) std::reverse(v.end() - nPts, v.end());
842     }
843     return nCorner == 3 ? TYPE_TRI : TYPE_QUA;
844   case TYPE_PYR:
845     nCorner = k < 4 ? 3 : 4;
846     v.push_back(vCorner[MPyramid::faces_pyramid(k, 0)]);
847     v.push_back(vCorner[MPyramid::faces_pyramid(k, 1)]);
848     v.push_back(vCorner[MPyramid::faces_pyramid(k, 2)]);
849     if(nCorner == 4) v.push_back(vCorner[MPyramid::faces_pyramid(k, 3)]);
850     for(int i = 0; i < nCorner; ++i) {
851       int edge = MPyramid::faces2edge_pyramid(k, i);
852       int n = std::abs(edge) - 1;
853       v.insert(v.end(), vEdges.begin() + n * nPts,
854                vEdges.begin() + (n + 1) * nPts);
855       if(edge < 0) std::reverse(v.end() - nPts, v.end());
856     }
857     return nCorner == 3 ? TYPE_TRI : TYPE_QUA;
858   default: return -1;
859   }
860 }
861 
862 // Get new face (excluding edge) vertices for a face of a 3D element
getFaceVertices(GRegion * gr,MElement * ele,std::vector<MVertex * > & newVertices,faceContainer & faceVertices,int nPts=1)863 static void getFaceVertices(GRegion *gr, MElement *ele,
864                             std::vector<MVertex *> &newVertices,
865                             faceContainer &faceVertices, int nPts = 1)
866 {
867   std::vector<MVertex *> vCorner;
868   ele->getVertices(vCorner);
869   // NB: We can get more than corner vertices but we use only corners
870 
871   for(int i = 0; i < ele->getNumFaces(); i++) {
872     MFace face = ele->getFace(i);
873     std::vector<MVertex *> vFace;
874     auto fIter = faceVertices.find(face);
875     if(fIter != faceVertices.end()) { // Vertices already exist
876       std::vector<MVertex *> vtcs = fIter->second;
877       int orientation;
878       bool swap;
879       if(fIter->first.computeCorrespondence(face, orientation, swap)) {
880         // Check correspondence and apply permutation if needed
881         if(face.getNumVertices() == 3 && nPts > 1)
882           reorientTrianglePoints(vtcs, orientation, swap);
883         else if(face.getNumVertices() == 4)
884           reorientQuadPoints(vtcs, orientation, swap, nPts - 1);
885       }
886       else
887         Msg::Error(
888           "Error in face lookup for retrieval of high order face nodes");
889       vFace.assign(vtcs.begin(), vtcs.end());
890     }
891     else { // Vertices do not exist, create them by interpolation
892       std::vector<MVertex *> faceBoundaryVertices;
893       int type = retrieveFaceBoundaryVertices(
894         i, ele->getType(), nPts, vCorner, newVertices, faceBoundaryVertices);
895       fullMatrix<double> *coefficients =
896         getInnerVertexPlacement(type, nPts + 1);
897       interpVerticesInExistingFace(gr, *coefficients, faceBoundaryVertices,
898                                    vFace);
899       faceVertices[face].insert(faceVertices[face].end(), vFace.begin(),
900                                 vFace.end());
901     }
902     newVertices.insert(newVertices.end(), vFace.begin(), vFace.end());
903   }
904 }
905 
906 // Get new interior vertices for a 3D element
getVolumeVertices(GRegion * gr,MElement * ele,std::vector<MVertex * > & newVertices,int nPts=1)907 static void getVolumeVertices(GRegion *gr, MElement *ele,
908                               std::vector<MVertex *> &newVertices, int nPts = 1)
909 {
910   std::vector<MVertex *> boundaryVertices;
911   {
912     std::size_t nCorner = ele->getNumPrimaryVertices();
913     boundaryVertices.reserve(nCorner + newVertices.size());
914     ele->getVertices(boundaryVertices);
915     boundaryVertices.resize(nCorner);
916     boundaryVertices.insert(boundaryVertices.end(), newVertices.begin(),
917                             newVertices.end());
918   }
919   int type = ele->getType();
920   fullMatrix<double> &coefficients = *getInnerVertexPlacement(type, nPts + 1);
921 
922   for(int k = 0; k < coefficients.size1(); k++) {
923     double x(0), y(0), z(0);
924     for(int j = 0; j < coefficients.size2(); j++) {
925       MVertex *v = boundaryVertices[j];
926       x += coefficients(k, j) * v->x();
927       y += coefficients(k, j) * v->y();
928       z += coefficients(k, j) * v->z();
929     }
930     MVertex *v = new MVertex(x, y, z, gr);
931     newVertices.push_back(v);
932   }
933 }
934 
935 // Creation of high-order elements
936 
setHighOrder(GEdge * ge,edgeContainer & edgeVertices,bool linear,int nbPts=1)937 static void setHighOrder(GEdge *ge, edgeContainer &edgeVertices, bool linear,
938                          int nbPts = 1)
939 {
940   std::vector<MLine *> lines2;
941   for(std::size_t i = 0; i < ge->lines.size(); i++) {
942     MLine *l = ge->lines[i];
943     std::vector<MVertex *> ve;
944     getEdgeVertices(ge, l, ve, edgeVertices, linear, nbPts);
945     if(nbPts == 1)
946       lines2.push_back(
947         new MLine3(l->getVertex(0), l->getVertex(1), ve[0], l->getPartition()));
948     else
949       lines2.push_back(
950         new MLineN(l->getVertex(0), l->getVertex(1), ve, l->getPartition()));
951     delete l;
952   }
953   ge->lines = lines2;
954   ge->deleteVertexArrays();
955 }
956 
setHighOrder(MTriangle * t,GFace * gf,edgeContainer & edgeVertices,faceContainer & faceVertices,bool linear,bool incomplete,int nPts)957 static MTriangle *setHighOrder(MTriangle *t, GFace *gf,
958                                edgeContainer &edgeVertices,
959                                faceContainer &faceVertices, bool linear,
960                                bool incomplete, int nPts)
961 {
962   std::vector<MVertex *> v;
963   getEdgeVertices(gf, t, v, edgeVertices, linear, nPts);
964   if(nPts == 1) {
965     return new MTriangle6(t->getVertex(0), t->getVertex(1), t->getVertex(2),
966                           v[0], v[1], v[2], 0, t->getPartition());
967   }
968   else {
969     if(!incomplete) getFaceVertices(gf, t, v, faceVertices, linear, nPts);
970     return new MTriangleN(t->getVertex(0), t->getVertex(1), t->getVertex(2), v,
971                           nPts + 1, 0, t->getPartition());
972   }
973 }
974 
setHighOrder(MQuadrangle * q,GFace * gf,edgeContainer & edgeVertices,faceContainer & faceVertices,bool linear,bool incomplete,int nPts)975 static MQuadrangle *setHighOrder(MQuadrangle *q, GFace *gf,
976                                  edgeContainer &edgeVertices,
977                                  faceContainer &faceVertices, bool linear,
978                                  bool incomplete, int nPts)
979 {
980   std::vector<MVertex *> v;
981   getEdgeVertices(gf, q, v, edgeVertices, linear, nPts);
982   if(incomplete) {
983     if(nPts == 1) {
984       return new MQuadrangle8(q->getVertex(0), q->getVertex(1), q->getVertex(2),
985                               q->getVertex(3), v[0], v[1], v[2], v[3], 0,
986                               q->getPartition());
987     }
988     else {
989       return new MQuadrangleN(q->getVertex(0), q->getVertex(1), q->getVertex(2),
990                               q->getVertex(3), v, nPts + 1, 0,
991                               q->getPartition());
992     }
993   }
994   else {
995     getFaceVertices(gf, q, v, faceVertices, linear, nPts);
996     if(nPts == 1) {
997       return new MQuadrangle9(q->getVertex(0), q->getVertex(1), q->getVertex(2),
998                               q->getVertex(3), v[0], v[1], v[2], v[3], v[4], 0,
999                               q->getPartition());
1000     }
1001     else {
1002       return new MQuadrangleN(q->getVertex(0), q->getVertex(1), q->getVertex(2),
1003                               q->getVertex(3), v, nPts + 1, 0,
1004                               q->getPartition());
1005     }
1006   }
1007 }
1008 
setHighOrder(GFace * gf,edgeContainer & edgeVertices,faceContainer & faceVertices,bool linear,bool incomplete,int nPts=1)1009 static void setHighOrder(GFace *gf, edgeContainer &edgeVertices,
1010                          faceContainer &faceVertices, bool linear,
1011                          bool incomplete, int nPts = 1)
1012 {
1013   std::vector<MTriangle *> triangles2;
1014   for(std::size_t i = 0; i < gf->triangles.size(); i++) {
1015     MTriangle *t = gf->triangles[i];
1016     MTriangle *tNew =
1017       setHighOrder(t, gf, edgeVertices, faceVertices, linear, incomplete, nPts);
1018     triangles2.push_back(tNew);
1019     delete t;
1020   }
1021   gf->triangles = triangles2;
1022 
1023   std::vector<MQuadrangle *> quadrangles2;
1024   for(std::size_t i = 0; i < gf->quadrangles.size(); i++) {
1025     MQuadrangle *q = gf->quadrangles[i];
1026     MQuadrangle *qNew =
1027       setHighOrder(q, gf, edgeVertices, faceVertices, linear, incomplete, nPts);
1028     quadrangles2.push_back(qNew);
1029     delete q;
1030   }
1031   gf->quadrangles = quadrangles2;
1032   gf->deleteVertexArrays();
1033 }
1034 
setHighOrder(MTetrahedron * t,GRegion * gr,edgeContainer & edgeVertices,faceContainer & faceVertices,bool incomplete,int nPts)1035 static MTetrahedron *setHighOrder(MTetrahedron *t, GRegion *gr,
1036                                   edgeContainer &edgeVertices,
1037                                   faceContainer &faceVertices, bool incomplete,
1038                                   int nPts)
1039 {
1040   std::vector<MVertex *> v;
1041   getEdgeVertices(gr, t, v, edgeVertices, nPts);
1042   if(nPts == 1) {
1043     return new MTetrahedron10(t->getVertex(0), t->getVertex(1), t->getVertex(2),
1044                               t->getVertex(3), v[0], v[1], v[2], v[3], v[4],
1045                               v[5], 0, t->getPartition());
1046   }
1047   else {
1048     if(!incomplete) {
1049       getFaceVertices(gr, t, v, faceVertices, nPts);
1050       getVolumeVertices(gr, t, v, nPts);
1051     }
1052     return new MTetrahedronN(t->getVertex(0), t->getVertex(1), t->getVertex(2),
1053                              t->getVertex(3), v, nPts + 1, 0,
1054                              t->getPartition());
1055   }
1056 }
1057 
setHighOrder(MHexahedron * h,GRegion * gr,edgeContainer & edgeVertices,faceContainer & faceVertices,bool incomplete,int nPts)1058 static MHexahedron *setHighOrder(MHexahedron *h, GRegion *gr,
1059                                  edgeContainer &edgeVertices,
1060                                  faceContainer &faceVertices, bool incomplete,
1061                                  int nPts)
1062 {
1063   std::vector<MVertex *> v;
1064   getEdgeVertices(gr, h, v, edgeVertices, nPts);
1065   if(incomplete) {
1066     if(nPts == 1) {
1067       return new MHexahedron20(
1068         h->getVertex(0), h->getVertex(1), h->getVertex(2), h->getVertex(3),
1069         h->getVertex(4), h->getVertex(5), h->getVertex(6), h->getVertex(7),
1070         v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10],
1071         v[11], 0, h->getPartition());
1072     }
1073     else {
1074       return new MHexahedronN(h->getVertex(0), h->getVertex(1), h->getVertex(2),
1075                               h->getVertex(3), h->getVertex(4), h->getVertex(5),
1076                               h->getVertex(6), h->getVertex(7), v, nPts + 1, 0,
1077                               h->getPartition());
1078     }
1079   }
1080   else {
1081     getFaceVertices(gr, h, v, faceVertices, nPts);
1082     getVolumeVertices(gr, h, v, nPts);
1083     if(nPts == 1) {
1084       return new MHexahedron27(
1085         h->getVertex(0), h->getVertex(1), h->getVertex(2), h->getVertex(3),
1086         h->getVertex(4), h->getVertex(5), h->getVertex(6), h->getVertex(7),
1087         v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10],
1088         v[11], v[12], v[13], v[14], v[15], v[16], v[17], v[18], 0,
1089         h->getPartition());
1090     }
1091     else {
1092       return new MHexahedronN(h->getVertex(0), h->getVertex(1), h->getVertex(2),
1093                               h->getVertex(3), h->getVertex(4), h->getVertex(5),
1094                               h->getVertex(6), h->getVertex(7), v, nPts + 1, 0,
1095                               h->getPartition());
1096     }
1097   }
1098 }
1099 
setHighOrder(MPrism * p,GRegion * gr,edgeContainer & edgeVertices,faceContainer & faceVertices,bool incomplete,int nPts)1100 static MPrism *setHighOrder(MPrism *p, GRegion *gr, edgeContainer &edgeVertices,
1101                             faceContainer &faceVertices, bool incomplete,
1102                             int nPts)
1103 {
1104   std::vector<MVertex *> v;
1105   getEdgeVertices(gr, p, v, edgeVertices, nPts);
1106   if(incomplete) {
1107     if(nPts == 1) {
1108       return new MPrism15(p->getVertex(0), p->getVertex(1), p->getVertex(2),
1109                           p->getVertex(3), p->getVertex(4), p->getVertex(5),
1110                           v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8],
1111                           0, p->getPartition());
1112     }
1113     else {
1114       return new MPrismN(p->getVertex(0), p->getVertex(1), p->getVertex(2),
1115                          p->getVertex(3), p->getVertex(4), p->getVertex(5), v,
1116                          nPts + 1, 0, p->getPartition());
1117     }
1118   }
1119   else {
1120     getFaceVertices(gr, p, v, faceVertices, nPts);
1121     if(nPts == 1) {
1122       return new MPrism18(p->getVertex(0), p->getVertex(1), p->getVertex(2),
1123                           p->getVertex(3), p->getVertex(4), p->getVertex(5),
1124                           v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8],
1125                           v[9], v[10], v[11], 0, p->getPartition());
1126     }
1127     else {
1128       getVolumeVertices(gr, p, v, nPts);
1129       return new MPrismN(p->getVertex(0), p->getVertex(1), p->getVertex(2),
1130                          p->getVertex(3), p->getVertex(4), p->getVertex(5), v,
1131                          nPts + 1, 0, p->getPartition());
1132     }
1133   }
1134 }
1135 
setHighOrder(MPyramid * p,GRegion * gr,edgeContainer & edgeVertices,faceContainer & faceVertices,bool incomplete,int nPts)1136 static MPyramid *setHighOrder(MPyramid *p, GRegion *gr,
1137                               edgeContainer &edgeVertices,
1138                               faceContainer &faceVertices, bool incomplete,
1139                               int nPts)
1140 {
1141   std::vector<MVertex *> v;
1142   getEdgeVertices(gr, p, v, edgeVertices, nPts);
1143   if(!incomplete) {
1144     getFaceVertices(gr, p, v, faceVertices, nPts);
1145     if(nPts > 1) { getVolumeVertices(gr, p, v, nPts); }
1146   }
1147   return new MPyramidN(p->getVertex(0), p->getVertex(1), p->getVertex(2),
1148                        p->getVertex(3), p->getVertex(4), v, nPts + 1, 0,
1149                        p->getPartition());
1150 }
1151 
setHighOrder(GRegion * gr,edgeContainer & edgeVertices,faceContainer & faceVertices,bool incomplete,int nPts=1)1152 static void setHighOrder(GRegion *gr, edgeContainer &edgeVertices,
1153                          faceContainer &faceVertices, bool incomplete,
1154                          int nPts = 1)
1155 {
1156   std::vector<MTetrahedron *> tetrahedra2;
1157   for(std::size_t i = 0; i < gr->tetrahedra.size(); i++) {
1158     MTetrahedron *t = gr->tetrahedra[i];
1159     MTetrahedron *tNew =
1160       setHighOrder(t, gr, edgeVertices, faceVertices, incomplete, nPts);
1161     tetrahedra2.push_back(tNew);
1162     delete t;
1163   }
1164   gr->tetrahedra = tetrahedra2;
1165 
1166   std::vector<MHexahedron *> hexahedra2;
1167   for(std::size_t i = 0; i < gr->hexahedra.size(); i++) {
1168     MHexahedron *h = gr->hexahedra[i];
1169     MHexahedron *hNew =
1170       setHighOrder(h, gr, edgeVertices, faceVertices, incomplete, nPts);
1171     hexahedra2.push_back(hNew);
1172     delete h;
1173   }
1174   gr->hexahedra = hexahedra2;
1175 
1176   std::vector<MPrism *> prisms2;
1177   for(std::size_t i = 0; i < gr->prisms.size(); i++) {
1178     MPrism *p = gr->prisms[i];
1179     MPrism *pNew =
1180       setHighOrder(p, gr, edgeVertices, faceVertices, incomplete, nPts);
1181     prisms2.push_back(pNew);
1182     delete p;
1183   }
1184   gr->prisms = prisms2;
1185 
1186   std::vector<MPyramid *> pyramids2;
1187   for(std::size_t i = 0; i < gr->pyramids.size(); i++) {
1188     MPyramid *p = gr->pyramids[i];
1189     MPyramid *pNew =
1190       setHighOrder(p, gr, edgeVertices, faceVertices, incomplete, nPts);
1191     pyramids2.push_back(pNew);
1192     delete p;
1193   }
1194   gr->pyramids = pyramids2;
1195 
1196   gr->deleteVertexArrays();
1197 }
1198 
1199 // High-level functions
1200 
1201 template <class T>
setFirstOrder(GEntity * e,std::vector<T * > & elements,bool onlyVisible,bool skipDiscrete)1202 static void setFirstOrder(GEntity *e, std::vector<T *> &elements,
1203                           bool onlyVisible, bool skipDiscrete)
1204 {
1205   if(onlyVisible && !e->getVisibility()) return;
1206   if(skipDiscrete && e->isFullyDiscrete()) return;
1207   std::vector<T *> elements1;
1208   for(std::size_t i = 0; i < elements.size(); i++) {
1209     T *ele = elements[i];
1210     std::size_t n = ele->getNumPrimaryVertices();
1211     std::vector<MVertex *> v1;
1212     v1.reserve(n);
1213     for(std::size_t j = 0; j < n; j++) v1.push_back(ele->getVertex(j));
1214     elements1.push_back(new T(v1, 0, ele->getPartition()));
1215     delete ele;
1216   }
1217   elements = elements1;
1218   e->deleteVertexArrays();
1219 }
1220 
deleteHighOrderVertices(GEntity * e,bool onlyVisible,bool skipDiscrete)1221 static void deleteHighOrderVertices(GEntity *e, bool onlyVisible,
1222                                     bool skipDiscrete)
1223 {
1224   if(onlyVisible && !e->getVisibility()) return;
1225   if(skipDiscrete && e->isFullyDiscrete()) return;
1226   std::vector<MVertex *> v1;
1227   for(std::size_t i = 0; i < e->mesh_vertices.size(); i++) {
1228     if(e->mesh_vertices[i]->getPolynomialOrder() > 1)
1229       delete e->mesh_vertices[i];
1230     else
1231       v1.push_back(e->mesh_vertices[i]);
1232   }
1233   e->mesh_vertices = v1;
1234   e->deleteVertexArrays();
1235 }
1236 
SetOrder1(GModel * m,bool onlyVisible,bool skipDiscrete)1237 void SetOrder1(GModel *m, bool onlyVisible, bool skipDiscrete)
1238 {
1239   m->destroyMeshCaches();
1240 
1241   // replace all elements with first order elements
1242   for(auto it = m->firstEdge(); it != m->lastEdge(); ++it) {
1243     setFirstOrder(*it, (*it)->lines, onlyVisible, skipDiscrete);
1244   }
1245   for(auto it = m->firstFace(); it != m->lastFace(); ++it) {
1246     setFirstOrder(*it, (*it)->triangles, onlyVisible, skipDiscrete);
1247     setFirstOrder(*it, (*it)->quadrangles, onlyVisible, skipDiscrete);
1248   }
1249   for(auto it = m->firstRegion(); it != m->lastRegion(); ++it) {
1250     setFirstOrder(*it, (*it)->tetrahedra, onlyVisible, skipDiscrete);
1251     setFirstOrder(*it, (*it)->hexahedra, onlyVisible, skipDiscrete);
1252     setFirstOrder(*it, (*it)->prisms, onlyVisible, skipDiscrete);
1253     setFirstOrder(*it, (*it)->pyramids, onlyVisible, skipDiscrete);
1254   }
1255 
1256   // remove all high order vertices
1257   for(auto it = m->firstEdge(); it != m->lastEdge(); ++it)
1258     deleteHighOrderVertices(*it, onlyVisible, skipDiscrete);
1259   for(auto it = m->firstFace(); it != m->lastFace(); ++it)
1260     deleteHighOrderVertices(*it, onlyVisible, skipDiscrete);
1261   for(auto it = m->firstRegion(); it != m->lastRegion(); ++it)
1262     deleteHighOrderVertices(*it, onlyVisible, skipDiscrete);
1263 }
1264 
checkHighOrderTriangles(const char * cc,GModel * m,std::vector<MElement * > & bad,double & minJGlob)1265 void checkHighOrderTriangles(const char *cc, GModel *m,
1266                              std::vector<MElement *> &bad, double &minJGlob)
1267 {
1268   bad.clear();
1269   minJGlob = 1.0;
1270   double minGGlob = 1.0;
1271   double avg = 0.0;
1272   int count = 0, nbfair = 0;
1273   for(auto it = m->firstFace(); it != m->lastFace(); ++it) {
1274     for(std::size_t i = 0; i < (*it)->triangles.size(); i++) {
1275       MTriangle *t = (*it)->triangles[i];
1276       double disto_ = t->distoShapeMeasure();
1277       double gamma_ = t->gammaShapeMeasure();
1278       double disto = disto_;
1279       minJGlob = std::min(minJGlob, disto);
1280       minGGlob = std::min(minGGlob, gamma_);
1281       avg += disto;
1282       count++;
1283       if(disto < 0)
1284         bad.push_back(t);
1285       else if(disto < 0.2)
1286         nbfair++;
1287     }
1288     /*
1289     for(std::size_t i = 0; i < (*it)->quadrangles.size(); i++){
1290       MQuadrangle *t = (*it)->quadrangles[i];
1291       double disto_ = t->distoShapeMeasure();
1292       double gamma_ = t->gammaShapeMeasure();
1293       double disto = disto_;
1294       minJGlob = std::min(minJGlob, disto);
1295       minGGlob = std::min(minGGlob, gamma_);
1296       avg += disto; count++;
1297       if (disto < 0) bad.push_back(t);
1298       else if (disto < 0.2) nbfair++;
1299     }
1300     */
1301   }
1302   if(!count) return;
1303   if(minJGlob > 0)
1304     Msg::Info(
1305       "%s: worst distortion = %g (%d elements in ]0, 0.2]); worst gamma = %g",
1306       cc, minJGlob, nbfair, minGGlob);
1307   else
1308     Msg::Warning(
1309       "%s: worst distortion = %g (avg = %g, %d elements with jac. < 0); "
1310       "worst gamma = %g",
1311       cc, minJGlob, avg / (count ? count : 1), bad.size(), minGGlob);
1312 }
1313 
checkHighOrderTetrahedron(const char * cc,GModel * m,std::vector<MElement * > & bad,double & minJGlob)1314 void checkHighOrderTetrahedron(const char *cc, GModel *m,
1315                                std::vector<MElement *> &bad, double &minJGlob)
1316 {
1317   bad.clear();
1318   minJGlob = 1.0;
1319   double avg = 0.0;
1320   int count = 0, nbfair = 0;
1321   for(auto it = m->firstRegion(); it != m->lastRegion(); ++it) {
1322     for(std::size_t i = 0; i < (*it)->tetrahedra.size(); i++) {
1323       MTetrahedron *t = (*it)->tetrahedra[i];
1324       double disto_ = t->distoShapeMeasure();
1325       minJGlob = std::min(minJGlob, disto_);
1326       avg += disto_;
1327       count++;
1328       if(disto_ < 0)
1329         bad.push_back(t);
1330       else if(disto_ < 0.2)
1331         nbfair++;
1332     }
1333   }
1334   if(!count) return;
1335 
1336   if(minJGlob > 0)
1337     Msg::Info("%s: worst distortion = %g (%d elements in ]0, 0.2])", cc,
1338               minJGlob, nbfair);
1339   else
1340     Msg::Warning(
1341       "%s: worst distortion = %g (avg = %g, %d elements with jac. < 0)", cc,
1342       minJGlob, avg / (count ? count : 1), bad.size());
1343 }
1344 
getOrder(GEntity * ge)1345 static int getOrder(GEntity *ge)
1346 {
1347   for(std::size_t i = 0; i < ge->getNumMeshElements(); i++)
1348     return ge->getMeshElement(i)->getPolynomialOrder();
1349   return 0;
1350 }
1351 
setHighOrderFromExistingMesh(GEdge * ge,edgeContainer & edgeVertices)1352 static void setHighOrderFromExistingMesh(GEdge *ge, edgeContainer &edgeVertices)
1353 {
1354   for(std::size_t i = 0; i < ge->getNumMeshElements(); i++) {
1355     MElement *e = ge->getMeshElement(i);
1356     std::vector<MVertex *> v;
1357     e->getVertices(v);
1358     MVertex *vMin, *vMax;
1359     getMinMaxVert(v[0], v[1], vMin, vMax);
1360     std::pair<MVertex *, MVertex *> p(vMin, vMax);
1361     if(edgeVertices.count(p) == 0) {
1362       for(std::size_t j = e->getNumPrimaryVertices(); j < e->getNumVertices();
1363           j++) {
1364         edgeVertices[p].push_back(v[j]);
1365       }
1366     }
1367   }
1368 }
1369 
setHighOrderFromExistingMesh(GFace * gf,edgeContainer & edgeVertices,faceContainer & faceVertices)1370 static void setHighOrderFromExistingMesh(GFace *gf, edgeContainer &edgeVertices,
1371                                          faceContainer &faceVertices)
1372 {
1373   for(std::size_t i = 0; i < gf->getNumMeshElements(); i++) {
1374     MElement *e = gf->getMeshElement(i);
1375     for(int j = 0; j < e->getNumEdges(); j++) {
1376       MEdge edg = e->getEdge(j);
1377       MVertex *vMin, *vMax;
1378       getMinMaxVert(edg.getVertex(0), edg.getVertex(1), vMin, vMax);
1379       std::pair<MVertex *, MVertex *> p(vMin, vMax);
1380       if(edgeVertices.count(p) == 0) {
1381         std::vector<MVertex *> edgv;
1382         e->getEdgeVertices(j, edgv);
1383         for(std::size_t k = 2; k < edgv.size(); k++) {
1384           edgeVertices[p].push_back(edgv[k]);
1385         }
1386       }
1387     }
1388     MFace f = e->getFace(0);
1389     std::vector<MVertex *> facev;
1390     if(faceVertices.count(f) == 0) {
1391       e->getFaceVertices(0, facev);
1392       for(std::size_t j = e->getNumPrimaryVertices() + e->getNumEdgeVertices();
1393           j < facev.size(); j++) {
1394         faceVertices[f].push_back(facev[j]);
1395       }
1396     }
1397   }
1398 }
1399 
SetOrderN(GModel * m,int order,bool linear,bool incomplete,bool onlyVisible)1400 void SetOrderN(GModel *m, int order, bool linear, bool incomplete,
1401                bool onlyVisible)
1402 {
1403   if(CTX::instance()->abortOnError && Msg::GetErrorCount()) return;
1404 
1405   // replace all the elements in the mesh with second order elements
1406   // by creating unique vertices on the edges/faces of the mesh:
1407   //
1408   // - if linear is set to true, new vertices are created by linear
1409   //   interpolation between existing ones. If not, new vertices are
1410   //   created on the exact geometry, provided that the geometrical
1411   //   edges/faces are discretized with 1D/2D elements. (I.e., if
1412   //   there are only 3D elements in the mesh then any new vertices on
1413   //   the boundary will always be created by linear interpolation,
1414   //   whether linear is set or not.)
1415   //
1416   // - if incomplete is set to true, we only create new vertices on
1417   //   edges (creating 8-node quads, 20-node hexas, etc., instead of
1418   //   9-node quads, 27-node hexas, etc.)
1419 
1420   // - if onlyVisible is true, then only the visible entities will be curved.
1421 
1422   int nPts = order - 1;
1423 
1424   char msg[256];
1425   sprintf(msg, "Meshing order %d (curvilinear %s)...", order,
1426           linear ? "off" : "on");
1427 
1428   Msg::StatusBar(true, msg);
1429 
1430   double t1 = Cpu(), w1 = TimeOfDay();
1431 
1432   m->destroyMeshCaches();
1433 
1434   // Keep track of vertex/entities created
1435   edgeContainer edgeVertices;
1436   faceContainer faceVertices;
1437 
1438   int counter = 0;
1439   int nTot = m->getNumEdges() + m->getNumFaces() + m->getNumRegions();
1440   Msg::StartProgressMeter(nTot);
1441 
1442   // TODO: we can leak nodes of discrete entities with existing high-order
1443   // nodes, if we ask a mesh with a different order
1444 
1445   for(auto it = m->firstEdge(); it != m->lastEdge(); ++it) {
1446     Msg::Info("Meshing curve %d order %d", (*it)->tag(), order);
1447     Msg::ProgressMeter(++counter, false, msg);
1448     if(onlyVisible && !(*it)->getVisibility()) continue;
1449     if(getOrder(*it) != order)
1450       setHighOrder(*it, edgeVertices, linear, nPts);
1451     else
1452       setHighOrderFromExistingMesh(*it, edgeVertices);
1453   }
1454 
1455   for(auto it = m->firstFace(); it != m->lastFace(); ++it) {
1456     Msg::Info("Meshing surface %d order %d", (*it)->tag(), order);
1457     Msg::ProgressMeter(++counter, false, msg);
1458     if(onlyVisible && !(*it)->getVisibility()) continue;
1459     if(getOrder(*it) != order)
1460       setHighOrder(*it, edgeVertices, faceVertices, linear, incomplete, nPts);
1461     else
1462       setHighOrderFromExistingMesh(*it, edgeVertices, faceVertices);
1463     if((*it)->getColumns() != nullptr) (*it)->getColumns()->clearElementData();
1464   }
1465 
1466   for(auto it = m->firstRegion(); it != m->lastRegion(); ++it) {
1467     Msg::Info("Meshing volume %d order %d", (*it)->tag(), order);
1468     Msg::ProgressMeter(++counter, false, msg);
1469     if(onlyVisible && !(*it)->getVisibility()) continue;
1470     if(getOrder(*it) != order)
1471       setHighOrder(*it, edgeVertices, faceVertices, incomplete, nPts);
1472     if((*it)->getColumns() != nullptr) (*it)->getColumns()->clearElementData();
1473   }
1474 
1475   // store nodes in entities
1476   m->pruneMeshVertexAssociations();
1477 
1478   Msg::StopProgressMeter();
1479   double t2 = Cpu(), w2 = TimeOfDay();
1480 
1481   std::vector<MElement *> bad;
1482   double worst;
1483   checkHighOrderTriangles("Surface mesh", m, bad, worst);
1484   checkHighOrderTetrahedron("Volume mesh", m, bad, worst);
1485 
1486   Msg::StatusBar(true, "Done meshing order %d (Wall %gs, CPU %gs)", order,
1487                  w2 - w1, t2 - t1);
1488 }
1489