From acb56d9865c6f34f25288cbf8abd793764c00332 Mon Sep 17 00:00:00 2001
From: Thomas Toulorge <thomas.toulorge@mines-paristech.fr>
Date: Thu, 3 Oct 2013 23:22:37 +0000
Subject: [PATCH] Added capability to fix invalid p1 surface meshes to OptHOM.
 Changed 1-by-1 optimization strategy (to be cleaned up).

---
 Fltk/highOrderToolsWindow.cpp                 |   1 +
 Geo/MElement.cpp                              |  23 ++-
 Geo/MElement.h                                |   2 +-
 contrib/HighOrderMeshOptimizer/OptHOM.cpp     |  13 +-
 contrib/HighOrderMeshOptimizer/OptHOM.h       |   3 +-
 contrib/HighOrderMeshOptimizer/OptHomMesh.cpp |  44 ++++-
 contrib/HighOrderMeshOptimizer/OptHomMesh.h   |   6 +-
 contrib/HighOrderMeshOptimizer/OptHomRun.cpp  | 169 +++++++++++-------
 contrib/HighOrderMeshOptimizer/OptHomRun.h    |   3 +-
 9 files changed, 182 insertions(+), 82 deletions(-)

diff --git a/Fltk/highOrderToolsWindow.cpp b/Fltk/highOrderToolsWindow.cpp
index 43c6093393..63b4feb541 100644
--- a/Fltk/highOrderToolsWindow.cpp
+++ b/Fltk/highOrderToolsWindow.cpp
@@ -151,6 +151,7 @@ static void highordertools_runopti_cb(Fl_Widget *w, void *data)
     p.maxAdaptBlob = o->value[9]->value();
     p.adaptBlobLayerFact = (int) o->value[10]->value();
     p.adaptBlobDistFact = o->value[11]->value();
+    p.optPrimSurfMesh = false;
     HighOrderMeshOptimizer(GModel::current(), p);
     break;
   }
diff --git a/Geo/MElement.cpp b/Geo/MElement.cpp
index 2194c5a07c..17c2385f5a 100644
--- a/Geo/MElement.cpp
+++ b/Geo/MElement.cpp
@@ -112,7 +112,7 @@ double MElement::rhoShapeMeasure()
     return 0.;
 }
 
-void MElement::scaledJacRange(double &jmin, double &jmax)
+void MElement::scaledJacRange(double &jmin, double &jmax, GEntity *ge)
 {
   jmin = jmax = 1.0;
 #if defined(HAVE_MESH)
@@ -122,6 +122,27 @@ void MElement::scaledJacRange(double &jmin, double &jmax)
   getNodesCoord(nodesXYZ);
   fullVector<double> SJi(numJacNodes), Bi(numJacNodes);
   jac->getScaledJacobian(nodesXYZ,SJi);
+  if (ge && (ge->dim() == 2) && ge->haveParametrization()) {  // If parametrized surface entity provided...
+    SVector3 geoNorm(0.,0.,0.);                               // ... correct Jacobian sign with geometrical normal
+    for (int i=0; i<jac->getNumPrimMapNodes(); i++) {
+      MVertex *vert = getVertex(i);
+      GEntity *vge = vert->onWhat();
+      if (vert->onWhat() == ge) {
+        double u, v;
+        vert->getParameter(0,u);
+        vert->getParameter(1,v);
+        geoNorm += ((GFace*)ge)->normal(SPoint2(u,v));
+      }
+    }
+    if (geoNorm.normSq() == 0.) {   // If no vertex on surface or average is zero, take normal at barycenter
+      SPoint2 param = ((GFace*)ge)->parFromPoint(barycenter(true),false);
+      geoNorm = ((GFace*)ge)->normal(param);
+    }
+    fullMatrix<double> elNorm(1,3);
+    jac->getPrimNormal2D(nodesXYZ,elNorm);
+    const double scal = geoNorm(0)*elNorm(0,0)+geoNorm(1)*elNorm(0,1)+geoNorm(2)*elNorm(0,2);
+    if (scal < 0.) SJi.scale(-1.);
+  }
   jac->lag2Bez(SJi,Bi);
   jmin = *std::min_element(Bi.getDataPtr(),Bi.getDataPtr()+Bi.size());
   jmax = *std::max_element(Bi.getDataPtr(),Bi.getDataPtr()+Bi.size());
diff --git a/Geo/MElement.h b/Geo/MElement.h
index 8955f835f4..5e50155ca2 100644
--- a/Geo/MElement.h
+++ b/Geo/MElement.h
@@ -188,7 +188,7 @@ class MElement
     return jmin;
   }
   virtual double angleShapeMeasure() { return 1.0; }
