diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp
index 7dbb2746ecb654510e215afa3db32bdef26ff5bc..f52029259fffe7ee9019f11c4cd321da2bf7d6e3 100644
--- a/Common/CommandLine.cpp
+++ b/Common/CommandLine.cpp
@@ -279,6 +279,13 @@ void GetOptions(int argc, char *argv[])
         CTX::instance()->mesh.optimize = 1;
         i++;
       }
+      else if(!strcmp(argv[i] + 1, "bunin")) {
+        i++;
+        if(argv[i])
+          CTX::instance()->mesh.bunin = atoi(argv[i++]);
+        else
+          Msg::Fatal("Missing cavity size in bunin optimization");
+      }
       else if(!strcmp(argv[i] + 1, "optimize_netgen")) {
         CTX::instance()->mesh.optimizeNetgen = 1;
         i++;
diff --git a/Common/Context.h b/Common/Context.h
index 0bc5f13fd6d097888210c929b789948de8ed0020..1bf75b31d7c24878d53c559807fb117b32c3de6d 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -41,6 +41,7 @@ struct contextMeshOptions {
   int multiplePasses;
   int cgnsImportOrder;
   std::map<int,int> algo2d_per_face;
+  int bunin;
 };
 
 struct contextGeometryOptions {
diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h
index 84a60032de493619ac99334da8d4be84b8d7f898..5e02118424c102d41c01c474204bc03ab9b54bba 100644
--- a/Common/DefaultOptions.h
+++ b/Common/DefaultOptions.h
@@ -785,6 +785,8 @@ StringXNumber MeshOptions_Number[] = {
     "Field format for Nastran BDF files (0=free, 1=small, 2=large)" },
   { F|O, "Binary" , opt_mesh_binary , 0. ,
     "Write mesh files in binary format (if possible)" },
+  { F|O, "Bunin" , opt_mesh_bunin , 0. ,
+    "Apply Bunin optimization on quad meshes (the parameter is the maximal size of a cavity that may be remeshed)" },
 
   { F|O, "CgnsImportOrder" , opt_mesh_cgns_import_order , 1. ,
    "Enable the creation of high-order mesh from CGNS structured meshes."
diff --git a/Common/Options.cpp b/Common/Options.cpp
index 58a8931307ee1d26a6c5f0684bf82fdd739aba12..a0db5004b570ddd26fe1d6dceee52758c7e2fde4 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -4883,6 +4883,13 @@ double opt_mesh_binary(OPT_ARGS_NUM)
   return CTX::instance()->mesh.binary;
 }
 
+double opt_mesh_bunin(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET)
+    CTX::instance()->mesh.bunin = (int)val;
+  return CTX::instance()->mesh.bunin;
+}
+
 double opt_mesh_bdf_field_format(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET){
diff --git a/Common/Options.h b/Common/Options.h
index 3a483bd6c62b88186d6fe6ecf483707b4e611178..da13f2a027a163e067af0c1032be2a352c024524 100644
--- a/Common/Options.h
+++ b/Common/Options.h
@@ -386,6 +386,7 @@ double opt_mesh_partition_qua_weight(OPT_ARGS_NUM);
 double opt_mesh_partition_tet_weight(OPT_ARGS_NUM);
 double opt_mesh_partition_tri_weight(OPT_ARGS_NUM);
 double opt_mesh_binary(OPT_ARGS_NUM);
+double opt_mesh_bunin(OPT_ARGS_NUM);
 double opt_mesh_bdf_field_format(OPT_ARGS_NUM);
 double opt_mesh_nb_smoothing(OPT_ARGS_NUM);
 double opt_mesh_algo2d(OPT_ARGS_NUM);
diff --git a/Mesh/Generator.cpp b/Mesh/Generator.cpp
index e3218406c2ca3af2ff888466a07c4b05eff87539..c22b42aabc4694641b8597ded5997aaaf090a935 100644
--- a/Mesh/Generator.cpp
+++ b/Mesh/Generator.cpp
@@ -613,8 +613,11 @@ void RecombineMesh(GModel *m)
   Msg::StatusBar(2, true, "Done recombining 2D mesh (%g s)", t2 - t1);
 }
 
+//#include <google/profiler.h>
+
 void GenerateMesh(GModel *m, int ask)
 {
+  //  ProfilerStart("gmsh.prof");
   if(CTX::instance()->lock) {
     Msg::Info("I'm busy! Ask me that later...");
     return;
@@ -684,4 +687,5 @@ void GenerateMesh(GModel *m, int ask)
 
   CTX::instance()->lock = 0;
   CTX::instance()->mesh.changed = ENT_ALL;
+  //  ProfilerStop();
 }
diff --git a/Mesh/meshGFaceDelaunayInsertion.cpp b/Mesh/meshGFaceDelaunayInsertion.cpp
index 6b924fc97bf3de59458a2f0eee553dfbd31a46c9..d22b960dad36949c7efbe1e89e13024adb7cfcf5 100644
--- a/Mesh/meshGFaceDelaunayInsertion.cpp
+++ b/Mesh/meshGFaceDelaunayInsertion.cpp
@@ -371,6 +371,30 @@ void connectTris(ITER beg, ITER end)
   }
 }
 
+template <class ITER>
+void connectQuas(ITER beg, ITER end)
+{
+  std::set<edgeXquad> conn;
+  while (beg != end){
+    for (int i = 0; i < 4; i++){
+      edgeXquad fxt(*beg, i);
+      std::set<edgeXquad>::iterator found = conn.find(fxt);
+      if (found == conn.end())
+	conn.insert(fxt);
+      else if (found->t1 != *beg){
+	found->t1->setNeigh(found->i1, *beg);
+	(*beg)->setNeigh(i, found->t1);
+      }
+    }
+    ++beg;
+  }
+}
+
+void connectQuads(std::vector<MQua4*> &l)
+{
+  connectQuas(l.begin(), l.end());
+}
+
 void connectTriangles(std::list<MTri3*> &l)
 {
   connectTris(l.begin(), l.end());
diff --git a/Mesh/meshGFaceDelaunayInsertion.h b/Mesh/meshGFaceDelaunayInsertion.h
index 0d515f27d6e2578d25400ad235b3b286975044b4..d5e1081a176d85b4d3911e0e81068d5a6fe21f38 100644
--- a/Mesh/meshGFaceDelaunayInsertion.h
+++ b/Mesh/meshGFaceDelaunayInsertion.h
@@ -7,6 +7,7 @@
 #define _MESH_GFACE_DELAUNAY_INSERTIONFACE_H_
 
 #include "MTriangle.h"
+#include "MQuadrangle.h"
 #include "STensor3.h"
 #include <list>
 #include <set>
@@ -80,6 +81,48 @@ class MTri3
   }
 };
 
+/*        2
+  3 +------------+ 2
+    |            |
+    |            |
+  3 |            | 1
+    |            |
+    |            |
+    +------------+
+  0       0        1 
+	  
+   We require that quads are oriented
+   We'd like to walk into the quad mesh and 
+   create sheets
+   
+   We start from a given quad and one edge
+   We give a path as an array of int's
+   If path[i] == 1 --> 
+      
+ */
+
+class MQua4
+{
+ protected :
+  MQuadrangle *base;
+  MQua4 *neigh[4];
+ public :
+ MQua4(MQuadrangle *q) : base(q) {
+    neigh[0] = neigh[1] = neigh[2] = neigh[3] = NULL;}
+  inline MQuadrangle *qua() const { return base; }
+  inline void  setNeigh(int iN , MQua4 *n) { neigh[iN] = n; }
+  inline MQua4 *getNeigh(int iN ) const { return neigh[iN]; }
+  inline int getEdge(MVertex *v1, MVertex *v2){
+    for (int i=0;i<4;i++){
+      MEdge e = base->getEdge(i);
+      if (e.getVertex(0) == v1 && e.getVertex(1) == v2)return i;
+      if (e.getVertex(0) == v2 && e.getVertex(1) == v1)return i;      
+    }
+    return -1;
+  }
+};
+
+
 class compareTri3Ptr
 {
  public:
@@ -91,6 +134,7 @@ class compareTri3Ptr
   }
 };
 
+void connectQuads(std::vector<MQua4*> &);
 void connectTriangles(std::list<MTri3*> &);
 void connectTriangles(std::vector<MTri3*> &);
 void connectTriangles(std::set<MTri3*,compareTri3Ptr> &AllTris);
