diff --git a/Geo/MElement.h b/Geo/MElement.h
index 90de79149e84abe11f99ff9851e6d7f25381bc30..45f3ffdca3eda10045fe12e73e6bfab4573b0423 100644
--- a/Geo/MElement.h
+++ b/Geo/MElement.h
@@ -159,6 +159,7 @@ class MElement
   virtual double gammaShapeMeasure(){ return 0.; }
   virtual double etaShapeMeasure(){ return 0.; }
   virtual double distoShapeMeasure(){ return 1.0; }
+  virtual double angleShapeMeasure() { return 1.0; }
 
   // compute the barycenter
   virtual SPoint3 barycenter();
diff --git a/Geo/MTriangle.cpp b/Geo/MTriangle.cpp
index 45b1b501c7e1fbea9de5a532a033a3612c6b1e04..04be90f28b0a152f7bdf67e49e63c8d1dfc4d7d4 100644
--- a/Geo/MTriangle.cpp
+++ b/Geo/MTriangle.cpp
@@ -32,6 +32,15 @@ double MTriangle::distoShapeMeasure()
 #endif
 }
 
+double MTriangle::angleShapeMeasure()
+{
+#if defined(HAVE_MESH)
+  return qmTriangleAngles(this);
+#else
+  return 0.;
+#endif
+}
+
 double MTriangle::gammaShapeMeasure()
 {
 #if defined(HAVE_MESH)
diff --git a/Geo/MTriangle.h b/Geo/MTriangle.h
index 67172ff3581b9e3ca7b213f6d151579eb3b0554f..b8ec8fef1d87ff88312036c4e80698f8762da5ad 100644
--- a/Geo/MTriangle.h
+++ b/Geo/MTriangle.h
@@ -52,6 +52,7 @@ class MTriangle : public MElement {
   virtual int getDim(){ return 2; }
   virtual double gammaShapeMeasure();
   virtual double distoShapeMeasure();
+  virtual double angleShapeMeasure();
   virtual int getNumVertices() const { return 3; }
   virtual MVertex *getVertex(int num){ return _v[num]; }
   virtual MVertex *getVertexMED(int num)
diff --git a/Mesh/highOrderSmoother.cpp b/Mesh/highOrderSmoother.cpp
index e6e5168e5325ad08b6436ab5f67ef4bccf5e996e..c593b323401a2ddd2919b4f67df43a06d6290c68 100644
--- a/Mesh/highOrderSmoother.cpp
+++ b/Mesh/highOrderSmoother.cpp
@@ -28,6 +28,7 @@
 #include "dofManager.h"
 #include "elasticityTerm.h"
 #include "linearSystemCSR.h"
+#include "linearSystemFull.h"
 
 #define SQU(a)      ((a)*(a))
 
@@ -43,7 +44,8 @@ extern double angle3Points(MVertex *p1, MVertex *p2, MVertex *p3);
 
 static double shapeMeasure(MElement *e)
 {
-  const double d1 = e->distoShapeMeasure();
+  //const double d1 = e->distoShapeMeasure();
+  const double d1 = e->angleShapeMeasure();
   //const double d2 = e->gammaShapeMeasure();
   return d1;
 }
@@ -365,7 +367,7 @@ void highOrderSmoother::optimize(GFace * gf,
     //    }
     // then try to swap for better configurations  
 
-    smooth(gf, true);
+    //smooth(gf, true);
     //int nbSwap = 
         swapHighOrderTriangles(gf,edgeVertices,faceVertices,this);
     // smooth(gf,true);
@@ -711,6 +713,128 @@ void highOrderSmoother::smooth(std::vector<MElement*> &all)
   delete lsys;
 }
 
+void highOrderSmoother::smooth_cavity(std::vector<MElement*>& cavity,
+				       std::vector<MElement*>& old_elems,
+				       GFace* gf) {
+
+  printf("Smoothing a cavity...");
+
+  linearSystemFull<double> *lsys = new linearSystemFull<double>;
+
+  dofManager<double> myAssembler(lsys);
+  elasticityTerm El(0,1.0,0.333, getTag());
+
+  std::vector<MElement*> layer,v;
+
+  v.insert(v.begin(), gf->triangles.begin(),gf->triangles.end());
+  v.insert(v.begin(), gf->quadrangles.begin(),gf->quadrangles.end());
+
+  addOneLayer(v,cavity,layer);
+  
+  for (unsigned int i = 0; i < layer.size(); i++){
+    bool skip = false;
+    for (std::vector<MElement*>::iterator itold = old_elems.begin();
+	 itold != old_elems.end();
+	 itold++)
+      if (*itold == layer[i]) {
+	skip = true;
+	break;
+      }
+    if (skip) continue;
+    for (int j = 0; j < layer[i]->getNumVertices(); j++){
+      MVertex *vert = layer[i]->getVertex(j);
+      //printf("i = %d, j = %d \n",i,j);
+      myAssembler.fixVertex(vert, 0, getTag(), 0);
+      myAssembler.fixVertex(vert, 1, getTag(), 0);
+    }
+  }
+  
+  std::map<MVertex*,SVector3>::iterator its;
+  std::set<MVertex*> verticesToMove;
+
+  for (unsigned int i = 0; i < cavity.size(); i++){
+    for (int j = 0; j < cavity[i]->getNumVertices(); j++){
+      MVertex *vert = cavity[i]->getVertex(j);
+      //printf("Vertex on dim %d with tag %d\n",vert->onWhat()->dim(),vert->onWhat()->tag());
+
+      // printf("%d %d %d v\n", i, j, v[i]->getNumVertices());
+      its = _straightSidedLocation.find(vert);
+      if (its == _straightSidedLocation.end()){
+	//printf("Ping\n");
+        _straightSidedLocation[vert] = 
+          SVector3(vert->x(), vert->y(), vert->z());     
+        _targetLocation[vert] = 
+          SVector3(vert->x(), vert->y(), vert->z()); 
+        if (vert->onWhat()->dim() < _dim){
+          myAssembler.fixVertex(vert, 0, getTag(), 0);
+          myAssembler.fixVertex(vert, 1, getTag(), 0);
+        }    
+      }
+      else{
+	//printf("Pong\n");
+        vert->x() = its->second.x();
+        vert->y() = its->second.y();
+        vert->z() = its->second.z();
+        if (vert->onWhat()->dim() < _dim){
+          myAssembler.fixVertex(vert, 0, getTag(), 0);
+          myAssembler.fixVertex(vert, 1, getTag(), 0);
+        }
+      }
+    }
+  }
+
+  // number the other DOFs
+  for (unsigned int i = 0; i < cavity.size(); i++){
+    for (int j = 0; j < cavity[i]->getNumVertices(); j++){
+      //printf("Numbering i = %d, j = %d \n",i,j);
+      //printf("%d vertices FIXED %d NUMBERED\n", myAssembler.sizeOfF()
+      //      , myAssembler.sizeOfR());
+      MVertex *vert = cavity[i]->getVertex(j);
+      myAssembler.numberVertex(vert, 0, getTag());
+      myAssembler.numberVertex(vert, 1, getTag());
+      verticesToMove.insert(vert);
+    } 
+  }
+
+  //Msg::Info("%d vertices FIXED %d NUMBERED\n", myAssembler.sizeOfF()
+  //          , myAssembler.sizeOfR());
+
+  double dx0 = smooth_metric_(cavity, gf, myAssembler, verticesToMove, El);
+  double dx = dx0;
+  printf(" dx0 = %12.5E\n", dx0);
+  int iter = 0;
+  while(1){
+    double dx2 = smooth_metric_(cavity, gf, myAssembler, verticesToMove, El);
+    printf(" dx2  = %12.5E\n", dx2);
+    if (fabs(dx2 - dx) < 1.e-4 * dx0)break;
+    if (iter++ > 2)break;
+    dx = dx2;
+  }
+
+  std::set<MVertex*>::iterator it;
+
+  for (it = verticesToMove.begin(); it != verticesToMove.end(); ++it){
+    SPoint2 param;
+    reparamMeshVertexOnFace(*it, gf, param);  
+    GPoint gp = gf->point(param);    
+    if ((*it)->onWhat()->dim() == 2){
+      (*it)->x() = gp.x();
+      (*it)->y() = gp.y();
+      (*it)->z() = gp.z();
+      _targetLocation[*it] = SVector3(gp.x(), gp.y(), gp.z());
+    }
+    else{
+      SVector3 p =  _targetLocation[(*it)];
+      (*it)->x() = p.x();
+      (*it)->y() = p.y();
+      (*it)->z() = p.z();      
+    }
+  }
+ 
+  delete lsys;
+}
+
+
 /*
   n3    n23     n2
   x-----x-------x
@@ -988,7 +1112,13 @@ struct swap_triangles_pN
                        !t1->getNumFaceVertices(),
                        t1->getPolynomialOrder()-1,s);
     
-    optimalLocationPN_ (gf,me, t3, t4,s);
+    //optimalLocationPN_ (gf,me, t3, t4,s);
+    std::vector<MElement*> cavity, old_elems;
+    cavity.push_back(t3);
+    cavity.push_back(t4);
+    old_elems.push_back(t1);
+    old_elems.push_back(t2);
+    s->smooth_cavity(cavity, old_elems, gf);
       
     const double qnew1 = shapeMeasure(t3);
     const double qnew2 = shapeMeasure(t4);
@@ -1047,7 +1177,8 @@ static int optimalLocationP2_(GFace *gf,
   n12->y() = gp12.y();
   n12->z() = gp12.z();
   n12->setParameter(0,pp(0));
-  n12->setParameter(1,pp(1));      
+  n12->setParameter(1,pp(1));
+  printf("Hum, order 2 here...");
   return 1;
 }
 
@@ -1151,11 +1282,10 @@ static int swapHighOrderTriangles(GFace *gf,
     if (it->second.second){
       MTriangle *t1 = dynamic_cast<MTriangle*>(it->second.first);
       MTriangle *t2 = dynamic_cast<MTriangle*>(it->second.second);
+
       const double qold1 = shapeMeasure(t1);
       const double qold2 = shapeMeasure(t2);
 
-      //      printf("swap : %g %g\n",qold1,qold2);
-
       if (qold1 < 0.6 || qold2 < 0.6)
         pairs.insert(swap_triangles_pN(it->first,t1,t2,gf,
                                        edgeVertices,faceVertices,s));
@@ -1174,29 +1304,101 @@ static int swapHighOrderTriangles(GFace *gf,
   itp = pairs.begin();
   while(itp != pairs.end()){
     double diff = fabs(itp->s_before - itp->s_after);
+
+    std::vector<MVertex*> v1,v2,v3,v4;
+    int o1 = itp->t1->getPolynomialOrder();
+    int o2 = itp->t2->getPolynomialOrder();
+    int o3 = itp->t3->getPolynomialOrder();
+    int o4 = itp->t4->getPolynomialOrder();
+
+    itp->t1->getFaceVertices(0,v1);
+    itp->t2->getFaceVertices(0,v2);
+    itp->t3->getFaceVertices(0,v3);
+    itp->t4->getFaceVertices(0,v4);
+    std::vector<MVertex*> ve1(v1.begin()+3,v1.begin()+3*o1);
+    std::vector<MVertex*> ve2(v2.begin()+3,v2.begin()+3*o2);
+    std::vector<MVertex*> ve3(v3.begin()+3,v3.begin()+3*o3);
+    std::vector<MVertex*> ve4(v4.begin()+3,v4.begin()+3*o4);
+    std::vector<MVertex*> vf1(v1.begin()+3*o1,v1.end());
+    std::vector<MVertex*> vf2(v2.begin()+3*o2,v2.end());
+    std::vector<MVertex*> vf3(v3.begin()+3*o3,v3.end());
+    std::vector<MVertex*> vf4(v4.begin()+3*o4,v4.end());
+
+    //    for (int ed = 0; ed < 3; ed++) {
+    //  std::vector<MVertex*> v1,v2,v3,v4;
+    //  itp->t1->getEdgeVertices(ed,v1);
+    //  itp->t2->getEdgeVertices(ed,v2);
+    //  itp->t3->getEdgeVertices(ed,v3);
+    //  itp->t4->getEdgeVertices(ed,v4);
+    //  ve1.insert(ve1.end(),v1.begin()+2,v1.end());
+    //  ve2.insert(ve2.end(),v2.begin()+2,v2.end());
+    //  ve3.insert(ve3.end(),v3.begin()+2,v3.end());
+    //  ve4.insert(ve4.end(),v4.begin()+2,v4.end()); 
+    // }
+
+
     if ( t_removed.find(itp->t1) == t_removed.end() &&
          t_removed.find(itp->t2) == t_removed.end() &&
          itp->quality_new > itp->quality_old &&
          diff < 1.e-9){
       //      itp->print();
+      printf("quality was %f, is now %f\n",itp->quality_old,itp->quality_new); 
       t_removed.insert(itp->t1);
       t_removed.insert(itp->t2);
       triangles2.push_back(itp->t3);
       triangles2.push_back(itp->t4);
+
+      v_removed.insert(vf1.begin(),vf1.end());
+      v_removed.insert(vf2.begin(),vf2.end());
+      mesh_vertices2.insert(mesh_vertices2.end(),vf3.begin(),vf3.end());
+      mesh_vertices2.insert(mesh_vertices2.end(),vf4.begin(),vf4.end());
+
+      for(std::vector<MVertex*>::iterator vit = ve1.begin(); vit != ve1.end(); vit++) {
+	if (find(ve2.begin(),ve2.begin(),*vit)!=ve2.end())
+	  v_removed.insert(*vit);
+      }
+      for(std::vector<MVertex*>::iterator vit = ve3.begin(); vit != ve3.end(); vit++) {
+	if (find(ve4.begin(),ve4.end(),*vit)!=ve4.end())
+	  mesh_vertices2.push_back(*vit);
+      }
+
       //      if (itp->n34 != itp->n12){
-        //      v_removed.insert(itp->n12);
-        //      mesh_vertices2.push_back(itp->n34);
+      //        v_removed.insert(itp->n12);
+      //        mesh_vertices2.push_back(itp->n34);
       //      }
       nbSwap++;
     }
     else{
+      for(std::vector<MVertex*>::iterator vit = ve1.begin(); vit != ve1.end(); vit++) {
+	if (find(ve2.begin(),ve2.end(),*vit)!=ve2.end())
+	  mesh_vertices2.push_back(*vit);
+      }
+      for(std::vector<MVertex*>::iterator vit = ve3.begin(); vit != ve3.end(); vit++) {
+	if (find(ve4.begin(),ve4.end(),*vit)!=ve4.end())
+	  v_removed.insert(*vit);
+      }
       delete itp->t3;
       delete itp->t4;
+      
       //      if (itp->n34 != itp->n12) delete itp->n34;
     }
     ++itp;
   }
-  
+  int c1=0,c2=0;
+  for (unsigned int i = 0; i < gf->mesh_vertices.size(); i++){
+    if (v_removed.find(gf->mesh_vertices[i]) == v_removed.end()){
+      //mesh_vertices2.push_back(gf->mesh_vertices[i]);
+      c1++;
+    }
+    else {
+      delete gf->mesh_vertices[i];
+      c2++;
+    }    
+  }
+  gf->mesh_vertices = mesh_vertices2;
+  printf("Deleted %d vertices from %d\n",c2,gf->mesh_vertices.size());
+  printf("Added %d vertices\n",c1);
+
   for (unsigned int i = 0; i < gf->triangles.size(); i++){
     if (t_removed.find(gf->triangles[i]) == t_removed.end()){
       triangles2.push_back(gf->triangles[i]);
@@ -1205,7 +1407,8 @@ static int swapHighOrderTriangles(GFace *gf,
       delete gf->triangles[i];
     }    
   }
-  //  printf("replacing %d by %d\n",gf->triangles.size(),triangles2.size());
+
+  printf("replacing %d by %d\n",gf->triangles.size(),triangles2.size());
   gf->triangles = triangles2;
   return nbSwap;
 }
diff --git a/Mesh/highOrderSmoother.h b/Mesh/highOrderSmoother.h
index 3b4379fbc00a6e4678dc1174fb8c6e2c42c3b046..be8cf2f307e2b3236c8f22ae92c3324683a15bd1 100644
--- a/Mesh/highOrderSmoother.h
+++ b/Mesh/highOrderSmoother.h
@@ -52,6 +52,9 @@ public:
   void smooth_p2point(GFace *);
   void smooth_pNpoint(GFace *);
   void smooth(GRegion*);
+  void smooth_cavity(std::vector<MElement*> &,
+		     std::vector<MElement*> &,
+		     GFace *gf);
   int getTag() const { return _tag; }
   void swap(GFace *, 
             edgeContainer &edgeVertices,
diff --git a/Mesh/multiscalePartition.cpp b/Mesh/multiscalePartition.cpp
index 8cf1826f0fc394cbf8781c28951e48cd4388b622..6075ae69a90f94ee99151cbd99e9f86bfe8c0d84 100644
--- a/Mesh/multiscalePartition.cpp
+++ b/Mesh/multiscalePartition.cpp
@@ -102,8 +102,8 @@ static int getAspectRatio (std::vector<MElement *> &elements,
       vs.insert(e->getVertex(j));
     }
   }
- SBoundingBox3d bb;
- SOrientedBoundingBox obbox ;
+  SBoundingBox3d bb;
+  SOrientedBoundingBox obbox ;
   std::vector<SPoint3> vertices;
   for (std::set<MVertex* >::iterator it = vs.begin(); it != vs.end(); it++){
     SPoint3 pt((*it)->x(),(*it)->y(), (*it)->z());
diff --git a/Mesh/qualityMeasures.cpp b/Mesh/qualityMeasures.cpp
index a4bc0b5b97e4826fd920ede441b377ce7723d548..ec701c9c61a06c08a3bacd8b86ea12ba4056bb8e 100644
--- a/Mesh/qualityMeasures.cpp
+++ b/Mesh/qualityMeasures.cpp
@@ -11,6 +11,7 @@
 #include "Numeric.h"
 #include "polynomialBasis.h"
 #include "GmshMessage.h"
+#include <limits>
 
 double qmTriangle(const BDS_Point *p1, const BDS_Point *p2, const BDS_Point *p3, 
                   const qualityMeasure4Triangle &cr)
@@ -293,3 +294,59 @@ double qmDistorsionOfMapping(MTetrahedron *e)
 
   return dmin;
 }
+
+double qmTriangleAngles (MTriangle *e) {
+  double a = 100;
+  double worst_quality = std::numeric_limits<double>::max();
+  double mat[3][3];
+  double mat2[3][3];
+  double den = atan(a*(M_PI/9)) + atan(a*(2*M_PI/9 - (M_PI/9)));
+
+  // This matrix is used to "rotate" the triangle to get each vertex
+  // as the "origin" of the mapping in turn 
+  double rot[3][3];
+  rot[0][0]=-1; rot[0][1]=1; rot[0][2]=0;
+  rot[1][0]=-1; rot[1][1]=0; rot[1][2]=0;
+  rot[2][0]= 0; rot[2][1]=0; rot[2][2]=1;
+  double tmp[3][3];
+
+  for (int i = 0; i < e->getNumPrimaryVertices(); i++) {
+    const double u = i == 1 ? 1 : 0;
+    const double v = i == 2 ? 1 : 0;
+    const double w = 0;
+    e->getJacobian(u, v, w, mat);
+    e->getPrimaryJacobian(u,v,w,mat2);
+    for (int j = 0; j < i; j++) {
+      matmat(rot,mat,tmp);
+      memcpy(mat, tmp, sizeof(mat)); 
+    }
+    //get angle
+    double v1[3] = {mat[0][0],  mat[0][1],  mat[0][2] };
+    double v2[3] = {mat[1][0],  mat[1][1],  mat[1][2] };
+    double v3[3] = {mat2[0][0],  mat2[0][1],  mat2[0][2] };
+    double v4[3] = {mat2[1][0],  mat2[1][1],  mat2[1][2] };
+    norme(v1);
+    norme(v2);
+    norme(v3);
+    norme(v4);
+    double v12[3], v34[3];
+    prodve(v1,v2,v12);
+    prodve(v3,v4,v34);
+    norme(v12); 
+    norme(v34);
+    double orientation;
+    prosca(v12,v34,&orientation);
+
+    // If the if the triangle is "flipped" it's no good
+    if (orientation < 0)
+      return -std::numeric_limits<double>::max();
+
+    double c;
+    prosca(v1,v2,&c);
+    double x = acos(c)-M_PI/3;
+    double quality = (atan(a*(x+M_PI/9)) + atan(a*(2*M_PI/9 - (x+M_PI/9))))/den;
+    worst_quality = std::min(worst_quality, quality);
+  }
+
+  return worst_quality;
+}
diff --git a/Mesh/qualityMeasures.h b/Mesh/qualityMeasures.h
index 0daf095b5021429e87f0b4eea2722e1297f52ccb..cacf55add0a5e8d777fc16daae34f6acafd961b2 100644
--- a/Mesh/qualityMeasures.h
+++ b/Mesh/qualityMeasures.h
@@ -19,6 +19,8 @@ enum qualityMeasure4Tet{QMTET_1, QMTET_2, QMTET_3, QMTET_ONE, QMTET_COND};
 double qmDistorsionOfMapping (MTriangle *e);
 double qmDistorsionOfMapping (MTetrahedron *e);
 
+double qmTriangleAngles (MTriangle *e);
+
 double qmTriangle(MTriangle *f, const qualityMeasure4Triangle &cr); 
 double qmTriangle(BDS_Face *f, const qualityMeasure4Triangle &cr); 
 double qmTriangle(const BDS_Point *p1, const BDS_Point *p2, const BDS_Point *p3, 
diff --git a/Numeric/Numeric.cpp b/Numeric/Numeric.cpp
index 3f110c426f955324b1557134b23fcde4a789d1a8..3de18b979b942f1ee731ba5a546db3511814e13c 100644
--- a/Numeric/Numeric.cpp
+++ b/Numeric/Numeric.cpp
@@ -43,6 +43,19 @@ void matvec(double mat[3][3], double vec[3], double res[3])
   res[2] = mat[2][0] * vec[0] + mat[2][1] * vec[1] + mat[2][2] * vec[2];
 }
 
+void matmat(double mat1[3][3], double mat2[3][3], double res[3][3])
+{
+  res[0][0] = mat1[0][0]*mat2[0][0] + mat1[0][1]*mat2[1][0] + mat1[0][2]*mat2[2][0];
+  res[0][1] = mat1[0][0]*mat2[0][1] + mat1[0][1]*mat2[1][1] + mat1[0][2]*mat2[2][1];
+  res[0][2] = mat1[0][0]*mat2[0][2] + mat1[0][1]*mat2[1][2] + mat1[0][2]*mat2[2][2];
+  res[1][0] = mat1[1][0]*mat2[0][0] + mat1[1][1]*mat2[1][0] + mat1[1][2]*mat2[2][0];
+  res[1][1] = mat1[1][0]*mat2[0][1] + mat1[1][1]*mat2[1][1] + mat1[1][2]*mat2[2][1];
+  res[1][2] = mat1[1][0]*mat2[0][2] + mat1[1][1]*mat2[1][2] + mat1[1][2]*mat2[2][2];
+  res[2][0] = mat1[2][0]*mat2[0][0] + mat1[2][1]*mat2[1][0] + mat1[2][2]*mat2[2][0];
+  res[2][1] = mat1[2][0]*mat2[0][1] + mat1[2][1]*mat2[1][1] + mat1[2][2]*mat2[2][1];
+  res[2][2] = mat1[2][0]*mat2[0][2] + mat1[2][1]*mat2[1][2] + mat1[2][2]*mat2[2][2];
+}
+
 void normal3points(double x0, double y0, double z0,
                    double x1, double y1, double z1,
                    double x2, double y2, double z2,
diff --git a/Numeric/Numeric.h b/Numeric/Numeric.h
index 29db8d3d60a1da570bb6415f37759ec2370f862f..9678825457fccf2761f8591f74e6865389ffd83e 100644
--- a/Numeric/Numeric.h
+++ b/Numeric/Numeric.h
@@ -29,6 +29,7 @@ inline void prosca(double a[3], double b[3], double *c)
   *c = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
 }
 void matvec(double mat[3][3], double vec[3], double res[3]);
+void matmat(double mat1[3][3], double mat2[3][3], double res[3][3]);
 inline double norm3(double a[3])
 {
   return sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);