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