@@ -119,4 +163,25 @@ struct edgeXface
   }
 };
 
+struct edgeXquad
+{
+  MVertex *v[2];
+  MQua4 * t1;
+  int i1;
+  edgeXquad(MQua4 *_t, int iFac) : t1(_t), i1(iFac)
+  {
+    v[0] = t1->qua()->getVertex(iFac);
+    v[1] = t1->qua()->getVertex((iFac+1)%4);
+    std::sort(v, v + 2);
+  }
+  inline bool operator < ( const edgeXquad &other) const
+  {
+    if(v[0] < other.v[0]) return true;
+    if(v[0] > other.v[0]) return false;
+    if(v[1] < other.v[1]) return true;
+    return false;
+  }
+};
+
+
 #endif
diff --git a/Mesh/meshGFaceOptimize.cpp b/Mesh/meshGFaceOptimize.cpp
index b7d8b76aaca56dc91686489c5fa40e5ff4f1b07b..807b568f94f4a6a4487c65332d2c7ad444f49c80 100644
--- a/Mesh/meshGFaceOptimize.cpp
+++ b/Mesh/meshGFaceOptimize.cpp
@@ -3,6 +3,7 @@
 // See the LICENSE.txt file for license information. Please report all
 // bugs and problems to <gmsh@geuz.org>.
 
+#include <stack>
 #include "GmshConfig.h"
 #include "meshGFaceOptimize.h"
 #include "qualityMeasures.h"
@@ -23,6 +24,11 @@
 #include "SVector3.h"
 #include "SPoint3.h"
 
+#if defined(HAVE_BFGS)
+#include "stdafx.h"
+#include "optimization.h"
+#endif
+
 #if defined(HAVE_POST)
 #include "PView.h"
 #include "PViewData.h"
