Skip to content
Snippets Groups Projects
Select Git revision
  • bd8d36f42970695d549fb819e74487904c76b6ca
  • master default
  • cgnsUnstructured
  • partitioning
  • poppler
  • HighOrderBLCurving
  • gmsh_3_0_4
  • gmsh_3_0_3
  • gmsh_3_0_2
  • gmsh_3_0_1
  • gmsh_3_0_0
  • gmsh_2_16_0
  • gmsh_2_15_0
  • gmsh_2_14_1
  • gmsh_2_14_0
  • gmsh_2_13_2
  • gmsh_2_13_1
  • gmsh_2_12_0
  • gmsh_2_11_0
  • gmsh_2_10_1
  • gmsh_2_10_0
  • gmsh_2_9_3
  • gmsh_2_9_2
  • gmsh_2_9_1
  • gmsh_2_9_0
  • gmsh_2_8_6
26 results

GamePad.cpp

Blame
  • Forked from gmsh / gmsh
    Source project has a limited visibility.
    meshMetric.cpp 32.77 KiB
    // Gmsh - Copyright (C) 1997-2019 C. Geuzaine, J.-F. Remacle
    //
    // See the LICENSE.txt file for license information. Please report all
    // issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
    
    #include "meshMetric.h"
    #include "meshGFaceOptimize.h"
    #include "Context.h"
    #include "GEntity.h"
    #include "GModel.h"
    #include "gmshLevelset.h"
    #include "MElementOctree.h"
    #include "OS.h"
    #include <algorithm>
    
    meshMetric::meshMetric(GModel *gm)
    {
      hasAnalyticalMetric = false;
      _dim = gm->getDim();
      std::map<MElement *, MElement *> newP;
      std::map<MElement *, MElement *> newD;
    
      if(_dim == 2) {
        for(GModel::fiter fit = gm->firstFace(); fit != gm->lastFace(); ++fit) {
          for(std::size_t i = 0; i < (*fit)->getNumMeshElements(); i++) {
            MElement *e = (*fit)->getMeshElement(i);
            MElement *copy = e->copy(_vertexMap, newP, newD);
            _elements.push_back(copy);
          }
        }
      }
      else if(_dim == 3) {
        for(GModel::riter rit = gm->firstRegion(); rit != gm->lastRegion(); ++rit) {
          for(std::size_t i = 0; i < (*rit)->getNumMeshElements(); i++) {
            MElement *e = (*rit)->getMeshElement(i);
            MElement *copy = e->copy(_vertexMap, newP, newD);
            _elements.push_back(copy);
          }
        }
      }
      _octree = new MElementOctree(_elements);
      buildVertexToElement(_elements, _adj);
    }
    
    meshMetric::meshMetric(std::vector<MElement *> elements)
    {
      hasAnalyticalMetric = false;
    
      _dim = elements[0]->getDim();
      std::map<MElement *, MElement *> newP;
      std::map<MElement *, MElement *> newD;
    
      for(std::size_t i = 0; i < elements.size(); i++) {
        MElement *e = elements[i];
        MElement *copy = e->copy(_vertexMap, newP, newD);
        _elements.push_back(copy);
      }
    
      _octree = new MElementOctree(_elements);
      buildVertexToElement(_elements, _adj);
    }
    
    void meshMetric::addMetric(int technique, simpleFunction<double> *fct,
                               const std::vector<double> &parameters)
    {
      needMetricUpdate = true;
    
      int metricNumber = setOfMetrics.size();
      setOfFcts[metricNumber] = fct;
      setOfParameters[metricNumber] = parameters;
      setOfTechniques[metricNumber] = technique;
    
      if(fct->hasDerivatives()) hasAnalyticalMetric = true;
    
      computeMetric(metricNumber);
    }
    
    void meshMetric::updateMetrics()
    {
      if(!setOfMetrics.size()) {
        Msg::Error("Can't intersect metrics, no metric registered");
        return;
      }
    
      v2t_cont ::iterator it = _adj.begin();
      for(; it != _adj.end(); it++) {
        MVertex *ver = it->first;
        _nodalMetrics[ver] = setOfMetrics[0][ver];
        _nodalSizes[ver] = setOfSizes[0][ver];
    
        if(setOfMetrics.size() > 1)
          for(std::size_t i = 1; i < setOfMetrics.size(); i++) {
            _nodalMetrics[ver] =
              (_dim == 3) ? intersection_conserve_mostaniso(_nodalMetrics[ver],
                                                            setOfMetrics[i][ver]) :
                            intersection_conserve_mostaniso_2d(
                              _nodalMetrics[ver], setOfMetrics[i][ver]);
            _nodalSizes[ver] = std::min(_nodalSizes[ver], setOfSizes[i][ver]);
          }
      }
      needMetricUpdate = false;
    }
    
    void meshMetric::exportInfo(const char *fileendname)
    {
      if(needMetricUpdate) updateMetrics();
      std::stringstream sg, sm, sl, sh, shm;
      sg << "meshmetric_gradients_" << fileendname;
      sm << "meshmetric_metric_" << fileendname;
      sl << "meshmetric_levelset_" << fileendname;
      sh << "meshmetric_hessian_" << fileendname;
      shm << "meshmetric_hessianmat_" << fileendname;
      std::ofstream out_grad(sg.str().c_str());
      std::ofstream out_metric(sm.str().c_str());
      std::ofstream out_ls(sl.str().c_str());
      std::ofstream out_hess(sh.str().c_str());
      std::ofstream out_hessmat(shm.str().c_str());
      out_grad << "View \"ls_gradient\"{" << std::endl;
      out_metric << "View \"metric\"{" << std::endl;
      out_ls << "View \"ls\"{" << std::endl;
      out_hess << "View \"hessian\"{" << std::endl;
      out_hessmat << "View \"hessian_mat\"{" << std::endl;
      std::vector<MElement *>::iterator itelem = _elements.begin();
      std::vector<MElement *>::iterator itelemen = _elements.end();
      for(; itelem != itelemen; itelem++) {
        MElement *e = *itelem;
        if(e->getDim() == 2) {
          out_metric << "TT(";
          out_grad << "VT(";
          out_ls << "ST(";
          out_hess << "ST(";
          out_hessmat << "TT(";
        }
        else {
          out_metric << "TS(";
          out_grad << "VS(";
          out_ls << "SS(";
          out_hess << "SS(";
          out_hessmat << "TS(";
        }
        for(std::size_t i = 0; i < e->getNumVertices(); i++) {
          MVertex *ver = e->getVertex(i);
          out_metric << ver->x() << "," << ver->y() << "," << ver->z();
          out_grad << ver->x() << "," << ver->y() << "," << ver->z();
          out_ls << ver->x() << "," << ver->y() << "," << ver->z();
          out_hess << ver->x() << "," << ver->y() << "," << ver->z();
          out_hessmat << ver->x() << "," << ver->y() << "," << ver->z();
          if(i != e->getNumVertices() - 1) {
            out_metric << ",";
            out_grad << ",";
            out_ls << ",";
            out_hess << ",";
            out_hessmat << ",";
          }
          else {
            out_metric << "){";
            out_grad << "){";
            out_ls << "){";
            out_hess << "){";
            out_hessmat << "){";
          }
        }
        for(std::size_t i = 0; i < e->getNumVertices(); i++) {
          MVertex *ver = e->getVertex(i);
          out_ls << vals[ver];
          out_hess << (hessians[ver](0, 0) + hessians[ver](1, 1) +
                       hessians[ver](2, 2));
          if(i == (e->getNumVertices() - 1)) {
            out_ls << "};" << std::endl;
            out_hess << "};" << std::endl;
          }
          else {
            out_ls << ",";
            out_hess << ",";
          }
          for(int k = 0; k < 3; k++) {
            out_grad << grads[ver](k);
            if((k == 2) && (i == (e->getNumVertices() - 1)))
              out_grad << "};" << std::endl;
            else
              out_grad << ",";
            for(int l = 0; l < 3; l++) {
              out_metric << _nodalMetrics[ver](k, l);
              out_hessmat << hessians[ver](k, l);
              if((k == 2) && (l == 2) && (i == (e->getNumVertices() - 1))) {
                out_metric << "};" << std::endl;
                out_hessmat << "};" << std::endl;
              }
              else {
                out_metric << ",";
                out_hessmat << ",";
              }
            }
          }
        }
      }
      out_grad << "};" << std::endl;
      out_metric << "};" << std::endl;
      out_ls << "};" << std::endl;
      out_hess << "};" << std::endl;
      out_hessmat << "};" << std::endl;
      out_grad.close();
      out_metric.close();
      out_ls.close();
      out_hess.close();
      out_hessmat.close();
    }
    
    meshMetric::~meshMetric()
    {
      if(_octree) delete _octree;
      for(std::size_t i = 0; i < _elements.size(); i++) delete _elements[i];
    }
    
    void meshMetric::computeValues()
    {
      v2t_cont ::iterator it = _adj.begin();
      while(it != _adj.end()) {
        std::vector<MElement *> lt = it->second;
        MVertex *ver = it->first;
        vals[ver] = (*_fct)(ver->x(), ver->y(), ver->z());
        it++;
      }
    }
    
    // Determines set of vertices to use for least squares
    std::vector<MVertex *> getLSBlob(std::size_t minNbPt, v2t_cont::iterator it,
                                     v2t_cont &adj)
    {
      //  static const double RADFACT = 3;
      //
      //  SPoint3 p0 = it->first->point();  // Vertex coordinates (center of circle)
      //
      //  double rad = 0.;
      //  for (std::vector<MElement*>::iterator itEl = it->second.begin(); itEl !=
      //  it->second.end(); itEl++)
      //    rad = std::max(rad,(*itEl)->getOuterRadius());
      //  rad *= RADFACT;
    
      std::vector<MVertex *> vv(1, it->first),
        bvv = vv; // Vector of vertices in blob and in boundary of blob
      do {
        std::set<MVertex *> nbvv; // Set of vertices in new boundary
        for(std::vector<MVertex *>::iterator itBV = bvv.begin(); itBV != bvv.end();
            itBV++) { // For each boundary vertex...
          std::vector<MElement *> &adjBV = adj[*itBV];
          for(std::vector<MElement *>::iterator itBVEl = adjBV.begin();
              itBVEl != adjBV.end(); itBVEl++) {
            for(std::size_t iV = 0; iV < (*itBVEl)->getNumVertices();
                iV++) { // ... look for adjacent vertices...
              MVertex *v = (*itBVEl)->getVertex(iV);
              //          if ((find(vv.begin(),vv.end(),v) == vv.end()) &&
              //          (p0.distance(v->point()) <= rad)) nbvv.insert(v);
              if(find(vv.begin(), vv.end(), v) == vv.end())
                nbvv.insert(v); // ... and add them in the new boundary if they are
                                // not already in the blob
            }
          }
        }
        if(nbvv.empty())
          bvv.clear();
        else {
          bvv.assign(nbvv.begin(), nbvv.end());
          vv.insert(vv.end(), nbvv.begin(), nbvv.end());
        }
        //  } while (!bvv.empty());
      } while(vv.size() < minNbPt); // Repeat until min. number of points is reached
    
      return vv;
    }
    
    // Compute derivatives and second order derivatives using least squares
    // 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
    // 3D LS system:
    // 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
    void meshMetric::computeHessian()
    {
      std::size_t sysDim = (_dim == 2) ? 6 : 10;
      std::size_t minNbPtBlob = 3 * sysDim;
    
      for(v2t_cont::iterator it = _adj.begin(); it != _adj.end(); it++) {
        MVertex *ver = it->first;
        std::vector<MVertex *> vv = getLSBlob(minNbPtBlob, it, _adj);
        fullMatrix<double> A(vv.size(), sysDim), ATA(sysDim, sysDim);
        fullVector<double> b(vv.size()), ATb(sysDim), coeffs(sysDim);
        for(std::size_t i = 0; i < vv.size(); i++) {
          const double &x = vv[i]->x(), &y = vv[i]->y(), &z = vv[i]->z();
          if(_dim == 2) {
            A(i, 0) = x * x;
            A(i, 1) = x * y;
            A(i, 2) = y * y;
            A(i, 3) = x;
            A(i, 4) = y;
            A(i, 5) = 1.;
          }
          else {
            A(i, 0) = x * x;
            A(i, 1) = x * y;
            A(i, 2) = x * z;
            A(i, 3) = y * y;
            A(i, 4) = y * z;
            A(i, 5) = z * z;
            A(i, 6) = x;
            A(i, 7) = y;
            A(i, 8) = z;
            A(i, 9) = 1.;
          }
          b(i) = vals[vv[i]];
        }
        ATA.gemm(A, A, 1., 0., true, false);
        A.multWithATranspose(b, 1., 0., ATb);
        ATA.luSolve(ATb, coeffs);
        const double &x = ver->x(), &y = ver->y(), &z = ver->z();
        double d2udx2, d2udy2, d2udz2, d2udxy, d2udxz, d2udyz, dudx, dudy, dudz;
        if(_dim == 2) {
          d2udx2 = 2. * coeffs(0);
          d2udy2 = 2. * coeffs(2);
          d2udz2 = 0.;
          d2udxy = coeffs(1);
          d2udxz = 0.;
          d2udyz = 0.;
          dudx = d2udx2 * x + d2udxy * y + coeffs(3);
          dudy = d2udxy * x + d2udy2 * y + coeffs(4);
          dudz = 0.;
        }
        else {
          d2udx2 = 2. * coeffs(0);
          d2udy2 = 2. * coeffs(3);
          d2udz2 = 2. * coeffs(5);
          d2udxy = coeffs(1);
          d2udxz = coeffs(2);
          d2udyz = coeffs(4);
          dudx = d2udx2 * x + d2udxy * y + d2udxz * z + coeffs(6);
          dudy = d2udxy * x + d2udy2 * y + d2udyz * z + coeffs(7);
          dudz = d2udxz * x + d2udyz * y + d2udz2 * z + coeffs(8);
        }
        double duNorm = sqrt(dudx * dudx + dudy * dudy + dudz * dudz);
        if(duNorm == 0. || _technique == meshMetric::HESSIAN ||
           _technique == meshMetric::EIGENDIRECTIONS ||
           _technique == meshMetric::EIGENDIRECTIONS_LINEARINTERP_H)
          duNorm = 1.;
        grads[ver] = SVector3(dudx / duNorm, dudy / duNorm, dudz / duNorm);
        hessians[ver](0, 0) = d2udx2;
        hessians[ver](0, 1) = d2udxy;
        hessians[ver](0, 2) = d2udxz;
        hessians[ver](1, 0) = d2udxy;
        hessians[ver](1, 1) = d2udy2;
        hessians[ver](1, 2) = d2udyz;
        hessians[ver](2, 0) = d2udxz;
        hessians[ver](2, 1) = d2udyz;
        hessians[ver](2, 2) = d2udz2;
      }
    }
    
    void meshMetric::computeMetricLevelSet(MVertex *ver, SMetric3 &hessian,
                                           SMetric3 &metric, double &size, double x,
                                           double y, double z)
    {
      double signed_dist;
      SVector3 gr;
      if(ver) {
        signed_dist = vals[ver];
        gr = grads[ver];
        hessian = hessians[ver];
      }
      else {
        signed_dist = (*_fct)(x, y, z);
        _fct->gradient(x, y, z, gr(0), gr(1), gr(2));
        _fct->hessian(x, y, z, hessian(0, 0), hessian(0, 1), hessian(0, 2),
                      hessian(1, 0), hessian(1, 1), hessian(1, 2), hessian(2, 0),
                      hessian(2, 1), hessian(2, 2));
      }
    
      double dist = fabs(signed_dist);
    
      SMetric3 H;
      double norm = gr(0) * gr(0) + gr(1) * gr(1) + gr(2) * gr(2);
      if(dist < _e && norm != 0.0) {
        double h = hmin * (hmax / hmin - 1) * dist / _e + hmin;
        double C = 1. / (h * h) - 1. / (hmax * hmax);
        H(0, 0) += C * gr(0) * gr(0) / norm;
        H(1, 1) += C * gr(1) * gr(1) / norm;
        H(2, 2) += C * gr(2) * gr(2) / norm;
        H(1, 0) = H(0, 1) = C * gr(1) * gr(0) / norm;
        H(2, 0) = H(0, 2) = C * gr(2) * gr(0) / norm;
        H(2, 1) = H(1, 2) = C * gr(2) * gr(1) / norm;
      }
    
      fullMatrix<double> V(3, 3);
      fullVector<double> S(3);
      H.eig(V, S);
    
      double lambda1, lambda2, lambda3;
      lambda1 = S(0);
      lambda2 = S(1);
      lambda3 = (_dim == 3) ? S(2) : 1.;
    
      SVector3 t1(V(0, 0), V(1, 0), V(2, 0));
      SVector3 t2(V(0, 1), V(1, 1), V(2, 1));
      SVector3 t3(V(0, 2), V(1, 2), V(2, 2));
    
      size =
        std::min(std::min(1 / sqrt(lambda1), 1 / sqrt(lambda2)), 1 / sqrt(lambda3));
      metric = SMetric3(lambda1, lambda2, lambda3, t1, t2, t3);
    }
    
    void meshMetric::computeMetricHessian(MVertex *ver, SMetric3 &hessian,
                                          SMetric3 &metric, double &size, double x,
                                          double y, double z)
    {
      SVector3 gr;
      if(ver != NULL) {
        gr = grads[ver];
        hessian = hessians[ver];
      }
      else if(ver == NULL) {
        _fct->gradient(x, y, z, gr(0), gr(1), gr(2));
        _fct->hessian(x, y, z, hessian(0, 0), hessian(0, 1), hessian(0, 2),
                      hessian(1, 0), hessian(1, 1), hessian(1, 2), hessian(2, 0),
                      hessian(2, 1), hessian(2, 2));
      }
    
      double _epsilonP = 1.;
      double hminP = 1.e-12;
      double hmaxP = 1.e+12;
    
      fullMatrix<double> V(3, 3);
      fullVector<double> S(3);
      hessian.eig(V, S);
    
      double lambda1 =
        std::min(std::max(fabs(S(0)) / _epsilonP, 1. / (hmaxP * hmaxP)),
                 1. / (hminP * hminP));
      double lambda2 =
        std::min(std::max(fabs(S(1)) / _epsilonP, 1. / (hmaxP * hmaxP)),
                 1. / (hminP * hminP));
      double lambda3 =
        (_dim == 3) ?
          std::min(std::max(fabs(S(2)) / _epsilonP, 1. / (hmaxP * hmaxP)),
                   1. / (hminP * hminP)) :
          1.;
    
      SVector3 t1(V(0, 0), V(1, 0), V(2, 0));
      SVector3 t2(V(0, 1), V(1, 1), V(2, 1));
      SVector3 t3 =
        (_dim == 3) ? SVector3(V(0, 2), V(1, 2), V(2, 2)) : SVector3(0., 0., 1.);
    
      size =
        std::min(std::min(1 / sqrt(lambda1), 1 / sqrt(lambda2)), 1 / sqrt(lambda3));
      metric = SMetric3(lambda1, lambda2, lambda3, t1, t2, t3);
    }
    
    void meshMetric::computeMetricFrey(MVertex *ver, SMetric3 &hessian,
                                       SMetric3 &metric, double &size, double x,
                                       double y, double z)
    {
      double signed_dist;
      SVector3 gr;
      if(ver) {
        signed_dist = vals[ver];
        gr = grads[ver];
        hessian = hessians[ver];
      }
      else {
        signed_dist = (*_fct)(x, y, z);
        _fct->gradient(x, y, z, gr(0), gr(1), gr(2));
        _fct->hessian(x, y, z, hessian(0, 0), hessian(0, 1), hessian(0, 2),
                      hessian(1, 0), hessian(1, 1), hessian(1, 2), hessian(2, 0),
                      hessian(2, 1), hessian(2, 2));
      }
    
      double dist = fabs(signed_dist);
    
      SMetric3 H(1. / (hmax * hmax));
      double norm = gr(0) * gr(0) + gr(1) * gr(1) + gr(2) * gr(2);
      if(dist < _e && norm != 0.0) {
        double h = hmin * (hmax / hmin - 1.0) * dist / _e + hmin;
        double C = 1. / (h * h) - 1. / (hmax * hmax);
        double kappa = hessian(0, 0) + hessian(1, 1) + hessian(2, 2);
        double epsGeom = 4.0 * 3.14 * 3.14 / (kappa * _np * _np);
        H(0, 0) += C * gr(0) * gr(0) / (norm) + hessian(0, 0) / epsGeom;
        H(1, 1) += C * gr(1) * gr(1) / (norm) + hessian(1, 1) / epsGeom;
        H(2, 2) += C * gr(2) * gr(2) / (norm) + hessian(2, 2) / epsGeom;
        H(1, 0) = H(0, 1) = C * gr(1) * gr(0) / (norm) + hessian(1, 0) / epsGeom;
        H(2, 0) = H(0, 2) = C * gr(2) * gr(0) / (norm) + hessian(2, 0) / epsGeom;
        H(2, 1) = H(1, 2) = C * gr(2) * gr(1) / (norm) + hessian(2, 1) / epsGeom;
      }
    
      fullMatrix<double> V(3, 3);
      fullVector<double> S(3);
      H.eig(V, S);
    
      double lambda1, lambda2, lambda3;
      lambda1 = S(0);
      lambda2 = S(1);
      lambda3 = (_dim == 3) ? S(2) : 1.;
    
      if(dist < _e) {
        lambda1 = std::min(std::max(fabs(S(0)) / _epsilon, 1. / (hmax * hmax)),
                           1. / (hmin * hmin));
        lambda2 = std::min(std::max(fabs(S(1)) / _epsilon, 1. / (hmax * hmax)),
                           1. / (hmin * hmin));
        lambda3 = (_dim == 3) ?
                    std::min(std::max(fabs(S(2)) / _epsilon, 1. / (hmax * hmax)),
                             1. / (hmin * hmin)) :
                    1.;
      }
    
      SVector3 t1(V(0, 0), V(1, 0), V(2, 0));
      SVector3 t2(V(0, 1), V(1, 1), V(2, 1));
      SVector3 t3(V(0, 2), V(1, 2), V(2, 2));
    
      size =
        std::min(std::min(1 / sqrt(lambda1), 1 / sqrt(lambda2)), 1 / sqrt(lambda3));
      metric = SMetric3(lambda1, lambda2, lambda3, t1, t2, t3);
    }
    
    void meshMetric::computeMetricEigenDir(MVertex *ver, SMetric3 &hessian,
                                           SMetric3 &metric, double &size, double x,
                                           double y, double z)
    {
      double signed_dist;
      SVector3 gVec;
      if(ver) {
        signed_dist = vals[ver];
        gVec = grads[ver];
        hessian = hessians[ver];
      }
      else {
        signed_dist = (*_fct)(x, y, z);
        _fct->gradient(x, y, z, gVec(0), gVec(1), gVec(2));
        _fct->hessian(x, y, z, hessian(0, 0), hessian(0, 1), hessian(0, 2),
                      hessian(1, 0), hessian(1, 1), hessian(1, 2), hessian(2, 0),
                      hessian(2, 1), hessian(2, 2));
      }
    
      double dist = fabs(signed_dist);
    
      const double metric_value_hmax = 1. / (hmax * hmax);
      const double gMag = gVec.norm(), invGMag = 1. / gMag;
    
      if(signed_dist < _e && signed_dist > _e_moins && gMag != 0.0) {
        const double metric_value_hmin = 1. / (hmin * hmin);
        const SVector3 nVec = invGMag * gVec; // Unit normal vector
        double lambda_n =
          0.; // Eigenvalues of metric for normal & tangential directions
        if(_technique == meshMetric::EIGENDIRECTIONS_LINEARINTERP_H) {
          // const double h_dist = hmin + ((hmax-hmin)/_e)*dist;
          // // Characteristic element size in the normal direction - linear interp
          // between hmin and hmax  lambda_n = 1./(h_dist*h_dist);
          double beta = CTX::instance()->mesh.smoothRatio;
          double h_dista = std::min((hmin + (dist * log(beta))), hmax);
          lambda_n = 1. / (h_dista * h_dista);
        }
        else if(_technique == meshMetric::EIGENDIRECTIONS) {
          const double maximum_distance =
            (signed_dist > 0.) ? _e : fabs(_e_moins); // ... or linear interpolation
                                                      // between 1/h_min^2 and
                                                      // 1/h_max^2
          lambda_n =
            metric_value_hmin +
            ((metric_value_hmax - metric_value_hmin) / maximum_distance) * dist;
        }
        std::vector<SVector3> tVec; // Unit tangential vectors
        std::vector<double> kappa; // Curvatures
        if(_dim == 2) { // 2D curvature formula: cf. R. Goldman, "Curvature formulas
                        // for implicit curves and surfaces", Computer Aided
                        // Geometric Design 22 (2005), pp. 632–658
          kappa.resize(2);
          kappa[0] =
            fabs(-gVec(1) * (-gVec(1) * hessian(0, 0) + gVec(0) * hessian(0, 1)) +
                 gVec(0) * (-gVec(1) * hessian(1, 0) + gVec(0) * hessian(1, 1))) *
            pow(invGMag, 3);
          kappa[1] = 1.;
          tVec.resize(2);
          tVec[0] = SVector3(-nVec(1), nVec(0), 0.);
          tVec[1] = SVector3(0., 0., 1.);
        }
        else { // 3D curvature formula: cf. A.G. Belyaev, A.A. Pasko and T.L. Kunii,
               // "Ridges and Ravines on Implicit Surfaces," CGI, pp.530-535,
               // Computer Graphics International 1998 (CGI'98), 1998
          fullMatrix<double> ImGG(3, 3);
          ImGG(0, 0) = 1. - gVec(0) * gVec(0);
          ImGG(0, 1) = -gVec(0) * gVec(1);
          ImGG(0, 2) = -gVec(0) * gVec(2);
          ImGG(1, 0) = -gVec(1) * gVec(0);
          ImGG(1, 1) = 1. - gVec(1) * gVec(1);
          ImGG(1, 2) = -gVec(1) * gVec(2);
          ImGG(2, 0) = -gVec(2) * gVec(0);
          ImGG(2, 1) = -gVec(2) * gVec(1);
          ImGG(2, 2) = 1. - gVec(2) * gVec(2);
          fullMatrix<double> hess(3, 3);
          hessian.getMat(hess);
          fullMatrix<double> gN(3, 3); // Gradient of unit normal
          gN.gemm(ImGG, hess, 1., 0.);
          gN.scale(invGMag);
          fullMatrix<double> eigVecL(3, 3), eigVecR(3, 3);
          fullVector<double> eigValRe(3), eigValIm(3);
          gN.eig(eigValRe, eigValIm, eigVecL, eigVecR,
                 false); // Eigendecomp. of gradient of unit normal
          kappa.resize(3); // Store abs. val. of eigenvalues (= principal curvatures
                           // only in non-normal directions)
          kappa[0] = fabs(eigValRe(0));
          kappa[1] = fabs(eigValRe(1));
          kappa[2] = fabs(eigValRe(2));
          tVec.resize(3); // Store normalized eigenvectors (= principal directions
                          // only in non-normal directions)
          tVec[0] = SVector3(eigVecR(0, 0), eigVecR(1, 0), eigVecR(2, 0));
          tVec[0].normalize();
          tVec[1] = SVector3(eigVecR(0, 1), eigVecR(1, 1), eigVecR(2, 1));
          tVec[1].normalize();
          tVec[2] = SVector3(eigVecR(0, 2), eigVecR(1, 2), eigVecR(2, 2));
          tVec[2].normalize();
          std::vector<double> tVecDotNVec(3); // Store dot products with normal
                                              // vector to look for normal direction
          tVecDotNVec[0] = fabs(dot(tVec[0], nVec));
          tVecDotNVec[1] = fabs(dot(tVec[1], nVec));
          tVecDotNVec[2] = fabs(dot(tVec[2], nVec));
          const int i_N = max_element(tVecDotNVec.begin(), tVecDotNVec.end()) -
                          tVecDotNVec.begin(); // Index of normal dir. = max. dot
                                               // products (close to 0. in
                                               // tangential dir.)
          kappa.erase(kappa.begin() + i_N); // Remove normal dir.
          tVec.erase(tVec.begin() + i_N);
        }
        const double invh_t0 = (_np * kappa[0]) / 6.283185307,
                     invh_t1 = (_np * kappa[1]) /
                               6.283185307; // Inverse of tangential size 0
        const double lambda_t0 = invh_t0 * invh_t0, lambda_t1 = invh_t1 * invh_t1;
        const double lambdaP_n =
          std::min(std::max(lambda_n, metric_value_hmax),
                   metric_value_hmin); // Truncate eigenvalues
        const double lambdaP_t0 =
          std::min(std::max(lambda_t0, metric_value_hmax), metric_value_hmin);
        const double lambdaP_t1 =
          (_dim == 2) ?
            1. :
            std::min(std::max(lambda_t1, metric_value_hmax), metric_value_hmin);
        metric =
          SMetric3(lambdaP_n, lambdaP_t0, lambdaP_t1, nVec, tVec[0], tVec[1]);
        const double h_n = 1. / sqrt(lambdaP_n), h_t0 = 1. / sqrt(lambdaP_t0),
                     h_t1 = 1. / sqrt(lambdaP_t1);
        size = std::min(std::min(h_n, h_t0), h_t1);
      }
      else { // isotropic metric !
        SMetric3 mymetric(metric_value_hmax);
        metric = mymetric;
        size = hmax;
      }
    }
    
    void meshMetric::computeMetricIsoLinInterp(MVertex *ver, SMetric3 &hessian,
                                               SMetric3 &metric, double &size,
                                               double x, double y, double z)
    {
      double signed_dist;
      SVector3 gr;
      if(ver) {
        signed_dist = vals[ver];
        gr = grads[ver];
        hessian = hessians[ver];
      }
      else {
        signed_dist = (*_fct)(x, y, z);
        _fct->gradient(x, y, z, gr(0), gr(1), gr(2));
        _fct->hessian(x, y, z, hessian(0, 0), hessian(0, 1), hessian(0, 2),
                      hessian(1, 0), hessian(1, 1), hessian(1, 2), hessian(2, 0),
                      hessian(2, 1), hessian(2, 2));
      }
    
      double dist = fabs(signed_dist);
      double norm = gr.normalize();
      size = hmax; // the characteristic element size in all directions - linear
                   // interp between hmin and hmax
      if(norm != 0.) {
        if((signed_dist >= 0) && (signed_dist < _e))
          size = hmin + ((hmax - hmin) / _e) * dist;
        else if((signed_dist < 0) && (signed_dist > _e_moins))
          size = hmin - ((hmax - hmin) / _e_moins) * dist;
      }
    
      double lambda = 1. / size / size;
      metric(0, 0) = lambda;
      metric(0, 1) = 0.;
      metric(0, 2) = 0.;
      metric(1, 0) = 0.;
      metric(1, 1) = lambda;
      metric(1, 2) = 0.;
      metric(2, 0) = 0.;
      metric(2, 1) = 0.;
      metric(2, 2) = (_dim == 3) ? lambda : 1.;
    }
    
    // this function scales the mesh metric in order
    // to reach a target number of elements
    // We know that the number of elements in the final
    // mesh will be (assuming M_e the metric at centroid of element e)
    //   N = \sum_e \sqrt {\det (M_e)} V_e
    // where V_e is the volume of e
    // assuming that N_{target} = K N, we have
    //   K N = K \sum_e \sqrt {\det (M_e)} V_e
    //       =   \sum_e \sqrt {K^2 \det (M_e)} V_e
    //       =   \sum_e \sqrt {\det (K^{2/d} M_e)} V_e
    //  where d is the dimension of the problem.
    // This means that the metric should be scaled by K^{2/d} where
    // K is N_target / N
    void meshMetric::scaleMetric(int nbElementsTarget, nodalMetricTensor &nmt)
    {
      // compute N
      double N = 0;
      for(std::size_t i = 0; i < _elements.size(); i++) {
        MElement *e = _elements[i];
        SMetric3 m1 = nmt[e->getVertex(0)];
        SMetric3 m2 = nmt[e->getVertex(1)];
        SMetric3 m3 = nmt[e->getVertex(2)];
        if(_dim == 2) {
          SMetric3 m = interpolation(m1, m2, m3, 0.3333, 0.3333);
          N += sqrt(m.determinant()) * e->getVolume() * 4. / sqrt(3.0); // 3.0
        }
        else {
          SMetric3 m4 = nmt[e->getVertex(3)];
          SMetric3 m = interpolation(m1, m2, m3, m4, 0.25, 0.25, 0.25);
          N += sqrt(m.determinant()) * e->getVolume() * 12. / sqrt(2.0); // 4.0;
        }
      }
      double scale = pow((double)nbElementsTarget / N, 2.0 / _dim);
      for(nodalMetricTensor::iterator it = nmt.begin(); it != nmt.end(); ++it) {
        if(_dim == 3) {
          it->second *= scale;
        }
        else {
          it->second(0, 0) *= scale;
          it->second(1, 0) *= scale;
          it->second(1, 1) *= scale;
        }
        SMetric3 &m = it->second;
        fullMatrix<double> V(3, 3);
        fullVector<double> S(3);
        m.eig(V, S);
        S(0) = std::min(std::max(S(0), 1 / (hmax * hmax)), 1 / (hmin * hmin));
        S(1) = std::min(std::max(S(1), 1 / (hmax * hmax)), 1 / (hmin * hmin));
        if(_dim == 3)
          S(2) = std::min(std::max(S(2), 1 / (hmax * hmax)), 1 / (hmin * hmin));
        SVector3 t1(V(0, 0), V(1, 0), V(2, 0));
        SVector3 t2(V(0, 1), V(1, 1), V(2, 1));
        SVector3 t3(V(0, 2), V(1, 2), V(2, 2));
        m = SMetric3(S(0), S(1), S(2), t1, t2, t3);
      }
    }
    
    void meshMetric::computeMetric(int metricNumber)
    {
      _fct = setOfFcts[metricNumber];
      std::vector<double> parameters = setOfParameters[metricNumber];
      int technique = setOfTechniques[metricNumber];
    
      hmin = parameters.size() >= 3 ? parameters[1] : CTX::instance()->mesh.lcMin;
      hmax = parameters.size() >= 3 ? parameters[2] : CTX::instance()->mesh.lcMax;
      _e = parameters[0];
      _e_moins = (parameters.size() >= 5) ? parameters[4] : -parameters[0];
      if(_e_moins > 0.) _e_moins *= -1.;
      _epsilon = parameters[0];
      _technique = (MetricComputationTechnique)technique;
      _np = (parameters.size() >= 4) ? parameters[3] : 15.;
    
      computeValues();
      computeHessian();
    
      for(v2t_cont::iterator it = _adj.begin(); it != _adj.end(); it++) {
        MVertex *ver = it->first;
        SMetric3 hessian, metric;
        double size;
        switch(_technique) {
        case(LEVELSET): computeMetricLevelSet(ver, hessian, metric, size); break;
        case(HESSIAN): computeMetricHessian(ver, hessian, metric, size); break;
        case(FREY): computeMetricFrey(ver, hessian, metric, size); break;
        case(EIGENDIRECTIONS):
          computeMetricEigenDir(ver, hessian, metric, size);
          break;
        case(EIGENDIRECTIONS_LINEARINTERP_H):
          computeMetricEigenDir(ver, hessian, metric, size);
          break;
        case(ISOTROPIC_LINEARINTERP_H):
          computeMetricIsoLinInterp(ver, hessian, metric, size);
          break;
        }
    
        setOfSizes[metricNumber].insert(std::make_pair(ver, size));
        setOfMetrics[metricNumber].insert(std::make_pair(ver, metric));
      }
    
      if(_technique == HESSIAN) scaleMetric(_epsilon, setOfMetrics[metricNumber]);
    }
    
    double meshMetric::operator()(double x, double y, double z, GEntity *ge)
    {
      if(needMetricUpdate) updateMetrics();
      if(!setOfMetrics.size()) {
        std::cout << "meshMetric::operator() : No metric defined ! " << std::endl;
        throw;
      }
      SPoint3 xyz(x, y, z), uvw;
      double initialTol = MElement::getTolerance();
      MElement::setTolerance(1.e-4);
      MElement *e = _octree->find(x, y, z, _dim);
      MElement::setTolerance(initialTol);
      double value = 0.;
      if(e) {
        e->xyz2uvw(xyz, uvw);
        double *val = new double[e->getNumVertices()];
        for(std::size_t i = 0; i < e->getNumVertices(); i++) {
          val[i] = _nodalSizes[e->getVertex(i)];
        }
        value = e->interpolate(val, uvw[0], uvw[1], uvw[2]);
        delete[] val;
      }
      else {
        Msg::Warning("point %g %g %g not found, looking for nearest node", x, y, z);
        double minDist = 1.e100;
        for(nodalField::iterator it = _nodalSizes.begin(); it != _nodalSizes.end();
            it++) {
          const double dist = xyz.distance(it->first->point());
          if(dist <= minDist) {
            minDist = dist;
            value = it->second;
          }
        }
      }
      return value;
    }
    
    void meshMetric::operator()(double x, double y, double z, SMetric3 &metr,
                                GEntity *ge)
    {
      if(needMetricUpdate) {
        updateMetrics();
      }
      if(!setOfMetrics.size()) {
        std::cout << "meshMetric::operator() : No metric defined ! " << std::endl;
        throw;
      }
      metr = SMetric3(1.e-22);
    
      // RECOMPUTE MESH METRIC AT XYZ
      if(hasAnalyticalMetric) {
        int nbMetrics = setOfMetrics.size();
        std::vector<SMetric3> newSetOfMetrics(nbMetrics);
        for(int iMetric = 0; iMetric < nbMetrics; iMetric++) {
          _fct = setOfFcts[iMetric];
          _technique = (MetricComputationTechnique)setOfTechniques[iMetric];
          if(_fct->hasDerivatives()) {
            SMetric3 hessian, metric;
            double size;
            switch(_technique) {
            case(LEVELSET):
              computeMetricLevelSet(NULL, hessian, metric, size, x, y, z);
              break;
            case(HESSIAN):
              computeMetricHessian(NULL, hessian, metric, size, x, y, z);
              break;
            case(FREY):
              computeMetricFrey(NULL, hessian, metric, size, x, y, z);
              break;
            case(EIGENDIRECTIONS):
              computeMetricEigenDir(NULL, hessian, metric, size, x, y, z);
              break;
            case(EIGENDIRECTIONS_LINEARINTERP_H):
              computeMetricEigenDir(NULL, hessian, metric, size, x, y, z);
              break;
            case(ISOTROPIC_LINEARINTERP_H):
              computeMetricIsoLinInterp(NULL, hessian, metric, size, x, y, z);
              break;
            }
            newSetOfMetrics[iMetric] = metric;
          }
          else {
            // find other metrics here
            SMetric3 metric;
            SPoint3 xyz(x, y, z), uvw;
            double initialTol = MElement::getTolerance();
            MElement::setTolerance(1.e-4);
            MElement *e = _octree->find(x, y, z, _dim);
            MElement::setTolerance(initialTol);
            if(e) {
              e->xyz2uvw(xyz, uvw);
              SMetric3 m1 = setOfMetrics[iMetric][e->getVertex(0)];
              SMetric3 m2 = setOfMetrics[iMetric][e->getVertex(1)];
              SMetric3 m3 = setOfMetrics[iMetric][e->getVertex(2)];
              if(_dim == 2)
                metric = interpolation(m1, m2, m3, uvw[0], uvw[1]);
              else {
                SMetric3 m4 = setOfMetrics[iMetric][e->getVertex(3)];
                metric = interpolation(m1, m2, m3, m4, uvw[0], uvw[1], uvw[2]);
              }
              newSetOfMetrics[iMetric] = metric;
            }
            else {
              Msg::Warning("point %g %g %g not found, looking for nearest node", x,
                           y, z);
            }
          }
        }
        // intersect metrics here
        metr = newSetOfMetrics[0];
        for(int i = 1; i < nbMetrics; i++)
          metr = intersection_conserve_mostaniso(metr, newSetOfMetrics[i]);
      }
      // INTERPOLATE DISCRETE MESH METRIC
      else {
        SPoint3 xyz(x, y, z), uvw;
        double initialTol = MElement::getTolerance();
        MElement::setTolerance(1.e-4);
        MElement *e = _octree->find(x, y, z, _dim);
        MElement::setTolerance(initialTol);
    
        if(e) {
          e->xyz2uvw(xyz, uvw);
          SMetric3 m1 = _nodalMetrics[e->getVertex(0)];
          SMetric3 m2 = _nodalMetrics[e->getVertex(1)];
          SMetric3 m3 = _nodalMetrics[e->getVertex(2)];
          if(_dim == 2)
            metr = interpolation(m1, m2, m3, uvw[0], uvw[1]);
          else {
            SMetric3 m4 = _nodalMetrics[e->getVertex(3)];
            metr = interpolation(m1, m2, m3, m4, uvw[0], uvw[1], uvw[2]);
          }
        }
        else {
          Msg::Warning("point %g %g %g not found, looking for nearest node", x, y,
                       z);
          double minDist = 1.e100;
          for(nodalMetricTensor::iterator it = _nodalMetrics.begin();
              it != _nodalMetrics.end(); it++) {
            const double dist = xyz.distance(it->first->point());
            if(dist <= minDist) {
              minDist = dist;
              metr = it->second;
            }
          }
        }
      }
    }
    
    double meshMetric::getLaplacian(MVertex *v)
    {
      MVertex *vNew = _vertexMap[v->getNum()];
      std::map<MVertex *, SMetric3>::const_iterator it = hessians.find(vNew);
      SMetric3 h = it->second;
      return h(0, 0) + h(1, 1) + h(2, 2);
    }
    
    SVector3 meshMetric::getGradient(MVertex *v)
    {
      MVertex *vNew = _vertexMap[v->getNum()];
      std::map<MVertex *, SVector3>::const_iterator it = grads.find(vNew);
      SVector3 gr = it->second;
      return gr;
    }