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 #include "GModel.h"
7 #include "GRegion.h"
8 #include "MLine.h"
9 #include "MTriangle.h"
10 #include "MQuadrangle.h"
11 #include "MTetrahedron.h"
12 #include "MPyramid.h"
13 #include "MPrism.h"
14 #include "MHexahedron.h"
15 #include "Context.h"
16 #include "meshGFaceOptimize.h"
17 #include "qualityMeasures.h"
18
objective_function(double xi,MVertex * ver,double xTarget,double yTarget,double zTarget,const std::vector<MElement * > & lt,bool onlytet=false)19 static double objective_function(double xi, MVertex *ver, double xTarget,
20 double yTarget, double zTarget,
21 const std::vector<MElement *> <,
22 bool onlytet = false)
23 {
24 double x = ver->x();
25 double y = ver->y();
26 double z = ver->z();
27 ver->x() = (1. - xi) * ver->x() + xi * xTarget;
28 ver->y() = (1. - xi) * ver->y() + xi * yTarget;
29 ver->z() = (1. - xi) * ver->z() + xi * zTarget;
30 double minQual = 1.0;
31 for(std::size_t i = 0; i < lt.size(); i++) {
32 if(lt[i]->getNumVertices() == 4) {
33 double V;
34 double Q = qmTetrahedron::gamma(
35 lt[i]->getVertex(0)->x(), lt[i]->getVertex(0)->y(),
36 lt[i]->getVertex(0)->z(), lt[i]->getVertex(1)->x(),
37 lt[i]->getVertex(1)->y(), lt[i]->getVertex(1)->z(),
38 lt[i]->getVertex(2)->x(), lt[i]->getVertex(2)->y(),
39 lt[i]->getVertex(2)->z(), lt[i]->getVertex(3)->x(),
40 lt[i]->getVertex(3)->y(), lt[i]->getVertex(3)->z(), &V);
41 if(V > 0) Q = -Q;
42 minQual = std::min(Q, minQual);
43 }
44 else if(!onlytet) {
45 minQual = std::min(std::abs(lt[i]->minSICNShapeMeasure()) * .2, minQual);
46 }
47 }
48 ver->x() = x;
49 ver->y() = y;
50 ver->z() = z;
51 return minQual;
52 }
53
objective_function(double xi,MVertex * ver,GFace * gf,SPoint2 & p1,SPoint2 & p2,const std::vector<MElement * > & lt)54 static double objective_function(double xi, MVertex *ver, GFace *gf,
55 SPoint2 &p1, SPoint2 &p2,
56 const std::vector<MElement *> <)
57 {
58 double x = ver->x();
59 double y = ver->y();
60 double z = ver->z();
61 SPoint2 p = p1 * (1. - xi) + p2 * xi;
62 GPoint pp = gf->point(p);
63 ver->x() = pp.x();
64 ver->y() = pp.y();
65 ver->z() = pp.z();
66 double minQual = 1.0;
67 for(std::size_t i = 0; i < lt.size(); i++) {
68 if(lt[i]->getNumVertices() == 4)
69 minQual = std::min((lt[i]->etaShapeMeasure()), minQual);
70 else
71 minQual = std::min(std::abs(lt[i]->gammaShapeMeasure()), minQual);
72 }
73 ver->x() = x;
74 ver->y() = y;
75 ver->z() = z;
76 return minQual;
77 }
78
objective_function(double const xi,MVertex * const ver,GFace * const gf,SPoint3 & p1,SPoint3 & p2,const std::vector<MElement * > & lt)79 static double objective_function(double const xi, MVertex *const ver,
80 GFace *const gf, SPoint3 &p1, SPoint3 &p2,
81 const std::vector<MElement *> <)
82 {
83 double const x = ver->x();
84 double const y = ver->y();
85 double const z = ver->z();
86 SPoint3 p = p1 * (1. - xi) + p2 * xi;
87
88 double initialGuess[2] = {0, 0};
89
90 GPoint pp = gf->closestPoint(p, initialGuess);
91 ver->x() = pp.x();
92 ver->y() = pp.y();
93 ver->z() = pp.z();
94 double minQual = 1.0;
95 for(std::size_t i = 0; i < lt.size(); i++) {
96 if(lt[i]->getNumVertices() == 4)
97 minQual = std::min(lt[i]->etaShapeMeasure(), minQual);
98 else
99 minQual = std::min(std::abs(lt[i]->gammaShapeMeasure()), minQual);
100 }
101 ver->x() = x;
102 ver->y() = y;
103 ver->z() = z;
104 return minQual;
105 }
106
Stopping_Rule(double const x0,double const x1,double const tol)107 static bool Stopping_Rule(double const x0, double const x1, double const tol)
108 {
109 return std::abs(x1 - x0) < tol;
110 }
111
Maximize_Quality_Golden_Section(MVertex * ver,double xTarget,double yTarget,double zTarget,const std::vector<MElement * > & lt,double const tol,double & q)112 static double Maximize_Quality_Golden_Section(MVertex *ver, double xTarget,
113 double yTarget, double zTarget,
114 const std::vector<MElement *> <,
115 double const tol, double &q)
116 {
117 const double lambda = 0.5 * (std::sqrt(5.0) - 1.0);
118 const double mu = 0.5 * (3.0 - std::sqrt(5.0)); // = 1 - lambda
119 double a = 0.0;
120 double b = 2.0;
121
122 double x1 = b - lambda * (b - a);
123 double x2 = a + lambda * (b - a);
124 double fx1 = objective_function(x1, ver, xTarget, yTarget, zTarget, lt);
125 double fx2 = objective_function(x2, ver, xTarget, yTarget, zTarget, lt);
126
127 if(tol < 0.0) return fx1 > fx2 ? x1 : x2;
128
129 while(!Stopping_Rule(a, b, tol)) {
130 // printf("GOLDEN : %g %g (%12.5E,%12.5E)\n",a,b,fa,fb);
131 if(fx1 < fx2) {
132 a = x1;
133 if(Stopping_Rule(a, b, tol)) break;
134 x1 = x2;
135 fx1 = fx2;
136 x2 = b - mu * (b - a);
137 fx2 = objective_function(x2, ver, xTarget, yTarget, zTarget, lt);
138 }
139 else {
140 b = x2;
141 if(Stopping_Rule(a, b, tol)) break;
142 x2 = x1;
143 fx2 = fx1;
144 x1 = a + mu * (b - a);
145 fx1 = objective_function(x1, ver, xTarget, yTarget, zTarget, lt);
146 }
147 }
148 // printf("finally : %g %g (%12.5E,%12.5E)\n",a,b,fa,fb);
149 q = std::min(fx1, fx2);
150 return a;
151 }
152
Maximize_Quality_Golden_Section(MVertex * ver,GFace * gf,SPoint2 & p1,SPoint2 & p2,const std::vector<MElement * > & lt,double tol,double & worst)153 static double Maximize_Quality_Golden_Section(MVertex *ver, GFace *gf,
154 SPoint2 &p1, SPoint2 &p2,
155 const std::vector<MElement *> <,
156 double tol, double &worst)
157 {
158 const double lambda = 0.5 * (std::sqrt(5.0) - 1.0);
159 const double mu = 0.5 * (3.0 - std::sqrt(5.0)); // = 1 - lambda
160 double a = 0.0;
161 double b = 1.0;
162
163 worst = objective_function(0.0, ver, gf, p1, p2, lt);
164
165 if(worst > 0.5) return 0.0;
166
167 double x1 = b - lambda * (b - a);
168 double x2 = a + lambda * (b - a);
169 double fx1 = objective_function(x1, ver, gf, p1, p2, lt);
170 double fx2 = objective_function(x2, ver, gf, p1, p2, lt);
171
172 if(tol < 0.0) return fx1 > fx2 ? x1 : x2;
173
174 while(!Stopping_Rule(a, b, tol)) {
175 // printf("GOLDEN : %g %g (%12.5E,%12.5E)\n",a,b,fa,fb);
176 if(fx1 < fx2) {
177 a = x1;
178 if(Stopping_Rule(a, b, tol)) break;
179 x1 = x2;
180 fx1 = fx2;
181 x2 = b - mu * (b - a);
182 fx2 = objective_function(x2, ver, gf, p1, p2, lt);
183 }
184 else {
185 b = x2;
186 if(Stopping_Rule(a, b, tol)) break;
187 x2 = x1;
188 fx2 = fx1;
189 x1 = a + mu * (b - a);
190 fx1 = objective_function(x1, ver, gf, p1, p2, lt);
191 }
192 }
193 double final = objective_function(a, ver, gf, p1, p2, lt);
194 if(final < worst) return 0.0;
195 worst = final;
196 // printf("finally : %g %g (%12.5E,%12.5E)\n",a,b,fa,fb);
197 return a;
198 }
199
Maximize_Quality_Golden_Section(MVertex * ver,GFace * gf,SPoint3 & p1,SPoint3 & p2,const std::vector<MElement * > & lt,double tol,double & worst)200 static double Maximize_Quality_Golden_Section(MVertex *ver, GFace *gf,
201 SPoint3 &p1, SPoint3 &p2,
202 const std::vector<MElement *> <,
203 double tol, double &worst)
204 {
205 const double lambda = 0.5 * (std::sqrt(5.0) - 1.0);
206 const double mu = 0.5 * (3.0 - std::sqrt(5.0)); // = 1 - lambda
207 double a = 0.0;
208 double b = 1.0;
209
210 worst = objective_function(0.0, ver, gf, p1, p2, lt);
211
212 if(worst > 0.5) return 0.0;
213
214 double x1 = b - lambda * (b - a);
215 double x2 = a + lambda * (b - a);
216 double fx1 = objective_function(x1, ver, gf, p1, p2, lt);
217 double fx2 = objective_function(x2, ver, gf, p1, p2, lt);
218
219 if(tol < 0.0) return fx1 > fx2 ? x1 : x2;
220
221 while(!Stopping_Rule(a, b, tol)) {
222 // printf("GOLDEN : %g %g (%12.5E,%12.5E)\n",a,b,fa,fb);
223 if(fx1 < fx2) {
224 a = x1;
225 if(Stopping_Rule(a, b, tol)) break;
226 x1 = x2;
227 fx1 = fx2;
228 x2 = b - mu * (b - a);
229 fx2 = objective_function(x2, ver, gf, p1, p2, lt);
230 }
231 else {
232 b = x2;
233 if(Stopping_Rule(a, b, tol)) break;
234 x2 = x1;
235 fx2 = fx1;
236 x1 = a + mu * (b - a);
237 fx1 = objective_function(x1, ver, gf, p1, p2, lt);
238 }
239 }
240 double final = objective_function(a, ver, gf, p1, p2, lt);
241 if(final < worst) return 0.0;
242 worst = final;
243 // printf("finally : %g %g (%12.5E,%12.5E)\n",a,b,fa,fb);
244 return a;
245 }
246
_relocateVertexOfPyramid(MVertex * ver,const std::vector<MElement * > & lt,double relax)247 static void _relocateVertexOfPyramid(MVertex *ver,
248 const std::vector<MElement *> <,
249 double relax)
250 {
251 if(ver->onWhat()->dim() != 3) return;
252 double x = 0.0, y = 0.0, z = 0.0;
253 int N = 0;
254 MElement *pyramid = nullptr;
255
256 for(std::size_t i = 0; i < lt.size(); i++) {
257 double XCG = 0.0, YCG = 0.0, ZCG = 0.0;
258 if(lt[i]->getNumVertices() == 5)
259 pyramid = lt[i];
260 else {
261 for(std::size_t j = 0; j < lt[i]->getNumVertices(); j++) {
262 XCG += lt[i]->getVertex(j)->x();
263 YCG += lt[i]->getVertex(j)->y();
264 ZCG += lt[i]->getVertex(j)->z();
265 }
266 x += XCG;
267 y += YCG;
268 z += ZCG;
269 N += lt[i]->getNumVertices();
270 }
271 }
272 x /= N;
273 y /= N;
274 z /= N;
275
276 if(pyramid) {
277 MFace q = pyramid->getFace(4);
278 double A = q.approximateArea();
279 SVector3 n = q.normal();
280 n.normalize();
281 SPoint3 c = q.barycenter();
282 SVector3 d(x - c.x(), y - c.y(), z - c.z());
283 if(dot(n, d) < 0) n = n * (-1.0);
284 double H = .5 * sqrt(fabs(A));
285 double XOPT = c.x() + relax * H * n.x();
286 double YOPT = c.y() + relax * H * n.y();
287 double ZOPT = c.z() + relax * H * n.z();
288 double FULL_MOVE_OBJ =
289 objective_function(1.0, ver, XOPT, YOPT, ZOPT, lt, true);
290 // printf("relax %g obj %g\n",relax,FULL_MOVE_OBJ);
291 if(FULL_MOVE_OBJ > 0.1) {
292 ver->x() = XOPT;
293 ver->y() = YOPT;
294 ver->z() = ZOPT;
295 return;
296 }
297 }
298 }
299
_relocateVertexGolden(MVertex * ver,const std::vector<MElement * > & lt,double relax,double tol)300 static void _relocateVertexGolden(MVertex *ver,
301 const std::vector<MElement *> <,
302 double relax, double tol)
303 {
304 if(ver->onWhat()->dim() != 3) return;
305 double x = 0.0, y = 0.0, z = 0.0;
306 int N = 0;
307 for(std::size_t i = 0; i < lt.size(); i++) {
308 double XCG = 0.0, YCG = 0.0, ZCG = 0.0;
309 for(std::size_t j = 0; j < lt[i]->getNumVertices(); j++) {
310 XCG += lt[i]->getVertex(j)->x();
311 YCG += lt[i]->getVertex(j)->y();
312 ZCG += lt[i]->getVertex(j)->z();
313 }
314 x += XCG;
315 y += YCG;
316 z += ZCG;
317 N += lt[i]->getNumVertices();
318 }
319
320 double NO_MOVE_OBJ = objective_function(0.0, ver, x / N, y / N, z / N, lt);
321 if(NO_MOVE_OBJ > 0.1) return;
322 double FULL_MOVE_OBJ = objective_function(1.0, ver, x / N, y / N, z / N, lt);
323 if(FULL_MOVE_OBJ > NO_MOVE_OBJ) {
324 ver->x() = x / N;
325 ver->y() = y / N;
326 ver->z() = z / N;
327 return;
328 }
329
330 double q;
331 double xi = relax * Maximize_Quality_Golden_Section(ver, x / N, y / N, z / N,
332 lt, tol, q);
333 ver->x() = (1. - xi) * ver->x() + xi * x / N;
334 ver->y() = (1. - xi) * ver->y() + xi * y / N;
335 ver->z() = (1. - xi) * ver->z() + xi * z / N;
336 }
337
338 // use real space + projection at the end
_relocateVertex2(GFace * gf,MVertex * ver,const std::vector<MElement * > & lt,double tol)339 static double _relocateVertex2(GFace *gf, MVertex *ver,
340 const std::vector<MElement *> <, double tol)
341 {
342 SPoint3 p1(0, 0, 0);
343 std::size_t counter = 0;
344 for(std::size_t i = 0; i < lt.size(); i++) {
345 for(std::size_t j = 0; j < lt[i]->getNumVertices(); j++) {
346 MVertex *v = lt[i]->getVertex(j);
347 p1 += SPoint3(v->x(), v->y(), v->z());
348 counter++;
349 }
350 }
351 p1 *= 1.0 / (double)counter;
352 SPoint3 p2(ver->x(), ver->y(), ver->z());
353 double worst;
354 double xi = Maximize_Quality_Golden_Section(ver, gf, p1, p2, lt, tol, worst);
355
356 SPoint3 p = p1 * (1 - xi) + p2 * xi;
357 double initialGuess[2] = {0, 0};
358 GPoint pp = gf->closestPoint(p, initialGuess);
359 if(!pp.succeeded()) return 2.0;
360 ver->x() = pp.x();
361 ver->y() = pp.y();
362 ver->z() = pp.z();
363 return worst;
364 }
365
_relocateVertex(GFace * gf,MVertex * ver,const std::vector<MElement * > & lt,double tol)366 static double _relocateVertex(GFace *gf, MVertex *ver,
367 const std::vector<MElement *> <, double tol)
368 {
369 if(ver->onWhat()->dim() != 2) return 2.0;
370
371 SPoint2 p1(0, 0);
372 SPoint2 p2;
373 if(ver->getParameter(0, p2[0])) { ver->getParameter(1, p2[1]); }
374 else {
375 return _relocateVertex2(gf, ver, lt, tol);
376 }
377
378 std::size_t counter = 0;
379 for(std::size_t i = 0; i < lt.size(); i++) {
380 for(std::size_t j = 0; j < lt[i]->getNumVertices(); j++) {
381 MVertex *v = lt[i]->getVertex(j);
382 SPoint2 pp;
383 reparamMeshVertexOnFace(v, gf, pp);
384 counter++;
385 if(v->onWhat()->dim() == 1) {
386 GEdge *ge = dynamic_cast<GEdge *>(v->onWhat());
387 // do not take any chance
388 if(ge->isSeam(gf)) return 2.0;
389 }
390 p1 += pp;
391 }
392 }
393 p1 *= 1. / (double)counter;
394 double worst;
395 double xi = Maximize_Quality_Golden_Section(ver, gf, p1, p2, lt, tol, worst);
396 // if (xi != 0) printf("xi = %g\n",xi);
397 SPoint2 p = p1 * (1 - xi) + p2 * xi;
398 GPoint pp = gf->point(p);
399 if(!pp.succeeded()) return 2.0;
400 ver->x() = pp.x();
401 ver->y() = pp.y();
402 ver->z() = pp.z();
403 ver->setParameter(0, pp.u());
404 ver->setParameter(1, pp.v());
405 return worst;
406 }
407
408 void getAllBoundaryLayerVertices(GFace *gf, std::set<MVertex *> &vs);
409
RelocateVertices(GFace * gf,int niter,double tol)410 void RelocateVertices(GFace *gf, int niter, double tol)
411 {
412 if(!niter) return;
413 Msg::Debug("relocate vertices (face %i)", gf->tag());
414
415 std::set<MVertex *> vs;
416 getAllBoundaryLayerVertices(gf, vs);
417
418 v2t_cont adj;
419 buildVertexToElement(gf->triangles, adj);
420 buildVertexToElement(gf->quadrangles, adj);
421 for(int i = 0; i < niter; i++) {
422 auto it = adj.begin();
423 while(it != adj.end()) {
424 if(vs.find(it->first) == vs.end()) {
425 _relocateVertex(gf, it->first, it->second, tol);
426 }
427 ++it;
428 }
429 }
430 }
431
RelocateVertices(GRegion * region,int niter,double tol)432 void RelocateVertices(GRegion *region, int niter, double tol)
433 {
434 if(!niter) return;
435
436 v2t_cont adj;
437 buildVertexToElement(region->tetrahedra, adj);
438 buildVertexToElement(region->pyramids, adj);
439 buildVertexToElement(region->prisms, adj);
440 buildVertexToElement(region->hexahedra, adj);
441 for(int i = 0; i < niter + 2; i++) {
442 auto it = adj.begin();
443 double relax = std::min((double)(i + 1) / niter, 1.0);
444 while(it != adj.end()) {
445 _relocateVertexGolden(it->first, it->second, relax, tol);
446 ++it;
447 }
448 }
449 }
450
RelocateVerticesOfPyramids(GRegion * region,int niter,double tol)451 void RelocateVerticesOfPyramids(GRegion *region, int niter, double tol)
452 {
453 if(!niter) return;
454
455 // FAST IMPLEMENTATION
456 std::vector<MVertex *> _v_pyr;
457 for(size_t i = 0; i < region->pyramids.size(); i++) {
458 _v_pyr.push_back(region->pyramids[i]->getVertex(4));
459 }
460 std::sort(_v_pyr.begin(), _v_pyr.end());
461
462 std::vector<MTetrahedron *> _tets;
463 std::set<MVertex *> _vts;
464 for(size_t i = 0; i < region->tetrahedra.size(); i++) {
465 MTetrahedron *t = region->tetrahedra[i];
466 for(size_t j = 0; j < 4; j++) {
467 MVertex *v = t->getVertex(j);
468 if(std::binary_search(_v_pyr.begin(), _v_pyr.end(), v)) {
469 _tets.push_back(t);
470 _vts.insert(t->getVertex(0));
471 _vts.insert(t->getVertex(1));
472 _vts.insert(t->getVertex(2));
473 _vts.insert(t->getVertex(3));
474 break;
475 }
476 }
477 }
478
479 _tets.clear();
480 for(size_t i = 0; i < region->tetrahedra.size(); i++) {
481 MTetrahedron *t = region->tetrahedra[i];
482 for(size_t j = 0; j < 4; j++) {
483 MVertex *v = t->getVertex(j);
484 if(_vts.find(v) != _vts.end()) {
485 _tets.push_back(t);
486 break;
487 }
488 }
489 }
490
491 v2t_cont adj;
492 // buildVertexToElement(region->tetrahedra, adj);
493 buildVertexToElement(_tets, adj);
494 buildVertexToElement(region->pyramids, adj);
495 buildVertexToElement(region->prisms, adj);
496 buildVertexToElement(region->hexahedra, adj);
497
498 for(int i = 0; i < 10; i++) {
499 double X = (double)(i + 1) / 10.;
500 auto it = adj.begin();
501 while(it != adj.end()) {
502 _relocateVertexOfPyramid(it->first, it->second, X);
503 ++it;
504 }
505 }
506 // return;
507 for(int i = 0; i < niter + 2; i++) {
508 auto it = adj.begin();
509 double relax = std::min((double)(i + 1) / niter, 1.0);
510 while(it != adj.end()) {
511 _relocateVertexGolden(it->first, it->second, relax, tol);
512 ++it;
513 }
514 }
515 }
516
RelocateVerticesOfPyramids(std::vector<GRegion * > & regions,int niter,double tol)517 void RelocateVerticesOfPyramids(std::vector<GRegion *> ®ions, int niter,
518 double tol)
519 {
520 for(std::size_t k = 0; k < regions.size(); k++) {
521 RelocateVerticesOfPyramids(regions[k], niter, tol);
522 }
523 }
524
RelocateVertices(std::vector<GRegion * > & regions,int niter,double tol)525 void RelocateVertices(std::vector<GRegion *> ®ions, int niter, double tol)
526 {
527 for(std::size_t k = 0; k < regions.size(); k++) {
528 RelocateVertices(regions[k], niter, tol);
529 }
530 }
531