@@ -611,55 +617,33 @@ static bool _isItAGoodIdeaToMoveThatVertex (GFace *gf,
   double surface_old = 0;
   double surface_new = 0;
 
-  double qualityOld[256],qualityNew[256];
+  double qualityNew[256];
 
   GPoint gp = gf->point(after);
   SPoint3 pafter  (gp.x(),gp.y(),gp.z());
   SPoint3 pbefore (v1->x(),v1->y(),v1->z());
 
-  for (unsigned int j=0;j<e1.size();++j){
+  for (unsigned int j=0;j<e1.size();++j)
     surface_old += surfaceFaceUV(e1[j],gf);
-    qualityOld[j] = (e1[j]->getNumVertices() == 4) ? e1[j]->etaShapeMeasure() : e1[j]->gammaShapeMeasure();
-  }
 
   v1->setParameter(0,after.x());
   v1->setParameter(1,after.y());
-  v1->x() = pafter.x();
-  v1->y() = pafter.y();
-  v1->z() = pafter.z();
+  v1->setXYZ(pafter.x(),pafter.y(),pafter.z());
 
   for (unsigned int j=0;j<e1.size();++j){
     surface_new += surfaceFaceUV(e1[j],gf);
     qualityNew[j] = (e1[j]->getNumVertices() == 4) ? e1[j]->etaShapeMeasure() : e1[j]->gammaShapeMeasure();
+    if (qualityNew[j] < 0.01)return false;
   }
 
   v1->setParameter(0,before.x());
   v1->setParameter(1,before.y());
-  v1->x() = pbefore.x();
-  v1->y() = pbefore.y();
-  v1->z() = pbefore.z();
+  v1->setXYZ(pbefore.x(),pbefore.y(),pbefore.z());
 
-  if ( surface_new - surface_old  > 1.e-10 * surface_old) {
+  if ( (surface_new - surface_old)  > 1.e-10 * surface_old) {
     return false;
   }
-  //printf("returning true ... \n");
   return true;
-  int nBetter=0,nWorse=0;
-  int nReallyBadOld=0,nReallyBadNew=0;
-  for (unsigned int j=0;j<e1.size();++j){
-    if (qualityNew[j] >= qualityOld[j])nBetter++;
-    else {
-      nWorse++;
-    }
-    if (qualityNew[j] < 0.2) nReallyBadNew ++;
-    if (qualityOld[j] < 0.2) nReallyBadOld ++;
-  }
-
-  if (nReallyBadNew < nReallyBadOld)return true;
-  if (nReallyBadOld < nReallyBadNew)return false;
-
-  return (nBetter >= nWorse);
-
 }
 
 
@@ -733,12 +717,728 @@ static int  _countCommon(std::vector<MElement*> &a, std::vector<MElement*> &b) {
 
 // see paper from Bunin
 //Guy Bunin (2006) Non-Local Topological Cleanup ,15th International Meshing Roundtable.
-static int _defectRemovalBunin(GFace *gf)
+// this is our interpretation of the algorithm.
+
+
+static std::vector<MVertex*> closestPathBetweenTwoDefects (v2t_cont &adj,
+							   v2t_cont :: iterator it) {
+  // get neighboring vertices
+  std::stack<std::vector<MVertex*> > paths;
+
+  std::vector<MVertex*> thisPath;
+  thisPath.push_back(it->first);
+  paths.push(thisPath);
+
+  int depth = 0;
+  while (1) {
+    std::vector<MVertex*> path = paths.top();
+    paths.pop();    
+    it = adj.find (path[path.size() - 1]);
+    std::set<MVertex *> neigh;
+    for (int i = 0; i < it->second.size(); i++){
+      for (int j=0;j<4;j++){
+	MEdge e = it->second[i]->getEdge(j);
+	if (e.getVertex(0) == it->first) neigh.insert(e.getVertex(1));
+	else if (e.getVertex(1) == it->first) neigh.insert(e.getVertex(0));
+      }
+    }
+    //    printf("vertex with %d neighbors\n",neigh.size());
+    for (std::set<MVertex *>::iterator itv = neigh.begin() ; 
+       itv != neigh.end(); ++itv){
+      v2t_cont :: iterator itb = adj.find (*itv);
+      if (std::find(path.begin(), path.end(), itb->first) == path.end()){
+	if (itb->first->onWhat()->dim() == 2 && itb->second.size() != 4) {
+	  // YEAH WE FOUND A PATH !!!!!
+	  path.push_back(itb->first);
+	  //	  printf("PATH : ");
+	  //	  for (int i=0;i<path.size();i++){
+	  //	    printf("%d ",path[i]->getNum());
+	  //	  }
+	  //	  printf("\n");
+	  return path;
+	}
+	// no path, we create new possible paths
+	else if (itb->first->onWhat()->dim() == 2){
+	  std::vector<MVertex*> newPath = path;
+	  newPath.push_back(itb->first);
+	  paths.push(newPath);
+	}
+      }
+    }
+    if (depth++ > 10 || !paths.size()){
+      std::vector<MVertex*> empty;
+      return empty;
+    }
+  } 
+}
+
+static MVertex * createNewVertex (GFace *gf, SPoint2 p){
+  GPoint gp = gf->point(p);
+  return new MFaceVertex(gp.x(),gp.y(),gp.z(),gf,gp.u(),gp.v());
+}
+static std::vector<MVertex*> saturateEdge (GFace *gf, SPoint2 p1, SPoint2 p2, int n){
+  std::vector<MVertex*> pts;
+  for (int i=1;i<n;i++){
+    SPoint2 p = p1 + (p2-p1)*((double)i/((double)(n)));
+    pts.push_back(createNewVertex(gf,p));
+  }
+  return pts;
+}
+
+/*
+  c4          N              c3
+  +--------------------------+
+  |       |      |    |      |
+  |       |      |    |      |
+  +--------------------------+  M
+  |       |      |    |      |
+  |       |      |    |      |
+  +--------------------------+
+ c1                          c2
+
+             N
+
+*/
+#define TRAN_QUA(c1,c2,c3,c4,s1,s2,s3,s4,u,v) \
+   (1.-u)*c4+u*c2+(1.-v)*c1+v*c3-((1.-u)*(1.-v)*s1+u*(1.-v)*s2+u*v*s3+(1.-u)*v*s4)
+
+void  createRegularMesh (GFace *gf, 
+			 MVertex *v1, SPoint2 &c1, 
+			 std::vector<MVertex*> &e12, int sign12, 
+			 MVertex *v2, SPoint2 &c2, 
+			 std::vector<MVertex*> &e23, int sign23, 
+			 MVertex *v3, SPoint2 &c3, 
+			 std::vector<MVertex*> &e34, int sign34, 
+			 MVertex *v4, SPoint2 &c4, 
+			 std::vector<MVertex*> &e41, int sign41, 
+			 std::vector<MQuadrangle*> &q){
+  int N = e12.size();
+  int M = e23.size();
+
+  //  printf("%d %d vs %d %d\n",e12.size(),e23.size(),e34.size(),e41.size());
+
+  //create points using transfinite interpolation
+ 
+  std::vector<std::vector<MVertex*> > tab(M+2);
+  for(int i = 0; i <= M+1; i++) tab[i].resize(N+2);
+ 
+  tab[0][0] = v1;
+  tab[0][N+1] = v2;
+  tab[M+1][N+1] = v3;
+  tab[M+1][0] = v4;
+  for (int i=0;i<N;i++){
+    tab[0][i+1]   = sign12 > 0 ? e12 [i] : e12 [N-i-1];
+    tab[M+1][i+1] = sign34 < 0 ? e34 [i] : e34 [N-i-1];
+  }
+  for (int i=0;i<M;i++){
+    tab[i+1][N+1] = sign23 > 0 ? e23 [i] : e23 [M-i-1];
+    tab[i+1][0] = sign41 < 0 ? e41 [i] : e41 [M-i-1];
+  }
+
+  for (int i=0;i<N;i++){
+    for (int j=0;j<M;j++){
+      double u = (double) (i+1)/ ((double)(N+1));
+      double v = (double) (j+1)/ ((double)(M+1));
+      MVertex *v12 = (sign12 >0) ? e12[i] : e12 [N-1-i]; 
+      MVertex *v23 = (sign23 >0) ? e23[j] : e23 [M-1-j]; 
+      MVertex *v34 = (sign12 <0) ? e34[i] : e34 [N-1-i]; 
+      MVertex *v41 = (sign41 <0) ? e41[j] : e41 [M-1-j]; 
+      //      printf("HOPLA %p %p %p %p\n",v12->onWhat(),v23->onWhat(),v34->onWhat(),v41->onWhat());
+      SPoint2 p12; reparamMeshVertexOnFace(v12, gf, p12);
+      SPoint2 p23; reparamMeshVertexOnFace(v23, gf, p23);
+      SPoint2 p34; reparamMeshVertexOnFace(v34, gf, p34);
+      SPoint2 p41; reparamMeshVertexOnFace(v41, gf, p41);
+      double Up = TRAN_QUA(p12.x(), p23.x(), p34.x(), p41.x(),
+			   c1.x(),c2.x(),c3.x(),c4.x(),u,v); 
+      double Vp = TRAN_QUA(p12.y(), p23.y(), p34.y(), p41.y(),
+			   c1.y(),c2.y(),c3.y(),c4.y(),u,v); 
+      
+      MVertex *vnew = createNewVertex (gf, SPoint2(Up,Vp));
+      tab[j+1][i+1] = vnew;
+    }
+  }
+  // create quads
+  for (int i=0;i<=N;i++){
+    for (int j=0;j<=M;j++){
+      MQuadrangle *qnew = new MQuadrangle (tab[j][i],tab[j][i+1],tab[j+1][i+1],tab[j+1][i]);
+      //      printf("%p %p %p %p\n",tab[j][i],tab[j][i+1],tab[j+1][i+1],tab[j+1][i]);
+      q.push_back(qnew);
+    }
+  }
+} 
+
+void updateQuadCavity (GFace *gf, 
+		       v2t_cont &adj,
+		       std::vector<MElement*> &oldq,
+		       std::vector<MQuadrangle*> &newq){
+
+  for (int i=0;i<oldq.size();i++){
+    gf->quadrangles.erase(std::remove(gf->quadrangles.begin(),
+				      gf->quadrangles.end(),oldq[i]));
+    for (int j=0;j<4;j++){
+      MVertex *v = oldq[i]->getVertex(j);
+      v2t_cont :: iterator it = adj.find(v);
+      if (it == adj.end())Msg::Error("cannot update a quad cavity");
+      it->second.erase(std::remove(it->second.begin(),it->second.end(),oldq[i]));
+      //      for (int k=0;k<it->second.size();++k){
+      //	if (it->second[k] == oldq[i])printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARGH\n");
+      //      }
+    }
+    //    delete oldq[i];
+  }
+  for (int i=0;i<newq.size();i++){
+    gf->quadrangles.push_back(newq[i]);
+    for (int j=0;j<4;j++){
+      MVertex *v = newq[i]->getVertex(j);
+      v2t_cont :: iterator it = adj.find(v);
+      if (it != adj.end()){
+	it->second.push_back((MElement*)newq[i]);
+      }
+      else{
+	gf->mesh_vertices.push_back(v);
+	std::vector<MElement*> vv;
+	vv.push_back(newq[i]);
+	adj[v] = vv;
+      }
+    }
+  }    
+}
+
+
+struct  quadBlob {
+  GFace *gf;
+  int maxCavitySize;
+  v2t_cont &adj;
+  std::vector<MElement*> quads;
+  std::vector<MVertex*> nodes;
+  std::vector<MVertex*> bnodes;
+  static bool matricesDone;
+  static fullMatrix<double> M3 ;
+  static fullMatrix<double> M5 ;
+
+  quadBlob(v2t_cont &a, std::vector<MVertex*> & path, GFace *g, int maxC) : adj(a),gf(g),maxCavitySize(maxC)
+  {
+    expand_blob(path);
+  }
+  inline bool inBlob(MElement* e) const {
+    return std::find(quads.begin(), quads.end(), e) != quads.end();
+  }
+  inline bool inBoundary(MVertex *v, std::vector<MVertex*> &b) const {
+    return std::find(b.begin(), b.end(), v) != b.end();
+  }
+
+  static void computeMatrices(){
+    if (matricesDone)return;
+    matricesDone = true;
+    M3.resize(6,6); 
+    M5.resize(10,10); 
+
+    // compute M3
+    M3.setAll(0);
+    M5.setAll(0);
+    M3(0,2) = M3(1,0) = M3(2,1) = -1.;
+    M3(0,3+0) = M3(1,3+1) =M3(2,3+2) =1.;
+    M3(3+0,0) = M3(3+1,1) =M3(3+2,2) =1.;
+    M3(3+0,3+0) = M3(3+1,3+1) =M3(3+2,3+2) =1.;
+    M3.invertInPlace();
+    // compute M5
+    M5(0,2) = M5(1,3) = M5(2,4) = M5(3,0) = M5(4,1) = -1;
+    for (int i=0;i<5;i++){
+      M5(5+i,5+i) = 1;
+      M5(i,5+i) = M5(5+i,i) = 1;
+    }
+    M5.invertInPlace();
+  }
+  
+  void expand_blob (std::vector<MVertex*> & path) {
+    std::set<MElement*> e;
+    e.insert(quads.begin(),quads.end());
+    for (int i=0;i<path.size();i++){
+      v2t_cont :: const_iterator it = adj.find(path[i]);
+      e.insert(it->second.begin(),it->second.end());
+    }
+    quads.clear();
+    quads.insert(quads.begin(),e.begin(),e.end());
+    std::set<MVertex*> all;
+    for (int i=0;i<quads.size();i++)
+      for (int j=0;j<4;j++)
+	all.insert(quads[i]->getVertex(j));
+    nodes.clear();
+    nodes.insert(nodes.begin(),all.begin(),all.end());
+    bnodes.clear();
+  }
+  
+  void buildBoundaryNodes(){
+    bnodes.clear();
+    for (int i=0;i<nodes.size();i++){
+      v2t_cont :: const_iterator it = adj.find(nodes[i]);
+      if (it->first->onWhat()->dim() < 2) bnodes.push_back(nodes[i]);
+      else {
+	bool found = false;
+	for (int j=0;j<it->second.size();j++){
+	  if (!inBlob(it->second[j])){
+	    found = true;
+	  }	 
+	}
+	if(found){
+	  bnodes.push_back(nodes[i]);
+	}
+      }
+    }
+  }
+
+  int topologicalAngle(MVertex*v) const {
+    int typical = 0;
+    v2t_cont :: const_iterator it = adj.find(v);
+    int outside = 0;
+    for (int j=0;j<it->second.size();j++)
+      if (!inBlob(it->second[j]))outside++;  
+
+    if (v->onWhat()->dim() == 1)typical = 2;
+    else if (v->onWhat()->dim() == 0)typical = 3;
+
+    return outside - 2 + typical;
+  }
+  int construct_meshable_blob (int iter) {
+    int blobsize = quads.size();
+    //    printf("starting with a blob of size %d\n",blobsize);
+    while(1){
+      if(quads.size() > maxCavitySize)return 0;
+      if(quads.size() >= gf->quadrangles.size())return 0;
+      //      printBlob(102021120);
+      buildBoundaryNodes();      
+      //      printf("blob has %d bnodes and %d nodes\n",bnodes.size(),nodes.size());
+      int topo_convex_region = 1;
+      for (int i=0; i<bnodes.size();i++){
+	MVertex *v = bnodes[i];
+	//	if (v->onWhat()->dim() != 2)return 0;
+	//if (v->onWhat()->dim() == 2){
+	  int topo_angle = topologicalAngle(v);
+	  if (topo_angle < 0){
+	    std::vector<MVertex*> vv; vv.push_back(v);
+	    expand_blob(vv);
+	    topo_convex_region = 0;
+	  }
+	  //	} // inside node
+	  //	else {
+	  //	  return 0;
+	  //	}
+      }// loop on boundatry nodes
+      if (topo_convex_region){
+	if (meshable(iter))return 1;
+	else expand_blob(bnodes);
+      }
+      if (blobsize == quads.size())return 0;
+      blobsize = quads.size();
+    }// while 1
+  }
+
+  bool orderBNodes () {
+    MVertex *v = 0;
+    std::vector<MVertex*> temp;
+    for (int i=0;i<bnodes.size();i++){
+      if (topologicalAngle(bnodes[i]) > 0){
+	v = bnodes[i];
+	break;
+      }
+    }
+    temp.push_back(v);
+    //    if (!v)printf("aaaaaaaaaaaaaaaaaaargh\n");
+    while(1){
+      v2t_cont :: const_iterator it = adj.find(v);
+      int ss = temp.size();
+      for (int j=0;j<it->second.size();j++){
+	bool found = false;
+	for (int i=0;i<4;i++){
+	  MEdge e = it->second[j]->getEdge(i);
+	  if (e.getVertex(0) == v && 
+	      inBoundary(e.getVertex(1),bnodes) &&
+	      !inBoundary(e.getVertex(1),temp)) {
+	    v = e.getVertex(1);
+	    temp.push_back(e.getVertex(1));
+	    found = true;
+	    break;
+	  }
+	  else if (e.getVertex(1) == v && 
+		   inBoundary(e.getVertex(0),bnodes) &&
+		   !inBoundary(e.getVertex(0),temp)) {
+	    v = e.getVertex(0);
+	    temp.push_back(e.getVertex(0));
+	    found = true;
+	    break;
+	  }
+	}
+	if (found)break;
+      }
+      if (temp.size() == ss)return false;
+      //      printf("%d %d\n",temp.size(),bnodes.size());
+      if (temp.size() == bnodes.size())break;
+    }    
+    bnodes = temp;
+    return true;
+  }
+
+  void printBlob(int iblob){
+    char name[234];
+    sprintf(name,"blob%d.pos",iblob);
+    FILE *f = fopen(name,"w");
+    fprintf(f,"View \"%s\" {\n",name);
+    for (int i=0;i<quads.size();i++){
+      quads[i]->writePOS(f,true,false,false,false,false,false);
+    }
+    for (int i=0;i<bnodes.size();i++){
+      fprintf(f,"SP(%g,%g,%g) {%d};\n",
+	      bnodes[i]->x(),
+	      bnodes[i]->y(),
+	      bnodes[i]->z(),topologicalAngle(bnodes[i]));
+    }
+    fprintf(f,"};\n");
+    fclose(f);
+
+  }
+  
+  void smooth (int);
+
+  bool meshable (int iter) {
+    int ncorners = 0;
+    MVertex *corners[5];
+    for (int i=0;i<bnodes.size();i++){
+      if (topologicalAngle(bnodes[i]) > 0) ncorners ++; 
+      if (ncorners > 5)return false;      
+    }
+    if (ncorners != 3 && ncorners != 4 && ncorners != 5){
+      return false;
+    }
+    //    printf("a blob with %d corners has been found\n",ncorners);
+    // look if it is possible to build a mesh with one defect only
+    if (!orderBNodes () )return false;
+    int side = -1;
+    int count[5] = {0,0,0,0,0};
+    for (int i=0;i<bnodes.size();i++){
+      if (topologicalAngle(bnodes[i]) > 0){
+	side++;
+	corners[side] = (bnodes[i]);
+      }
+      else count[side]++;
+    }
+    /*
+
+      This is the configuration for the 4 corners defect
+      only the simple case is considered
+     */
+    if (ncorners == 4){
+      if (count[0] != count[2] || count[1] != count[3]){
+	//	printf("4 CORNERS : %d %d %d %d\n",count[0],count[1],count[2],count[3]);
+	return 0;
+      }
+      int a1 = (int)count[0]+1;
+      int a2 = (int)count[1]+1;
+      MVertex *v0 = corners[0]; SPoint2 p0; reparamMeshVertexOnFace(v0, gf, p0);
+      MVertex *v1 = corners[1]; SPoint2 p1; reparamMeshVertexOnFace(v1, gf, p1);
+      MVertex *v2 = corners[2]; SPoint2 p2; reparamMeshVertexOnFace(v2, gf, p2);
+      MVertex *v3 = corners[3]; SPoint2 p3; reparamMeshVertexOnFace(v3, gf, p3);
+
+      std::vector<MVertex*> e0_1,e1_2,e2_3,e3_0;
+      for (int i=0;i<a1-1;i++)e0_1.push_back(bnodes[i+1]);
+      for (int i=0;i<a2-1;i++)e1_2.push_back(bnodes[i+1 + a1]);
+      for (int i=0;i<a1-1;i++)e2_3.push_back(bnodes[i+1 + a1 + a2]);
+      for (int i=0;i<a2-1;i++)e3_0.push_back(bnodes[i+1 + a1 + a2 + a1]);
+      //      printf("%d bnodes last inserted %d a123 = %d %d \n",bnodes.size(),a2 + a1 + a2 + a1,a1,a2);
+      std::vector<MQuadrangle*> q;
+      createRegularMesh (gf,
+			 v0,p0, 
+			 e0_1, +1, 
+			 v1,p1, 
+			 e1_2, +1,
+			 v2,p2,
+			 e2_3, +1,
+			 v3,p3,
+			 e3_0, +1,
+			 q); 
+      //      printBlob(iter+10000);
+      updateQuadCavity (gf,adj,quads,q);      
+      quads.clear();
+      quads.insert(quads.begin(),q.begin(),q.end());
+      //      printBlob(iter+20000);      
+      return 1;
+      //      getchar();
+    }
+    else if (ncorners == 3){
+      //      printBlob(iter);
+      fullVector<double> rhs(6);
+      fullVector<double> result(6);
+      rhs(3) = count[0]+1.;
+      rhs(4) = count[1]+1.;
+      rhs(5) = count[2]+1.;
+      rhs(0) = rhs(1) = rhs(2) = 0.;
+      //      printf("%d %d %d\n",count[0],count[1],count[2]);
+      M3.mult(rhs,result);
+      //      printf("%g %g %g %g %g %g\n",result(0),result(1),result(2),result(3),result(4),result(5));
+      int a1 = (int)result(0);
+      int a2 = (int)result(1);
+      int a3 = (int)result(2);
+      if (a1 <= 0 || a2  <=0  || a3 <= 0)return false;
+      MVertex *v0 = corners[0]; SPoint2 p0; reparamMeshVertexOnFace(v0, gf, p0);
+      MVertex *v1 = corners[1]; SPoint2 p1; reparamMeshVertexOnFace(v1, gf, p1);
+      MVertex *v2 = corners[2]; SPoint2 p2; reparamMeshVertexOnFace(v2, gf, p2);
+      MVertex *v01 = bnodes[a1]; SPoint2 p01; reparamMeshVertexOnFace(v01, gf, p01);
+      MVertex *v12 = bnodes[a1+a3+a2]; SPoint2 p12; reparamMeshVertexOnFace(v12, gf, p12);
+      MVertex *v20 = bnodes[a1+a3+a2+a1+a3]; SPoint2 p20; reparamMeshVertexOnFace(v20, gf, p20);
+      SPoint2 p012 = (p01+p12+p20)*(1./3.0); MVertex *v012 = createNewVertex (gf, p012);
+
+      std::vector<MVertex*> e012_01 = saturateEdge (gf,p012,p01,a2);
+      std::vector<MVertex*> e012_12 = saturateEdge (gf,p012,p12,a3);
+      std::vector<MVertex*> e012_20 = saturateEdge (gf,p012,p20,a1);
+      
+      std::vector<MVertex*> e0_01,e01_1,e1_12,e12_2,e2_20,e20_0;
+      for (int i=0;i<a1-1;i++)e0_01.push_back(bnodes[i+1]);
+      for (int i=0;i<a3-1;i++)e01_1.push_back(bnodes[i+1 + a1]);
+      for (int i=0;i<a2-1;i++)e1_12.push_back(bnodes[i+1 + a1 + a3]);
+      for (int i=0;i<a1-1;i++)e12_2.push_back(bnodes[i+1 + a1 + a3 + a2]);
+      for (int i=0;i<a3-1;i++)e2_20.push_back(bnodes[i+1 + a1 + a3 + a2 + a1 ]);
+      for (int i=0;i<a2-1;i++)e20_0.push_back(bnodes[i+1 + a1 + a3 + a2 + a1 + a3]);
+      //      printf("%d bnodes last inserted %d a123 = %d %d %d\n",bnodes.size(),a2 + a1 + a3 + a2 + a1 + a3,a1,a2,a3);
+      std::vector<MQuadrangle*> q;
+      createRegularMesh (gf,
+			 v012,p012, 
+			 e012_01, +1, 
+			 v01,p01, 
+			 e0_01, -1,
+			 v0,p0,
+			 e20_0, -1,
+			 v20,p20,
+			 e012_20, -1,
+			 q); 
+      
+      createRegularMesh (gf,
+			 v012,p012, 
+			 e012_20, +1, 
+			 v20,p20, 
+			 e2_20, -1,
+			 v2,p2,
+			 e12_2, -1,
+			 v12,p12,
+			 e012_12, -1,
+			 q); 
+      
+      createRegularMesh (gf,
+			 v012,p012, 
+			 e012_12, +1, 
+			 v12,p12, 
+			 e1_12, -1,
+			 v1,p1,
+			 e01_1, -1,
+			 v01,p01,
+			 e012_01, -1,
+			 q); 
+
+
+      //      printBlob(iter+10000);
+      updateQuadCavity (gf,adj,quads,q);      
+      quads.clear();
+      quads.insert(quads.begin(),q.begin(),q.end());
+      //      printBlob(iter+20000);      
+      return 1;
+      //      getchar();
+    }
+    /*
+
+      This is the configuration for the 5 corners defect
+
+                    v3
+                    /\
+            a4    /    \  a5
+                /        \
+         v34  /            \ v23
+            / \    4      /  \
+      a1  /    \        /      \  a3
+        /        \    /          \
+   v4  X    5     \  /      3     X v2
+       \         /v01234\         /
+   a5   \    /      |      \     / a4
+   v40   \/     1   |   2      \/ v12
+       a2 \         |          /   
+           X--------+---------X    a2
+           v0      v01        v1
+               a1       a3
+
+
+
+     */
+    else if (ncorners == 5){
+      //      printBlob(iter);
+      fullVector<double> rhs(10);
+      fullVector<double> result(10);
+      rhs(5) = count[0]+1.;
+      rhs(6) = count[1]+1.;
+      rhs(7) = count[2]+1.;
+      rhs(8) = count[3]+1.;
+      rhs(9) = count[4]+1.;
+      rhs(0) = rhs(1) = rhs(2) = rhs(3) = rhs(4) = 0.;
+      //      printf("%d %d %d\n",count[0],count[1],count[2]);
+      M5.mult(rhs,result);
+      //      printf("%g %g %g %g %g %g\n",result(0),result(1),result(2),result(3),result(4),result(5));
+      int a1 = (int)result(0);
+      int a2 = (int)result(1);
+      int a3 = (int)result(2);
+      int a4 = (int)result(3);
+      int a5 = (int)result(4);
+      if (a1 <= 0 || a2  <=0  || a3 <= 0 || a4 <= 0 || a5 <= 0)return false;
+      MVertex *v0 = corners[0]; SPoint2 p0; reparamMeshVertexOnFace(v0, gf, p0);
+      MVertex *v1 = corners[1]; SPoint2 p1; reparamMeshVertexOnFace(v1, gf, p1);
+      MVertex *v2 = corners[2]; SPoint2 p2; reparamMeshVertexOnFace(v2, gf, p2);
+      MVertex *v3 = corners[3]; SPoint2 p3; reparamMeshVertexOnFace(v3, gf, p3);
+      MVertex *v4 = corners[4]; SPoint2 p4; reparamMeshVertexOnFace(v4, gf, p4);
+
+      MVertex *v01 = bnodes[a1]; SPoint2 p01; reparamMeshVertexOnFace(v01, gf, p01);
+      MVertex *v12 = bnodes[a1+a3+a2]; SPoint2 p12; reparamMeshVertexOnFace(v12, gf, p12);
+      MVertex *v23 = bnodes[a1+a3+a2+a4+a3]; SPoint2 p23; reparamMeshVertexOnFace(v23, gf, p23);
+      MVertex *v34 = bnodes[a1+a3+a2+a4+a3+a5+a4]; SPoint2 p34; reparamMeshVertexOnFace(v34, gf, p34);
+      MVertex *v40 = bnodes[a1+a3+a2+a4+a3+a5+a4+a1+a5]; SPoint2 p40; reparamMeshVertexOnFace(v40, gf, p40);
+      SPoint2 p01234 = (p01+p12+p23+p34+p40)*(1./5.0); MVertex *v01234 = createNewVertex (gf, p01234);
+
+      std::vector<MVertex*> e01234_01 = saturateEdge (gf,p01234,p01,a2);
+      std::vector<MVertex*> e01234_12 = saturateEdge (gf,p01234,p12,a3);
+      std::vector<MVertex*> e01234_23 = saturateEdge (gf,p01234,p23,a4);
+      std::vector<MVertex*> e01234_34 = saturateEdge (gf,p01234,p34,a5);
+      std::vector<MVertex*> e01234_40 = saturateEdge (gf,p01234,p40,a1);
+      
+      std::vector<MVertex*> e0_01,e01_1,e1_12,e12_2,e2_23,e23_3,e3_34,e34_4,e4_40,e40_0;
+      for (int i=0;i<a1-1;i++)e0_01.push_back(bnodes[i+1]);
+      for (int i=0;i<a3-1;i++)e01_1.push_back(bnodes[i+1 + a1]);
+      for (int i=0;i<a2-1;i++)e1_12.push_back(bnodes[i+1 + a1 + a3]);
+      for (int i=0;i<a4-1;i++)e12_2.push_back(bnodes[i+1 + a1 + a3 + a2]);
+      for (int i=0;i<a3-1;i++)e2_23.push_back(bnodes[i+1 + a1 + a3 + a2 + a4]);
+      for (int i=0;i<a5-1;i++)e23_3.push_back(bnodes[i+1 + a1 + a3 + a2 + a4 + a3]);
+      for (int i=0;i<a4-1;i++)e3_34.push_back(bnodes[i+1 + a1 + a3 + a2 + a4 + a3 + a5]);
+      for (int i=0;i<a1-1;i++)e34_4.push_back(bnodes[i+1 + a1 + a3 + a2 + a4 + a3 + a5 + a4]);
+      for (int i=0;i<a5-1;i++)e4_40.push_back(bnodes[i+1 + a1 + a3 + a2 + a4 + a3 + a5 + a4 + a1]);
+      for (int i=0;i<a2-1;i++)e40_0.push_back(bnodes[i+1 + a1 + a3 + a2 + a4 + a3 + a5 + a4 + a1 + a5]);
+
+      std::vector<MQuadrangle*> q;
+      // 1
+      createRegularMesh (gf,
+			 v01234,p01234, 
+			 e01234_01, +1, 
+			 v01,p01, 
+			 e0_01, -1,
+			 v0,p0,
+			 e40_0, -1,
+			 v40,p40,
+			 e01234_40, -1,
+			 q); 
+      
+      // 2
+      createRegularMesh (gf,
+			 v01234,p01234, 
+			 e01234_12, +1, 
+			 v12,p12, 
+			 e1_12, -1,
+			 v1,p1,
+			 e01_1, -1,
+			 v01,p01,
+			 e01234_01, -1,
+			 q); 
+      // 3
+      createRegularMesh (gf,
+			 v01234,p01234, 
+			 e01234_23, +1, 
+			 v23,p23, 
+			 e2_23, -1,
+			 v2,p2,
+			 e12_2, -1,
+			 v12,p12,
+			 e01234_12, -1,
+			 q); 
+
+      // 4
+      createRegularMesh (gf,
+			 v01234,p01234, 
+			 e01234_34, +1, 
+			 v34,p34, 
+			 e3_34, -1,
+			 v3,p3,
+			 e23_3, -1,
+			 v23,p23,
+			 e01234_23, -1,
+			 q); 
+
+      // 5
+      createRegularMesh (gf,
+			 v01234,p01234, 
+			 e01234_40, +1, 
+			 v40,p40, 
+			 e4_40, -1,
+			 v4,p4,
+			 e34_4, -1,
+			 v34,p34,
+			 e01234_34, -1,
+			 q); 
+      
+      // YES
+      
+      //      printBlob(iter+10000);
+      updateQuadCavity (gf,adj,quads,q);      
+      quads.clear();
+      quads.insert(quads.begin(),q.begin(),q.end());
+      //      printBlob(iter+20000);      
+      return 1;
+      //      getchar();
+    }
+    return 0;
+  }  
+};
+
+bool quadBlob::matricesDone = false;
+fullMatrix<double> quadBlob::M3;
+fullMatrix<double> quadBlob::M5;
+
+static int _defectsRemovalBunin(GFace *gf, int maxCavitySize)
 {
+  if (maxCavitySize == 0)return 0;
   v2t_cont adj;
-  buildVertexToElement(gf->triangles,adj);
-  buildVertexToElement(gf->quadrangles,adj);
+  std::vector<MElement*> c;
+  buildVertexToElement(gf->quadrangles, adj);
+  quadBlob::computeMatrices();
+
+  int iter = 0;
+  //  printf("%d adj\n",adj.size());
+  std::vector<MVertex*> defects;
   v2t_cont :: iterator it = adj.begin();
+  clock_t t1 = clock();
+  while (it != adj.end()) {
+    if (it->first->onWhat()->dim() == 2 && it->second.size() != 4) {
+      defects.push_back(it->first);
+    }
+    ++it;
+  }
+  clock_t t2 = clock();
+  double C = (double)(t2-t1)/CLOCKS_PER_SEC;
+  double A=0,B=0;
+  for (int i=0;i<defects.size();i++){
+    it = adj.find(defects[i]);
+    if (it->first->onWhat()->dim() == 2 && it->second.size() != 4 && it->second.size() != 0) {
+      clock_t t3 = clock();
+      std::vector<MVertex*> path = closestPathBetweenTwoDefects (adj,it);
+      clock_t t4 = clock();
+      B += (double)(t4-t3)/CLOCKS_PER_SEC;
+      if (path.size()){
+	clock_t t5 = clock();
+	quadBlob blob(adj,path,gf, maxCavitySize);
+	if(blob.construct_meshable_blob (iter)){
+	  blob.smooth(2*(int)(sqrt((double)maxCavitySize)));
+	  iter ++;
+	  //	  if (iter > 0)break;
+	}
+	clock_t t6 = clock();
+	A += (double)(t6-t5)/CLOCKS_PER_SEC;
+      }
+    }
+    ++it;
+  }
+  Msg::Debug("%i blobs remeshed %g %g %g",iter,A,B,C);
+
+  return iter;
 }
 
 static int _splitFlatQuads(GFace *gf, double minQual)
@@ -799,6 +1499,7 @@ static int _splitFlatQuads(GFace *gf, double minQual)
 	gf->mesh_vertices.push_back(b2);
 	gf->mesh_vertices.push_back(b3);
 	gf->mesh_vertices.push_back(b4);
+	break;
       }
     }
     ++it;