-  virtual void scaledJacRange(double &jmin, double &jmax);
+  virtual void scaledJacRange(double &jmin, double &jmax, GEntity *ge = 0);
 
   // get the radius of the inscribed circle/sphere if it exists,
   // otherwise get the minimum radius of all the circles/spheres
diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.cpp b/contrib/HighOrderMeshOptimizer/OptHOM.cpp
index 45db5d249c..c7fb2a88a3 100644
--- a/contrib/HighOrderMeshOptimizer/OptHOM.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHOM.cpp
@@ -67,9 +67,10 @@ static inline double compute_f1(double v, double barrier)
   // else return powP*pow(v-1.,powP-1.);
 }
 
-OptHOM::OptHOM(const std::set<MElement*> &els, std::set<MVertex*> & toFix,
+OptHOM::OptHOM(const std::map<MElement*,GEntity*> &element2entity,
+               const std::set<MElement*> &els, std::set<MVertex*> & toFix,
                bool fixBndNodes, bool fastJacEval) :
-  mesh(els, toFix, fixBndNodes, fastJacEval)
+  mesh(element2entity, els, toFix, fixBndNodes, fastJacEval)
 {
   _optimizeMetricMin = false;
 }
@@ -318,7 +319,13 @@ int OptHOM::optimize(double weightFixed, double weightFree, double b_min,
   std::vector<double> dSq(mesh.nVert());
   mesh.distSqToStraight(dSq);
   const double maxDSq = *max_element(dSq.begin(),dSq.end());
-  invLengthScaleSq = 1./maxDSq;  // Length scale for non-dimensional distance
+  if (maxDSq < 1.e-10) {                                        // Length scale for non-dim. distance
+    std::vector<double> sSq(mesh.nEl());
+    mesh.elSizeSq(sSq);
+    const double maxSSq = *max_element(sSq.begin(),sSq.end());
+    invLengthScaleSq = 1./maxSSq;
+  }
+  else invLengthScaleSq = 1./maxDSq;
 
   // Set initial guess
   alglib::real_1d_array x;
diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.h b/contrib/HighOrderMeshOptimizer/OptHOM.h
index debbc6a856..098d829132 100644
--- a/contrib/HighOrderMeshOptimizer/OptHOM.h
+++ b/contrib/HighOrderMeshOptimizer/OptHOM.h
@@ -43,7 +43,8 @@ class OptHOM
 {
 public:
   Mesh mesh;
-  OptHOM(const std::set<MElement*> &els, std::set<MVertex*> & toFix,
+  OptHOM(const std::map<MElement*,GEntity*> &element2entity,
+         const std::set<MElement*> &els, std::set<MVertex*> & toFix,
          bool fixBndNodes, bool fastJacEval = false);
   // returns 1 if the mesh has been optimized with success i.e. all jacobians
   // are in the range; returns 0 if the mesh is valid (all jacobians positive,
diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
index 727d645a4b..4b19087dde 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
@@ -35,7 +35,8 @@
 #include "ParamCoord.h"
 #include "OptHomMesh.h"
 
-Mesh::Mesh(const std::set<MElement*> &els, std::set<MVertex*> &toFix,
+Mesh::Mesh(const std::map<MElement*,GEntity*> &element2entity,
+           const std::set<MElement*> &els, std::set<MVertex*> &toFix,
            bool fixBndNodes, bool fastJacEval) :
   _fastJacEval(fastJacEval)
 {
@@ -105,7 +106,7 @@ Mesh::Mesh(const std::set<MElement*> &els, std::set<MVertex*> &toFix,
   // Jacobians of 3D elements
   if (_dim == 2) {
     _scaledNormEl.resize(nEl());
-    for (int iEl = 0; iEl < nEl(); iEl++) calcScaledNormalEl2D(iEl);
+    for (int iEl = 0; iEl < nEl(); iEl++) calcScaledNormalEl2D(element2entity,iEl);
   }
   else {
     _invStraightJac.resize(nEl(),1.);
@@ -116,24 +117,41 @@ Mesh::Mesh(const std::set<MElement*> &els, std::set<MVertex*> &toFix,
 
 }
 
-void Mesh::calcScaledNormalEl2D(int iEl)
+void Mesh::calcScaledNormalEl2D(const std::map<MElement*,GEntity*> &element2entity, int iEl)
 {
   const JacobianBasis *jac = _el[iEl]->getJacobianFuncSpace();
 
   fullMatrix<double> primNodesXYZ(jac->getNumPrimMapNodes(),3);
+  SVector3 geoNorm(0.,0.,0.);
+  std::map<MElement*,GEntity*>::const_iterator itEl2ent = element2entity.find(_el[iEl]);
+  GEntity *ge = (itEl2ent == element2entity.end()) ? 0 : itEl2ent->second;
+  const bool hasGeoNorm = ge && (ge->dim() == 2) && ge->haveParametrization();
   for (int i=0; i<jac->getNumPrimMapNodes(); i++) {
     const int &iV = _el2V[iEl][i];
     primNodesXYZ(i,0) = _xyz[iV].x();
     primNodesXYZ(i,1) = _xyz[iV].y();
     primNodesXYZ(i,2) = _xyz[iV].z();
+    if (hasGeoNorm && (_vert[iV]->onWhat() == ge)) {
+      double u, v;
+      _vert[iV]->getParameter(0,u);
+      _vert[iV]->getParameter(1,v);
+      geoNorm += ((GFace*)ge)->normal(SPoint2(u,v));
+    }
+  }
+  if (hasGeoNorm && (geoNorm.normSq() == 0.)) {
+    SPoint2 param = ((GFace*)ge)->parFromPoint(_el[iEl]->barycenter(true),false);
+    geoNorm = ((GFace*)ge)->normal(param);
   }
 
-  _scaledNormEl[iEl].resize(1,3);
-  const double norm = jac->getPrimNormal2D(primNodesXYZ,_scaledNormEl[iEl]);
-
-  _scaledNormEl[iEl](0,0) /= norm;         // Re-scaling normal here is faster than an
-  _scaledNormEl[iEl](0,1) /= norm;         // extra scaling operation on the Jacobian
-  _scaledNormEl[iEl](0,2) /= norm;
+  fullMatrix<double> &elNorm = _scaledNormEl[iEl];
+  elNorm.resize(1,3);
+  const double norm = jac->getPrimNormal2D(primNodesXYZ,elNorm);
+  double factor = 1./norm;
+  if (hasGeoNorm) {
+    const double scal = geoNorm(0)*elNorm(0,0)+geoNorm(1)*elNorm(0,1)+geoNorm(2)*elNorm(0,2);
+    if (scal < 0.) factor = -factor;
+  }
+  elNorm.scale(factor);   // Re-scaling normal here is faster than an extra scaling operation on the Jacobian
 
 }
 
@@ -220,6 +238,14 @@ void Mesh::distSqToStraight(std::vector<double> &dSq)
   }
 }
 
+void Mesh::elSizeSq(std::vector<double> &sSq)
+{
+  for (int iEl = 0; iEl < nEl(); iEl++) {
+    const double s = _el[iEl]->getOuterRadius();
+    sSq[iEl] = s*s;
+  }
+}
+
 void Mesh::updateGEntityPositions()
 {
   for (int iV = 0; iV < nVert(); iV++)
diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.h b/contrib/HighOrderMeshOptimizer/OptHomMesh.h
index 06876da687..481cc1f96e 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomMesh.h
+++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.h
@@ -41,7 +41,8 @@
 class Mesh
 {
 public:
-  Mesh(const std::set<MElement*> &els, std::set<MVertex*> & toFix, bool fixBndNodes, bool fastJacEval);
+  Mesh(const std::map<MElement*,GEntity*> &element2entity,
+       const std::set<MElement*> &els, std::set<MVertex*> & toFix, bool fixBndNodes, bool fastJacEval);
 
   inline const int &nPC() { return _nPC; }
   inline int nVert() { return _vert.size(); }
@@ -67,6 +68,7 @@ public:
   void getUvw(double *it);
   void updateMesh(const double *it);
   void distSqToStraight(std::vector<double> &dSq);
+  void elSizeSq(std::vector<double> &sSq);
 
   void updateGEntityPositions();
   void writeMSH(const char *filename);
@@ -109,7 +111,7 @@ private:
   int addVert(MVertex* vert);
   int addFreeVert(MVertex* vert, const int iV, const int nPCV,
                   std::set<MVertex*> &toFix);
-  void calcScaledNormalEl2D(int iEl);
+  void calcScaledNormalEl2D(const std::map<MElement*,GEntity*> &element2entity, int iEl);
   static inline int indJB2DBase(int nNod, int l, int i, int j)
   {
     return (l*nNod+i)*nNod+j;
diff --git a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
index 9edc393daf..8c0b20135c 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
@@ -163,11 +163,13 @@ static std::set<MVertex *> getAllBndVertices
 static std::set<MElement*> getSurroundingBlob
    (MElement *el, int depth,
     const std::map<MVertex*, std::vector<MElement*> > &vertex2elements,
-    const double distFactor, int forceDepth = 0)
+    const double distFactor, int forceDepth, bool optPrimSurfMesh)
 {
 
-  const SPoint3 p = el->barycenter_infty();
-  const double limDist = distMaxStraight(el) * distFactor;
+  const SPoint3 p = el->barycenter();
+  const double dist = distMaxStraight(el);
+  const double limDist = ((optPrimSurfMesh && (dist < 1.e-10)) ?
+                          el->getOuterRadius() : dist) * distFactor;
 
   std::set<MElement*> blob;
   std::list<MElement*> currentLayer, lastLayer;
@@ -220,10 +222,18 @@ static void calcVertex2Elements(int dim, GEntity *entity,
   }
 }
 
+static void calcElement2Entity(GEntity *entity, std::map<MElement*,GEntity*> &element2entity)
+{
+  for (size_t i = 0; i < entity->getNumMeshElements(); ++i) {
+    MElement *element = entity->getMeshElement(i);
+    element2entity.insert(std::pair<MElement*,GEntity*>(element,entity));
+  }
+}
+
 static std::vector<std::pair<std::set<MElement*>, std::set<MVertex*> > > getConnectedBlobs
   (const std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
    const std::set<MElement*> &badElements, int depth, const double distFactor,
-   bool weakMerge = false)
+   bool weakMerge, bool optPrimSurfMesh)
 {
 
   Msg::Info("Starting blob generation from %i bad elements...", badElements.size());
@@ -232,9 +242,11 @@ static std::vector<std::pair<std::set<MElement*>, std::set<MVertex*> > > getConn
   Msg::Info("Constructing %i primary blobs", badElements.size());
   std::vector<std::set<MElement*> > primBlobs;
   primBlobs.reserve(badElements.size());
-  for (std::set<MElement*>::const_iterator it = badElements.begin();
-       it != badElements.end(); ++it)
-    primBlobs.push_back(getSurroundingBlob(*it, depth, vertex2elements, distFactor));
+  for (std::set<MElement*>::const_iterator it = badElements.begin(); it != badElements.end(); ++it) {
+    const int minLayers = ((*it)->getDim() == 3) ? 1 : 0;
+    primBlobs.push_back(getSurroundingBlob(*it, depth, vertex2elements,
+                                distFactor, minLayers, optPrimSurfMesh));
+  }
 
   // Compute blob connectivity
   Msg::Info("Computing blob connectivity...");
@@ -292,6 +304,7 @@ static std::vector<std::pair<std::set<MElement*>, std::set<MVertex*> > > getConn
 
 static void optimizeConnectedBlobs
   (const std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
+   const std::map<MElement*,GEntity*> &element2entity,
    std::set<MElement*> &badasses,
    OptHomParameters &p, int samples, bool weakMerge = false)
 {
@@ -300,14 +313,15 @@ static void optimizeConnectedBlobs
   p.maxJac = -1.e100;
 
   std::vector<std::pair<std::set<MElement*>, std::set<MVertex*> > > toOptimize =
-      getConnectedBlobs(vertex2elements, badasses, p.nbLayers, p.distanceFactor, weakMerge);
+                          getConnectedBlobs(vertex2elements, badasses, p.nbLayers,
+                                    p.distanceFactor, weakMerge, p.optPrimSurfMesh);
 
   //#pragma omp parallel for schedule(dynamic, 1)
   for (int i = 0; i < toOptimize.size(); ++i) {
     Msg::Info("Optimizing a blob %i/%i composed of %4d elements", i+1,
               toOptimize.size(), toOptimize[i].first.size());
     fflush(stdout);
-    OptHOM temp(toOptimize[i].first, toOptimize[i].second, p.fixBndNodes);
+    OptHOM temp(element2entity, toOptimize[i].first, toOptimize[i].second, p.fixBndNodes);
     //std::ostringstream ossI1;
     //ossI1 << "initial_ITER_" << i << ".msh";
     //temp.mesh.writeMSH(ossI1.str().c_str());
@@ -460,6 +474,7 @@ static bool detectNewBrokenElement(std::set<MElement*> &layer,
 
 static void optimizeOneByOne
   (const std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
+   const std::map<MElement*,GEntity*> &element2entity,
    std::set<MElement*> badElts, OptHomParameters &p, int samples)
 {
   p.SUCCESS = 1;
@@ -484,60 +499,84 @@ static void optimizeOneByOne
 
     for (int iterBlob=0; iterBlob<p.maxAdaptBlob; iterBlob++) {
 
-      OptHOM *opt;
+//      OptHOM *opt;
+//
+//      // First step: small blob with unsafe optimization (only 1st-order
+//      // bnd. vertices fixed)
+//      std::set<MElement*> toOptimizePrim = getSurroundingBlob
+//        (worstEl, nbLayers, vertex2elements, distanceFactor, 0);
+//      std::set<MVertex*> toFix1 = getPrimBndVertices(toOptimizePrim, vertex2elements);
+//      std::set<MElement*> toOptimize1;
+//      std::set_difference(toOptimizePrim.begin(),toOptimizePrim.end(),
+//                          badElts.begin(),badElts.end(), // Do not optimize badElts
+//                          std::inserter(toOptimize1, toOptimize1.end()));
+//      Msg::Info("Optimizing primary blob %i (max. %i remaining) composed of"
+//                " %4d elements", iBadEl, badElts.size(), toOptimize1.size());
+//      fflush(stdout);
+//      opt = new OptHOM(toOptimize1, toFix1, p.fixBndNodes);
+//      //std::ostringstream ossI1;
+//      //ossI1 << "initial_primary_" << iter << ".msh";
+//      //opt->mesh.writeMSH(ossI1.str().c_str());
+//      success = opt->optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN, p.BARRIER_MAX,
+//                              false, samples, p.itMax, p.optPassMax);
+//
+//      // Second step: add external layer, check it and optimize it safely (all
+//      // bnd. vertices fixed) if new broken element
+//      if (success > 0) {
+//        opt->mesh.updateGEntityPositions();
+//        std::set<MElement*> layer = addBlobLayer(toOptimizePrim, vertex2elements);
+//        if (detectNewBrokenElement(layer, badElts, p)) {
+//          delete opt;
+//          std::set<MVertex*> toFix2 = getAllBndVertices(toOptimizePrim, vertex2elements);
+//          std::set<MElement*> toOptimize2;
+//          std::set_difference(toOptimizePrim.begin(),toOptimizePrim.end(),
+//                              badElts.begin(),badElts.end(),
+//                              std::inserter(toOptimize2, toOptimize2.end()));
+//          Msg::Info("Optimizing corrective blob %i (max. %i remaining) "
+//                    "composed of %4d elements", iBadEl, badElts.size(),
+//                    toOptimize2.size());
+//          fflush(stdout);
+//          opt = new OptHOM(toOptimize2, toFix2, p.fixBndNodes);
+//          //std::ostringstream ossI1;
+//          //ossI1 << "initial_corrective_" << iter << ".msh";
+//          //opt->mesh.writeMSH(ossI1.str().c_str());
+//          success = opt->optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN,
+//                                  p.BARRIER_MAX, false, samples, p.itMax, p.optPassMax);
+//          if (success >= 0 && p.BARRIER_MIN_METRIC > 0) {
+//            Msg::Info("Jacobian optimization succeed, starting svd optimization");
+//            success = opt->optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN_METRIC,
+//                                    p.BARRIER_MAX, true, samples, p.itMax, p.optPassMax);
+//          }
+//        }
+//        else {
+//          Msg::Info("Primary blob %i did not break new elements, "
+//                    "no correction needed", iBadEl);
+//          fflush(stdout);
+//        }
+//      }
 
-      // First step: small blob with unsafe optimization (only 1st-order
-      // bnd. vertices fixed)
       std::set<MElement*> toOptimizePrim = getSurroundingBlob
-        (worstEl, nbLayers, vertex2elements, distanceFactor, 0);
-      std::set<MVertex*> toFix1 = getPrimBndVertices(toOptimizePrim, vertex2elements);
-      std::set<MElement*> toOptimize1;
+        (worstEl, nbLayers, vertex2elements, distanceFactor, 1, p.optPrimSurfMesh);
+//    std::set<MElement*> layer = addBlobLayer(toOptimizePrim, vertex2elements);
+      std::set<MVertex*> toFix = getAllBndVertices(toOptimizePrim, vertex2elements);
+      std::set<MElement*> toOptimize;
       std::set_difference(toOptimizePrim.begin(),toOptimizePrim.end(),
-                          badElts.begin(),badElts.end(), // Do not optimize badElts
-                          std::inserter(toOptimize1, toOptimize1.end()));
-      Msg::Info("Optimizing primary blob %i (max. %i remaining) composed of"
-                " %4d elements", iBadEl, badElts.size(), toOptimize1.size());
+                          badElts.begin(),badElts.end(),
+                          std::inserter(toOptimize, toOptimize.end()));
+      Msg::Info("Optimizing blob %i (max. %i remaining) "
+                "composed of %4d elements", iBadEl, badElts.size(),
+                toOptimize.size());
       fflush(stdout);
-      opt = new OptHOM(toOptimize1, toFix1, p.fixBndNodes);
+      OptHOM *opt = new OptHOM(element2entity, toOptimize, toFix, p.fixBndNodes);
       //std::ostringstream ossI1;
-      //ossI1 << "initial_primary_" << iter << ".msh";
+      //ossI1 << "initial_corrective_" << iter << ".msh";
       //opt->mesh.writeMSH(ossI1.str().c_str());
-      success = opt->optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN, p.BARRIER_MAX,
-                              false, samples, p.itMax, p.optPassMax);
-
-      // Second step: add external layer, check it and optimize it safely (all
-      // bnd. vertices fixed) if new broken element
-      if (success > 0) {
-        opt->mesh.updateGEntityPositions();
-        std::set<MElement*> layer = addBlobLayer(toOptimizePrim, vertex2elements);
-        if (detectNewBrokenElement(layer, badElts, p)) {
-          delete opt;
-          std::set<MVertex*> toFix2 = getAllBndVertices(toOptimizePrim, vertex2elements);
-          std::set<MElement*> toOptimize2;
-          std::set_difference(toOptimizePrim.begin(),toOptimizePrim.end(),
-                              badElts.begin(),badElts.end(),
-                              std::inserter(toOptimize2, toOptimize2.end()));
-          Msg::Info("Optimizing corrective blob %i (max. %i remaining) "
-                    "composed of %4d elements", iBadEl, badElts.size(),
-                    toOptimize2.size());
-          fflush(stdout);
-          opt = new OptHOM(toOptimize2, toFix2, p.fixBndNodes);
-          //std::ostringstream ossI1;
-          //ossI1 << "initial_corrective_" << iter << ".msh";
-          //opt->mesh.writeMSH(ossI1.str().c_str());
-          success = opt->optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN,
-                                  p.BARRIER_MAX, false, samples, p.itMax, p.optPassMax);
-          if (success >= 0 && p.BARRIER_MIN_METRIC > 0) {
-            Msg::Info("Jacobian optimization succeed, starting svd optimization");
-            success = opt->optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN_METRIC,
-                                    p.BARRIER_MAX, true, samples, p.itMax, p.optPassMax);
-          }
-        }
-        else {
-          Msg::Info("Primary blob %i did not break new elements, "
-                    "no correction needed", iBadEl);
-          fflush(stdout);
-        }
+      success = opt->optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN,
+                              p.BARRIER_MAX, false, samples, p.itMax, p.optPassMax);
+      if (success >= 0 && p.BARRIER_MIN_METRIC > 0) {
+        Msg::Info("Jacobian optimization succeed, starting svd optimization");
+        success = opt->optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN_METRIC,
+                                p.BARRIER_MAX, true, samples, p.itMax, p.optPassMax);
       }
 
       // Measure min and max Jac., update mesh
@@ -558,11 +597,11 @@ static void optimizeOneByOne
         nbLayers *= p.adaptBlobLayerFact;
         Msg::Info("Blob %i failed (adapt #%i), adapting with increased size",
                   iBadEl, iterBlob);
-        if (iterBlob == p.maxAdaptBlob-1) {
+//        if (iterBlob == p.maxAdaptBlob-1) {
           std::ostringstream ossI2;
           ossI2 << "final_" << iBadEl << ".msh";
           opt->mesh.writeMSH(ossI2.str().c_str());
-        }
+//        }
       }
       else break;
     }
@@ -589,18 +628,20 @@ void HighOrderMeshOptimizer(GModel *gm, OptHomParameters &p)
   gm->getEntities(entities);
 
   std::map<MVertex*, std::vector<MElement *> > vertex2elements;
+  std::map<MElement*,GEntity*> element2entity;
   std::set<MElement*> badasses;
   for (int iEnt = 0; iEnt < entities.size(); ++iEnt) {
     GEntity* &entity = entities[iEnt];
     if (entity->dim() != p.dim || (p.onlyVisible && !entity->getVisibility())) continue;
     Msg::Info("Computing connectivity and bad elements for entity %d...",
               entity->tag());
-    calcVertex2Elements(p.dim,entity,vertex2elements); // Compute vert. -> elt. connectivity
+    calcVertex2Elements(p.dim,entity,vertex2elements);
+    if (p.optPrimSurfMesh) calcElement2Entity(entity,element2entity);
     for (int iEl = 0; iEl < entity->getNumMeshElements();iEl++) { // Detect bad elements
       double jmin, jmax;
       MElement *el = entity->getMeshElement(iEl);
       if (el->getDim() == p.dim) {
-        el->scaledJacRange(jmin, jmax);
+        el->scaledJacRange(jmin, jmax, p.optPrimSurfMesh ? entity : 0);
         if (p.BARRIER_MIN_METRIC > 0) jmax = jmin;
         if (jmin < p.BARRIER_MIN || jmax > p.BARRIER_MAX) badasses.insert(el);
       }
@@ -608,11 +649,11 @@ void HighOrderMeshOptimizer(GModel *gm, OptHomParameters &p)
   }
 
   if (p.strategy == 0)
-    optimizeConnectedBlobs(vertex2elements, badasses, p, samples, false);
+    optimizeConnectedBlobs(vertex2elements, element2entity, badasses, p, samples, false);
   else if (p.strategy == 2)
-    optimizeConnectedBlobs(vertex2elements, badasses, p, samples, true);
+    optimizeConnectedBlobs(vertex2elements, element2entity, badasses, p, samples, true);
   else
-    optimizeOneByOne(vertex2elements, badasses, p, samples);
+    optimizeOneByOne(vertex2elements, element2entity, badasses, p, samples);
 
   if (p.SUCCESS == 1)
     Msg::Info("Optimization succeeded");
diff --git a/contrib/HighOrderMeshOptimizer/OptHomRun.h b/contrib/HighOrderMeshOptimizer/OptHomRun.h
index 865301c078..f82751da05 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomRun.h
+++ b/contrib/HighOrderMeshOptimizer/OptHomRun.h
@@ -53,6 +53,7 @@ struct OptHomParameters {
   int maxAdaptBlob; // Max. nb. of blob adaptation interations
   int adaptBlobLayerFact; // Growth factor in number of layers for blob adaptation
   double adaptBlobDistFact; // Growth factor in distance factor for blob adaptation
+  bool optPrimSurfMesh; // Enable optimisation of p1 surface meshes
 
   // OUTPUT ------>
   int SUCCESS ; // 0 --> success , 1 --> Not converged
@@ -63,7 +64,7 @@ struct OptHomParameters {
     : BARRIER_MIN_METRIC(-1.), BARRIER_MIN(0.1), BARRIER_MAX(2.0), weightFixed(1.e6),
       weightFree (1.e2), nbLayers (6) , dim(3) , itMax(300), onlyVisible(true),
       distanceFactor(12), fixBndNodes(false), strategy(0), maxAdaptBlob(3),
-      adaptBlobLayerFact(2.), adaptBlobDistFact(2.)
+      adaptBlobLayerFact(2.), adaptBlobDistFact(2.), optPrimSurfMesh(false)
   {
   }
 };
-- 
GitLab