From cc86cd4942d19adccc94b039954dc35702f3cdba Mon Sep 17 00:00:00 2001
From: Jean-Francois Remacle <jean-francois.remacle@uclouvain.be>
Date: Wed, 15 Apr 2009 14:07:11 +0000
Subject: [PATCH] *** empty log message ***

---
 Common/Makefile                     |   22 +-
 Fltk/Makefile                       |   27 +-
 Fltk/menuWindow.cpp                 |    2 +-
 Geo/GEdge.cpp                       |    2 +-
 Geo/GEdge.h                         |    7 +-
 Geo/GEdgeCompound.cpp               |    7 +-
 Geo/GFaceCompound.cpp               |  155 ++--
 Geo/GFaceCompound.h                 |   16 +-
 Geo/MElement.cpp                    |   13 +-
 Geo/Makefile                        |   16 +-
 Geo/STensor3.h                      |  139 ++++
 Mesh/BackgroundMesh.cpp             |   51 +-
 Mesh/Field.cpp                      |   82 +-
 Mesh/Field.h                        |    6 +-
 Mesh/HighOrder.cpp                  |  150 ++--
 Mesh/HighOrder.h                    |   13 +
 Mesh/Makefile                       |   76 +-
 Mesh/gmshSmoothHighOrder.cpp        | 1105 ++++++++++++++++++++++++---
 Mesh/gmshSmoothHighOrder.h          |   61 +-
 Mesh/meshGEdge.cpp                  |   38 +-
 Mesh/meshGFace.cpp                  |   21 +
 Mesh/meshGFaceBDS.cpp               |   23 +-
 Mesh/meshGFaceDelaunayInsertion.cpp |   22 +-
 Mesh/qualityMeasures.cpp            |   23 +-
 Numeric/GmshMatrix.cpp              |   60 ++
 Numeric/GmshMatrix.h                |   27 +
 Numeric/Numeric.cpp                 |  122 +++
 Numeric/Numeric.h                   |    2 +
 Numeric/gmshAssembler.h             |    7 +
 Numeric/gmshElasticity.cpp          |    2 +-
 Numeric/gmshElasticity.h            |    3 +-
 Numeric/gmshLaplace.cpp             |    4 +
 Numeric/gmshLaplace.h               |   13 +-
 Numeric/gmshLinearSystemGmm.h       |    4 +-
 Numeric/gmshTermOfFormulation.h     |    3 +-
 Parser/Makefile                     |    6 +-
 Plugin/Makefile                     |   37 +-
 Post/shapeFunctions.h               |    1 +
 benchmarks/2d/naca12_2d.geo         |   20 +-
 39 files changed, 2018 insertions(+), 370 deletions(-)
 create mode 100644 Geo/STensor3.h

diff --git a/Common/Makefile b/Common/Makefile
index e615364441..36e3c13d28 100644
--- a/Common/Makefile
+++ b/Common/Makefile
@@ -67,7 +67,9 @@ Gmsh${OBJEXT}: Gmsh.cpp GmshConfig.h GmshDefines.h ../Numeric/GmshPredicates.h \
   ../Geo/Pair.h ../Geo/GRegion.h ../Geo/GEntity.h ../Geo/SPoint3.h \
   ../Geo/SBoundingBox3d.h GmshMessage.h OpenFile.h CreateFile.h Options.h \
   ../Post/ColorTable.h CommandLine.h OS.h ../Mesh/Generator.h \
-  ../Mesh/Field.h ../Common/GmshConfig.h ../Post/PView.h Context.h \
+  ../Mesh/Field.h ../Common/GmshConfig.h ../Geo/STensor3.h \
+  ../Geo/SVector3.h ../Numeric/GmshMatrix.h ../Common/GmshMessage.h \
+  ../Numeric/Numeric.h ../Numeric/GmshMatrix.h ../Post/PView.h Context.h \
   ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h \
   ../Mesh/meshPartition.h GmshDaemon.h ../Plugin/PluginManager.h
 GmshMessage${OBJEXT}: GmshMessage.cpp GmshConfig.h GmshMessage.h Gmsh.h \
@@ -88,14 +90,16 @@ Options${OBJEXT}: Options.cpp GmshConfig.h GmshDefines.h ../Geo/GModel.h \
   ../Geo/SBoundingBox3d.h GmshMessage.h ../Fltk/Draw.h \
   ../Mesh/Generator.h Context.h ../Geo/CGNSOptions.h \
   ../Mesh/meshPartitionOptions.h Options.h ../Post/ColorTable.h \
-  DefaultOptions.h ../Mesh/Field.h ../Common/GmshConfig.h ../Post/PView.h \
-  ../Mesh/BackgroundMesh.h ../Post/PViewOptions.h ../Post/ColorTable.h \
-  ../Post/PViewData.h ../Numeric/GmshMatrix.h ../Common/GmshMessage.h \
-  ../Post/adaptiveData.h ../Plugin/PluginManager.h ../Plugin/Plugin.h \
-  ../Common/Options.h ../Post/PViewDataList.h ../Post/PViewData.h \
-  ../Common/ListUtils.h ../Fltk/GUI.h ../Fltk/Solvers.h \
-  ../Fltk/menuWindow.h ../Fltk/popupButton.h ../Fltk/graphicWindow.h \
-  ../Fltk/openglWindow.h ../Graphics/drawContext.h ../Fltk/optionWindow.h \
+  DefaultOptions.h ../Mesh/Field.h ../Common/GmshConfig.h \
+  ../Geo/STensor3.h ../Geo/SVector3.h ../Numeric/GmshMatrix.h \
+  ../Common/GmshMessage.h ../Numeric/Numeric.h ../Numeric/GmshMatrix.h \
+  ../Post/PView.h ../Mesh/BackgroundMesh.h ../Post/PViewOptions.h \
+  ../Post/ColorTable.h ../Post/PViewData.h ../Post/adaptiveData.h \
+  ../Plugin/PluginManager.h ../Plugin/Plugin.h ../Common/Options.h \
+  ../Post/PViewDataList.h ../Post/PViewData.h ../Common/ListUtils.h \
+  ../Fltk/GUI.h ../Fltk/Solvers.h ../Fltk/menuWindow.h \
+  ../Fltk/popupButton.h ../Fltk/graphicWindow.h ../Fltk/openglWindow.h \
+  ../Graphics/drawContext.h ../Fltk/optionWindow.h \
   ../Fltk/spherePositionWidget.h ../Fltk/colorbarWindow.h \
   ../Fltk/solverWindow.h ../Fltk/manipWindow.h ../Fltk/messageWindow.h \
   ../Fltk/contextWindow.h ../Fltk/clippingWindow.h
diff --git a/Fltk/Makefile b/Fltk/Makefile
index eedf5f0376..d6c3571841 100644
--- a/Fltk/Makefile
+++ b/Fltk/Makefile
@@ -82,7 +82,9 @@ Main${OBJEXT}: Main.cpp GUI.h menuWindow.h popupButton.h ../Common/Gmsh.h \
   ../Geo/GEdge.h ../Geo/SPoint2.h ../Geo/SVector3.h ../Geo/Pair.h \
   ../Geo/GRegion.h ../Geo/GEntity.h ../Geo/SPoint3.h \
   ../Geo/SBoundingBox3d.h ../Mesh/Field.h ../Common/GmshConfig.h \
-  ../Post/PView.h ../Mesh/BackgroundMesh.h
+  ../Geo/STensor3.h ../Geo/SVector3.h ../Numeric/GmshMatrix.h \
+  ../Numeric/Numeric.h ../Numeric/GmshMatrix.h ../Post/PView.h \
+  ../Mesh/BackgroundMesh.h
 GUI${OBJEXT}: GUI.cpp GUI.h graphicWindow.h openglWindow.h \
   ../Graphics/drawContext.h ../Geo/SBoundingBox3d.h ../Geo/SPoint3.h \
   menuWindow.h popupButton.h optionWindow.h spherePositionWidget.h \
@@ -102,10 +104,12 @@ GUI${OBJEXT}: GUI.cpp GUI.h graphicWindow.h openglWindow.h \
   ../Geo/MVertex.h ../Geo/SVector3.h ../Geo/MFace.h ../Geo/MVertex.h \
   ../Geo/MEdge.h ../Geo/SVector3.h ../Numeric/FunctionSpace.h \
   ../Numeric/GmshMatrix.h ../Numeric/Gauss.h ../Post/PView.h Solvers.h \
-  ../Mesh/Field.h ../Plugin/Plugin.h ../Common/Options.h \
-  ../Post/PViewDataList.h ../Post/PViewData.h ../Common/ListUtils.h \
-  ../Plugin/PluginManager.h ../Common/OpenFile.h Win32Icon.h \
-  ../Common/Context.h ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h
+  ../Mesh/Field.h ../Geo/STensor3.h ../Geo/SVector3.h \
+  ../Numeric/Numeric.h ../Numeric/GmshMatrix.h ../Plugin/Plugin.h \
+  ../Common/Options.h ../Post/PViewDataList.h ../Post/PViewData.h \
+  ../Common/ListUtils.h ../Plugin/PluginManager.h ../Common/OpenFile.h \
+  Win32Icon.h ../Common/Context.h ../Geo/CGNSOptions.h \
+  ../Mesh/meshPartitionOptions.h
 graphicWindow${OBJEXT}: graphicWindow.cpp GUI.h graphicWindow.h openglWindow.h \
   ../Graphics/drawContext.h ../Geo/SBoundingBox3d.h ../Geo/SPoint3.h \
   paletteWindow.h mainWindow.h menuWindow.h popupButton.h messageWindow.h \
@@ -154,10 +158,11 @@ menuWindow${OBJEXT}: menuWindow.cpp ../Common/GmshConfig.h \
   Solvers.h ../Common/CommandLine.h ../Mesh/Generator.h \
   ../Mesh/HighOrder.h ../Post/PView.h ../Post/PViewData.h \
   ../Post/PViewOptions.h ../Post/ColorTable.h ../Mesh/Field.h \
-  ../Common/OS.h ../Common/StringUtils.h ../Common/OpenFile.h \
-  ../Common/CreateFile.h ../Geo/findLinks.h ../Common/ListUtils.h \
-  ../Geo/GeoStringInterface.h ../Common/Context.h ../Geo/CGNSOptions.h \
-  ../Mesh/meshPartitionOptions.h
+  ../Geo/STensor3.h ../Geo/SVector3.h ../Numeric/Numeric.h \
+  ../Numeric/GmshMatrix.h ../Common/OS.h ../Common/StringUtils.h \
+  ../Common/OpenFile.h ../Common/CreateFile.h ../Geo/findLinks.h \
+  ../Common/ListUtils.h ../Geo/GeoStringInterface.h ../Common/Context.h \
+  ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h
 optionWindow${OBJEXT}: optionWindow.cpp ../Common/GmshConfig.h \
   ../Common/GmshDefines.h ../Common/GmshMessage.h GUI.h optionWindow.h \
   spherePositionWidget.h colorbarWindow.h ../Post/ColorTable.h \
@@ -190,7 +195,9 @@ fieldWindow${OBJEXT}: fieldWindow.cpp GUI.h Draw.h fieldWindow.h paletteWindow.h
   ../Geo/GEdge.h ../Geo/SPoint2.h ../Geo/SVector3.h ../Geo/Pair.h \
   ../Geo/GRegion.h ../Geo/GEntity.h ../Geo/SPoint3.h \
   ../Geo/SBoundingBox3d.h ../Post/PView.h ../Common/GmshMessage.h \
-  ../Mesh/Field.h ../Common/GmshConfig.h ../Geo/GeoStringInterface.h \
+  ../Mesh/Field.h ../Common/GmshConfig.h ../Geo/STensor3.h \
+  ../Geo/SVector3.h ../Numeric/GmshMatrix.h ../Numeric/Numeric.h \
+  ../Numeric/GmshMatrix.h ../Geo/GeoStringInterface.h \
   ../Common/ListUtils.h ../Common/Options.h ../Post/ColorTable.h \
   ../Common/Context.h ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h
 pluginWindow${OBJEXT}: pluginWindow.cpp GUI.h Draw.h pluginWindow.h \
diff --git a/Fltk/menuWindow.cpp b/Fltk/menuWindow.cpp
index 9f744901e6..1afbfd1dc6 100644
--- a/Fltk/menuWindow.cpp
+++ b/Fltk/menuWindow.cpp
@@ -2410,7 +2410,7 @@ contextItem menu_mesh[] = {
 #if defined(HAVE_FOURIER_MODEL)
   {"Reparameterize",   (Fl_Callback *)mesh_parameterize_cb} , 
 #endif
-  //  {"Reclassify",   (Fl_Callback *)mesh_classify_cb} , 
+  {"Reclassify",   (Fl_Callback *)mesh_classify_cb} , 
   {"Save",         (Fl_Callback *)mesh_save_cb} ,
   {""} 
 };  
diff --git a/Geo/GEdge.cpp b/Geo/GEdge.cpp
index 85309dda0a..e1379d1a36 100644
--- a/Geo/GEdge.cpp
+++ b/Geo/GEdge.cpp
@@ -21,7 +21,7 @@
 #endif
 
 GEdge::GEdge(GModel *model, int tag, GVertex *_v0, GVertex *_v1)