@@ -812,7 +1513,7 @@ static int _splitFlatQuads(GFace *gf, double minQual)
     }
   }
   gf->quadrangles = added_quadrangles;
-  Msg::Debug("%i flat quads removed",deleted_quadrangles.size());
+  //  Msg::Debug("%i flat quads removed",deleted_quadrangles.size());
   //Msg::Info("%i flat quads removed",deleted_quadrangles.size());
  return deleted_quadrangles.size();
 }
@@ -964,90 +1665,117 @@ int removeDiamonds(GFace *gf){
   return nbRemove;
 }
 
+int splitFlatQuads(GFace *gf, double q){
+  int nbRemove = 0;
+  while(1){
+    int x = _splitFlatQuads(gf,q);
+    if (!x)break;
+    nbRemove += x;
+  }
+  Msg::Debug("%i flat quads removed",nbRemove);
+  return nbRemove;
+}
+
 struct p1p2p3 {
   MVertex *p1,*p2;
 };
-static void _relocateVertexConstrained (GFace *gf,
-					MVertex *ver,
-					const std::vector<MElement*> &lt){
-  if( ver->onWhat()->dim() == 2){
-
-    MFaceVertex *fv = dynamic_cast<MFaceVertex*>(ver);
-    if (fv->bl_data)return;
 
-    std::map<MVertex*,p1p2p3> ring;
-    double initu,initv;
-    ver->getParameter(0, initu);
-    ver->getParameter(1, initv);
-    double XX=0,YY=0,ZZ=0;
-    for(unsigned int i = 0; i < lt.size(); i++){
-      for (int j=0;j<lt[i]->getNumEdges();j++){
-	MEdge ed = lt[i]->getEdge(j);
-	if (ed.getVertex(0) != ver && ed.getVertex(1) != ver){
-	  std::map<MVertex*,p1p2p3>::iterator it = ring.find(ed.getVertex(0));
-	  if (it == ring.end()){
-	    p1p2p3 a;
-	    a.p1 = ed.getVertex(1);
-	    ring[ed.getVertex(0)] = a;
-	  }
-	  else{
-	    it->second.p2 = ed.getVertex(1);
-	  }
-	  it = ring.find(ed.getVertex(1));
-	  if (it == ring.end()){
-	    p1p2p3 a;
-	    a.p1 = ed.getVertex(0);
-	    ring[ed.getVertex(1)] = a;
-	  }
-	  else{
-	    it->second.p2 = ed.getVertex(0);
-	  }
-	}
-      }
-    }
+#if defined(HAVE_BFGS)
+// Callback function for BFGS
 
-    double cu=0,cv=0;
-    std::map<MVertex*,p1p2p3>::iterator it = ring.begin();
-    for (; it!=ring.end();++it){
-      MVertex *center  = it->first;
-      MVertex *left    = it->second.p1;
-      MVertex *right   = it->second.p2;
-      SPoint2 pcenter,pleft,pright;
-      reparamMeshVertexOnFace(center, gf, pcenter);
-      reparamMeshVertexOnFace(left, gf, pleft);
-      reparamMeshVertexOnFace(right, gf, pright);
-      SVector3 vj   (pcenter.x()-initu,pcenter.y()-initv,0);
-      SVector3 vjp1 (pleft.x()-initu,pleft.y()-initv,0);
-      SVector3 vjm1 (pright.x()-initu,pright.y()-initv,0);
-      vjp1.normalize();
-      vjm1.normalize();
-      SVector3 bissector = (vjp1+vjm1)*0.5;
-      double dist = vj.norm();
-      cu += (pcenter.x() + dist * bissector.x());
-      cv += (pcenter.y() + dist * bissector.y());
-    }
-    cu/= ring.size();
-    cv/= ring.size();
-    SPoint2 before(initu,initv);
-    SPoint2 after(cu,cv);
-    //    if (isSphere){
-    //      GPoint gp = gf->closestPoint(SPoint3(XX/fact, YY/fact, ZZ/fact), after);
-    //      after = SPoint2(gp.u(),gp.v());
-    //    }
-    bool success = _isItAGoodIdeaToMoveThatVertex (gf,  lt, ver,before,after);
-    if (success){
-      ver->setParameter(0, after.x());
-      ver->setParameter(1, after.y());
-      GPoint pt = gf->point(after);
-      if(pt.succeeded()){
-	ver->x() = pt.x();
-	ver->y() = pt.y();
-	ver->z() = pt.z();
-      }
-    }
+struct opti_data_vertex_relocation {
+  const std::vector<MElement*> & e;
+  MVertex *v;
+  GFace *gf;
+  double minq,eps;
+  opti_data_vertex_relocation (const std::vector<MElement*> & _e, MVertex * _v, GFace *_gf) : e(_e), v(_v), gf(_gf)
+  {
+    minq = -2;
+    eps = minq / (1. - minq);
+  } 
+  double f() const {
+    double val = 0.0;
+    for (int i=0;i < e.size();i++){
+      const double q = (e[i]->getNumVertices() == 4) ? e[i]->etaShapeMeasure() :
+	e[i]->gammaShapeMeasure();
+      const double dd = (1 + eps) * q - eps;
+      const double  l = (dd>0) ? log (dd) : 1.e22;
+      const double  m = (q-1);
+      val += l * l + m*m;
+    }
+    return val;
+  }
+  void df(const double &F, const double &U, const double &V, double &dfdu, double &dfdv)  {
+    const double eps = 1.e-6;
+    set_(U+eps,V);
+    const double FU = f();
+    set_(U,V+eps);
+    const double FV = f();
+    set_(U,V);
+    dfdu = -(F-FU)/eps;
+    dfdv = -(F-FV)/eps;
+  }
+  void set_(double U, double V){
+    GPoint gp = gf->point(U,V);
+    v->x() = gp.x();
+    v->y() = gp.y();
+    v->z() = gp.z();
+    v->setParameter(0,U);
+    v->setParameter(1,V);
   }
+};
+
+void bfgs_callback_vertex_relocation (const alglib::real_1d_array& x, double& func, alglib::real_1d_array& grad, void* ptr) {
+  opti_data_vertex_relocation* w = static_cast<opti_data_vertex_relocation*>(ptr);
+  w->set_(x[0],x[1]);
+  func = w->f();
+  //  printf("F(%3.2f,%3.2f) = %12.5E\n",x[0],x[1],func);
+  w->df(func,x[0],x[1],grad[0],grad[1]);
 }
 
+// use optimization 
+static void _relocateVertexOpti(GFace *gf, MVertex *ver,
+				const std::vector<MElement*> &lt)
+{
+  if(ver->onWhat()->dim() != 2)return;
+  //  printf ("optimizing vertex position with BFGS\n");
+  // we optimize the local coordinates of the node
+  alglib::ae_int_t dim = 2;
+  alglib::ae_int_t corr = 2; // Num of corrections in the scheme in [3,7]
+  alglib::minlbfgsstate state;
+  alglib::real_1d_array x; // = "[0.5,0.5]";
+  std::vector<double> initial_conditions(dim);
+  opti_data_vertex_relocation data(lt,ver,gf);
+  double U, V;
+  ver->getParameter(0,U);
+  ver->getParameter(1,V);  
+  initial_conditions[0] = U;
+  initial_conditions[1] = V;
+  x.setcontent(dim, &initial_conditions[0]);
+  minlbfgscreate(2, corr, x, state);
+  double epsg = 1.e-12;
+  double epsf = 0.0;
+  double epsx = 0.0;
+  alglib::ae_int_t maxits = 10;
+  minlbfgssetcond(state,epsg,epsf,epsx,maxits);
+  minlbfgsoptimize(state, bfgs_callback_vertex_relocation,NULL,&data);
+  alglib::minlbfgsreport rep;
+  minlbfgsresults(state,x,rep);
+
+  double minq = 0;
+  for (int i=0;i < data.e.size();i++){
+    const double q = (data.e[i]->getNumVertices() == 4) ? data.e[i]->etaShapeMeasure() :
+      data.e[i]->gammaShapeMeasure();
+    minq = std::min(q,minq);
+  } 
+  if (minq < 0.01)data.set_(U,V);
+
+  //  if (rep.terminationtype != 4){
+  //    data.set_(U,V);
+  //  }  
+}
+#endif
+
 static void _relocateVertex(GFace *gf, MVertex *ver,
                             const std::vector<MElement*> &lt)
 {
@@ -1090,59 +1818,50 @@ static void _relocateVertex(GFace *gf, MVertex *ver,
 	GPoint gp = gf->closestPoint(SPoint3(XX/fact, YY/fact, ZZ/fact), after);
 	after = SPoint2(gp.u(),gp.v());
       }
-      bool success = false;
-      double factor = 1.0;
-      SPoint2 newp;
-      for (int i=0;i<10;i++){
-	newp = after + before*(1.-factor);
-	success = _isItAGoodIdeaToMoveThatVertex (gf,  lt, ver,before,newp);
-	if (success) break;
-	factor *= 0.5;
-      }
+      bool success = _isItAGoodIdeaToMoveThatVertex (gf,  lt, ver,before,after);
 
       if (success){
-	ver->setParameter(0, newp.x());
-	ver->setParameter(1, newp.y());
-	GPoint pt = gf->point(newp);
+	ver->setParameter(0, after.x());
+	ver->setParameter(1, after.y());
+	GPoint pt = gf->point(after);
 	if(pt.succeeded()){
 	  ver->x() = pt.x();
 	  ver->y() = pt.y();
 	  ver->z() = pt.z();
 	}
       }
+      else{
+#if defined(HAVE_BFGS)
+	_relocateVertexOpti(gf, ver, lt);
+#endif
+      }
     }
   }
 }
 
