From bf66c842fb8a492b13e7709f48d68b74235bb6c2 Mon Sep 17 00:00:00 2001
From: Jean-Francois Remacle <jean-francois.remacle@uclouvain.be>
Date: Tue, 5 Apr 2011 10:39:18 +0000
Subject: [PATCH] mesh matching enhanced high order smoother class added : high
 order meshes are guaranteed to be valid : some elements are "un-curved" in
 order to ensure good value of the distorsion mapping. P2 triangle quality
 estimate is robust

---
 Common/gmshpy.i               |   2 +
 Fltk/classificationEditor.cpp |  12 +-
 Fltk/classificationEditor.h   |   2 +-
 Geo/GEdge.cpp                 |  97 +++--
 Geo/GModelFactory.cpp         |   3 +-
 Geo/GeomMeshMatcher.cpp       | 169 +++++++++
 Geo/GeomMeshMatcher.h         |   2 +
 Geo/MElement.h                |  10 +
 Mesh/CMakeLists.txt           |   1 +
 Mesh/HighOrder.cpp            |  24 +-
 Mesh/highOrderSmoother.cpp    |  77 +++-
 Mesh/highOrderSmoother.h      |   1 -
 Mesh/highOrderTools.cpp       | 668 ++++++++++++++++++++++++++++++++++
 Mesh/highOrderTools.h         |  92 +++++
 Mesh/meshGFaceOptimize.cpp    |   6 +
 Mesh/meshGFaceOptimize.h      |   2 +
 Mesh/qualityMeasures.cpp      |  18 +-
 Numeric/DivideAndConquer.cpp  |  55 ++-
 Numeric/DivideAndConquer.h    |  18 +-
 Solver/elasticityTerm.cpp     |  71 +++-
 20 files changed, 1253 insertions(+), 77 deletions(-)
 create mode 100644 Mesh/highOrderTools.cpp
 create mode 100644 Mesh/highOrderTools.h

