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 <algorithm>
7 #include "meshMetric.h"
8 #include "meshGFaceOptimize.h"
9 #include "Context.h"
10 #include "GEntity.h"
11 #include "GModel.h"
12 #include "gmshLevelset.h"
13 #include "MElementOctree.h"
14 #include "OS.h"
15 #include "Context.h"
16 
meshMetric(GModel * gm)17 meshMetric::meshMetric(GModel *gm)
18 {
19   hasAnalyticalMetric = false;
20   _dim = gm->getDim();
21   std::map<MElement *, MElement *> newP;
22   std::map<MElement *, MElement *> newD;
23 
24   if(_dim == 2) {
25     for(auto fit = gm->firstFace(); fit != gm->lastFace(); ++fit) {
26       for(std::size_t i = 0; i < (*fit)->getNumMeshElements(); i++) {
27         MElement *e = (*fit)->getMeshElement(i);
28         MElement *copy = e->copy(_vertexMap, newP, newD);
29         _elements.push_back(copy);
30       }
31     }
32   }
33   else if(_dim == 3) {
34     for(auto rit = gm->firstRegion(); rit != gm->lastRegion(); ++rit) {
35       for(std::size_t i = 0; i < (*rit)->getNumMeshElements(); i++) {
36         MElement *e = (*rit)->getMeshElement(i);
37         MElement *copy = e->copy(_vertexMap, newP, newD);
38         _elements.push_back(copy);
39       }
40     }
41   }
42   _octree = new MElementOctree(_elements);
43   buildVertexToElement(_elements, _adj);
44 }
45 
meshMetric(std::vector<MElement * > elements)46 meshMetric::meshMetric(std::vector<MElement *> elements)
47 {
48   hasAnalyticalMetric = false;
49 
50   _dim = elements[0]->getDim();
51   std::map<MElement *, MElement *> newP;
52   std::map<MElement *, MElement *> newD;
53 
54   for(std::size_t i = 0; i < elements.size(); i++) {
55     MElement *e = elements[i];
56     MElement *copy = e->copy(_vertexMap, newP, newD);
57     _elements.push_back(copy);
58   }
59 
60   _octree = new MElementOctree(_elements);
61   buildVertexToElement(_elements, _adj);
62 }
63 
addMetric(int technique,simpleFunction<double> * fct,const std::vector<double> & parameters)64 void meshMetric::addMetric(int technique, simpleFunction<double> *fct,
65                            const std::vector<double> &parameters)
66 {
67   needMetricUpdate = true;
68 
69   int metricNumber = setOfMetrics.size();
70   setOfFcts[metricNumber] = fct;
71   setOfParameters[metricNumber] = parameters;
72   setOfTechniques[metricNumber] = technique;
73 
74   if(fct->hasDerivatives()) hasAnalyticalMetric = true;
75 
76   computeMetric(metricNumber);
77 }
78 
updateMetrics()79 void meshMetric::updateMetrics()
80 {
81   if(!setOfMetrics.size()) {
82     Msg::Error("Can't intersect metrics, no metric registered");
83     return;
84   }
85 
86   auto it = _adj.begin();
87   for(; it != _adj.end(); it++) {
88     MVertex *ver = it->first;
89     _nodalMetrics[ver] = setOfMetrics[0][ver];
90     _nodalSizes[ver] = setOfSizes[0][ver];
91 
92     if(setOfMetrics.size() > 1)
93       for(std::size_t i = 1; i < setOfMetrics.size(); i++) {
94         _nodalMetrics[ver] =
95           (_dim == 3) ? intersection_conserve_mostaniso(_nodalMetrics[ver],
96                                                         setOfMetrics[i][ver]) :
97                         intersection_conserve_mostaniso_2d(
98                           _nodalMetrics[ver], setOfMetrics[i][ver]);
99         _nodalSizes[ver] = std::min(_nodalSizes[ver], setOfSizes[i][ver]);
100       }
101   }
102   needMetricUpdate = false;
103 }
104 
exportInfo(const char * fileendname)105 void meshMetric::exportInfo(const char *fileendname)
106 {
107   if(needMetricUpdate) updateMetrics();
108   std::stringstream sg, sm, sl, sh, shm;
109   sg << "meshmetric_gradients_" << fileendname;
110   sm << "meshmetric_metric_" << fileendname;
111   sl << "meshmetric_levelset_" << fileendname;
112   sh << "meshmetric_hessian_" << fileendname;
113   shm << "meshmetric_hessianmat_" << fileendname;
114   std::ofstream out_grad(sg.str().c_str());
115   std::ofstream out_metric(sm.str().c_str());
116   std::ofstream out_ls(sl.str().c_str());
117   std::ofstream out_hess(sh.str().c_str());
118   std::ofstream out_hessmat(shm.str().c_str());
119   out_grad << "View \"ls_gradient\"{" << std::endl;
120   out_metric << "View \"metric\"{" << std::endl;
121   out_ls << "View \"ls\"{" << std::endl;
122   out_hess << "View \"hessian\"{" << std::endl;
123   out_hessmat << "View \"hessian_mat\"{" << std::endl;
124   auto itelem = _elements.begin();
125   auto itelemen = _elements.end();
126   for(; itelem != itelemen; itelem++) {
127     MElement *e = *itelem;
128     if(e->getDim() == 2) {
129       out_metric << "TT(";
130       out_grad << "VT(";
131       out_ls << "ST(";
132       out_hess << "ST(";
133       out_hessmat << "TT(";
134     }
135     else {
136       out_metric << "TS(";
137       out_grad << "VS(";
138       out_ls << "SS(";
139       out_hess << "SS(";
140       out_hessmat << "TS(";
141     }
142     for(std::size_t i = 0; i < e->getNumVertices(); i++) {
143       MVertex *ver = e->getVertex(i);
144       out_metric << ver->x() << "," << ver->y() << "," << ver->z();
145       out_grad << ver->x() << "," << ver->y() << "," << ver->z();
146       out_ls << ver->x() << "," << ver->y() << "," << ver->z();
147       out_hess << ver->x() << "," << ver->y() << "," << ver->z();
148       out_hessmat << ver->x() << "," << ver->y() << "," << ver->z();
149       if(i != e->getNumVertices() - 1) {
150         out_metric << ",";
151         out_grad << ",";
152         out_ls << ",";
153         out_hess << ",";
154         out_hessmat << ",";
155       }
156       else {
157         out_metric << "){";
158         out_grad << "){";
159         out_ls << "){";
160         out_hess << "){";
161         out_hessmat << "){";
162       }
163     }
164     for(std::size_t i = 0; i < e->getNumVertices(); i++) {
165       MVertex *ver = e->getVertex(i);
166       out_ls << vals[ver];
167       out_hess << (hessians[ver](0, 0) + hessians[ver](1, 1) +
168                    hessians[ver](2, 2));
169       if(i == (e->getNumVertices() - 1)) {
170         out_ls << "};" << std::endl;
171         out_hess << "};" << std::endl;
172       }
173       else {
174         out_ls << ",";
175         out_hess << ",";
176       }
177       for(int k = 0; k < 3; k++) {
178         out_grad << grads[ver](k);
179         if((k == 2) && (i == (e->getNumVertices() - 1)))
180           out_grad << "};" << std::endl;
181         else
182           out_grad << ",";
183         for(int l = 0; l < 3; l++) {
184           out_metric << _nodalMetrics[ver](k, l);
185           out_hessmat << hessians[ver](k, l);
186           if((k == 2) && (l == 2) && (i == (e->getNumVertices() - 1))) {
187             out_metric << "};" << std::endl;
188             out_hessmat << "};" << std::endl;
189           }
190           else {
191             out_metric << ",";
192             out_hessmat << ",";
193           }
194         }
195       }
196     }
197   }
198   out_grad << "};" << std::endl;
199   out_metric << "};" << std::endl;
200   out_ls << "};" << std::endl;
201   out_hess << "};" << std::endl;
202   out_hessmat << "};" << std::endl;
203   out_grad.close();
204   out_metric.close();
205   out_ls.close();
206   out_hess.close();
207   out_hessmat.close();
208 }
209 
~meshMetric()210 meshMetric::~meshMetric()
211 {
212   if(_octree) delete _octree;
213   for(std::size_t i = 0; i < _elements.size(); i++) delete _elements[i];
214 }
215 
computeValues()216 void meshMetric::computeValues()
217 {
218   auto it = _adj.begin();
219   while(it != _adj.end()) {
220     std::vector<MElement *> lt = it->second;
221     MVertex *ver = it->first;
222     vals[ver] = (*_fct)(ver->x(), ver->y(), ver->z());
223     it++;
224   }
225 }
226 
227 // Determines set of vertices to use for least squares
getLSBlob(std::size_t minNbPt,v2t_cont::iterator it,v2t_cont & adj)228 std::vector<MVertex *> getLSBlob(std::size_t minNbPt, v2t_cont::iterator it,
229                                  v2t_cont &adj)
230 {
231   //  static const double RADFACT = 3;
232   //
233   //  SPoint3 p0 = it->first->point();  // Vertex coordinates (center of circle)
234   //
235   //  double rad = 0.;
236   //  for (auto itEl = it->second.begin(); itEl != it->second.end(); itEl++)
237   //    rad = std::max(rad,(*itEl)->getOuterRadius());
238   //  rad *= RADFACT;
239 
240   std::vector<MVertex *> vv(1, it->first),
241     bvv = vv; // Vector of vertices in blob and in boundary of blob
242   do {
243     std::set<MVertex *> nbvv; // Set of vertices in new boundary
244     for(auto itBV = bvv.begin(); itBV != bvv.end();
245         itBV++) { // For each boundary vertex...
246       std::vector<MElement *> &adjBV = adj[*itBV];
247       for(auto itBVEl = adjBV.begin(); itBVEl != adjBV.end(); itBVEl++) {
248         for(std::size_t iV = 0; iV < (*itBVEl)->getNumVertices();
249             iV++) { // ... look for adjacent vertices...
250           MVertex *v = (*itBVEl)->getVertex(iV);
251           //          if ((find(vv.begin(),vv.end(),v) == vv.end()) &&
252           //          (p0.distance(v->point()) <= rad)) nbvv.insert(v);
253           if(find(vv.begin(), vv.end(), v) == vv.end())
254             nbvv.insert(v); // ... and add them in the new boundary if they are
255                             // not already in the blob
256         }
257       }
258     }
259     if(nbvv.empty())
260       bvv.clear();
261     else {
262       bvv.assign(nbvv.begin(), nbvv.end());
263       vv.insert(vv.end(), nbvv.begin(), nbvv.end());
264     }
265     //  } while (!bvv.empty());
266   } while(vv.size() < minNbPt); // Repeat until min. number of points is reached
267 
268   return vv;
269 }
270 
271 // Compute derivatives and second order derivatives using least squares
272 // 2D LS system: a_i0*x^2+a_i1*x*y+a_i2*y^2+a_i3*x+a_i4*y+a_i5=b_i
273 // 3D LS system:
274 // a_i0*x^2+a_i1*x*y+a_i2*x*z+a_i3*y^2+a_i4*y*z+a_i5*z^2+a_i6*x+a_i7*y+a_i8*z+a_i9=b_i
computeHessian()275 void meshMetric::computeHessian()
276 {
277   std::size_t sysDim = (_dim == 2) ? 6 : 10;
278   std::size_t minNbPtBlob = 3 * sysDim;
279 
280   for(auto it = _adj.begin(); it != _adj.end(); it++) {
281     MVertex *ver = it->first;
282     std::vector<MVertex *> vv = getLSBlob(minNbPtBlob, it, _adj);
283     fullMatrix<double> A(vv.size(), sysDim), ATA(sysDim, sysDim);
284     fullVector<double> b(vv.size()), ATb(sysDim), coeffs(sysDim);
285     for(std::size_t i = 0; i < vv.size(); i++) {
286       const double &x = vv[i]->x(), &y = vv[i]->y(), &z = vv[i]->z();
287       if(_dim == 2) {
288         A(i, 0) = x * x;
289         A(i, 1) = x * y;
290         A(i, 2) = y * y;
291         A(i, 3) = x;
292         A(i, 4) = y;
293         A(i, 5) = 1.;
294       }
295       else {
296         A(i, 0) = x * x;
297         A(i, 1) = x * y;
298         A(i, 2) = x * z;
299         A(i, 3) = y * y;
300         A(i, 4) = y * z;
301         A(i, 5) = z * z;
302         A(i, 6) = x;
303         A(i, 7) = y;
304         A(i, 8) = z;
305         A(i, 9) = 1.;
306       }
307       b(i) = vals[vv[i]];
308     }
309     ATA.gemm(A, A, 1., 0., true, false);
310     A.multWithATranspose(b, 1., 0., ATb);
311     ATA.luSolve(ATb, coeffs);
312     const double &x = ver->x(), &y = ver->y(), &z = ver->z();
313     double d2udx2, d2udy2, d2udz2, d2udxy, d2udxz, d2udyz, dudx, dudy, dudz;
314     if(_dim == 2) {
315       d2udx2 = 2. * coeffs(0);
316       d2udy2 = 2. * coeffs(2);
317       d2udz2 = 0.;
318       d2udxy = coeffs(1);
319       d2udxz = 0.;
320       d2udyz = 0.;
321       dudx = d2udx2 * x + d2udxy * y + coeffs(3);
322       dudy = d2udxy * x + d2udy2 * y + coeffs(4);
323       dudz = 0.;
324     }
325     else {
326       d2udx2 = 2. * coeffs(0);
327       d2udy2 = 2. * coeffs(3);
328       d2udz2 = 2. * coeffs(5);
329       d2udxy = coeffs(1);
330       d2udxz = coeffs(2);
331       d2udyz = coeffs(4);
332       dudx = d2udx2 * x + d2udxy * y + d2udxz * z + coeffs(6);
333       dudy = d2udxy * x + d2udy2 * y + d2udyz * z + coeffs(7);
334       dudz = d2udxz * x + d2udyz * y + d2udz2 * z + coeffs(8);
335     }
336     double duNorm = sqrt(dudx * dudx + dudy * dudy + dudz * dudz);
337     if(duNorm == 0. || _technique == meshMetric::HESSIAN ||
338        _technique == meshMetric::EIGENDIRECTIONS ||
339        _technique == meshMetric::EIGENDIRECTIONS_LINEARINTERP_H)
340       duNorm = 1.;
341     grads[ver] = SVector3(dudx / duNorm, dudy / duNorm, dudz / duNorm);
342     hessians[ver](0, 0) = d2udx2;
343     hessians[ver](0, 1) = d2udxy;
344     hessians[ver](0, 2) = d2udxz;
345     hessians[ver](1, 0) = d2udxy;
346     hessians[ver](1, 1) = d2udy2;
347     hessians[ver](1, 2) = d2udyz;
348     hessians[ver](2, 0) = d2udxz;
349     hessians[ver](2, 1) = d2udyz;
350     hessians[ver](2, 2) = d2udz2;
351   }
352 }
353 
computeMetricLevelSet(MVertex * ver,SMetric3 & hessian,SMetric3 & metric,double & size,double x,double y,double z)354 void meshMetric::computeMetricLevelSet(MVertex *ver, SMetric3 &hessian,
355                                        SMetric3 &metric, double &size, double x,
356                                        double y, double z)
357 {
358   double signed_dist;
359   SVector3 gr;
360   if(ver) {
361     signed_dist = vals[ver];
362     gr = grads[ver];
363     hessian = hessians[ver];
364   }
365   else {
366     signed_dist = (*_fct)(x, y, z);
367     _fct->gradient(x, y, z, gr(0), gr(1), gr(2));
368     _fct->hessian(x, y, z, hessian(0, 0), hessian(0, 1), hessian(0, 2),
369                   hessian(1, 0), hessian(1, 1), hessian(1, 2), hessian(2, 0),
370                   hessian(2, 1), hessian(2, 2));
371   }
372 
373   double dist = fabs(signed_dist);
374 
375   SMetric3 H;
376   double norm = gr(0) * gr(0) + gr(1) * gr(1) + gr(2) * gr(2);
377   if(dist < _e && norm != 0.0) {
378     double h = hmin * (hmax / hmin - 1) * dist / _e + hmin;
379     double C = 1. / (h * h) - 1. / (hmax * hmax);
380     H(0, 0) += C * gr(0) * gr(0) / norm;
381     H(1, 1) += C * gr(1) * gr(1) / norm;
382     H(2, 2) += C * gr(2) * gr(2) / norm;
383     H(1, 0) = H(0, 1) = C * gr(1) * gr(0) / norm;
384     H(2, 0) = H(0, 2) = C * gr(2) * gr(0) / norm;
385     H(2, 1) = H(1, 2) = C * gr(2) * gr(1) / norm;
386   }
387 
388   fullMatrix<double> V(3, 3);
389   fullVector<double> S(3);
390   H.eig(V, S);
391 
392   double lambda1, lambda2, lambda3;
393   lambda1 = S(0);
394   lambda2 = S(1);
395   lambda3 = (_dim == 3) ? S(2) : 1.;
396 
397   SVector3 t1(V(0, 0), V(1, 0), V(2, 0));
398   SVector3 t2(V(0, 1), V(1, 1), V(2, 1));
399   SVector3 t3(V(0, 2), V(1, 2), V(2, 2));
400 
401   size =
402     std::min(std::min(1 / sqrt(lambda1), 1 / sqrt(lambda2)), 1 / sqrt(lambda3));
403   metric = SMetric3(lambda1, lambda2, lambda3, t1, t2, t3);
404 }
405 
computeMetricHessian(MVertex * ver,SMetric3 & hessian,SMetric3 & metric,double & size,double x,double y,double z)406 void meshMetric::computeMetricHessian(MVertex *ver, SMetric3 &hessian,
407                                       SMetric3 &metric, double &size, double x,
408                                       double y, double z)
409 {
410   SVector3 gr;
411   if(ver != nullptr) {
412     gr = grads[ver];
413     hessian = hessians[ver];
414   }
415   else if(ver == nullptr) {
416     _fct->gradient(x, y, z, gr(0), gr(1), gr(2));
417     _fct->hessian(x, y, z, hessian(0, 0), hessian(0, 1), hessian(0, 2),
418                   hessian(1, 0), hessian(1, 1), hessian(1, 2), hessian(2, 0),
419                   hessian(2, 1), hessian(2, 2));
420   }
421 
422   double _epsilonP = 1.;
423   double hminP = 1.e-12;
424   double hmaxP = 1.e+12;
425 
426   fullMatrix<double> V(3, 3);
427   fullVector<double> S(3);
428   hessian.eig(V, S);
429 
430   double lambda1 =
431     std::min(std::max(fabs(S(0)) / _epsilonP, 1. / (hmaxP * hmaxP)),
432              1. / (hminP * hminP));
433   double lambda2 =
434     std::min(std::max(fabs(S(1)) / _epsilonP, 1. / (hmaxP * hmaxP)),
435              1. / (hminP * hminP));
436   double lambda3 =
437     (_dim == 3) ?
438       std::min(std::max(fabs(S(2)) / _epsilonP, 1. / (hmaxP * hmaxP)),
439                1. / (hminP * hminP)) :
440       1.;
441 
442   SVector3 t1(V(0, 0), V(1, 0), V(2, 0));
443   SVector3 t2(V(0, 1), V(1, 1), V(2, 1));
444   SVector3 t3 =
445     (_dim == 3) ? SVector3(V(0, 2), V(1, 2), V(2, 2)) : SVector3(0., 0., 1.);
446 
447   size =
448     std::min(std::min(1 / sqrt(lambda1), 1 / sqrt(lambda2)), 1 / sqrt(lambda3));
449   metric = SMetric3(lambda1, lambda2, lambda3, t1, t2, t3);
450 }
451 
computeMetricFrey(MVertex * ver,SMetric3 & hessian,SMetric3 & metric,double & size,double x,double y,double z)452 void meshMetric::computeMetricFrey(MVertex *ver, SMetric3 &hessian,
453                                    SMetric3 &metric, double &size, double x,
454                                    double y, double z)
455 {
456   double signed_dist;
457   SVector3 gr;
458   if(ver) {
459     signed_dist = vals[ver];
460     gr = grads[ver];
461     hessian = hessians[ver];
462   }
463   else {
464     signed_dist = (*_fct)(x, y, z);
465     _fct->gradient(x, y, z, gr(0), gr(1), gr(2));
466     _fct->hessian(x, y, z, hessian(0, 0), hessian(0, 1), hessian(0, 2),
467                   hessian(1, 0), hessian(1, 1), hessian(1, 2), hessian(2, 0),
468                   hessian(2, 1), hessian(2, 2));
469   }
470 
471   double dist = fabs(signed_dist);
472 
473   SMetric3 H(1. / (hmax * hmax));
474   double norm = gr(0) * gr(0) + gr(1) * gr(1) + gr(2) * gr(2);
475   if(dist < _e && norm != 0.0) {
476     double h = hmin * (hmax / hmin - 1.0) * dist / _e + hmin;
477     double C = 1. / (h * h) - 1. / (hmax * hmax);
478     double kappa = hessian(0, 0) + hessian(1, 1) + hessian(2, 2);
479     double epsGeom = 4.0 * 3.14 * 3.14 / (kappa * _np * _np);
480     H(0, 0) += C * gr(0) * gr(0) / (norm) + hessian(0, 0) / epsGeom;
481     H(1, 1) += C * gr(1) * gr(1) / (norm) + hessian(1, 1) / epsGeom;
482     H(2, 2) += C * gr(2) * gr(2) / (norm) + hessian(2, 2) / epsGeom;
483     H(1, 0) = H(0, 1) = C * gr(1) * gr(0) / (norm) + hessian(1, 0) / epsGeom;
484     H(2, 0) = H(0, 2) = C * gr(2) * gr(0) / (norm) + hessian(2, 0) / epsGeom;
485     H(2, 1) = H(1, 2) = C * gr(2) * gr(1) / (norm) + hessian(2, 1) / epsGeom;
486   }
487 
488   fullMatrix<double> V(3, 3);
489   fullVector<double> S(3);
490   H.eig(V, S);
491 
492   double lambda1, lambda2, lambda3;
493   lambda1 = S(0);
494   lambda2 = S(1);
495   lambda3 = (_dim == 3) ? S(2) : 1.;
496 
497   if(dist < _e) {
498     lambda1 = std::min(std::max(fabs(S(0)) / _epsilon, 1. / (hmax * hmax)),
499                        1. / (hmin * hmin));
500     lambda2 = std::min(std::max(fabs(S(1)) / _epsilon, 1. / (hmax * hmax)),
501                        1. / (hmin * hmin));
502     lambda3 = (_dim == 3) ?
503                 std::min(std::max(fabs(S(2)) / _epsilon, 1. / (hmax * hmax)),
504                          1. / (hmin * hmin)) :
505                 1.;
506   }
507 
508   SVector3 t1(V(0, 0), V(1, 0), V(2, 0));
509   SVector3 t2(V(0, 1), V(1, 1), V(2, 1));
510   SVector3 t3(V(0, 2), V(1, 2), V(2, 2));
511 
512   size =
513     std::min(std::min(1 / sqrt(lambda1), 1 / sqrt(lambda2)), 1 / sqrt(lambda3));
514   metric = SMetric3(lambda1, lambda2, lambda3, t1, t2, t3);
515 }
516 
computeMetricEigenDir(MVertex * ver,SMetric3 & hessian,SMetric3 & metric,double & size,double x,double y,double z)517 void meshMetric::computeMetricEigenDir(MVertex *ver, SMetric3 &hessian,
518                                        SMetric3 &metric, double &size, double x,
519                                        double y, double z)
520 {
521   double signed_dist;
522   SVector3 gVec;
523   if(ver) {
524     signed_dist = vals[ver];
525     gVec = grads[ver];
526     hessian = hessians[ver];
527   }
528   else {
529     signed_dist = (*_fct)(x, y, z);
530     _fct->gradient(x, y, z, gVec(0), gVec(1), gVec(2));
531     _fct->hessian(x, y, z, hessian(0, 0), hessian(0, 1), hessian(0, 2),
532                   hessian(1, 0), hessian(1, 1), hessian(1, 2), hessian(2, 0),
533                   hessian(2, 1), hessian(2, 2));
534   }
535 
536   double dist = fabs(signed_dist);
537 
538   const double metric_value_hmax = 1. / (hmax * hmax);
539   const double gMag = gVec.norm(), invGMag = 1. / gMag;
540 
541   if(signed_dist < _e && signed_dist > _e_moins && gMag != 0.0) {
542     const double metric_value_hmin = 1. / (hmin * hmin);
543     const SVector3 nVec = invGMag * gVec; // Unit normal vector
544     double lambda_n =
545       0.; // Eigenvalues of metric for normal & tangential directions
546     if(_technique == meshMetric::EIGENDIRECTIONS_LINEARINTERP_H) {
547       // const double h_dist = hmin + ((hmax-hmin)/_e)*dist;
548       // // Characteristic element size in the normal direction - linear interp
549       // between hmin and hmax  lambda_n = 1./(h_dist*h_dist);
550       double beta = CTX::instance()->mesh.smoothRatio;
551       double h_dista = std::min((hmin + (dist * log(beta))), hmax);
552       lambda_n = 1. / (h_dista * h_dista);
553     }
554     else if(_technique == meshMetric::EIGENDIRECTIONS) {
555       const double maximum_distance =
556         (signed_dist > 0.) ? _e : fabs(_e_moins); // ... or linear interpolation
557                                                   // between 1/h_min^2 and
558                                                   // 1/h_max^2
559       lambda_n =
560         metric_value_hmin +
561         ((metric_value_hmax - metric_value_hmin) / maximum_distance) * dist;
562     }
563     std::vector<SVector3> tVec; // Unit tangential vectors
564     std::vector<double> kappa; // Curvatures
565     if(_dim == 2) { // 2D curvature formula: cf. R. Goldman, "Curvature formulas
566                     // for implicit curves and surfaces", Computer Aided
567                     // Geometric Design 22 (2005), pp. 632–658
568       kappa.resize(2);
569       kappa[0] =
570         fabs(-gVec(1) * (-gVec(1) * hessian(0, 0) + gVec(0) * hessian(0, 1)) +
571              gVec(0) * (-gVec(1) * hessian(1, 0) + gVec(0) * hessian(1, 1))) *
572         pow(invGMag, 3);
573       kappa[1] = 1.;
574       tVec.resize(2);
575       tVec[0] = SVector3(-nVec(1), nVec(0), 0.);
576       tVec[1] = SVector3(0., 0., 1.);
577     }
578     else { // 3D curvature formula: cf. A.G. Belyaev, A.A. Pasko and T.L. Kunii,
579            // "Ridges and Ravines on Implicit Surfaces," CGI, pp.530-535,
580            // Computer Graphics International 1998 (CGI'98), 1998
581       fullMatrix<double> ImGG(3, 3);
582       ImGG(0, 0) = 1. - gVec(0) * gVec(0);
583       ImGG(0, 1) = -gVec(0) * gVec(1);
584       ImGG(0, 2) = -gVec(0) * gVec(2);
585       ImGG(1, 0) = -gVec(1) * gVec(0);
586       ImGG(1, 1) = 1. - gVec(1) * gVec(1);
587       ImGG(1, 2) = -gVec(1) * gVec(2);
588       ImGG(2, 0) = -gVec(2) * gVec(0);
589       ImGG(2, 1) = -gVec(2) * gVec(1);
590       ImGG(2, 2) = 1. - gVec(2) * gVec(2);
591       fullMatrix<double> hess(3, 3);
592       hessian.getMat(hess);
593       fullMatrix<double> gN(3, 3); // Gradient of unit normal
594       gN.gemm(ImGG, hess, 1., 0.);
595       gN.scale(invGMag);
596       fullMatrix<double> eigVecL(3, 3), eigVecR(3, 3);
597       fullVector<double> eigValRe(3), eigValIm(3);
598       gN.eig(eigValRe, eigValIm, eigVecL, eigVecR,
599              false); // Eigendecomp. of gradient of unit normal
600       kappa.resize(3); // Store abs. val. of eigenvalues (= principal curvatures
601                        // only in non-normal directions)
602       kappa[0] = fabs(eigValRe(0));
603       kappa[1] = fabs(eigValRe(1));
604       kappa[2] = fabs(eigValRe(2));
605       tVec.resize(3); // Store normalized eigenvectors (= principal directions
606                       // only in non-normal directions)
607       tVec[0] = SVector3(eigVecR(0, 0), eigVecR(1, 0), eigVecR(2, 0));
608       tVec[0].normalize();
609       tVec[1] = SVector3(eigVecR(0, 1), eigVecR(1, 1), eigVecR(2, 1));
610       tVec[1].normalize();
611       tVec[2] = SVector3(eigVecR(0, 2), eigVecR(1, 2), eigVecR(2, 2));
612       tVec[2].normalize();
613       std::vector<double> tVecDotNVec(3); // Store dot products with normal
614                                           // vector to look for normal direction
615       tVecDotNVec[0] = fabs(dot(tVec[0], nVec));
616       tVecDotNVec[1] = fabs(dot(tVec[1], nVec));
617       tVecDotNVec[2] = fabs(dot(tVec[2], nVec));
618       const int i_N = max_element(tVecDotNVec.begin(), tVecDotNVec.end()) -
619                       tVecDotNVec.begin(); // Index of normal dir. = max. dot
620                                            // products (close to 0. in
621                                            // tangential dir.)
622       kappa.erase(kappa.begin() + i_N); // Remove normal dir.
623       tVec.erase(tVec.begin() + i_N);
624     }
625     const double invh_t0 = (_np * kappa[0]) / 6.283185307,
626                  invh_t1 = (_np * kappa[1]) /
627                            6.283185307; // Inverse of tangential size 0
628     const double lambda_t0 = invh_t0 * invh_t0, lambda_t1 = invh_t1 * invh_t1;
629     const double lambdaP_n =
630       std::min(std::max(lambda_n, metric_value_hmax),
631                metric_value_hmin); // Truncate eigenvalues
632     const double lambdaP_t0 =
633       std::min(std::max(lambda_t0, metric_value_hmax), metric_value_hmin);
634     const double lambdaP_t1 =
635       (_dim == 2) ?
636         1. :
637         std::min(std::max(lambda_t1, metric_value_hmax), metric_value_hmin);
638     metric =
639       SMetric3(lambdaP_n, lambdaP_t0, lambdaP_t1, nVec, tVec[0], tVec[1]);
640     const double h_n = 1. / sqrt(lambdaP_n), h_t0 = 1. / sqrt(lambdaP_t0),
641                  h_t1 = 1. / sqrt(lambdaP_t1);
642     size = std::min(std::min(h_n, h_t0), h_t1);
643   }
644   else { // isotropic metric !
645     SMetric3 mymetric(metric_value_hmax);
646     metric = mymetric;
647     size = hmax;
648   }
649 }
650 
computeMetricIsoLinInterp(MVertex * ver,SMetric3 & hessian,SMetric3 & metric,double & size,double x,double y,double z)651 void meshMetric::computeMetricIsoLinInterp(MVertex *ver, SMetric3 &hessian,
652                                            SMetric3 &metric, double &size,
653                                            double x, double y, double z)
654 {
655   double signed_dist;
656   SVector3 gr;
657   if(ver) {
658     signed_dist = vals[ver];
659     gr = grads[ver];
660     hessian = hessians[ver];
661   }
662   else {
663     signed_dist = (*_fct)(x, y, z);
664     _fct->gradient(x, y, z, gr(0), gr(1), gr(2));
665     _fct->hessian(x, y, z, hessian(0, 0), hessian(0, 1), hessian(0, 2),
666                   hessian(1, 0), hessian(1, 1), hessian(1, 2), hessian(2, 0),
667                   hessian(2, 1), hessian(2, 2));
668   }
669 
670   double dist = fabs(signed_dist);
671   double norm = gr.normalize();
672   size = hmax; // the characteristic element size in all directions - linear
673                // interp between hmin and hmax
674   if(norm != 0.) {
675     if((signed_dist >= 0) && (signed_dist < _e))
676       size = hmin + ((hmax - hmin) / _e) * dist;
677     else if((signed_dist < 0) && (signed_dist > _e_moins))
678       size = hmin - ((hmax - hmin) / _e_moins) * dist;
679   }
680 
681   double lambda = 1. / size / size;
682   metric(0, 0) = lambda;
683   metric(0, 1) = 0.;
684   metric(0, 2) = 0.;
685   metric(1, 0) = 0.;
686   metric(1, 1) = lambda;
687   metric(1, 2) = 0.;
688   metric(2, 0) = 0.;
689   metric(2, 1) = 0.;
690   metric(2, 2) = (_dim == 3) ? lambda : 1.;
691 }
692 
693 // this function scales the mesh metric in order
694 // to reach a target number of elements
695 // We know that the number of elements in the final
696 // mesh will be (assuming M_e the metric at centroid of element e)
697 //   N = \sum_e \sqrt {\det (M_e)} V_e
698 // where V_e is the volume of e
699 // assuming that N_{target} = K N, we have
700 //   K N = K \sum_e \sqrt {\det (M_e)} V_e
701 //       =   \sum_e \sqrt {K^2 \det (M_e)} V_e
702 //       =   \sum_e \sqrt {\det (K^{2/d} M_e)} V_e
703 //  where d is the dimension of the problem.
704 // This means that the metric should be scaled by K^{2/d} where
705 // K is N_target / N
scaleMetric(int nbElementsTarget,nodalMetricTensor & nmt)706 void meshMetric::scaleMetric(int nbElementsTarget, nodalMetricTensor &nmt)
707 {
708   // compute N
709   double N = 0;
710   for(std::size_t i = 0; i < _elements.size(); i++) {
711     MElement *e = _elements[i];
712     SMetric3 m1 = nmt[e->getVertex(0)];
713     SMetric3 m2 = nmt[e->getVertex(1)];
714     SMetric3 m3 = nmt[e->getVertex(2)];
715     if(_dim == 2) {
716       SMetric3 m = interpolation(m1, m2, m3, 0.3333, 0.3333);
717       N += sqrt(m.determinant()) * e->getVolume() * 4. / sqrt(3.0); // 3.0
718     }
719     else {
720       SMetric3 m4 = nmt[e->getVertex(3)];
721       SMetric3 m = interpolation(m1, m2, m3, m4, 0.25, 0.25, 0.25);
722       N += sqrt(m.determinant()) * e->getVolume() * 12. / sqrt(2.0); // 4.0;
723     }
724   }
725   double scale = pow((double)nbElementsTarget / N, 2.0 / _dim);
726   for(auto it = nmt.begin(); it != nmt.end(); ++it) {
727     if(_dim == 3) { it->second *= scale; }
728     else {
729       it->second(0, 0) *= scale;
730       it->second(1, 0) *= scale;
731       it->second(1, 1) *= scale;
732     }
733     SMetric3 &m = it->second;
734     fullMatrix<double> V(3, 3);
735     fullVector<double> S(3);
736     m.eig(V, S);
737     S(0) = std::min(std::max(S(0), 1 / (hmax * hmax)), 1 / (hmin * hmin));
738     S(1) = std::min(std::max(S(1), 1 / (hmax * hmax)), 1 / (hmin * hmin));
739     if(_dim == 3)
740       S(2) = std::min(std::max(S(2), 1 / (hmax * hmax)), 1 / (hmin * hmin));
741     SVector3 t1(V(0, 0), V(1, 0), V(2, 0));
742     SVector3 t2(V(0, 1), V(1, 1), V(2, 1));
743     SVector3 t3(V(0, 2), V(1, 2), V(2, 2));
744     m = SMetric3(S(0), S(1), S(2), t1, t2, t3);
745   }
746 }
747 
computeMetric(int metricNumber)748 void meshMetric::computeMetric(int metricNumber)
749 {
750   _fct = setOfFcts[metricNumber];
751   std::vector<double> parameters = setOfParameters[metricNumber];
752   int technique = setOfTechniques[metricNumber];
753 
754   hmin = parameters.size() >= 3 ? parameters[1] : CTX::instance()->mesh.lcMin;
755   hmax = parameters.size() >= 3 ? parameters[2] : CTX::instance()->mesh.lcMax;
756   _e = parameters[0];
757   _e_moins = (parameters.size() >= 5) ? parameters[4] : -parameters[0];
758   if(_e_moins > 0.) _e_moins *= -1.;
759   _epsilon = parameters[0];
760   _technique = (MetricComputationTechnique)technique;
761   _np = (parameters.size() >= 4) ? parameters[3] : 15.;
762 
763   computeValues();
764   computeHessian();
765 
766   for(auto it = _adj.begin(); it != _adj.end(); it++) {
767     MVertex *ver = it->first;
768     SMetric3 hessian, metric;
769     double size;
770     switch(_technique) {
771     case(LEVELSET): computeMetricLevelSet(ver, hessian, metric, size); break;
772     case(HESSIAN): computeMetricHessian(ver, hessian, metric, size); break;
773     case(FREY): computeMetricFrey(ver, hessian, metric, size); break;
774     case(EIGENDIRECTIONS):
775       computeMetricEigenDir(ver, hessian, metric, size);
776       break;
777     case(EIGENDIRECTIONS_LINEARINTERP_H):
778       computeMetricEigenDir(ver, hessian, metric, size);
779       break;
780     case(ISOTROPIC_LINEARINTERP_H):
781       computeMetricIsoLinInterp(ver, hessian, metric, size);
782       break;
783     }
784 
785     setOfSizes[metricNumber].insert(std::make_pair(ver, size));
786     setOfMetrics[metricNumber].insert(std::make_pair(ver, metric));
787   }
788 
789   if(_technique == HESSIAN) scaleMetric(_epsilon, setOfMetrics[metricNumber]);
790 }
791 
operator ()(double x,double y,double z,GEntity * ge)792 double meshMetric::operator()(double x, double y, double z, GEntity *ge)
793 {
794   if(needMetricUpdate) updateMetrics();
795   if(!setOfMetrics.size()) {
796     Msg::Error("No metric defined");
797     return 0.;
798   }
799   SPoint3 xyz(x, y, z), uvw;
800   double initialTol =  CTX::instance()->mesh.toleranceReferenceElement;
801   CTX::instance()->mesh.toleranceReferenceElement = 1.e-4;
802   MElement *e = _octree->find(x, y, z, _dim);
803   CTX::instance()->mesh.toleranceReferenceElement = initialTol;
804   double value = 0.;
805   if(e) {
806     e->xyz2uvw(xyz, uvw);
807     double *val = new double[e->getNumVertices()];
808     for(std::size_t i = 0; i < e->getNumVertices(); i++) {
809       val[i] = _nodalSizes[e->getVertex(i)];
810     }
811     value = e->interpolate(val, uvw[0], uvw[1], uvw[2]);
812     delete[] val;
813   }
814   else {
815     Msg::Warning("point %g %g %g not found, looking for nearest node", x, y, z);
816     double minDist = 1.e100;
817     for(auto it = _nodalSizes.begin(); it != _nodalSizes.end(); it++) {
818       const double dist = xyz.distance(it->first->point());
819       if(dist <= minDist) {
820         minDist = dist;
821         value = it->second;
822       }
823     }
824   }
825   return value;
826 }
827 
operator ()(double x,double y,double z,SMetric3 & metr,GEntity * ge)828 void meshMetric::operator()(double x, double y, double z, SMetric3 &metr,
829                             GEntity *ge)
830 {
831   if(needMetricUpdate) { updateMetrics(); }
832   if(!setOfMetrics.size()) {
833     Msg::Error("No metric defined");
834     return;
835   }
836   metr = SMetric3(1.e-22);
837 
838   // RECOMPUTE MESH METRIC AT XYZ
839   if(hasAnalyticalMetric) {
840     int nbMetrics = setOfMetrics.size();
841     std::vector<SMetric3> newSetOfMetrics(nbMetrics);
842     for(int iMetric = 0; iMetric < nbMetrics; iMetric++) {
843       _fct = setOfFcts[iMetric];
844       _technique = (MetricComputationTechnique)setOfTechniques[iMetric];
845       if(_fct->hasDerivatives()) {
846         SMetric3 hessian, metric;
847         double size;
848         switch(_technique) {
849         case(LEVELSET):
850           computeMetricLevelSet(nullptr, hessian, metric, size, x, y, z);
851           break;
852         case(HESSIAN):
853           computeMetricHessian(nullptr, hessian, metric, size, x, y, z);
854           break;
855         case(FREY):
856           computeMetricFrey(nullptr, hessian, metric, size, x, y, z);
857           break;
858         case(EIGENDIRECTIONS):
859           computeMetricEigenDir(nullptr, hessian, metric, size, x, y, z);
860           break;
861         case(EIGENDIRECTIONS_LINEARINTERP_H):
862           computeMetricEigenDir(nullptr, hessian, metric, size, x, y, z);
863           break;
864         case(ISOTROPIC_LINEARINTERP_H):
865           computeMetricIsoLinInterp(nullptr, hessian, metric, size, x, y, z);
866           break;
867         }
868         newSetOfMetrics[iMetric] = metric;
869       }
870       else {
871         // find other metrics here
872         SMetric3 metric;
873         SPoint3 xyz(x, y, z), uvw;
874         double initialTol =  CTX::instance()->mesh.toleranceReferenceElement;
875         CTX::instance()->mesh.toleranceReferenceElement = 1.e-4;
876         MElement *e = _octree->find(x, y, z, _dim);
877         CTX::instance()->mesh.toleranceReferenceElement = initialTol;
878         if(e) {
879           e->xyz2uvw(xyz, uvw);
880           SMetric3 m1 = setOfMetrics[iMetric][e->getVertex(0)];
881           SMetric3 m2 = setOfMetrics[iMetric][e->getVertex(1)];
882           SMetric3 m3 = setOfMetrics[iMetric][e->getVertex(2)];
883           if(_dim == 2)
884             metric = interpolation(m1, m2, m3, uvw[0], uvw[1]);
885           else {
886             SMetric3 m4 = setOfMetrics[iMetric][e->getVertex(3)];
887             metric = interpolation(m1, m2, m3, m4, uvw[0], uvw[1], uvw[2]);
888           }
889           newSetOfMetrics[iMetric] = metric;
890         }
891         else {
892           Msg::Warning("point %g %g %g not found, looking for nearest node", x,
893                        y, z);
894         }
895       }
896     }
897     // intersect metrics here
898     metr = newSetOfMetrics[0];
899     for(int i = 1; i < nbMetrics; i++)
900       metr = intersection_conserve_mostaniso(metr, newSetOfMetrics[i]);
901   }
902   // INTERPOLATE DISCRETE MESH METRIC
903   else {
904     SPoint3 xyz(x, y, z), uvw;
905     double initialTol =  CTX::instance()->mesh.toleranceReferenceElement;
906     CTX::instance()->mesh.toleranceReferenceElement = 1.e-4;
907     MElement *e = _octree->find(x, y, z, _dim);
908     CTX::instance()->mesh.toleranceReferenceElement = initialTol;
909 
910     if(e) {
911       e->xyz2uvw(xyz, uvw);
912       SMetric3 m1 = _nodalMetrics[e->getVertex(0)];
913       SMetric3 m2 = _nodalMetrics[e->getVertex(1)];
914       SMetric3 m3 = _nodalMetrics[e->getVertex(2)];
915       if(_dim == 2)
916         metr = interpolation(m1, m2, m3, uvw[0], uvw[1]);
917       else {
918         SMetric3 m4 = _nodalMetrics[e->getVertex(3)];
919         metr = interpolation(m1, m2, m3, m4, uvw[0], uvw[1], uvw[2]);
920       }
921     }
922     else {
923       Msg::Warning("point %g %g %g not found, looking for nearest node", x, y,
924                    z);
925       double minDist = 1.e100;
926       for(auto it = _nodalMetrics.begin(); it != _nodalMetrics.end(); it++) {
927         const double dist = xyz.distance(it->first->point());
928         if(dist <= minDist) {
929           minDist = dist;
930           metr = it->second;
931         }
932       }
933     }
934   }
935 }
936 
getLaplacian(MVertex * v)937 double meshMetric::getLaplacian(MVertex *v)
938 {
939   MVertex *vNew = _vertexMap[v->getNum()];
940   auto it = hessians.find(vNew);
941   SMetric3 h = it->second;
942   return h(0, 0) + h(1, 1) + h(2, 2);
943 }
944 
getGradient(MVertex * v)945 SVector3 meshMetric::getGradient(MVertex *v)
946 {
947   MVertex *vNew = _vertexMap[v->getNum()];
948   auto it = grads.find(vNew);
949   SVector3 gr = it->second;
950   return gr;
951 }
952