diff --git a/Fltk/highOrderToolsWindow.cpp b/Fltk/highOrderToolsWindow.cpp
index 236d257873219ddbd1142411b601e44dfe5086d5..566a5b5e3616f69b9e6218aa460e7fbe320bb530 100644
--- a/Fltk/highOrderToolsWindow.cpp
+++ b/Fltk/highOrderToolsWindow.cpp
@@ -96,20 +96,33 @@ static void chooseopti_cb(Fl_Widget *w, void *data)
 
   if (elastic == 1){
     o->choice[0]->deactivate();
+    o->choice[3]->deactivate();
     for (int i=3;i<=6;i++)
       o->value[i]->deactivate();
+    for (int i=9;i<=11;i++) o->value[i]->deactivate();
     //   o->push[1]->deactivate();
   }
   else {
     o->push[0]->activate();
     o->choice[0]->activate();
+    o->choice[3]->activate();
     for (int i=3;i<=6;i++)
       o->value[i]->activate();
+    for (int i=9;i<=11;i++) o->value[i]->activate();
     //    o->push[1]->activate();
   }
 #endif
 }
 
+static void chooseopti_strategy(Fl_Widget *w, void *data)
+{
+#if defined(HAVE_OPTHOM)
+  highOrderToolsWindow *o = FlGui::instance()->highordertools;
+  if (o->choice[3]->value() == 0) for (int i=9;i<=11;i++) o->value[i]->deactivate();
+  else for (int i=9;i<=11;i++) o->value[i]->activate();
+#endif
+}
+
 static void highordertools_runelas_cb(Fl_Widget *w, void *data)
 {
 #if defined(HAVE_OPTHOM)
@@ -133,8 +146,12 @@ static void highordertools_runelas_cb(Fl_Widget *w, void *data)
     p.optPassMax = (int) o->value[4]->value();
     p.weightFixed =  o->value[5]->value();
     p.weightFree =  o->value[6]->value();
-    p.DistanceFactor =  o->value[7]->value();
-    p.method =  o->CAD ? (int)o->choice[0]->value() : 2;
+    p.distanceFactor =  o->value[7]->value();
+    p.fixBndNodes = (!o->CAD) || (o->choice[0]->value() == 0);
+    p.strategy = o->choice[3]->value();
+    p.maxAdaptBlob = o->value[9]->value();
+    p.adaptBlobLayerFact = (int) o->value[10]->value();
+    p.adaptBlobDistFact = o->value[11]->value();
     HighOrderMeshOptimizer (GModel::current(),p);
     printf("CPU TIME = %4f seconds\n",p.CPU);
   }
@@ -149,7 +166,7 @@ highOrderToolsWindow::highOrderToolsWindow(int deltaFontSize)
   FL_NORMAL_SIZE -= deltaFontSize;
 
   int width = 3 * IW + 4 * WB;
-  int height = 25 * BH;
+  int height = 28 * BH;
 
   win = new paletteWindow
     (width, height, CTX::instance()->nonModalWindows ? true : false, "High Order Tools");
@@ -315,6 +332,48 @@ highOrderToolsWindow::highOrderToolsWindow(int deltaFontSize)
   value[4]->align(FL_ALIGN_RIGHT);
   value[4]->value(50);
 
+  static Fl_Menu_Item menu_strategy[] = {
+    {"Connected blobs", 0, 0, 0},
+    {"Adaptive one-by-one", 0, 0, 0},
+    {0}
+  };
+
+  y += BH;
+  choice[3] = new Fl_Choice
+    (x,y, IW, BH, "Strategy");
+  choice[3]->menu(menu_strategy);
+  choice[3]->align(FL_ALIGN_RIGHT);
+  choice[3]->callback(chooseopti_strategy);
+
+  y += BH;
+  value[9] = new Fl_Value_Input
+    (x,y, IW, BH, "Max. number of blob adaptation iter.");
+  value[9]->minimum(1);
+  value[9]->maximum(100);
+  value[9]->step(1);
+  value[9]->align(FL_ALIGN_RIGHT);
+  value[9]->value(2);
+  value[9]->deactivate();
+
+  y += BH;
+  value[10] = new Fl_Value_Input
+    (x,y, IW, BH, "Num. layer adaptation factor");
+  value[10]->align(FL_ALIGN_RIGHT);
+  value[10]->minimum(1);
+  value[10]->maximum(100);
+  value[10]->step(1);
+  value[10]->value(2);
+  value[10]->deactivate();
+
+  y += BH;
+  value[11] = new Fl_Value_Input
+    (x,y, IW, BH, "Distance adaptation factor");
+  value[11]->align(FL_ALIGN_RIGHT);
+  value[11]->minimum(1.);
+  value[11]->maximum(100.);
+  value[11]->value(2.);
+  value[11]->deactivate();
+
   y += 1.5*BH;
   push[1] = new Fl_Button
     (x,y, IW, BH, "Apply");