-void laplaceSmoothing(GFace *gf, int niter)
-{
-  v2t_cont adj;
-  buildVertexToElement(gf->triangles, adj);
-  buildVertexToElement(gf->quadrangles, adj);
-  for(int i = 0; i < niter; i++){
-    v2t_cont::iterator it = adj.begin();
-    while (it != adj.end()){
+void quadBlob::smooth (int niter) {
+  for (int i=0;i<niter;i++){
+    for (int j=0;j<nodes.size();j++){
+      v2t_cont::iterator it = adj.find(nodes[j]);
       _relocateVertex(gf, it->first, it->second);
-      ++it;
     }
   }
 }
 
-void laplaceSmoothingConstrained(GFace *gf)
+void laplaceSmoothing(GFace *gf, int niter)
 {
   v2t_cont adj;
   buildVertexToElement(gf->triangles, adj);
   buildVertexToElement(gf->quadrangles, adj);
-  for(int i = 0; i < 5; i++){
+  for(int i = 0; i < niter; i++){
     v2t_cont::iterator it = adj.begin();
     while (it != adj.end()){
-      _relocateVertexConstrained(gf, it->first, it->second);
+      _relocateVertex(gf, it->first, it->second);
       ++it;
     }
   }
 }
 
-
 int _edgeSwapQuadsForBetterQuality(GFace *gf)
 {
   e2t_cont adj;
@@ -1209,7 +1928,7 @@ int _edgeSwapQuadsForBetterQuality(GFace *gf)
 	double new_surface_B = surfaceFaceUV(q1B,gf,&cb1) + surfaceFaceUV(q2B,gf,&cb2) ;	
 
 	if (!ca1 && !ca2 && 
-	    old_surface >= new_surface_A && 
+	    fabs(old_surface - new_surface_A) < 1.e-10 * old_surface && 
 	    0.8*worst_quality_A > worst_quality_old && 
 	    0.8*worst_quality_A > worst_quality_B && 
 	    SANITY_(gf,q1A,q2A,12121)){
@@ -1223,7 +1942,7 @@ int _edgeSwapQuadsForBetterQuality(GFace *gf)
 	  COUNT++;
 	}
 	else if (!cb1 && !cb2 && 
-		 old_surface >= new_surface_B  &&
+		 fabs(old_surface - new_surface_B) < 1.e-10 * old_surface  &&
 		 0.8*worst_quality_B > worst_quality_old && 
 		 0.8*worst_quality_B > worst_quality_A && 
 		 SANITY_(gf,q1B,q2B,12121)){
@@ -1341,7 +2060,7 @@ int postProcessExtraEdges (GFace *gf, std::vector<std::pair<MElement*,MElement*>
   // so we recombine them in another way (adding a new node)
 
 
-  printf("%d extra edges to be processed\n",(int)toProcess.size());
+  Msg::Debug("%d extra edges to be processed",(int)toProcess.size());
 
   //  return 1;
 
@@ -1363,7 +2082,7 @@ int postProcessExtraEdges (GFace *gf, std::vector<std::pair<MElement*,MElement*>
 	break;
       }
     }
-    printf("extra edge %d common vertex %p\n",i,common);
+    //    printf("extra edge %d common vertex %p\n",i,common);
     if (common){
       deleted.insert(t1);
       deleted.insert(t2);
@@ -1391,16 +2110,29 @@ int postProcessExtraEdges (GFace *gf, std::vector<std::pair<MElement*,MElement*>
 						    gf,
 						    p.x(),
 						    p.y());
+	  gf->mesh_vertices.push_back(newVertex);
 	  int start1,start2;
-	  int orientation1, orientation2;
+	  int orientation1=1, orientation2=1;
 	  for (int k=0;k<3;k++){
-	    if (t1->getVertex(k) == it->first)start1 = k;
+	    if (t1->getVertex(k) == it->first){
+	      //	      if (t1->getVertex((k+1)%3)->onWhat()->dim
+	      start1 = k;
+	    }
 	    if (t2->getVertex(k) == it->first)start2 = k;
 	  }
+	  /*
+	    FIX THAT !!!!!
+	    x--------x--------x
+             \ t1   / \  t2  /
+              \    /   \    /
+               \  /     \  /
+                \/       \/
+
+	   */
 	  MQuadrangle *q1;
 	  if (orientation1)
-	    q1 = new MQuadrangle(newVertex,
-				 t1->getVertex(start1),
+	    q1 = new MQuadrangle(t1->getVertex(start1),
+				 newVertex,
 				 t1->getVertex((start1+1)%3),
 				 t1->getVertex((start1+2)%3));
 	  else
@@ -1411,8 +2143,8 @@ int postProcessExtraEdges (GFace *gf, std::vector<std::pair<MElement*,MElement*>
 
 	  MQuadrangle *q2;
 	  if (orientation2)
-	    q2 = new MQuadrangle(newVertex,
-				 t2->getVertex(start2),
+	    q2 = new MQuadrangle(t2->getVertex(start2),
+				 newVertex,
 				 t2->getVertex((start2+1)%3),
 				 t2->getVertex((start2+2)%3));
 	  else
@@ -1426,8 +2158,13 @@ int postProcessExtraEdges (GFace *gf, std::vector<std::pair<MElement*,MElement*>
 	  newAdj.push_back(q1);
 	  newAdj.push_back(q2);
 	  for (int k=0;k<it->second.size();k++){
-	    if (it->second[k]->getNumVertices() == 4)
+	    if (it->second[k]->getNumVertices() == 4){
 	      newAdj.push_back(it->second[k]);
+	      for (int vv=0;vv<4;vv++){
+		if (it->first == it->second[k]->getVertex(vv))
+		  it->second[k]->setVertex(vv,newVertex);
+	      }
+	    }
 	  }
 	  gf->quadrangles.push_back(q1);
 	  gf->quadrangles.push_back(q2);
@@ -2122,10 +2859,12 @@ static int _recombineIntoQuads(GFace *gf, int recur_level, bool cubicGraph = 1)
   return success;
 }
 
+
 void recombineIntoQuads(GFace *gf,
 			bool topologicalOpti,
 			bool nodeRepositioning)
 {
+  
   double t1 = Cpu();
 
   bool haveParam = true;
@@ -2152,25 +2891,54 @@ void recombineIntoQuads(GFace *gf,
         int COUNT = 0, ITER=0;
         char NAME[256];
 
+	double tFQ=0,tTQ=0,tDI=0,tLA=0,tSW=0,tBU=0;
+	double oldoptistatus[5] = {0,0,0,0,0};
+	double optistatus[5] = {0,0,0,0,0};
         while(1){
-	  int w = _splitFlatQuads(gf, .3);
+	  clock_t t6 = clock();
+	  int maxCavitySize = CTX::instance()->mesh.bunin;
+	  optistatus[4] = _defectsRemovalBunin(gf,maxCavitySize);
+          if(optistatus[4] && saveAll){ sprintf(NAME,"iter%dB.msh",COUNT++); gf->model()->writeMSH(NAME); }
+	  clock_t t7 = clock();
+	  clock_t t1 = clock();
+	  optistatus[0] = splitFlatQuads(gf, .3);
 	  //	  printf("%d flat quads splittttouilled\n",w);
-          if(w && saveAll){ sprintf(NAME,"iter%dSF.msh",COUNT++); gf->model()->writeMSH(NAME);}
-          int x = removeTwoQuadsNodes(gf);
-          if(x && saveAll){ sprintf(NAME,"iter%dTQ.msh",COUNT++); gf->model()->writeMSH(NAME);}
-          int y = removeDiamonds(gf);
-          if(y && saveAll){ sprintf(NAME,"iter%dD.msh",COUNT++); gf->model()->writeMSH(NAME); }
+          if(optistatus[0] && saveAll){ sprintf(NAME,"iter%dSF.msh",COUNT++); gf->model()->writeMSH(NAME);}
+	  clock_t t2 = clock();
+	  optistatus[1] = removeTwoQuadsNodes(gf);
+          if(optistatus[1] && saveAll){ sprintf(NAME,"iter%dTQ.msh",COUNT++); gf->model()->writeMSH(NAME);}
+	  clock_t t3 = clock();
+          optistatus[2] = removeDiamonds(gf);
+          if(optistatus[2] && saveAll){ sprintf(NAME,"iter%dD.msh",COUNT++); gf->model()->writeMSH(NAME); }
+	  clock_t t4 = clock();
           laplaceSmoothing(gf,CTX::instance()->mesh.nbSmoothing);
-          int z = edgeSwapQuadsForBetterQuality(gf);
+	  clock_t t5 = clock();
+          optistatus[3] = edgeSwapQuadsForBetterQuality(gf);
 	  //	  if (z) printf("%d swops !!\n",z);
-          if(z && saveAll){ sprintf(NAME,"iter%dS.msh",COUNT++); gf->model()->writeMSH(NAME); }
-          if (!(w+x+y+z)) break;
-	  if (ITER++ >= 14)break;
+          if(optistatus[3] && saveAll){ sprintf(NAME,"iter%dS.msh",COUNT++); gf->model()->writeMSH(NAME); }
+	  tFQ += (double)(t2-t1)/CLOCKS_PER_SEC;
+	  tTQ += (double)(t3-t2)/CLOCKS_PER_SEC;
+	  tDI += (double)(t4-t3)/CLOCKS_PER_SEC;
+	  tLA += (double)(t5-t4)/CLOCKS_PER_SEC;
+	  tSW += (double)(t6-t5)/CLOCKS_PER_SEC;
+	  tBU += (double)(t7-t6)/CLOCKS_PER_SEC;
+          if (!(optistatus[0]+optistatus[1]+optistatus[2]+optistatus[3]+optistatus[4])) break;
+	  bool theSame = true;
+	  for (int i=0;i<5;i++){
+	    if (optistatus[i] != oldoptistatus[i])theSame = false;
+	      oldoptistatus[i] = optistatus[i];
+	  }
+	  if(theSame)break;	  
+	  if (ITER++ >= 20)break;
         }
+	printf("times = %4.3f %4.3f %4.3f %4.3f %4.3f %4.3f\n",tFQ,tTQ,tDI,tLA,tSW,tBU);
       }
-      edgeSwapQuadsForBetterQuality(gf);
+      //      edgeSwapQuadsForBetterQuality(gf);
       //if(z){ sprintf(NAME,"iter%dS.msh",COUNT++); gf->model()->writeMSH(NAME); }
+      //      removeTwoQuadsNodes(gf);
       if(haveParam) laplaceSmoothing(gf,CTX::instance()->mesh.nbSmoothing);
+      //      for (int i=0;i<20;i++)_defectsRemovalBunin(gf);
+      //      if(haveParam) laplaceSmoothing(gf,CTX::instance()->mesh.nbSmoothing);
     }
     double t2 = Cpu();
     Msg::Info("Blossom recombination algorithm completed (%g s)", t2 - t1);
diff --git a/benchmarks/2d/stator_parametric.geo b/benchmarks/2d/stator_parametric.geo
index cf80130ae9ae4ff46a4e9cb3e990ca9f3f296ad7..f04aa0964b36b661e4378a68ff412ff7770fca30 100644
--- a/benchmarks/2d/stator_parametric.geo
+++ b/benchmarks/2d/stator_parametric.geo
@@ -108,3 +108,4 @@ ll = newll;
 Line Loop(ll) = { l1[{1:n}], l2[{1:n}], l3[{1:n}], l4[{1:n}], l5[{1:n}], l6[{1:n}], 
                   l7[{1:n}], -l[{1:n+1}], -links, -rechts, aussen};
 Plane Surface(news) = ll;
+Recombine Surface {10, 21, 32, 43, 54, 65, 76, 87, 98, 109, 116};