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 *> &lt,
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 *> &lt)
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 *> &lt)
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 *> &lt,
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 *> &lt,
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 *> &lt,
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 *> &lt,
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 *> &lt,
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 *> &lt, 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 *> &lt, 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 *> &regions, 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 *> &regions, 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