diff --git a/Geo/MPyramid.h b/Geo/MPyramid.h
index c2d7c049959265721f46ba13e79e3361f0f1b979..60fe3e071641403cd3b6d43f99bf9d7133d97efb 100644
--- a/Geo/MPyramid.h
+++ b/Geo/MPyramid.h
@@ -221,18 +221,17 @@ class MPyramidN : public MPyramid {
  protected:
   std::vector<MVertex*> _vs;
   const char _order;
-  double _disto;
  public:
   MPyramidN(MVertex* v0, MVertex* v1, MVertex* v2, MVertex* v3, MVertex* v4,
       const std::vector<MVertex*> &v, char order, int num=0, int part=0)
-    : MPyramid(v0, v1, v2, v3, v4, num, part), _vs(v), _order(order), _disto(-1e22)
+    : MPyramid(v0, v1, v2, v3, v4, num, part), _vs(v), _order(order)
   {
     for (unsigned int i = 0; i < _vs.size(); i++) _vs[i]->setPolynomialOrder(_order);
     getFunctionSpace(order);
   }
 
   MPyramidN(const std::vector<MVertex*> &v, char order, int num=0, int part=0)
-    : MPyramid(v[0], v[1], v[2], v[3], v[4], num, part), _order(order), _disto(-1e22)
+    : MPyramid(v[0], v[1], v[2], v[3], v[4], num, part), _order(order)
   {
     for (unsigned int i = 5; i < v.size(); i++ ) _vs.push_back(v[i]);
     for (unsigned int i = 0; i < _vs.size(); i++) _vs[i]->setPolynomialOrder(_order);
diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.cpp b/contrib/HighOrderMeshOptimizer/OptHOM.cpp
index 41ecbf88fe8d8983e6e3107495cff1bc722a4d4a..e722f24a545dc4a9073f5c68bf826e5bc84417ef 100644
--- a/contrib/HighOrderMeshOptimizer/OptHOM.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHOM.cpp
@@ -42,11 +42,11 @@ static inline double compute_f1(double v, double barrier)
 
 
 // Constructor
-OptHOM::OptHOM(GEntity *ge, const std::set<MElement*> &els, std::set<MVertex*> & toFix, int method) :
-       mesh(ge, els, toFix, method)
+OptHOM::OptHOM(const std::set<MElement*> &els, std::set<MVertex*> & toFix, bool fixBndNodes) :
+       mesh(els, toFix, fixBndNodes)
 {
   _optimizeMetricMin = false;
-};
+}
 
 
 
@@ -241,6 +241,8 @@ void OptHOM::OptimPass(alglib::real_1d_array &x, const alglib::real_1d_array &in
   static int OPTMETHOD = 1;
 
   Msg::Debug("--- Optimization pass with jacBar = %12.5E",jacBar);
+  std::cout << "--- Optimization pass with initial jac. range ("
+            << minJac << "," << maxJac << "), jacBar = " << jacBar << "\n";
 
   iter = 0;
 
@@ -321,7 +323,6 @@ int OptHOM::optimize(double weightFixed, double weightFree, double b_min, double
 
   const double jacBarStart = (minJac > 0.) ? 0.9*minJac : 1.1*minJac;
   jacBar = jacBarStart;
-  setBarrierTerm(jacBarStart);
 
   _optimizeBarrierMax = false;
   // Calculate initial objective function value and gradient
@@ -337,24 +338,37 @@ int OptHOM::optimize(double weightFixed, double weightFree, double b_min, double
             << " and max. barrier = " << barrier_max << std::endl;
 
   int ITER = 0;
+  bool minJacOK = true;
   while (minJac < barrier_min) {
+    const double startMinJac = minJac;
     OptimPass(x, gradObj, itMax);
     recalcJacDist();
     jacBar = (minJac > 0.) ? 0.9*minJac : 1.1*minJac;
-    setBarrierTerm(jacBar);
-    if (ITER ++ > optPassMax) break;
+    if (ITER ++ > optPassMax) {
+      minJacOK = (minJac > barrier_min);
+      break;
+    }
+    if (fabs((minJac-startMinJac)/startMinJac) < 0.01) {
+      std::cout << "Stagnation in minJac detected, stopping optimization\n";
+      minJacOK = false;
+      break;
+    }
   }
 
-  if (!_optimizeMetricMin) {
+  ITER = 0;
+  if (minJacOK && (!_optimizeMetricMin)) {
     _optimizeBarrierMax = true;
     jacBar =  1.1 * maxJac;
-    setBarrierTerm(jacBar);
     while (maxJac > barrier_max ) {
+      const double startMaxJac = maxJac;
       OptimPass(x, gradObj, itMax);
       recalcJacDist();
       jacBar =  1.1 * maxJac;
-      setBarrierTerm(jacBar);
       if (ITER ++ > optPassMax) break;
+      if (fabs((maxJac-startMaxJac)/startMaxJac) < 0.01) {
+        std::cout << "Stagnation in maxJac detected, stopping optimization\n";
+        break;
+      }
     }
   }
 
diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.h b/contrib/HighOrderMeshOptimizer/OptHOM.h
index 2d6fcda080b0504318f649dc2fa5ad25209d8e8b..53e60216ec7084353b8fc2d0cb86f5412f90bde2 100644
--- a/contrib/HighOrderMeshOptimizer/OptHOM.h
+++ b/contrib/HighOrderMeshOptimizer/OptHOM.h
@@ -21,7 +21,7 @@ public:
 
   Mesh mesh;
 
-  OptHOM(GEntity *gf, const std::set<MElement*> &els, std::set<MVertex*> & toFix, int method);
+  OptHOM(const std::set<MElement*> &els, std::set<MVertex*> & toFix, bool fixBndNodes);
   // 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, JMIN > 0) but JMIN < barrier_min || JMAX > barrier_max
   // returns -1 if the mesh is invalid : some jacobians cannot be made positive
@@ -36,7 +36,6 @@ public:
 
 private:
 
-//  double lambda, lambda2, powM, powP, invLengthScaleSq;
   double lambda, lambda2, jacBar, invLengthScaleSq;
   int iter, progressInterv;            // Current iteration, interval of iterations for reporting
   bool _optimizeMetricMin;
@@ -45,7 +44,6 @@ private:
 
   bool _optimizeBarrierMax; // false : only moving barrier min; true : fixed barrier min + moving barrier max
 
-  inline void setBarrierTerm(double jacBarrier) {jacBar = jacBarrier;}
   bool addJacObjGrad(double &Obj, alglib::real_1d_array &gradObj);
   bool addMetricMinObjGrad(double &Obj, alglib::real_1d_array &gradObj);
   bool addDistObjGrad(double Fact, double Fact2, double &Obj, alglib::real_1d_array &gradObj);
diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
index 08398951b89659e78805abe5d62e3a90f43b78e6..ca60318901b226100e15407f34c12c66b81a7318 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
@@ -8,32 +8,21 @@
 
 
 
-Mesh::Mesh(GEntity *ge, const std::set<MElement*> &els, std::set<MVertex*> &toFix, int method) :
-    _ge(ge)
+Mesh::Mesh(const std::set<MElement*> &els, std::set<MVertex*> &toFix, bool fixBndNodes)
 {
-  _dim = _ge->dim();
 
-  if (method & METHOD_PHYSCOORD) {
+  _dim = (*els.begin())->getDim();
+
+  if (fixBndNodes) {
     if (_dim == 2) _pc = new ParamCoordPhys2D;
     else _pc = new ParamCoordPhys3D;
-    Msg::Debug("METHOD: Using physical coordinates");
-  }
-  else if (method & METHOD_SURFCOORD)  {
-    if (_dim == 2) {
-      _pc = new ParamCoordSurf(_ge);
-      Msg::Debug("METHOD: Using surface parametric coordinates");
-    }
-    else Msg::Error("ERROR: Surface parametric coordinates only for 2D optimization");
+    Msg::Debug("METHOD: Fixing boundary nodes and using physical coordinates");
   }
   else {
     _pc = new ParamCoordParent;
-    Msg::Debug("METHOD: Using parent parametric coordinates");
+    Msg::Debug("METHOD: Freeing boundary nodes and using parent parametric coordinates");
   }
 
-  if (method & METHOD_RELAXBND)Msg::Debug("METHOD: Relaxing boundary vertices");
-  else if (method & METHOD_FIXBND) Msg::Debug("METHOD: Fixing all boundary vertices");
-  else Msg::Debug("METHOD: Fixing vertices on geometric points and \"toFix\" boundary");
-
   // Initialize elements, vertices, free vertices and element->vertices connectivity
   const int nElements = els.size();
   _nPC = 0;
@@ -55,8 +44,7 @@ Mesh::Mesh(GEntity *ge, const std::set<MElement*> &els, std::set<MVertex*> &toFi
       _el2V[iEl].push_back(iV);
       const int nPCV = _pc->nCoord(vert);
       bool isFV = false;
-      if (method & METHOD_RELAXBND) isFV = true;
-      else if (method & METHOD_FIXBND) isFV = (vert->onWhat()->dim() == _dim) && (toFix.find(vert) == toFix.end());
+      if (fixBndNodes) isFV = (vert->onWhat()->dim() == _dim) && (toFix.find(vert) == toFix.end());
       else isFV = (vert->onWhat()->dim() >= 1) && (toFix.find(vert) == toFix.end());
       if (isFV) {
         int iFV = addFreeVert(vert,iV,nPCV,toFix);
@@ -87,14 +75,7 @@ Mesh::Mesh(GEntity *ge, const std::set<MElement*> &els, std::set<MVertex*> &toFi
     double dumJac[3][3];
     for (int iEl = 0; iEl < nEl(); iEl++) _invStraightJac[iEl] = 1. / _el[iEl]->getPrimaryJacobian(0.,0.,0.,dumJac);
   }
-  if ((_dim == 2) && (method & METHOD_PROJJAC)) {
-    projJac = true;
-    Msg::Debug("METHOD: Using projected Jacobians");
-  }
-  else {
-    projJac = false;
-    Msg::Debug("METHOD: Using usual Jacobians");
-  }
+
 }
 
 
@@ -371,7 +352,7 @@ void Mesh::writeMSH(const char *filename)
   fprintf(f, "%d\n", nEl());
   for (int iEl = 0; iEl < nEl(); iEl++) {
 //    MElement *MEl = _el[iEl];
-    fprintf(f, "%d %d 2 0 %d", iEl+1, _el[iEl]->getTypeForMSH(), _ge->tag());
+    fprintf(f, "%d %d 2 0 0", iEl+1, _el[iEl]->getTypeForMSH());
     for (size_t iVEl = 0; iVEl < _el2V[iEl].size(); iVEl++) fprintf(f, " %d", _el2V[iEl][iVEl] + 1);
     fprintf(f, "\n");
   }
diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.h b/contrib/HighOrderMeshOptimizer/OptHomMesh.h
index 0a716ae5a2760fc45126aaf2e3b4a21289d7e26a..481d6a2b8e292c1e0268626dea52378135efbd44 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomMesh.h
+++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.h
@@ -17,7 +17,7 @@ class Mesh
 
 public:
 
-  Mesh(GEntity *ge, const std::set<MElement*> &els, std::set<MVertex*> & toFix, int method);
+  Mesh(const std::set<MElement*> &els, std::set<MVertex*> & toFix, bool fixBndNodes);
 
   inline const int &nPC() { return _nPC; }
   inline int nVert() { return _vert.size(); }
@@ -46,9 +46,6 @@ public:
   void updateGEntityPositions();
   void writeMSH(const char *filename);
 
-  enum { METHOD_RELAXBND = 1 << 0, METHOD_FIXBND = 1 << 1, METHOD_PHYSCOORD = 1 << 2,
-    METHOD_SURFCOORD = 1 << 3, METHOD_PROJJAC = 1 << 4 };
-
 //  inline double xyzDBG(int iV, int iCoord) { return _xyz[iV][iCoord]; }
 //  inline double ixyzDBG(int iV, int iCoord) { return _ixyz[iV][iCoord]; }
 //  inline double sxyzDBG(int iV, int iCoord) { return _sxyz[iV][iCoord]; }
@@ -58,7 +55,6 @@ public:
 
 private:
 
-  GEntity *_ge;
   int _dim;
   int _nPC;                                       // Total nb. of parametric coordinates
 
@@ -77,7 +73,6 @@ private:
   std::vector<std::vector<int> > _indPCEl;        // Index of parametric coord. for an el.
 
   ParamCoord *_pc;
-  bool projJac;                                   // Using "projected" Jacobians or not
 
   int addVert(MVertex* vert);
   int addFreeVert(MVertex* vert, const int iV, const int nPCV, std::set<MVertex*> &toFix);
diff --git a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
index b713254b1e1bda8282e80ea24da9ee8e1f0d97c1..0cc9e087fa984893d8c50a81d651205d42fab05b 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
@@ -138,12 +138,13 @@ void exportMeshToDassault (GModel *gm, const std::string &fn, int dim){
 
 
 
-static std::set<MVertex *> getBndVertices(std::set<MElement*> &elements, std::map<MVertex *, std::vector<MElement*> > &vertex2elements)
+static std::set<MVertex *> getAllBndVertices(std::set<MElement*> &elements,
+            const std::map<MVertex*, std::vector<MElement*> > &vertex2elements)
 {
   std::set<MVertex*> bnd;
   for (std::set<MElement*>::iterator itE = elements.begin(); itE != elements.end(); ++itE) {
     for (int i = 0; i < (*itE)->getNumPrimaryVertices(); ++i) {
-      const std::vector<MElement*> &neighbours = vertex2elements[(*itE)->getVertex(i)];
+      const std::vector<MElement*> &neighbours = vertex2elements.find((*itE)->getVertex(i))->second;
       for (size_t k = 0; k < neighbours.size(); ++k) {
         if (elements.find(neighbours[k]) == elements.end()) {
           for (int j = 0; j < neighbours[k]->getNumVertices(); ++j) {
@@ -158,7 +159,9 @@ static std::set<MVertex *> getBndVertices(std::set<MElement*> &elements, std::ma
 
 
 
-static std::set<MElement*> getSurroundingBlob(MElement *el, int depth, std::map<MVertex *, std::vector<MElement*> > &vertex2elements, const double distFactor)
+static std::set<MElement*> getSurroundingBlob(MElement *el, int depth,
+                                              const std::map<MVertex*, std::vector<MElement*> > &vertex2elements,
+                                              const double distFactor, int forceDepth = 0)
 {
 
   const SPoint3 p = el->barycenter_infty();
@@ -173,9 +176,9 @@ static std::set<MElement*> getSurroundingBlob(MElement *el, int depth, std::map<
     currentLayer.clear();
     for (std::list<MElement*>::iterator it = lastLayer.begin(); it != lastLayer.end(); ++it) {
       for (int i = 0; i < (*it)->getNumPrimaryVertices(); ++i) {
-        const std::vector<MElement*> &neighbours = vertex2elements[(*it)->getVertex(i)];
+        const std::vector<MElement*> &neighbours = vertex2elements.find((*it)->getVertex(i))->second;
         for (std::vector<MElement*>::const_iterator itN = neighbours.begin(); itN != neighbours.end(); ++itN)
-          if (p.distance((*itN)->barycenter_infty()) < limDist)
+          if ((d < forceDepth) || (p.distance((*itN)->barycenter_infty()) < limDist))
             if (blob.insert(*itN).second) currentLayer.push_back(*itN);       // Assume that if an el is too far, its neighbours are too far as well
       }
     }
@@ -203,20 +206,24 @@ static void addBlobChaintoGroup(std::set<int> &group, const std::vector<std::set
 
 
 
-static std::vector<std::pair<std::set<MElement*> , std::set<MVertex*> > > getConnectedBlobs(GEntity &entity, const std::set<MElement*> &badElements, int depth, const double distFactor)
+static void calcVertex2Elements(int dim, GEntity *entity, std::map<MVertex*, std::vector<MElement *> > &vertex2elements)
 {
+  for (size_t i = 0; i < entity->getNumMeshElements(); ++i) {
+    MElement *element = entity->getMeshElement(i);
+    if (element->getDim() == dim)
+      for (int j = 0; j < element->getNumPrimaryVertices(); ++j)
+        vertex2elements[element->getVertex(j)].push_back(element);
+  }
+}
 
-  OptHomMessage("Starting blob generation from %i bad elements...",badElements.size());
 
-  // Compute vertex -> element connectivity
-  std::cout << "Computing vertex -> element connectivity...\n";
-  std::map<MVertex*, std::vector<MElement *> > vertex2elements;
-  for (size_t i = 0; i < entity.getNumMeshElements(); ++i) {
-    MElement &element = *entity.getMeshElement(i);
-    for (int j = 0; j < element.getNumPrimaryVertices(); ++j) {
-      vertex2elements[element.getVertex(j)].push_back(&element);
-    }
-  }
+
+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)
+{
+
+  OptHomMessage("Starting blob generation from %i bad elements...",badElements.size());
 
   // Contruct primary blobs
   std::cout << "Constructing " << badElements.size() << " primary blobs...\n";
@@ -268,7 +275,7 @@ static std::vector<std::pair<std::set<MElement*> , std::set<MVertex*> > > getCon
   std::cout << "Computing boundaries for " << blobs.size() << " blobs...\n";
   std::vector<std::pair<std::set<MElement *>, std::set<MVertex*> > > result;
   for (std::list<std::set<MElement*> >::iterator itB = blobs.begin(); itB != blobs.end(); ++itB)
-    result.push_back(std::pair<std::set<MElement*>, std::set<MVertex*> >(*itB, getBndVertices(*itB, vertex2elements)));
+    result.push_back(std::pair<std::set<MElement*>, std::set<MVertex*> >(*itB, getAllBndVertices(*itB, vertex2elements)));
 
   OptHomMessage("Generated %i blobs",blobs.size());
 
@@ -278,90 +285,506 @@ static std::vector<std::pair<std::set<MElement*> , std::set<MVertex*> > > getCon
 
 
 
-void HighOrderMeshOptimizer (GModel *gm, OptHomParameters &p)
+static void optimizeConnectedBlobs(const std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
+                                   std::set<MElement*> &badasses,
+                                   OptHomParameters &p, int samples)
 {
 
-  double t1 = Cpu();
+  p.SUCCESS = 1;
+  p.minJac = 1.e100;
+  p.maxJac = -1.e100;
+
+  std::vector<std::pair<std::set<MElement*>, std::set<MVertex*> > > toOptimize =
+      getConnectedBlobs(vertex2elements, badasses, p.nbLayers, p.distanceFactor);
+
+  //#pragma omp parallel for schedule(dynamic, 1)
+  for (int i = 0; i < toOptimize.size(); ++i) {
+    OptHomMessage("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);
+//std::ostringstream ossI1;
+//ossI1 << "initial_ITER_" << i << ".msh";
+//temp.mesh.writeMSH(ossI1.str().c_str());
+    int success = temp.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) {
+      OptHomMessage("Jacobian optimization succeed, starting svd optimization");
+      success = temp.optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN_METRIC, p.BARRIER_MAX,
+                              true, samples, p.itMax, p.optPassMax);
+    }
+    double minJac, maxJac, distMaxBND, distAvgBND;
+    temp.recalcJacDist();
+    temp.getJacDist(minJac, maxJac, distMaxBND, distAvgBND);
+    p.minJac = std::min(p.minJac,minJac);
+    p.maxJac = std::max(p.maxJac,maxJac);
+    temp.mesh.updateGEntityPositions();
+    if (success <= 0) {
+      std::ostringstream ossI2;
+      ossI2 << "final_ITER_" << i << ".msh";
+      temp.mesh.writeMSH(ossI2.str().c_str());
+    }
+    //#pragma omp critical
+    p.SUCCESS = std::min(p.SUCCESS, success);
+  }
 
-  int samples = 30;
+}
 
-  int method = 0;
-  if (p.method == 0)
-    method = Mesh::METHOD_FIXBND | Mesh::METHOD_PROJJAC;
-  else if (p.method == 1)
-    method = Mesh::METHOD_PROJJAC;
-  else if (p.method == 2)
-    method = Mesh::METHOD_FIXBND | Mesh::METHOD_PHYSCOORD | Mesh::METHOD_PROJJAC;
-  else if(p.method < 0)
-    method = -p.method;
 
-  SVector3 BND(gm->bounds().max(), gm->bounds().min());
 
-  OptHomMessage("High order mesh optimizer starts");
-  std::vector<GEntity*> entities;
-  gm->getEntities(entities);
+static MElement *getWorstElement(const std::set<MElement*> &badasses, OptHomParameters &p){
+
+  double worst = 1.e300;
+  MElement *worstEl = 0;
 
-  for (int i = 0; i < entities.size(); ++i) {
+  for (std::set<MElement*>::iterator it=badasses.begin(); it!=badasses.end(); it++) {
+    double jmin, jmax, val;
+    (*it)->scaledJacRange(jmin,jmax);
+    val = jmin-p.BARRIER_MIN + p.BARRIER_MAX-jmax;
+    if (val < worst) {
+      worst = val;
+      worstEl = *it;
+    }
+  }
 
-    double tf1 = Cpu();
-    GEntity &entity = *entities[i];
+  return worstEl;
 
-    if (entity.dim() != p.dim || (p.onlyVisible && !entity.getVisibility())) continue;
+}
 
-    OptHomMessage("Optimizing Model entity %4d...", entity.tag());
-    std::set<MElement*> badasses;
-    for (int i = 0; i < entity.getNumMeshElements();i++) {
-      double jmin, jmax;
-      entity.getMeshElement(i)->scaledJacRange(jmin, jmax);
-      if (p.BARRIER_MIN_METRIC > 0) jmax = jmin;
-      if (jmin < p.BARRIER_MIN || jmax > p.BARRIER_MAX)
-        badasses.insert(entity.getMeshElement(i));
+
+
+static std::set<MVertex *> getPrimBndVertices(std::set<MElement*> &elements,
+            const std::map<MVertex*, std::vector<MElement*> > &vertex2elements)
+{
+  std::set<MVertex*> bnd;
+  for (std::set<MElement*>::iterator itE = elements.begin(); itE != elements.end(); ++itE) {
+    for (int i = 0; i < (*itE)->getNumPrimaryVertices(); ++i) {
+      const std::vector<MElement*> &neighbours = vertex2elements.find((*itE)->getVertex(i))->second;
+      for (size_t k = 0; k < neighbours.size(); ++k) {
+        if (elements.find(neighbours[k]) == elements.end()) {
+            bnd.insert((*itE)->getVertex(i));
+        }
+      }
+    }
+  }
+  return bnd;
+}
+
+
+
+static std::set<MElement*> getSurroundingBlob3D(MElement *el, int depth,
+                                                const std::map<MVertex*, std::vector<MElement*> > &vertex2elements,
+                                                const double distFactor)
+{
+
+  const double limDist = distMaxStraight(el)*distFactor;
+
+  std::set<MElement*> blob;
+  std::list<MElement*> currentLayer, lastLayer;
+
+  std::list<SPoint3> seedPts;
+
+  blob.insert(el);
+  lastLayer.push_back(el);
+  for (int d = 0; d < depth; ++d) {
+    currentLayer.clear();
+    for (std::list<MElement*>::iterator it = lastLayer.begin(); it != lastLayer.end(); ++it) {
+      for (int i = 0; i < (*it)->getNumPrimaryVertices(); ++i) {
+        const std::vector<MElement*> &neighbours = vertex2elements.find((*it)->getVertex(i))->second;
+        for (std::vector<MElement*>::const_iterator itN = neighbours.begin(); itN != neighbours.end(); ++itN) {
+          SPoint3 pt = (*itN)->barycenter_infty();                            // Check distance from all seed points
+          bool nearSeed = false;
+          for (std::list<SPoint3>::const_iterator itS = seedPts.begin(); itS != seedPts.end(); ++itS)
+            if (itS->distance(pt) < limDist) {
+              nearSeed = true;
+              break;
+            }
+          if ((d == 0) || nearSeed)
+            if (blob.insert(*itN).second) currentLayer.push_back(*itN);       // Assume that if an el is too far, its neighbours are too far as well
+        }
+      }
     }
-    if (badasses.empty()) continue;
+    if (d == 0)                                                               // Elts of 1st layer are seed points
+      for (std::list<MElement*>::iterator itL = currentLayer.begin(); itL != currentLayer.end(); ++itL)
+        seedPts.push_back((*itL)->barycenter_infty());
+    lastLayer = currentLayer;
+  }
+
+  return blob;
+
+}
 
-    std::vector<std::pair<std::set<MElement*>, std::set<MVertex*> > > toOptimize = getConnectedBlobs(entity, badasses, p.nbLayers, p.DistanceFactor);
 
-    //#pragma omp parallel for schedule(dynamic, 1)
-    p.SUCCESS = 1;
-    p.minJac = 1.e100;
-    p.maxJac = -1.e100;
-    for (int i = 0; i < toOptimize.size(); ++i) {
-      OptHomMessage("Optimizing a blob %i/%i composed of %4d elements", i+1, toOptimize.size(), toOptimize[i].first.size());
+
+static std::set<MElement*> addBlobLayer(std::set<MElement*> &blob,
+                                        const std::map<MVertex*, std::vector<MElement*> > &vertex2elements)
+{
+
+  std::set<MElement*> layer;
+  const std::set<MElement*> initBlob = blob;
+
+  for (std::set<MElement*>::const_iterator it = initBlob.begin(); it != initBlob.end(); ++it)
+    for (int i = 0; i < (*it)->getNumPrimaryVertices(); ++i) {
+      const std::vector<MElement*> &neighbours = vertex2elements.find((*it)->getVertex(i))->second;
+      for (std::vector<MElement*>::const_iterator itN = neighbours.begin(); itN != neighbours.end(); ++itN)
+          if (blob.insert(*itN).second) layer.insert(*itN);
+    }
+
+  return layer;
+
+}
+
+
+
+static bool detectNewBrokenElement(const std::set<MElement*> &layer,
+                                   const std::set<MElement*> &badasses, OptHomParameters &p){
+
+  for (std::set<MElement*>::iterator it=layer.begin(); it!=layer.end(); it++)
+    if (badasses.find(*it) == badasses.end()) {
+      double jmin, jmax, val;
+      (*it)->scaledJacRange(jmin,jmax);
+      if ((jmin < p.BARRIER_MIN) || (jmax > p.BARRIER_MAX)) return true;
+    }
+
+  return false;
+
+}
+
+
+
+static void optimizeOneByOne(const std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
+                             std::set<MElement*> badElts, OptHomParameters &p, int samples)
+{
+
+  p.SUCCESS = 1;
+  p.minJac = 1.e100;
+  p.maxJac = -1.e100;
+
+  const int initNumBadElts = badElts.size();
+  std::cout << "DBGTT: " << initNumBadElts << " badasses, starting to iterate...\n";
+
+  for (int iBadEl=0; iBadEl<initNumBadElts; iBadEl++) {
+
+    if (badElts.empty()) break;
+
+    // Create blob around worst element and remove it from badElts
+    MElement *worstEl = getWorstElement(badElts,p);
+    badElts.erase(worstEl);
+
+    int nbLayers = p.nbLayers;
+    double distanceFactor = p.distanceFactor;
+
+    int success;
+
+    for (int iterBlob=0; iterBlob<p.maxAdaptBlob; iterBlob++) {
+
+      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()));
+      OptHomMessage("Optimizing primary blob %i (max. %i remaining) composed of %4d elements", iBadEl,
+                    badElts.size(), toOptimize1.size());
       fflush(stdout);
-      OptHOM temp(&entity, toOptimize[i].first, toOptimize[i].second, method);
-std::ostringstream ossI1;
-ossI1 << "initial_" << entity.tag() << "ITER_" << i << ".msh";
-temp.mesh.writeMSH(ossI1.str().c_str());
-      int success = temp.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) {
-        OptHomMessage("Jacobian optimization succeed, starting svd optimization");
-        success = temp.optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN_METRIC, p.BARRIER_MAX, true, samples, p.itMax, p.optPassMax);
+      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()));
+          OptHomMessage("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);
+        }
+        else {
+          OptHomMessage("Primary blob %i did not break new elements, no correction needed", iBadEl);
+          fflush(stdout);
+        }
       }
-      double minJac, maxJac, distMaxBND, distAvgBND;
-      temp.recalcJacDist();
-      temp.getJacDist(minJac, maxJac, distMaxBND, distAvgBND);
-      p.minJac = std::min(p.minJac,minJac);
-      p.maxJac = std::max(p.maxJac,maxJac);
-      temp.mesh.updateGEntityPositions();
+
+      // Measure min and max Jac., update mesh
+      if ((success > 0) || (iterBlob == p.maxAdaptBlob-1)) {
+        double minJac, maxJac, distMaxBND, distAvgBND;
+        opt->recalcJacDist();
+        opt->getJacDist(minJac, maxJac, distMaxBND, distAvgBND);
+        p.minJac = std::min(p.minJac,minJac);
+        p.maxJac = std::max(p.maxJac,maxJac);
+        opt->mesh.updateGEntityPositions();
+      }
+
+//std::ostringstream ossI2;
+//ossI2 << "final_ITER_" << iter << ".msh";
+//temp.mesh.writeMSH(ossI2.str().c_str());
       if (success <= 0) {
-        std::ostringstream ossI2;
-        ossI2 << "final_" << entity.tag() << "ITER_" << i << ".msh";
-        temp.mesh.writeMSH(ossI2.str().c_str());
+        distanceFactor *= p.adaptBlobDistFact;
+        nbLayers *= p.adaptBlobLayerFact;
+        OptHomMessage("Blob %i failed (adapt #%i), adapting with increased size", iBadEl, iterBlob);
+        if (iterBlob == p.maxAdaptBlob-1) {
+          std::ostringstream ossI2;
+          ossI2 << "final_" << iBadEl << ".msh";
+          opt->mesh.writeMSH(ossI2.str().c_str());
+        }
       }
-      //#pragma omp critical
-      p.SUCCESS = std::min(p.SUCCESS, success);
+      else break;
+
     }
 
-    double DTF = Cpu()-tf1;
-    if (p.SUCCESS == 1)
-      OptHomMessage("Optimization succeeded for entity %d (CPU %g sec)", entity.tag(), DTF);
-    else if (p.SUCCESS == 0)
-      OptHomMessage("Warning : Model entity %4d has all jacobians positive but not all in the range (CPU %g sec)", entity.tag(), DTF);
-    else if (p.SUCCESS == -1)
-      OptHomMessage("Error : Model entity %4d has still negative jacobians (CPU %g sec)", entity.tag(), DTF);
+    //#pragma omp critical
+    p.SUCCESS = std::min(p.SUCCESS, success);
+
+  }
+
+}
+
+
+
+//static void optimizeOneByOneTest(const std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
+//                                 std::set<MElement*> badasses, OptHomParameters &p, int method, int samples)
+//{
+//
+//  p.SUCCESS = 1;
+//  p.minJac = 1.e100;
+//  p.maxJac = -1.e100;
+//
+//  const int initNumBadAsses = badasses.size();
+//  std::cout << "DBGTT: " << initNumBadAsses << " badasses, starting to iterate...\n";
+//
+//  for (int iter=0; iter<initNumBadAsses; iter++) {
+//
+//    if (badasses.empty()) break;
+//
+//    // Create blob around worst element
+//    MElement *worstEl = getWorstElement(badasses,p);
+//
+//    const int maxRepeatBlob = 1;
+//    const int repeatBlobLayerFact = 2;
+//    const int repeatBlobDistFact = 2.;
+//
+//    int nbLayers = p.nbLayers, minNbLayers = 1;
+//    double distanceFactor = p.distanceFactor;
+//
+//    int success;
+//
+//    for (int iterBlob=0; iterBlob<maxRepeatBlob; iterBlob++) {
+//
+//      // TODO: First opt. with only 1st-order bnd. vertices fixed,
+//      //       then add external layer and opt. with all bnd. vert. fixed
+////      std::set<MElement*> toOptimizePrim = getSurroundingBlob(worstEl, nbLayers, vertex2elements, distanceFactor, 0);
+////      std::set<MVertex*> toFix = getBndVertices3D(toOptimizePrim, vertex2elements);
+//      std::set<MElement*> toOptimizePrim = getSurroundingBlob3D(worstEl, nbLayers, vertex2elements, distanceFactor);
+//      std::set<MVertex*> toFix = getAllBndVertices(toOptimizePrim, vertex2elements);
+//      badasses.erase(worstEl);
+//      std::set<MElement*> toOptimize;
+//      std::set_difference(toOptimizePrim.begin(),toOptimizePrim.end(),badasses.begin(),badasses.end(),
+//                          std::inserter(toOptimize, toOptimize.end()));
+//
+//      // Optimize blob
+//      OptHomMessage("Optimizing blob %i (max. %i remaining) composed of %4d elements", iter,
+//                    badasses.size(), toOptimize.size());
+//      fflush(stdout);
+//      OptHOM temp(toOptimize, toFix, method);
+////std::cout << "DBGTT: toOptimize:\n";
+////for (std::set<MElement*>::iterator it=toOptimize.begin(); it!=toOptimize.end(); it++ ) {
+////  SPoint3 bar = (*it)->barycenter();
+////  std::cout << "DBGTT:   -> (" << bar.x() << "," << bar.y() << "," << bar.z() << ")\n";
+////  std::cout << "DBGTT:   -> (" << (*it)->getVertex(0)->onWhat()->dim();
+////  for (int j=1; j<(*it)->getNumVertices(); j++) std::cout << "," << (*it)->getVertex(j)->onWhat()->dim();
+////  std::cout << ")\n";
+////}
+////std::cout << "DBGTT: toFix:\n";
+////for (std::set<MVertex*>::iterator it=toFix.begin(); it!=toFix.end(); it++ )
+////  std::cout << "DBGTT:   -> (" << (*it)->x() << "," << (*it)->y() << "," << (*it)->z() << ")\n";
+//std::ostringstream ossI1;
+//ossI1 << "initial_ITER_" << iter << ".msh";
+//temp.mesh.writeMSH(ossI1.str().c_str());
+//      success = temp.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) {
+//        OptHomMessage("Jacobian optimization succeed, starting svd optimization");
+//        success = temp.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
+//      double minJac, maxJac, distMaxBND, distAvgBND;
+//      temp.recalcJacDist();
+//      temp.getJacDist(minJac, maxJac, distMaxBND, distAvgBND);
+//      p.minJac = std::min(p.minJac,minJac);
+//      p.maxJac = std::max(p.maxJac,maxJac);
+//      temp.mesh.updateGEntityPositions();
+//
+////std::ostringstream ossI2;
+////ossI2 << "final_ITER_" << iter << ".msh";
+////temp.mesh.writeMSH(ossI2.str().c_str());
+//      if (success <= 0) {
+//        distanceFactor *= repeatBlobDistFact;
+//        nbLayers *= repeatBlobLayerFact;
+//        OptHomMessage("Blob %i failed (repeat %i), repeating with increased size", iter, iterBlob);
+//        if (iterBlob == maxRepeatBlob-1) {
+//          std::ostringstream ossI2;
+//          ossI2 << "final_ITER_" << iter << ".msh";
+//          temp.mesh.writeMSH(ossI2.str().c_str());
+//        }
+//      }
+//      else break;
+//
+//    }
+//
+//    //#pragma omp critical
+//    p.SUCCESS = std::min(p.SUCCESS, success);
+//
+//  }
+//
+//}
+//
+//
+//
+//static void optimizePropagate(const std::map<MVertex*, std::vector<MElement*> > &vertex2elements,
+//                              std::set<MElement*> badasses, OptHomParameters &p, int method, int samples)
+//{
+//
+//  p.SUCCESS = 1;
+//  p.minJac = 1.e100;
+//  p.maxJac = -1.e100;
+//
+//  const int initNumBadAsses = badasses.size();
+//  std::cout << "DBGTT: " << initNumBadAsses << " badasses, starting to iterate...\n";
+//
+//  std::set<MVertex*> toFix;
+//  std::set<MElement*> done;
+//
+//  while (!badasses.empty()) {
+//
+//    OptHomMessage("Optimizing first of %i bad elements, %i already done", badasses.size(), done.size());
+//    fflush(stdout);
+////char dum;
+////std::cin >> dum;
+//
+//    MElement *el = *badasses.begin();
+//
+//    // Optimize element
+//    std::set<MElement*> blob;
+//    blob.insert(el);
+//    OptHOM temp(blob, toFix, method);
+////std::ostringstream ossI1;
+////ossI1 << "initial.msh";
+////temp.mesh.writeMSH(ossI1.str().c_str());
+//    int success = temp.optimize(p.weightFixed, p.weightFree, p.BARRIER_MIN, p.BARRIER_MAX,
+//                                false, samples, p.itMax, p.optPassMax);
+//    if (success <= 0) {
+//      OptHomMessage("Error, element optimization failed");
+//      fflush(stdout);
+//      std::ostringstream ossI1;
+//      ossI1 << "failed_" << el->getNum() << ".msh";
+//      temp.mesh.writeMSH(ossI1.str().c_str());
+//    }
+////std::ostringstream ossI2;
+////ossI2 << "final.msh";
+////temp.mesh.writeMSH(ossI2.str().c_str());
+//
+//    // Move element from list of bad elements to liste of elements done
+//    badasses.erase(el);
+//    done.insert(el);
+//std::cout << "DBGTT: Removing el. " << el->getNum() << "\n";
+//
+//    // Measure success, min and max Jac and update mesh
+//    double minJac, maxJac, distMaxBND, distAvgBND;
+//    temp.recalcJacDist();
+//    temp.getJacDist(minJac, maxJac, distMaxBND, distAvgBND);
+//    p.minJac = std::min(p.minJac,minJac);
+//    p.maxJac = std::max(p.maxJac,maxJac);
+//    p.SUCCESS = std::min(p.SUCCESS, success);
+//    temp.mesh.updateGEntityPositions();
+//
+//    // Fix elements nodes
+//    for (int i = 0; i < el->getNumVertices(); ++i) toFix.insert(el->getVertex(i));
+//
+//    // Add elements that have been broken by optimization to list of bad elements
+//    std::set<MElement*> layer;
+//    for (int i = 0; i < el->getNumPrimaryVertices(); ++i) {
+//      const std::vector<MElement*> &neighbours = vertex2elements.find(el->getVertex(i))->second;
+//      for (size_t k = 0; k < neighbours.size(); ++k) layer.insert(neighbours[k]);
+//    }
+//    for (std::set<MElement*>::const_iterator it = layer.begin(); it != layer.end(); ++it)
+//      if (done.find(*it) == done.end()) {           // Add only if not done
+//        double jmin, jmax;
+//        (*it)->scaledJacRange(jmin, jmax);
+//        if (p.BARRIER_MIN_METRIC > 0) jmax = jmin;
+//        if (jmin < p.BARRIER_MIN || jmax > p.BARRIER_MAX) {
+//std::cout << "DBGTT: Adding el. " << (*it)->getNum() << "\n";
+//          badasses.insert(*it);
+//        }
+//      }
+//
+//  }
+//
+//}
+
+
+
+void HighOrderMeshOptimizer (GModel *gm, OptHomParameters &p)
+{
+
+  double t1 = Cpu();
+
+  int samples = 30;
+
+  double tf1 = Cpu();
+
+  OptHomMessage("High order mesh optimizer starts");
+  std::vector<GEntity*> entities;
+  gm->getEntities(entities);
 
+  std::map<MVertex*, std::vector<MElement *> > vertex2elements;
+  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;
+    std::cout << "Computing connectivity and bad elements for entity " << entity->tag() << " ...\n";
+    calcVertex2Elements(p.dim,entity,vertex2elements);                       // Compute vert. -> elt. connectivity
+//std::cout << " -> " << entity->getNumMeshElements()
+//            << " elements, vertex2elements.size() = " << vertex2elements.size() << "\n";
+    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);
+        if (p.BARRIER_MIN_METRIC > 0) jmax = jmin;
+        if (jmin < p.BARRIER_MIN || jmax > p.BARRIER_MAX) badasses.insert(el);
+      }
+    }
   }
 
+  if (p.strategy == 0) optimizeConnectedBlobs(vertex2elements, badasses, p, samples);
+  else optimizeOneByOne(vertex2elements, badasses, p, samples);
+
+  double DTF = Cpu()-tf1;
+  if (p.SUCCESS == 1)
+    OptHomMessage("Optimization succeeded (CPU %g sec)", DTF);
+  else if (p.SUCCESS == 0)
+    OptHomMessage("Warning : All jacobians positive but not all in the range (CPU %g sec)", DTF);
+  else if (p.SUCCESS == -1)
+    OptHomMessage("Error : Still negative jacobians (CPU %g sec)", DTF);
+
   double t2 = Cpu();
   p.CPU = t2-t1;
 
diff --git a/contrib/HighOrderMeshOptimizer/OptHomRun.h b/contrib/HighOrderMeshOptimizer/OptHomRun.h
index 1e1c3a082c12031bd23014888a5a1c081dcb39e5..2382923312a25b0ddce40c373af01dc368c93057 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomRun.h
+++ b/contrib/HighOrderMeshOptimizer/OptHomRun.h
@@ -16,9 +16,14 @@ struct OptHomParameters {
   int optPassMax ; // max number of optimization passes
   double TMAX ; // max CPU time allowed
   bool onlyVisible ; // apply optimization to visible entities ONLY
-  double DistanceFactor; // filter elements such that no elements further away than
+  double distanceFactor; // filter elements such that no elements further away than
                          // DistanceFactor times the max distance to straight sided version of an element are optimized
-  int method ;  // how jacobians are computed and if points can move on boundaries
+  bool fixBndNodes;  // how jacobians are computed and if points can move on boundaries
+  int strategy;       // 0 = connected blobs, 1 = adaptive one-by-one
+  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
+
   // OUTPUT ------>
   int SUCCESS ; // 0 --> success , 1 --> Not converged
   double minJac, maxJac; // after optimization, range of jacobians
@@ -29,8 +34,9 @@ struct OptHomParameters {
 
   OptHomParameters ()
   // default values
-  : BARRIER_MIN_METRIC (-1.), BARRIER_MIN (0.1), BARRIER_MAX (2.0) , weightFixed (1.e6),  weightFree (1.e2),
-    nbLayers (6) , dim(3) , itMax(10000), onlyVisible(true), DistanceFactor(12), method(1)
+  : 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.)
   {
   }
 };
diff --git a/contrib/HighOrderMeshOptimizer/ParamCoord.cpp b/contrib/HighOrderMeshOptimizer/ParamCoord.cpp
index cdb3e3c8db07d182b61f63b46123bd7cd430a5d0..b8f2518e194f6418097daa71a99ccd85b03540aa 100644
--- a/contrib/HighOrderMeshOptimizer/ParamCoord.cpp
+++ b/contrib/HighOrderMeshOptimizer/ParamCoord.cpp
@@ -6,53 +6,6 @@
 
 
 
-ParamCoordSurf::ParamCoordSurf(GEntity *ge)
-{
-  if ((ge->dim() == 2) && (ge->geomType() != GEntity::DiscreteSurface)) _gf = static_cast<GFace*>(ge);
-  else std::cout << "ERROR: Using surface parametric coordinates with non-surface geometric entity" << std::endl;
-}
-
-
-
-SPoint3 ParamCoordSurf::getUvw(MVertex* vert)
-{
-  SPoint2 p;
-  reparamMeshVertexOnFace(vert,_gf,p);
-  return SPoint3(p[0],p[1],0.);
-}
-
-
-
-SPoint3 ParamCoordSurf::uvw2Xyz(MVertex* vert, const SPoint3 &uvw)
-{
-  GPoint gp = _gf->point(uvw[0],uvw[1]);
-  return SPoint3(gp.x(),gp.y(),gp.z());
-}
-
-
-
-void ParamCoordSurf::gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw)
-{
-  Pair<SVector3,SVector3> der = _gf->firstDer(SPoint2(uvw[0],uvw[1]));
-  gUvw[0] = gXyz.x() * der.first().x() + gXyz.y() * der.first().y() + gXyz.z() * der.first().z();
-  gUvw[1] = gXyz.x() * der.second().x() + gXyz.y() * der.second().y() + gXyz.z() * der.second().z();
-}
-
-
-
-void ParamCoordSurf::gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw)
-{
-  Pair<SVector3,SVector3> der = _gf->firstDer(SPoint2(uvw[0],uvw[1]));
-  std::vector<SPoint3>::iterator itUvw=gUvw.begin();
-  for (std::vector<SPoint3>::const_iterator itXyz=gXyz.begin(); itXyz != gXyz.end(); itXyz++) {
-    (*itUvw)[0] = itXyz->x() * der.first().x() + itXyz->y() * der.first().y() + itXyz->z() * der.first().z();
-    (*itUvw)[1] = itXyz->x() * der.second().x() + itXyz->y() * der.second().y() + itXyz->z() * der.second().z();
-    itUvw++;
-  }
-}
-
-
-
 SPoint3 ParamCoordParent::getUvw(MVertex* vert)
 {
 
diff --git a/contrib/HighOrderMeshOptimizer/ParamCoord.h b/contrib/HighOrderMeshOptimizer/ParamCoord.h
index cb3db207db7cd24f2381a02e607b33c24b25db7b..1927505508f26b6f4ec9729176314ebbfd46cc91 100644
--- a/contrib/HighOrderMeshOptimizer/ParamCoord.h
+++ b/contrib/HighOrderMeshOptimizer/ParamCoord.h
@@ -64,26 +64,6 @@ public:
 
 
 
-class ParamCoordSurf : public ParamCoord
-{
-
-public:
-
-  ParamCoordSurf(GEntity *ge);
-  int nCoord(MVertex* vert) { return 2; }
-  virtual SPoint3 getUvw(MVertex* vert);
-  virtual SPoint3 uvw2Xyz(MVertex* vert, const SPoint3 &uvw);
-  virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const SPoint3 &gXyz, SPoint3 &gUvw);
-  virtual void gXyz2gUvw(MVertex* vert, const SPoint3 &uvw, const std::vector<SPoint3> &gXyz, std::vector<SPoint3> &gUvw);
-
-private:
-
-  GFace *_gf;
-
-};
-
-
-
 class ParamCoordParent : public ParamCoord
 {