diff --git a/Common/gmshpy.i b/Common/gmshpy.i
index 452afd9951..0c87dfb7f1 100644
--- a/Common/gmshpy.i
+++ b/Common/gmshpy.i
@@ -6,6 +6,7 @@
 %{
   #include "GmshConfig.h"
   #include "GModel.h"
+  #include "highOrderTools.h"
   #include "DefaultOptions.h"
   #include "fullMatrix.h"
   #include "function.h"
@@ -92,6 +93,7 @@ namespace std {
 %include "dofManager.h"
 %template(dofManagerDouble) dofManager<double>;
 %include "GModel.h"
+%include "highOrderTools.h"
 %include "function.h"
 %include "linearSystem.h"
 %template(linearSystemDouble) linearSystem<double>;
diff --git a/Fltk/classificationEditor.cpp b/Fltk/classificationEditor.cpp
index 7c76c9c505..b9203faf09 100644
--- a/Fltk/classificationEditor.cpp
+++ b/Fltk/classificationEditor.cpp
@@ -15,6 +15,7 @@
 #include "Context.h"
 #include "GmshMessage.h"
 #include "MLine.h"
+#include "MQuadrangle.h"
 #include "meshGFaceDelaunayInsertion.h"
 #include "discreteEdge.h"
 #include "discreteFace.h"
@@ -104,9 +105,12 @@ static void select_elements_cb(Fl_Widget *w, void *data)
 
   if(all){
     for(GModel::fiter it = GModel::current()->firstFace(); 
-        it != GModel::current()->lastFace(); ++it)
+        it != GModel::current()->lastFace(); ++it){
       e->elements.insert(e->elements.end(), (*it)->triangles.begin(), 
                          (*it)->triangles.end());
+      e->elements.insert(e->elements.end(), (*it)->quadrangles.begin(), 
+                         (*it)->quadrangles.end());
+    }
   }
   else{
     CTX::instance()->pickElements = 1;
@@ -120,9 +124,9 @@ static void select_elements_cb(Fl_Widget *w, void *data)
       if(ib == 'l') {
         for(unsigned int i = 0; i < FlGui::instance()->selectedElements.size(); i++){
           MElement *me = FlGui::instance()->selectedElements[i];
-          if(me->getType() == TYPE_TRI && me->getVisibility() != 2){
+          if(me->getDim() == 2 && me->getVisibility() != 2){
             me->setVisibility(2); 
-            e->elements.push_back((MTriangle*)me);
+            e->elements.push_back(me);
           }
         }
       }
@@ -148,7 +152,7 @@ static void select_elements_cb(Fl_Widget *w, void *data)
   }
 
   e2t_cont adj;
-  buildEdgeToTriangle(e->elements, adj);
+  buildEdgeToElements(e->elements, adj);
   buildListOfEdgeAngle(adj, e->edges_detected, e->edges_lonly);
   ElementsSelectedMode(e);
   update_edges_cb(0, data);
diff --git a/Fltk/classificationEditor.h b/Fltk/classificationEditor.h
index c265da22c8..d7eee0113f 100644
--- a/Fltk/classificationEditor.h
+++ b/Fltk/classificationEditor.h
@@ -31,7 +31,7 @@
 
 class classificationEditor {
  public:
-  std::vector<MTriangle*> elements;
+  std::vector<MElement*> elements;
   std::set<GFace*> faces;
   Fl_Window *window;
   Fl_Button *buttons[10];
diff --git a/Geo/GEdge.cpp b/Geo/GEdge.cpp
index adf7d5431c..7e0a86b73b 100644
--- a/Geo/GEdge.cpp
+++ b/Geo/GEdge.cpp
@@ -259,38 +259,83 @@ double GEdge::length(const double &u0, const double &u1, const int nbQuadPoints)
   return L;
 }
 
+/*
+  consider a curve x(t) and a point y
+  
+  use a golden section algorithm that minimizes 
+
+  min_t \|x(t)-y\|   
+
+*/
+
+const double GOLDEN  = (1. + sqrt(5)) / 2.;
+const double GOLDEN2 = 2 - GOLDEN;
+ 
+// x1 and x3 are the current bounds; the minimum is between them.
+// x2 is the center point, which is closer to x1 than to x3
+
+double goldenSectionSearch(const GEdge *ge, const SPoint3 &q, double x1, double x2, double x3, double tau)
+{ 
+  // Create a new possible center in the area between x2 and x3, closer to x2
+  double x4 = x2 + GOLDEN2 * (x3 - x2);
+  
+  // Evaluate termination criterion
+  if (fabs(x3 - x1) < tau * (fabs(x2) + fabs(x4)))
+    return (x3 + x1) / 2;
+  
+  const SVector3 dp4 = q - ge->position(x4);
+  const SVector3 dp2 = q - ge->position(x2);
+  
+  const double d4 = dp4.norm();
+  const double d2 = dp2.norm();
+  
+  if (d4 < d2)
+    return goldenSectionSearch(ge, q, x2, x4, x3, tau);
+  else
+    return goldenSectionSearch(ge,q , x4, x2, x1, tau);
+}
+
 GPoint GEdge::closestPoint(const SPoint3 &q, double &t) const
 {
+
+  //  printf("looking for closest point in curve %d to point %g %g\n",tag(),q.x(),q.y());
+  
+  const int nbSamples = 100;
+  
   double tolerance = 1.e-12;
 
   Range<double> interval = parBounds(0);
   
   double tMin = std::min(interval.high(), interval.low());
   double tMax = std::max(interval.high(), interval.low());
-  double relax = 1.;
-  double dt, dt0, t0;
-  int nb = 10;
-  
-  t = (tMin + tMax) * 0.5;
-  
-  while (relax > 0.1) {
-    int i = 0;
-    t = 0.5 * (tMin + tMax);
-    dt0 = tMax - tMin;
-    dt = dt0;
-    while (dt > tolerance * dt0 && i++ < nb) {
-      t0 = t;
-      dt0 = dt;
-      SVector3 dp = q - position(t);
-      SVector3 derP = firstDer(t);
-      double b = dot(derP, derP);
-      double c = dot(derP, dp);
-      dt = c / b;
-      t = std::max(tMin, std::min(tMax, t0 + relax * dt));
-      dt = fabs(t - t0);
+
+  double DMIN = 1.e22;
+  double topt;
+  const double DT = (tMax-tMin)/(nbSamples-1) ;
+  for (int i=0;i<nbSamples;i++){
+    t = tMin + i * DT;
+    const SVector3 dp = q - position(t);
+    const double D = dp.norm();
+    if (D < DMIN) {
+      topt = t;
+      DMIN = D;
     }
-    if (i > nb)  relax *= 0.5;
   }
+  
+  //  printf("parameter %g as an initial guess (dist = %g)\n",topt,DMIN);
+
+  if (topt == tMin)
+    t = goldenSectionSearch (this, q, topt, topt + DT/2, topt + DT,  1.e-7);
+  else if (topt == tMax)
+    t = goldenSectionSearch (this, q, topt - DT, topt - DT/2 , topt, 1.e-7);
+  else
+    t = goldenSectionSearch (this, q, topt - DT, topt, topt + DT, 1.e-7);
+
+  const SVector3 dp = q - position(t);
+  const double D = dp.norm();
+
+  //  printf("after golden section parameter %g  (dist = %g)\n",t,D);
+  
   return point(t);
 }
 
@@ -348,13 +393,13 @@ bool GEdge::XYZToU(const double X, const double Y, const double Z,
   }
   
   if(relax > 1.e-2) {
-    Msg::Info("point %g %g %g on edge %d : Relaxation factor = %g", 
-              Q.x(), Q.y(), Q.z(), 0.75 * relax);
+    //    Msg::Info("point %g %g %g on edge %d : Relaxation factor = %g", 
+    //              Q.x(), Q.y(), Q.z(), 0.75 * relax);
     return XYZToU(Q.x(), Q.y(), Q.z(), u, 0.75 * relax);
   }
   
-  Msg::Error("Could not converge reparametrisation of point (%e,%e,%e) on edge %d",
-             Q.x(), Q.y(), Q.z(), tag());
+  //  Msg::Error("Could not converge reparametrisation of point (%e,%e,%e) on edge %d",
+  //             Q.x(), Q.y(), Q.z(), tag());
   return false;
 }
 
diff --git a/Geo/GModelFactory.cpp b/Geo/GModelFactory.cpp
index ce18e3bb22..06b6889550 100644
--- a/Geo/GModelFactory.cpp
+++ b/Geo/GModelFactory.cpp
@@ -228,6 +228,7 @@ GEdge *OCCFactory::addCircleArc(GModel *gm, const arcCreationMethod &method,
   gp_Pnt aP3(end->x(), end->y(), end->z());
   TopoDS_Edge occEdge;
 
+
   OCCVertex *occv1 = dynamic_cast<OCCVertex*>(start);
   OCCVertex *occv2 = dynamic_cast<OCCVertex*>(end);
 
@@ -241,7 +242,7 @@ GEdge *OCCFactory::addCircleArc(GModel *gm, const arcCreationMethod &method,
   }
   else if (method == GModelFactory::CENTER_START_END){
     Standard_Real Radius = aP1.Distance(aP2);
-    gce_MakeCirc MC(aP1,gce_MakePln(aP1, aP2, aP3).Value(), Radius);
+    gce_MakeCirc MC(aP2,gce_MakePln(aP1, aP2, aP3).Value(), Radius);
     const gp_Circ& Circ = MC.Value();
     Standard_Real Alpha1 = ElCLib::Parameter(Circ, aP1);
     Standard_Real Alpha2 = ElCLib::Parameter(Circ, aP3);
diff --git a/Geo/GeomMeshMatcher.cpp b/Geo/GeomMeshMatcher.cpp
index 04b501d11d..2d524a234f 100644
--- a/Geo/GeomMeshMatcher.cpp
+++ b/Geo/GeomMeshMatcher.cpp
@@ -17,6 +17,8 @@
 #include "GmshMessage.h"
 #include "SOrientedBoundingBox.h"
 #include "MElement.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
 #include "MVertex.h"
 #include "MLine.h"
 #include "MPoint.h"
@@ -423,6 +425,173 @@ void GeomMeshMatcher::destroy()
     delete GeomMeshMatcher::_gmm_instance;
 }
 
+static GVertex *getGVertex (MVertex *v1, GModel *gm, const double TOL){
+  GVertex *best = 0;
+  double bestScore = TOL;
+  for (GModel::eiter it = gm->firstEdge(); it != gm->lastEdge(); ++it){
+    {
+      GVertex *v2 = (*it)->getBeginVertex();    
+      double score = sqrt((v1->x() - v2->x())*(v1->x() - v2->x()) +
+			  (v1->y() - v2->y())*(v1->y() - v2->y()) +
+			  (v1->z() - v2->z())*(v1->z() - v2->z()));
+      if (score < bestScore){
+	bestScore = score;
+	best =  v2;
+      }    
+    }
+    {
+      GVertex *v2 = (*it)->getEndVertex();    
+      double score = sqrt((v1->x() - v2->x())*(v1->x() - v2->x()) +
+			  (v1->y() - v2->y())*(v1->y() - v2->y()) +
+			  (v1->z() - v2->z())*(v1->z() - v2->z()));
+      if (score < bestScore){
+	bestScore = score;
+	best =  v2;
+      }    
+    }
+  }
+  //  if (best)printf("getting point %g %g on vertices best score is %12.5E\n",v1->x(),v1->y(),bestScore);
+  return best;
+}
+
+static GPoint getGEdge (MVertex *v1, GModel *gm, const double TOL){
+  GPoint gpBest;
+  double bestScore = TOL;
+  
+
+  for (GModel::eiter it = gm->firstEdge(); it != gm->lastEdge(); ++it){
+    GEdge *e = *it;
+    double pp;
+    GPoint gp = e->closestPoint(SPoint3(v1->x(),v1->y(),v1->z()), pp);
+    double score = sqrt((v1->x() - gp.x())*(v1->x() - gp.x()) +
+			(v1->y() - gp.y())*(v1->y() - gp.y()) +
+			(v1->z() - gp.z())*(v1->z() - gp.z()));
+    if (score < bestScore){
+      bestScore = score;
+      gpBest =  gp;
+    }    
+  }
+  
+  //  printf("getting point %g %g (%g %g) on edges best score is %12.5E\n",v1->x(),v1->y(),gpBest.x(),gpBest.y(),bestScore);
+  return gpBest;
+}
+
+static GPoint getGFace (MVertex *v1, GModel *gm, const double TOL){
+  GPoint gpBest;
+  double bestScore = TOL;
+  for (GModel::fiter it = gm->firstFace(); it != gm->lastFace(); ++it){
+    GFace *gf = *it;
+    SPoint2 pp;
+    double guess[2] = {0,0};
+    GPoint gp = gf->closestPoint(SPoint3(v1->x(),v1->y(),v1->z()), guess);
+    double score = sqrt((v1->x() - gp.x())*(v1->x() - gp.x()) +
+			(v1->y() - gp.y())*(v1->y() - gp.y()) +
+			(v1->z() - gp.z())*(v1->z() - gp.z()));
+    if (score < bestScore){
+      bestScore = score;
+      gpBest =  gp;
+    }    
+  }
+  //  printf("getting point %g %g (%g %g) on faces best score is %12.5E\n",v1->x(),v1->y(),gpBest.x(),gpBest.y(),bestScore);
+  return gpBest;
+}
+
+
+int GeomMeshMatcher::forceTomatch(GModel *geom, GModel *mesh, const double TOL)
+{
+  // assume that the geometry is the right one
+
+  std::vector<GEntity*> entities;
+  mesh->getEntities(entities);
+  for(unsigned int i = 0; i < entities.size(); i++){
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++){
+      MVertex *v = entities[i]->mesh_vertices[j];
+      GVertex *gv = getGVertex (v, geom, TOL);
+      bool found = 0;
+      if (gv){
+	printf("vertex %d matches GVertex %d\n",v->getNum(),gv->tag());
+	found=1;
+	MVertex *vvv = new MVertex (v->x(),v->y(),v->z(),gv,v->getNum());
+	gv->mesh_vertices.push_back(vvv);
+	gv->points.push_back(new MPoint(vvv,v->getNum()));
+	
+      }
+      else if (v->onWhat()->dim() == 1){
+	GPoint gp = getGEdge (v, geom, 1.e22);
+	if(gp.g()){
+	  GEntity *gg = (GEntity*)gp.g();
+	  found=1;
+	  //	  printf("vertex %d matches GEdge %d on position %g\n",v->getNum(),gg->tag(),gp.u());
+	  gg->mesh_vertices.push_back(new MEdgeVertex (gp.x(),gp.y(),gp.z(),
+						       gg,gp.u(),-1.,v->getNum()));
+	}
+      }
+      if (!found && v->onWhat()->dim() <= 2){
+	GPoint gp = getGFace (v, geom, TOL);
+	if(gp.g()){
+	  GEntity *gg = (GEntity*)gp.g();
+	  found = 1;
+	  //	  printf("vertex %d matches GFace %d\n",v->getNum(),gg->tag());
+	  gg->mesh_vertices.push_back(new MFaceVertex (gp.x(),gp.y(),gp.z(),
+						       gg,gp.u(),gp.v(),v->getNum()));
+	}	
+      }
+      if (!found) Msg::Error("vertex %d classified on %d %d not matched",v->getNum(),v->onWhat()->dim(),v->onWhat()->tag());
+    }
+  }
+  //  printf("creating edges\n");
+  for (GModel::eiter it = mesh->firstEdge(); it != mesh->lastEdge(); ++it){
+    //    printf("edge %d\n",(*it)->tag());
+    for (int i=0;i<(*it)->lines.size();i++){
+      //      printf("medge %d %d\n",(*it)->lines[i]->getVertex(0)->getNum(),(*it)->lines[i]->getVertex(1)->getNum());
+      MVertex *v1 = geom->getMeshVertexByTag((*it)->lines[i]->getVertex(0)->getNum());
+      MVertex *v2 = geom->getMeshVertexByTag((*it)->lines[i]->getVertex(1)->getNum());
+      if (v1 && v2){
+	GEdge *ge= 0;
+	if (v1->onWhat()->dim() == 1)ge = (GEdge*)v1->onWhat();
+	if (v2->onWhat()->dim() == 1)ge = (GEdge*)v2->onWhat();
+	if (ge){
+	  double u1,u2;
+	  reparamMeshVertexOnEdge(v1, ge, u1);
+	  reparamMeshVertexOnEdge(v2, ge, u2);
+	  if (u1< u2)ge->lines.push_back(new MLine(v1,v2)); 
+	  else ge->lines.push_back(new MLine(v2,v1)); 
+	}
+	else printf("argh !\n");
+      }
+      else{
+	if (!v1)printf("Vertex %d has not been found\n", (*it)->lines[i]->getVertex(0)->getNum());
+	if (!v2)printf("Vertex %d has not been found\n", (*it)->lines[i]->getVertex(1)->getNum());
+      }
+    }
+  }  
+  printf("creating faces\n");
+  for (GModel::fiter it = mesh->firstFace(); it != mesh->lastFace(); ++it){
+    for (int i=0;i<(*it)->triangles.size();i++){
+      MVertex *v1 = geom->getMeshVertexByTag((*it)->triangles[i]->getVertex(0)->getNum());
+      MVertex *v2 = geom->getMeshVertexByTag((*it)->triangles[i]->getVertex(1)->getNum());
+      MVertex *v3 = geom->getMeshVertexByTag((*it)->triangles[i]->getVertex(2)->getNum());
+      if (v1->onWhat()->dim() == 2)((GFace*)v1->onWhat())->triangles.push_back(new MTriangle(v1,v2,v3)); 
+      else if (v2->onWhat()->dim() == 2)((GFace*)v2->onWhat())->triangles.push_back(new MTriangle(v1,v2,v3)); 
+      else if (v3->onWhat()->dim() == 2)((GFace*)v3->onWhat())->triangles.push_back(new MTriangle(v1,v2,v3));       
+    }
+    for (int i=0;i<(*it)->quadrangles.size();i++){
+      MVertex *v1 = geom->getMeshVertexByTag((*it)->quadrangles[i]->getVertex(0)->getNum());
+      MVertex *v2 = geom->getMeshVertexByTag((*it)->quadrangles[i]->getVertex(1)->getNum());
+      MVertex *v3 = geom->getMeshVertexByTag((*it)->quadrangles[i]->getVertex(2)->getNum());
+      MVertex *v4 = geom->getMeshVertexByTag((*it)->quadrangles[i]->getVertex(3)->getNum());
+      //      printf("quad %p %p %p %p\n",v1,v2,v3,v4);
+      if (v1->onWhat()->dim() == 2)((GFace*)v1->onWhat())->quadrangles.push_back(new MQuadrangle(v1,v2,v3,v4)); 
+      else if (v2->onWhat()->dim() == 2)((GFace*)v2->onWhat())->quadrangles.push_back(new MQuadrangle(v1,v2,v3,v4)); 
+      else if (v3->onWhat()->dim() == 2)((GFace*)v3->onWhat())->quadrangles.push_back(new MQuadrangle(v1,v2,v3,v4));       
+      else if (v4->onWhat()->dim() == 2)((GFace*)v4->onWhat())->quadrangles.push_back(new MQuadrangle(v1,v2,v3,v4));       
+    }
+  }  
+  geom->writeMSH("hopla.msh",2.2,false,false,true);
+}
+
+
+
 int GeomMeshMatcher::match(GModel *geom, GModel *mesh)
 {
   mesh->createTopologyFromMesh();
diff --git a/Geo/GeomMeshMatcher.h b/Geo/GeomMeshMatcher.h
index 7ec6f51002..a05eff3dd7 100644
--- a/Geo/GeomMeshMatcher.h
+++ b/Geo/GeomMeshMatcher.h
@@ -35,6 +35,8 @@ class GeomMeshMatcher {
   static GeomMeshMatcher *instance();
   static void destroy();
   int match(GModel* geom, GModel* mesh);
+  int forceTomatch(GModel *geom, GModel *mesh, const double TOL);
+
 };
 
 #endif
diff --git a/Geo/MElement.h b/Geo/MElement.h
index b4836a4dc1..534f22c1a4 100644
--- a/Geo/MElement.h
+++ b/Geo/MElement.h
@@ -246,6 +246,16 @@ class MElement
   // parametric coordinates
   double getJacobian(const fullMatrix<double> &gsf, double jac[3][3]);
   double getJacobian(double u, double v, double w, double jac[3][3]);
+  inline double getJacobian(double u, double v, double w, fullMatrix<double> &j){
+    double JAC[3][3];
+    const double detJ = getJacobian (u,v,w,JAC);
+    for (int i=0;i<3;i++){
+      j(i,0) = JAC[i][0];
+      j(i,1) = JAC[i][1];
+      j(i,2) = JAC[i][2];
+    }
+    return detJ;
+  }
   double getPrimaryJacobian(double u, double v, double w, double jac[3][3]);
   double getJacobianDeterminant(double u, double v, double w)
   {
diff --git a/Mesh/CMakeLists.txt b/Mesh/CMakeLists.txt
index 07ef9fcd35..22aeb98b05 100644
--- a/Mesh/CMakeLists.txt
+++ b/Mesh/CMakeLists.txt
@@ -23,6 +23,7 @@ set(SRC
     BDS.cpp 
     HighOrder.cpp 
       highOrderSmoother.cpp 
+      highOrderTools.cpp 
     meshPartition.cpp
     meshPartitionOptions.cpp
     meshRefine.cpp
diff --git a/Mesh/HighOrder.cpp b/Mesh/HighOrder.cpp
index b34adb5348..e03cc759a8 100644
--- a/Mesh/HighOrder.cpp
+++ b/Mesh/HighOrder.cpp
@@ -9,6 +9,7 @@
 
 #include "HighOrder.h"
 #include "highOrderSmoother.h"
+#include "highOrderTools.h"
 #include "MLine.h"
 #include "MTriangle.h"
 #include "MQuadrangle.h"
@@ -256,7 +257,7 @@ static void getEdgeVertices(GEdge *ge, MElement *ele, std::vector<MVertex*> &ve,
         if(reparamOK){
           double relax = 1.;
           while (1){
-            if(computeEquidistantParameters(ge, u0, u1, nPts + 2, US, relax)) 
+            if(computeEquidistantParameters(ge, std::min(u0,u1), std::max(u0,u1), nPts + 2, US, relax)) 
                 break;
             relax /= 2.0;
             if(relax < 1.e-2) 
@@ -264,8 +265,8 @@ static void getEdgeVertices(GEdge *ge, MElement *ele, std::vector<MVertex*> &ve,
           } 
           if(relax < 1.e-2)
             Msg::Warning
-              ("Failed to compute equidistant parameters (relax = %g) for edge %d-%d",
-               relax, v0->getNum(), v1->getNum());
+              ("Failed to compute equidistant parameters (relax = %g, value = %g) for edge %d-%d parametrized with %g %g on GEdge %d",
+               relax, US[1], v0->getNum(), v1->getNum(),u0,u1,ge->tag());
         }
       }
       for(int j = 0; j < nPts; j++){
@@ -273,7 +274,7 @@ static void getEdgeVertices(GEdge *ge, MElement *ele, std::vector<MVertex*> &ve,
         
         double uc = (1. - t) * u0 + t * u1; // can be wrong, that's ok
         MVertex *v;
-        if(linear || !reparamOK || uc < u0 || uc > u1){ 
+        if(linear || !reparamOK || uc < std::min(u0,u1) || uc > std::max(u0,u1)){ 
           Msg::Warning("We don't have a valid parameter on curve %d-%d",
                        v0->getNum(), v1->getNum());
           // we don't have a (valid) parameter on the curve
@@ -281,8 +282,8 @@ static void getEdgeVertices(GEdge *ge, MElement *ele, std::vector<MVertex*> &ve,
           v = new MVertex(pc.x(), pc.y(), pc.z(), ge);
         }
         else {          
-          GPoint pc = ge->point(US[j + 1]);
-          v = new MEdgeVertex(pc.x(), pc.y(), pc.z(), ge, US[j + 1]);
+          GPoint pc = ge->point(US[u0<u1? j + 1 : nPts - 1 - (j + 1)]);
+          v = new MEdgeVertex(pc.x(), pc.y(), pc.z(), ge, US[u0<u1? j + 1 : nPts - 1 - (j + 1)]);
             
           if (displ2D || displ3D){
             SPoint3 pc2 = edge.interpolate(t);          
@@ -1212,10 +1213,10 @@ void checkHighOrderTriangles(const char* cc, GModel *m,
     }
   }
   if (minJGlob > 0) 
-    Msg::Info("%s : Worst Face Smoothness %g Gamma %g NbFair = %d", 
+    Msg::Info("%s : Worst Face Distorsion Mapping %g Gamma %g Nb elem. (0<d<0.2) = %d", 
               cc, minJGlob, minGGlob,nbfair );
     else
-    Msg::Warning("%s : Worst Face Smoothness %g (%d negative jacobians) "
+    Msg::Warning("%s : Worst Face Distorsion Mapping %g (%d negative jacobians) "
                  "Worst Gamma %g Avg Smoothness %g", cc, minJGlob, bad.size(),
                  minGGlob, avg / (count ? count : 1));
 }
@@ -1252,7 +1253,7 @@ static void checkHighOrderTetrahedron(const char* cc, GModel *m,
 
 extern double mesh_functional_distorsion(MElement *t, double u, double v);
 
-static void printJacobians(GModel *m, const char *nm)
+void printJacobians(GModel *m, const char *nm)
 {
   const int n = 100;
   double D[n][n], X[n][n], Y[n][n], Z[n][n];
@@ -1380,5 +1381,10 @@ void SetOrderN(GModel *m, int order, bool linear, bool incomplete)
   //  printJacobians(m, "smoothness.pos");
   
   double t2 = Cpu();
+
+  highOrderTools hot (m);
+  hot.ensureMinimumDistorsion(0.1);
+  checkHighOrderTriangles("final mesh", m, bad, worst);
+
   Msg::StatusBar(2, true, "Done meshing order %d (%g s)", order, t2 - t1);
 }
diff --git a/Mesh/highOrderSmoother.cpp b/Mesh/highOrderSmoother.cpp
index 6992b2f64b..42f21fcbf2 100644
--- a/Mesh/highOrderSmoother.cpp
+++ b/Mesh/highOrderSmoother.cpp
@@ -483,7 +483,7 @@ void highOrderSmoother::smooth_metric(std::vector<MElement*>  & all, GFace *gf)
   std::vector<MElement*> layer, v;
   double minD;
   
-  getDistordedElements(all, 0.5, v, minD);
+  getDistordedElements(all, 0.99, v, minD);
 
   const int nbLayers = 3; //2, originally :)
   for (int i = 0; i < nbLayers; i++){
@@ -591,11 +591,6 @@ double highOrderSmoother::smooth_metric_(std::vector<MElement*>  & v,
 
   if (myAssembler.sizeOfR()){
 
-    // initialize _materialLaw to 1 everywhere
-    for (unsigned int i = 0; i < v.size(); i++){
-      MElement *e = v[i];
-      //      _materialLaw[e] = 1.0;
-    }
     // while convergence -------------------------------------------------------
     for (unsigned int i = 0; i < v.size(); i++){
       MElement *e = v[i];
@@ -612,8 +607,6 @@ double highOrderSmoother::smooth_metric_(std::vector<MElement*>  & v,
       fullMatrix<double> J23K33(n2, n3);
       K33.setAll(0.0);
       SElement se(e);
-      // set kappa to the actual kappa of the material law
-      //      El.setCompressibility( compressibilityFunction ( _materialLaw[e] ) );
       El.elementMatrix(&se, K33);
       computeMetricVector(gf, e, J32, J23, D3);
       J23K33.gemm(J23, K33, 1, 0);
@@ -861,7 +854,7 @@ void highOrderSmoother::smooth(std::vector<MElement*> &all)
   std::vector<MElement*> layer, v;
   double minD;
 
-  getDistordedElements(all, 0.5, v, minD);
+  getDistordedElements(all, 0.85, v, minD);
 
   Msg::Info("%d elements / %d distorted  min Disto = %g\n",
              all.size(), v.size(), minD);
@@ -1756,5 +1749,71 @@ void  highOrderSmoother::smooth_pNpoint(GFace *gf)
   findOptimalLocationsPN(gf,this);
 }
 
+//-------------------------------------------------
+// Assume a GModel that is meshed with high order nodes that
+// are on their final position (at least they all lie on
+// surfaces and curves and the smoother may move them)
+//-------------------------------------------------
+
+highOrderSmoother::highOrderSmoother (GModel *gm)
+  : _tag(111)
+{
+  _clean();
+  // compute straigh sided positions that are actually X,Y,Z positions
+  // that are NOT always on curves and surfaces 
+
+  // compute stright sided positions of vertices that are classified on model edges
+  for(GModel::eiter it = gm->firstEdge(); it != gm->lastEdge(); ++it){    
+    for (int i=0;i<(*it)->lines.size();i++){
+      MLine *l = (*it)->lines[i];
+      int N = l->getNumVertices()-2;
+      SVector3 p0((*it)->lines[i]->getVertex(0)->x(),
+		  (*it)->lines[i]->getVertex(0)->y(),
+		  (*it)->lines[i]->getVertex(0)->z());
+      SVector3 p1((*it)->lines[i]->getVertex(1)->x(),
+		  (*it)->lines[i]->getVertex(1)->y(),
+		  (*it)->lines[i]->getVertex(1)->z());
+      for (int j=1;j<=N;j++){
+	const double xi = (double)j/(N+1);
+	const SVector3 straightSidedPoint = p0 *(1.-xi) + p1*xi;
+	_straightSidedLocation [(*it)->lines[i]->getVertex(j)] = straightSidedPoint;
+      }
+    }
+  }
+
+  // compute stright sided positions of vertices that are classified on model faces
+  for(GModel::fiter it = gm->firstFace(); it != gm->lastFace(); ++it){
+    for (int i=0;i<(*it)->triangles.size();i++){
+      MTriangle *t = (*it)->triangles[i];
+      MFace face = t->getFace(0);
+      const polynomialBasis* fs = t->getFunctionSpace();
+      for (int j=0;j<t->getNumVertices();j++){
+	if (t->getVertex(j)->onWhat() == *it){
+	  const double t1 = fs->points(j, 0);
+	  const double t2 = fs->points(j, 1);
+	  SPoint3 pc = face.interpolate(t1, t2);
+	  _straightSidedLocation [t->getVertex(j)] = 
+	    SVector3(pc.x(),pc.y(),pc.z()); 
+	}
+      }
+    }
+    for (int i=0;i<(*it)->quadrangles.size();i++){
+      MQuadrangle *q = (*it)->quadrangles[i];
+      MFace face = q->getFace(0);
+      const polynomialBasis* fs = q->getFunctionSpace();
+      for (int j=0;j<q->getNumVertices();j++){
+	if (q->getVertex(j)->onWhat() == *it){
+	  const double t1 = fs->points(j, 0);
+	  const double t2 = fs->points(j, 1);
+	  SPoint3 pc = face.interpolate(t1, t2);
+	  _straightSidedLocation [q->getVertex(j)] = 
+	    SVector3(pc.x(),pc.y(),pc.z()); 
+	}
+      }
+    }
+  }
+}
+
+
 #endif
 
diff --git a/Mesh/highOrderSmoother.h b/Mesh/highOrderSmoother.h
index ef1303ef89..3c5439c822 100644
--- a/Mesh/highOrderSmoother.h
+++ b/Mesh/highOrderSmoother.h
@@ -26,7 +26,6 @@ class GRegion;
 class highOrderSmoother 
 {
   const int _tag;
-  std::map<MElement*,std::vector<double> > _materialLaw;
   std::map<MVertex*,SVector3> _straightSidedLocation;
   std::map<MVertex*,SVector3> _targetLocation;
   int _dim;
diff --git a/Mesh/highOrderTools.cpp b/Mesh/highOrderTools.cpp
new file mode 100644
index 0000000000..5c8df2bf57
--- /dev/null
+++ b/Mesh/highOrderTools.cpp
@@ -0,0 +1,668 @@
+// Gmsh - Copyright (C) 1997-2010 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+//
+// Contributor(s):
+//   Koen Hillewaert
+//
+
+#include "GmshConfig.h"
+
+#if defined(HAVE_SOLVER)
+
+#include "MLine.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+#include "MTetrahedron.h"
+#include "MHexahedron.h"
+#include "MPrism.h"
+#include "MPyramid.h"
+#include "MPoint.h"
+#include "HighOrder.h"
+#include "meshGFaceOptimize.h"
+#include "highOrderTools.h"
+#include "GFace.h"
+#include "GRegion.h"
+#include "GeomMeshMatcher.h"
+#include "Context.h"
+#include "Numeric.h"
+#include "dofManager.h"
+#include "elasticityTerm.h"
+#include "linearSystemCSR.h"
+#include "linearSystemFull.h"
+#include "linearSystemPETSc.h"
+
+#define SQU(a)      ((a)*(a))
+
+void highOrderTools::moveToStraightSidedLocation(MElement *e) const
+{
+  for(int i = 0; i < e->getNumVertices(); i++){
+    MVertex *v = e->getVertex(i);
+    std::map<MVertex*,SVector3>::const_iterator it = _straightSidedLocation.find(v);
+    if (it != _straightSidedLocation.end()){
+      //      printf("move %d : %g %g -> %g %g\n",v->getNum(),v->x() ,v->y() ,  it->second.x() ,  it->second.y() );
+      v->x() = it->second.x();
+      v->y() = it->second.y();
+      v->z() = it->second.z();
+    }
+  } 
+}
+
+void highOrderTools::ensureMinimumDistorsion(MElement *e, double threshold)
+{
+  double dist = e->distoShapeMeasure();
+  if (dist > threshold) return;
+  double a1 = 0., a2 = 1.0, x[1000][3], X[1000][3] ;
+  for(int i = 0; i < e->getNumVertices(); i++){
+    MVertex *v = e->getVertex(i);
+    x[i][0] = v->x();
+    x[i][1] = v->y();
+    x[i][2] = v->z();
+    std::map<MVertex*,SVector3>::const_iterator it = _straightSidedLocation.find(v);
+    if (it != _straightSidedLocation.end()){
+      X[i][0] = it->second.x();
+      X[i][1] = it->second.y();
+      X[i][2] = it->second.z();
+    }
+    else {
+      X[i][0] = v->x();
+      X[i][1] = v->y();
+      X[i][2] = v->z();
+    }
+  }
+  // a == 0 -> straight
+  // a == 1 -> curved
+  while(1){
+    double a = 0.5*(a1+a2);
+    for(int i = 0; i < e->getNumVertices(); i++){
+      MVertex *v = e->getVertex(i);
+      v->x() = a * x[i][0] + (1.-a) * X[i][0]; 
+      v->y() = a * x[i][1] + (1.-a) * X[i][1]; 
+      v->z() = a * x[i][2] + (1.-a) * X[i][2]; 
+    } 
+    double dist = e->distoShapeMeasure();
+    //    printf("a = %g dist = %g\n",a,dist);
+    //    getchar();
+    if (dist > 0 && fabs(dist-threshold) < .05)break;
+    if (dist < threshold)a2 = a;
+    else a1 = a;    
+  }
+  //  printf("element done\n");
+}
+
+
+static void getDistordedElements(const std::vector<MElement*> &v,
+				 const double & threshold,
+				 std::vector<MElement*> &d,
+				 double &minD)
+{
+  d.clear();
+  minD = 1;
+  for (unsigned int i = 0; i < v.size(); i++){
+    const double disto = v[i]->distoShapeMeasure();
+    if (disto < threshold)
+      d.push_back(v[i]);
+    minD = std::min(minD, disto);
+  }
+}
+
+static void addOneLayer(const std::vector<MElement*> &v, 
+                 std::vector<MElement*> &d,
+                 std::vector<MElement*> &layer)
+{
+  std::set<MVertex*> all;
+  for (unsigned int i = 0; i < d.size(); i++){
+    MElement *e = d[i];
+    int n = e->getNumPrimaryVertices();
+    for (int j = 0; j < n; j++){
+      all.insert(e->getVertex(j));
+    }
+  }
+  layer.clear();  
+  std::sort(d.begin(), d.end());
+
+  for (unsigned int i = 0; i < v.size(); i++){
+    MElement *e = v[i];
+    bool found = std::binary_search(d.begin(), d.end(), e);
+    // element is not yet there
+    if (!found){
+      int n = e->getNumPrimaryVertices();
+      for (int j = 0; j < n; j++){
+        MVertex *vert = e->getVertex(j);
+        if (all.find(vert) != all.end()){
+          layer.push_back(e);
+          j = n;
+        }
+      }
+    }
+  }
+}
+
+
+double highOrderTools::applySmoothingTo(GFace *gf, double tres, bool mixed)
+{
+  if (!gf){
+    Msg::Error("Cannot smooth that face");
+    return -1;
+  }
+  std::vector<MElement*> v;
+  v.insert(v.begin(), gf->triangles.begin(),gf->triangles.end());
+  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());
+  //  applySmoothingTo(v,gf);
+  return applySmoothingTo(v,tres, mixed);
+}
+
+
+void highOrderTools::ensureMinimumDistorsion (double threshold){
+  std::vector<MElement*> v;
+  if (_dim == 2){
+    for (GModel::fiter fit = _gm->firstFace(); fit != _gm->lastFace(); ++fit) {
+      v.insert(v.begin(), (*fit)->triangles.begin(),(*fit)->triangles.end());
+      v.insert(v.begin(), (*fit)->quadrangles.begin(),(*fit)->quadrangles.end());      
+    }
+  }
+  else if (_dim == 3){
+    for (GModel::riter rit = _gm->firstRegion(); rit != _gm->lastRegion(); ++rit) {
+      v.insert(v.begin(), (*rit)->hexahedra.begin(),(*rit)->hexahedra.end());
+      v.insert(v.begin(), (*rit)->tetrahedra.begin(),(*rit)->tetrahedra.end());
+      v.insert(v.begin(), (*rit)->prisms.begin(),(*rit)->prisms.end());
+    }
+  }
+  ensureMinimumDistorsion(v,threshold);
+}
+
+
+/*
+void highOrderTools::applySmoothingTo(GRegion *gr)
+{
+  std::vector<MElement*> v;
+  v.insert(v.begin(), gr->tetrahedra.begin(),gr->tetrahedra.end());
+  v.insert(v.begin(), gr->hexahedra.begin(),gr->hexahedra.end());
+  v.insert(v.begin(), gr->prisms.begin(),gr->prisms.end());
+  Msg::Info("Smoothing high order mesh : model region %d (%d elements)", gr->tag(),
+            v.size());
+  applySmoothingTo(v,gr);
+}
+*/
+
+void highOrderTools::computeMetricInfo(GFace *gf, 
+				       MElement *e, 
+				       fullMatrix<double> &J,
+				       fullMatrix<double> &JT,
+				       fullVector<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 highOrderTools::applySmoothingTo(std::vector<MElement*> & all, GFace *gf)
+{
+#ifdef HAVE_TAUCS
+  linearSystemCSRTaucs<double> *lsys = new linearSystemCSRTaucs<double>;
+#else
+  linearSystemPETSc<double> *lsys = new  linearSystemPETSc<double>;    
+#endif
+
+  dofManager<double> myAssembler(lsys);
+  elasticityTerm El(0, 1.0, .33, _tag);  
+  std::vector<MElement*> layer, v;
+  double minD;  
+  getDistordedElements(all, 0.5, v, minD);
+  const int nbLayers = 3;
+  for (int i = 0; i < nbLayers; i++){
+    addOneLayer(all, v, layer);
+    v.insert(v.end(), layer.begin(), layer.end());
+  }
+  Msg::Debug("%d elements after adding %d layers", (int)v.size(), nbLayers);
+
+  if (!v.size()) return;
+  addOneLayer(all, v, layer);
+  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;
+
+  // on the last layer, fix displacement to 0
+  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, _tag, 0);
+      myAssembler.fixVertex(vert, 1, _tag, 0);
+    }
+  }
+
+  // fix all vertices that cannot move
+  for (unsigned int i = 0; i < v.size(); i++){
+    for (int j = 0; j < v[i]->getNumVertices(); j++){
+      MVertex *vert = v[i]->getVertex(j);
+      if (vert->onWhat()->dim() < 2){
+	myAssembler.fixVertex(vert, 0, _tag, 0);
+	myAssembler.fixVertex(vert, 1, _tag, 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, _tag);
+      myAssembler.numberVertex(vert, 1, _tag);
+      verticesToMove.insert(vert);
+    } 
+  }
+  
+  double dx0 = smooth_metric_(v, gf, myAssembler, verticesToMove, El);
+  double dx = dx0;
+  Msg::Debug(" dx0 = %12.5E", dx0);
+  int iter = 0;
+  while(1){
+    double dx2 = smooth_metric_(v, gf, myAssembler, verticesToMove, El);
+    Msg::Debug(" dx2  = %12.5E", dx2);
+    if (fabs(dx2 - dx) < 1.e-4 * dx0)break;
+    if (iter++ > 2)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 highOrderTools::smooth_metric_(std::vector<MElement*>  & v, 
+				      GFace *gf, 
+				      dofManager<double> &myAssembler,
+				      std::set<MVertex*> &verticesToMove,
+				      elasticityTerm &El)
+{
+  std::set<MVertex*>::iterator it;
+  double dx = 0.0;
+
+  //  printf("size %d\n",myAssembler.sizeOfR());
+
+  if (myAssembler.sizeOfR()){
+
+    // while convergence -------------------------------------------------------
+    for (unsigned 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;
+
+      fullMatrix<double> K33(n3, n3);
+      fullMatrix<double> K22(n2, n2);
+      fullMatrix<double> J32(n3, n2);
+      fullMatrix<double> J23(n2, n3);
+      fullVector<double> D3(n3);
+      fullVector<double> R2(n2);
+      fullMatrix<double> J23K33(n2, n3);
+      K33.setAll(0.0);
+      SElement se(e);
+      El.elementMatrix(&se, K33);
+      computeMetricInfo(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++){
+        Dof RDOF = El.getLocalDofR(&se, j);
+        myAssembler.assemble(RDOF, -R2(j));
+        for (int k = 0; k < n2; k++){
+          Dof CDOF = El.getLocalDofC(&se, k);
+          myAssembler.assemble(RDOF, CDOF, K22(j, k));
+        }
+      }
+    }
+    myAssembler.systemSolve();
+    // for all element, compute detJ at integration points --> material law
+    // end while convergence -------------------------------------------------------
+  
+    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, _tag, dparam[0]);
+	myAssembler.getDofValue((*it), 1, _tag, dparam[1]);
+	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;
+}
+
+
+highOrderTools::highOrderTools (GModel *gm) : _gm(gm), _dim(2), _tag(111)
+{
+  computeStraightSidedPositions(); 
+}
+
+highOrderTools::highOrderTools (GModel *gm, GModel *mesh, int order) : _gm(gm), _dim(2), _tag(111)
+{
+  GeomMeshMatcher::instance()->forceTomatch(gm,mesh,1.e-5);
+  GeomMeshMatcher::instance()->destroy();
+  SetOrderN(gm, order, false, false);
+  computeStraightSidedPositions();   
+}
+
+
+void highOrderTools::computeStraightSidedPositions ()
+{
+  _clean();
+  // compute straigh sided positions that are actually X,Y,Z positions
+  // that are NOT always on curves and surfaces 
+
+  // points classified on model vertices shall not move !
+  for(GModel::viter it = _gm->firstVertex(); it != _gm->lastVertex(); ++it){    
+    if ((*it)->points.size()){
+      MPoint *p = (*it)->points[0];
+      MVertex *v = p->getVertex(0);
+      _straightSidedLocation [v] = SVector3((*it)->x(),(*it)->y(),(*it)->z());
+      _targetLocation [v] = SVector3((*it)->x(),(*it)->y(),(*it)->z());    
+    }
+  }
+
+  //  printf("coucou2\n");
+  // compute stright sided positions of vertices that are classified on model edges
+  for(GModel::eiter it = _gm->firstEdge(); it != _gm->lastEdge(); ++it){    
+    for (int i=0;i<(*it)->lines.size();i++){
+      MLine *l = (*it)->lines[i];
+      int N = l->getNumVertices()-2;
+      SVector3 p0((*it)->lines[i]->getVertex(0)->x(),
+		  (*it)->lines[i]->getVertex(0)->y(),
+		  (*it)->lines[i]->getVertex(0)->z());
+      SVector3 p1((*it)->lines[i]->getVertex(1)->x(),
+		  (*it)->lines[i]->getVertex(1)->y(),
+		  (*it)->lines[i]->getVertex(1)->z());
+      for (int j=1;j<=N;j++){
+	const double xi = (double)j/(N+1);
+	//	printf("xi = %g\n",xi);
+	const SVector3 straightSidedPoint = p0 *(1.-xi) + p1*xi;
+	MVertex *v = (*it)->lines[i]->getVertex(j+1);
+	if (_straightSidedLocation.find(v) == _straightSidedLocation.end()){	  
+	  _straightSidedLocation [v] = straightSidedPoint;
+	  _targetLocation[v] = SVector3(v->x(),v->y(),v->z());
+	}
+      }
+    }
+  }
+
+  //  printf("coucou3\n");
+  // compute stright sided positions of vertices that are classified on model faces
+  for(GModel::fiter it = _gm->firstFace(); it != _gm->lastFace(); ++it){
+    for (int i=0;i<(*it)->mesh_vertices.size();i++){
+      MVertex *v = (*it)->mesh_vertices[i];
+      _targetLocation[v] = SVector3(v->x(),v->y(),v->z());      
+    }
+
+    for (int i=0;i<(*it)->triangles.size();i++){
+      MTriangle *t = (*it)->triangles[i];
+      MFace face = t->getFace(0);
+      const polynomialBasis* fs = t->getFunctionSpace();
+      for (int j=0;j<t->getNumVertices();j++){
+	if (t->getVertex(j)->onWhat() == *it){
+	  const double t1 = fs->points(j, 0);
+	  const double t2 = fs->points(j, 1);
+	  SPoint3 pc = face.interpolate(t1, t2);
+	  _straightSidedLocation [t->getVertex(j)] = 
+	    SVector3(pc.x(),pc.y(),pc.z()); 
+	}
+      }
+    }
+    for (int i=0;i<(*it)->quadrangles.size();i++){
+      //      printf("coucou quad %d\n",i);
+      MQuadrangle *q = (*it)->quadrangles[i];
+      MFace face = q->getFace(0);
+      const polynomialBasis* fs = q->getFunctionSpace();
+      for (int j=0;j<q->getNumVertices();j++){
+	if (q->getVertex(j)->onWhat() == *it){
+	  const double t1 = fs->points(j, 0);
+	  const double t2 = fs->points(j, 1);
+	  SPoint3 pc = face.interpolate(t1, t2);
+	  _straightSidedLocation [q->getVertex(j)] = 
+	    SVector3(pc.x(),pc.y(),pc.z()); 
+	}
+      }
+    }
+  }
+
+  Msg::Info("highOrderTools has been set up : %d nodes are considered",_straightSidedLocation.size());
+}
+
+
+// apply a displacement that does not create elements that are
+// distorted over a value "thres"
+double highOrderTools::apply_incremental_displacement (double max_incr,
+						       std::vector<MElement*> & v,
+						       bool mixed,
+						       double thres,
+						       char *meshName,
+						       std::vector<MElement*> & disto)
+{
+#ifdef HAVE_PETSC
+  // assume that the mesh is OK, yet already curved
+  linearSystemPETSc<double> *lsys = new  linearSystemPETSc<double>;    
+  lsys->setParameter("petscOptions","-pc_type ilu");
+
+  dofManager<double> myAssembler(lsys);
+  elasticityMixedTerm El_mixed (0, 1.0, .333, _tag);
+  elasticityTerm El (0, 1.0, .333, _tag);
+
+  std::set<MVertex*> _vertices;
+
+  //+++++++++ Boundary Conditions & Numbering +++++++++++++++++++++++++++++++
+  // fix all dof that correspond to vertices on the boundary 
+  // the value is equal 
+  for (unsigned int i = 0; i < v.size(); i++){
+    for (int j = 0; j < v[i]->getNumVertices(); j++){
+      MVertex *vert = v[i]->getVertex(j);
+      _vertices.insert(vert);
+    }
+  }
+
+  //+++++++++ Fix d tr(eps) = 0 +++++++++++++++++++++++++++++++
+  if (mixed){
+    for (unsigned int i = 0; i < disto.size(); i++){
+      for (int j = 0; j < disto[i]->getNumVertices(); j++){
+	MVertex *vert = disto[i]->getVertex(j);
+	myAssembler.fixVertex(vert, 4, _tag, 0.0);
+      }
+    }
+  }
+
+  for (std::set<MVertex*>::iterator it = _vertices.begin(); it != _vertices.end(); ++it){
+    MVertex *vert = *it;
+    std::map<MVertex*,SVector3>::iterator itt = _targetLocation.find(vert);
+    // impose displacement @ boundary
+    //    if (!(vert->onWhat()->geomType()  == GEntity::Line 
+    //	  && vert->onWhat()->tag()  == 2)){
+      if (itt != _targetLocation.end() && vert->onWhat()->dim() < _dim){
+	//		printf("fixing motion of vertex %d to %g %g\n",vert->getNum(),itt->second.x()-vert->x(),itt->second.y()-vert->y());
+	myAssembler.fixVertex(vert, 0, _tag, itt->second.x()-vert->x());
+	myAssembler.fixVertex(vert, 1, _tag, itt->second.y()-vert->y());
+	myAssembler.fixVertex(vert, 2, _tag, itt->second.z()-vert->z());
+      }
+      // ensure we do not touch any vertex that is on the boundary
+      else if (vert->onWhat()->dim() < _dim){
+	myAssembler.fixVertex(vert, 0, _tag, 0);
+	myAssembler.fixVertex(vert, 1, _tag, 0);
+	myAssembler.fixVertex(vert, 2, _tag, 0);
+      }
+      //    }
+    if (_dim == 2)myAssembler.fixVertex(vert, 2, _tag, 0);
+    // number vertices 
+    myAssembler.numberVertex(vert, 0, _tag);
+    myAssembler.numberVertex(vert, 1, _tag);
+    myAssembler.numberVertex(vert, 2, _tag);
+    if (mixed){
+      myAssembler.numberVertex(vert, 3, _tag);
+      myAssembler.numberVertex(vert, 4, _tag);
+    }
+  }
+
+  //+++++++++ Assembly  & Solve ++++++++++++++++++++++++++++++++++++
+  if (myAssembler.sizeOfR()){
+    // assembly of the elasticity term on the
+    for (unsigned int i = 0; i < v.size(); i++){
+      SElement se(v[i]);
+      if (mixed)El_mixed.addToMatrix(myAssembler, &se);
+      else El.addToMatrix(myAssembler, &se);
+    }
+    // solve the system
+    lsys->systemSolve();
+  }
+  
+  //+++++++++ Move vertices @ maximum ++++++++++++++++++++++++++++++++++++
+  FILE *fd = fopen ("d.msh","w");
+  fprintf(fd,"$MeshFormat\n2 0 8\n$EndMeshFormat\n$NodeData\n1\n\"tr(sigma)\"\n1\n0.0\n3\n1\n3\n%d\n",_vertices.size());
+  for (std::set<MVertex*>::iterator it = _vertices.begin(); it != _vertices.end(); ++it){
+    double ax, ay, az;
+    myAssembler.getDofValue(*it, 0, _tag, ax);
+    myAssembler.getDofValue(*it, 1, _tag, ay);
+    myAssembler.getDofValue(*it, 2, _tag, az);    
+    (*it)->x() += max_incr*ax;
+    (*it)->y() += max_incr*ay;
+    (*it)->z() += max_incr*az;
+    fprintf(fd,"%d %g %g %g\n",(*it)->getIndex(), ax,ay,az);
+  }
+  fprintf(fd,"$EndNodeData\n");
+  fclose(fd);
+
+  //+------------------- Check now if elements are ok ---------------+++++++
+
+  (*_vertices.begin())->onWhat()->model()->writeMSH(meshName);
+
+  double percentage = max_incr * 100.;
+  while(1){
+    std::vector<MElement*> disto;
+    double minD;
+    getDistordedElements(v, 0.5, disto, minD);    
+    if (minD < thres){
+      percentage -= 10.;
+      for (std::set<MVertex*>::iterator it = _vertices.begin(); it != _vertices.end(); ++it){
+	double ax, ay, az;
+	myAssembler.getDofValue(*it, 0, _tag, ax);
+	myAssembler.getDofValue(*it, 1, _tag, ay);
+	myAssembler.getDofValue(*it, 2, _tag, az);    
+	(*it)->x() -= .1*ax;
+	(*it)->y() -= .1*ay;
+	(*it)->z() -= .1*az;
+      }
+    }
+    else break;
+  }
+    
+  delete lsys;
+  return percentage;
+#endif
+  return 0.0;
+}
+
+// uncurve elements that are invalid
+void highOrderTools::ensureMinimumDistorsion (std::vector<MElement*> &all, 
+					      double threshold){
+  while(1){
+    double minD;
+    std::vector<MElement*> disto;
+    getDistordedElements(all, threshold, disto, minD);    
+    if (!disto.size())break;
+    Msg::Info("fixing %d bad curved elements (worst disto %g)",disto.size(),minD);
+    for (int i=0;i<disto.size();i++){
+      ensureMinimumDistorsion(disto[i],threshold);
+    } 
+  }
+}
+
+double highOrderTools::applySmoothingTo (std::vector<MElement*> &all, 
+					 double threshold, bool mixed)
+{
+  int ITER = 0;
+  double minD, FACT = 1.0;
+  std::vector<MElement*> disto;
+  // move the points to their straight sided locations
+  for (unsigned int i = 0; i < all.size(); i++)
+    moveToStraightSidedLocation(all[i]);
+  // apply the displacement
+
+  _gm->writeMSH("straightSided.msh");
+
+
+  double percentage_of_what_is_left = apply_incremental_displacement (1.,all, mixed, -10000000000 ,"sm.msh",all);
+  ensureMinimumDistorsion (all,threshold);
+  return 1.;
+
+  double percentage = 0.0;
+  while(1){
+    char NN[256];
+    sprintf(NN,"smoothing-%d.msh",ITER++);
+    double percentage_of_what_is_left = apply_incremental_displacement (1.,all, mixed, threshold,NN,all);
+    percentage += (1.-percentage) * percentage_of_what_is_left/100.;
+    Msg::Info("The smoother was able to do %3d percent of the motion",(int)(percentage*100.));
+    if (percentage_of_what_is_left == 0.0) break;
+    else if (percentage_of_what_is_left == 100.)break;
+  }
+
+  getDistordedElements(all, 0.3, disto, minD);    
+  Msg::Info("iter %d : %d elements / %d distorted  min Disto = %g ",ITER,
+	    all.size(), disto.size(), minD);       
+  return percentage;
+}
+
+extern void printJacobians(GModel *m, const char *nm);
+void highOrderTools::makePosViewWithJacobians (const char *fn){
+  printJacobians(_gm,fn);
+}
+
+#endif
+
diff --git a/Mesh/highOrderTools.h b/Mesh/highOrderTools.h
new file mode 100644
index 0000000000..a740079138
--- /dev/null
+++ b/Mesh/highOrderTools.h
@@ -0,0 +1,92 @@
+// Gmsh - Copyright (C) 1997-2010 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#ifndef _HIGH_ORDER_TOOLS_H_
+#define _HIGH_ORDER_TOOLS_H_
+
+#include <map>
+#include <vector>
+#include "GmshConfig.h"
+#include "GmshMessage.h"
+
+#if defined(HAVE_SOLVER)
+
+#include "SVector3.h"
+#include "fullMatrix.h"
+#include "dofManager.h"
+#include "elasticityTerm.h"
+
+class MVertex;
+class MElement;
+class GFace;
+class GRegion;
+
+class highOrderTools
+{
+  GModel *_gm;
+  const int _tag;
+  // contains the position of vertices in a straight sided mesh
+  std::map<MVertex*,SVector3> _straightSidedLocation;
+  // contains the position of vertices in the best curvilinear mesh
+  // available 
+  std::map<MVertex*,SVector3> _targetLocation;
+  int _dim;
+  void _clean()
+  {
+    _straightSidedLocation.clear();
+    _targetLocation.clear();
+  }
+  double smooth_metric_(std::vector<MElement*>  & v, 
+			GFace *gf, 
+			dofManager<double> &myAssembler,
+			std::set<MVertex*> &verticesToMove,
+			elasticityTerm &El);
+  void computeMetricInfo(GFace *gf, 
+			 MElement *e, 
+			 fullMatrix<double> &J,
+			 fullMatrix<double> &JT,
+			 fullVector<double> &D);
+  double apply_incremental_displacement (double max_incr,
+					 std::vector<MElement*> & v,
+					 bool mixed,
+					 double thres,
+					 char *meshName,
+					 std::vector<MElement*> & disto);
+  void moveToStraightSidedLocation(MElement *) const;
+  void computeStraightSidedPositions ();
+ public:  
+  highOrderTools(GModel *gm);
+  highOrderTools(GModel *gm, GModel *mesh, int order);
+  //  void applyGlobalSmoothing ();
+  void ensureMinimumDistorsion(MElement *e, double threshold);
+  void ensureMinimumDistorsion (std::vector<MElement*> &all, double threshold);
+  void ensureMinimumDistorsion (double threshold);
+  double applySmoothingTo (GFace *gf, double tres = 0.1, bool mixed = false);
+  void applySmoothingTo(std::vector<MElement*> & all, GFace *gf);
+  double applySmoothingTo (std::vector<MElement*> &all,  double threshold, bool mixed);
+  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());
+  }
+  void makePosViewWithJacobians(const char *nm);
+};
+
+#else
+
+class highOrderTools
+{
+ public:
+  highOrderTools(GModel *gm);
+  {
+    Msg::Error("Gmsh has to be compiled with solver support to use highOrderSmoother");
+  }
+  void applyGlobalSmoothing (){}
+};
+
+#endif
+
+#endif
diff --git a/Mesh/meshGFaceOptimize.cpp b/Mesh/meshGFaceOptimize.cpp
index bf064ae572..3705a67305 100644
--- a/Mesh/meshGFaceOptimize.cpp
+++ b/Mesh/meshGFaceOptimize.cpp
@@ -231,6 +231,12 @@ void buildEdgeToTriangle(std::vector<MTriangle*> &tris, e2t_cont &adj)
   buildEdgeToElement(tris, adj);
 }
 
+void buildEdgeToElements(std::vector<MElement*> &tris, e2t_cont &adj)
+{
+  adj.clear();
+  buildEdgeToElement(tris, adj);
+}
+
 void buildListOfEdgeAngle(e2t_cont adj, std::vector<edge_angle> &edges_detected,
                           std::vector<edge_angle> &edges_lonly)
 {
diff --git a/Mesh/meshGFaceOptimize.h b/Mesh/meshGFaceOptimize.h
index 98d6b6f17e..988d82a96a 100644
--- a/Mesh/meshGFaceOptimize.h
+++ b/Mesh/meshGFaceOptimize.h
@@ -54,6 +54,8 @@ void buildVertexToTriangle(std::vector<MTriangle*> &, v2t_cont &adj);
 void buildEdgeToTriangle(std::vector<MTriangle*> &, e2t_cont &adj);
 void buildListOfEdgeAngle(e2t_cont adj, std::vector<edge_angle> &edges_detected,
                           std::vector<edge_angle> &edges_lonly);
+void buildEdgeToElements(std::vector<MElement*> &tris, e2t_cont &adj);
+
 void laplaceSmoothing(GFace *gf, int niter=1);
 void edgeSwappingLawson(GFace *gf);
 
diff --git a/Mesh/qualityMeasures.cpp b/Mesh/qualityMeasures.cpp
index bfa67191ca..e4bed315d4 100644
--- a/Mesh/qualityMeasures.cpp
+++ b/Mesh/qualityMeasures.cpp
@@ -212,20 +212,22 @@ double mesh_functional_distorsion(MElement *t, double u, double v)
 
 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 d1 =mesh_functional_distorsion(t,0.0,0.0);
+  double d2 =mesh_functional_distorsion(t,1.0,0.0);
+  double d3 =mesh_functional_distorsion(t,0.0,1.0);
+  double d4 =mesh_functional_distorsion(t,0.5,0.0);
+  double d5 =mesh_functional_distorsion(t,0.5,0.5);
+  double d6 =mesh_functional_distorsion(t,0.0,0.5);
+  double d[6] = {d1,d2,d3,4*d4-d1-d2,4*d5-d2-d3,4*d6-d1-d3};
+  
+  return *std::min_element(d,d+6);
 }
 
 double qmDistorsionOfMapping (MTriangle *e)
 {
   //  return 1.0;
   if (e->getPolynomialOrder() == 1) return 1.0;
-  //  if (e->getPolynomialOrder() == 2) return mesh_functional_distorsion_p2(e);
+  if (e->getPolynomialOrder() == 2) return mesh_functional_distorsion_p2(e);
 
   IntPt *pts;
   int npts;
diff --git a/Numeric/DivideAndConquer.cpp b/Numeric/DivideAndConquer.cpp
index e732bcd9b0..8071ad32d1 100644
--- a/Numeric/DivideAndConquer.cpp
+++ b/Numeric/DivideAndConquer.cpp
@@ -835,7 +835,7 @@ DocRecord::~DocRecord()
     for(int i = 0; i < numPoints; i++)
       delete [] _adjacencies[i].t;
     delete _adjacencies;
-  }
+  }  
 }
 
 void DocRecord::MakeMeshWithPoints()
@@ -853,6 +853,59 @@ void DocRecord::Voronoi()
   ConvertDListToVoronoiData();
 }
 
+void DocRecord::recur_tag_triangles (int iTriangle, 
+				     std::set<int> &taggedTriangles, 
+				     std::map< std::pair<void*,void*>, std::pair<int,int> > & edgesToTriangles){
+
+  if (taggedTriangles.find(iTriangle) != taggedTriangles.end())return;
+  
+  taggedTriangles.insert(iTriangle);
+  
+  int a = triangles[iTriangle].a;
+  int b = triangles[iTriangle].b;
+  int c = triangles[iTriangle].c;
+  PointRecord *p[3] = {&points[a],&points[b],&points[c]};
+  
+  for (int j=0;j<3;j++){
+    if (!lookForBoundaryEdge(p[j]->data,p[(j+1)%3]->data)){
+      std::pair<void*,void*> ab = std::make_pair(std::min(p[j]->data,p[(j+1)%3]->data),std::max(p[j]->data,p[(j+1)%3]->data));
+      std::pair<int,int> & adj = edgesToTriangles[ab];
+      if (iTriangle == adj.first && adj.second != -1)recur_tag_triangles(adj.second, taggedTriangles, edgesToTriangles);
+      else recur_tag_triangles(adj.first, taggedTriangles, edgesToTriangles);
+    }
+  }  
+}
+
+
+std::set<int> DocRecord::tagInterior (double x, double y){
+  std::map< std::pair<void*,void*>, std::pair<int,int> > edgesToTriangles;
+  int iFirst = 1;
+  for (int i=0;i<numTriangles;i++){
+    int a = triangles[i].a;
+    int b = triangles[i].b;
+    int c = triangles[i].c;
+    PointRecord *p[3] = {&points[a],&points[b],&points[c]};
+
+    // TODO : regarde quel triangle contient x y et retiens le dans iFirst;
+
+    for (int j=0;j<3;j++){
+      std::pair<void*,void*> ab = std::make_pair(std::min(p[j]->data,p[(j+1)%3]->data),std::max(p[j]->data,p[(j+1)%3]->data));
+      std::map< std::pair<void*,void*>, std::pair<int,int> > :: iterator it = edgesToTriangles.find(ab);
+      if (it == edgesToTriangles.end()){
+	edgesToTriangles[ab] = std::make_pair(i,-1);
+      }
+      else {
+	it->second.second = i;
+      }
+    }
+  }
+  
+  std::set<int> taggedTriangles;
+  recur_tag_triangles (iFirst, taggedTriangles, edgesToTriangles);  
+  return taggedTriangles;
+}
+
+
 void DocRecord::setPoints(fullMatrix<double> *p)
 { 
   if (numPoints != p->size1())throw;
diff --git a/Numeric/DivideAndConquer.h b/Numeric/DivideAndConquer.h
index 98c6b24622..a04b72cbb2 100644
--- a/Numeric/DivideAndConquer.h
+++ b/Numeric/DivideAndConquer.h
@@ -89,11 +89,26 @@ class DocRecord{
   PointRecord *points;  // points to triangulate
   int numTriangles;     // number of triangles
   Triangle *triangles;  // 2D results
+  std::set<std::pair<void*,void*> > boundaryEdges;
   DocRecord(int n);
   double &x(int i){ return points[i].where.h; } 
   double &y(int i){ return points[i].where.v; } 
   void*  &data(int i){ return points[i].data; } 
-  void setPoints(fullMatrix<double> *p);
+  void addBoundaryEdge (void* p1, void* p2){
+    void *a = (p1 < p2) ? p1 : p2;
+    void *b = (p1 > p2) ? p1 : p2;
+    boundaryEdges.insert(std::make_pair(a,b));
+  }
+  bool lookForBoundaryEdge(void *p1, void* p2){
+    void *a = (p1 < p2) ? p1 : p2;
+    void *b = (p1 > p2) ? p1 : p2;
+    std::set<std::pair<void*,void*> >::iterator it = boundaryEdges.find(std::make_pair(a,b));
+    return it != boundaryEdges.end();
+  } 
+  void recur_tag_triangles (int iTriangle, 
+			    std::set<int> &taggedTriangles, 
+			    std::map< std::pair<void*,void*>, std::pair<int,int> > & edgesToTriangles);
+    void setPoints(fullMatrix<double> *p);
   ~DocRecord();
   void MakeMeshWithPoints();
   void Voronoi ();
@@ -108,6 +123,7 @@ class DocRecord{
   void remove_all();
   void add_point(double,double,GFace*);
   PointNumero *ConvertDlistToArray(DListPeek *dlist, int *n);
+  std::set<int> tagInterior (double x, double y);
 };
 
 void centroidOfOrientedBox(std::vector<SPoint2> &pts,
diff --git a/Solver/elasticityTerm.cpp b/Solver/elasticityTerm.cpp
index 945c7c5f80..dc034b2a29 100644
--- a/Solver/elasticityTerm.cpp
+++ b/Solver/elasticityTerm.cpp
@@ -143,7 +143,7 @@ void elasticityMixedTerm::elementMatrix(SElement *se, fullMatrix<double> &m) con
 {
   setPolynomialBasis(se);
   MElement *e = se->getMeshElement();
-  int integrationOrder = 3 * (e->getPolynomialOrder()) + 2;
+  int integrationOrder = 4 * (e->getPolynomialOrder()) + 2;
   int npts;
   IntPt *GP;
   double jac[3][3];
@@ -158,6 +158,7 @@ void elasticityMixedTerm::elementMatrix(SElement *se, fullMatrix<double> &m) con
   fullMatrix<double> KUG(3 * _sizeN,_sizeM);
   fullMatrix<double> KPG(_sizeM,_sizeM);
   fullMatrix<double> KGG(_sizeM,_sizeM);
+  fullMatrix<double> KPP(_sizeM,_sizeM); // stabilization
 
   double FACT = _E / ((1 + _nu)*(1.-2*_nu));
   double C11 = FACT * (1 - _nu) ;
@@ -192,7 +193,7 @@ void elasticityMixedTerm::elementMatrix(SElement *se, fullMatrix<double> &m) con
     for (int j = 0; j < 6; j++)
       H(i, j) = C[i][j];
 
-  double _dimension = 3.0;
+  double _dimension = 2.0;
 
   for (int i = 0; i < npts; i++){
     const double u = GP[i].pt[0];
@@ -206,33 +207,69 @@ void elasticityMixedTerm::elementMatrix(SElement *se, fullMatrix<double> &m) con
 
     B.setAll(0.);
     BT.setAll(0.);
+
+    const double K = .0000002*weight * detJ * pow (detJ,2/_dimension); // the second detJ is for stabilization
+
+    for (int j = 0; j < _sizeM; j++){
+      for (int k = 0; k < _sizeM; k++){
+	KPP(j, k) += (Grads[j][0] * Grads[k][0] +
+		      Grads[j][1] * Grads[k][1] +
+		      Grads[j][2] * Grads[k][2])* K;
+      }
+    }
+
+    const double K2 = weight * detJ; 
+
+    for (int j = 0; j < _sizeN; j++){
+      for (int k = 0; k < _sizeM; k++){
+	KUP(j+0*_sizeN, k) += Grads[j][0] * SF[k] * K2;
+	KUP(j+1*_sizeN, k) += Grads[j][1] * SF[k] * K2;
+	KUP(j+2*_sizeN, k) += Grads[j][2] * SF[k] * K2;
+      }
+    }
+
+    const double K3 = weight * detJ * _E/(2*(1+_nu)); 
+    /*
+    for (int j = 0; j < _sizeN; j++){
+      for (int k = 0; k < _sizeN; k++){
+	const double fa = (Grads[j][0] * Grads[k][0] +
+			   Grads[j][1] * Grads[k][1] +
+			   Grads[j][2] * Grads[k][2]) * K3;
+	KUU(j+0*_sizeN, k+0*_sizeN) += fa;
+	KUU(j+1*_sizeN, k+1*_sizeN) += fa;
+	KUU(j+2*_sizeN, k+2*_sizeN) += fa;
+      }
+    }
+    */
+
+
     for (int j = 0; j < _sizeN; j++){
 
       //      printf("%g %g %g\n",Grads[j][0],Grads[j][1],Grads[j][2]);
 
       BDILT(j, 0) = BDIL(0, j) = Grads[j][0]/_dimension;
 
-      BT(j, 0) = B(0, j) = Grads[j][0]-Grads[j][0]/_dimension;
-      BT(j, 1) = B(1, j) =            -Grads[j][1]/_dimension;
-      BT(j, 2) = B(2, j) =            -Grads[j][2]/_dimension;
+      BT(j, 0) = B(0, j) = Grads[j][0];//-Grads[j][0]/_dimension;
+      //      BT(j, 1) = B(1, j) =            -Grads[j][1]/_dimension;
+      //      BT(j, 2) = B(2, j) =            -Grads[j][2]/_dimension;
 
       BT(j, 3) = B(3, j) = Grads[j][1];
       BT(j, 4) = B(4, j) = Grads[j][2];
       
       BDILT(j + _sizeN, 0) = BDIL(0, j + _sizeN) = Grads[j][1]/_dimension;
 
-      BT(j + _sizeN, 0) = B(0, j + _sizeN) =            -Grads[j][0]/_dimension;
-      BT(j + _sizeN, 1) = B(1, j + _sizeN) = Grads[j][1]-Grads[j][1]/_dimension;
-      BT(j + _sizeN, 2) = B(2, j + _sizeN) =            -Grads[j][2]/_dimension;
+      //      BT(j + _sizeN, 0) = B(0, j + _sizeN) =            -Grads[j][0]/_dimension;
+      BT(j + _sizeN, 1) = B(1, j + _sizeN) = Grads[j][1];//-Grads[j][1]/_dimension;
+      //      BT(j + _sizeN, 2) = B(2, j + _sizeN) =            -Grads[j][2]/_dimension;
 
       BT(j + _sizeN, 3) = B(3, j + _sizeN) = Grads[j][0];
       BT(j + _sizeN, 5) = B(5, j + _sizeN) = Grads[j][2];
       
       BDILT(j + 2 * _sizeN, 0) = BDIL(0, j + 2 * _sizeN) = Grads[j][2]/_dimension;
 
-      BT(j + 2 * _sizeN, 0) = B(0, j + 2 * _sizeN) =            -Grads[j][0]/_dimension;
-      BT(j + 2 * _sizeN, 1) = B(1, j + 2 * _sizeN) =            -Grads[j][1]/_dimension;
-      BT(j + 2 * _sizeN, 2) = B(2, j + 2 * _sizeN) = Grads[j][2]-Grads[j][2]/_dimension;
+      //      BT(j + 2 * _sizeN, 0) = B(0, j + 2 * _sizeN) =            -Grads[j][0]/_dimension;
+      //      BT(j + 2 * _sizeN, 1) = B(1, j + 2 * _sizeN) =            -Grads[j][1]/_dimension;
+      BT(j + 2 * _sizeN, 2) = B(2, j + 2 * _sizeN) = Grads[j][2];//-Grads[j][2]/_dimension;
 
       BT(j + 2 * _sizeN, 4) = B(4, j + 2 * _sizeN) = Grads[j][1];
       BT(j + 2 * _sizeN, 5) = B(5, j + 2 * _sizeN) = Grads[j][0];
@@ -252,17 +289,17 @@ void elasticityMixedTerm::elementMatrix(SElement *se, fullMatrix<double> &m) con
     }
 
     KUU.gemm(BTH, B, weight * detJ);
-    KUP.gemm(BDILT, M, weight * detJ);
-    KPG.gemm(MT, M, -weight * detJ);
-    KUG.gemm(trBTH, M, weight * detJ/_dimension);
-    KGG.gemm(MT, M, weight * detJ * (_K)/(_dimension*_dimension));    
+    //    KUP.gemm(BDILT, M, weight * detJ);
+    //    KPG.gemm(MT, M, -weight * detJ);
+    //    KUG.gemm(trBTH, M, weight * detJ/_dimension);
+    //    KGG.gemm(MT, M, weight * detJ * (_K)/(_dimension*_dimension));    
 
   }
     /*       
             3*_sizeN  _sizeM _sizeM
 
             KUU     KUP     KUG
-      m  =  KUP^t    0      KPG
+      m  =  KUP^t   KPP      KPG
             KUG^T    KPG^t  KGG
       
      */
@@ -288,6 +325,8 @@ void elasticityMixedTerm::elementMatrix(SElement *se, fullMatrix<double> &m) con
 
       m(i+3*_sizeN+_sizeM,j+3*_sizeN+_sizeM) = KGG(i,j); // assemble KGG
 
+      m(i+3*_sizeN,j+3*_sizeN) = KPP(i,j); // assemble KPP
+
     }
   }
   //    m.print("Mixed");
-- 
GitLab