diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.cpp b/contrib/HighOrderMeshOptimizer/OptHOM.cpp
index 18fc68ee7094a2ed6da582d8a80ad5637abf14ed..807a5ed6756747320ca24db7ddc57f0bd72d49a8 100644
--- a/contrib/HighOrderMeshOptimizer/OptHOM.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHOM.cpp
@@ -15,10 +15,10 @@
 
 
 // Constructor
-OptHOM::OptHOM(GEntity *ge, std::set<MVertex*> & toFix, int method) :
-       mesh(ge, toFix, method)
+OptHOM::OptHOM(GEntity *ge,const std::set<MElement*> &els, std::set<MVertex*> & toFix, int method) :
+       mesh(ge, els, toFix, method)
 {
-};
+}
 
 // Contribution of the element Jacobians to the objective function value and gradients (2D version)
 bool OptHOM::addJacObjGrad(double &Obj, alglib::real_1d_array &gradObj)
diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.h b/contrib/HighOrderMeshOptimizer/OptHOM.h
index 29880cfb16527891d39787e0e5eb4f1bd31250f1..39e49aabf3bf35a5cbb081bb81f53b6f6e097b61 100644
--- a/contrib/HighOrderMeshOptimizer/OptHOM.h
+++ b/contrib/HighOrderMeshOptimizer/OptHOM.h
@@ -21,7 +21,7 @@ public:
 
   Mesh mesh;
 
-  OptHOM(GEntity *gf, std::set<MVertex*> & toFix, int method);
+  OptHOM(GEntity *gf, const std::set<MElement*> &els, std::set<MVertex*> & toFix, int method);
   void recalcJacDist();
   inline void getJacDist(double &minJ, double &maxJ, double &maxD, double &avgD);
   int optimize(double lambda, double lambda2, double barrier, int pInt, int itMax);  // optimize one list of elements
diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
index 3b03ad6ecf1fef99a6ce8bcf1227f734c1e21b84..0a598eeb11ce2e92f0938c347445dce57b5a11de 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.cpp
@@ -65,7 +65,7 @@ std::vector<double> Mesh::computeJB(const polynomialBasis *lagrange, const bezie
 
 
 
-Mesh::Mesh(GEntity *ge, std::set<MVertex*> &toFix, int method) :
+Mesh::Mesh(GEntity *ge,const std::set<MElement*> &elements, std::set<MVertex*> & toFix, int method):
     _ge(ge)
 {
   _dim = _ge->dim();
@@ -93,14 +93,16 @@ Mesh::Mesh(GEntity *ge, std::set<MVertex*> &toFix, int method) :
 
   // Initialize elements, vertices, free vertices and element->vertices connectivity
   _nPC = 0;
-  _el.resize(_ge->getNumMeshElements());
-  _el2FV.resize(_ge->getNumMeshElements());
-  _el2V.resize(_ge->getNumMeshElements());
-  _nBezEl.resize(_ge->getNumMeshElements());
-  _nNodEl.resize(_ge->getNumMeshElements());
-  _indPCEl.resize(_ge->getNumMeshElements());
-  for (int iEl = 0; iEl < nEl(); iEl++) {
-    MElement *el = _ge->getMeshElement(iEl);
+  int nElements = elements.size();
+  _el.resize(nElements);
+  _el2FV.resize(nElements);
+  _el2V.resize(nElements);
+  _nBezEl.resize(nElements);
+  _nNodEl.resize(nElements);
+  _indPCEl.resize(nElements);
+  int iEl = 0;
+  for (std::set<MElement *>::iterator it  = elements.begin(); it != elements.end(); ++it, ++iEl) {
+    MElement *el = *it;
     _el[iEl] = el;
     const polynomialBasis *lagrange = el->getFunctionSpace();
     const bezierBasis *bezier = JacobianBasis::find(lagrange->type)->bezier;
diff --git a/contrib/HighOrderMeshOptimizer/OptHomMesh.h b/contrib/HighOrderMeshOptimizer/OptHomMesh.h
index 2e2741fbb413a9002338c3170577b4339357fe5b..5636311267e6c88fd5d5722f072328adb6c62ccb 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomMesh.h
+++ b/contrib/HighOrderMeshOptimizer/OptHomMesh.h
@@ -17,7 +17,7 @@ class Mesh
 
 public:
 
-  Mesh(GEntity *ge, std::set<MVertex*> & toFix, int method);
+  Mesh(GEntity *ge,const std::set<MElement*> &els, std::set<MVertex*> & toFix, int method);
 
   inline const int &nPC() { return _nPC; }
   inline int nVert() { return _vert.size(); }
diff --git a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
index 1bac92a11bb759c05a7cacef9bdd1e1ca7087e12..0c171c63081216dcd22c9ac48865e4f16664e267 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
@@ -11,162 +11,106 @@
 #include "highOrderTools.h"
 #include "OptHomMesh.h"
 #include "OptHOM.h"
+#include <stack>
 
-std::set<MVertex*> filter2D(GFace *gf, int nbLayers, double _qual, bool onlytheworst = false)
+static std::set<MVertex*> filter(GEntity *gf, int nbLayers, double _qual, std::set<MElement*> &elements, bool onlytheworst = false)
 {
+  elements.clear();
   std::set<MVertex*> touched;
   std::set<MVertex*> not_touched;
-  std::set<MTriangle *> ts;
-  std::set<MQuadrangle *> qs;
-
-  if (onlytheworst) {
-    double minQ = 1.e22;
-    MQuadrangle *worst;
-    for (int i = 0; i < gf->quadrangles.size(); i++) {
-      MQuadrangle *q = gf->quadrangles[i];
-      double Q = q->distoShapeMeasure();
+  double minQ = 1e22;
+  MElement *worst;
+  for (unsigned int i = 0; i < gf->getNumMeshElements(); ++i) {
+    MElement *el = gf->getMeshElement(i);
+    double Q = el->distoShapeMeasure();
+    if (Q < _qual || Q > 1.01) {
       if (Q < minQ) {
-        worst = q;
+        worst = el;
         minQ = Q;
       }
-    }
-    qs.insert(worst);
-    for (int j = 0; j < worst->getNumVertices(); j++)
-      touched.insert(worst->getVertex(j));
-  }
-
-  else {
-    for (int i = 0; i < gf->triangles.size(); i++) {
-      MTriangle *t = gf->triangles[i];
-      double Q = t->distoShapeMeasure();
-      if (Q < _qual || Q > 1.01) {
-        ts.insert(t);
-        for (int j = 0; j < t->getNumVertices(); j++)
-          touched.insert(t->getVertex(j));
-      }
-    }
-    for (int i = 0; i < gf->quadrangles.size(); i++) {
-      MQuadrangle *q = gf->quadrangles[i];
-      double Q = q->distoShapeMeasure();
-      if (Q < _qual || Q > 1.0) {
-        qs.insert(q);
-        for (int j = 0; j < q->getNumVertices(); j++)
-          touched.insert(q->getVertex(j));
+      if (!onlytheworst) {
+        elements.insert(el);
       }
     }
   }
-  //  printf("FILTER2D : %d bad triangles found among %6d\n", ts.size(), gf->triangles.size());
-  //  printf("FILTER2D : %d bad quads     found among %6d\n", qs.size(), gf->quadrangles.size());
+  if (onlytheworst) {
+    elements.insert(worst);
+  }
+  for (std::set<MElement*>::iterator it = elements.begin(); it != elements.end(); ++it) {
+    MElement &el = **it;
+    for (int j = 0; j < el.getNumVertices(); j++)
+      touched.insert(el.getVertex(j));
+  }
+  Msg::Info("FILTER2D : %d bad elements found among %6d\n", elements.size(), gf->getNumMeshElements());
 
   // add layers of elements around bad elements
   for (int layer = 0; layer < nbLayers; layer++) {
-    for (int i = 0; i < gf->triangles.size(); i++) {
-      MTriangle *t = gf->triangles[i];
-      bool add_ = false;
-      for (int j = 0; j < t->getNumVertices(); j++) {
-        if (touched.find(t->getVertex(j)) != touched.end()) {
-          add_ = true;
+    for (unsigned int i = 0; i < gf->getNumMeshElements(); ++i) {
+      MElement *el = gf->getMeshElement(i);
+      for (int j = 0; j < el->getNumVertices(); ++j) {
+        if (touched.find(el->getVertex(j)) != touched.end()) {
+          elements.insert(el);
+          break;
         }
       }
-      if (add_) ts.insert(t);
     }
-    for (int i = 0; i < gf->quadrangles.size(); i++) {
-      bool add_ = false;
-      MQuadrangle *q = gf->quadrangles[i];
-      for (int j = 0; j < q->getNumVertices(); j++) {
-        if (touched.find(q->getVertex(j)) != touched.end()) {
-          add_ = true;
-        }
-      }
-      if (add_) qs.insert(q);
-    }
-
-    for (std::set<MTriangle*>::iterator it = ts.begin(); it != ts.end(); ++it) {
-      for (int j = 0; j < (*it)->getNumVertices(); j++) {
-        touched.insert((*it)->getVertex(j));
-      }
-    }
-    for (std::set<MQuadrangle*>::iterator it = qs.begin(); it != qs.end(); ++it) {
-      for (int j = 0; j < (*it)->getNumVertices(); j++) {
-        touched.insert((*it)->getVertex(j));
+    for (std::set<MElement *>::iterator it = elements.begin(); it != elements.end(); ++it) {
+      MElement &el = **it;
+      for (int j = 0; j < el.getNumVertices(); ++j) {
+        touched.insert(el.getVertex(j));
       }
     }
   }
-
-  for (int i = 0; i < gf->triangles.size(); i++) {
-    if (ts.find(gf->triangles[i]) == ts.end()) {
-      for (int j = 0; j < gf->triangles[i]->getNumVertices(); j++) {
-        if (touched.find(gf->triangles[i]->getVertex(j)) != touched.end()) not_touched.insert(gf->triangles[i]->getVertex(j));
+  for (unsigned int i = 0; i < gf->getNumMeshElements(); ++i) {
+    MElement &el = *gf->getMeshElement(i);
+    for (int j = 0; j < el.getNumVertices(); ++j) {
+      if (touched.find(el.getVertex(j)) == touched.end()) {
+        not_touched.insert(el.getVertex(j));
       }
     }
   }
-  for (int i = 0; i < gf->quadrangles.size(); i++) {
-    if (qs.find(gf->quadrangles[i]) == qs.end()) {
-      for (int j = 0; j < gf->quadrangles[i]->getNumVertices(); j++) {
-        if (touched.find(gf->quadrangles[i]->getVertex(j)) != touched.end()) not_touched.insert(gf->quadrangles[i]->getVertex(j));
-      }
-    }
-  }
-
-  gf->triangles.clear();
-  gf->quadrangles.clear();
-  gf->triangles.insert(gf->triangles.begin(), ts.begin(), ts.end());
-  gf->quadrangles.insert(gf->quadrangles.begin(), qs.begin(), qs.end());
 
   //  printf("FILTER2D : AFTER FILTERING %d triangles %d quads remain %d nodes on the boundary\n", gf->triangles.size(), gf->quadrangles.size(), not_touched.size());
   return not_touched;
 }
 
-std::set<MVertex*> filter3D(GRegion *gr, int nbLayers, double _qual)
+static std::vector<std::set<MElement*> > splitConnex(const std::set<MElement*> &in)
 {
-  std::set<MVertex*> touched;
-  std::set<MVertex*> not_touched;
-  std::set<MTetrahedron *> ts;
-  for (int i = 0; i < gr->tetrahedra.size(); i++) {
-    MTetrahedron *t = gr->tetrahedra[i];
-    if (t->distoShapeMeasure() < _qual) {
-      ts.insert(t);
-      for (int j = 0; j < t->getNumVertices(); j++)
-        touched.insert(t->getVertex(j));
+  std::map<int, std::vector<int> > vertex2elements;
+  std::vector<MElement *> elements(in.begin(), in.end());
+  for (size_t i = 0; i < elements.size(); ++i) {
+    for (int j = 0; j < elements[i]->getNumPrimaryVertices(); ++j) {
+      vertex2elements[elements[i]->getVertex(j)->getNum()].push_back(i);
     }
-//    if (ts.size() == 1) break;
   }
-
-  //  printf("FILTER3D : %d bad tets found among %6d\n", ts.size(), gr->tetrahedra.size());
-
-  // add layers of elements around bad elements
-  for (int layer = 0; layer < nbLayers; layer++) {
-    for (int i = 0; i < gr->tetrahedra.size(); i++) {
-      MTetrahedron *t = gr->tetrahedra[i];
-      bool add_ = false;
-      for (int j = 0; j < t->getNumVertices(); j++) {
-        if (touched.find(t->getVertex(j)) != touched.end()) {
-          add_ = true;
+  std::vector<int> colors(elements.size(), -1);
+  int color = 0;
+  for (size_t i = 0; i < elements.size(); ++i) {
+    if (colors[i] == -1) {
+      colors[i] = color;
+      std::stack<int> todo;
+      todo.push(i);
+      while (! todo.empty()) {
+        int top = todo.top();
+        todo.pop();
+        for (int j = 0; j < elements[top]->getNumPrimaryVertices(); ++j) {
+          const std::vector<int> &neighbours = vertex2elements[elements[top]->getVertex(j)->getNum()];
+          for (size_t k = 0; k < neighbours.size(); ++k) {
+            if (colors[neighbours[k]] == -1) {
+              colors[neighbours[k]] = color;
+              todo.push(neighbours[k]);
+            }
+          }
         }
       }
-      if (add_) ts.insert(t);
-    }
-
-    for (std::set<MTetrahedron*>::iterator it = ts.begin(); it != ts.end(); ++it) {
-      for (int j = 0; j < (*it)->getNumVertices(); j++) {
-        touched.insert((*it)->getVertex(j));
-      }
+      color ++;
     }
   }
-
-  for (int i = 0; i < gr->tetrahedra.size(); i++) {
-    if (ts.find(gr->tetrahedra[i]) == ts.end()) {
-      for (int j = 0; j < gr->tetrahedra[i]->getNumVertices(); j++) {
-        if (touched.find(gr->tetrahedra[i]->getVertex(j)) != touched.end()) not_touched.insert(gr->tetrahedra[i]->getVertex(j));
-      }
-    }
+  std::vector<std::set<MElement*> > splitted(color);
+  for (size_t i = 0; i < elements.size(); ++i) {
+    splitted[colors[i]].insert(elements[i]);
   }
-
-  gr->tetrahedra.clear();
-  gr->tetrahedra.insert(gr->tetrahedra.begin(), ts.begin(), ts.end());
-
-  //  printf("FILTER3D : AFTER FILTERING %d tets remain %d nodes on the boundary\n", gr->tetrahedra.size(), not_touched.size());
-  return not_touched;
+  return splitted;
 }
 
 void HighOrderMeshOptimizer (GModel *gm, OptHomParameters &p)
@@ -188,36 +132,29 @@ void HighOrderMeshOptimizer (GModel *gm, OptHomParameters &p)
     for (GModel::fiter itf = gm->firstFace(); itf != gm->lastFace(); ++itf) {
 
       if (p.onlyVisible && !(*itf)->getVisibility())continue;
-      std::vector<MTriangle*> tris = (*itf)->triangles;
-      std::vector<MQuadrangle*> quas = (*itf)->quadrangles;
-      std::set<MVertex*> toFix = filter2D(*itf, p.nbLayers, p.BARRIER);
-      OptHOM *temp = new OptHOM(*itf, toFix, method);
-      (*itf)->triangles = tris;
-      (*itf)->quadrangles = quas;
-
-      double distMaxBND, distAvgBND, minJac, maxJac;
-      temp->recalcJacDist();
-      temp->getJacDist(minJac, maxJac, distMaxBND, distAvgBND);
-      //      std::ostringstream ossI;
-      //      ossI << "initial_" << (*itf)->tag() << ".msh";
-      //      temp->mesh.writeMSH(ossI.str().c_str());
-      //      printf("--> Mesh Loaded for GFace %d :  minJ %12.5E -- maxJ %12.5E\n", (*itf)->tag(), minJac, maxJac);
-      if (distMaxBND < 1.e-8 * SIZE && minJac > p.BARRIER) continue;
-      p.SUCCESS = temp->optimize(p.weightFixed, p.weightFree, p.BARRIER, samples, p.itMax);
-      temp->getJacDist(p.minJac, p.maxJac, distMaxBND, distAvgBND);
-      temp->mesh.updateGEntityPositions();
-      //      std::ostringstream ossF;
-      //      ossF << "final_" << (*itf)->tag() << ".msh";
-      //      temp->mesh.writeMSH(ossF.str().c_str());
+      std::set<MElement*> elements;
+      std::set<MVertex*> toFix = filter(*itf, p.nbLayers, p.BARRIER, elements);
+      std::vector<std::set<MElement*> > splitted = splitConnex(elements);
+      for (size_t i = 0; i < splitted.size(); ++i) {
+        OptHOM *temp = new OptHOM(*itf, splitted[i], toFix, method);
+        double distMaxBND, distAvgBND, minJac, maxJac;
+        temp->recalcJacDist();
+        temp->getJacDist(minJac, maxJac, distMaxBND, distAvgBND);
+        if (distMaxBND < 1.e-8 * SIZE && minJac > p.BARRIER) continue;
+        p.SUCCESS = temp->optimize(p.weightFixed, p.weightFree, p.BARRIER, samples, p.itMax);
+        temp->getJacDist(p.minJac, p.maxJac, distMaxBND, distAvgBND);
+        temp->mesh.updateGEntityPositions();
+        delete temp;
+      }
     }
   }
   else if (p.dim == 3) {
     for (GModel::riter itr = gm->firstRegion(); itr != gm->lastRegion(); ++itr) {
       if (p.onlyVisible && !(*itr)->getVisibility())continue;
       std::vector<MTetrahedron*> tets = (*itr)->tetrahedra;
-      std::set<MVertex*> toFix;
-      filter3D(*itr, p.nbLayers, p.BARRIER);
-      OptHOM *temp = new OptHOM(*itr, toFix, method);
+      std::set<MElement*> elements;
+      std::set<MVertex*> toFix = filter(*itr, p.nbLayers, p.BARRIER, elements);
+      OptHOM *temp = new OptHOM(*itr, elements, toFix, method);
       double distMaxBND, distAvgBND, minJac, maxJac;
       temp->recalcJacDist();
       temp->getJacDist(minJac, maxJac, distMaxBND, distAvgBND);