-  : GEntity(model, tag), _tooSmall(false), v0(_v0), v1(_v1)
+  : GEntity(model, tag), _tooSmall(false), v0(_v0), v1(_v1), compound(0)
 {
   if(v0) v0->addEdge(this);
   if(v1 && v1 != v0) v1->addEdge(this);
diff --git a/Geo/GEdge.h b/Geo/GEdge.h
index a4489807ac..3cdad01497 100644
--- a/Geo/GEdge.h
+++ b/Geo/GEdge.h
@@ -16,6 +16,7 @@
 class MElement;
 class MLine;
 class ExtrudeParams;
+class GEdgeCompound;
 
 #include <set>
 
@@ -27,8 +28,8 @@ class GEdge : public GEntity {
 
  protected:
   GVertex *v0, *v1;
+  GEdgeCompound *compound; // this model edge belongs to a compound 
   std::list<GFace *> l_faces;
-
   std::set<GFace *> bl_faces;
 
  public:
@@ -153,6 +154,10 @@ class GEdge : public GEntity {
   
   virtual bool XYZToU(const SVector3& Q,double& t,const double relax=0.5) const;
 
+  // compound
+  void setCompound (GEdgeCompound *gec) {compound = gec;}
+  GEdgeCompound *getCompound () const {return compound;}
+
   struct {
     char Method;
     double coeffTransfinite;
diff --git a/Geo/GEdgeCompound.cpp b/Geo/GEdgeCompound.cpp
index aba627092c..f2ad5d907a 100644
--- a/Geo/GEdgeCompound.cpp
+++ b/Geo/GEdgeCompound.cpp
@@ -23,13 +23,16 @@ void GEdgeCompound::orderEdges()
 {
   std::list<GEdge*> edges ;  
   std::vector<GEdge*> _c ;  
-  for (int i=0;i<_compound.size();i++)edges.push_back(_compound[i]);
+  for (int i=0;i<_compound.size();i++){
+    _compound[i]->setCompound(this);
+    edges.push_back(_compound[i]);
+  }
   _c.push_back(*(edges.begin())); 
   edges.erase(edges.begin());
   _orientation.push_back(true);
   GVertex *first = _c[0]->getBeginVertex();
   GVertex *last = _c[0]->getEndVertex();  
-  
+
   while (first != last){
     if (edges.empty())break;
     for (std::list<GEdge*>::iterator it = edges.begin() ; it != edges.end() ; ++it){
diff --git a/Geo/GFaceCompound.cpp b/Geo/GFaceCompound.cpp
index 1a0542bb1f..6268d4a1b2 100644
--- a/Geo/GFaceCompound.cpp
+++ b/Geo/GFaceCompound.cpp
@@ -45,6 +45,29 @@ class gmshGradientBasedDiffusivity : public gmshFunction<double>
   }
 };
 
+class gmshDistanceBasedDiffusivity : public gmshFunction<double>
+{
+ private:
+  MElement *_current;
+  mutable std::map<MVertex*, SPoint3> _coordinates;
+ public:
+  gmshDistanceBasedDiffusivity (std::map<MVertex*,SPoint3> &coordinates) 
+    : _current (0),_coordinates(coordinates){}
+  void setCurrent (MElement *current){ _current = current; }
+  virtual double operator () (double x, double y, double z) const
+  {
+    double xyz[3] = {x, y, z}, uvw[3];
+    _current->xyz2uvw(xyz, uvw);
+    double value[256];
+    for (int i = 0; i < _current->getNumVertices(); i++){
+      const SPoint3 p = _coordinates[_current->getVertex(i)];
+      value[i] = p[2];
+    }
+    double val = _current->interpolate(value, uvw[0], uvw[1], uvw[2]);
+    return 1.0;//exp(15*val);
+  }
+};
+
 static void fixEdgeToValue(GEdge *ed, double value, gmshAssembler<double> &myAssembler)
 {
   myAssembler.fixVertex(ed->getBeginVertex()->mesh_vertices[0], 0, 1, value);
@@ -58,10 +81,9 @@ void GFaceCompound::parametrize() const
 {
   if (!oct){
     coordinates.clear();
-    parametrize(0, 0);
-    parametrize(1, 0);
-    // parametrize(0,1);
-    // parametrize(1,1);
+    parametrize(ITERD);
+    parametrize(ITERU);
+    parametrize(ITERV);
     computeNormals();
     buildOct();
   }
@@ -69,17 +91,12 @@ void GFaceCompound::parametrize() const
 
 void GFaceCompound::getBoundingEdges()
 {
-  if (_U0.size() && !_V1.size()){
+  if (_U0.size()){
     std::list<GEdge*> :: const_iterator it = _U0.begin();
     for ( ; it != _U0.end() ; ++it){
       l_edges.push_back(*it);
       (*it)->addFace(this);
     }
-    it = _U1.begin();
-    for ( ; it != _U1.end() ; ++it){
-      l_edges.push_back(*it);
-      (*it)->addFace(this);
-    }
     it = _V0.begin();
     for ( ; it != _V0.end() ; ++it){
       l_edges.push_back(*it);
@@ -112,6 +129,8 @@ void GFaceCompound::getBoundingEdges()
     l_edges.push_back(*itf);
     (*itf)->addFace(this);
   }
+
+  _U0 = l_edges;
 }
 
 GFaceCompound::GFaceCompound(GModel *m, int tag, std::list<GFace*> &compound,
@@ -121,9 +140,7 @@ GFaceCompound::GFaceCompound(GModel *m, int tag, std::list<GFace*> &compound,
 {
   getBoundingEdges();
   if (!_U0.size()) _type = UNITCIRCLE;
-  else if (!_V0.size()) _type = UNITCIRCLE;
-  else if (!_U1.size()) _type = CYLINDER;
-  else if (!_V1.size()) _type = BIFURCATION;
+  else if (!_V1.size()) _type = UNITCIRCLE;
   else _type = SQUARE;
 }
 
@@ -196,26 +213,29 @@ static bool orderVertices(const std::list<GEdge*> &e, std::vector<MVertex*> &l,
   return true;
 }
 
-void GFaceCompound::parametrize(bool _isU, int ITER) const
+void GFaceCompound::parametrize(iterationStep step) const
 {
-  Msg::Info("Parametrizing Surface %d coordinate %d (ITER %d)", tag(), _isU, ITER); 
+  Msg::Info("Parametrizing Surface %d coordinate (ITER %d)", tag(), step); 
   
 #ifdef HAVE_GMM
   gmshLinearSystemGmm<double> lsys;
-  lsys.setPrec(1.e-10);
+  lsys.setPrec(1.e-15);
   if(Msg::GetVerbosity() == 99) lsys.setNoisy(2);
 #else
   gmshLinearSystemFull<double> lsys;
 #endif
   gmshAssembler<double> myAssembler(&lsys);
-  gmshGradientBasedDiffusivity diffusivity(coordinates);
-  if (ITER > 0) diffusivity.setComponent(_isU);
-  
+
+  gmshDistanceBasedDiffusivity diffusivity(coordinates);
+  gmshFunction<double> ONE(1.0);
+
   if (_type == UNITCIRCLE){
     // maps the boundary onto a circle
     std::vector<MVertex*> ordered;
+    std::vector<MVertex*> ordered1;
     std::vector<double> coords;  
-    bool success = orderVertices(l_edges, ordered, coords);
+    std::vector<double> coords1;  
+    bool success = orderVertices(_U0, ordered, coords);
     if (!success){
       Msg::Error("Could not order vertices on boundary");
       return;
@@ -223,25 +243,24 @@ void GFaceCompound::parametrize(bool _isU, int ITER) const
     for (unsigned int i = 0; i < ordered.size(); i++){
       MVertex *v = ordered[i];
       const double theta = 2 * M_PI * coords[i];
-      if (_isU) myAssembler.fixVertex(v, 0, 1, cos(theta));
-      else myAssembler.fixVertex(v, 0, 1, sin(theta));
+      if (step == ITERU) myAssembler.fixVertex(v, 0, 1, cos(theta));
+      else if (step == ITERV) myAssembler.fixVertex(v, 0, 1, sin(theta));
+      else if (step == ITERD) myAssembler.fixVertex(v, 0, 1, 1.0);      
     }
-  }
-  else if (_type == CYLINDER){
-    // maps the boundary onto an annulus
-    std::vector<MVertex*> ordered;
-    std::vector<double> coords;  
-    bool success = orderVertices (_U0, ordered, coords);
-    if (!success)throw;
-    for (unsigned int i = 0; i < ordered.size(); i++){
-      MVertex *v = ordered[i];
-      const double theta = 2 * M_PI * coords[i];
-      if (_isU) myAssembler.fixVertex(v, 0, 1, cos(theta));
-      else myAssembler.fixVertex(v, 0, 1, sin(theta));
+    if (step == ITERD && _V0.size()) {
+      success = orderVertices(_V0, ordered1, coords1);
+      if (!success){
+	Msg::Error("Could not order vertices on boundary");
+	return;
+      }
+      for (unsigned int i = 0; i < ordered1.size(); i++){
+	MVertex *v = ordered1[i];
+	myAssembler.fixVertex(v, 0, 1, 0.0);      
+      }    
     }
   }
-  else{
-    if (_isU){
+  else if (_type == SQUARE){
+    if (step == ITERU){
       std::list<GEdge*> :: const_iterator it = _U0.begin();
       for ( ; it != _U0.end(); ++it){
 	fixEdgeToValue(*it, 0.0, myAssembler);
@@ -251,7 +270,7 @@ void GFaceCompound::parametrize(bool _isU, int ITER) const
 	fixEdgeToValue(*it, 1.0, myAssembler);
       }
     }
-    else{
+    else if (step == ITERV){
       std::list<GEdge*> :: const_iterator it = _V0.begin();
       for ( ; it != _V0.end(); ++it){
 	fixEdgeToValue (*it, 0.0, myAssembler);
@@ -262,7 +281,8 @@ void GFaceCompound::parametrize(bool _isU, int ITER) const
       }
     }
   }
-  
+  else throw;
+
   std::list<GFace*>::const_iterator it = _compound.begin();
   for ( ; it != _compound.end(); ++it){
     for (unsigned int i = 0; i < (*it)->triangles.size(); ++i){
@@ -276,15 +296,28 @@ void GFaceCompound::parametrize(bool _isU, int ITER) const
   Msg::Debug("Creating term %d dofs numbered %d fixed",
              myAssembler.sizeOfR(), myAssembler.sizeOfF());
 
-  gmshLaplaceTerm laplace(model(), &diffusivity, 1);
-  it = _compound.begin();
-  for ( ; it != _compound.end() ; ++it){
-    for (unsigned int i = 0; i < (*it)->triangles.size(); ++i){
-      MTriangle *t = (*it)->triangles[i];
-      diffusivity.setCurrent(t);
-      laplace.addToMatrix(myAssembler, t);
-    }
-  }    
+  if (step == ITERD){
+    gmshLaplaceTerm laplace(model(), &ONE, 1);
+    it = _compound.begin();
+    for ( ; it != _compound.end() ; ++it){
+      for (unsigned int i = 0; i < (*it)->triangles.size(); ++i){
+	MTriangle *t = (*it)->triangles[i];
+	diffusivity.setCurrent(t);
+	laplace.addToMatrix(myAssembler, t);
+      }
+    }    
+  }
+  else{
+    gmshLaplaceTerm laplace(model(), &diffusivity, 1);
+    it = _compound.begin();
+    for ( ; it != _compound.end() ; ++it){
+      for (unsigned int i = 0; i < (*it)->triangles.size(); ++i){
+	MTriangle *t = (*it)->triangles[i];
+	diffusivity.setCurrent(t);
+	laplace.addToMatrix(myAssembler, t);
+      }
+    }    
+  }
 
   Msg::Debug("Assembly done");
   lsys.systemSolve();
@@ -303,14 +336,14 @@ void GFaceCompound::parametrize(bool _isU, int ITER) const
   for (std::set<MVertex *>::iterator itv = allNodes.begin(); itv !=allNodes.end() ; ++itv){
     MVertex *v = *itv;
     double value = myAssembler.getDofValue(v,0,1);
-    std::map<MVertex*,SPoint2>::iterator itf = coordinates.find(v);
+    std::map<MVertex*,SPoint3>::iterator itf = coordinates.find(v);
     if (itf == coordinates.end()){
-      SPoint2 p (_isU ? value:0.0,_isU ? 0.0:value);
+      SPoint3 p(0,0,0);
+      p[step] = value;
       coordinates[v] = p;
     }
     else{
-      if(_isU) itf->second[0]= value;
-      else itf->second[1]= value;	  
+      itf->second[step]= value;
     }      
   }
 }
@@ -453,9 +486,9 @@ static int GFaceCompoundInEle(void *a, double*c)
   GFaceCompoundTriangle *t = (GFaceCompoundTriangle *)a;
   double M[2][2],R[2],X[2];
   const double eps = 1.e-8;
-  const SPoint2 p0 = t->p1;
-  const SPoint2 p1 = t->p2;
-  const SPoint2 p2 = t->p3;
+  const SPoint3 p0 = t->p1;
+  const SPoint3 p1 = t->p2;
+  const SPoint3 p2 = t->p3;
   M[0][0] = p1.x() - p0.x();
   M[0][1] = p2.x() - p0.x();
   M[1][0] = p1.y() - p0.y();
@@ -480,9 +513,9 @@ void GFaceCompound::getTriangle(double u, double v,
   if (!(*lt)) return;
 
   double M[2][2],X[2],R[2];
-  const SPoint2 p0 = (*lt)->p1;
-  const SPoint2 p1 = (*lt)->p2;
-  const SPoint2 p2 = (*lt)->p3;
+  const SPoint3 p0 = (*lt)->p1;
+  const SPoint3 p1 = (*lt)->p2;
+  const SPoint3 p2 = (*lt)->p3;
   M[0][0] = p1.x() - p0.x();
   M[0][1] = p2.x() - p0.x();
   M[1][0] = p1.y() - p0.y();
@@ -504,7 +537,7 @@ void GFaceCompound::buildOct() const
     for (unsigned int i = 0; i < (*it)->triangles.size(); ++i){
       MTriangle *t = (*it)->triangles[i];
       for (int j=0;j<3;j++){
-	std::map<MVertex*,SPoint2>::const_iterator itj = 
+	std::map<MVertex*,SPoint3>::const_iterator itj = 
 	  coordinates.find(t->getVertex(j));
 	bb += SPoint3(itj->second.x(),itj->second.y(),0.0);
       }
@@ -539,11 +572,11 @@ void GFaceCompound::buildOct() const
   for ( ; it != _compound.end() ; ++it){
     for (unsigned int i = 0; i < (*it)->triangles.size(); ++i){
       MTriangle *t = (*it)->triangles[i];
-      std::map<MVertex*,SPoint2>::const_iterator it0 = 
+      std::map<MVertex*,SPoint3>::const_iterator it0 = 
 	coordinates.find(t->getVertex(0));
-      std::map<MVertex*,SPoint2>::const_iterator it1 = 
+      std::map<MVertex*,SPoint3>::const_iterator it1 = 
 	coordinates.find(t->getVertex(1));
-      std::map<MVertex*,SPoint2>::const_iterator it2 = 
+      std::map<MVertex*,SPoint3>::const_iterator it2 = 
 	coordinates.find(t->getVertex(2));
       _gfct[count].p1 = it0->second;
       _gfct[count].p2 = it1->second;
diff --git a/Geo/GFaceCompound.h b/Geo/GFaceCompound.h
index 5ceae9b7d3..669fe7a202 100644
--- a/Geo/GFaceCompound.h
+++ b/Geo/GFaceCompound.h
@@ -28,7 +28,7 @@ generator of gmsh!
 
 class  GFaceCompoundTriangle {
 public:
-  SPoint2 p1, p2, p3;
+  SPoint3 p1, p2, p3;
   SPoint2 gfp1, gfp2, gfp3;
   SPoint3 v1, v2, v3;
   GFace *gf;
@@ -39,23 +39,25 @@ public:
 class Octree;
 
 class GFaceCompound : public GFace {
+ public:
+  typedef enum {ITERU=0,ITERV=1,ITERD=2} iterationStep;
  protected:
   std::list<GFace*> _compound;
   std::list<GEdge*> _U0, _U1, _V0, _V1;
   mutable GFaceCompoundTriangle *_gfct;
   mutable Octree *oct;
-  mutable std::map<MVertex*,SPoint2> coordinates;
+  mutable std::map<MVertex*,SPoint3> coordinates;
   mutable std::map<MVertex*,SVector3> _normals;
   void buildOct() const ;
-  void parametrize(bool,int) const ;
   void parametrize() const ;
+  void parametrize(iterationStep) const ;
   void computeNormals () const;
   void getBoundingEdges();
   void getTriangle(double u, double v, GFaceCompoundTriangle **lt, 
                    double &_u, double &_v) const;
   virtual double curvature(MTriangle *t) const;
 public:
-  typedef enum {UNITCIRCLE, CYLINDER, BIFURCATION, SQUARE} typeOfIsomorphism;
+  typedef enum {UNITCIRCLE, SQUARE} typeOfIsomorphism;
   GFaceCompound(GModel *m, int tag, 
 		std::list<GFace*> &compound,
 		std::list<GEdge*> &U0,
@@ -69,7 +71,11 @@ public:
   virtual GEntity::GeomType geomType() const { return CompoundSurface; }
   ModelType getNativeType() const { return GmshModel; }
   void * getNativePtr() const { return 0; }
-  SPoint2 getCoordinates(MVertex *v) const { parametrize() ; return coordinates[v]; }
+  SPoint2 getCoordinates(MVertex *v) const { 
+    parametrize() ; 
+    std::map<MVertex*,SPoint3>::iterator it = coordinates.find(v);
+    return SPoint2(it->second.x(),it->second.y()); 
+  }
   virtual bool buildRepresentationCross(){ return false; }
   virtual double curvature(const SPoint2 &param) const;
 private:
diff --git a/Geo/MElement.cpp b/Geo/MElement.cpp
index d5689b65d9..fa48c1dab8 100644
--- a/Geo/MElement.cpp
+++ b/Geo/MElement.cpp
@@ -144,9 +144,16 @@ static double _computeDeterminantAndRegularize(MElement *ele, double jac[3][3])
   
   switch (ele->getDim()) {
 
+  case 0:
+    {
+      jac[0][0] = jac[1][1] = jac[2][2] = 1.0;
+      jac[0][1] = jac[1][0] = jac[2][0] = 0.0;
+      jac[0][2] = jac[1][2] = jac[2][1] = 0.0;    
+      break;
+    } 
   case 1: 
     {
-      dJ = sqrt(SQU(jac[0][0]) + SQU(jac[0][1]) + SQU(jac[0][2]));
+      dJ = sqrt(SQU(jac[0][0]) + SQU(jac[1][0]) + SQU(jac[2][0]));
 
       // regularize matrix
       double a[3], b[3], c[3];
@@ -161,8 +168,8 @@ static double _computeDeterminantAndRegularize(MElement *ele, double jac[3][3])
         b[0] = 0.; b[1] = a[2]; b[2] = -a[1];
       }
       prodve(a, b, c);
-      jac[1][0] = b[0]; jac[1][1] = b[1]; jac[1][2] = b[2]; 
-      jac[2][0] = c[0]; jac[2][1] = c[1]; jac[2][2] = c[2]; 
+      jac[0][1] = b[0]; jac[1][1] = b[1]; jac[2][1] = b[2]; 
+      jac[0][2] = c[0]; jac[1][2] = c[1]; jac[2][2] = c[2]; 
       break;
     }
   case 2:
diff --git a/Geo/Makefile b/Geo/Makefile
index 28fc4a8679..acc4f2e589 100644
--- a/Geo/Makefile
+++ b/Geo/Makefile
@@ -236,9 +236,9 @@ GModel${OBJEXT}: GModel.cpp ../Common/GmshConfig.h ../Common/GmshMessage.h \
   MHexahedron.h MPrism.h MPyramid.h discreteRegion.h discreteFace.h \
   discreteEdge.h discreteVertex.h gmshSurface.h ../Numeric/Numeric.h \
   ../Numeric/GmshMatrix.h ../Common/Octree.h ../Common/OctreeInternals.h \
-  ../Common/SmoothData.h ../Mesh/Field.h ../Post/PView.h ../Geo/SPoint3.h \
-  ../Mesh/Generator.h ../Common/Context.h ../Geo/CGNSOptions.h \
-  ../Mesh/meshPartitionOptions.h
+  ../Common/SmoothData.h ../Mesh/Field.h ../Geo/STensor3.h \
+  ../Geo/SVector3.h ../Post/PView.h ../Geo/SPoint3.h ../Mesh/Generator.h \
+  ../Common/Context.h ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h
 GModelIO_Geo${OBJEXT}: GModelIO_Geo.cpp ../Common/GmshConfig.h \
   ../Common/GmshMessage.h GModel.h GVertex.h GEntity.h Range.h SPoint3.h \
   SBoundingBox3d.h GPoint.h SPoint2.h GEdge.h SVector3.h GFace.h \
@@ -247,8 +247,9 @@ GModelIO_Geo${OBJEXT}: GModelIO_Geo.cpp ../Common/GmshConfig.h \
   ../Common/ListUtils.h ../Common/TreeUtils.h ../Common/avl.h \
   ../Common/ListUtils.h ExtrudeParams.h ../Common/SmoothData.h \
   ../Common/OpenFile.h gmshVertex.h gmshFace.h GFaceCompound.h \
-  GEdgeCompound.h gmshEdge.h gmshRegion.h ../Mesh/Field.h ../Post/PView.h \
-  ../Geo/SPoint3.h ../Parser/Parser.h
+  GEdgeCompound.h gmshEdge.h gmshRegion.h ../Mesh/Field.h \
+  ../Geo/STensor3.h ../Geo/SVector3.h ../Post/PView.h ../Geo/SPoint3.h \
+  ../Parser/Parser.h
 GModelIO_Mesh${OBJEXT}: GModelIO_Mesh.cpp GModel.h GVertex.h GEntity.h Range.h \
   SPoint3.h SBoundingBox3d.h GPoint.h SPoint2.h GEdge.h SVector3.h \
   GFace.h GEdgeLoop.h Pair.h GRegion.h ../Common/GmshDefines.h MPoint.h \
@@ -292,8 +293,9 @@ Geo${OBJEXT}: Geo.cpp ../Common/GmshMessage.h ../Numeric/Numeric.h \
   ../Common/TreeUtils.h ../Common/avl.h ../Common/ListUtils.h \
   ExtrudeParams.h ../Common/SmoothData.h GModel.h GVertex.h GEntity.h \
   GPoint.h GEdge.h GFace.h GEdgeLoop.h GRegion.h GeoInterpolation.h \
-  ../Mesh/Field.h ../Post/PView.h ../Geo/SPoint3.h ../Common/Context.h \
-  ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h
+  ../Mesh/Field.h ../Geo/STensor3.h ../Geo/SVector3.h ../Post/PView.h \
+  ../Geo/SPoint3.h ../Common/Context.h ../Geo/CGNSOptions.h \
+  ../Mesh/meshPartitionOptions.h
 GeoStringInterface${OBJEXT}: GeoStringInterface.cpp ../Common/GmshConfig.h \
   ../Common/GmshMessage.h ../Numeric/Numeric.h ../Numeric/GmshMatrix.h \
   ../Common/StringUtils.h Geo.h ../Common/GmshDefines.h gmshSurface.h \
diff --git a/Geo/STensor3.h b/Geo/STensor3.h
new file mode 100644
index 0000000000..a4659ffed5
--- /dev/null
+++ b/Geo/STensor3.h
@@ -0,0 +1,139 @@
+// Gmsh - Copyright (C) 1997-2009 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#ifndef _STENSOR3_H_
+#define _STENSOR3_H_
+
+#include "SVector3.h"
+#include "GmshMatrix.h"
+#include "Numeric.h"
+// concrete class for symmetric positive definite 3x3 matrix
+class SMetric3 {
+ protected:
+  // lower diagonal storage
+  // 00 10 11 20 21 22 
+  double _val[6];
+ public:
+  inline int getIndex(int i, int j) const{
+    static int _index[3][3] = {{0,1,3},{1,2,4},{3,4,5}};
+    return _index[i][j];
+  }
+  void getMat (gmshMatrix<double> & mat) const{
+    for (int i=0;i<3;i++)
+      for (int j=0;j<3;j++)
+	mat(i,j) = _val[getIndex(i,j)];			     
+  }
+  void setMat (const gmshMatrix<double> & mat){
+    for (int i=0;i<3;i++)
+      for (int j=0;j<3;j++)
+	_val[getIndex(i,j)] = mat(i,j);			     
+  }
+  // default constructor, identity 
+  SMetric3(const double v = 1.0){
+    _val[0] = _val[2] = _val[5] = v;
+    _val[1] = _val[3] = _val[4] = 0.0;
+  }
+  SMetric3(const double l1,
+	   const double l2,
+	   const double l3,
+	   const SVector3 &t1,
+	   const SVector3 &t2,
+	   const SVector3 &t3){
+    SVector3 t1b = t1;
+    SVector3 t2b = t2;
+    SVector3 t3b = t3;
+    t1b[0] *= l1; t2b[0] *= l1; t3b[0] *= l1;
+    t1b[1] *= l2; t2b[1] *= l2; t3b[1] *= l2;
+    t1b[2] *= l3; t2b[2] *= l3; t3b[2] *= l3;
+    _val[0] = dot (t1b,t1);
+    _val[1] = dot (t2b,t1);
+    _val[2] = dot (t2b,t2);
+    _val[3] = dot (t3b,t1);
+    _val[4] = dot (t3b,t2);    
+    _val[5] = dot (t3b,t3);
+  }
+  inline double &operator()(int i, int j){ 
+    return _val[getIndex(i,j)];
+  }
+  inline double operator()(int i, int j) const{ 
+    return _val[getIndex(i,j)];
+  }  
+  SMetric3 invert () const {
+    gmshMatrix<double> m(3,3);
+    getMat(m);
+    m.invertInPlace();
+    SMetric3 ithis;
+    ithis.setMat(m);
+    return ithis;
+  }
+  SMetric3 operator + (const SMetric3 &other) const {
+    SMetric3 res(*this);
+    for (int i=0;i<6;i++)res._val[i]+=other._val[i];
+    return res;
+  }
+  SMetric3& operator += (const SMetric3 &other)  {
+    for (int i=0;i<6;i++)_val[i]+=other._val[i];
+    return *this;
+  }
+  SMetric3& operator *= (const double &other) {
+    for (int i=0;i<6;i++)_val[i]*=other;
+    return *this;
+  }
+  SMetric3& operator *= (const SMetric3 &other) {
+    gmshMatrix<double> m1(3,3),m2(3,3),m3(3,3);
+    getMat(m1);
+    other.getMat(m2);
+    m1.mult(m2,m3);
+    setMat(m3);
+    return *this;
+  }
+  void eig (gmshMatrix<double> &V, gmshVector<double> &S) {
+    gmshMatrix<double> me(3,3),right(3,3);
+    gmshVector<double> im(3);
+    getMat(me);
+    me.eig(V,S,im,right);
+  }
+};
+
+// scalar product with respect to the metric
+inline double dot(const SVector3 &a, const SMetric3 &m, const SVector3 &b)
+{ return 
+    b.x() * ( m(0,0) * a.x() + m(0,1) * a.y() + m(0,2) * a.z() ) + 
+    b.y() * ( m(1,0) * a.x() + m(1,1) * a.y() + m(1,2) * a.z() ) + 
+    b.z() * ( m(2,0) * a.x() + m(2,1) * a.y() + m(2,2) * a.z() ) ;}
+
+// compute the largest inscribed ellipsoid...
+inline SMetric3 intersection (const SMetric3 &m1, const SMetric3 &m2)
+{
+  SMetric3 im1 = m1.invert();
+  gmshMatrix<double> V(3,3);
+  gmshVector<double> S(3);
+  im1 *= m2;
+  im1.eig(V,S);
+  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));
+  
+  return SMetric3(l0,l1,l2,v0,v1,v2);  
+}
+
+// (1-t) * m1 + t * m2
+inline SMetric3 interpolation (const SMetric3 &m1, 
+			       const SMetric3 &m2, 
+			       const double t)
+{
+  SMetric3 im1 = m1.invert();
+  SMetric3 im2 = m2.invert();
+  im1 *= (1.-t);
+  im2 *= t;
+  im1 += im2;
+  return im1.invert();
+}
+
+
+#endif
diff --git a/Mesh/BackgroundMesh.cpp b/Mesh/BackgroundMesh.cpp
index 6c05841e43..606605cc32 100644
--- a/Mesh/BackgroundMesh.cpp
+++ b/Mesh/BackgroundMesh.cpp
@@ -135,7 +135,8 @@ static double LC_MVertex_PNTS(GEntity *ge, double U, double V)
 }
 
 // This is the only function that is used by the meshers
-double BGM_MeshSize(GEntity *ge, double U, double V, double X, double Y, double Z)
+double BGM_MeshSize(GEntity *ge, double U, double V, 
+		    double X, double Y, double Z)
 {
   // default lc (mesh size == size of the model)
   double l1 = CTX::instance()->lc;
@@ -172,6 +173,54 @@ double BGM_MeshSize(GEntity *ge, double U, double V, double X, double Y, double
   return lc * CTX::instance()->mesh.lcFactor;
 }
 
+
+// anisotropic version
+SMetric3 BGM_MeshMetric(GEntity *ge, 
+			double U, double V, 
+			double X, double Y, double Z)
+{
+  // default lc (mesh size == size of the model)
+  SMetric3 l1(CTX::instance()->lc);
+
+  // lc from points
+  SMetric3 l2(MAX_LC);
+  if(CTX::instance()->mesh.lcFromPoints && ge->dim() < 2) 
+    l2 = SMetric3(LC_MVertex_PNTS(ge, U, V));
+  
+  // lc from curvature
+  SMetric3 l3 (MAX_LC);
+  if(CTX::instance()->mesh.lcFromCurvature && ge->dim() < 3)
+    l3 = SMetric3(LC_MVertex_CURV(ge, U, V));
+
+  // lc from fields
+  SMetric3 l4 (MAX_LC);
+  FieldManager *fields = GModel::current()->getFields();
+  if(fields->background_field > 0){
+    Field *f = fields->get(fields->background_field);
+    if(f){
+      //      if (!f->isotropic())
+      //	(*f)(X, Y, Z, ge,l4);
+      //      else
+	l4 = SMetric3((*f)(X, Y, Z, ge));
+    }
+  }
+  
+  // take the minimum, then constrain by lcMin and lcMax
+  //  double lc = std::min(std::min(std::min(l1, l2), l3), l4);
+
+  //  lc = std::max(lc, CTX::instance()->mesh.lcMin);
+  //  lc = std::min(lc, CTX::instance()->mesh.lcMax);
+
+  //  if(lc <= 0.){
+  ///    Msg::Error("Wrong characteristic length lc = %g (lcmin = %g, lcmax = %g)",
+  //	       lc, CTX::instance()->mesh.lcMin, CTX::instance()->mesh.lcMax);
+  //    lc = l1;
+  //  }
+
+//  return lc * CTX::instance()->mesh.lcFactor;
+}
+
+
 bool Extend1dMeshIn2dSurfaces()
 {
   return CTX::instance()->mesh.lcExtendFromBoundary ? true : false;
diff --git a/Mesh/Field.cpp b/Mesh/Field.cpp
index ffcbf5f828..40abe3b406 100644
--- a/Mesh/Field.cpp
+++ b/Mesh/Field.cpp
@@ -477,15 +477,16 @@ class BoxField : public Field
 
 class ThresholdField : public Field
 {
+ protected :
   int iField;
   double dmin, dmax, lcmin, lcmax;
   bool sigmoid, stopAtDistMax;
  public:
-  const char *getName()
+  virtual const char *getName()
   {
     return "Threshold";
   }
-  std::string getDescription()
+  virtual std::string getDescription()
   {
     return "F = LCMin if Field[IField] <= DistMin,\n"
       "F = LCMax if Field[IField] >= DistMax,\n"
@@ -538,6 +539,80 @@ class ThresholdField : public Field
   }
 };
 
+class BoundaryLayerField : public ThresholdField {
+public:
+  BoundaryLayerField() 
+  {  }
+  virtual bool isotropic () const {return false;}
+  virtual const char *getName()
+  {
+    return "BoundaryLayer";
+  }
+  virtual std::string getDescription()
+  {
+    return "F = LCMin if Field[IField] <= DistMin,\n"
+      "F = LCMax if Field[IField] >= DistMax,\n"
+      "F = interpolation between LcMin and LcMax if DistMin < Field[IField] < DistMax";
+  }
+  virtual void operator() (double x, double y, double z, SMetric3 &metr, GEntity *ge=0)
+  {
+    Field *field = GModel::current()->getFields()->get(iField);
+    if(!field) {
+      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;
+    }
+    double dist = (*field) (x, y, z);
+
+    double r = (dist - dmin) / (dmax - dmin);
+    r = std::max(std::min(r, 1.), 0.);
+    double lc;
+    if(stopAtDistMax && r >= 1.){
+      lc = MAX_LC;
+    }
+    else if(sigmoid){
+      double s = exp(12. * r - 6.) / (1. + exp(12. * r - 6.));
+      lc = lcmin * (1. - s) + lcmax * s;
+    }
+    else{ // linear
+      lc = lcmin * (1 - r) + lcmax * r;
+    }
+    
+    double delta = std::min(CTX::instance()->lc / 1e4, 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(gx,gy,gz);
+    g.normalize();
+    SVector3 t1,t2;
+
+    if (fabs(gx) < fabs(gy) && fabs(gx) < fabs(gz))
+      t1 = SVector3(1,0,0);
+    else if (fabs(gy) < fabs(gx) && fabs(gy) < fabs(gz))
+      t1 = SVector3(0,1,0);
+    else
+      t1 = SVector3(0,0,1);
+    
+    t2 = crossprod(g,t1);
+    t2.normalize();
+    t1 = crossprod(t2,g);
+    
+    metr  = SMetric3(1./(lc*lc), 
+		     1/(lcmax*lcmax),
+		     1/(lcmax*lcmax),
+		     g,t1,t2);
+  }
+};
+
 class GradientField : public Field
 {
   int iField, kind;
@@ -600,6 +675,7 @@ class GradientField : public Field
   }
 };
 
+
 class CurvatureField : public Field
 {
   int iField;
@@ -1236,6 +1312,7 @@ class AttractorField : public Field
     return sqrt(dist[0]);
   }
 };
+
 #endif
 
 template<class F> class FieldFactoryT : public FieldFactory {
@@ -1252,6 +1329,7 @@ FieldManager::FieldManager()
 {
   map_type_name["Structured"] = new FieldFactoryT<StructuredField>();
   map_type_name["Threshold"] = new FieldFactoryT<ThresholdField>();
+  map_type_name["BoundaryLayer"] = new FieldFactoryT<BoundaryLayerField>();
   map_type_name["Box"] = new FieldFactoryT<BoxField>();
   map_type_name["LonLat"] = new FieldFactoryT<LonLatField>();
 #if !defined(HAVE_NO_POST)
diff --git a/Mesh/Field.h b/Mesh/Field.h
index a35e770137..d4ca54e45b 100644
--- a/Mesh/Field.h
+++ b/Mesh/Field.h
@@ -10,6 +10,7 @@
 #include <map>
 #include <list>
 #include "GmshConfig.h"
+#include "STensor3.h"
 
 #if !defined(HAVE_NO_POST)
 #include "PView.h"
@@ -62,7 +63,10 @@ class Field {
  public:
   int id;
   std::map<std::string, FieldOption *> options;
-  virtual double operator() (double x, double y, double z, GEntity *ge=0) = 0;
+  virtual double   operator() (double x, double y, double z, GEntity *ge=0) = 0;
+  // start of the anisotropic field implementation
+  virtual void operator() (double x, double y, double z, SMetric3 &, GEntity *ge=0){throw;}
+  virtual bool isotropic () const {return true;}
   virtual ~Field() {}
   bool update_needed;
   Field();
diff --git a/Mesh/HighOrder.cpp b/Mesh/HighOrder.cpp
index 954b6b0c4f..06e100b7e1 100644
--- a/Mesh/HighOrder.cpp
+++ b/Mesh/HighOrder.cpp
@@ -288,10 +288,10 @@ static void getEdgeVertices(GEdge *ge, MElement *ele, std::vector<MVertex*> &ve,
           GPoint pc = ge->point(US[j + 1]);
           v = new MEdgeVertex(pc.x(), pc.y(), pc.z(), ge, US[j + 1]);
             
-          if (displ2D){
+          if (displ2D || displ3D){
             SPoint3 pc2 = edge.interpolate(t);          
-            displ2D->add(v, SVector3(pc2.x(), pc2.y(), pc2.z()));
-            displ3D->add(v, SVector3(pc2.x(), pc2.y(), pc2.z()));
+            if (displ2D)displ2D->add(v, SVector3(pc2.x(), pc2.y(), pc2.z()));
+            if (displ3D)displ3D->add(v, SVector3(pc2.x(), pc2.y(), pc2.z()));
           }
         }
         temp.push_back(v);
@@ -309,9 +309,10 @@ static void getEdgeVertices(GEdge *ge, MElement *ele, std::vector<MVertex*> &ve,
 }
 
 static void getEdgeVertices(GFace *gf, MElement *ele, std::vector<MVertex*> &ve,
-                            edgeContainer &edgeVertices, bool linear,
-                            int nPts = 1, gmshHighOrderSmoother *displ2D = 0,
-                            gmshHighOrderSmoother *displ3D = 0)
+			    edgeContainer &edgeVertices, bool linear,
+			    int nPts = 1, 
+			    gmshHighOrderSmoother *displ2D = 0,
+			    gmshHighOrderSmoother *displ3D = 0)
 {
   if(gf->geomType() == GEntity::DiscreteSurface ||
      gf->geomType() == GEntity::BoundaryLayerSurface)
@@ -349,9 +350,10 @@ static void getEdgeVertices(GFace *gf, MElement *ele, std::vector<MVertex*> &ve,
         else{
           GPoint pc = gf->point(US[j + 1], VS[j + 1]);
 	  v = new MFaceVertex(pc.x(), pc.y(), pc.z(), gf, US[j + 1], VS[j + 1]);
-	  if (displ3D){
+	  if (displ2D || displ3D){
 	    SPoint3 pc2 = edge.interpolate(t);          
-	    displ3D->add(v, SVector3(pc2.x(), pc2.y(), pc2.z()));
+	    if (displ3D) displ3D->add(v, SVector3(pc2.x(), pc2.y(), pc2.z()));
+	    if (displ2D) displ2D->add(v, SVector3(pc2.x(), pc2.y(), pc2.z()));
 	  }
         }
         temp.push_back(v);
@@ -477,9 +479,10 @@ static void getFaceVertices(GFace *gf, MElement *incomplete, MElement *ele,
 	    else{
 	      v = new MVertex(X, Y, Z, gf);
 	    }
-	    if(displ3D){
+	    if(displ3D || displ2D){
 	      SPoint3 pc2 = face.interpolate(t1, t2);
-	      displ3D->add(v, SVector3(pc2.x(), pc2.y(), pc2.z()));
+	      if(displ3D)displ3D->add(v, SVector3(pc2.x(), pc2.y(), pc2.z()));
+	      if(displ2D)displ2D->add(v, SVector3(pc2.x(), pc2.y(), pc2.z()));
 	    }	    
           }
           // should be expensive -> induces a new search each time
@@ -698,6 +701,47 @@ static void setHighOrder(GEdge *ge, edgeContainer &edgeVertices, bool linear,
   ge->deleteVertexArrays();
 }
 
+MTriangle* setHighOrder(MTriangle *t,
+			GFace *gf, 
+			edgeContainer &edgeVertices, 
+			faceContainer &faceVertices, 
+			bool linear, 
+			bool incomplete,
+			int nPts, 
+			gmshHighOrderSmoother *displ2D,
+			gmshHighOrderSmoother *displ3D){
+
+  std::vector<MVertex*> ve, vf;
+  getEdgeVertices(gf, t, ve, edgeVertices, linear, nPts, displ2D, displ3D);
+  if(nPts == 1){
+    return new MTriangle6(t->getVertex(0), t->getVertex(1), t->getVertex(2),
+			  ve[0], ve[1], ve[2]);
+  }
+  else{
+    if(incomplete){
+      return new MTriangleN(t->getVertex(0), t->getVertex(1), t->getVertex(2),
+			    ve, nPts + 1);
+    }
+    else{
+      //      MTriangleN incpl(t->getVertex(0), t->getVertex(1), t->getVertex(2),
+      //      ve, nPts + 1);
+      if (displ2D && gf->geomType() == GEntity::Plane){
+	MTriangle incpl(t->getVertex(0), t->getVertex(1), t->getVertex(2));
+	getFaceVertices(gf, &incpl, t, vf, faceVertices, linear, nPts, displ2D, displ3D);
+      }
+      else{
+            MTriangleN incpl(t->getVertex(0), t->getVertex(1), t->getVertex(2),
+			     ve, nPts + 1);
+	    getFaceVertices(gf, &incpl, t, vf, faceVertices, linear, nPts, displ2D, displ3D);
+      }
+      ve.insert(ve.end(), vf.begin(), vf.end());
+      return new MTriangleN(t->getVertex(0), t->getVertex(1), t->getVertex(2),
+			    ve, nPts + 1);
+    }
+  }  
+}
+
+
 static void setHighOrder(GFace *gf, edgeContainer &edgeVertices, 
                          faceContainer &faceVertices, bool linear, bool incomplete,
                          int nPts = 1, gmshHighOrderSmoother *displ2D = 0,
@@ -706,29 +750,9 @@ static void setHighOrder(GFace *gf, edgeContainer &edgeVertices,
   std::vector<MTriangle*> triangles2;
   for(unsigned int i = 0; i < gf->triangles.size(); i++){
     MTriangle *t = gf->triangles[i];
-    std::vector<MVertex*> ve, vf;
-    getEdgeVertices(gf, t, ve, edgeVertices, linear, nPts, displ2D, displ3D);
-    if(nPts == 1){
-      triangles2.push_back
-        (new MTriangle6(t->getVertex(0), t->getVertex(1), t->getVertex(2),
-                        ve[0], ve[1], ve[2]));
-    }
-    else{
-      if(incomplete){
-        triangles2.push_back
-          (new MTriangleN(t->getVertex(0), t->getVertex(1), t->getVertex(2),
-                          ve, nPts + 1));
-      }
-      else{
-        MTriangleN incpl(t->getVertex(0), t->getVertex(1), t->getVertex(2),
-                         ve, nPts + 1);
-        getFaceVertices(gf, &incpl, t, vf, faceVertices, linear, nPts, displ2D, displ3D);
-        ve.insert(ve.end(), vf.begin(), vf.end());
-        triangles2.push_back
-          (new MTriangleN(t->getVertex(0), t->getVertex(1), t->getVertex(2),
-                          ve, nPts + 1));
-      }
-    }
+    MTriangle *tNew = setHighOrder(t,gf,edgeVertices,faceVertices, linear, incomplete,
+				   nPts,displ2D,displ3D);
+    triangles2.push_back(tNew);
     delete t;
   }
   gf->triangles = triangles2;
@@ -932,29 +956,63 @@ void SetOrder1(GModel *m)
     removeHighOrderVertices(*it);
 }
 
-static void checkHighOrderTriangles(GModel *m, std::vector<MElement*> &bad, double &minJGlob)
+static void checkHighOrderTriangles(const char* cc, GModel *m, std::vector<MElement*> &bad, double &minJGlob)
 {
   bad.clear();
   minJGlob = 1.0;
+  double minGGlob = 1.0;
+  double avg = 0.0;
+  int count = 0, nbfair=0;
   for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){
     for(unsigned int i = 0; i < (*it)->triangles.size(); i++){
       MTriangle *t = (*it)->triangles[i];
-      double disto = t->distoShapeMeasure();
+      double disto_ = t->distoShapeMeasure();
+      double gamma_ = t->gammaShapeMeasure();
+      double disto = disto_;
+      minJGlob = std::min(minJGlob, disto);
+      minGGlob = std::min(minGGlob, gamma_);
+      avg += disto; count++;
+      if (disto < 0) bad.push_back(t);
+      else if (disto < 0.2) nbfair++;
+    }
+  }
+  if (minJGlob > 0) Msg::Info("%s : Worst Triangle Smoothness %g Gamma %g NbFair = %d", cc,minJGlob, minGGlob,nbfair );
+  else Msg::Warning("%s : Worst Triangle Smoothness %g (%d negative jacobians) Worst Gamma %g Avg Smoothness %g",cc, minJGlob, bad.size(),minGGlob,avg/(count?count:1));
+}
+
+
+static void checkHighOrderTetrahedron(const char* cc,GModel *m, std::vector<MElement*> &bad, double &minJGlob)
+{
+  bad.clear();
+  minJGlob = 1.0;
+  double minGGlob = 1.0;
+  double avg = 0.0;
+  int count = 0, nbfair=0;
+  for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it){
+    for(unsigned int i = 0; i < (*it)->tetrahedra.size(); i++){
+      MTetrahedron *t = (*it)->tetrahedra[i];
+      double disto_ = t->distoShapeMeasure();
+      double gamma_ = t->gammaShapeMeasure();
+      double disto = disto_;
       minJGlob = std::min(minJGlob, disto);
+      minGGlob = std::min(minGGlob, gamma_);
+      avg += disto; count++;
       if (disto < 0) bad.push_back(t);
+      else if (disto < 0.2) nbfair++;
     }
   }
-  if (minJGlob > 0) Msg::Info("Worst Element Smoothness %g", minJGlob);
-  else Msg::Warning("Worst Element Smoothness %g", minJGlob);
+  if (minJGlob > 0) Msg::Info("%s : Worst Tetrahedron Smoothness %g Gamma %g NbFair = %d", cc, minJGlob, minGGlob,nbfair );
+  else Msg::Warning("%s : Worst Tetrahedron Smoothness %g (%d negative jacobians) Worst Gamma %g Avg Smoothness %g", cc, minJGlob, bad.size(),minGGlob,avg/(count?count:1));
 }
 
+
 extern double mesh_functional_distorsion(MTriangle *t, double u, double v);
 
 static void printJacobians(GModel *m, const char *nm)
 {
-  return;
+  //  return;
 
-  const int n = 25;
+  const int n = 15;
   double D[n][n], X[n][n], Y[n][n], Z[n][n];
 
   FILE *f = fopen(nm,"w");
@@ -1050,14 +1108,11 @@ void SetOrderN(GModel *m, int order, bool linear, bool incomplete)
   // we do that model face by model face
   std::vector<MElement*> bad;
   double worst;
+  checkHighOrderTriangles("Before optimization ", m, bad, worst);
   if (displ2D){
-    checkHighOrderTriangles(m, bad, worst);
     for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it)
-      if ((*it)->geomType() == GEntity::Plane) {
-	gmshSwapHighOrderTriangles(*it);
-	if (displ2D) displ2D->smooth(*it);
-      }
-    // will have to smooth in the planar coordinates, using the metric
+      displ2D->optimize(*it,edgeVertices,faceVertices);
+    checkHighOrderTriangles("After optimization ", m, bad, worst);
   }
 
   for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it)
@@ -1065,9 +1120,11 @@ void SetOrderN(GModel *m, int order, bool linear, bool incomplete)
                  displ2D, displ3D);
 
   // smooth the 3D regions
+  checkHighOrderTetrahedron("Before optimization", m, bad, worst);
   if (displ3D){
     for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it)
       displ3D->smooth(*it);
+    checkHighOrderTetrahedron("After optimization", m, bad, worst);
   }
 
   if(displ2D){    
@@ -1075,8 +1132,7 @@ void SetOrderN(GModel *m, int order, bool linear, bool incomplete)
     delete displ3D;
   }
 
-  printJacobians(m, "detjIni.pos");
-  checkHighOrderTriangles(m, bad, worst);
+  printJacobians(m, "smoothness.pos");
 
   double t2 = Cpu();
   Msg::Info("Meshing order %d complete (%g s)", order, t2 - t1);
diff --git a/Mesh/HighOrder.h b/Mesh/HighOrder.h
index fbdaf071e9..ca6e7d2a3a 100644
--- a/Mesh/HighOrder.h
+++ b/Mesh/HighOrder.h
@@ -19,7 +19,20 @@ typedef std::map<std::pair<MVertex*, MVertex*>, std::vector<MVertex*> > edgeCont
 // are the high order representation of the face
 typedef std::map<MFace, std::vector<MVertex*>, Less_Face> faceContainer;
 
+// the high order smoother class
+class gmshHighOrderSmoother;
+
 void SetOrder1(GModel *m);
 void SetOrderN(GModel *m, int order, bool linear=true, bool incomplete=false);
 
+
+MTriangle* setHighOrder(MTriangle *t,
+			GFace *gf, 
+			edgeContainer &edgeVertices, 
+			faceContainer &faceVertices, 
+			bool linear, 
+			bool incomplete,
+			int nPts = 1, 
+			gmshHighOrderSmoother *displ2D = 0,
+			gmshHighOrderSmoother *displ3D = 0);
 #endif
diff --git a/Mesh/Makefile b/Mesh/Makefile
index fb4a23be92..6c56f66267 100644
--- a/Mesh/Makefile
+++ b/Mesh/Makefile
@@ -100,23 +100,24 @@ Generator${OBJEXT}: Generator.cpp ../Common/GmshConfig.h ../Common/GmshMessage.h
 Field${OBJEXT}: Field.cpp ../Common/GmshConfig.h \
   ../contrib/ANN/include/ANN/ANN.h ../Common/Context.h \
   ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h Field.h \
-  ../Post/PView.h ../Geo/SPoint3.h ../Geo/GeoInterpolation.h ../Geo/Geo.h \
-  ../Common/GmshDefines.h ../Geo/gmshSurface.h ../Geo/Pair.h \
+  ../Geo/STensor3.h ../Geo/SVector3.h ../Geo/SPoint3.h \
+  ../Numeric/GmshMatrix.h ../Common/GmshMessage.h ../Numeric/Numeric.h \
+  ../Numeric/GmshMatrix.h ../Post/PView.h ../Geo/GeoInterpolation.h \
+  ../Geo/Geo.h ../Common/GmshDefines.h ../Geo/gmshSurface.h ../Geo/Pair.h \
   ../Geo/Range.h ../Geo/SPoint2.h ../Geo/SPoint3.h ../Geo/SVector3.h \
-  ../Geo/SPoint3.h ../Geo/SBoundingBox3d.h ../Geo/SPoint3.h \
-  ../Numeric/Numeric.h ../Numeric/GmshMatrix.h ../Common/GmshMessage.h \
-  ../Common/ListUtils.h ../Common/TreeUtils.h ../Common/avl.h \
-  ../Common/ListUtils.h ../Geo/SPoint2.h ../Geo/ExtrudeParams.h \
-  ../Common/SmoothData.h ../Geo/GModel.h ../Geo/GVertex.h \
-  ../Geo/GEntity.h ../Geo/Range.h ../Geo/SPoint3.h \
-  ../Geo/SBoundingBox3d.h ../Geo/GPoint.h ../Geo/SPoint2.h ../Geo/GEdge.h \
-  ../Geo/GEntity.h ../Geo/GVertex.h ../Geo/SVector3.h ../Geo/SPoint3.h \
-  ../Geo/SPoint2.h ../Geo/GFace.h ../Geo/GEntity.h ../Geo/GPoint.h \
-  ../Geo/GEdgeLoop.h ../Geo/GEdge.h ../Geo/SPoint2.h ../Geo/SVector3.h \
-  ../Geo/Pair.h ../Geo/GRegion.h ../Geo/GEntity.h ../Geo/SPoint3.h \
-  ../Geo/SBoundingBox3d.h ../Post/OctreePost.h ../Common/Octree.h \
-  ../Common/OctreeInternals.h ../Post/PViewDataList.h ../Post/PViewData.h \
-  ../Geo/MVertex.h ../Geo/SPoint2.h ../Geo/SPoint3.h
+  ../Geo/SBoundingBox3d.h ../Geo/SPoint3.h ../Common/ListUtils.h \
+  ../Common/TreeUtils.h ../Common/avl.h ../Common/ListUtils.h \
+  ../Geo/SPoint2.h ../Geo/ExtrudeParams.h ../Common/SmoothData.h \
+  ../Geo/GModel.h ../Geo/GVertex.h ../Geo/GEntity.h ../Geo/Range.h \
+  ../Geo/SPoint3.h ../Geo/SBoundingBox3d.h ../Geo/GPoint.h \
+  ../Geo/SPoint2.h ../Geo/GEdge.h ../Geo/GEntity.h ../Geo/GVertex.h \
+  ../Geo/SVector3.h ../Geo/SPoint3.h ../Geo/SPoint2.h ../Geo/GFace.h \
+  ../Geo/GEntity.h ../Geo/GPoint.h ../Geo/GEdgeLoop.h ../Geo/GEdge.h \
+  ../Geo/SPoint2.h ../Geo/SVector3.h ../Geo/Pair.h ../Geo/GRegion.h \
+  ../Geo/GEntity.h ../Geo/SPoint3.h ../Geo/SBoundingBox3d.h \
+  ../Post/OctreePost.h ../Common/Octree.h ../Common/OctreeInternals.h \
+  ../Post/PViewDataList.h ../Post/PViewData.h ../Geo/MVertex.h \
+  ../Geo/SPoint2.h ../Geo/SPoint3.h
 gmshSmoothHighOrder${OBJEXT}: gmshSmoothHighOrder.cpp ../Geo/MLine.h \
   ../Geo/MElement.h ../Common/GmshDefines.h ../Geo/MVertex.h \
   ../Geo/SPoint2.h ../Geo/SPoint3.h ../Geo/MEdge.h ../Geo/MVertex.h \
@@ -189,15 +190,16 @@ meshGFace${OBJEXT}: meshGFace.cpp meshGFace.h meshGFaceBDS.h \
   ../Geo/SPoint3.h ../Geo/SBoundingBox3d.h ../Geo/SPoint3.h \
   ../Geo/GPoint.h ../Geo/SPoint2.h ../Geo/GEdge.h ../Geo/GEntity.h \
   ../Geo/GVertex.h ../Geo/SVector3.h ../Geo/SPoint3.h ../Geo/SPoint2.h \
-  ../Geo/GFace.h ../Geo/GEntity.h ../Geo/GPoint.h ../Geo/GEdgeLoop.h \
-  ../Geo/GEdge.h ../Geo/SPoint2.h ../Geo/SVector3.h ../Geo/Pair.h \
-  ../Geo/GModel.h ../Geo/GVertex.h ../Geo/GEdge.h ../Geo/GFace.h \
-  ../Geo/GRegion.h ../Geo/GEntity.h ../Geo/SPoint3.h \
-  ../Geo/SBoundingBox3d.h ../Geo/MLine.h ../Geo/MElement.h \
-  ../Geo/MQuadrangle.h ../Geo/MElement.h ../Common/Context.h \
-  ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h \
+  ../Geo/GEdgeCompound.h ../Geo/GFace.h ../Geo/GEntity.h ../Geo/GPoint.h \
+  ../Geo/GEdgeLoop.h ../Geo/GEdge.h ../Geo/SPoint2.h ../Geo/SVector3.h \
+  ../Geo/Pair.h ../Geo/GEdge.h ../Geo/GModel.h ../Geo/GVertex.h \
+  ../Geo/GEdge.h ../Geo/GFace.h ../Geo/GRegion.h ../Geo/GEntity.h \
+  ../Geo/SPoint3.h ../Geo/SBoundingBox3d.h ../Geo/MLine.h \
+  ../Geo/MElement.h ../Geo/MQuadrangle.h ../Geo/MElement.h \
+  ../Common/Context.h ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h \
   ../Numeric/Numeric.h ../Numeric/GmshMatrix.h BDS.h qualityMeasures.h \
-  Field.h ../Post/PView.h ../Common/OS.h HighOrder.h
+  Field.h ../Geo/STensor3.h ../Geo/SVector3.h ../Post/PView.h \
+  ../Common/OS.h HighOrder.h
 meshGFaceTransfinite${OBJEXT}: meshGFaceTransfinite.cpp meshGFace.h \
   ../Geo/GVertex.h ../Geo/GEntity.h ../Geo/Range.h ../Geo/SPoint3.h \
   ../Geo/SBoundingBox3d.h ../Geo/SPoint3.h ../Geo/GPoint.h \
@@ -249,7 +251,8 @@ meshGFaceBDS${OBJEXT}: meshGFaceBDS.cpp ../Common/GmshMessage.h \
   ../Geo/GModel.h ../Geo/GVertex.h ../Geo/GEdge.h ../Geo/GFace.h \
   ../Geo/GRegion.h ../Geo/GEntity.h ../Geo/SPoint3.h \
   ../Geo/SBoundingBox3d.h ../Numeric/Numeric.h ../Numeric/GmshMatrix.h \
-  BDS.h qualityMeasures.h Field.h ../Post/PView.h ../Common/OS.h
+  BDS.h qualityMeasures.h Field.h ../Geo/STensor3.h ../Geo/SVector3.h \
+  ../Post/PView.h ../Common/OS.h
 meshGFaceDelaunayInsertion${OBJEXT}: meshGFaceDelaunayInsertion.cpp \
   ../Common/GmshMessage.h ../Numeric/GmshPredicates.h BackgroundMesh.h \
   meshGFaceDelaunayInsertion.h ../Geo/MTriangle.h ../Geo/MElement.h \
@@ -264,7 +267,7 @@ meshGFaceDelaunayInsertion${OBJEXT}: meshGFaceDelaunayInsertion.cpp \
   ../Geo/GVertex.h ../Geo/GEntity.h ../Geo/GPoint.h ../Geo/SPoint2.h \
   ../Geo/SVector3.h ../Geo/SPoint3.h ../Geo/SPoint2.h ../Geo/SPoint2.h \
   ../Geo/SVector3.h ../Geo/Pair.h ../Numeric/Numeric.h \
-  ../Numeric/GmshMatrix.h
+  ../Numeric/GmshMatrix.h ../Geo/STensor3.h ../Geo/SVector3.h
 meshGFaceOptimize${OBJEXT}: meshGFaceOptimize.cpp meshGFaceOptimize.h \
   ../Geo/MElement.h ../Common/GmshDefines.h ../Geo/MVertex.h \
   ../Geo/SPoint2.h ../Geo/SPoint3.h ../Geo/MEdge.h ../Geo/MVertex.h \
@@ -417,7 +420,8 @@ BackgroundMesh${OBJEXT}: BackgroundMesh.cpp ../Common/GmshMessage.h \
   ../Geo/GPoint.h ../Geo/GEdgeLoop.h ../Geo/GEdge.h ../Geo/SPoint2.h \
   ../Geo/SVector3.h ../Geo/Pair.h ../Geo/GModel.h ../Geo/GVertex.h \
   ../Geo/GEdge.h ../Geo/GFace.h ../Geo/GRegion.h ../Geo/GEntity.h \
-  ../Geo/SPoint3.h ../Geo/SBoundingBox3d.h Field.h ../Post/PView.h
+  ../Geo/SPoint3.h ../Geo/SBoundingBox3d.h Field.h ../Geo/STensor3.h \
+  ../Geo/SVector3.h ../Post/PView.h
 qualityMeasures${OBJEXT}: qualityMeasures.cpp qualityMeasures.h BDS.h \
   ../Common/GmshMessage.h ../Geo/MVertex.h ../Geo/SPoint2.h \
   ../Geo/SPoint3.h ../Geo/MTriangle.h ../Geo/MElement.h \
@@ -467,16 +471,16 @@ HighOrder${OBJEXT}: HighOrder.cpp HighOrder.h ../Geo/GModel.h ../Geo/GVertex.h \
   ../Geo/SBoundingBox3d.h ../Geo/MFace.h ../Geo/MVertex.h \
   ../Geo/SPoint2.h ../Geo/SPoint3.h ../Geo/MEdge.h ../Geo/MVertex.h \
   ../Geo/SVector3.h ../Geo/SVector3.h gmshSmoothHighOrder.h \
+  ../Numeric/GmshMatrix.h ../Common/GmshConfig.h ../Common/GmshMessage.h \
   ../Geo/MLine.h ../Geo/MElement.h ../Common/GmshDefines.h \
-  ../Geo/MVertex.h ../Geo/MEdge.h ../Geo/MFace.h ../Common/GmshMessage.h \
-  ../Numeric/FunctionSpace.h ../Numeric/GmshMatrix.h \
-  ../Common/GmshConfig.h ../Numeric/Gauss.h ../Geo/MTriangle.h \
-  ../Geo/MElement.h ../Geo/MQuadrangle.h ../Geo/MElement.h \
-  ../Geo/MTetrahedron.h ../Geo/MElement.h ../Geo/MHexahedron.h \
-  ../Geo/MElement.h ../Geo/MPrism.h ../Geo/MElement.h ../Geo/MPyramid.h \
-  ../Geo/MElement.h ../Common/OS.h ../Numeric/Numeric.h \
-  ../Numeric/GmshMatrix.h ../Common/Context.h ../Geo/CGNSOptions.h \
-  ../Mesh/meshPartitionOptions.h
+  ../Geo/MVertex.h ../Geo/MEdge.h ../Geo/MFace.h \
+  ../Numeric/FunctionSpace.h ../Numeric/GmshMatrix.h ../Numeric/Gauss.h \
+  ../Geo/MTriangle.h ../Geo/MElement.h ../Geo/MQuadrangle.h \
+  ../Geo/MElement.h ../Geo/MTetrahedron.h ../Geo/MElement.h \
+  ../Geo/MHexahedron.h ../Geo/MElement.h ../Geo/MPrism.h \
+  ../Geo/MElement.h ../Geo/MPyramid.h ../Geo/MElement.h ../Common/OS.h \
+  ../Numeric/Numeric.h ../Numeric/GmshMatrix.h ../Common/Context.h \
+  ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h
 meshPartition${OBJEXT}: meshPartition.cpp ../Common/GmshConfig.h ../Geo/GModel.h \
   ../Geo/GVertex.h ../Geo/GEntity.h ../Geo/Range.h ../Geo/SPoint3.h \
   ../Geo/SBoundingBox3d.h ../Geo/SPoint3.h ../Geo/GPoint.h \
diff --git a/Mesh/gmshSmoothHighOrder.cpp b/Mesh/gmshSmoothHighOrder.cpp
index 1967a77eac..4ec085e0db 100644
--- a/Mesh/gmshSmoothHighOrder.cpp
+++ b/Mesh/gmshSmoothHighOrder.cpp
@@ -28,6 +28,248 @@
 
 #define SQU(a)      ((a)*(a))
 
+int optimalLocationPN_ (GFace *gf, const MEdge &me, MTriangle *t1, MTriangle *t2,gmshHighOrderSmoother *s);
+static int _gmshSwapHighOrderTriangles(GFace *gf);
+static int _gmshSwapHighOrderTriangles(GFace *gf,edgeContainer&,faceContainer&, gmshHighOrderSmoother *s);
+static int _gmshFindOptimalLocationsP2(GFace *gf, gmshHighOrderSmoother *s);
+static int _gmshFindOptimalLocationsPN(GFace *gf,gmshHighOrderSmoother *s);
+
+static double shapeMeasure (MElement *e) {
+  const double d1 = e->distoShapeMeasure();
+  const double d2 = e->gammaShapeMeasure();
+  return d1;
+}
+
+void gmshHighOrderSmoother::moveTo (MVertex *v,  const std::map<MVertex*,SVector3> &m) const
+{
+  std::map<MVertex*,SVector3>::const_iterator it = m.find(v);
+  if (it != m.end()){
+    v->x() = it->second.x();
+    v->y() = it->second.y();
+    v->z() = it->second.z();
+  }
+} 
+
+void gmshHighOrderSmoother::moveToStraightSidedLocation (MVertex *v) const
+{
+  moveTo ( v, _straightSidedLocation);
+}
+
+void gmshHighOrderSmoother::moveToTargetLocation (MVertex *v) const
+{
+  moveTo ( v, _targetLocation);
+}
+
+void gmshHighOrderSmoother::moveToStraightSidedLocation (MElement *e) const
+{
+  for (int i=0;i<e->getNumVertices();i++)
+    moveToStraightSidedLocation(e->getVertex(i));
+}
+
+void gmshHighOrderSmoother::moveToTargetLocation (MElement *e) const
+{
+  for (int i=0;i<e->getNumVertices();i++)
+    moveToTargetLocation(e->getVertex(i));
+}
+
+void gmshHighOrderSmoother::updateTargetLocation (MVertex*v, const SPoint3 &p3, const SPoint2 &p2)
+{
+  v->x() = p3.x();
+  v->y() = p3.y();
+  v->z() = p3.z();
+  v->setParameter(0,p2.x());
+  v->setParameter(1,p2.y());
+  _targetLocation[v] = SVector3(p3.x(),p3.y(),p3.z());
+}
+
+
+struct p2data{
+  GFace *gf;
+  MTriangle *t1,*t2;
+  MVertex *n12;
+  gmshMatrix<double> *m1,*m2;
+  gmshHighOrderSmoother *s;
+  p2data (GFace *_gf,MTriangle *_t1,MTriangle *_t2,MVertex *_n12,gmshHighOrderSmoother *_s)
+    : gf(_gf),t1(_t1),t2(_t2),n12(_n12),s(_s){
+    gmshElasticityTerm el(0,1.e3,.3333);
+    s->moveToStraightSidedLocation(t1);
+    s->moveToStraightSidedLocation(t2);
+    m1 = new  gmshMatrix<double>(3*t1->getNumVertices(),
+				 3*t1->getNumVertices());
+    m2 = new  gmshMatrix<double>(3*t2->getNumVertices(),
+				 3*t2->getNumVertices()); 
+    el.elementMatrix(t1,*m1);
+    el.elementMatrix(t2,*m2);
+    s->moveToTargetLocation(t1);
+    s->moveToTargetLocation(t2);
+  }
+  ~p2data(){
+    delete m1;
+    delete m2;
+  }
+};
+
+struct pNdata{
+  GFace *gf;
+  MTriangle *t1,*t2;
+  const std::vector<MVertex*> &n;
+  gmshMatrix<double> *m1,*m2;
+  gmshHighOrderSmoother *s;
+  pNdata (GFace *_gf,MTriangle *_t1,MTriangle *_t2,const std::vector<MVertex*> &_n,gmshHighOrderSmoother *_s)
+    : gf(_gf),t1(_t1),t2(_t2),n(_n),s(_s)
+  {
+    gmshElasticityTerm el(0,1.e3,.3333);
+    s->moveToStraightSidedLocation(t1);
+    s->moveToStraightSidedLocation(t2);
+    m1 = new  gmshMatrix<double>(3*t1->getNumVertices(),
+				 3*t1->getNumVertices());
+    m2 = new  gmshMatrix<double>(3*t2->getNumVertices(),
+				 3*t2->getNumVertices()); 
+    el.elementMatrix(t1,*m1);
+    el.elementMatrix(t2,*m2);
+    s->moveToTargetLocation(t1);
+    s->moveToTargetLocation(t2);
+  }
+  ~pNdata(){
+    delete m1;
+    delete m2;
+  }
+};
+
+
+static double _DeformationEnergy (MElement *e, 
+				  gmshMatrix<double> *K,
+				  gmshHighOrderSmoother *s){
+
+  int N = e->getNumVertices();
+  gmshVector<double> Kdx(N*3),dx(N*3);
+
+  dx.scale(0.0);
+  Kdx.scale(0.0);
+  int C = 0;
+  for (int i=0;i<N;i++){
+    SVector3 disp = s->getDisplacement(e->getVertex(i));
+    SVector3 str  = s->getSSL(e->getVertex(i));
+    dx(i)      = disp.x();
+    dx(i+N)    = disp.y();
+    dx(i+2*N)  = disp.z();
+    if (0 && (fabs(disp.x())>1.e-12 ||fabs(disp.y())>1.e-12))
+            printf("%6d (%12.5E %12.5E %12.5E -- %12.5E %12.5E %12.5E -- %12.5E %12.5E %12.5E)\n",
+	       e->getVertex(i)->getNum(),
+	       disp.x(),disp.y(),disp.z(),
+	       str.x(),str.y(),str.z(),e->getVertex(i)->x(),e->getVertex(i)->y(),
+	       e->getVertex(i)->z());
+  }  
+
+  K->mult(dx,Kdx);
+  const double E = Kdx * dx;
+  return E;
+}
+
+static double _DeformationEnergy (p2data *p){
+  
+  //  printf("DEFEN \n");
+
+  return 
+    _DeformationEnergy (p->t1,p->m1,p->s) + 
+    _DeformationEnergy (p->t2,p->m2,p->s);
+}
+
+static double _DeformationEnergy (pNdata *p){
+
+  return _DeformationEnergy (p->t1,p->m1,p->s) + 
+    _DeformationEnergy (p->t2,p->m2,p->s);
+}
+
+
+static double _function_p2tB (gmshVector<double> &x, void *data){
+  p2data *p = (p2data*)data;
+  GPoint gp12 = p->gf->point(SPoint2(x(0),x(1)));
+  double xx = p->n12->x();
+  double yy = p->n12->y();
+  double zz = p->n12->z();
+  p->n12->x() = gp12.x();
+  p->n12->y() = gp12.y();
+  p->n12->z() = gp12.z();
+
+  double E = _DeformationEnergy(p);
+  //  printf("%12.5E %12.5E -- %12.5E %12.5E => %12.5E %12.5E E %12.5E\n",x(0),x(1),xx,yy,gp12.x(),gp12.y(),E);
+
+  p->n12->x() = xx;
+  p->n12->y() = yy;
+  p->n12->z() = zz;  
+  return E;
+}
+
+
+
+static double _function_p2t (gmshVector<double> &x, void *data){
+  p2data *p = (p2data*)data;
+  GPoint gp12 = p->gf->point(SPoint2(x(0),x(1)));
+  //  printf("%g %g = %g %g\n",x(0),x(1),gp12.x(),gp12.y());
+  double xx = p->n12->x();
+  double yy = p->n12->y();
+  double zz = p->n12->z();
+  p->n12->x() = gp12.x();
+  p->n12->y() = gp12.y();
+  p->n12->z() = gp12.z();
+  double q = std::min(shapeMeasure(p->t1),shapeMeasure(p->t2));      
+  p->n12->x() = xx;
+  p->n12->y() = yy;
+  p->n12->z() = zz;  
+  return -q;
+}
+
+static double _function_pNt (gmshVector<double> &x, void *data){
+  pNdata *p = (pNdata*)data;
+  static double xx[256];
+  static double yy[256];
+  static double zz[256];
+  for (int i=0;i<p->n.size();i++){
+    GPoint gp12 = p->gf->point(SPoint2(x(2*i),x(2*i+1)));
+    //  printf("%g %g = %g %g\n",x(0),x(1),gp12.x(),gp12.y());
+    xx[i] = p->n[i]->x();
+    yy[i] = p->n[i]->y();
+    zz[i] = p->n[i]->z();
+    p->n[i]->x() = gp12.x();
+    p->n[i]->y() = gp12.y();
+    p->n[i]->z() = gp12.z();
+  }
+  double q = std::min(shapeMeasure(p->t1),shapeMeasure(p->t2));      
+  for (int i=0;i<p->n.size();i++){
+    p->n[i]->x() = xx[i];
+    p->n[i]->y() = yy[i];
+    p->n[i]->z() = zz[i];
+  }  
+  return -q;
+}
+
+static double _function_pNtB (gmshVector<double> &x, void *data){
+  pNdata *p = (pNdata*)data;
+  static double xx[256];
+  static double yy[256];
+  static double zz[256];
+  for (int i=0;i<p->n.size();i++){
+    GPoint gp12 = p->gf->point(SPoint2(x(2*i),x(2*i+1)));
+    //  printf("%g %g = %g %g\n",x(0),x(1),gp12.x(),gp12.y());
+    xx[i] = p->n[i]->x();
+    yy[i] = p->n[i]->y();
+    zz[i] = p->n[i]->z();
+    p->n[i]->x() = gp12.x();
+    p->n[i]->y() = gp12.y();
+    p->n[i]->z() = gp12.z();
+  }
+  double E = _DeformationEnergy(p);
+  for (int i=0;i<p->n.size();i++){
+    p->n[i]->x() = xx[i];
+    p->n[i]->y() = yy[i];
+    p->n[i]->z() = zz[i];
+  }  
+  //  printf("E(%g,%g) = %g\n",x(0),x(1),E);
+  return E;
+}
+
+
 void getDistordedElements(const std::vector<MElement*>  & v, 
                           const double & threshold,
                           std::vector<MElement*>  & d,
@@ -77,7 +319,7 @@ void addOneLayer(const std::vector<MElement*>  & v,
   }
 }
 
-void gmshHighOrderSmoother::smooth(GFace *gf)
+void gmshHighOrderSmoother::smooth(GFace *gf, bool metric)
 {
   std::vector<MElement*> v;
 
@@ -85,7 +327,8 @@ void gmshHighOrderSmoother::smooth(GFace *gf)
   v.insert(v.begin(), gf->quadrangles.begin(),gf->quadrangles.end());
   Msg::Info("Smoothing high order mesh : model face %d (%d elements)", gf->tag(),
 	    v.size());
-  smooth(v);
+  if (metric)smooth_metric(v,gf);
+  else smooth(v);
 }
 
 void gmshHighOrderSmoother::smooth(GRegion *gr)
@@ -99,19 +342,106 @@ void gmshHighOrderSmoother::smooth(GRegion *gr)
   smooth(v);
 }
 
-void gmshHighOrderSmoother::smooth ( std::vector<MElement*>  & all)
+// Use elastic solver to move the nodes
+
+// compute the stiffness matrix of an element
+// that correspond to the deformation of a
+// straight sided element to a curvilinear one
+
+
+
+
+
+void gmshHighOrderSmoother::optimize(GFace * gf, 
+				     edgeContainer &edgeVertices,
+				     faceContainer &faceVertices){
+
+  //  if (gf->geomType() != GEntity::Plane)return;
+
+  while (1) {
+    // relocate the vertices using elliptic smoother
+    //    smooth(gf);
+    //    for (int i=0;i<20;i++){
+    //    _gmshFindOptimalLocationsPN(gf,this);
+      //      _gmshFindOptimalLocationsPN(gf,this);
+      //    }
+    // then try to swap for better configurations  
+
+    smooth(gf,true);
+    int nbSwap = _gmshSwapHighOrderTriangles(gf,edgeVertices,faceVertices,this);
+    //smooth(gf,true);
+    //    smooth(gf,true);
+    //    smooth(gf,true);
+    //    smooth(gf,true);
+    break;
+
+    //    printf("%d %g\n",nbSwap,_MIDDLE);
+    // if the smoothing procedure has been able to
+    // move the nodes to their right location, break !
+    //    break;
+    if (_MIDDLE == 1.0) break;
+    //    if (!nbSwap){
+    //      printf("Cannot move thet nodes to their optimal location, splits have to be added\n");
+    //      break;
+    //    }
+  }
+}
+
+
+void gmshHighOrderSmoother::computeMetricVector (GFace *gf, 
+						 MElement *e, 
+						 gmshMatrix<double> &J,
+						 gmshMatrix<double> &JT,
+						 gmshVector<double> &D){
+  int nbNodes = e->getNumVertices();
+  for (int j = 0; j < nbNodes; j++){
+    SPoint2 param;
+    reparamMeshVertexOnFace(e->getVertex(j), gf, param);  
+    Pair<SVector3,SVector3> der = gf->firstDer(param);    
+    int XJ = j;
+    int YJ = j+nbNodes;
+    int ZJ = j+2*nbNodes;
+    int UJ = j;
+    int VJ = j+nbNodes;
+    J(XJ,UJ) = der.first().x();
+    J(YJ,UJ) = der.first().y();
+    J(ZJ,UJ) = der.first().z();
+    J(XJ,VJ) = der.second().x();
+    J(YJ,VJ) = der.second().y();
+    J(ZJ,VJ) = der.second().z();
+    
+    JT(UJ,XJ) = der.first().x();
+    JT(UJ,YJ) = der.first().y();
+    JT(UJ,ZJ) = der.first().z();
+    JT(VJ,XJ) = der.second().x();
+    JT(VJ,YJ) = der.second().y();
+    JT(VJ,ZJ) = der.second().z();
+
+    GPoint gp = gf->point(param);    
+    SVector3 ss = getSSL(e->getVertex(j));
+    D(XJ) = gp.x() - ss.x();
+    D(YJ) = gp.y() - ss.y();
+    D(ZJ) = gp.z() - ss.z();
+  } 
+}
+
+
+void gmshHighOrderSmoother::smooth_metric ( std::vector<MElement*>  & all, GFace *gf)
 {
   gmshLinearSystemGmm<double> *lsys = new gmshLinearSystemGmm<double>;
+  //  lsys->setNoisy(1);
+  lsys->setGmres(1);
+  lsys->setPrec(1.e-7);
   gmshAssembler<double> myAssembler(lsys);
   gmshElasticityTerm El(0, 1.0, .333, getTag());
   
-  std::vector<MElement*> v, layer;
+  std::vector<MElement*> layer,v;
 
   double minD;
 
-  getDistordedElements ( all, 0.5, v,minD);
+  getDistordedElements ( all, .6, v,minD);
 
-  //  printf("%d elements / %d distorted  min Disto = %g\n",all.size(),v.size(), minD);
+  Msg::Debug("%d elements / %d distorted  min Disto = %g",all.size(),v.size(), minD);
 
   if (!v.size()) return;
 
@@ -122,24 +452,212 @@ void gmshHighOrderSmoother::smooth ( std::vector<MElement*>  & all)
   }
 
   // 3 -> .4
-  printf("%d elements after adding %d layers\n", (int)v.size(), nbLayers);
+  Msg::Debug("%d elements after adding %d layers", (int)v.size(), nbLayers);
 
   addOneLayer(all, v, layer);
 
   //  printf("%d elements in the next layer\n",layer.size());
 
+  std::set<MVertex*>::iterator it;
+  std::map<MVertex*,SVector3>::iterator its;
+  std::map<MVertex*,SVector3>::iterator itpresent;
+  std::map<MVertex*,SVector3>::iterator ittarget;
+  std::set<MVertex*> verticesToMove;
+
 
   for (unsigned int i = 0; i < layer.size(); i++){
     for (int j = 0; j < layer[i]->getNumVertices(); j++){
       MVertex *vert = layer[i]->getVertex(j);
       myAssembler.fixVertex(vert, 0, getTag(), 0);
       myAssembler.fixVertex(vert, 1, getTag(), 0);
-      myAssembler.fixVertex(vert, 2, getTag(), 0);
     }
   }
+
+  //  printf("%d vertices \n", _displ.size());
+
+  for (unsigned int i = 0; i < v.size(); i++){
+    for (int j = 0; j < v[i]->getNumVertices(); j++){
+      MVertex *vert = v[i]->getVertex(j);
+      //      printf("%d %d %d v\n",i,j,v[i]->getNumVertices());
+      its = _straightSidedLocation.find(vert);
+      if (its == _straightSidedLocation.end()){
+	_straightSidedLocation[vert] = 
+	  SVector3(vert->x(),vert->y(),vert->z());     
+	_targetLocation[vert] = 
+	  SVector3(vert->x(),vert->y(),vert->z());     
+      }
+      else{
+	vert->x() = its->second.x();
+	vert->y() = its->second.y();
+	vert->z() = its->second.z();
+	if (vert->onWhat()->dim() < _dim){
+	  myAssembler.fixVertex ( vert , 0 , getTag() , 0);
+	  myAssembler.fixVertex ( vert , 1 , getTag() , 0);
+	}
+      }
+    }
+  }
+  
+  // number the other DOFs
+  for (unsigned int i = 0; i < v.size(); i++){
+    for (int j = 0; j < v[i]->getNumVertices(); j++){
+      MVertex *vert = v[i]->getVertex(j);
+      myAssembler.numberVertex(vert, 0, getTag());
+      myAssembler.numberVertex(vert, 1, getTag());
+      verticesToMove.insert(vert);
+    } 
+  }
+
+  
+  double dx0 = smooth_metric_ ( v, gf, myAssembler, verticesToMove,El);
+  double dx = dx0;
+  //  printf(" dx0 = %12.5E\n",dx0);
+  int iter = 0;
+  while(1){
+    double dx2 = smooth_metric_ ( v,gf, myAssembler, verticesToMove,El);
+    //    printf(" dx2  = %12.5E\n",dx2);
+    if (fabs(dx2-dx) < 1.e-4 * dx0)break;
+    if (iter++ > 10)break;
+    dx = dx2;
+  }
+
+  for (it = verticesToMove.begin()  ; it != verticesToMove.end() ; ++it){
+    SPoint2 param;
+    reparamMeshVertexOnFace(*it, gf, param);  
+    GPoint gp = gf->point(param);    
+    if ((*it)->onWhat()->dim() == 2){
+      (*it)->x() = gp.x();
+      (*it)->y() = gp.y();
+      (*it)->z() = gp.z();
+      _targetLocation[*it] = SVector3(gp.x(),gp.y(),gp.z());
+    }
+    else{
+      SVector3 p =  _targetLocation[(*it)];
+      (*it)->x() = p.x();
+      (*it)->y() = p.y();
+      (*it)->z() = p.z();      
+    }
+  }
+  delete lsys;
+}
+
+
+double gmshHighOrderSmoother::smooth_metric_ ( std::vector<MElement*>  & v, 
+					       GFace *gf, 
+					       gmshAssembler<double> &myAssembler,
+					       std::set<MVertex*> &verticesToMove,
+					       gmshElasticityTerm &El)
+{
+  //  Msg::Info("%d vertices FIXED %d NUMBERED", myAssembler.sizeOfF()
+  //	    , myAssembler.sizeOfR());
+
+  std::set<MVertex*>::iterator it;
+
+  if (myAssembler.sizeOfR()){
+
+    for (int i=0;i<v.size();i++){
+      MElement *e = v[i];            
+      int nbNodes = e->getNumVertices();
+      const int n2 = 2*nbNodes;
+      const int n3 = 3*nbNodes;
+
+      gmshMatrix<double> K33 (n3,n3);
+      gmshMatrix<double> K22 (n2,n2);
+      gmshMatrix<double> J32 (n3,n2);
+      gmshMatrix<double> J23 (n2,n3);
+      gmshVector<double> D3  (n3);
+      gmshVector<double> R2  (n2);
+      gmshMatrix<double> J23K33 (n2,n3);
+      K33.set_all(0.0);
+      El.elementMatrix(e,K33);
+      computeMetricVector (gf,e,J32,J23,D3);
+      J23K33.gemm(J23,K33,1,0);      
+      K22.gemm(J23K33,J32,1,0);      
+      J23K33.mult(D3,R2);
+      for (int j = 0; j < n2; j++){
+	MVertex *vR;
+	int iCompR, iFieldR;
+	El.getLocalDofR(e, j, &vR, &iCompR, &iFieldR);
+	myAssembler.assemble(vR, iCompR, iFieldR,-R2(j));
+	for (int k = 0; k < n2; k++){
+	  MVertex *vC;
+	  int iCompC, iFieldC;
+	  El.getLocalDofC(e, k, &vC, &iCompC, &iFieldC);
+	  myAssembler.assemble(vR, iCompR, iFieldR, 
+			 vC, iCompC, iFieldC, 
+			 K22(j, k));
+	}
+      }
+    }
+    // solve the system
+    myAssembler.systemSolve();
+  }
+  
+
+  double dx = 0.0;
+  for (it = verticesToMove.begin()  ; it != verticesToMove.end() ; ++it){
+    if ((*it)->onWhat()->dim() == 2){
+      SPoint2 param;
+      reparamMeshVertexOnFace((*it), gf, param);  
+      SPoint2 dparam (myAssembler.getDofValue ((*it), 0 ,getTag()),
+		      myAssembler.getDofValue ((*it), 1 ,getTag()));
+      SPoint2 newp = param+dparam;
+      dx += newp.x()*newp.x()+newp.y()*newp.y();
+      (*it)->setParameter(0,newp.x());
+      (*it)->setParameter(1,newp.y());
+    }
+  }
+  
+  myAssembler.systemClear();
+  
+  return dx;
+}
+
+
+void gmshHighOrderSmoother::smooth ( std::vector<MElement*>  & all)
+{
+  gmshLinearSystemGmm<double> *lsys = new gmshLinearSystemGmm<double>;
+  lsys->setNoisy(1);
+  lsys->setGmres(1);
+  lsys->setPrec(1.e-5);
+  gmshAssembler<double> myAssembler(lsys);
+  gmshElasticityTerm El(0, 1.0, .333, getTag());
   
+  std::vector<MElement*> layer,v;
+
+  double minD;
+
+  getDistordedElements ( all, .5, v,minD);
+
+  Msg::Debug("%d elements / %d distorted  min Disto = %g\n",all.size(),v.size(), minD);
+
+  if (!v.size()) return;
+
+  const int nbLayers = 2;
+  for (int i=0;i<nbLayers;i++){
+    addOneLayer ( all, v, layer);
+    v.insert(v.end(),layer.begin(),layer.end());
+  }
+
+  // 3 -> .4
+  Msg::Debug("%d elements after adding %d layers\n", (int)v.size(), nbLayers);
+
+  addOneLayer(all, v, layer);
+
+  //  printf("%d elements in the next layer\n",layer.size());
+
+  for (unsigned int i = 0; i < layer.size(); i++){
+    for (int j = 0; j < layer[i]->getNumVertices(); j++){
+      MVertex *vert = layer[i]->getVertex(j);
+      myAssembler.fixVertex(vert, 0, getTag(), 0);
+      myAssembler.fixVertex(vert, 1, getTag(), 0);
+      myAssembler.fixVertex(vert, 2, getTag(), 0);
+    }
+  }
 
   std::map<MVertex*,SVector3>::iterator it;
+  std::map<MVertex*,SVector3>::iterator its;
+  std::map<MVertex*,SVector3>::iterator itt;
   std::map<MVertex*,SVector3> verticesToMove;
 
   //  printf("%d vertices \n", _displ.size());
@@ -148,13 +666,12 @@ void gmshHighOrderSmoother::smooth ( std::vector<MElement*>  & all)
     for (int j = 0; j < v[i]->getNumVertices(); j++){
       MVertex *vert = v[i]->getVertex(j);
       //      printf("%d %d %d v\n",i,j,v[i]->getNumVertices());
-      it = _displ.find(vert);
-      if (it != _displ.end()){
-	myAssembler.fixVertex ( vert , 0 , getTag() , vert->x()-it->second.x());
-	myAssembler.fixVertex ( vert , 1 , getTag() , vert->y()-it->second.y());
-	myAssembler.fixVertex ( vert , 2 , getTag() , vert->z()-it->second.z());
-	//	printf("%g %g vs %g %g\n", it->second.x(), it->second.y(),vert->x(),vert->y());
-	verticesToMove[vert] = it->second;
+      its = _straightSidedLocation.find(vert);
+      itt = _targetLocation.find(vert);
+      if (its != _straightSidedLocation.end() && vert->onWhat()->dim() < _dim){
+	myAssembler.fixVertex ( vert , 0 , getTag() , vert->x()-its->second.x());
+	myAssembler.fixVertex ( vert , 1 , getTag() , vert->y()-its->second.y());
+	myAssembler.fixVertex ( vert , 2 , getTag() , vert->z()-its->second.z());
       }
       // ensure we do not touch any vertex that is on the boundary
       else if (vert->onWhat()->dim() < _dim){
@@ -164,14 +681,9 @@ void gmshHighOrderSmoother::smooth ( std::vector<MElement*>  & all)
       }
     }
   }
-  
-  // move back high order nodes to their straight sided 
-  // location
-  for (it = verticesToMove.begin()  ; it != verticesToMove.end() ; ++it){
-    it->first->x() = it->second.x();
-    it->first->y() = it->second.y();
-    it->first->z() = it->second.z();
-  }
+
+  for (unsigned int i = 0; i < v.size(); i++)
+    moveToStraightSidedLocation (v[i]);    
   
   // number the other DOFs
   for (unsigned int i = 0; i < v.size(); i++){
@@ -197,24 +709,15 @@ void gmshHighOrderSmoother::smooth ( std::vector<MElement*>  & all)
     // solve the system
     lsys->systemSolve();
   }
-
-  // move the nodes that were involved in the process
-  // to their new lcation
+  
   for (it = verticesToMove.begin()  ; it != verticesToMove.end() ; ++it){
-    it->first->x() =  it->first->x() + myAssembler.getDofValue (it->first, 0 ,getTag());  
-    it->first->y() =  it->first->y() + myAssembler.getDofValue (it->first, 1 ,getTag());  
-    it->first->z() =  it->first->z() + myAssembler.getDofValue (it->first, 2 ,getTag());  
-    //    printf("%g %g\n", myAssembler.getDofValue (it->first, 0 ,getTag()), myAssembler.getDofValue (it->first, 1 ,getTag()));
+    it->first->x() += myAssembler.getDofValue (it->first, 0 ,getTag());  
+    it->first->y() += myAssembler.getDofValue (it->first, 1 ,getTag());  
+    it->first->z() += myAssembler.getDofValue (it->first, 2 ,getTag());  
   }
 
   // delete matrices and vectors
-
-  double minD2;
-  getDistordedElements ( v, 0.5, layer,minD2);
-
-  printf("Smooting efficiency %g -> %g\n",minD,minD2);
-
-
+  
   delete lsys;
 }
 
@@ -232,6 +735,120 @@ void gmshHighOrderSmoother::smooth ( std::vector<MElement*>  & all)
   n1    n14     n4
 */
 
+/*
+  n34 is the new vertex, we'd like to put it at the 
+  best place i.e. at a place where it maximizes the 
+  minimal smoothness of the neighboring elements.
+
+  One can actually maximize this value or one can use
+  a standard interpolation scheme (transfinite or elliptic)
+  to place the point.
+*/
+
+
+static void getNodesP2 (const MEdge &me, MTriangle *t1, MTriangle *t2,
+			MVertex * &n1,MVertex * &n2,MVertex * &n3,MVertex * &n4,
+			MVertex * &n12,MVertex * &n14,MVertex * &n24,MVertex * &n23,MVertex * &n13){
+
+  n1 = me.getVertex(0);
+  n2 = me.getVertex(1);    
+  
+  if (t1->getVertex(0) != n1 && t1->getVertex(0) != n2) n3 = t1->getVertex(0);
+  else if (t1->getVertex(1) != n1 && t1->getVertex(1) != n2) n3 = t1->getVertex(1);
+  else if (t1->getVertex(2) != n1 && t1->getVertex(2) != n2) n3 = t1->getVertex(2);
+  int iStart = 0;
+  for (;iStart<3;iStart++)
+    if (t1->getVertex(iStart) == n1)
+      break;
+  if (n2 == t1->getVertex((iStart+1)%3)){
+    n12 = t1->getVertex((iStart+0)%3 + 3);
+    n23 = t1->getVertex((iStart+1)%3 + 3);
+    n13 = t1->getVertex((iStart+2)%3 + 3);
+  }
+  else{
+    n13 = t1->getVertex((iStart+0)%3 + 3);
+    n23 = t1->getVertex((iStart+1)%3 + 3);
+    n12 = t1->getVertex((iStart+2)%3 + 3);
+  }
+  
+  if (t2->getVertex(0) != n1 && t2->getVertex(0) != n2) n4 = t2->getVertex(0);
+  else if (t2->getVertex(1) != n1 && t2->getVertex(1) != n2) n4 = t2->getVertex(1);
+  else if (t2->getVertex(2) != n1 && t2->getVertex(2) != n2) n4 = t2->getVertex(2);
+  iStart = 0;
+  for (;iStart<3;iStart++)
+    if (t2->getVertex(iStart) == n1)
+      break;
+  if (n2 == t2->getVertex((iStart+1)%3)){
+    n24 = t2->getVertex((iStart+1)%3 + 3);
+    n14 = t2->getVertex((iStart+2)%3 + 3);
+  }
+  else{
+    n14 = t2->getVertex((iStart+0)%3 + 3);
+    n24 = t2->getVertex((iStart+1)%3 + 3);
+  }
+}
+
+static void getNodesPN (const MEdge &me, MTriangle *t1, MTriangle *t2,
+			MVertex * &n1,MVertex * &n2,MVertex * &n3,MVertex * &n4,
+			std::vector<MVertex *> &n12,
+			std::vector<MVertex *> &n14,
+			std::vector<MVertex *> &n24,
+			std::vector<MVertex *> &n23,
+			std::vector<MVertex *> &n13){
+
+  n1 = me.getVertex(0);
+  n2 = me.getVertex(1);    
+  
+  int order = t1->getPolynomialOrder();
+
+  if (t1->getVertex(0) != n1 && t1->getVertex(0) != n2) n3 = t1->getVertex(0);
+  else if (t1->getVertex(1) != n1 && t1->getVertex(1) != n2) n3 = t1->getVertex(1);
+  else if (t1->getVertex(2) != n1 && t1->getVertex(2) != n2) n3 = t1->getVertex(2);
+  int iStart = 0;
+  for (;iStart<3;iStart++)
+    if (t1->getVertex(iStart) == n1)
+      break;
+  if (n2 == t1->getVertex((iStart+1)%3)){
+    int start12 = 3 + ((iStart+0)%3) * (order-1);
+    for (int i=0;i<order-1;i++)n12.push_back(t1->getVertex(start12+i));
+    int start23 = 3 + ((iStart+1)%3) * (order-1);
+    for (int i=0;i<order-1;i++)n23.push_back(t1->getVertex(start23+i));
+    int start13 = 3 + ((iStart+2)%3) * (order-1);
+    for (int i=0;i<order-1;i++)n13.push_back(t1->getVertex(start13+i));
+  }
+  else{
+    int start13 = 3 + ((iStart+0)%3) * (order-1);
+    for (int i=order-2;i>=0;i--)n13.push_back(t1->getVertex(start13+i));
+    int start23 = 3 + ((iStart+1)%3) * (order-1);
+    for (int i=order-2;i>=0;i--)n23.push_back(t1->getVertex(start23+i));
+    int start12 = 3 + ((iStart+2)%3) * (order-1);
+    for (int i=order-2;i>=0;i--)n12.push_back(t1->getVertex(start12+i));
+  }
+  
+  if (t2->getVertex(0) != n1 && t2->getVertex(0) != n2) n4 = t2->getVertex(0);
+  else if (t2->getVertex(1) != n1 && t2->getVertex(1) != n2) n4 = t2->getVertex(1);
+  else if (t2->getVertex(2) != n1 && t2->getVertex(2) != n2) n4 = t2->getVertex(2);
+  iStart = 0;
+  for (;iStart<3;iStart++)
+    if (t2->getVertex(iStart) == n1)
+      break;
+
+  if (n2 == t2->getVertex((iStart+1)%3)){
+    int start24 = 3 + ((iStart+1)%3) * (order-1);
+    for (int i=0;i<order-1;i++)n24.push_back(t2->getVertex(start24+i));
+    int start14 = 3 + ((iStart+2)%3) * (order-1);
+    for (int i=0;i<order-1;i++)n14.push_back(t2->getVertex(start14+i));
+  }
+  else{
+    int start14 = 3 + ((iStart+0)%3) * (order-1);
+    for (int i=order-2;i>=0;i--)n14.push_back(t1->getVertex(start14+i));
+    int start24 = 3 + ((iStart+1)%3) * (order-1);
+    for (int i=order-2;i>=0;i--)n24.push_back(t1->getVertex(start24+i));
+  }
+}
+
+
+
 static double surfaceTriangleUV(SPoint2 &v1, SPoint2 &v2, SPoint2 &v3)           
 {
   const double v12[2] = {v2.x() - v1.x(),v2.y() - v1.y()};
@@ -239,8 +856,6 @@ static double surfaceTriangleUV(SPoint2 &v1, SPoint2 &v2, SPoint2 &v3)
   return 0.5 * fabs (v12[0] * v13[1] - v12[1] * v13[0]);
 }
 
-
-
 struct swap_triangles_p2
 {
   MTriangle *t1, *t2;
@@ -251,61 +866,66 @@ struct swap_triangles_p2
   MVertex *n1, *n2, *n3, *n4;
   MVertex *n12, *n13, *n23, *n14, *n24;
   MVertex *n34;
+
+
+  double evalConfiguration (GFace *gf, SPoint2 & p) const {
+    GPoint gp34 = gf->point(p);
+    MFaceVertex _test (gp34.x(),gp34.y(),gp34.z(),gf,p.x(),p.y());        
+    std::vector<MVertex *>vv;
+    vv.push_back(n13);vv.push_back(&_test);vv.push_back(n14);
+    MTriangleN t3_test (n1,n3,n4,vv,2,t1->getNum(),t1->getPartition());
+    vv.clear();
+    vv.push_back(&_test);vv.push_back(n23);vv.push_back(n24);
+    MTriangleN t4_test (n4,n3,n2,vv,2,t2->getNum(),t2->getPartition());
+    const double q3 = shapeMeasure(&t3_test);
+    const double q4 = shapeMeasure(&t4_test);
+    return std::min(q3,q4);
+  }
+
+  MVertex *optimalLocation (GFace *gf, 
+			    SPoint2 &p3,
+			    SPoint2 &p4) const {
+    SPoint2 p34_linear = (p3+p4)*.5;
+    
+    
+    GPoint gp34 = gf->point(p34_linear);
+    MFaceVertex *_test = new MFaceVertex (gp34.x(),gp34.y(),gp34.z(),
+					  gf,p34_linear.x(),p34_linear.y());        
+    std::vector<MVertex *>vv;
+    vv.push_back(n13);vv.push_back(_test);vv.push_back(n14);
+    MTriangleN t3_test (n1,n3,n4,vv,2,t1->getNum(),t1->getPartition());
+    vv.clear();
+    vv.push_back(_test);vv.push_back(n23);vv.push_back(n24);
+    MTriangleN t4_test (n4,n3,n2,vv,2,t2->getNum(),t2->getPartition());
+    p2data data(gf,&t3_test,&t4_test,_test,0);
+    gmshVector<double> pp(2);
+    pp(0) = p34_linear.x();
+    pp(1) = p34_linear.y();
+    double opti = minimize_grad_fd (_function_p2tB, pp, &data);
+    return _test;
+  }
+
+
   swap_triangles_p2(const MEdge &me, MTriangle *_t1, MTriangle *_t2, GFace *gf)
     : t1(_t1), t2(_t2),n1(0),n2(0),n3(0),n12(0),n13(0), n23(0), n14(0), n24(0),n34(0)
   {
-    n1 = me.getVertex(0);
-    n2 = me.getVertex(1);    
-
-    if (t1->getVertex(0) != n1 && t1->getVertex(0) != n2) n3 = t1->getVertex(0);
-    else if (t1->getVertex(1) != n1 && t1->getVertex(1) != n2) n3 = t1->getVertex(1);
-    else if (t1->getVertex(2) != n1 && t1->getVertex(2) != n2) n3 = t1->getVertex(2);
-    int iStart = 0;
-    for (;iStart<3;iStart++)
-      if (t1->getVertex(iStart) == n1)
-	break;
-    if (n2 == t1->getVertex((iStart+1)%3)){
-      n12 = t1->getVertex((iStart+0)%3 + 3);
-      n23 = t1->getVertex((iStart+1)%3 + 3);
-      n13 = t1->getVertex((iStart+2)%3 + 3);
-    }
-    else{
-      n13 = t1->getVertex((iStart+0)%3 + 3);
-      n23 = t1->getVertex((iStart+1)%3 + 3);
-      n12 = t1->getVertex((iStart+2)%3 + 3);
-    }
-
-    if (t2->getVertex(0) != n1 && t2->getVertex(0) != n2) n4 = t2->getVertex(0);
-    else if (t2->getVertex(1) != n1 && t2->getVertex(1) != n2) n4 = t2->getVertex(1);
-    else if (t2->getVertex(2) != n1 && t2->getVertex(2) != n2) n4 = t2->getVertex(2);
-    iStart = 0;
-    for (;iStart<3;iStart++)
-      if (t2->getVertex(iStart) == n1)
-	break;
-    if (n2 == t2->getVertex((iStart+1)%3)){
-      n24 = t2->getVertex((iStart+1)%3 + 3);
-      n14 = t2->getVertex((iStart+2)%3 + 3);
-    }
-    else{
-      n14 = t2->getVertex((iStart+0)%3 + 3);
-      n24 = t2->getVertex((iStart+1)%3 + 3);
-    }
     
-    //printf("%p %p %p %p %p %p %p %p %p\n",n1,n2,n3,n4,n12,n23,n13,n24,n14);
+    const double qold1 = shapeMeasure(t1);
+    const double qold2 = shapeMeasure(t2);
 
-    SPoint2 p1,p2,p3,p4;
+    //printf("%p %p %p %p %p %p %p %p %p\n",n1,n2,n3,n4,n12,n23,n13,n24,n14);
+    getNodesP2 (me,t1,t2,n1,n2,n3,n4,n12,n14,n24,n23,n13);
+    SPoint2 p1,p2,p3,p4,p13,p24,p23,p14;
     reparamMeshEdgeOnFace(n1,n2,gf,p1,p2);
     reparamMeshEdgeOnFace(n3,n4,gf,p3,p4);
+    reparamMeshEdgeOnFace(n13,n24,gf,p13,p24);
+    reparamMeshEdgeOnFace(n23,n14,gf,p23,p14);
 
     s_before = surfaceTriangleUV(p1,p2,p4) + surfaceTriangleUV(p1,p2,p3);
     s_after =  surfaceTriangleUV(p3,p4,p1) + surfaceTriangleUV(p3,p4,p2);
 
-    SPoint2 p34 = (p3+p4)*.5;
-    GPoint gp34 = gf->point(p34);
-    n34 = new MFaceVertex (gp34.x(),gp34.y(),gp34.z(),gf,p34.x(),p34.y());        
+    n34 = optimalLocation (gf,p3,p4);
 
-    const double qold1 = t1->distoShapeMeasure() * t1->gammaShapeMeasure();
-    const double qold2 = t2->distoShapeMeasure() * t2->gammaShapeMeasure();
 
     std::vector<MVertex *>vv;
     vv.push_back(n13);vv.push_back(n34);vv.push_back(n14);
@@ -314,12 +934,13 @@ struct swap_triangles_p2
     vv.push_back(n34);vv.push_back(n23);vv.push_back(n24);
     t4 = new MTriangleN (n4,n3,n2,vv,2,t2->getNum(),t2->getPartition());
 
-    const double qnew1 = t3->distoShapeMeasure() * t3->gammaShapeMeasure();
-    const double qnew2 = t4->distoShapeMeasure() * t4->gammaShapeMeasure();
+    const double qnew1 = shapeMeasure(t3);
+    const double qnew2 = shapeMeasure(t4);
 
-    quality_old = std::min(qold1,qold2);
-    quality_new = std::min(qnew1,qnew2);
 
+    quality_new = std::min(qnew1,qnew2);
+    quality_old = std::min(qold1,qold2);
+    //    printf("old %g linear %g transfinite %g\n",quality_old,quality_new_linear,quality_new_transfinite);
   }
   bool operator < (const swap_triangles_p2 &other) const
   {
@@ -342,6 +963,273 @@ struct swap_triangles_p2
   
 };
 
+struct swap_triangles_pN
+{
+  MTriangle *t1, *t2;
+  MTriangle *t3, *t4;
+  double quality_old;
+  double quality_new;
+  double s_before,s_after;
+  MVertex *n1, *n2, *n3, *n4;
+  std::vector<MVertex*> n12, n13, n23, n14, n24, n34;
+  std::vector<MVertex*> n123, n124, n134, n234;
+  edgeContainer &edgeVertices;
+  faceContainer &faceVertices;
+  gmshHighOrderSmoother *s;
+
+  swap_triangles_pN(const MEdge &me, MTriangle *_t1, MTriangle *_t2, GFace *gf,
+		    edgeContainer &_edgeVertices,
+		    faceContainer &_faceVertices,
+		    gmshHighOrderSmoother *_s)
+    : t1(_t1), t2(_t2),edgeVertices(_edgeVertices),faceVertices(_faceVertices),s(_s)
+  {
+
+    const double qold1 = shapeMeasure(t1);
+    const double qold2 = shapeMeasure(t2);
+
+    getNodesPN (me,t1,t2,n1,n2,n3,n4,n12,n14,n24,n23,n13);
+    SPoint2 p1,p2,p3,p4,p13,p24,p23,p14;
+    reparamMeshEdgeOnFace(n1,n2,gf,p1,p2);
+    reparamMeshEdgeOnFace(n3,n4,gf,p3,p4);
+
+    s_before = surfaceTriangleUV(p1,p2,p4) + surfaceTriangleUV(p1,p2,p3);
+    s_after =  surfaceTriangleUV(p3,p4,p1) + surfaceTriangleUV(p3,p4,p2);
+
+    MTriangle t3lin(n3,n4,n1);
+    MTriangle t4lin(n4,n3,n2);
+
+    t3 =  setHighOrder(&t3lin,gf,edgeVertices,faceVertices,false,
+		       !t1->getNumFaceVertices(),
+		       t1->getPolynomialOrder()-1,s);
+    t4 =  setHighOrder(&t4lin,gf,edgeVertices,faceVertices,false,
+		       !t1->getNumFaceVertices(),
+		       t1->getPolynomialOrder()-1,s);
+    
+    optimalLocationPN_ (gf,me, t3, t4,s);
+      
+    const double qnew1 = shapeMeasure(t3);
+    const double qnew2 = shapeMeasure(t4);
+
+    quality_new = std::min(qnew1,qnew2);
+    quality_old = std::min(qold1,qold2);
+
+    //    if (quality_old < quality_new)
+      printf("QUALITY GOING FROM %12.5E TO %12.5E\n",quality_old,quality_new);
+
+  }
+  bool operator < (const swap_triangles_pN &other) const
+  {
+    return  other.quality_new - other.quality_old < quality_new - quality_old;
+  }
+};
+
+
+
+static int optimalLocationP2_ (GFace *gf, 
+			       const MEdge &me,
+			       MTriangle *t1, MTriangle *t2, 
+			       gmshHighOrderSmoother *s){
+
+  double qini = std::min(shapeMeasure(t1),shapeMeasure(t2));
+
+  if (qini > 0.6) return 0;
+  
+  MVertex *n1,*n2,*n3,*n4,*n12,*n14,*n24,*n23,*n13;
+  getNodesP2 (me,t1,t2,n1,n2,n3,n4,n12,n14,n24,n23,n13);
+  SPoint2 p1,p2,p3,p4,p12;
+  reparamMeshVertexOnFace(n12,gf,p12);
+  reparamMeshEdgeOnFace(n3,n4,gf,p3,p4);
+  reparamMeshEdgeOnFace(n3,n4,gf,p3,p4);
+  SPoint2 p34_linear = (p1+p2)*.5;
+  SPoint2 dirt = p4-p3;
+  SPoint2 dirn = (-dirt.y(), dirt.x());
+
+  gmshVector<double> pp(2);
+  pp(0) = p12.x();
+  pp(1) = p12.y();
+  p2data data(gf,t1,t2,n12,s);
+
+  double init = _DeformationEnergy (&data);
+  double opti = minimize_grad_fd (_function_p2tB, pp, &data);
+
+  printf("opti %g init %g\n",opti,init);
+
+  if (init-opti < 1.e-5*(init))return 0;
+
+  double u0 = gf->parBounds(0).low();
+  double u1 = gf->parBounds(0).high();
+  double v0 = gf->parBounds(1).low();
+  double v1 = gf->parBounds(1).high();
+  if (pp(0) < u0 || pp(0) > u1 || pp(1) < v0 || pp(1) > v1)return 0;   
+  
+  GPoint gp12 = gf->point(SPoint2(pp(0),pp(1)));
+  n12->x() = gp12.x();
+  n12->y() = gp12.y();
+  n12->z() = gp12.z();
+  n12->setParameter(0,pp(0));
+  n12->setParameter(1,pp(1));      
+  return 1;
+}
+
+int optimalLocationPN_ (GFace *gf, const MEdge &me, MTriangle *t1, MTriangle *t2,gmshHighOrderSmoother *s){
+  // if quality is sufficient, do nothing (this is an expensive
+  //  optimization process)
+  double qopt = std::min(shapeMeasure(t1),shapeMeasure(t2));
+  if (qopt > 0.6) return 0;
+  // get the 2 end vertex of the edge
+  MVertex *n1 = me.getVertex(0);
+  MVertex *n2 = me.getVertex(1);
+  // get all the other nodes that are on the edge
+  int N = t1->getNumVertices();
+  int NF = t1->getNumFaceVertices();
+  int NE = t1->getNumEdgeVertices();
+  std::vector<MVertex*> toOptimize;
+  for (int i=3;i<3+NE;i++){
+    MVertex *v1 = t1->getVertex(i);
+    for (int j=3;j<3+NE;j++){
+      MVertex *v2 = t2->getVertex(j);
+      if (v1 == v2 && v1 != n1 && v1 != n2){
+	toOptimize.push_back(v1);
+	break;
+      }
+    }
+  }
+  
+
+  for (int i=3+NE;i<N;i++){
+    toOptimize.push_back(t1->getVertex(i));
+    toOptimize.push_back(t2->getVertex(i));
+  }
+
+  gmshVector<double> pp(2*toOptimize.size());
+  for (int i=0;i<toOptimize.size();i++){
+    SPoint2 pt;
+    reparamMeshVertexOnFace(toOptimize[i],gf,pt);
+    pp(2*i)   = pt[0];
+    pp(2*i+1) = pt[1];
+  }
+
+  pNdata data(gf,t1,t2,toOptimize,s);
+  double init = _DeformationEnergy (&data);
+  double opti = minimize_grad_fd (_function_pNtB, pp, &data);
+  ///double opti = minimize_grad_fd (_function_pNt, pp, &data);
+  if (init-opti < 1.e-5*(init))return 0;
+  printf("Optimization has reduced the deformation energy %g -> %g\n",
+	 init,opti);
+  for (int i=0;i<toOptimize.size();i++){
+    GPoint gp12 = gf->point(SPoint2(pp(2*i),pp(2*i+1)));
+    toOptimize[i]->x() = gp12.x();
+    toOptimize[i]->y() = gp12.y();
+    toOptimize[i]->z() = gp12.z();
+    toOptimize[i]->setParameter(0,pp(2*i));
+    toOptimize[i]->setParameter(1,pp(2*i+1));
+  }
+  return 1;
+}
+
+
+static int _gmshFindOptimalLocationsP2(GFace *gf, gmshHighOrderSmoother *s)
+{
+  e2t_cont adj;
+  buildEdgeToTriangle(gf->triangles, adj);
+  int N=0;
+  for (e2t_cont::iterator it = adj.begin(); it!= adj.end(); ++it){
+    if (it->second.second)
+      N += optimalLocationP2_(gf,it->first, dynamic_cast<MTriangle*>(it->second.first),
+			      dynamic_cast<MTriangle*>(it->second.second),s);
+  }
+  return N;
+}
+
+static int _gmshFindOptimalLocationsPN(GFace *gf,gmshHighOrderSmoother *s)
+{
+  printf("coucou1\n");
+  e2t_cont adj;
+  buildEdgeToTriangle(gf->triangles, adj);
+  int N=0;
+  printf("coucou2\n");
+  
+  for (e2t_cont::iterator it = adj.begin(); it!= adj.end(); ++it){
+    if (it->second.second)
+      N += optimalLocationPN_(gf,it->first, dynamic_cast<MTriangle*>(it->second.first),
+			      dynamic_cast<MTriangle*>(it->second.second),s);
+  }
+  printf("coucou3\n");
+  return N;
+}
+
+static int _gmshSwapHighOrderTriangles(GFace *gf, 
+				       edgeContainer &edgeVertices,
+				       faceContainer &faceVertices,
+				       gmshHighOrderSmoother *s)
+{
+  e2t_cont adj;
+  buildEdgeToTriangle(gf->triangles, adj);
+
+  std::set<swap_triangles_pN> pairs;
+
+  for (e2t_cont::iterator it = adj.begin(); it!= adj.end(); ++it){
+    if (it->second.second){
+      MTriangle *t1 = dynamic_cast<MTriangle*>(it->second.first);
+      MTriangle *t2 = dynamic_cast<MTriangle*>(it->second.second);
+      const double qold1 = shapeMeasure(t1);
+      const double qold2 = shapeMeasure(t2);
+
+      //      printf("swap : %g %g\n",qold1,qold2);
+
+      if (qold1 < 0.6 || qold2 < 0.6)
+	pairs.insert(swap_triangles_pN(it->first,t1,t2,gf,
+				       edgeVertices,faceVertices,s));
+    }
+  }
+  std::set<swap_triangles_pN>::iterator itp = pairs.begin();
+
+  int nbSwap = 0;
+
+  std::set<MTriangle*> t_removed;
+  std::set<MVertex*> v_removed;
+
+  std::vector<MTriangle*> triangles2;
+  std::vector<MVertex*> mesh_vertices2;
+
+  itp = pairs.begin();
+  while(itp != pairs.end()){
+    double diff = fabs(itp->s_before - itp->s_after);
+    if ( t_removed.find(itp->t1) == t_removed.end() &&
+	 t_removed.find(itp->t2) == t_removed.end() &&
+	 itp->quality_new > itp->quality_old &&
+	 diff < 1.e-9){
+      //      itp->print();
+      t_removed.insert(itp->t1);
+      t_removed.insert(itp->t2);
+      triangles2.push_back(itp->t3);
+      triangles2.push_back(itp->t4);
+      //      if (itp->n34 != itp->n12){
+	//	v_removed.insert(itp->n12);
+	//	mesh_vertices2.push_back(itp->n34);
+      //      }
+      nbSwap++;
+    }
+    else{
+      delete itp->t3;
+      delete itp->t4;
+      //      if (itp->n34 != itp->n12) delete itp->n34;
+    }
+    ++itp;
+  }
+  
+  for (unsigned int i = 0; i < gf->triangles.size(); i++){
+    if (t_removed.find(gf->triangles[i]) == t_removed.end()){
+      triangles2.push_back(gf->triangles[i]);
+    }
+    else {
+      delete gf->triangles[i];
+    }    
+  }
+  //  printf("replacing %d by %d\n",gf->triangles.size(),triangles2.size());
+  gf->triangles = triangles2;
+  return nbSwap;
+}
 
 static int _gmshSwapHighOrderTriangles(GFace *gf)
 {
@@ -351,41 +1239,47 @@ static int _gmshSwapHighOrderTriangles(GFace *gf)
   std::set<swap_triangles_p2> pairs;
 
   for (e2t_cont::iterator it = adj.begin(); it!= adj.end(); ++it){
-    if (it->second.second)
-      pairs.insert(swap_triangles_p2(it->first,
-				     dynamic_cast<MTriangle*>(it->second.first),
-				     dynamic_cast<MTriangle*>(it->second.second),
-				     gf));
+    if (it->second.second){
+      MTriangle *t1 = dynamic_cast<MTriangle*>(it->second.first);
+      MTriangle *t2 = dynamic_cast<MTriangle*>(it->second.second);
+      const double qold1 = shapeMeasure(t1);
+      const double qold2 = shapeMeasure(t2);
+      if (qold1 < 0.6 || qold2 < 0.6)
+	pairs.insert(swap_triangles_p2(it->first,t1,t2,gf));
+    }
   }
+  std::set<swap_triangles_p2>::iterator itp = pairs.begin();
 
   int nbSwap = 0;
 
   std::set<MTriangle*> t_removed;
   std::set<MVertex*> v_removed;
-  std::set<swap_triangles_p2>::iterator itp = pairs.begin();
 
   std::vector<MTriangle*> triangles2;
   std::vector<MVertex*> mesh_vertices2;
 
+  itp = pairs.begin();
   while(itp != pairs.end()){
     double diff = fabs(itp->s_before - itp->s_after);
     if ( t_removed.find(itp->t1) == t_removed.end() &&
 	 t_removed.find(itp->t2) == t_removed.end() &&
-	 itp->quality_new - itp->quality_old > 0.01 &&
+	 itp->quality_new > itp->quality_old &&
 	 diff < 1.e-9){
+      //      itp->print();
       t_removed.insert(itp->t1);
       t_removed.insert(itp->t2);
-      v_removed.insert(itp->n12);
-      itp->print();
       triangles2.push_back(itp->t3);
       triangles2.push_back(itp->t4);
-      mesh_vertices2.push_back(itp->n34);
+      if (itp->n34 != itp->n12){
+	v_removed.insert(itp->n12);
+	mesh_vertices2.push_back(itp->n34);
+      }
       nbSwap++;
     }
     else{
       delete itp->t3;
       delete itp->t4;
-      delete itp->n34;
+      if (itp->n34 != itp->n12) delete itp->n34;
     }
     ++itp;
   }
@@ -414,11 +1308,24 @@ static int _gmshSwapHighOrderTriangles(GFace *gf)
   return nbSwap;
 }
 
-void gmshSwapHighOrderTriangles(GFace *gf){
-  while(_gmshSwapHighOrderTriangles(gf));
+void  gmshHighOrderSmoother::swap(GFace *gf, 
+				  edgeContainer &edgeVertices,
+				  faceContainer &faceVertices){
+  //  _gmshSwapHighOrderTriangles(gf);
+  _gmshSwapHighOrderTriangles(gf,edgeVertices,faceVertices,this);
+  //_gmshSwapHighOrderTriangles(gf);
+  //_gmshSwapHighOrderTriangles(gf);
   //  _gmshSwapHighOrderTriangles(gf);
 }
 
+void  gmshHighOrderSmoother::smooth_p2point(GFace *gf){
+  _gmshFindOptimalLocationsP2(gf,this);
+  //_gmshFindOptimalLocationsP2(gf);
+  //_gmshFindOptimalLocationsP2(gf);
+}
+void  gmshHighOrderSmoother::smooth_pNpoint(GFace *gf){
+  _gmshFindOptimalLocationsPN(gf,this);
+}
 
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/Mesh/gmshSmoothHighOrder.h b/Mesh/gmshSmoothHighOrder.h
index 91858db4b1..9f52604cd3 100644
--- a/Mesh/gmshSmoothHighOrder.h
+++ b/Mesh/gmshSmoothHighOrder.h
@@ -9,27 +9,76 @@
 #include <map>
 #include <vector>
 #include "SVector3.h"
+#include "GmshMatrix.h"
 
 class MVertex;
 class MElement;
 class GFace;
 class GRegion;
-
-void gmshSwapHighOrderTriangles(GFace *gf);
+template<class T> class gmshAssembler;
+class gmshElasticityTerm;
 
 class gmshHighOrderSmoother 
 {
   const int _tag;
-  std::map<MVertex*,SVector3> _displ;
+  std::map<MVertex*,SVector3> _straightSidedLocation;
+  std::map<MVertex*,SVector3> _targetLocation;
   int _dim;
-  void _clean () {_displ.clear();}
+  void _clean () {
+    _straightSidedLocation.clear();
+    _targetLocation.clear();
+  }
+  double _MIDDLE;
+  void moveTo (MVertex *v,  const std::map<MVertex*,SVector3> &) const;
 public:  
   gmshHighOrderSmoother (int dim) : _tag(111), _dim(dim) {_clean();}
-  void add ( MVertex * v, const SVector3 &d ) {_displ[v] = d;}  
+  void add ( MVertex * v, const SVector3 &d ) {
+    _straightSidedLocation[v] = d;
+    _targetLocation[v]        = SPoint3(v->x(),v->y(),v->z());
+  }  
   void smooth ( std::vector<MElement*> & );
-  void smooth ( GFace* );
+  double smooth_metric_ ( std::vector<MElement*> &, GFace *gf,
+			  gmshAssembler<double> &myAssembler,
+			  std::set<MVertex*> &verticesToMove,
+			  gmshElasticityTerm &El);
+  void smooth_metric ( std::vector<MElement*> &, GFace *gf );
+  void smooth ( GFace* , bool metric = false);
+  void smooth_p2point ( GFace* );
+  void smooth_pNpoint ( GFace* );
   void smooth ( GRegion* );
   int getTag() const {return _tag;}
+  void swap(GFace *, 
+	    edgeContainer &edgeVertices,
+	    faceContainer &faceVertices);
+  void optimize(GFace *, 
+		edgeContainer &edgeVertices,
+		faceContainer &faceVertices);
+  void computeMetricVector (GFace *gf, 
+			    MElement *e, 
+			    gmshMatrix<double> &J,
+			    gmshMatrix<double> &JT,
+			    gmshVector<double> &D);
+  void moveToStraightSidedLocation  (MVertex *) const;
+  void moveToTargetLocation  (MVertex *) const;
+  void moveToStraightSidedLocation  (MElement *) const;
+  void moveToTargetLocation (MElement *) const;  
+  void updateTargetLocation (MVertex*, const SPoint3 &, const SPoint2 &) ;
+  inline SVector3 getSSL(MVertex *v) const{
+     std::map<MVertex*,SVector3>::const_iterator it =  _straightSidedLocation.find(v);
+     if (it != _straightSidedLocation.end())return it->second;
+     else return SVector3(v->x(),v->y(),v->z());
+  }
+  inline SVector3 getDisplacement(MVertex *v) const{
+     std::map<MVertex*,SVector3>::const_iterator it  =  _straightSidedLocation.find(v);
+     std::map<MVertex*,SVector3>::const_iterator itt =  _targetLocation.find(v);
+     if (it == _straightSidedLocation.end())
+       return SVector3(0.0,0.0,0.0);
+     else{
+       return SVector3(itt->second.x() - it->second.x(),
+		       itt->second.y() - it->second.y(),
+		       itt->second.z() - it->second.z());
+     }
+  }
 };
 
 #endif
diff --git a/Mesh/meshGEdge.cpp b/Mesh/meshGEdge.cpp
index 19cdb668de..55dd9cd302 100644
--- a/Mesh/meshGEdge.cpp
+++ b/Mesh/meshGEdge.cpp
@@ -33,30 +33,6 @@ struct xi2lc {
 
 static std::vector<xi2lc> interpLc;
 
-static void smoothInterpLc(int nbSmooth)
-{
-  for(int j = 0; j < nbSmooth; j++){
-    for(int i = 0 ; i < (int)interpLc.size(); i++){               
-      xi2lc &left = (i == 0) ? interpLc[0] : interpLc[i - 1];
-      xi2lc &mid = interpLc[i];
-      xi2lc &right = (i == (int)interpLc.size() - 1) ?
-        interpLc[interpLc.size() - 1] : interpLc[i+1];
-      if(1. / mid.lc > 1.1 * 1. / left.lc) mid.lc = left.lc / 1.1;
-      if(1. / mid.lc > 1.1 * 1. / right.lc) mid.lc = right.lc / 1.1;
-    }
-  } 
-}
-
-static void printInterpLc(const char *name)
-{
-  FILE *f = fopen(name,"w");
-  for(unsigned int i = 0; i < interpLc.size(); i++){              
-    xi2lc &interp = interpLc[i];
-    fprintf(f,"%12.5E %12.5E\n", interp.xi, 1 / interp.lc);
-  }
-  fclose(f);
-}
-
 static void buildInterpLc(const std::vector<IntPoint> &lcPoints)
 {
   IntPoint p;
@@ -74,13 +50,11 @@ static double F_Lc_usingInterpLc(GEdge *ge, double t)
   double t1 = it->xi;
   double l1 = it->lc;
   it++;
-  SVector3 der = ge->firstDer(t);
-  const double d = norm(der);
-  if(it == interpLc.end()) return d * l1;
+  if(it == interpLc.end()) return l1;
   double t2 = it->xi;
   double l2 = it->lc;
   double l = l1 + ((t - t1) / (t2 - t1)) * (l2 - l1);
-  return d * l;
+  return l;
 }
 
 static double F_Lc_usingInterpLcBis(GEdge *ge, double t)
@@ -92,6 +66,9 @@ static double F_Lc_usingInterpLcBis(GEdge *ge, double t)
   double t_begin = bounds.low();
   double t_end = bounds.high();
 
+  SVector3 der = ge->firstDer(t);
+  const double d = norm(der);
+
   if(t == t_begin)
     lc_here = BGM_MeshSize(ge->getBeginVertex(), t, 0, p.x(), p.y(), p.z());
   else if(t == t_end)
@@ -99,7 +76,7 @@ static double F_Lc_usingInterpLcBis(GEdge *ge, double t)
   else
     lc_here = BGM_MeshSize(ge, t, 0, p.x(), p.y(), p.z());
 
-  return 1 / lc_here;
+  return d / lc_here;
 }
 
 static double F_Lc(GEdge *ge, double t)
@@ -305,9 +282,6 @@ void meshGEdge::operator() (GEdge *ge)
       Integration(ge, t_begin, t_end, F_Lc_usingInterpLcBis, lcPoints, 
                   CTX::instance()->mesh.lcIntegrationPrecision);
       buildInterpLc(lcPoints);
-      // printInterpLc("toto1.dat");
-      // smoothInterpLc(20);
-      // printInterpLc("toto2.dat");
       a = Integration(ge, t_begin, t_end, F_Lc_usingInterpLc, Points, 1.e-8);
     }
     else{
diff --git a/Mesh/meshGFace.cpp b/Mesh/meshGFace.cpp
index de8a3658e6..3c0c4055b6 100644
--- a/Mesh/meshGFace.cpp
+++ b/Mesh/meshGFace.cpp
@@ -13,6 +13,7 @@
 #include "BackgroundMesh.h"
 #include "GVertex.h"
 #include "GEdge.h"
+#include "GEdgeCompound.h"
 #include "GFace.h"
 #include "GModel.h"
 #include "MVertex.h"
@@ -373,6 +374,26 @@ static bool gmsh2DMeshGenerator(GFace *gf, int RECUR_ITER, bool repairSelfInters
   v_container all_vertices;
   std::map<int, MVertex*>numbered_vertices;
   std::list<GEdge*> edges = gf->edges();
+
+  // here, we will replace edges by their compounds
+  if (gf->geomType() == GEntity::CompoundSurface){
+    std::set<GEdge*> mySet;
+    std::list<GEdge*>::iterator it = edges.begin();
+    while(it != edges.end()){
+      if ((*it)->getCompound()){
+	mySet.insert((*it)->getCompound());
+	printf("compound edge %d found in %d\n",(*it)->getCompound()->tag(), (*it)->tag());
+      }
+      else 
+	mySet.insert(*it);
+      ++it;
+    }
+    printf("replacing %d edges by %d in the compound %d\n",edges.size(),mySet.size(),gf->tag());
+    edges.clear();
+    edges.insert(edges.begin(), mySet.begin(), mySet.end());
+  }
+  
+
   std::list<GEdge*> emb_edges = gf->embeddedEdges();
   std::list<GVertex*> emb_vertx = gf->embeddedVertices();
   std::list<GEdge*>::iterator it = edges.begin();
diff --git a/Mesh/meshGFaceBDS.cpp b/Mesh/meshGFaceBDS.cpp
index d48963a058..ccac813653 100644
--- a/Mesh/meshGFaceBDS.cpp
+++ b/Mesh/meshGFaceBDS.cpp
@@ -32,8 +32,11 @@ double computeEdgeLinearLength(BDS_Point *p1, BDS_Point *p2)
   return l;
 }
 
-inline double computeEdgeLinearLength(BDS_Point *p1, BDS_Point *p2, GFace *f,
-                                      double SCALINGU, double SCALINGV)
+inline double computeEdgeLinearLength(BDS_Point *p1, 
+				      BDS_Point *p2, 
+				      GFace *f,
+                                      double SCALINGU, 
+				      double SCALINGV)
 {
   GPoint GP = f->point(SPoint2(0.5 * (p1->u + p2->u) * SCALINGU,
                                0.5 * (p1->v + p2->v) * SCALINGV));
@@ -49,8 +52,11 @@ inline double computeEdgeLinearLength(BDS_Point *p1, BDS_Point *p2, GFace *f,
   return l1 + l2;
 }
 
-inline double computeEdgeMiddleCoord(BDS_Point *p1, BDS_Point *p2, GFace *f,
-                                     double SCALINGU, double SCALINGV)
+inline double computeEdgeMiddleCoord(BDS_Point *p1, 
+				     BDS_Point *p2, 
+				     GFace *f,
+                                     double SCALINGU, 
+				     double SCALINGV)
 {
   if (f->geomType() == GEntity::Plane)
     return 0.5;
@@ -73,8 +79,10 @@ inline double computeEdgeMiddleCoord(BDS_Point *p1, BDS_Point *p2, GFace *f,
     return 0.25 * (3 * l2 - l1) / l2;
 }
 
-inline double computeEdgeLinearLength(BDS_Edge *e, GFace *f, 
-                                      double SCALINGU, double SCALINGV)
+inline double computeEdgeLinearLength(BDS_Edge *e, 
+				      GFace *f, 
+                                      double SCALINGU, 
+				      double SCALINGV)
 {
   if (f->geomType() == GEntity::Plane)
     return e->length();
@@ -84,7 +92,8 @@ inline double computeEdgeLinearLength(BDS_Edge *e, GFace *f,
 
 double NewGetLc(BDS_Point *p)
 {
-  return Extend1dMeshIn2dSurfaces() ? std::min(p->lc(), p->lcBGM()) : p->lcBGM();
+  return Extend1dMeshIn2dSurfaces() ? 
+    std::min(p->lc(), p->lcBGM()) : p->lcBGM();
 }
 
 double NewGetLc(BDS_Edge *e, GFace *f, double SCALINGU, double SCALINGV)
diff --git a/Mesh/meshGFaceDelaunayInsertion.cpp b/Mesh/meshGFaceDelaunayInsertion.cpp
index 6aebd9d324..9e85a48160 100644
--- a/Mesh/meshGFaceDelaunayInsertion.cpp
+++ b/Mesh/meshGFaceDelaunayInsertion.cpp
@@ -14,6 +14,7 @@
 #include "meshGFace.h"
 #include "GFace.h"
 #include "Numeric.h"
+#include "STensor3.h"
 
 const double LIMIT_ = 0.5 * sqrt(2.0);
 
@@ -521,8 +522,8 @@ static void insertAPoint(GFace *gf,
 
     v->setNum(Us.size());
     double lc1 = ((1. - uv[0] - uv[1]) * vSizes[ptin->tri()->getVertex(0)->getNum()] + 
-		  uv[0] * vSizes [ptin->tri()->getVertex(1)->getNum()] + 
-		  uv[1] * vSizes [ptin->tri()->getVertex(2)->getNum()]); 
+    		  uv[0] * vSizes [ptin->tri()->getVertex(1)->getNum()] + 
+    		  uv[1] * vSizes [ptin->tri()->getVertex(2)->getNum()]); 
     // double eigMetricSurface = gf->getMetricEigenvalue(SPoint2(center[0],center[1]));
     double lc = BGM_MeshSize(gf,center[0],center[1],p.x(),p.y(),p.z());
     vSizesBGM.push_back(lc);
@@ -645,12 +646,29 @@ static double length_metric ( const double p[2], const double q[2], const double
      
 */
 
+void testTensor(){
+  SMetric3 t(1.0);  
+  t(0,0) = 1;
+  t(1,0) = .2;
+  t(1,1) = 2;
+  t(2,2) = 3;
+  gmshMatrix<double> m(3,3);
+  gmshVector<double> v(3);
+  t.eig(m,v);
+  printf("%12.5E %12.5E %12.5E \n",v(0),v(1),v(2));
+  printf("%12.5E %12.5E %12.5E \n",m(0,0),m(1,0),m(2,0));
+  printf("%12.5E %12.5E %12.5E \n",m(0,1),m(1,1),m(2,1));
+  printf("%12.5E %12.5E %12.5E \n",m(0,2),m(1,2),m(2,2));
+}
+
 void gmshBowyerWatsonFrontal(GFace *gf)
 {
   std::set<MTri3*,compareTri3Ptr> AllTris;
   std::set<MTri3*,compareTri3Ptr> ActiveTris;
   std::vector<double> vSizes, vSizesBGM, Us, Vs;
 
+  testTensor();
+
   buildMeshGenerationDataStructures(gf, AllTris, vSizes, vSizesBGM, Us, Vs);
 
   // delaunise the initial mesh
diff --git a/Mesh/qualityMeasures.cpp b/Mesh/qualityMeasures.cpp
index 6a6699c712..92fb0015ba 100644
--- a/Mesh/qualityMeasures.cpp
+++ b/Mesh/qualityMeasures.cpp
@@ -199,20 +199,35 @@ double mesh_functional_distorsion(MTriangle *t, double u, double v)
   double normal[3];
   prodve(v1b, v2b, normal);
   
-  double sign;
+  double sign = 1.0;
   prosca(normal1, normal, &sign);
   double det = norm3(normal) * (sign > 0 ? 1. : -1.) / nn;  
 
   // compute distorsion
-  double dist = std::min(1. / det, det); 
-  return dist;
+  //  double dist = std::min(1. / det, det); 
+  return det;
+}
+
+
+double mesh_functional_distorsion_p2(MTriangle *t)
+{
+  double d = mesh_functional_distorsion(t,0.,0.);
+  d = std::min(d,mesh_functional_distorsion(t,1.,0.));
+  d = std::min(d,mesh_functional_distorsion(t,0.,1.));
+  d = std::min(d,mesh_functional_distorsion(t,.5,0.));
+  d = std::min(d,mesh_functional_distorsion(t,0.,0.5));
+  d = std::min(d,mesh_functional_distorsion(t,.5,0.5));
+  return d;
 }
 
 
+
 double qmDistorsionOfMapping (MTriangle *e)
 {
   //  return 1.0;
   if (e->getPolynomialOrder() == 1)return 1.0;
+  //  if (e->getPolynomialOrder() == 2)return mesh_functional_distorsion_p2(e);
+
   IntPt *pts;
   int npts;
   e->getIntegrationPoints(e->getPolynomialOrder(),&npts, &pts);
@@ -283,5 +298,5 @@ double qmDistorsionOfMapping (MTetrahedron *e)
   }
   //  printf("DMIN = %g\n\n",dmin);
 
-  return dmin< 0 ? 0 :dmin;
+  return dmin;
 }
diff --git a/Numeric/GmshMatrix.cpp b/Numeric/GmshMatrix.cpp
index b43788bf68..96281ed051 100644
--- a/Numeric/GmshMatrix.cpp
+++ b/Numeric/GmshMatrix.cpp
@@ -104,8 +104,68 @@ extern "C" {
   void dgesvd_(const char* jobu, const char *jobvt, int *M, int *N,
                double *A, int *lda, double *S, double* U, int *ldu,
                double *VT, int *ldvt, double *work, int *lwork, int *info);
+  void dgeev_(const char *jobvl, const char *jobvr, 
+	      int *n, double *a, int *lda, 
+	      double *wr, double *wi, 
+	      double *vl, int *ldvl, 
+	      double *vr, int *ldvr, 
+	      double *work, int *lwork,
+	      int *info); 
 }
 
+template<> 
+bool gmshMatrix<double>::invertInPlace()
+{
+  int N = size1(), nrhs = N, lda = N, ldb = N, info;
+  int *ipiv = new int[N];
+  double * invA = new double[N*N];
+
+  for (size_t i=0;i<N*N;i++) invA[i     ] = 0.;
+  for (size_t i=0;i<N;i++)   invA[i*N+i]  = 1.;
+
+  dgesv_(&N, &nrhs, _data, &lda, ipiv, invA, &ldb, &info);
+  std::memcpy(_data,invA,N*N*sizeof(double));
+
+  delete [] invA;
+  delete [] ipiv;
+
+  if(info == 0) return true;
+  if(info > 0)
+    Msg::Error("U(%d,%d)=0 in matric inversion", info, info);
+  else
+    Msg::Error("Wrong %d-th argument in matrix inversion", -info);
+  return false;
+}
+
+
+template<> 
+bool gmshMatrix<double>::eig(gmshMatrix<double> &VL, // left eigenvectors 
+			     gmshVector<double> &DR, // Real part of eigenvalues
+			     gmshVector<double> &DI, // Im part of eigenvalues
+			     gmshMatrix<double> &VR )
+{
+  int N = size1(), info;
+  int LWORK = 10*N;
+  double * work = new double[LWORK];
+
+  dgeev_("V","V",
+	 &N,_data,
+	 &N,DR._data,DI._data,
+	 VL._data,&N,
+	 VR._data,&N,
+	 work,&LWORK,&info);
+  
+  delete [] work;
+
+  if(info == 0) return true;
+  if(info > 0)
+    Msg::Error("QR Algorithm failed to compute all the eigenvalues", info, info);
+  else
+    Msg::Error("Wrong %d-th argument in eig", -info);
+  return false;
+}
+
+
 template<> 
 bool gmshMatrix<double>::lu_solve(const gmshVector<double> &rhs, gmshVector<double> &result)
 {
diff --git a/Numeric/GmshMatrix.h b/Numeric/GmshMatrix.h
index 40df2676e8..771f963b79 100644
--- a/Numeric/GmshMatrix.h
+++ b/Numeric/GmshMatrix.h
@@ -53,6 +53,14 @@ class gmshVector
     else 
       for(int i = 0; i < _r; ++i) _data[i] *= s;
   }
+
+  inline scalar operator *(const gmshVector<scalar> & other)
+  {
+    scalar s = 0.0;
+    for(int i = 0; i < _r; ++i) s += _data[i]*other._data[i];
+    return s;
+  }
+
 };
 
 template <class scalar>
@@ -173,6 +181,25 @@ class gmshMatrix
     Msg::Error("LU factorization requires LAPACK");
     return false;
   }
+#endif
+  ;
+  bool invertInPlace()
+#if !defined(HAVE_LAPACK)
+  {
+    Msg::Error("Matrix inversion requires LAPACK");
+    return false;
+  }
+#endif
+  ;
+  bool eig(gmshMatrix<scalar> &VL, // left eigenvectors 
+	   gmshVector<double> &DR, // Real part of eigenvalues
+	   gmshVector<double> &DI, // Im part of eigen
+	   gmshMatrix<scalar> &VR)
+#if !defined(HAVE_LAPACK)
+  {
+    Msg::Error("Eigenvalue computations requires LAPACK");
+    return false;
+  }
 #endif
   ;
   bool invert(gmshMatrix<scalar> &result)
diff --git a/Numeric/Numeric.cpp b/Numeric/Numeric.cpp
index 6d6804c3e0..8519c76896 100644
--- a/Numeric/Numeric.cpp
+++ b/Numeric/Numeric.cpp
@@ -571,3 +571,125 @@ bool newton_fd(void (*func)(gmshVector<double> &, gmshVector<double> &, void *),
   }
   return false;
 }
+
+/*
+min_a f(x+a*d);
+
+f(x+a*d) = f(x) + f'(x) (
+
+*/
+
+void gmshLineSearch (double (*func)(gmshVector<double> &, void *), void* data, 
+		     gmshVector<double> &x,
+		     gmshVector<double> &p,  
+		     gmshVector<double> &g,  
+		     double &f, 
+		     double stpmax, int &check)
+{
+  int i;
+  double alam,alam2,alamin,f2,fold2,rhs1,rhs2,sum,temp,
+    tmplam;
+
+  const double ALF = 1.0e-4;
+  const double TOLX = 1.0e-9;
+
+  gmshVector<double> xold(x);
+  const double fold = (*func)(xold,data);
+  
+  check=0;
+  int n = x.size();
+  double norm = p.norm();
+  if (norm > stpmax)p.scale(stpmax/norm);
+  double slope=0.0;
+  for (i=0;i<n;i++)slope += g(i)*p(i);
+  double test=0.0;
+  for (i=0;i<n;i++) {
+    temp=fabs(p(i))/std::max(fabs(xold(i)),1.0);
+    if (temp > test) test=temp;
+  }
+  /*
+  for (int j=0;j<100;j++){
+    double sx = (double)j/99;
+    for (i=0;i<n;i++) x(i)=xold(i)+10*sx*p(i);    
+    double jzede = (*func)(x,data); 
+  }
+  */
+
+  alamin=TOLX/test;
+  alam=1.0;
+  while(1) {
+    for (i=0;i<n;i++) x(i)=xold(i)+alam*p(i);
+    f=(*func)(x,data);
+    //    printf("f = %g x = %g %g alam = %g p = %g %g\n",f,x(0),x(1),alam,p(0),p(1));
+   if (alam < alamin) {
+      for (i=0;i<n;i++) x(i)=xold(i);
+      //      printf("ALERT : alam %g alamin %g\n",alam,alamin);
+      check=1;
+      return;
+    } 
+    else if (f <= fold + ALF*alam*slope) return;
+    else {
+      if (alam == 1.0)
+	tmplam = -slope/(2.0*(f-fold-slope));
+      else {
+	rhs1 = f-fold-alam*slope;
+	rhs2=f2-fold2-alam2*slope;
+	const double a=(rhs1/(alam*alam)-rhs2/(alam2*alam2))/(alam-alam2);
+	const double b=(-alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2);
+	if (a == 0.0) tmplam = -slope/(2.0*b);
+	else {
+	  const double disc=b*b-3.0*a*slope;
+	  if (disc<0.0) Msg::Error("Roundoff problem in gmshLineSearch.");
+	  else tmplam=(-b+sqrt(disc))/(3.0*a);
+	}
+	if (tmplam>0.5*alam)
+	  tmplam=0.5*alam;
+      }
+    }
+    alam2=alam;
+    f2 = f;
+    fold2=fold;
+    alam=std::max(tmplam,0.1*alam);
+  }
+}
+
+double minimize_grad_fd (double (*func)(gmshVector<double> &, void *),
+		       gmshVector<double> &x, void *data)
+{
+  const int MAXIT = 3;
+  const double EPS = 1.e-4;
+  const int N = x.size();
+  
+  gmshVector<double> grad(N);
+  gmshVector<double> dir(N);
+  double f,feps,finit;
+
+  for (int iter = 0; iter < MAXIT; iter++){
+    // compute gradient of func
+    f = func(x,data);
+    if (iter == 0)finit = f;
+    //    printf("Opti iter %d x = (%g %g) f = %g\n",iter,x(0),x(1),f);
+    //    printf("grad = (");
+    for (int j = 0; j < N; j++){
+      double h = EPS * fabs(x(j));
+      if(h == 0.) h = EPS;
+      x(j) += h;
+      feps = func(x, data);
+      grad(j) = (feps - f) / h;
+      //      printf("%g ",grad(j));
+      dir(j) = -grad(j);
+      x(j) -= h;
+    }
+    //    printf(")\n ");
+    // do a 1D line search to fine the minimum
+    // of f(x - \alpha \nabla f)
+    double f, stpmax=100000;
+    int check;
+    gmshLineSearch (func, data, x,dir,grad,f,stpmax,check);
+    //    printf("Line search done x = (%g %g) f = %g\n",x(0),x(1),f);
+    if (check == 1)break;
+  }
+  
+  return f;
+}
+
diff --git a/Numeric/Numeric.h b/Numeric/Numeric.h
index 578a66f736..ba4e0deb72 100644
--- a/Numeric/Numeric.h
+++ b/Numeric/Numeric.h
@@ -74,5 +74,7 @@ double ComputeScalarRep(int numComp, double *val);
 void invert_singular_matrix3x3(double MM[3][3], double II[3][3]);
 bool newton_fd(void (*func)(gmshVector<double> &, gmshVector<double> &, void *),
                gmshVector<double> &x, void *data, double relax=1., double tolx=1.e-6);
+double minimize_grad_fd (double (*func)(gmshVector<double> &, void *),
+			 gmshVector<double> &x, void *data);
 
 #endif
diff --git a/Numeric/gmshAssembler.h b/Numeric/gmshAssembler.h
index bad4d5b052..56d56d9be7 100644
--- a/Numeric/gmshAssembler.h
+++ b/Numeric/gmshAssembler.h
@@ -166,6 +166,13 @@ class gmshAssembler {
   }
   int sizeOfR() const { return numbering.size(); }
   int sizeOfF() const { return fixed.size(); }
+  void systemSolve(){
+    lsys->systemSolve();
+  }  
+  void systemClear(){
+    lsys->zeroMatrix();
+    lsys->zeroRightHandSide();
+  }  
 };
 
 #endif
diff --git a/Numeric/gmshElasticity.cpp b/Numeric/gmshElasticity.cpp
index 2750a47ff1..3d323ca272 100644
--- a/Numeric/gmshElasticity.cpp
+++ b/Numeric/gmshElasticity.cpp
@@ -9,7 +9,7 @@
 void gmshElasticityTerm::elementMatrix(MElement *e, gmshMatrix<double> &m) const
 {
   int nbNodes = e->getNumVertices();
-  int integrationOrder = 3 * (e->getPolynomialOrder() - 1);
+  int integrationOrder = 3 * (e->getPolynomialOrder() - 1) ;
   int npts;
   IntPt *GP;
   double jac[3][3];
diff --git a/Numeric/gmshElasticity.h b/Numeric/gmshElasticity.h
index 037aeedbd2..ac4c81268c 100644
--- a/Numeric/gmshElasticity.h
+++ b/Numeric/gmshElasticity.h
@@ -13,10 +13,11 @@
 #include "GmshMatrix.h"
 
 class gmshElasticityTerm : public gmshNodalFemTerm<double> {
+ protected:
   double _E, _nu;
   int _iField;
   SVector3 _f;
- protected:
+ public:
   virtual int sizeOfR(MElement *e) const { return 3 * e->getNumVertices(); }
   virtual int sizeOfC(MElement *e) const { return 3 * e->getNumVertices(); }
   void getLocalDofR(MElement *e, int iRow, MVertex **vR, int *iCompR, int *iFieldR) const
diff --git a/Numeric/gmshLaplace.cpp b/Numeric/gmshLaplace.cpp
index 0c9f54af54..d368962cd5 100644
--- a/Numeric/gmshLaplace.cpp
+++ b/Numeric/gmshLaplace.cpp
@@ -50,3 +50,7 @@ void gmshLaplaceTerm::elementMatrix(MElement *e, gmshMatrix<double> &m) const
     for (int k = 0; k < j; k++)
 	m(k, j) = m(j, k);
 } 
+
+void gmshLaplaceTerm2DParametric::elementMatrix(MElement *e, gmshMatrix<double> &m) const
+{
+} 
diff --git a/Numeric/gmshLaplace.h b/Numeric/gmshLaplace.h
index 3a462294f9..223b5696da 100644
--- a/Numeric/gmshLaplace.h
+++ b/Numeric/gmshLaplace.h
@@ -14,10 +14,10 @@
 #include "GmshMatrix.h"
 
 class gmshLaplaceTerm : public gmshNodalFemTerm<double> {
- private:
+ protected:
   const gmshFunction<double> *_diffusivity;
   const int _iField ;
- protected:
+ public:
   virtual int sizeOfR(MElement *e) const { return e->getNumVertices(); }
   virtual int sizeOfC(MElement *e) const { return e->getNumVertices(); }
   void getLocalDofR(MElement *e, int iRow, MVertex **vR, int *iCompR, int *iFieldR) const
@@ -32,4 +32,13 @@ class gmshLaplaceTerm : public gmshNodalFemTerm<double> {
   virtual void elementMatrix(MElement *e, gmshMatrix<double> &m) const;
 };
 
+class gmshLaplaceTerm2DParametric : gmshLaplaceTerm {
+  GFace *_gf;
+ public:
+  gmshLaplaceTerm2DParametric(GFace *gf, gmshFunction<double> *diffusivity, int iField = 0) : 
+  _gf(gf),gmshLaplaceTerm(gf->model(),diffusivity,iField){}
+  virtual void elementMatrix(MElement *e, gmshMatrix<double> &m) const;
+};
+
+
 #endif
diff --git a/Numeric/gmshLinearSystemGmm.h b/Numeric/gmshLinearSystemGmm.h
index a1caffafd0..1ae20d1e84 100644
--- a/Numeric/gmshLinearSystemGmm.h
+++ b/Numeric/gmshLinearSystemGmm.h
@@ -75,8 +75,8 @@ class gmshLinearSystemGmm : public gmshLinearSystem<scalar> {
   void setGmres(int n){ _gmres = n; }
   virtual int systemSolve() 
   {
-    //gmm::ilutp_precond<gmm::row_matrix<gmm::wsvector<scalar> > > P(*_a, 10, 0.);
-    gmm::ildltt_precond<gmm::row_matrix<gmm::wsvector<scalar> > > P(*_a, 2, 1.e-10);
+    //gmm::ilutp_precond<gmm::row_matrix<gmm::wsvector<scalar> > > P(*_a, 15, 0.);
+    gmm::ildltt_precond<gmm::row_matrix<gmm::wsvector<scalar> > > P(*_a, 10, 1.e-10);
     gmm::iteration iter(_prec);
     iter.set_noisy(_noisy);
     if(_gmres) gmm::gmres(*_a, *_x, *_b, P, 100, iter);
diff --git a/Numeric/gmshTermOfFormulation.h b/Numeric/gmshTermOfFormulation.h
index 2ee14a8d3c..d42d47817b 100644
--- a/Numeric/gmshTermOfFormulation.h
+++ b/Numeric/gmshTermOfFormulation.h
@@ -29,7 +29,7 @@ class gmshTermOfFormulation {
 // of the mesh
 template<class scalar>
 class gmshNodalFemTerm : public gmshTermOfFormulation<scalar> {
- protected:
+ public:
   // return the number of columns of the element matrix
   virtual int sizeOfC(MElement*) const = 0;
   // return the number of rows of the element matrix
@@ -181,7 +181,6 @@ class gmshNodalFemTerm : public gmshTermOfFormulation<scalar> {
       }
     }
   }
-
 };
 
 #endif
diff --git a/Parser/Makefile b/Parser/Makefile
index 446dfe2d54..d29a736f87 100644
--- a/Parser/Makefile
+++ b/Parser/Makefile
@@ -73,9 +73,9 @@ Gmsh.tab${OBJEXT}: Gmsh.tab.cpp ../Common/GmshConfig.h ../Common/GmshMessage.h \
   ../Fltk/Draw.h ../Common/Options.h ../Post/ColorTable.h \
   ../Common/Colors.h ../Common/Options.h Parser.h ../Common/OpenFile.h \
   ../Common/CommandLine.h FunctionManager.h ../Common/OS.h \
-  ../Common/CreateFile.h ../Mesh/Field.h ../Post/PView.h \
-  ../Mesh/BackgroundMesh.h ../Post/PViewDataList.h ../Post/PViewData.h \
-  ../Plugin/PluginManager.h
+  ../Common/CreateFile.h ../Mesh/Field.h ../Geo/STensor3.h \
+  ../Geo/SVector3.h ../Post/PView.h ../Mesh/BackgroundMesh.h \
+  ../Post/PViewDataList.h ../Post/PViewData.h ../Plugin/PluginManager.h
 Gmsh.yy${OBJEXT}: Gmsh.yy.cpp ../Common/GmshMessage.h ../Geo/Geo.h \
   ../Common/GmshDefines.h ../Geo/gmshSurface.h ../Geo/Pair.h \
   ../Geo/Range.h ../Geo/SPoint2.h ../Geo/SPoint3.h ../Geo/SVector3.h \
diff --git a/Plugin/Makefile b/Plugin/Makefile
index 332c8d5a6a..5e6110d294 100644
--- a/Plugin/Makefile
+++ b/Plugin/Makefile
@@ -199,18 +199,20 @@ Skin${OBJEXT}: Skin.cpp Skin.h Plugin.h ../Common/Options.h ../Post/ColorTable.h
   ../Common/ListUtils.h ../Common/TreeUtils.h ../Common/avl.h \
   ../Common/ListUtils.h ../Common/MallocUtils.h ../Common/Context.h \
   ../Geo/CGNSOptions.h ../Mesh/meshPartitionOptions.h
-GSHHS${OBJEXT}: GSHHS.cpp ../Mesh/Field.h ../Common/GmshConfig.h ../Post/PView.h \
-  ../Geo/SPoint3.h ../Geo/SPoint2.h GSHHS.h Plugin.h ../Common/Options.h \
-  ../Post/ColorTable.h ../Common/GmshMessage.h ../Post/PViewDataList.h \
-  ../Post/PViewData.h ../Geo/SBoundingBox3d.h ../Geo/SPoint3.h \
-  ../Numeric/GmshMatrix.h ../Common/ListUtils.h ../Geo/GModel.h \
-  ../Geo/GVertex.h ../Geo/GEntity.h ../Geo/Range.h ../Geo/SPoint3.h \
+GSHHS${OBJEXT}: GSHHS.cpp ../Mesh/Field.h ../Common/GmshConfig.h \
+  ../Geo/STensor3.h ../Geo/SVector3.h ../Geo/SPoint3.h \
+  ../Numeric/GmshMatrix.h ../Common/GmshMessage.h ../Numeric/Numeric.h \
+  ../Numeric/GmshMatrix.h ../Post/PView.h ../Geo/SPoint2.h GSHHS.h \
+  Plugin.h ../Common/Options.h ../Post/ColorTable.h \
+  ../Post/PViewDataList.h ../Post/PViewData.h ../Geo/SBoundingBox3d.h \
+  ../Geo/SPoint3.h ../Common/ListUtils.h ../Geo/GModel.h ../Geo/GVertex.h \
+  ../Geo/GEntity.h ../Geo/Range.h ../Geo/SPoint3.h \
   ../Geo/SBoundingBox3d.h ../Geo/GPoint.h ../Geo/SPoint2.h ../Geo/GEdge.h \
   ../Geo/GEntity.h ../Geo/GVertex.h ../Geo/SVector3.h ../Geo/SPoint3.h \
-  ../Geo/SPoint3.h ../Geo/SPoint2.h ../Geo/GFace.h ../Geo/GEntity.h \
-  ../Geo/GPoint.h ../Geo/GEdgeLoop.h ../Geo/GEdge.h ../Geo/SPoint2.h \
-  ../Geo/SVector3.h ../Geo/Pair.h ../Geo/GRegion.h ../Geo/GEntity.h \
-  ../Geo/SPoint3.h ../Geo/SBoundingBox3d.h
+  ../Geo/SPoint2.h ../Geo/GFace.h ../Geo/GEntity.h ../Geo/GPoint.h \
+  ../Geo/GEdgeLoop.h ../Geo/GEdge.h ../Geo/SPoint2.h ../Geo/SVector3.h \
+  ../Geo/Pair.h ../Geo/GRegion.h ../Geo/GEntity.h ../Geo/SPoint3.h \
+  ../Geo/SBoundingBox3d.h
 Extract${OBJEXT}: Extract.cpp ../Common/GmshConfig.h Extract.h Plugin.h \
   ../Common/Options.h ../Post/ColorTable.h ../Common/GmshMessage.h \
   ../Post/PView.h ../Geo/SPoint3.h ../Post/PViewDataList.h \
@@ -244,13 +246,14 @@ FieldView${OBJEXT}: FieldView.cpp FieldView.h Plugin.h ../Common/Options.h \
   ../Geo/SPoint3.h ../Post/PViewDataList.h ../Post/PViewData.h \
   ../Geo/SBoundingBox3d.h ../Geo/SPoint3.h ../Numeric/GmshMatrix.h \
   ../Common/GmshConfig.h ../Common/ListUtils.h ../Mesh/Field.h \
-  ../Geo/GModel.h ../Geo/GVertex.h ../Geo/GEntity.h ../Geo/Range.h \
-  ../Geo/SPoint3.h ../Geo/SBoundingBox3d.h ../Geo/GPoint.h \
-  ../Geo/SPoint2.h ../Geo/GEdge.h ../Geo/GEntity.h ../Geo/GVertex.h \
-  ../Geo/SVector3.h ../Geo/SPoint3.h ../Geo/SPoint3.h ../Geo/SPoint2.h \
-  ../Geo/GFace.h ../Geo/GEntity.h ../Geo/GPoint.h ../Geo/GEdgeLoop.h \
-  ../Geo/GEdge.h ../Geo/SPoint2.h ../Geo/SVector3.h ../Geo/Pair.h \
-  ../Geo/GRegion.h ../Geo/GEntity.h ../Geo/SPoint3.h \
+  ../Geo/STensor3.h ../Geo/SVector3.h ../Geo/SPoint3.h \
+  ../Numeric/Numeric.h ../Numeric/GmshMatrix.h ../Geo/GModel.h \
+  ../Geo/GVertex.h ../Geo/GEntity.h ../Geo/Range.h ../Geo/SPoint3.h \
+  ../Geo/SBoundingBox3d.h ../Geo/GPoint.h ../Geo/SPoint2.h ../Geo/GEdge.h \
+  ../Geo/GEntity.h ../Geo/GVertex.h ../Geo/SVector3.h ../Geo/SPoint3.h \
+  ../Geo/SPoint2.h ../Geo/GFace.h ../Geo/GEntity.h ../Geo/GPoint.h \
+  ../Geo/GEdgeLoop.h ../Geo/GEdge.h ../Geo/SPoint2.h ../Geo/SVector3.h \
+  ../Geo/Pair.h ../Geo/GRegion.h ../Geo/GEntity.h ../Geo/SPoint3.h \
   ../Geo/SBoundingBox3d.h
 Integrate${OBJEXT}: Integrate.cpp Integrate.h Plugin.h ../Common/Options.h \
   ../Post/ColorTable.h ../Common/GmshMessage.h ../Post/PView.h \
diff --git a/Post/shapeFunctions.h b/Post/shapeFunctions.h
index 965347251d..1871bfdc16 100644
--- a/Post/shapeFunctions.h
+++ b/Post/shapeFunctions.h
@@ -84,6 +84,7 @@ public:
       }
       return sqrt(SQU(jac[0][0])+SQU(jac[0][1])+SQU(jac[0][2]));
     default:
+      jac[0][0] = jac[1][1] = jac[2][2] = 1.;
       return 1.;
     }
   }
diff --git a/benchmarks/2d/naca12_2d.geo b/benchmarks/2d/naca12_2d.geo
index 3b2bd214b3..6baf8b1e2f 100644
--- a/benchmarks/2d/naca12_2d.geo
+++ b/benchmarks/2d/naca12_2d.geo
@@ -1,6 +1,6 @@
-lc = 0.2;
-lc2 = 1;
-lc3 = 0.1;
+lc = 0.2 ;
+lc2 = 1 ;
+lc3 = 0.1 ;
 Point(1) =  {1.000000e+00,0.000000e+00,0.000000e+00,lc3}; 
 Point(2) =  {9.997533e-01,0.000000e+00,-3.498543e-05,lc}; 
 Point(3) =  {9.990134e-01,0.000000e+00,-1.398841e-04,lc}; 
@@ -224,7 +224,17 @@ Line Loop(9) = {6,7,8,5};
 Line Loop(10) = {2,3,4,1};
 Plane Surface(11) = {9,10};
 
+Physical Surface(11)={11};
 //Point(9999) = {0.6,0,0,1};
 
-//Recombine Surface {11};
-
+Field[1] = Attractor;
+Field[1].EdgesList = {1, 2, 3, 4};
+Field[1].NNodesByEdge = 2000;
+Field[2] = Laplacian;
+Field[2].Delta = 0.001;
+Field[2].IField = 1;
+Field[3] = MathEval;
+Field[3].F = "20/(F2*3.14)";
+//Background Field = 3;
+Field[4] = Gradient;
+Field[4].Delta = 0.001;
-- 
GitLab