diff --git a/Fltk/statisticsWindow.cpp b/Fltk/statisticsWindow.cpp
index 81e03d992f15fdd55a18a16174cec322f071ddc4..a4499b78692cbcaa95c1dc78ccfe872c3b162d1f 100644
--- a/Fltk/statisticsWindow.cpp
+++ b/Fltk/statisticsWindow.cpp
@@ -51,23 +51,25 @@ static void statistics_histogram_cb(Fl_Widget *w, void *data)
     new PView("Disto", "# Elements", x, y);
   }
   else{
-    std::vector<GEntity*> entities;
-    GModel::current()->getEntities(entities);
+    
+    std::vector<GEntity*> entities_;
+    GModel::current()->getEntities(entities_);
     std::map<int, std::vector<double> > d;
-    for(unsigned int i = 0; i < entities.size(); i++){
-      if(entities[i]->dim() < 2) continue;
-      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
-        MElement *e = entities[i]->getMeshElement(j);
-        if(name == "Gamma3D")
-          d[e->getNum()].push_back(e->gammaShapeMeasure());
-        else if(name == "Eta3D")
-          d[e->getNum()].push_back(e->etaShapeMeasure());
-        else if(name == "Rho3D")
-          d[e->getNum()].push_back(e->rhoShapeMeasure());
-        else
-          d[e->getNum()].push_back(e->distoShapeMeasure());
+    for(unsigned int i = 0; i < entities_.size(); i++){
+      if(entities_[i]->dim() < 2) continue;
+      for(unsigned int j = 0; j < entities_[i]->getNumMeshElements(); j++){
+	MElement *e = entities_[i]->getMeshElement(j);
+	if(name == "Gamma3D")
+	  d[e->getNum()].push_back(e->gammaShapeMeasure());
+	else if(name == "Eta3D")
+	  d[e->getNum()].push_back(e->etaShapeMeasure());
+	else if(name == "Rho3D")
+	  d[e->getNum()].push_back(e->rhoShapeMeasure());
+	else
+	  d[e->getNum()].push_back(e->distoShapeMeasure());
       }
     }
+    
     name.resize(name.size() - 2);
     new PView(name, "ElementData", GModel::current(), d);
   }
@@ -186,7 +188,7 @@ statisticsWindow::statisticsWindow(int deltaFontSize)
 void statisticsWindow::compute(bool elementQuality)
 {
   //emi hack - MINIMUM ANGLES
-  /*double minAngle = 120.0;
+  double minAngle = 120.0;
   double meanAngle = 0.0;
   int count = 0;
   std::vector<GEntity*> entities;
@@ -194,17 +196,97 @@ void statisticsWindow::compute(bool elementQuality)
   std::map<int, std::vector<double> > d;
   for(unsigned int i = 0; i < entities.size(); i++){
     if(entities[i]->dim() < 2) continue;
-	for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
-	  MElement *e = entities[i]->getMeshElement(j);
-	  double angle = e->angleShapeMeasure();
-	  minAngle = std::min(minAngle, angle);
-	  meanAngle += angle;
-	  count++;
-	}
+    for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
+      MElement *e = entities[i]->getMeshElement(j);
+      double angle = e->angleShapeMeasure();
+      minAngle = std::min(minAngle, angle);
+      meanAngle += angle;
+      count++;
+    }
   }
   meanAngle  = meanAngle / count;
-  printf("Angles = min=%g av=%g \n", minAngle, meanAngle);*/
-  //hack emi
+  printf("Angles = min=%g av=%g \n", minAngle, meanAngle);
+  //  Emi hack - MESH DEGREE VERTICES
+  //  std::vector<GEntity*> entities;
+  std::set<MEdge, Less_Edge> edges;
+  GModel::current()->getEntities(entities);
+  std::map<MVertex*, int > vert2Deg;
+  for(unsigned int i = 0; i < entities.size(); i++){
+    //    printf("entity dim =%d tag=%d \n", entities[i]->dim() , entities[i]->tag());
+    if(entities[i]->dim() < 2 ) continue;
+    if(entities[i]->tag() < 100) continue;
+    //    printf("continuing \n");
+    for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
+      MElement *e =  entities[i]->getMeshElement(j);
+      for(unsigned int k = 0; k < e->getNumEdges(); k++){
+  	edges.insert(e->getEdge(k));
+      }
+      for(unsigned int k = 0; k < e->getNumVertices(); k++){
+  	MVertex *v = e->getVertex(k);
+  	if (v->onWhat()->dim() < 2) continue; 
+  	std::map<MVertex*, int >::iterator it = vert2Deg.find(v);
+  	if (it == vert2Deg.end()) {
+  	  vert2Deg.insert(std::make_pair(v,1));
+  	}
+  	else{
+  	  int nbE = it->second+1;
+  	  it->second = nbE;
+  	}
+      }
+    }
+  }
+  int dMin = 10;
+  int dMax = 0;
+  int d4 = 0;
+  int nbElems = vert2Deg.size();
+  std::map<MVertex*, int >::const_iterator itmap = vert2Deg.begin();
+  for(; itmap !=vert2Deg.end(); itmap++){
+    MVertex *v = itmap->first;
+    int nbE =  itmap->second;
+    dMin = std::min(nbE, dMin);
+    dMax = std::max(nbE, dMax);
+    if (nbE == 4) d4 += 1;
+  }
+  if (nbElems > 0)
+    printf("Stats degree vertices: dMin=%d , dMax=%d, d4=%g \n", dMin, dMax, (double)d4/nbElems);
+
+  FieldManager *fields = GModel::current()->getFields();
+  Field *f = fields->get(fields->background_field);
+  int nbEdges = edges.size();
+  printf("nb edges =%d \n", nbEdges);
+  system("rm qualEdges.txt");
+  FILE *fp = fopen("qualEdges.txt", "w");
+  std::vector<int> qualE;
+  int nbS = 50;
+  qualE.resize(nbS);
+  if(fields->background_field > 0){
+    printf("found field \n");
+    std::set<MEdge, Less_Edge>::iterator it = edges.begin();
+    double sum = 0;
+    for (; it !=edges.end();++it){
+      MVertex *v0 = it->getVertex(0);
+      MVertex *v1 = it->getVertex(1);
+      double l = sqrt((v0->x()-v1->x())*(v0->x()-v1->x())+
+  		      (v0->y()-v1->y())*(v0->y()-v1->y())+
+  		      (v0->z()-v1->z())*(v0->z()-v1->z()));
+      double lf =  (*f)(0.5*(v0->x()+v1->x()), 0.5*(v0->y()+v1->y()),0.5*(v0->z()+v1->z()),v0->onWhat());
+      double el = l/lf;
+      int index = (int) ceil(el*nbS*0.5);
+      qualE[index]+= 1;
+      double e = (l>lf) ? lf/l : l/lf;
+      sum += e - 1.0;
+    }
+    double tau = exp ((1./edges.size()) * sum);
+    printf("N edges = %d tau = %g\n",(int)edges.size(),tau);
+
+    double ibegin = 2./(2*nbS);
+    double inext = 2./nbS;
+    for (int i= 0; i< qualE.size(); i++){
+      fprintf(fp, "0 0 0 0 %g 0 0 %g \n", ibegin+i*inext , (double)qualE[i]/nbEdges);
+    }
+
+  }
+  fclose(fp);
   //Emi hack - MESH DEGREE VERTICES
 #if 0
   std::vector<GEntity*> entities;
diff --git a/Geo/GFaceCompound.cpp b/Geo/GFaceCompound.cpp
index e62438a7ccfa877f37cedbf40e147295e4af6894..280e5554d85b8c491e682567d812436704da6def 100644
--- a/Geo/GFaceCompound.cpp
+++ b/Geo/GFaceCompound.cpp
@@ -450,6 +450,7 @@ void GFaceCompound::fillNeumannBCS() const
 
 bool GFaceCompound::trivial() const
 {
+  return false;
   if(_compound.size() == 1 && 
      (*(_compound.begin()))->getNativeType() == GEntity::OpenCascadeModel &&
      (*(_compound.begin()))->geomType() != GEntity::DiscreteSurface &&
@@ -1763,9 +1764,24 @@ void GFaceCompound::buildOct() const
       _gfct[count].p2 = it1->second;
       _gfct[count].p3 = it2->second;
       if((*it)->geomType() != GEntity::DiscreteSurface){
-        reparamMeshVertexOnFace(t->getVertex(0), *it, _gfct[count].gfp1); 
-        reparamMeshVertexOnFace(t->getVertex(1), *it, _gfct[count].gfp2); 
-        reparamMeshVertexOnFace(t->getVertex(2), *it, _gfct[count].gfp3); 
+	// take care of the seam !!!!
+	if (t->getVertex(0)->onWhat()->dim() == 2){
+	  reparamMeshEdgeOnFace(t->getVertex(0), t->getVertex(1),*it, _gfct[count].gfp1, _gfct[count].gfp2); 
+	  reparamMeshEdgeOnFace(t->getVertex(0), t->getVertex(2),*it, _gfct[count].gfp1, _gfct[count].gfp3); 
+	}
+	else if (t->getVertex(1)->onWhat()->dim() == 2){
+	  reparamMeshEdgeOnFace(t->getVertex(1), t->getVertex(0),*it, _gfct[count].gfp2, _gfct[count].gfp1); 
+	  reparamMeshEdgeOnFace(t->getVertex(1), t->getVertex(2),*it, _gfct[count].gfp2, _gfct[count].gfp3); 
+	}
+	else if (t->getVertex(2)->onWhat()->dim() == 2){
+	  reparamMeshEdgeOnFace(t->getVertex(2), t->getVertex(0),*it, _gfct[count].gfp3, _gfct[count].gfp1); 
+	  reparamMeshEdgeOnFace(t->getVertex(2), t->getVertex(1),*it, _gfct[count].gfp3, _gfct[count].gfp2); 
+	}
+	else {
+	  reparamMeshVertexOnFace(t->getVertex(0), *it, _gfct[count].gfp1); 
+	  reparamMeshVertexOnFace(t->getVertex(1), *it, _gfct[count].gfp2); 
+	  reparamMeshVertexOnFace(t->getVertex(2), *it, _gfct[count].gfp3); 
+	}
       }
       _gfct[count].v1 = SPoint3(t->getVertex(0)->x(), t->getVertex(0)->y(),
                                 t->getVertex(0)->z());      
diff --git a/Geo/MVertex.cpp b/Geo/MVertex.cpp
index fd431b0e54f2e5a43d5f6412c29c8c703b2aecef..b59e0e4422456faea67090c587141b70c89721e0 100644
--- a/Geo/MVertex.cpp
+++ b/Geo/MVertex.cpp
@@ -324,10 +324,10 @@ bool reparamMeshEdgeOnFace(MVertex *v1, MVertex *v2, GFace *gf,
   else if (p1.size() == 1 && p2.size() == 2){
     double d1 = 
       (p1[0].x() - p2[0].x()) * (p1[0].x() - p2[0].x()) +
-      (p1[0].x() - p2[0].y()) * (p1[0].y() - p2[0].y());
+      (p1[0].y() - p2[0].y()) * (p1[0].y() - p2[0].y());
     double d2 = 
       (p1[0].x() - p2[1].x()) * (p1[0].x() - p2[1].x()) +
-      (p1[0].x() - p2[1].y()) * (p1[0].y() - p2[1].y());
+      (p1[0].y() - p2[1].y()) * (p1[0].y() - p2[1].y());
     param1 = p1[0];
     param2 = d2 < d1 ? p2[1] : p2[0];
     return true;
@@ -335,10 +335,10 @@ bool reparamMeshEdgeOnFace(MVertex *v1, MVertex *v2, GFace *gf,
   else if (p2.size() == 1 && p1.size() == 2){
     double d1 = 
       (p2[0].x() - p1[0].x()) * (p2[0].x() - p1[0].x()) +
-      (p2[0].x() - p1[0].y()) * (p2[0].y() - p1[0].y());
+      (p2[0].y() - p1[0].y()) * (p2[0].y() - p1[0].y());
     double d2 = 
       (p2[0].x() - p1[1].x()) * (p2[0].x() - p1[1].x()) +
-      (p2[0].x() - p1[1].y()) * (p2[0].y() - p1[1].y());
+      (p2[0].y() - p1[1].y()) * (p2[0].y() - p1[1].y());
     param1 = d2 < d1 ? p1[1] : p1[0];
     param2 = p2[0];
     return true;
@@ -346,6 +346,9 @@ bool reparamMeshEdgeOnFace(MVertex *v1, MVertex *v2, GFace *gf,
   else if(p1.size() > 1 && p2.size() > 1){
     param1 = p1[0];
     param2 = p2[0];
+
+    printf("NO WAY : TWO VERTICES ON THE SEAM, CANNOT CHOOSE\n");
+    
     // shout, both vertices are on seams
     return false;
   }
@@ -357,6 +360,8 @@ bool reparamMeshEdgeOnFace(MVertex *v1, MVertex *v2, GFace *gf,
   }
 }
 
+
+
 bool reparamMeshVertexOnFace(const MVertex *v, const GFace *gf, SPoint2 &param,
                              bool onSurface)
 {
diff --git a/Geo/MVertex.h b/Geo/MVertex.h
index a931a7a9d0f820057797e98103bbc4ebf69dfb47..3e6e52c1746cb365715fcbcf2fd76e8dd81c82c8 100644
--- a/Geo/MVertex.h
+++ b/Geo/MVertex.h
@@ -145,10 +145,10 @@ class MFaceVertex : public MVertex{
   MVertexBoundaryLayerData* bl_data;
 
   MFaceVertex(double x, double y, double z, GEntity *ge, double u, double v, int num = 0) 
-    : MVertex(x, y, z, ge, num), _u(u), _v(v)
+    : MVertex(x, y, z, ge, num), _u(u), _v(v),bl_data(0)
   {
   }
-  virtual ~MFaceVertex(){}
+  virtual ~MFaceVertex(){if(bl_data) delete bl_data;}
   virtual bool getParameter(int i, double &par) const { par = (i ? _v : _u); return true; }
   virtual bool setParameter(int i, double par)
   {
diff --git a/Geo/OCCFace.cpp b/Geo/OCCFace.cpp
index fdbdedea78f9fee43aa9c0ebf276a0348d03722e..ebf4db718a2022a9ec4b4df38ed4323756901263 100644
--- a/Geo/OCCFace.cpp
+++ b/Geo/OCCFace.cpp
@@ -53,7 +53,7 @@ void OCCFace::setup()
       }
       else{
         l_wire.push_back(e);
-        Msg::Debug("Edge %d ori %d", e->tag(), edge.Orientation());
+        Msg::Debug("Edge %d (%d --> %d) ori %d", e->tag(), e->getBeginVertex()->tag(), e->getEndVertex()->tag(), edge.Orientation());
         e->addFace(this);
         if(!e->is3D()){
           OCCEdge *occe = (OCCEdge*)e;
@@ -95,17 +95,12 @@ void OCCFace::setup()
   umax += fabs(du) / 100.0;
   vmax += fabs(dv) / 100.0;
   occface = BRep_Tool::Surface(s);
-  // specific parametrization for a sphere.
-  _isSphere = isSphere(_radius,_center);
-  if (_isSphere){
-    for (std::list<GEdge*>::iterator it = l_edges.begin();
-	 it != l_edges.end(); ++it){
-      GEdge *ge = *it;
-      if (ge->isSeam(this)){
-	
-      }
-    }    
-  }
+
+  //  std::list<GEdge*>::const_iterator it = l_edges.begin();
+  //  for (; it != l_edges.end(); ++it){
+  //    printf("edge %d : %d %d iseam %d \n",(*it)->tag(),(*it)->getBeginVertex()->tag(),(*it)->getEndVertex()->tag(),(*it)->isSeam(this));
+  //  }
+
 }
 
 Range<double> OCCFace::parBounds(int i) const
diff --git a/Geo/STensor3.cpp b/Geo/STensor3.cpp
index 20e6594644ce449cdf369c6304b0b9d4e7c19445..7436bc7a510317408082580da8388b3a4d9d37fa 100644
--- a/Geo/STensor3.cpp
+++ b/Geo/STensor3.cpp
@@ -33,6 +33,22 @@ SMetric3 intersection (const SMetric3 &m1, const SMetric3 &m2)
   return iv;
 }
 
+// preserve orientation of m1 !!!
+SMetric3 intersection_conserveM1 (const SMetric3 &m1, const SMetric3 &m2)
+{
+  fullMatrix<double> V(3,3);
+  fullVector<double> S(3);
+  m1.eig(V,S,true);
+  SVector3 v0(V(0,0),V(1,0),V(2,0));
+  SVector3 v1(V(0,1),V(1,1),V(2,1));
+  SVector3 v2(V(0,2),V(1,2),V(2,2));
+  double l0 = std::max(dot(v0,m1,v0),dot(v0,m2,v0));
+  double l1 = std::max(dot(v1,m1,v1),dot(v1,m2,v1));
+  double l2 = std::max(dot(v2,m1,v2),dot(v2,m2,v2));
+  SMetric3 iv(l0,l1,l2,v0,v1,v2);
+  return iv;
+}
+
 // (1-t) * m1 + t * m2
 SMetric3 interpolation (const SMetric3 &m1, 
                                const SMetric3 &m2, 
diff --git a/Geo/STensor3.h b/Geo/STensor3.h
index 3562f71e904800419d5a308000f2257e994d4444..a039a045fb4884f684c3a77e049f99c4df92a567 100644
--- a/Geo/STensor3.h
+++ b/Geo/STensor3.h
@@ -163,6 +163,8 @@ inline double dot(const SVector3 &a, const SMetric3 &m, const SVector3 &b)
     b.z() * ( m(0,2) * a.x() + m(1,2) * a.y() + m(2,2) * a.z() ) ;
 }
 
+SMetric3 intersection_conserveM1 (const SMetric3 &m1,
+				  const SMetric3 &m2);
 // compute the largest inscribed ellipsoid...
 SMetric3 intersection (const SMetric3 &m1,
                        const SMetric3 &m2);
diff --git a/Graphics/drawGeom.cpp b/Graphics/drawGeom.cpp
index b6269dad7d2c401bec76344d6e0317d81726370b..f4634cffe6f078e559012be70ce151274e16fb43 100644
--- a/Graphics/drawGeom.cpp
+++ b/Graphics/drawGeom.cpp
@@ -421,7 +421,7 @@ class drawGRegion {
   void operator () (GRegion *r)
   {
     if(!r->getVisibility()) return;
-    if(r->geomType() == GEntity::DiscreteVolume) return;
+    //    if(r->geomType() == GEntity::DiscreteVolume) return;
     
     bool select = (_ctx->render_mode == drawContext::GMSH_SELECT && 
                    r->model() == GModel::current());
diff --git a/Mesh/BackgroundMesh.cpp b/Mesh/BackgroundMesh.cpp
index 1f4dadee434042d7f657efec96d95192aedbd95c..4c3ceb2e0a5948503c0c8831f1d17fb6f322c0c5 100644
--- a/Mesh/BackgroundMesh.cpp
+++ b/Mesh/BackgroundMesh.cpp
@@ -457,7 +457,7 @@ backgroundMesh::backgroundMesh(GFace *_gf)
       _sizes[itv2->first] = MAX_LC;
     }
   }
-  // ensure that other criteria are fullfilled 
+  // ensure that other criteria are fullfilled
   updateSizes(_gf);
 
   // compute optimal mesh orientations
@@ -717,6 +717,29 @@ void backgroundMesh::updateSizes(GFace *_gf)
     itv->second = std::max(itv->second, CTX::instance()->mesh.lcMin);
     itv->second = std::min(itv->second, CTX::instance()->mesh.lcMax);
   }  
+  // do not allow large variations in the size field
+  // INTERNATIONAL JOURNAL FOR NUMERICAL METHODS IN ENGINEERING Int. J. Numer. Meth. Engng. 43, 1143–1165 (1998)
+  // MESH GRADATION CONTROL, BOROUCHAKI, HECHT, FREY
+  std::set<MEdge,Less_Edge> edges;
+  for (int i=0;i < _triangles.size();i++){
+    for (int j=0;j< _triangles[i]->getNumEdges();j++){
+      edges.insert(_triangles[i]->getEdge(j));
+    }
+  }
+  const double _beta = 1.3;
+  for (int i=0;i<0;i++){
+    std::set<MEdge,Less_Edge>::iterator it = edges.begin();
+    for ( ; it != edges.end(); ++it){
+      MVertex *v0 = it->getVertex(0);
+      MVertex *v1 = it->getVertex(1);
+      MVertex *V0 = _2Dto3D[v0];
+      MVertex *V1 = _2Dto3D[v1];
+      std::map<MVertex*,double>::iterator s0 = _sizes.find(V0);    
+      std::map<MVertex*,double>::iterator s1 = _sizes.find(V1);    
+      if (s0->second < s1->second)s1->second = std::min(s1->second,_beta*s0->second);
+      else s0->second = std::min(s0->second,_beta*s1->second);
+    }
+  }
 }
 
 double backgroundMesh::operator() (double u, double v, double w) const
diff --git a/Mesh/CMakeLists.txt b/Mesh/CMakeLists.txt
index 329b6105876adb66a5129ddb032aee71dd79d002..1e6e62b777fe5bf6947b018b0189abbc02a1f037 100644
--- a/Mesh/CMakeLists.txt
+++ b/Mesh/CMakeLists.txt
@@ -12,7 +12,7 @@ set(SRC
       meshGFaceTransfinite.cpp meshGFaceExtruded.cpp 
       meshGFaceBamg.cpp meshGFaceBDS.cpp meshGFaceDelaunayInsertion.cpp 
       meshGFaceLloyd.cpp meshGFaceOptimize.cpp 
-      meshGFaceQuadrilateralize.cpp
+      meshGFaceQuadrilateralize.cpp meshGFaceBoundaryLayers.cpp
     meshGRegion.cpp 
       meshGRegionDelaunayInsertion.cpp meshGRegionTransfinite.cpp 
       meshGRegionExtruded.cpp  meshGRegionCarveHole.cpp 
diff --git a/Mesh/Field.cpp b/Mesh/Field.cpp
index 7f2a5304b064706002a782e4244bb1115b4eefad..88205789574eb00c72415ebf418c0ba0012c29c7 100644
--- a/Mesh/Field.cpp
+++ b/Mesh/Field.cpp
@@ -1422,7 +1422,7 @@ class AttractorAnisoCurveField : public Field {
         it != edges_id.end(); ++it) {
       Curve *c = FindCurve(*it);
       if(c) {
-        for(int i = 0; i < n_nodes_by_edge; i++) {
+        for(int i = 1; i < n_nodes_by_edge-1; i++) {
           double u = (double)i / (n_nodes_by_edge - 1);
           Vertex V = InterpolateCurve(c, u, 0);
           zeronodes[k][0] = V.Pos.X;
@@ -1437,7 +1437,7 @@ class AttractorAnisoCurveField : public Field {
       else {
         GEdge *e = GModel::current()->getEdgeByTag(*it);
         if(e) {
-          for(int i = 0; i < n_nodes_by_edge; i++) {
+          for(int i = 1; i < n_nodes_by_edge-1; i++) {
             double u = (double)i / (n_nodes_by_edge - 1);
             Range<double> b = e->parBounds(0);
             double t = b.low() + u * (b.high() - b.low());
@@ -1493,6 +1493,16 @@ class AttractorField : public Field
   Field *_xField, *_yField, *_zField;
   int n_nodes_by_edge;  
  public:
+  AttractorField(int dim, int tag, int nbe) : kdtree(0), zeronodes(0), n_nodes_by_edge(nbe)
+  {
+    index = new ANNidx[1];
+    dist = new ANNdist[1];
+    if (dim == 0) nodes_id.push_back(tag);
+    else if (dim == 1) edges_id.push_back(tag);
+    else if (dim == 2) faces_id.push_back(tag);
+    _xFieldId = _yFieldId = _zFieldId = -1;
+    update_needed = true;
+  }
   AttractorField() : kdtree(0), zeronodes(0)
   {
     index = new ANNidx[1];
@@ -1555,7 +1565,7 @@ class AttractorField : public Field
         annDeallocPts(zeronodes);
         delete kdtree;
       }
-      int totpoints = nodes_id.size() + n_nodes_by_edge * edges_id.size() + 
+      int totpoints = nodes_id.size() + (n_nodes_by_edge-2) * edges_id.size() + 
         n_nodes_by_edge * n_nodes_by_edge * faces_id.size();
       if(totpoints){
         zeronodes = annAllocPts(totpoints, 3);
@@ -1567,22 +1577,22 @@ class AttractorField : public Field
         Vertex *v = FindPoint(*it);
         if(v) {
           getCoord(v->Pos.X, v->Pos.Y, v->Pos.Z, zeronodes[k][0], zeronodes[k][1], zeronodes[k][2]);
-          k++;
+	  _infos[k++] = AttractorInfo(*it,0,0,0);
         }
         else {
           GVertex *gv = GModel::current()->getVertexByTag(*it);
           if(gv) {
             getCoord(gv->x(), gv->y(), gv->z(), zeronodes[k][0], zeronodes[k][1], zeronodes[k][2], gv);
-            k++;
+	    _infos[k++] = AttractorInfo(*it,0,0,0);
           }
         }
       }
       for(std::list<int>::iterator it = edges_id.begin();
           it != edges_id.end(); ++it) {
         Curve *c = FindCurve(*it);
-	GEdge *e = GModel::current()->getEdgeByTag(*it);
+	GEdge *e = GModel::current()->getEdgeByTag(*it);	
         if(c && !e) {
-          for(int i = 0; i < n_nodes_by_edge; i++) {
+          for(int i = 1; i < n_nodes_by_edge -1 ; i++) {
             double u = (double)i / (n_nodes_by_edge - 1);
             Vertex V = InterpolateCurve(c, u, 0);
             getCoord(V.Pos.X, V.Pos.Y, V.Pos.Z, zeronodes[k][0], zeronodes[k][1], zeronodes[k][2]);
@@ -1591,7 +1601,7 @@ class AttractorField : public Field
         }
         else {
           if(e) {
-            for(int i = 0; i < n_nodes_by_edge; i++) {
+            for(int i = 1; i < n_nodes_by_edge - 1; i++) {
               double u = (double)i / (n_nodes_by_edge - 1);
               Range<double> b = e->parBounds(0);
               double t = b.low() + u * (b.high() - b.low());
@@ -1648,203 +1658,235 @@ class AttractorField : public Field
   }
 };
 
-class BoundaryLayerField : public Field {
-  int iField;
-  double hwall_n,hwall_t,ratio,hfar; 
-public:
-  virtual bool isotropic () const {return false;}
-  virtual const char *getName()
-  {
-    return "BoundaryLayer";
-  }
-  virtual std::string getDescription()
-  {
-    return "hwall * ratio^(dist/hwall)";
-  }
-  BoundaryLayerField()
-  {
-    iField = 0;
-    hwall_n = .1;
-    hwall_t = .5;
-    hfar = 1;
-    ratio = 1.1;    
-    options["IField"] = new FieldOptionInt
-      (iField, "Index of the field that contains the distance function");
-    options["hwall_n"] = new FieldOptionDouble
-      (hwall_n, "Mesh Size Normal to the The Wall");
-    options["hwall_t"] = new FieldOptionDouble
-      (hwall_t, "Mesh Size Tangent to the Wall");
-    options["ratio"] = new FieldOptionDouble
-      (ratio, "Size Ratio Between Two Successive Layers");
-    options["hfar"] = new FieldOptionDouble
-      (hfar, "Element size far from the wall");
-  }
-  virtual double operator() (double x, double y, double z, GEntity *ge=0)
-  {
-    Field *field = GModel::current()->getFields()->get(iField);
-    if(!field || iField == id) {
-      return MAX_LC;
+const char *BoundaryLayerField ::getName()
+{
+  return "BoundaryLayer";
+}
+std::string BoundaryLayerField :: getDescription()
+{
+  return "hwall * ratio^(dist/hwall)";
+}
+BoundaryLayerField :: BoundaryLayerField()
+{
+  hwall_n = .1;
+  hwall_t = .5;
+  hfar = 1;
+  ratio = 1.1;    
+  thickness = 1.e-2;    
+  options["NodesList"] = new FieldOptionList
+    (nodes_id, "Indices of nodes in the geometric model", &update_needed);
+  options["EdgesList"] = new FieldOptionList
+    (edges_id, "Indices of curves in the geometric model for which a boundary layer is needed", &update_needed);
+  //  options["IField"] = new FieldOptionInt
+  //    (iField, "Index of the field that contains the distance function");
+  options["hwall_n"] = new FieldOptionDouble
+    (hwall_n, "Mesh Size Normal to the The Wall");
+  options["hwall_t"] = new FieldOptionDouble
+    (hwall_t, "Mesh Size Tangent to the Wall");
+  options["ratio"] = new FieldOptionDouble
+    (ratio, "Size Ratio Between Two Successive Layers");
+  options["hfar"] = new FieldOptionDouble
+    (hfar, "Element size far from the wall");
+  options["thickness"] = new FieldOptionDouble
+    (thickness, "Maximal thickness of the boundary layer");
+}
+
+double BoundaryLayerField :: operator() (double x, double y, double z, GEntity *ge)
+{
+  double dist = 1.e22;
+  AttractorField *cc;
+  for (std::list<AttractorField*>::iterator it = _att_fields.begin();
+       it != _att_fields.end(); ++it){
+    double cdist = (*(*it)) (x, y, z);
+    if (cdist < dist){
+      cc = *it;
+      dist = cdist;
     }
-    const double dist = (*field) (x, y, z);
-    const double lc = dist*(ratio-1) + hwall_n;
-    //    double lc = hwall * pow (ratio, dist / hwall);
-    return std::min (hfar,lc);
   }
-  virtual void operator() (double x, double y, double z, SMetric3 &metr, GEntity *ge=0)
-  {
-    Field *field = GModel::current()->getFields()->get(iField);
-    if(!field || iField == id) {
-      metr(0,0) = 1/(MAX_LC*MAX_LC);
-      metr(1,1) = 1/(MAX_LC*MAX_LC);
-      metr(2,2) = 1/(MAX_LC*MAX_LC);
-      metr(0,1) = metr(0,2) = metr(1,2) = 0;
-      return;
-    }
-    const double dist = (*field) (x, y, z);
-
-    // dist = hwall -> lc = hwall * ratio
-    // dist = hwall (1+ratio) -> lc = hwall ratio ^ 2
-    // dist = hwall (1+ratio+ratio^2) -> lc = hwall ratio ^ 3
-    // dist = hwall (1+ratio+ratio^2+...+ratio^{m-1}) = (ratio^{m} - 1)/(ratio-1) -> lc = hwall ratio ^ m
-    // -> find m
-    // dist/hwall = (ratio^{m} - 1)/(ratio-1)
-    // (dist/hwall)*(ratio-1) + 1 = ratio^{m}
-    // lc =  dist*(ratio-1) + hwall 
-    const double ll1   = dist*(ratio-1) + hwall_n;
-    double lc_n  = std::min(ll1,hfar);
-    const double ll2   = dist*(ratio-1) + hwall_t;
-    double lc_t  = std::min(lc_n*CTX::instance()->mesh.anisoMax, std::min(ll2,hfar));
-
-    SVector3 t1,t2,t3;
-    double L1,L2,L3;
-
-    AttractorField *cc = dynamic_cast<AttractorField*> (field);
-    if (cc){      
-      std::pair<AttractorInfo,SPoint3> pp = cc->getAttractorInfo();
-      if (pp.first.dim ==1){
-	GEdge *e = GModel::current()->getEdgeByTag(pp.first.ent);
-
-	// the tangent size at this point is the size of the
-	// 1D mesh at this point !
-	// hack : use curvature
-	if(CTX::instance()->mesh.lcFromCurvature){
-	  double Crv = e->curvature(pp.first.u);
-	  double lc = Crv > 0 ? 2 * M_PI / Crv / CTX::instance()->mesh.minCircPoints : 1.e-22;
-	  const double ll2b   = dist*(ratio-1) + lc;
-	  double lc_tb  = std::min(lc_n*CTX::instance()->mesh.anisoMax, std::min(ll2b,hfar));
-	  lc_t = std::min(lc_t,lc_tb);
-	}
+  current_distance = dist;
+  //  const double dist = (*field) (x, y, z);
+  //  current_distance = dist;
+  const double lc = dist*(ratio-1) + hwall_t;
+  //    double lc = hwall * pow (ratio, dist / hwall);
+  //  printf("coucou\n");
+  return std::min (hfar,lc);
+}
 
+void BoundaryLayerField :: operator() (AttractorField *cc, double dist, double x, double y, double z, SMetric3 &metr, GEntity *ge)
+{
+  // dist = hwall -> lc = hwall * ratio
+  // dist = hwall (1+ratio) -> lc = hwall ratio ^ 2
+  // dist = hwall (1+ratio+ratio^2) -> lc = hwall ratio ^ 3
+  // dist = hwall (1+ratio+ratio^2+...+ratio^{m-1}) = (ratio^{m} - 1)/(ratio-1) -> lc = hwall ratio ^ m
+  // -> find m
+  // dist/hwall = (ratio^{m} - 1)/(ratio-1)
+  // (dist/hwall)*(ratio-1) + 1 = ratio^{m}
+  // lc =  dist*(ratio-1) + hwall 
 
-	if (dist < hwall_t){
-	  L1 = lc_t;
-	  L2 = lc_n;
-	  L3 = lc_n;
-	  t1 = e->firstDer(pp.first.u);
-	  t1.normalize();
-	  if (fabs(t1.x()) < fabs(t1.y()) && fabs(t1.x()) < fabs(t1.z()))
-	    t2 = SVector3(1,0,0);
-	  else if (fabs(t1.y()) < fabs(t1.x()) && fabs(t1.y()) < fabs(t1.z()))
-	    t2 = SVector3(0,1,0);
-	  else
-	    t2 = SVector3(0,0,1);
-	  t3 = crossprod(t1,t2);
-	  t3.normalize();
-	  t2 = crossprod(t3,t1);
-	  //	  printf("hfar = %g lc = %g dir %g %g \n",hfar,lc,t1.x(),t1.y());
-	}
-	else {
-	  L1 = lc_t;
-	  L2 = lc_n;
-	  L3 = lc_t;
-	  GPoint p = e->point(pp.first.u);
-	  t2 = SVector3(p.x() -x,p.y() -y,p.z() -z);
-	  if (fabs(t2.x()) < fabs(t2.y()) && fabs(t2.x()) < fabs(t2.z()))
-	    t1 = SVector3(1,0,0);
-	  else if (fabs(t2.y()) < fabs(t2.x()) && fabs(t2.y()) < fabs(t2.z()))
-	    t1 = SVector3(0,1,0);
-	  else
-	    t1 = SVector3(0,0,1);
-	  t2.normalize();
-	  t3 = crossprod(t1,t2);
-	  t3.normalize();
-	  t1 = crossprod(t3,t2);	  
-	}
-      }
-      else {
-	GFace *gf = GModel::current()->getFaceByTag(pp.first.ent);
-	
-	if (dist < hwall_t){
-	  L1 = lc_n;
-	  L2 = lc_t;
-	  L3 = lc_t;
-	  t1 = gf->normal(SPoint2(pp.first.u,pp.first.v));
-	  t1.normalize();
-	  if (fabs(t1.x()) < fabs(t1.y()) && fabs(t1.x()) < fabs(t1.z()))
-	    t2 = SVector3(1,0,0);
-	  else if (fabs(t1.y()) < fabs(t1.x()) && fabs(t1.y()) < fabs(t1.z()))
-	    t2 = SVector3(0,1,0);
-	  else
-	    t2 = SVector3(0,0,1);
-	  t3 = crossprod(t1,t2);
-	  t3.normalize();
-	  t2 = crossprod(t3,t1);
-	  //	  printf("hfar = %g lc = %g dir %g %g \n",hfar,lc,t1.x(),t1.y());
-	}
-	else {
-	  L1 = lc_t;
-	  L2 = lc_n;
-	  L3 = lc_t;
-	  GPoint p = gf->point(SPoint2(pp.first.u,pp.first.v));
-	  t2 = SVector3(p.x() -x,p.y() -y,p.z() -z);
-	  if (fabs(t2.x()) < fabs(t2.y()) && fabs(t2.x()) < fabs(t2.z()))
-	    t1 = SVector3(1,0,0);
-	  else if (fabs(t2.y()) < fabs(t2.x()) && fabs(t2.y()) < fabs(t2.z()))
-	    t1 = SVector3(0,1,0);
-	  else
-	    t1 = SVector3(0,0,1);
-	  t2.normalize();
-	  t3 = crossprod(t1,t2);
-	  t3.normalize();
-	  t1 = crossprod(t3,t2);	  
-	}	
+  const double ll1   = dist*(ratio-1) + hwall_n;
+  double lc_n  = std::min(ll1,hfar);
+  const double ll2   = dist*(ratio-1) + hwall_t;
+  double lc_t  = std::min(lc_n*CTX::instance()->mesh.anisoMax, std::min(ll2,hfar));
+  
+  SVector3 t1,t2,t3;
+  double L1,L2,L3;
+  
+  std::pair<AttractorInfo,SPoint3> pp = cc->getAttractorInfo();
+  if (pp.first.dim ==0){
+    L1 = lc_n;
+    L2 = lc_n;
+    L3 = lc_n;
+    GVertex *v = GModel::current()->getVertexByTag(pp.first.ent);
+    t1 = SVector3(v->x() -x,v->y() -y,v->z() -z);
+    t1.normalize();
+    //      printf("%p %g %g %g\n",v,t1.x(),t1.y(),t1.z());
+    if (t1.norm() == 0)t1 = SVector3(1,0,0);
+    if (fabs(t1.x()) < fabs(t1.y()) && fabs(t1.x()) < fabs(t1.z()))
+      t2 = SVector3(1,0,0);
+    else if (fabs(t1.y()) < fabs(t1.x()) && fabs(t1.y()) < fabs(t1.z()))
+      t2 = SVector3(0,1,0);
+    else
+      t2 = SVector3(0,0,1);
+    t3 = crossprod(t1,t2);
+    t3.normalize();
+    t2 = crossprod(t3,t1);
+    //      printf("t1 %p %g %g %g\n",v,t1.x(),t1.y(),t1.z());
+    //      printf("t2 %p %g %g %g\n",v,t2.x(),t2.y(),t2.z());
+    //      printf("t3 %p %g %g %g\n",v,t3.x(),t3.y(),t3.z());
+  }
+  else if (pp.first.dim ==1){
+    GEdge *e = GModel::current()->getEdgeByTag(pp.first.ent);
+    
+    // the tangent size at this point is the size of the
+    // 1D mesh at this point !
+    // hack : use curvature
+    /*
+      if(CTX::instance()->mesh.lcFromCurvature){
+      double Crv = e->curvature(pp.first.u);
+      double lc = Crv > 0 ? 2 * M_PI / Crv / CTX::instance()->mesh.minCircPoints : 1.e-22;
+      const double ll2b   = dist*(ratio-1) + lc;
+      double lc_tb  = std::min(lc_n*CTX::instance()->mesh.anisoMax, std::min(ll2b,hfar));
+      lc_t = std::min(lc_t,lc_tb);
       }
+      */      
+    
+    if (dist < hwall_t){
+      L1 = lc_t;
+      L2 = lc_n;
+      L3 = lc_n;
+      t1 = e->firstDer(pp.first.u);
+      t1.normalize();
+      if (fabs(t1.x()) < fabs(t1.y()) && fabs(t1.x()) < fabs(t1.z()))
+	t2 = SVector3(1,0,0);
+      else if (fabs(t1.y()) < fabs(t1.x()) && fabs(t1.y()) < fabs(t1.z()))
+	t2 = SVector3(0,1,0);
+      else
+	t2 = SVector3(0,0,1);
+      t3 = crossprod(t1,t2);
+      t3.normalize();
+      t2 = crossprod(t3,t1);
+      //	  printf("hfar = %g lc = %g dir %g %g \n",hfar,lc,t1.x(),t1.y());
     }
-    else{   
-      double delta = std::min(CTX::instance()->lc / 1e2, dist);
-      double gx =
-	((*field) (x + delta / 2, y, z) -
-	 (*field) (x - delta / 2, y, z)) / delta;
-      double gy =
-	((*field) (x, y + delta / 2, z) -
-	 (*field) (x, y - delta / 2, z)) / delta;
-      double gz =
-	((*field) (x, y, z + delta / 2) -
-	 (*field) (x, y, z - delta / 2)) / delta;
-      SVector3 g = SVector3 (gx,gy,gz);
-      g.normalize();
-      
-      if (fabs(g.x()) < fabs(g.y()) && fabs(g.x()) < fabs(g.z()))
-	t1 = SVector3(1,0,0);
-      else if (fabs(g.y()) < fabs(g.x()) && fabs(g.y()) < fabs(g.z()))
+    else {
+      L1 = lc_t;
+      L2 = lc_n;
+      L3 = lc_t;
+      GPoint p = e->point(pp.first.u);
+      t2 = SVector3(p.x() -x,p.y() -y,p.z() -z);
+      if (fabs(t2.x()) < fabs(t2.y()) && fabs(t2.x()) < fabs(t2.z()))
+	  t1 = SVector3(1,0,0);
+      else if (fabs(t2.y()) < fabs(t2.x()) && fabs(t2.y()) < fabs(t2.z()))
 	t1 = SVector3(0,1,0);
       else
 	t1 = SVector3(0,0,1);
-      
-      t2 = crossprod(g,t1);
       t2.normalize();
-      t1 = crossprod(t2,g);   
-      t3 = g;
+      t3 = crossprod(t1,t2);
+      t3.normalize();
+      t1 = crossprod(t3,t2);	  
+    }
+  }
+  else {
+    GFace *gf = GModel::current()->getFaceByTag(pp.first.ent);
+    
+    if (dist < hwall_t){
       L1 = lc_n;
       L2 = lc_t;
       L3 = lc_t;
+      t1 = gf->normal(SPoint2(pp.first.u,pp.first.v));
+      t1.normalize();
+      if (fabs(t1.x()) < fabs(t1.y()) && fabs(t1.x()) < fabs(t1.z()))
+	t2 = SVector3(1,0,0);
+      else if (fabs(t1.y()) < fabs(t1.x()) && fabs(t1.y()) < fabs(t1.z()))
+	t2 = SVector3(0,1,0);
+      else
+	t2 = SVector3(0,0,1);
+      t3 = crossprod(t1,t2);
+      t3.normalize();
+      t2 = crossprod(t3,t1);
+      //	  printf("hfar = %g lc = %g dir %g %g \n",hfar,lc,t1.x(),t1.y());
+    }
+    else {
+      L1 = lc_t;
+      L2 = lc_n;
+      L3 = lc_t;
+      GPoint p = gf->point(SPoint2(pp.first.u,pp.first.v));
+      t2 = SVector3(p.x() -x,p.y() -y,p.z() -z);
+      if (fabs(t2.x()) < fabs(t2.y()) && fabs(t2.x()) < fabs(t2.z()))
+	t1 = SVector3(1,0,0);
+      else if (fabs(t2.y()) < fabs(t2.x()) && fabs(t2.y()) < fabs(t2.z()))
+	t1 = SVector3(0,1,0);
+      else
+	t1 = SVector3(0,0,1);
+      t2.normalize();
+      t3 = crossprod(t1,t2);
+      t3.normalize();
+      t1 = crossprod(t3,t2);	  
+    }	
+  }
+  metr  = SMetric3(1./(L1*L1), 
+		   1./(L2*L2),
+		   1./(L3*L3),
+		   t1,t2,t3);
+}
+
+void BoundaryLayerField :: operator() (double x, double y, double z, SMetric3 &metr, GEntity *ge)
+{
+
+  if (update_needed){
+    for(std::list<int>::iterator it = nodes_id.begin();
+	it != nodes_id.end(); ++it) {
+      _att_fields.push_back(new AttractorField(0,*it,100000));
+    }
+    for(std::list<int>::iterator it = edges_id.begin();
+	it != edges_id.end(); ++it) {
+      _att_fields.push_back(new AttractorField(1,*it,300000));
     }
-    metr  = SMetric3(1./(L1*L1), 
-                     1./(L2*L2),
-                     1./(L3*L3),
-                     t1,t2,t3);
+    update_needed = false;
   }
-};
+
+  current_distance = 1.e22;
+  current_closest = 0;
+  SMetric3 v (1./MAX_LC);
+  std::vector<SMetric3> hop;
+  for (std::list<AttractorField*>::iterator it = _att_fields.begin();
+       it != _att_fields.end(); ++it){
+    double cdist = (*(*it)) (x, y, z);
+    SPoint3 CLOSEST= (*it)->getAttractorInfo().second;
+
+    SMetric3 localMetric;
+    (*this)(*it, cdist,x, y, z, localMetric, ge);      
+    hop.push_back(localMetric);
+    //v = intersection(v,localMetric);
+    if (cdist < current_distance){
+      current_distance = cdist;
+      current_closest = *it;
+      v = localMetric;
+      _closest_point = CLOSEST;
+    }
+  }
+  //  for (int i=0;i<hop.size();i++)v = intersection_conserveM1(v,hop[i]);
+  metr = v;
+}
 #endif
 
 template<class F> class FieldFactoryT : public FieldFactory {
diff --git a/Mesh/Field.h b/Mesh/Field.h
index fdb070d201ff3e5122d30700810b72f1de6ca91b..dba2d5a1505001a028a0e1857bfa2d08bb3752d0 100644
--- a/Mesh/Field.h
+++ b/Mesh/Field.h
@@ -101,4 +101,29 @@ class FieldManager : public std::map<int, Field*> {
   void setBackgroundMesh(int iView);
 };
 
+// Boundary Layer Field
+// specific field that allow to build boundary layers
+// is used both for anisotropic meshing and BL extrusion
+
+#if defined(HAVE_ANN)
+class AttractorField;// : public Field;
+
+class BoundaryLayerField : public Field {
+  std::list<AttractorField *> _att_fields;
+  std::list<int> nodes_id, edges_id, faces_id;
+  void operator() (AttractorField *cc, double dist, double x, double y, double z, SMetric3 &metr, GEntity *ge);
+public:
+  double hwall_n,hwall_t,ratio,hfar,thickness; 
+  double current_distance;
+  SPoint3 _closest_point;
+  AttractorField *current_closest;
+  virtual bool isotropic () const {return false;}
+  virtual const char *getName();
+  virtual std::string getDescription();
+  BoundaryLayerField();
+  virtual double operator() (double x, double y, double z, GEntity *ge=0);
+  virtual void operator() (double x, double y, double z, SMetric3 &metr, GEntity *ge=0);
+};
+#endif
+
 #endif
diff --git a/Mesh/Generator.cpp b/Mesh/Generator.cpp
index a89fe66a2b5430c940c45111fec087f2dad6e1ee..a2abd8b623b3955990d99e6f3930e57184a0c897 100644
--- a/Mesh/Generator.cpp
+++ b/Mesh/Generator.cpp
@@ -474,11 +474,14 @@ static void Mesh2D(GModel *m)
   // lloyd optimization
   if (CTX::instance()->mesh.optimizeLloyd){
     for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){
-      if((*it)->geomType()==GEntity::CompoundSurface){
+      if((*it)->geomType()==GEntity::CompoundSurface || (*it)->geomType()==GEntity::Plane){
 	smoothing smm(CTX::instance()->mesh.optimizeLloyd,6);
+	m->writeMSH("beforeLLoyd.msh");
 	smm.optimize_face(*it);
-	int rec = (CTX::instance()->mesh.recombineAll || (*it)->meshAttributes.recombine);
-	if(rec) recombineIntoQuads(*it);
+	int rec = 1;//(CTX::instance()->mesh.recombineAll || (*it)->meshAttributes.recombine);
+	m->writeMSH("afterLLoyd.msh");
+	if(rec){printf("recombine\n"); recombineIntoQuads(*it);}
+	m->writeMSH("afterRecombine.msh");
       }
     }
     /*
diff --git a/Mesh/HighOrder.cpp b/Mesh/HighOrder.cpp
index aaa02290e3f338529e610513fa639ca980f91a36..4e854c560f533795c4542e80f4cd654dc507c292 100644
--- a/Mesh/HighOrder.cpp
+++ b/Mesh/HighOrder.cpp
@@ -1236,9 +1236,9 @@ void SetOrderN(GModel *m, int order, bool linear, bool incomplete)
 
   // printJacobians(m, "smoothness.pos");
   // m->writeMSH("SMOOTHED.msh");
-
+  // FIXME !!
   if (!linear){
-    hot.ensureMinimumDistorsion(0.1);
+  //hot.ensureMinimumDistorsion(0.1);
     checkHighOrderTriangles("Final mesh", m, bad, worst);
     checkHighOrderTetrahedron("Final mesh", m, bad, worst);
   }
diff --git a/Mesh/meshGEdge.cpp b/Mesh/meshGEdge.cpp
index adb99fec89ac752a3313cb4b183b024bba1788b1..31797c86685930945a37cc0b4785855e7582d653 100644
--- a/Mesh/meshGEdge.cpp
+++ b/Mesh/meshGEdge.cpp
@@ -309,7 +309,8 @@ void meshGEdge::operator() (GEdge *ge)
     N = ge->meshAttributes.nbPointsTransfinite;
   }
   else{
-    if (CTX::instance()->mesh.algo2d == ALGO_2D_BAMG) 
+    if (CTX::instance()->mesh.algo2d == ALGO_2D_BAMG /*|| 1  || // FIXME
+							CTX::instance()->mesh.algo2d == ALGO_2D_FRONTAL_QUAD */) 
       a = Integration(ge, t_begin, t_end, F_Lc_aniso, Points,
                       CTX::instance()->mesh.lcIntegrationPrecision);
     else
diff --git a/Mesh/meshGFace.cpp b/Mesh/meshGFace.cpp
index 393818c585818546ea0863b5f34416c1dc415f6d..74774d09576fe7adb2cf5a8734fca1535121cb4f 100644
--- a/Mesh/meshGFace.cpp
+++ b/Mesh/meshGFace.cpp
@@ -40,6 +40,7 @@
 #include "Context.h"
 #include "multiscalePartition.h"
 #include "meshGFaceLloyd.h"
+#include "meshGFaceBoundaryLayers.h"
 
 static void copyMesh(GFace *source, GFace *target)
 {
@@ -390,19 +391,256 @@ static bool recoverEdge(BDS_Mesh *m, GEdge *ge,
   return true;
 }
 
+void BDS2GMSH ( BDS_Mesh *m, GFace *gf, std::map<BDS_Point*, MVertex*> &recoverMap){ 
+  {
+    std::set<BDS_Point*,PointLessThan>::iterator itp = m->points.begin();
+    while (itp != m->points.end()){
+      BDS_Point *p = *itp;
+      if(recoverMap.find(p) == recoverMap.end()){
+        MVertex *v = new MFaceVertex
+          (p->X, p->Y, p->Z, gf, m->scalingU * p->u, m->scalingV * p->v);
+        recoverMap[p] = v;
+        gf->mesh_vertices.push_back(v);
+      }
+      ++itp;
+    }
+  }
+  {
+    std::list<BDS_Face*>::iterator itt = m->triangles.begin();
+    while (itt != m->triangles.end()){
+      BDS_Face *t = *itt;
+      if(!t->deleted){
+        BDS_Point *n[4];
+        t->getNodes(n);
+        MVertex *v1 = recoverMap[n[0]];
+        MVertex *v2 = recoverMap[n[1]];
+        MVertex *v3 = recoverMap[n[2]];
+        if(!n[3]){
+          // when a singular point is present, degenerated triangles
+          // may be created, for example on a sphere that contains one
+          // pole
+          if(v1 != v2 && v1 != v3 && v2 != v3)
+            gf->triangles.push_back(new MTriangle(v1, v2, v3));
+        }
+        else{
+          MVertex *v4 = recoverMap[n[3]];
+          gf->quadrangles.push_back(new MQuadrangle(v1, v2, v3, v4));
+        }
+      }
+      ++itt;
+    }
+  }
+}
+
+bool meshGenerator(GFace *gf, int RECUR_ITER, 
+		   bool repairSelfIntersecting1dMesh,
+		   bool onlyInitialMesh,
+		   bool debug = true,
+		   std::list<GEdge*> *replacement_edges = 0);
+
+static void addOrRemove ( MVertex *v1, MVertex *v2, std::set<MEdge,Less_Edge> & bedges){
+  MEdge e(v1,v2);
+  std::set<MEdge,Less_Edge>::iterator it = bedges.find(e);
+  if (it == bedges.end())bedges.insert(e);
+  else bedges.erase(it);
+}
+
+void modifyInitialMeshForTakingIntoAccountBoundaryLayers  (GFace *gf){
+
+  BoundaryLayerColumns *_columns = buidAdditionalPoints2D (gf, M_PI/6.);
+
+  if (!_columns)return;
+
+  std::set<MEdge,Less_Edge> bedges;
+
+  std::vector<MQuadrangle*> blQuads;
+  std::vector<MTriangle*> blTris;
+  std::list<GEdge*> edges = gf->edges();
+  std::list<GEdge*>::iterator ite = edges.begin();
+  FILE *ff2 = fopen ("tato.pos","w");
+  fprintf(ff2,"View \" \"{\n");
+  std::set<MVertex*> verts;
+  while(ite != edges.end()){
+    for(unsigned int i = 0; i< (*ite)->lines.size(); i++){
+      MVertex *v1 = (*ite)->lines[i]->getVertex(0);
+      MVertex *v2 = (*ite)->lines[i]->getVertex(1);
+      MEdge dv(v1,v2);
+      addOrRemove(v1,v2,bedges);
+      int nbCol1 = _columns->getNbColumns(v1);
+      int nbCol2 = _columns->getNbColumns(v2);
+      if (nbCol1 > 0 && nbCol2 > 0){ 
+	const BoundaryLayerData & c1 = _columns->getColumn(v1,MEdge(v1,v2));
+	const BoundaryLayerData & c2 = _columns->getColumn(v2,MEdge(v1,v2));
+	int N = std::min(c1._column.size(),c2._column.size());
+	for (int l=0;l < N ;++l){
+	  MVertex *v11,*v12,*v21,*v22;
+	  v21 = c1._column[l];
+	  v22 = c2._column[l];	    
+	  if (l == 0){
+	    v11 = v1;
+	    v12 = v2;
+	  }
+	  else {
+	    v11 = c1._column[l-1];
+	    v12 = c2._column[l-1];	    
+	  }
+	  MEdge dv2 (v21,v22);
+
+	  //avoid convergent errors
+	  if (dv2.length() < 0.5 * dv.length())break;
+	  //	printf("quadrangle generated\n");
+	  blQuads.push_back(new MQuadrangle(v11,v12,v22,v21));
+	  //blTris.push_back(new MTriangle(v11,v12,v22));
+	  //	  blTris.push_back(new MTriangle(v11,v22,v21));
+	  addOrRemove(v11,v12,bedges);
+	  addOrRemove(v12,v22,bedges);
+	  addOrRemove(v22,v21,bedges);
+	  addOrRemove(v21,v11,bedges);
+	  if(v11->onWhat() == gf)verts.insert(v11);
+	  if(v21->onWhat() == gf)verts.insert(v21);
+	  if(v12->onWhat() == gf)verts.insert(v12);
+	  if(v22->onWhat() == gf)verts.insert(v22);
+	  fprintf(ff2,"SQ (%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g){1,1,1,1};\n",
+		  v11->x(),v11->y(),v11->z(),
+		  v12->x(),v12->y(),v12->z(),
+		  v22->x(),v22->y(),v22->z(),
+		  v21->x(),v21->y(),v21->z());
+	}
+	int M = std::max(c1._column.size(),c2._column.size());
+
+	/*
+	if (M>N) M = N+1;
+	// close with triangles
+ 	for (int l=N-1;l < M-1 ;++l){
+	  MVertex *v11,*v12,*v21,*v22;
+	  v11 = c1._column[l>=c1._column.size() ? c1._column.size() -1 : l];
+	  v12 = c2._column[l>=c2._column.size() ? c2._column.size() -1 : l];
+	  v21 = c1._column[(l+1)>=c1._column.size() ? c1._column.size() -1 : l+1];
+	  v22 = c2._column[(l+1)>=c2._column.size() ? c2._column.size() -1 : l+1];
+
+	  MTriangle *nt = (v11 == v21) ? new MTriangle(v11,v12,v22) : new MTriangle(v11,v12,v21) ;
+	  blTris.push_back(nt);
+	  v11 = nt->getVertex(0);
+	  v12 = nt->getVertex(1);
+	  v21 = nt->getVertex(2);
+
+	  addOrRemove(v11,v12,bedges);
+	  addOrRemove(v12,v21,bedges);
+	  addOrRemove(v21,v11,bedges);
+	  if(v11->onWhat() == gf)verts.insert(v11);
+	  if(v21->onWhat() == gf)verts.insert(v21);
+	  if(v12->onWhat() == gf)verts.insert(v12);
+	  fprintf(ff2,"ST (%g,%g,%g,%g,%g,%g,%g,%g,%g){1,1,1};\n",
+		  v11->x(),v11->y(),v11->z(),
+		  v12->x(),v12->y(),v12->z(),
+		  v21->x(),v21->y(),v21->z());
+	}
+	*/
+      }
+   }
+    ++ite;
+  }
+
+  if (1){
+    for (BoundaryLayerColumns::iterf itf = _columns->beginf();
+	 itf != _columns->endf() ; ++itf){
+      MVertex *v = itf->first;
+      int nbCol = _columns->getNbColumns(v);
+      
+      for (int i=0;i<nbCol-1;i++){
+	//	printf("permut %d %d\n",permut[i],permut[i+1]);
+	const BoundaryLayerData & c1 = _columns->getColumn(v,i);
+	const BoundaryLayerData & c2 = _columns->getColumn(v,i+1);
+	int N = std::min(c1._column.size(),c2._column.size());
+	for (int l=0;l < N ;++l){
+	  MVertex *v11,*v12,*v21,*v22;
+	  v21 = c1._column[l];
+	  v22 = c2._column[l];	    
+	  if (l == 0){
+	    v11 = v;
+	    v12 = v;
+	  }
+	  else {
+	    v11 = c1._column[l-1];
+	    v12 = c2._column[l-1];	    
+	  }
+	  //	printf("quadrangle generated\n");
+	  if (v11 != v12){
+	    addOrRemove(v11,v12,bedges);
+	    addOrRemove(v12,v22,bedges);
+	    addOrRemove(v22,v21,bedges);
+	    addOrRemove(v21,v11,bedges);
+	    blQuads.push_back(new MQuadrangle(v11,v12,v22,v21));
+	    fprintf(ff2,"SQ (%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g){1,1,1,1};\n",
+		    v11->x(),v11->y(),v11->z(),
+		    v12->x(),v12->y(),v12->z(),
+		    v22->x(),v22->y(),v22->z(),
+		    v21->x(),v21->y(),v21->z());
+	  }
+	  else {
+	    addOrRemove(v,v22,bedges);
+	    addOrRemove(v22,v21,bedges);
+	    addOrRemove(v21,v,bedges);
+	    blTris.push_back(new MTriangle(v,v22,v21));
+	    fprintf(ff2,"ST (%g,%g,%g,%g,%g,%g,%g,%g,%g){1,1,1,1};\n",
+		    v->x(),v->y(),v->z(),
+		    v22->x(),v22->y(),v22->z(),
+		    v21->x(),v21->y(),v21->z());
+	  }
+	  if(v11->onWhat() == gf)verts.insert(v11);
+	  if(v21->onWhat() == gf)verts.insert(v21);
+	  if(v12->onWhat() == gf)verts.insert(v12);
+	  if(v22->onWhat() == gf)verts.insert(v22);
+	}
+      }
+    }
+  }
+  fprintf(ff2,"};\n");
+  fclose(ff2);
+
+  discreteEdge ne (gf->model(), 444444,0,
+		   (*edges.begin())->getEndVertex());
+  std::list<GEdge*> hop;
+  std::set<MEdge,Less_Edge>::iterator it =  bedges.begin();
+
+  FILE *ff = fopen ("toto.pos","w");
+  fprintf(ff,"View \" \"{\n");
+  for (; it != bedges.end(); ++it){
+    ne.lines.push_back(new MLine (it->getVertex(0),it->getVertex(1)));
+    fprintf(ff,"SL (%g,%g,%g,%g,%g,%g){1,1};\n",
+	    it->getVertex(0)->x(),it->getVertex(0)->y(),it->getVertex(0)->z(),
+	    it->getVertex(1)->x(),it->getVertex(1)->y(),it->getVertex(1)->z());
+  }
+  fprintf(ff,"};\n");
+  fclose(ff);
+
+  hop.push_back(&ne);
+
+  deMeshGFace kil_;
+  kil_(gf);
+  
+  meshGenerator(gf, 0, 0, true , true, &hop); 
+  
+  gf->quadrangles = blQuads;
+  gf->triangles.insert(gf->triangles.begin(),blTris.begin(),blTris.end());
+  gf->mesh_vertices.insert(gf->mesh_vertices.begin(),verts.begin(),verts.end());
+}
+
+
 // Builds An initial triangular mesh that respects the boundaries of
 // the domain, including embedded points and surfaces
-static bool meshGenerator(GFace *gf, int RECUR_ITER, 
-                          bool repairSelfIntersecting1dMesh,
-                          bool onlyInitialMesh,
-                          bool debug = true)
+bool meshGenerator(GFace *gf, int RECUR_ITER, 
+		   bool repairSelfIntersecting1dMesh,
+		   bool onlyInitialMesh,
+		   bool debug,
+		   std::list<GEdge*> *replacement_edges)
 {
 
   BDS_GeomEntity CLASS_F(1, 2);
   BDS_GeomEntity CLASS_EXTERIOR(1, 3);
   std::map<BDS_Point*, MVertex*> recoverMap;
   std::map<MVertex*, BDS_Point*> recoverMapInv;
-  std::list<GEdge*> edges = gf->edges();
+  std::list<GEdge*> edges = replacement_edges ? *replacement_edges : gf->edges();
   std::list<int> dir = gf->edgeOrientations();
 
   // replace edges by their compounds
@@ -494,6 +732,8 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER,
   }
   all_vertices.clear();
 
+  // here check if some boundary layer nodes should be added
+
   // compute the bounding box in parametric space
   SVector3 dd(bbox.max(), bbox.min());
   double LC2D = norm(dd);
@@ -578,8 +818,6 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER,
       ++ite;
     }
     
-    Msg::Debug("Recovering %d mesh Edges (%d not recovered)", edgesToRecover.size(),
-               edgesNotRecovered.size());
    
     // effectively recover the medge
     ite = edges.begin();
@@ -594,6 +832,9 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER,
       ++ite;
     }
     
+    Msg::Debug("Recovering %d mesh Edges (%d not recovered)", edgesToRecover.size(),
+               edgesNotRecovered.size());
+
     if(edgesNotRecovered.size()){
       std::ostringstream sstream;
       for(std::set<EdgeToRecover>::iterator itr = edgesNotRecovered.begin();
@@ -601,7 +842,7 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER,
         sstream << " " << itr->ge->tag();
       Msg::Warning(":-( There are %d intersections in the 1D mesh (curves%s)",
                    edgesNotRecovered.size(), sstream.str().c_str());
-      Msg::Warning("8-| Gmsh splits those edges and tries again");
+      if (repairSelfIntersecting1dMesh) Msg::Warning("8-| Gmsh splits those edges and tries again");
     
       if(debug){
         char name[245];
@@ -621,6 +862,7 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER,
           int p1 = itr->p1;
           int p2 = itr->p2;
           int tag = itr->ge->tag();
+	  printf("%d %d %d\n",p1,p2,tag);
           _error[3 * I + 0] = p1;
           _error[3 * I + 1] = p2;
           _error[3 * I + 2] = tag;
@@ -633,7 +875,7 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER,
       delete m;
       if(RECUR_ITER < 10 && facesToRemesh.size() == 0)
         return meshGenerator
-          (gf, RECUR_ITER + 1, repairSelfIntersecting1dMesh, onlyInitialMesh, debug);
+          (gf, RECUR_ITER + 1, repairSelfIntersecting1dMesh, onlyInitialMesh, debug,replacement_edges);
       return false;
     }
 
@@ -718,25 +960,27 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER,
     }
 
     // compute characteristic lengths at vertices    
-    Msg::Debug("Computing mesh size field at mesh vertices %d", 
-               edgesToRecover.size());
-    for(int i = 0; i < doc.numPoints; i++){
-      BDS_Point *pp = (BDS_Point*)doc.points[i].data;
-      std::map<BDS_Point*, MVertex*>::iterator itv = recoverMap.find(pp);
-      if(itv != recoverMap.end()){
-        MVertex *here = itv->second;
-        GEntity *ge = here->onWhat();
-        if(ge->dim() == 0){
-          pp->lcBGM() = BGM_MeshSize(ge, 0, 0, here->x(), here->y(), here->z());
-        }
-        else if(ge->dim() == 1){
-          double u;
-          here->getParameter(0, u);
-          pp->lcBGM() = BGM_MeshSize(ge, u, 0, here->x(), here->y(), here->z());
-        }
-        else
-          pp->lcBGM() = MAX_LC;      
-        pp->lc() = pp->lcBGM();
+    if (!onlyInitialMesh){
+      Msg::Debug("Computing mesh size field at mesh vertices %d", 
+		 edgesToRecover.size());
+      for(int i = 0; i < doc.numPoints; i++){
+	BDS_Point *pp = (BDS_Point*)doc.points[i].data;
+	std::map<BDS_Point*, MVertex*>::iterator itv = recoverMap.find(pp);
+	if(itv != recoverMap.end()){
+	  MVertex *here = itv->second;
+	  GEntity *ge = here->onWhat();
+	  if(ge->dim() == 0){
+	    pp->lcBGM() = BGM_MeshSize(ge, 0, 0, here->x(), here->y(), here->z());
+	  }
+	  else if(ge->dim() == 1){
+	    double u;
+	    here->getParameter(0, u);
+	    pp->lcBGM() = BGM_MeshSize(ge, u, 0, here->x(), here->y(), here->z());
+	  }
+	  else
+	    pp->lcBGM() = MAX_LC;      
+	  pp->lc() = pp->lcBGM();
+	}
       }
     }
   }
@@ -780,8 +1024,8 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER,
     sprintf(name, "surface%d-recovered-param.pos", gf->tag());
     outputScalarField(m->triangles, name, 1);
   }
-
-  {
+  
+  if(1){
     std::list<BDS_Face*>::iterator itt = m->triangles.begin();
     while (itt != m->triangles.end()){
       BDS_Face *t = *itt;
@@ -803,7 +1047,7 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER,
       ++itt;
     }
   }
- 
+  
   if (Msg::GetVerbosity() == 10){
     GEdge *ge = new discreteEdge(gf->model(), 1000, 0, 0);
     MElementOctree octree(gf->model());
@@ -819,69 +1063,45 @@ static bool meshGenerator(GFace *gf, int RECUR_ITER,
   gf->triangles.clear();
   gf->quadrangles.clear();
 
-  int nb_swap;
-  //outputScalarField(m->triangles, "beforeswop.pos",1);
-  Msg::Debug("Delaunizing the initial mesh");
-  delaunayizeBDS(gf, *m, nb_swap);
-  //outputScalarField(m->triangles, "afterswop.pos",0);
-  Msg::Debug("Starting to add internal points");
+  {
+    int nb_swap;
+    Msg::Debug("Delaunizing the initial mesh");
+    delaunayizeBDS(gf, *m, nb_swap);
+  }
 
+  Msg::Debug("Starting to add internal points");
   // start mesh generation
   if(!algoDelaunay2D(gf) && !onlyInitialMesh){
+    if(CTX::instance()->mesh.recombineAll || gf->meshAttributes.recombine) {
+      printf("coucou here !!!\n");
+      backgroundMesh::unset();
+      buildBackGroundMesh (gf);
+    }     
     refineMeshBDS(gf, *m, CTX::instance()->mesh.refineSteps, true,
                   &recoverMapInv);
     optimizeMeshBDS(gf, *m, 2);
     refineMeshBDS(gf, *m, CTX::instance()->mesh.refineSteps, false,
                 &recoverMapInv);
     optimizeMeshBDS(gf, *m, 2);
+    if(CTX::instance()->mesh.recombineAll || gf->meshAttributes.recombine) {
+      backgroundMesh::unset();
+    }     
   }
+  /*
   computeMeshSizeFieldAccuracy(gf, *m, gf->meshStatistics.efficiency_index,
                                gf->meshStatistics.longest_edge_length,
                                gf->meshStatistics.smallest_edge_length,
                                gf->meshStatistics.nbEdge,
                                gf->meshStatistics.nbGoodLength);
+  */
   gf->meshStatistics.status = GFace::DONE;
 
   // fill the small gmsh structures
-  {
-    std::set<BDS_Point*,PointLessThan>::iterator itp = m->points.begin();
-    while (itp != m->points.end()){
-      BDS_Point *p = *itp;
-      if(recoverMap.find(p) == recoverMap.end()){
-        MVertex *v = new MFaceVertex
-          (p->X, p->Y, p->Z, gf, m->scalingU * p->u, m->scalingV * p->v);
-        recoverMap[p] = v;
-        gf->mesh_vertices.push_back(v);
-      }
-      ++itp;
-    }
-  }
-  {
-    std::list<BDS_Face*>::iterator itt = m->triangles.begin();
-    while (itt != m->triangles.end()){
-      BDS_Face *t = *itt;
-      if(!t->deleted){
-        BDS_Point *n[4];
-        t->getNodes(n);
-        MVertex *v1 = recoverMap[n[0]];
-        MVertex *v2 = recoverMap[n[1]];
-        MVertex *v3 = recoverMap[n[2]];
-        if(!n[3]){
-          // when a singular point is present, degenerated triangles
-          // may be created, for example on a sphere that contains one
-          // pole
-          if(v1 != v2 && v1 != v3 && v2 != v3)
-            gf->triangles.push_back(new MTriangle(v1, v2, v3));
-        }
-        else{
-          MVertex *v4 = recoverMap[n[3]];
-          gf->quadrangles.push_back(new MQuadrangle(v1, v2, v3, v4));
-        }
-      }
-      ++itt;
-    }
-  }
+  BDS2GMSH ( m, gf, recoverMap);
 
+  // BOUNDARY LAYER
+  if (!onlyInitialMesh)modifyInitialMeshForTakingIntoAccountBoundaryLayers  (gf);
+    
   // the delaunay algo is based directly on internal gmsh structures
   // BDS mesh is passed in order not to recompute local coordinates of
   // vertices
@@ -1415,17 +1635,27 @@ static bool meshGeneratorPeriodic(GFace *gf, bool debug = true)
 
   // start mesh generation for periodic face
   if(!algoDelaunay2D(gf)){
+    // need for a BGM for cross field
+    if(CTX::instance()->mesh.recombineAll || gf->meshAttributes.recombine) {
+      //      printf("coucou here !!!\n");
+      //      backgroundMesh::unset();
+      //      buildBackGroundMesh (gf);
+    }     
     refineMeshBDS(gf, *m, CTX::instance()->mesh.refineSteps, true);
     optimizeMeshBDS(gf, *m, 2);
     refineMeshBDS(gf, *m, -CTX::instance()->mesh.refineSteps, false);
     optimizeMeshBDS(gf, *m, 2, &recoverMap);
     // compute mesh statistics
+    /*
     computeMeshSizeFieldAccuracy(gf, *m, gf->meshStatistics.efficiency_index,
                                  gf->meshStatistics.longest_edge_length,
                                  gf->meshStatistics.smallest_edge_length,
                                  gf->meshStatistics.nbEdge,
-                                 gf->meshStatistics.nbGoodLength);
+                                 gf->meshStatistics.nbGoodLength);*/
     gf->meshStatistics.status = GFace::DONE;
+    if(CTX::instance()->mesh.recombineAll || gf->meshAttributes.recombine) {
+      //      backgroundMesh::unset();
+    }     
   }
   
   // fill the small gmsh structures
@@ -1554,6 +1784,9 @@ void meshGFace::operator() (GFace *gf)
   }
 
   const char *algo = "Unknown";
+
+
+
   switch(CTX::instance()->mesh.algo2d){
   case ALGO_2D_MESHADAPT : algo = "MeshAdapt"; break;
   case ALGO_2D_FRONTAL : algo = "Frontal"; break;
@@ -1566,6 +1799,11 @@ void meshGFace::operator() (GFace *gf)
     break;
   }
 
+  if (!algoDelaunay2D(gf)){
+    algo = "MeshAdapt";
+  }
+
+
   Msg::Info("Meshing surface %d (%s, %s)", gf->tag(), gf->getTypeString().c_str(), algo);
 
   // compute loops on the fly (indices indicate start and end points
diff --git a/Mesh/meshGFaceBDS.cpp b/Mesh/meshGFaceBDS.cpp
index 61ca4979b3a8f29b1019e450a03a255a0953f31f..1173d253194928e0ed5221b902c79f3d7526d723 100644
--- a/Mesh/meshGFaceBDS.cpp
+++ b/Mesh/meshGFaceBDS.cpp
@@ -28,19 +28,34 @@ double computeEdgeLinearLength(BDS_Point *p1, BDS_Point *p2)
   const double dx = p1->X - p2->X;
   const double dy = p1->Y - p2->Y;
   const double dz = p1->Z - p2->Z;
+  // FIXME SUPER HACK
+  //const double l = std::max(fabs(dx),std::max(fabs(dy),fabs(dz)));
   const double l = sqrt(dx * dx + dy * dy + dz * dz);
   return l;
 }
 
+extern double lengthInfniteNorm(const double p[2], const double q[2], 
+				const double quadAngle);
+
 inline double computeEdgeLinearLength(BDS_Point *p1, BDS_Point *p2, GFace *f,
                                       double SCALINGU, double SCALINGV)
 {
+  // FIXME SUPER HACK
+  /*
+  if (CTX::instance()->mesh.recombineAll || f->meshAttributes.recombine){
+    double quadAngle  = backgroundMesh::current()->getAngle (0.5 * (p1->u + p2->u) * SCALINGU, 0.5 * (p1->v + p2->v) * SCALINGV,0);
+    const double a [2] = {p1->u,p1->v};
+    const double b [2] = {p2->u,p2->v};
+    return lengthInfniteNorm(a,b, quadAngle);
+  }
+  */
   GPoint GP = f->point(SPoint2(0.5 * (p1->u + p2->u) * SCALINGU,
                                0.5 * (p1->v + p2->v) * SCALINGV));
 
   if (!GP.succeeded())
     return computeEdgeLinearLength(p1,p2);
 
+
   const double dx1 = p1->X - GP.x();
   const double dy1 = p1->Y - GP.y();
   const double dz1 = p1->Z - GP.z();
@@ -143,6 +158,7 @@ inline double computeEdgeMiddleCoord(BDS_Point *p1, BDS_Point *p2, GFace *f,
 inline double computeEdgeLinearLength(BDS_Edge *e, GFace *f, 
                                       double SCALINGU, double SCALINGV)
 {
+  // FIXME !!!
   if (f->geomType() == GEntity::Plane)
     return e->length();
   else
@@ -532,7 +548,7 @@ void splitEdgePass(GFace *gf, BDS_Mesh &m, double MAXE_, int &nb_split)
         mid  = m.add_point(++m.MAXPOINTNUMBER, gpp.x(),gpp.y(),gpp.z());
         mid->u = U;
         mid->v = V;
-        if (backgroundMesh::current()){
+        if (backgroundMesh::current() && 0){
           mid->lc() = mid->lcBGM() = 
             backgroundMesh::current()->operator()
             ((coord * e->p1->u + (1 - coord) * e->p2->u)*m.scalingU,
@@ -613,7 +629,8 @@ void collapseEdgePassUnSorted(GFace *gf, BDS_Mesh &m, double MINE_, int MAXNP,
 
 void smoothVertexPass(GFace *gf, BDS_Mesh &m, int &nb_smooth, bool q)
 {
-  //  return;
+  // FIXME SUPER HACK
+  //    return;
   std::set<BDS_Point*,PointLessThan>::iterator itp = m.points.begin();
   while(itp != m.points.end()){      
     if(m.smooth_point_centroid(*itp, gf,q))
@@ -676,7 +693,7 @@ void refineMeshBDS(GFace *gf, BDS_Mesh &m, const int NIT,
 
   double t_spl = 0, t_sw = 0,t_col = 0,t_sm = 0;
 
-  const double MINE_ = 0.67, MAXE_ = 1.4;
+  const double MINE_ = 0.6666, MAXE_ = 1.4;
 
   while (1){
     
diff --git a/Mesh/meshGFaceDelaunayInsertion.cpp b/Mesh/meshGFaceDelaunayInsertion.cpp
index 1c09478da741d8001a79496d4c222b8f908d086b..ffda6e0a222dc01432de3f4bd39e90dabcc13bd8 100644
--- a/Mesh/meshGFaceDelaunayInsertion.cpp
+++ b/Mesh/meshGFaceDelaunayInsertion.cpp
@@ -16,10 +16,12 @@
 #include "Numeric.h"
 #include "STensor3.h"
 #include "Context.h"
+#include "meshGFaceBoundaryLayers.h"
+#include "MLine.h"
+#include "MQuadrangle.h"
 
 double LIMIT_ = 0.5 * sqrt(2.0) * 1;
 
-static const bool _experimental_anisotropic_blues_band_ = false;
 int MTri3::radiusNorm = 2;
 
 // This function controls the frontal algorithm
@@ -493,7 +495,7 @@ double getSurfUV(MTriangle *t, std::vector<double> &Us, std::vector<double> &Vs)
   return s * 0.5;
 }
 
-bool insertVertex(GFace *gf, MVertex *v, double *param , MTri3 *t,
+bool insertVertex(bool force, GFace *gf, MVertex *v, double *param , MTri3 *t,
                   std::set<MTri3*, compareTri3Ptr> &allTets,
                   std::set<MTri3*, compareTri3Ptr> *activeTets,
                   std::vector<double> &vSizes, 
@@ -513,6 +515,7 @@ bool insertVertex(GFace *gf, MVertex *v, double *param , MTri3 *t,
   }
   else{
     recurFindCavityAniso(gf, shell, cavity, metric, param, t, Us, Vs);  
+    //    printf("RECURSIVELY FIND A CAVITY %d %d \n",shell.size(),cavity.size());
   }
   
   // check that volume is conserved
@@ -543,17 +546,7 @@ bool insertVertex(GFace *gf, MVertex *v, double *param , MTri3 *t,
     double LL = Extend1dMeshIn2dSurfaces() ? std::min(lc, lcBGM) : lcBGM;
 
     MTri3 *t4;
-    if (_experimental_anisotropic_blues_band_){
-      SMetric3 metrBGM = interpolation(vMetricsBGM[t->getVertex(0)->getIndex()],
-                                       vMetricsBGM[t->getVertex(1)->getIndex()],
-                                       vMetricsBGM[t->getVertex(2)->getIndex()],
-                                       ONE_THIRD, ONE_THIRD);
-      
-      t4 = new MTri3(t, LL,&metrBGM); 
-    }
-    else{
-      t4 = new MTri3(t, LL,0,&Us,&Vs,gf); 
-    }
+    t4 = new MTri3(t, LL,0,&Us,&Vs,gf); 
     
     double d1 = sqrt((it->v[0]->x() - v->x()) * (it->v[0]->x() - v->x()) +
                      (it->v[0]->y() - v->y()) * (it->v[0]->y() - v->y()) +
@@ -563,8 +556,10 @@ bool insertVertex(GFace *gf, MVertex *v, double *param , MTri3 *t,
                      (it->v[1]->z() - v->z()) * (it->v[1]->z() - v->z()));
     const double MID[3] = {0.5*(it->v[0]->x()+it->v[1]->x()),0.5*(it->v[0]->y()+it->v[1]->y()),0.5*(it->v[0]->z()+it->v[1]->z())}; 
     double d3 = sqrt((MID[0] - v->x()) * (MID[0] - v->x()) + (MID[1] - v->y()) * (MID[1] - v->y()) + (MID[2] - v->z()) * (MID[2] - v->z()));
-    if (d1 < LL * .25 || d2 < LL * .25 || d3 < LL * .25) onePointIsTooClose = true;
-    
+    if ((d1 < LL * .25 || d2 < LL * .25 || d3 < LL * .25) && !force) {
+      //      printf("TOO CLOSE %g %g %g %g %g %g\n",d1,d2,d3,LL,lc,lcBGM);
+      onePointIsTooClose = true;
+    }
     // if (t4->getRadius () < LIMIT_ / 2) onePointIsTooClose = true;
 
     newTris[k++] = t4;
@@ -576,14 +571,15 @@ bool insertVertex(GFace *gf, MVertex *v, double *param , MTri3 *t,
     if (otherSide)
       new_cavity.push_back(otherSide);
     double ss = fabs(getSurfUV(t4->tri(), Us, Vs));
-    if (ss < 1.e-25) ss = 1256172121;
+    if (ss < 1.e-25) ss = 1.e22;
     newVolume += ss;
     ++it;
   }
-  if (fabs(oldVolume - newVolume) < 1.e-12 * oldVolume && shell.size() > 3 && 
+  if (fabs(oldVolume - newVolume) < 1.e-12 * oldVolume && shell.size() >= 3 && 
       !onePointIsTooClose){
     connectTris(new_cavity.begin(), new_cavity.end());      
     allTets.insert(newTris, newTris + shell.size());
+    //    printf("shell.size() = %d triangles.size() = %d \n",shell.size(),allTets.size());
     if (activeTets){
       for (std::list<MTri3*>::iterator i = new_cavity.begin(); i != new_cavity.end(); ++i){
         int active_edge;
@@ -598,6 +594,8 @@ bool insertVertex(GFace *gf, MVertex *v, double *param , MTri3 *t,
   }
   // The cavity is NOT star shaped
   else{      
+    //    printf("cavity not star shaped %22.15E vs %22.15E\n",oldVolume,newVolume);
+    //    printf("shell.size() = %d triangles.size() = %d \n",shell.size(),allTets.size());
     for (unsigned int i = 0; i < shell.size(); i++) delete newTris[i];
     delete [] newTris;      
     ittet = cavity.begin();
@@ -717,6 +715,7 @@ static bool insertAPoint(GFace *gf, std::set<MTri3*,compareTri3Ptr>::iterator it
   MTri3 *ptin = 0;
   bool inside = false;
   double uv[2];
+  // FIXME !!!
   if (MTri3::radiusNorm == 2){
     inside = invMapUV(worst->tri(), center, Us, Vs, uv, 1.e-8);    
     if (inside)ptin = worst;
@@ -736,6 +735,7 @@ static bool insertAPoint(GFace *gf, std::set<MTri3*,compareTri3Ptr>::iterator it
   else {
     ptin =  search4Triangle (worst, center, Us, Vs, AllTris);
     if (ptin)inside = true;
+    //    if (!ptin)printf("strange : %g %g seems to be out of the domain for face %d\n",center[0],center[1],gf->tag());
   }
 
   //  if (!ptin)ptin = worst;
@@ -766,20 +766,13 @@ static bool insertAPoint(GFace *gf, std::set<MTri3*,compareTri3Ptr>::iterator it
     Vs.push_back(center[1]);
     
     if(!p.succeeded() || !insertVertex
-       (gf, v, center, worst, AllTris,ActiveTris, vSizes, vSizesBGM,vMetricsBGM, 
+       (false, gf, v, center, worst, AllTris,ActiveTris, vSizes, vSizesBGM,vMetricsBGM, 
         Us, Vs, metric) ) {
            
       MTriangle *base = worst->tri();
-      /*      
-      Msg::Info("Point %g %g of (%g %g , %g %g , %g %g) cannot be inserted",
-		center[0], center[1],
-		Us[base->getVertex(0)->getIndex()], 
-		Vs[base->getVertex(0)->getIndex()], 
-		Us[base->getVertex(1)->getIndex()], 
-		Vs[base->getVertex(1)->getIndex()], 
-		Us[base->getVertex(2)->getIndex()], 
-		Vs[base->getVertex(2)->getIndex()]);
-      */
+                  
+      //      Msg::Info("Point %g %g cannot be inserted because %d", center[0], center[1], p.succeeded() );
+      
       AllTris.erase(it);
       worst->forceRadius(-1);
       AllTris.insert(worst);                    
@@ -787,13 +780,14 @@ static bool insertAPoint(GFace *gf, std::set<MTri3*,compareTri3Ptr>::iterator it
       return false;
     }
     else {
+      //      printf("ouf ! %d\n",AllTris.size());
       gf->mesh_vertices.push_back(v);
       return true;
     }
   }
   else {
     MTriangle *base = worst->tri();    
-    /*
+    /*    
     Msg::Info("Point %g %g is outside (%g %g , %g %g , %g %g)",
 	      center[0], center[1],
 	      Us[base->getVertex(0)->getIndex()], 
@@ -810,6 +804,137 @@ static bool insertAPoint(GFace *gf, std::set<MTri3*,compareTri3Ptr>::iterator it
   }
 }
 
+// initially adds some boundary layer points onto the mesh
+void addBoundaryLayerPoints  (GFace *gf){
+
+  std::set<MTri3*,compareTri3Ptr> AllTris;
+  std::vector<double> vSizes, vSizesBGM, Us, Vs;
+  std::vector<SMetric3> vMetricsBGM;
+
+  BoundaryLayerColumns *_columns = buidAdditionalPoints2D (gf, M_PI/6.);
+
+  buildMeshGenerationDataStructures
+    (gf, AllTris, vSizes, vSizesBGM, vMetricsBGM, Us, Vs);
+
+  for (BoundaryLayerColumns::iter it = _columns->begin(); it != _columns->end(); ++it){
+    for (int i=0;i<it->second._column.size();++i){
+      while(1){
+	MTri3 *worst = *AllTris.begin();
+	if (worst->isDeleted()){
+	  delete worst->tri();
+	  delete worst;
+	  AllTris.erase(AllTris.begin());
+	}
+	else {
+	  break;
+	}
+      }
+      MVertex *v = it->second._column[i];
+      SMetric3 mmm = it->second._metrics[i];
+ 
+      double center[2],metric[3] = {1.,0,1.};
+      v->getParameter(0,center[0]);
+      v->getParameter(1,center[1]);
+      buildMeshMetric (gf,center,mmm,metric);
+      v->setIndex(Us.size());
+      vSizesBGM.push_back(vSizesBGM[((it->first))->getIndex()]);
+      vSizes.push_back(vSizes[((it->first))->getIndex()]);
+      Us.push_back(center[0]);
+      Vs.push_back(center[1]);
+      MTri3 *ptin =  search4Triangle (*AllTris.begin(), center, Us, Vs, AllTris);
+      if (ptin){
+	bool ok  = insertVertex
+	  (true, gf, v, center, ptin, AllTris,0, vSizes, vSizesBGM,vMetricsBGM, 
+	   Us, Vs, metric);
+	if (ok)gf->mesh_vertices.push_back(v);
+	else printf("failed !\n");
+      }
+      else {
+	printf("boundary layer point outside the domain ;-)\n");
+      }
+    }
+  } 
+
+  // Create all edges of the triangular mesh
+  std::set<MEdge,Less_Edge> allEdges;
+  {
+    std::set<MTri3*,compareTri3Ptr>::iterator it = AllTris.begin();
+    for (;it != AllTris.end();++it ){
+      MTri3 *worst = *it;
+      if (!worst->isDeleted()){
+	allEdges.insert(MEdge((*it)->tri()->getVertex(0),(*it)->tri()->getVertex(1)));
+	allEdges.insert(MEdge((*it)->tri()->getVertex(0),(*it)->tri()->getVertex(2)));
+	allEdges.insert(MEdge((*it)->tri()->getVertex(1),(*it)->tri()->getVertex(2)));
+      }
+    }
+  }
+  //  printf("%d edges generated\n",allEdges.size());
+
+  //  look for existing cavities and fill them up with quads
+  std::list<GEdge*> edges = gf->edges();
+  std::list<GEdge*>::iterator ite = edges.begin();
+  while(ite != edges.end()){
+    for(unsigned int i = 0; i< (*ite)->lines.size(); i++){
+      MVertex *v1 = (*ite)->lines[i]->getVertex(0);
+      MVertex *v2 = (*ite)->lines[i]->getVertex(1);
+      int nbCol1 = _columns->getNbColumns(v1);
+      int nbCol2 = _columns->getNbColumns(v2);
+      if (nbCol1 >= 0 && nbCol2 >= 0){ 
+	const BoundaryLayerData & c1 = _columns->getColumn(v1,MEdge(v1,v2));
+	const BoundaryLayerData & c2 = _columns->getColumn(v2,MEdge(v1,v2));
+	int N = std::min(c1._column.size(),c2._column.size());
+	
+	// Look for the highest edge on the column
+	int highestHorizontal = -1;
+	int highestVertical = -1;
+	for (int l=0;l < N ;++l){
+	  MVertex *v11,*v12,*v21,*v22;
+	  v21 = c1._column[l];
+	  v22 = c2._column[l];	    
+	  if (l == 0){
+	    v11 = v1;
+	    v12 = v2;
+	  }
+	  else {
+	    v11 = c1._column[l-1];
+	    v12 = c2._column[l-1];	    
+	  }
+	  if (allEdges.find(MEdge(v11,v21)) != allEdges.end() && allEdges.find(MEdge(v12,v22)) != allEdges.end()){
+	    highestVertical = l;	    
+	  }
+	  else {
+	    break;
+	  }
+	  
+	  if (allEdges.find(MEdge(v21,v22))!= allEdges.end()){
+	    highestHorizontal = l;
+	  }
+	}
+	//	printf("%d vs %d %d\n",N,highestHorizontal,highestVertical);
+	N = std::min(highestHorizontal,highestVertical);
+		//N--;
+	for (int l=0;l <= N ;++l){
+	  MVertex *v11,*v12,*v21,*v22;
+	  v21 = c1._column[l];
+	  v22 = c2._column[l];	    
+	  if (l == 0){
+	    v11 = v1;
+	    v12 = v2;
+	  }
+	  else {
+	    v11 = c1._column[l-1];
+	    v12 = c2._column[l-1];	    
+	  }
+	  //	printf("quadrangle generated\n");
+	  gf->quadrangles.push_back(new MQuadrangle(v11,v12,v22,v21));
+	}
+      }
+    }
+    ++ite;
+  }
+  transferDataStructure(gf, AllTris, Us, Vs); 
+}
+
 void bowyerWatson(GFace *gf)
 {
   std::set<MTri3*,compareTri3Ptr> AllTris;
@@ -856,15 +981,7 @@ void bowyerWatson(GFace *gf)
                       (Vs[base->getVertex(0)->getIndex()] + 
                        Vs[base->getVertex(1)->getIndex()] + 
                        Vs[base->getVertex(2)->getIndex()]) / 3.};
-      if (_experimental_anisotropic_blues_band_){
-        SMetric3 m = interpolation (vMetricsBGM[base->getVertex(0)->getIndex()],
-                                    vMetricsBGM[base->getVertex(1)->getIndex()],
-                                    vMetricsBGM[base->getVertex(2)->getIndex()],
-                                    pa[0],pa[1]);
-        buildMetric(gf, pa, m, metric);
-      }
-      else
-        buildMetric(gf, pa,  metric);
+      buildMetric(gf, pa,  metric);
       circumCenterMetric(worst->tri(), metric, Us, Vs, center, r2);       
       insertAPoint(gf, AllTris.begin(), center, metric, Us, Vs, vSizes, 
                    vSizesBGM, vMetricsBGM, AllTris);
@@ -881,7 +998,7 @@ void bowyerWatson(GFace *gf)
   The point isertion is done on the Voronoi Edges
 */
 
-static double lengthInfniteNorm(const double p[2], const double q[2], 
+double lengthInfniteNorm(const double p[2], const double q[2], 
 				const double quadAngle){
   double xp =  p[0] * cos(quadAngle) + p[1] * sin(quadAngle);
   double yp = -p[0] * sin(quadAngle) + p[1] * cos(quadAngle);
@@ -1078,6 +1195,7 @@ void bowyerWatsonFrontal(GFace *gf)
 //   sprintf(name,"frontal%d-param.pos", gf->tag());
 //   _printTris (name, AllTris, Us, Vs,true);
   transferDataStructure(gf, AllTris, Us, Vs); 
+  quadsToTriangles(gf,10000);
 } 
 
 void optimalPointFrontalQuad (GFace *gf, 
@@ -1163,14 +1281,134 @@ void optimalPointFrontalQuad (GFace *gf,
   } 
 }	
 
+void optimalPointFrontalQuadAniso (GFace *gf, 
+				   MTri3* worst, 
+				   int active_edge,
+				   std::vector<double> &Us,
+				   std::vector<double> &Vs,
+				   std::vector<double> &vSizes,
+				   std::vector<double> &vSizesBGM,			  
+				   double newPoint[2],
+				   double metric[3]){
+  MTriangle *base = worst->tri();
+  int ip1 = active_edge - 1 < 0 ? 2 : active_edge - 1;
+  int ip2 = active_edge;
+  int ip3 = (active_edge+1)%3;
+	
+  double P[2] =  {Us[base->getVertex(ip1)->getIndex()],  
+		  Vs[base->getVertex(ip1)->getIndex()]};
+  double Q[2] =  {Us[base->getVertex(ip2)->getIndex()], 
+		  Vs[base->getVertex(ip2)->getIndex()]};
+  double O[2] =  {Us[base->getVertex(ip3)->getIndex()], 
+		  Vs[base->getVertex(ip3)->getIndex()]};
+  double midpoint[2] = {0.5 * (P[0] + Q[0]), 0.5 * (P[1] + Q[1])};
+  
+  // compute background mesh data
+  double quadAngle  = backgroundMesh::current()->getAngle (midpoint[0],midpoint[1],0);
+  double center[2];
+  circumCenterInfinite (base, quadAngle,Us,Vs,center);                        
+  
+  // rotate the points with respect to the angle
+  double XP1 = 0.5*(Q[0] - P[0]);
+  double YP1 = 0.5*(Q[1] - P[1]);
 
-void bowyerWatsonFrontalLayers(GFace *gf, bool quad)
-{
+  double DX = 0.5*(Q[0] + P[0]);
+  double DY = 0.5*(Q[0] + P[0]);
 
-  std::set<MTri3*,compareTri3Ptr> AllTris;
-  std::set<MTri3*,compareTri3Ptr> ActiveTris;
-  std::vector<double> vSizes, vSizesBGM, Us, Vs;
-  std::vector<SMetric3> vMetricsBGM;
+  double xp =  XP1 * cos(quadAngle) + YP1 * sin(quadAngle); 
+  double yp = -XP1 * sin(quadAngle) + YP1 * cos(quadAngle); 	  
+
+  double X4 = O[0] -DX;
+  double Y4 = O[1] -DY;
+
+  double x4 =  X4 * cos(quadAngle) + Y4 * sin(quadAngle); 
+  double y4 = -X4 * sin(quadAngle) + Y4 * cos(quadAngle); 	  
+
+  // ensure xp > yp
+  bool exchange = false;
+  if (fabs(xp) < fabs(yp)){
+    double temp = xp;
+    xp = yp;
+    yp = temp;
+    temp = x4;
+    x4 = y4;
+    y4 = temp;
+    exchange = true;
+  }
+	
+  buildMetric(gf, midpoint, metric);
+  double RATIO = 1./pow(metric[0]*metric[2]-metric[1]*metric[1],0.25);  
+  const double rhoM1 = 0.5 * RATIO * 
+    (vSizes[base->getVertex(ip1)->getIndex()] + 
+     vSizes[base->getVertex(ip2)->getIndex()] );
+  const double rhoM2 = 0.5 * RATIO *  
+    (vSizesBGM[base->getVertex(ip1)->getIndex()] + 
+     vSizesBGM[base->getVertex(ip2)->getIndex()] );// * RATIO;
+  const double rhoM  = Extend1dMeshIn2dSurfaces() ? std::min(rhoM1, rhoM2) : rhoM2;
+  
+  
+  double npx,npy;
+
+  const double L_edge = lengthInfniteNorm(P,Q,quadAngle);
+
+  const double XP[2]={fabs(xp),fabs(yp)};
+  if (xp*yp >  0){
+    double xe[2] = { rhoM - fabs(xp) + fabs(yp), rhoM };
+    double xc[2]  = {0.5*(x4 - fabs(xp)), 0.5*(x4 + fabs(xp)) - fabs(yp)};
+    double xl[2] = {rhoM, rhoM+fabs(xp)-fabs(yp)};
+    const double R_Active = lengthInfniteNorm(xc,XP,0.0); // alerady rotated !
+    const double L_ec = lengthInfniteNorm(xe,xc,0.0); // alerady rotated !
+    const double L_el = lengthInfniteNorm(xe,xl,0.0); // alerady rotated !    
+    double t = ( L_edge - rhoM)/(L_edge - R_Active);
+    t = std::max(1.,t);
+    t = std::min(-L_el/L_ec,t);  
+    npx = xe[0] + t*(xc[0]-xe[0]);
+    npy = xe[1] + t*(xc[1]-xe[1]);
+  }
+  else {
+    double xe[2] = { rhoM - xp + yp, rhoM };
+    double xc[2]  = {0.5*(x4 - xp), 0.5*(x4 + xp) - yp};
+    double xl[2] = {rhoM, rhoM+xp-yp};
+    const double R_Active = lengthInfniteNorm(xc,XP,0.0); // alerady rotated !
+    const double L_ec = lengthInfniteNorm(xe,xc,0.0); // alerady rotated !
+    const double L_el = lengthInfniteNorm(xe,xl,0.0); // alerady rotated !    
+    const double XP[2]={xp,yp};
+    double t = ( L_edge - rhoM)/(L_edge - R_Active);
+    t = std::max(1.,t);
+    t = std::min(-L_el/L_ec,t);  
+    npx = xe[0] + t*(xc[0]-xe[0]);
+    npy = xe[1] + t*(xc[1]-xe[1]);
+  }
+
+  /*
+  if (xp*yp >  0){
+    npx = - fabs(xp)*factor;
+    npy = fabs(xp)*(1.+factor) - fabs(yp);
+  }
+  else {
+    npx = fabs(xp) * factor;
+    npy = (1.+factor)*fabs(xp) - fabs(yp);
+  }
+  */
+  if (exchange){
+    double temp = npx;
+    npx = npy;
+    npy = temp;
+  }	  
+  
+  
+  newPoint[0] = midpoint[0] + cos(quadAngle) * npx - sin(quadAngle) * npy;
+  newPoint[1] = midpoint[1] + sin(quadAngle) * npx + cos(quadAngle) * npy;
+
+  if ((midpoint[0] - newPoint[0])*(midpoint[0] - O[0]) +
+      (midpoint[1] - newPoint[1])*(midpoint[1] - O[1]) < 0){
+    newPoint[0] = midpoint[0] - cos(quadAngle) * npx + sin(quadAngle) * npy;
+    newPoint[1] = midpoint[1] - sin(quadAngle) * npx - cos(quadAngle) * npy;
+  } 
+}	
+
+
+void buildBackGroundMesh (GFace *gf) {
 
   if (!backgroundMesh::current()) {
     std::vector<MTriangle*> TR;
@@ -1179,10 +1417,16 @@ void bowyerWatsonFrontalLayers(GFace *gf, bool quad)
 				 gf->triangles[i]->getVertex(1),
 				 gf->triangles[i]->getVertex(2)));
     }
+    // avoid computing curvatures on the fly : only on the 
+    // BGM computes once curvatures at each node
+    //  Disable curvature control
     int CurvControl = CTX::instance()->mesh.lcFromCurvature;
     CTX::instance()->mesh.lcFromCurvature = 0;    
+    //  Do a background mesh
     bowyerWatson(gf);
+    //  Re-enable curv control if asked
     CTX::instance()->mesh.lcFromCurvature = CurvControl;    
+    // apply this to the BGM
     backgroundMesh::set(gf);
     char name[256];
     if (CTX::instance()->mesh.saveAll){
@@ -1191,12 +1435,22 @@ void bowyerWatsonFrontalLayers(GFace *gf, bool quad)
       sprintf(name,"cross-%d.pos",gf->tag());
       backgroundMesh::current()->print(name,gf,1);
     }
-    // FIXME DELETE CURRENT MESH
-    gf->triangles = TR;    
-  }
+  }  
+}
+
+void bowyerWatsonFrontalLayers(GFace *gf, bool quad)
+{
+
+  std::set<MTri3*,compareTri3Ptr> AllTris;
+  std::set<MTri3*,compareTri3Ptr> ActiveTris;
+  std::vector<double> vSizes, vSizesBGM, Us, Vs;
+  std::vector<SMetric3> vMetricsBGM;
+
+  buildBackGroundMesh (gf);
 
   if (quad){
     LIMIT_ = sqrt(2.) * .99;
+    //LIMIT_ = 4./3.;//sqrt(2.) * .99;
     MTri3::radiusNorm =-1;
   }
   
@@ -1234,6 +1488,8 @@ void bowyerWatsonFrontalLayers(GFace *gf, bool quad)
     
     std::set<MTri3*,compareTri3Ptr> ActiveTrisNotInFront;
 
+    //    printf("%d active triangles\n",ActiveTris.size());
+
     while (1){
       
       if (!ActiveTris.size())break;
@@ -1252,6 +1508,8 @@ void bowyerWatsonFrontalLayers(GFace *gf, bool quad)
 	//	for (active_edge = 0 ; active_edge < 0 ; active_edge ++){
 	//	  if (active_edges[active_edge])break;	
 	//	}
+	//	Msg::Info("%7d points created -- Worst tri infinite radius is %8.3f -- front size %6d",
+	//		     vSizes.size(), worst->getRadius(),_front.size());
 	if(ITER++ % 5000 == 0)
 	  Msg::Debug("%7d points created -- Worst tri infinite radius is %8.3f -- front size %6d",
 		     vSizes.size(), worst->getRadius(),_front.size());
@@ -1262,12 +1520,13 @@ void bowyerWatsonFrontalLayers(GFace *gf, bool quad)
 	else optimalPointFrontal (gf,worst,active_edge,Us,Vs,vSizes,vSizesBGM,newPoint,metric);
 	
 	
+	//	printf("start INSERT A POINT %g %g \n",newPoint[0],newPoint[1]);
 	insertAPoint(gf, AllTris.end(), newPoint, 0, Us, Vs, vSizes,
 		     vSizesBGM, vMetricsBGM, AllTris, &ActiveTris, worst);
 	//      else if (!worst->isDeleted() && worst->getRadius() > LIMIT_){
 	//	ActiveTrisNotInFront.insert(worst);
 	//      }
-	
+	//	printf("-----------------> size %d\n",AllTris.size());
 	
 	/*
 	  if(ITER % 1== 0){
@@ -1295,11 +1554,11 @@ void bowyerWatsonFrontalLayers(GFace *gf, bool quad)
     if (!ActiveTris.size())break;
   }
   
-  //   char name[245];
+     char name[245];
   //   sprintf(name,"frontal%d-real.pos", gf->tag());
   //   _printTris (name, AllTris, Us, Vs,false);
-  //   sprintf(name,"frontal%d-param.pos", gf->tag());
-  //   _printTris (name, AllTris, Us, Vs,true);
+     //     sprintf(name,"frontal%d-param.pos", gf->tag());
+     //     _printTris (name, AllTris, Us, Vs,true);
   transferDataStructure(gf, AllTris, Us, Vs); 
   MTri3::radiusNorm = 2;
   LIMIT_ = 0.5 * sqrt(2.0) * 1;
diff --git a/Mesh/meshGFaceDelaunayInsertion.h b/Mesh/meshGFaceDelaunayInsertion.h
index 0890d9888c29358cd8a79d92f88da6472a523dda..ce4f189839760d02d98d1b418a7ab787ee69ee1c 100644
--- a/Mesh/meshGFaceDelaunayInsertion.h
+++ b/Mesh/meshGFaceDelaunayInsertion.h
@@ -97,6 +97,8 @@ void connectTriangles(std::set<MTri3*,compareTri3Ptr> &AllTris);
 void bowyerWatson(GFace *gf);
 void bowyerWatsonFrontal(GFace *gf);
 void bowyerWatsonFrontalLayers(GFace *gf, bool quad);
+void buildBackGroundMesh (GFace *gf);
+void addBoundaryLayerPoints  (GFace *gf);
 
 struct edgeXface
 {
diff --git a/Mesh/meshGFaceOptimize.cpp b/Mesh/meshGFaceOptimize.cpp
index e69abe59048b625404201aeae8e7f5cd8a1b4e56..6c32f154564b24a5d70fe3cf8fce11884af0f296 100644
--- a/Mesh/meshGFaceOptimize.cpp
+++ b/Mesh/meshGFaceOptimize.cpp
@@ -846,6 +846,8 @@ static void _relocateVertexConstrained (GFace *gf,
 					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;
@@ -923,8 +925,13 @@ static void _relocateVertexConstrained (GFace *gf,
 static void _relocateVertex (GFace *gf,
 			     MVertex *ver,
 			     const std::vector<MElement*> &lt){
+  
   double R; SPoint3 c; bool isSphere = gf->isSphere(R,c);
   if( ver->onWhat()->dim() == 2){
+
+    MFaceVertex *fv = dynamic_cast<MFaceVertex*>(ver);
+    if (fv->bl_data)return;
+
     double initu,initv;
     ver->getParameter(0, initu);
     ver->getParameter(1, initv);
@@ -957,17 +964,18 @@ static void _relocateVertex (GFace *gf,
       }
       bool success = false;
       double factor = 1.0;
+      SPoint2 newp;
       for (int i=0;i<10;i++){
-	SPoint2 newp = after + before*(1.-factor);
+	newp = after + before*(1.-factor);
 	success = _isItAGoodIdeaToMoveThatVertex (gf,  lt, ver,before,newp);
 	if (success)break;
 	factor *= 0.5;
       }
 
-      if (success){ // ne devrait-on pas utiliser newp ici au lieu de after ??? (vu la boucle precedente)
-	ver->setParameter(0, after.x());
-	ver->setParameter(1, after.y());
-	GPoint pt = gf->point(after);
+      if (success){ 
+	ver->setParameter(0, newp.x());
+	ver->setParameter(1, newp.y());
+	GPoint pt = gf->point(newp);
 	if(pt.succeeded()){
 	  ver->x() = pt.x();
 	  ver->y() = pt.y();
@@ -1876,8 +1884,6 @@ void recombineIntoQuads(GFace *gf,
   if (saveAll) gf->model()->writeMSH("before.msh");
   int success = _recombineIntoQuads(gf, 0);
 
-  // gf->addLayersOfQuads(1, 0);
-
   if (saveAll) gf->model()->writeMSH("raw.msh");
   if(haveParam && nodeRepositioning)
     laplaceSmoothing(gf, CTX::instance()->mesh.nbSmoothing);
@@ -1895,7 +1901,7 @@ void recombineIntoQuads(GFace *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); }
-          laplaceSmoothing(gf);
+          laplaceSmoothing(gf,CTX::instance()->mesh.nbSmoothing);
           int z = 0; //edgeSwapQuadsForBetterQuality(gf);
           if(z && saveAll){ sprintf(NAME,"iter%dS.msh",COUNT++); gf->model()->writeMSH(NAME); }
           if (!(x+y+z)) break;
@@ -1922,7 +1928,7 @@ void recombineIntoQuads(GFace *gf,
   Msg::Info("Simple recombination algorithm completed (%g s)", t2 - t1);
 }
 
-void quadsToTriangles(GFace *gf, double minqual = -10000.)
+void quadsToTriangles(GFace *gf, double minqual)
 {
   std::vector<MQuadrangle*> qds;
   for (int i=0;i<gf->quadrangles.size();i++){
@@ -1968,7 +1974,7 @@ void recombineIntoQuadsIterative(GFace *gf)
   return;
   int COUNT = 0;
   while (1){
-    quadsToTriangles(gf);
+    quadsToTriangles(gf,-10000);
     {char NAME[245];sprintf(NAME,"iterT%d.msh",COUNT); gf->model()->writeMSH(NAME);}
     std::set<MTri3*,compareTri3Ptr> AllTris;
     std::vector<double> vSizes, vSizesBGM, Us, Vs;
diff --git a/Mesh/meshGFaceOptimize.h b/Mesh/meshGFaceOptimize.h
index 55b2d34e371aef4b2dcfe9d4c7a66723f4a8af78..5aec057914fe62794aa8407e0835a6409ccfd038 100644
--- a/Mesh/meshGFaceOptimize.h
+++ b/Mesh/meshGFaceOptimize.h
@@ -98,6 +98,7 @@ void recombineIntoQuads(GFace *gf,
 			bool topologicalOpti   = true, 
 			bool nodeRepositioning = true);
 void recombineIntoQuadsIterative(GFace *gf);
+void quadsToTriangles(GFace *gf, double minqual);
 
 struct swapquad{
   int v[4];
diff --git a/Mesh/meshPartition.cpp b/Mesh/meshPartition.cpp
index ea84394a4b6b90f6323db0f9dd5e52fa830257d3..57717553ddf46e3d686ef3688a77268594e15039 100644
--- a/Mesh/meshPartition.cpp
+++ b/Mesh/meshPartition.cpp
@@ -987,8 +987,12 @@ void assignPartitionBoundary(GModel *model, MFace &me,
   }
   else ppe = *it;
   // to finish !!
-  ppe->triangles.push_back(new MTriangle (me.getVertex(0),me.getVertex(1),
-                                          me.getVertex(2)));
+  if (me.getNumVertices() == 3)
+    ppe->triangles.push_back(new MTriangle (me.getVertex(0),me.getVertex(1),
+					    me.getVertex(2)));
+  else
+    ppe->quadrangles.push_back(new MQuadrangle (me.getVertex(0),me.getVertex(1),
+					      me.getVertex(2),me.getVertex(3)));
 }
 
 void assignPartitionBoundary(GModel *model,
diff --git a/Mesh/qualityMeasures.cpp b/Mesh/qualityMeasures.cpp
index 18fe5f57aa5f5e87c7359fbe14ff4a97c12ae9b5..e39cd9e5d8aa2849c659d69e9317841324a9d9d9 100644
--- a/Mesh/qualityMeasures.cpp
+++ b/Mesh/qualityMeasures.cpp
@@ -323,8 +323,14 @@ double mesh_functional_distorsion_pN(MElement *t)
  
   fullVector<double> Bi( jac->matrixLag2Bez.size1() );
   jac->matrixLag2Bez.mult(Ji,Bi);
- 
-  //  jac->matrixLag2Bez.print("Lag2Bez");
+  /* 
+      jac->matrixLag2Bez.print("Lag2Bez");
+
+      jac->points.print("Points");
+      t->getFunctionSpace(t->getPolynomialOrder())->points.print("lagrangianNodes");
+      t->getFunctionSpace(t->getPolynomialOrder())->monomials.print("Monomials");
+      t->getFunctionSpace(t->getPolynomialOrder())->coefficients.print("Coefficients");
+  */
 
   return *std::min_element(Bi.getDataPtr(),Bi.getDataPtr()+Bi.size());
 }
@@ -337,10 +343,10 @@ double qmDistorsionOfMapping (MTriangle *e)
   else if  (e->getPolynomialOrder() == 2) {
     //    const double exact = mesh_functional_distorsion_p2_exact(e);
     const double bezier= mesh_functional_distorsion_pN(e);
-    if (bezier < .1){
-      const double bezier_refined= mesh_functional_distorsion_p2_bezier_refined(e);
-      return bezier_refined;
-    }
+    //    if (bezier < .1){
+    //      const double bezier_refined= mesh_functional_distorsion_p2_bezier_refined(e);
+    //      return bezier_refined;
+    //    }
     /*
     if (exact < .99 || exact > 1.01){
       FILE *f = fopen ("statistics.dat","a");
@@ -527,7 +533,7 @@ double qmQuadrangleAngles (MQuadrangle *e) {
 
     double c;
     prosca(v1,v2,&c);
-    printf("Youhou %g %g\n",c,acos(c)*180/M_PI);
+    //    printf("Youhou %g %g\n",c,acos(c)*180/M_PI);
     double x = fabs(acos(c))-M_PI/2;
     double quality = (atan(a*(x+M_PI/4)) + atan(a*(2*M_PI/4 - (x+M_PI/4))))/den;
     worst_quality = std::min(worst_quality, quality);
diff --git a/Numeric/fullMatrix.h b/Numeric/fullMatrix.h
index 76110a308b8488b3ea62723fbf57ea21c9d356d2..1285646b83c413981754002955ca77e2e0ca444d 100644
--- a/Numeric/fullMatrix.h
+++ b/Numeric/fullMatrix.h
@@ -125,12 +125,12 @@ class fullVector
   // printing and file treatment
   void print(const char *name="") const
   {
-    printf("Printing vector %s:\n", name);
-    printf("  ");
+    printf("double %s[%d]=\n", name,size());
+    printf("{  ");
     for(int I = 0; I < size(); I++){
       printf("%12.5E ", (*this)(I));
     }
-    printf("\n");
+    printf("};\n");
   }
   void binarySave (FILE *f) const
   {
@@ -493,16 +493,19 @@ class fullMatrix
 
   void print(const std::string name = "", const std::string format = "%12.5E ") const
   {
-    printf("Printing matrix %s:\n", name.c_str());
     int ni = size1();
     int nj = size2();
+    printf("double %s [ %d ][ %d ]= { \n", name.c_str(),ni,nj);
     for(int I = 0; I < ni; I++){
-      printf("  ");
+      printf("{  ");
       for(int J = 0; J < nj; J++){
         printf(format.c_str(), (*this)(I, J));
+	if (J!=nj-1)printf(",");
       }
-      printf("\n");
+      if (I!=ni-1)printf("},\n");
+      else printf("}\n");
     }
+    printf("};\n");
   }
 
 // specific functions for dgshell
diff --git a/Solver/helmholtzTerm.h b/Solver/helmholtzTerm.h
index ae03f491d401334d85e947404be575961b0be502..3557184ccd3a0384577d4be076856068a1de398c 100644
--- a/Solver/helmholtzTerm.h
+++ b/Solver/helmholtzTerm.h
@@ -50,9 +50,9 @@ class helmholtzTerm : public femTerm<scalar> {
 
     MElement *e = se->getMeshElement();
     // compute integration rule
-    const int integrationOrder = (_a) ? 2 * e->getPolynomialOrder() : 
-      2 * (e->getPolynomialOrder() - 1);
-   //    const int integrationOrder = 2 * e->getPolynomialOrder() + 1; 
+    // const int integrationOrder = (_a) ? 2 * e->getPolynomialOrder() : 
+    //2 * (e->getPolynomialOrder() - 1);
+       const int integrationOrder = 2 * e->getPolynomialOrder() + 1; 
 
     int npts; IntPt *GP;
     e->getIntegrationPoints(integrationOrder, &npts, &GP);
diff --git a/benchmarks/2d/conge.geo b/benchmarks/2d/conge.geo
index 41be12dc9bb454ffecbbb04f8c6042a61865eadc..b61af396b53d9e75a2fc658bddd30b61687f766c 100644
--- a/benchmarks/2d/conge.geo
+++ b/benchmarks/2d/conge.geo
@@ -78,5 +78,5 @@ Physical Line(25) = {12,13,14,15,16,17,18,19,20,10};
 Physical Surface(26) = {24};
 Physical Line(27) = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, 13, 12, 11};
 Physical Line(28) = {17, 16, 20, 19, 18, 15};
-//Recombine Surface {24, 22};
-//Mesh.RecombinationAlgorithm=1;
\ No newline at end of file
+Recombine Surface {24, 22};
+Mesh.RecombinationAlgorithm=1;
\ No newline at end of file
diff --git a/benchmarks/2d/naca12_2d.geo b/benchmarks/2d/naca12_2d.geo
index 6d1b45d0d4653271f039e6b3dda161886ff8126d..432e233608e2c0bcb2e444669d82104cfdf70608 100644
--- a/benchmarks/2d/naca12_2d.geo
+++ b/benchmarks/2d/naca12_2d.geo
@@ -227,3 +227,14 @@ Plane Surface(11) = {9,10};
 //Physical Surface(11)={11};
 //Point(9999) = {0.6,0,0,1};
 
+Field[2] = BoundaryLayer;
+Field[2].NodesList = {1};
+//Field[2].EdgesList = {1,2,3,4};
+Field[2].EdgesList = {1,2,3,4};
+Field[2].hfar = 1.5;
+Field[2].hwall_n = 0.0001;
+Field[2].hwall_t = 0.03;
+Field[2].ratio = 1.3;
+Field[2].thickness = .05;
+Background Field = 2;
+
diff --git a/benchmarks/2d/wing-splines.geo b/benchmarks/2d/wing-splines.geo
index 53c3aa4b630c4d1d28c4365c07cf72f930df2d5b..1bb082ea43c2a4f79d8b56ec895b13fe1cacd5ca 100644
--- a/benchmarks/2d/wing-splines.geo
+++ b/benchmarks/2d/wing-splines.geo
@@ -1,5 +1,6 @@
+Mesh.CharacteristicLengthExtendFromBoundary= 0;
 
-scale = .01 ;
+scale = 1 ;
 
 lc_wing = 0.05 * scale ;
 lc_box = 30 * scale ;
@@ -604,5 +605,16 @@ Circle(408) = {4355,4351,4354};
 Circle(409) = {4354,4351,4353};
 Circle(410) = {4353,4351,4352};
 */
-Recombine Surface {406};
-Mesh.RecombinationAlgorithm=1;
\ No newline at end of file
+//Recombine Surface {406};
+//Mesh.RecombinationAlgorithm=1;
+
+Field[2] = BoundaryLayer;
+Field[2].EdgesList = {12,1,2,3,4,5,6,11,10};
+Field[2].NodesList = {3847,3846,4006,4007,4008,4222,4221};
+Field[2].hfar = 20.1;
+Field[2].hwall_n = 1e-04;
+Field[2].hwall_t = 14.e-3;
+Field[2].ratio = 1.2;
+//Field[2].thickness = .01;
+Field[2].thickness = .0075;
+Background Field = 2;
diff --git a/benchmarks/step/linkrods.geo b/benchmarks/step/linkrods.geo
index 0ec28e0765c668d8cf31ef563d1e1e40cbf8b783..4770c2f0de162c91d70713d11749e44a61c0907b 100644
--- a/benchmarks/step/linkrods.geo
+++ b/benchmarks/step/linkrods.geo
@@ -1,12 +1,13 @@
 
-Mesh.CharacteristicLengthFactor=0.9;
+Mesh.CharacteristicLengthFactor=.4;
 
-Mesh.CharacteristicLengthFromCurvature=1; //-clcurv
-Mesh.CharacteristicLengthMin = 0.05; //-clmin
+//Mesh.CharacteristicLengthFromCurvature=1; //-clcurv
+Mesh.CharacteristicLengthMin = 0.25; //-clmin
 Mesh.CharacteristicLengthMax = 4.0; //-clmax
-Mesh.LcIntegrationPrecision=1.e-5; //-epslc1d
+//Mesh.LcIntegrationPrecision=1.e-5; //-epslc1d
 
 Mesh.MinimumCirclePoints=15; //default=7
-Mesh.CharacteristicLengthExtendFromBoundary=0;
+//Mesh.CharacteristicLengthExtendFromBoundary=0;
 
 Merge "linkrods.step";
+
diff --git a/benchmarks/stl/mobilette.geo b/benchmarks/stl/mobilette.geo
index b5242b188bdd69fea7ec7dfb159b2e8ad2425335..e754da25569b598f8603b15ac76e945ae585e4e7 100644
--- a/benchmarks/stl/mobilette.geo
+++ b/benchmarks/stl/mobilette.geo
@@ -1,4 +1,4 @@
-//Mesh.Algorithm = 8; //(1=MeshAdapt, 2=Automatic, 5=Delaunay, 6=Frontal, 7=bamg) 
+Mesh.Algorithm = 8; //(1=MeshAdapt, 2=Automatic, 5=Delaunay, 6=Frontal, 7=bamg) 
 Mesh.CharacteristicLengthMin=1.5;
 Mesh.CharacteristicLengthMax=2.5;
 Mesh.RemeshAlgorithm=1;
@@ -27,8 +27,8 @@ EndFor
 Surface Loop(1) = {s : s + #ss[]-1};
 Volume(1) = {1};
 
-//Mesh.RecombineAll=1;
-//Mesh.RecombinationAlgorithm=1;
+Mesh.RecombineAll=1;
+Mesh.RecombinationAlgorithm=1;
 
 Physical Surface(1) = {s : s + #ss[]-1};
 Physical Volume(1) = 1;