diff --git a/CMakeLists.txt b/CMakeLists.txt
index 529ef879a2b29e56c6aac5c2c663ecfd447c1440..f0fd5bae5e574f703f9ea1373ec7e7cce0965983 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -133,9 +133,9 @@ set(GMSH_API
   contrib/kbipack/gmp_normal_form.h contrib/kbipack/gmp_matrix.h 
     contrib/kbipack/gmp_blas.h contrib/kbipack/mpz.h
   contrib/DiscreteIntegration/Integration3D.h
-  contrib/HighOrderMeshOptimizer/OptHOM.h
-  contrib/HighOrderMeshOptimizer/OptHomMesh.h
-  contrib/HighOrderMeshOptimizer/ParamCoord.h
+  contrib/HighOrderMeshOptimizer/OptHOM.h contrib/HighOrderMeshOptimizer/OptHomMesh.h
+  contrib/HighOrderMeshOptimizer/OptHOMRun.h contrib/HighOrderMeshOptimizer/ParamCoord.h
+  contrib/HighOrderMeshOptimizer/OptHOMFastCurving.h contrib/HighOrderMeshOptimizer/SuperEl.h
   contrib/MathEx/mathex.h)
 
 execute_process(COMMAND date "+%Y%m%d" OUTPUT_VARIABLE DATE 
diff --git a/Fltk/highOrderToolsWindow.cpp b/Fltk/highOrderToolsWindow.cpp
index 36308cf65189f4934fb29f84c41bb96d89d4c658..141a39ab5889285af8aa34aa2528538d6c783fae 100644
--- a/Fltk/highOrderToolsWindow.cpp
+++ b/Fltk/highOrderToolsWindow.cpp
@@ -28,6 +28,7 @@
 #if defined(HAVE_OPTHOM)
 #include "OptHomRun.h"
 #include "OptHomElastic.h"
+#include "OptHomFastCurving.h"
 #endif
 
 static void change_completeness_cb(Fl_Widget *w, void *data)
@@ -75,25 +76,36 @@ static void highordertools_runp_cb(Fl_Widget *w, void *data)
 static void chooseopti_cb(Fl_Widget *w, void *data)
 {
   highOrderToolsWindow *o = FlGui::instance()->highordertools;
-  int elastic = o->choice[2]->value();
+  const int algo = o->choice[2]->value();
 
-  if (elastic == 1){
+  switch(algo) {
+  case 0: {                                           // Optimization
+    o->push[0]->activate();
+    o->choice[0]->activate();
+    o->choice[3]->activate();
+    for (int i=2;i<=11;i++) o->value[i]->activate();
+    //    o->push[1]->activate();
+    break;
+  }
+  case 1: {                                           // Elastic analogy
     o->choice[0]->deactivate();
     o->choice[3]->deactivate();
-    for (int i=3;i<=6;i++)
+    for (int i=2;i<=11;i++) o->value[i]->deactivate();
+    //   o->push[1]->deactivate();
+    break;
+  }
+  case 2: {                                           // Fast curving
+    o->choice[0]->deactivate();
+    o->choice[3]->deactivate();
+    for (int i=2;i<=6;i++)
       o->value[i]->deactivate();
     for (int i=9;i<=11;i++) o->value[i]->deactivate();
+    for (int i=7;i<=8;i++) o->value[i]->activate();
     //   o->push[1]->deactivate();
+    break;
   }
-  else {
-    o->push[0]->activate();
-    o->choice[0]->activate();
-    o->choice[3]->activate();
-    for (int i=3;i<=6;i++)
-      o->value[i]->activate();
-    for (int i=9;i<=11;i++) o->value[i]->activate();
-    //    o->push[1]->activate();
   }
+
 }
 
 static void chooseopti_strategy(Fl_Widget *w, void *data)
@@ -110,17 +122,15 @@ static void highordertools_runopti_cb(Fl_Widget *w, void *data)
   if(o->butt[3]->value())
     FlGui::instance()->graph[0]->showMessages();
 
-  bool elastic = o->choice[2]->value() == 1;
+  const int algo = o->choice[2]->value();
   double threshold_min = o->value[1]->value();
   bool onlyVisible = (bool)o->butt[1]->value();
   int nbLayers = (int) o->value[2]->value();
   double threshold_max = o->value[8]->value();
 
 #if defined(HAVE_OPTHOM)
-  if(elastic){
-    ElasticAnalogy(GModel::current(), threshold_min, onlyVisible);
-  }
-  else{
+  switch(algo) {
+  case 0: {                                                               // Optimization
     OptHomParameters p;
     p.nbLayers = nbLayers;
     p.BARRIER_MIN = threshold_min;
@@ -138,6 +148,22 @@ static void highordertools_runopti_cb(Fl_Widget *w, void *data)
     p.adaptBlobLayerFact = (int) o->value[10]->value();
     p.adaptBlobDistFact = o->value[11]->value();
     HighOrderMeshOptimizer(GModel::current(), p);
+    break;
+  }
+  case 1: {                                                               // Elastic analogy
+    ElasticAnalogy(GModel::current(), threshold_min, onlyVisible);
+    break;
+  }
+  case 2: {                                                               // Fast curving
+    FastCurvingParameters p;
+    p.BARRIER_MIN = threshold_min;
+    p.BARRIER_MAX = threshold_max;
+    p.onlyVisible = onlyVisible;
+    p.dim = GModel::current()->getDim();
+    p.distanceFactor =  o->value[7]->value();
+    HighOrderMeshFastCurving(GModel::current(), p);
+    break;
+  }
   }
 #else
   Msg::Error("High-order mesh optimization requires the OPTHOM module");
@@ -276,7 +302,8 @@ highOrderToolsWindow::highOrderToolsWindow(int deltaFontSize)
 
   static Fl_Menu_Item menu_method[] = {
     {"Optimization", 0, 0, 0},
-    {"ElasticAnalogy", 0, 0, 0},
+    {"Elastic Analogy", 0, 0, 0},
+    {"Fast Curving", 0, 0, 0},
     {0}
   };
 
diff --git a/contrib/HighOrderMeshOptimizer/CMakeLists.txt b/contrib/HighOrderMeshOptimizer/CMakeLists.txt
index ffe20b2110c3cd6700608355b48ceeced33d071a..8777797d500089ba251a9aa52c863b890a5c114a 100644
--- a/contrib/HighOrderMeshOptimizer/CMakeLists.txt
+++ b/contrib/HighOrderMeshOptimizer/CMakeLists.txt
@@ -8,7 +8,9 @@ set(SRC
   OptHOM.cpp 
   OptHomRun.cpp 
   ParamCoord.cpp 
+  SuperEl.cpp 
   OptHomElastic.cpp
+  OptHomFastCurving.cpp
 )
 
 file(GLOB_RECURSE HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.hpp)
diff --git a/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp b/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dbcb9d030f5a9a57c7973a8ad780d06a8c3a1ffe
--- /dev/null
+++ b/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp
@@ -0,0 +1,728 @@
+// Copyright (C) 2013 ULg-UCL
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, and/or sell copies of the
+// Software, and to permit persons to whom the Software is furnished
+// to do so, provided that the above copyright notice(s) and this
+// permission notice appear in all copies of the Software and that
+// both the above copyright notice(s) and this permission notice
+// appear in supporting documentation.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR
+// ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
+// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+// OF THIS SOFTWARE.
+//
+// Please report all bugs and problems to the public mailing list
+// <gmsh@geuz.org>.
+//
+// Contributors: Thomas Toulorge, Jonathan Lambrechts
+
+#include <stdio.h>
+#include <sstream>
+#include <fstream>
+#include <iterator>
+#include <string.h>
+#include "GmshConfig.h"
+#include "OptHomFastCurving.h"
+#include "GModel.h"
+#include "Gmsh.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+#include "MTetrahedron.h"
+#include "MHexahedron.h"
+#include "MPrism.h"
+#include "MLine.h"
+#include "OS.h"
+#include <stack>
+#include "SuperEl.h"
+#include "SVector3.h"
+#include "BasisFactory.h"
+
+
+
+namespace {
+
+
+
+void exportMeshToDassault(GModel *gm, const std::string &fn, int dim)
+{
+  FILE *f = fopen(fn.c_str(),"w");
+
+  int numVertices = gm->indexMeshVertices(true);
+  std::vector<GEntity*> entities;
+  gm->getEntities(entities);
+  fprintf(f,"%d %d\n", numVertices, dim);
+  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];
+      if (dim == 2)
+        fprintf(f,"%d %22.15E %22.15E\n", v->getIndex(), v->x(), v->y());
+      else if (dim == 3)
+        fprintf(f,"%d %22.15E %22.15E %22.5E\n", v->getIndex(), v->x(),
+                v->y(), v->z());
+    }
+
+  if (dim == 2){
+    int nt = 0;
+    int order  = 0;
+    for (GModel::fiter itf = gm->firstFace(); itf != gm->lastFace(); ++itf){
+      std::vector<MTriangle*> &tris = (*itf)->triangles;
+      nt += tris.size();
+      if (tris.size())order = tris[0]->getPolynomialOrder();
+    }
+    fprintf(f,"%d %d\n", nt,(order+1)*(order+2)/2);
+    int count = 1;
+    for (GModel::fiter itf = gm->firstFace(); itf != gm->lastFace(); ++itf){
+      std::vector<MTriangle*> &tris = (*itf)->triangles;
+      for (size_t i=0;i<tris.size();i++){
+  MTriangle *t = tris[i];
+  fprintf(f,"%d ", count++);
+  for (int j=0;j<t->getNumVertices();j++){
+    fprintf(f,"%d ", t->getVertex(j)->getIndex());
+  }
+  fprintf(f,"\n");
+      }
+    }
+    int ne = 0;
+    for (GModel::eiter ite = gm->firstEdge(); ite != gm->lastEdge(); ++ite){
+      std::vector<MLine*> &l = (*ite)->lines;
+      ne += l.size();
+    }
+    fprintf(f,"%d %d\n", ne,(order+1));
+    count = 1;
+    for (GModel::eiter ite = gm->firstEdge(); ite != gm->lastEdge(); ++ite){
+      std::vector<MLine*> &l = (*ite)->lines;
+      for (size_t i=0;i<l.size();i++){
+  MLine *t = l[i];
+  fprintf(f,"%d ", count++);
+  for (int j=0;j<t->getNumVertices();j++){
+    fprintf(f,"%d ", t->getVertex(j)->getIndex());
+  }
+  fprintf(f,"%d \n",(*ite)->tag());
+      }
+    }
+  }
+  fclose(f);
+}
+
+
+
+//double distMaxStraight(MElement *el)
+//{
+//  const polynomialBasis *lagrange = (polynomialBasis*)el->getFunctionSpace();
+//  const polynomialBasis *lagrange1 = (polynomialBasis*)el->getFunctionSpace(1);
+//  int nV = lagrange->points.size1();
+//  int nV1 = lagrange1->points.size1();
+//  SPoint3 sxyz[256];
+//  for (int i = 0; i < nV1; ++i) {
+//    sxyz[i] = el->getVertex(i)->point();
+//  }
+//  for (int i = nV1; i < nV; ++i) {
+//    double f[256];
+//    lagrange1->f(lagrange->points(i, 0), lagrange->points(i, 1),
+//                 lagrange->points(i, 2), f);
+//    for (int j = 0; j < nV1; ++j)
+//      sxyz[i] += sxyz[j] * f[j];
+//  }
+//
+//  double maxdx = 0.0;
+//  for (int iV = nV1; iV < nV; iV++) {
+//    SVector3 d = el->getVertex(iV)->point()-sxyz[iV];
+//    double dx = d.norm();
+//    if (dx > maxdx) maxdx = dx;
+//  }
+//
+//  return maxdx;
+//}
+
+
+
+void calcVertex2Elements(int dim, GEntity *entity,
+                                std::map<MVertex*, std::vector<MElement *> > &vertex2elements)
+{
+  for (size_t i = 0; i < entity->getNumMeshElements(); ++i) {
+    MElement *element = entity->getMeshElement(i);
+    if (element->getDim() == dim)
+      for (int j = 0; j < element->getNumPrimaryVertices(); ++j)
+        vertex2elements[element->getVertex(j)].push_back(element);
+  }
+}
+
+
+
+//void getNormals2D(const std::vector<MVertex*> &edgeVert, SVector3 &n0, SVector3 &n1) {
+//
+////  if (v0->onWhat() != 0) {
+//  if (false) {
+//    GEdge *ent = edgeVert.front()->onWhat()->cast2Edge();
+//    std::map<MVertex*, SVector3, std::less<MVertex*> > &allNormals = ent->getNormals();
+//    n0 = allNormals[edgeVert[0]];
+//    n1 = allNormals[edgeVert[1]];
+//  }
+//  else {
+//    const int &Nsf = edgeVert.size();
+//    const nodalBasis* fs = BasisFactory::getNodalBasis(nodalBasis::getTag(TYPE_LIN,Nsf-1,false));
+//    double dsf0[Nsf][3], dsf1[Nsf][3];
+//    fs->df(fs->points(0,0),0.,0.,dsf0);
+//    fs->df(fs->points(1,0),0.,0.,dsf1);
+//    double t0x = 0., t0y = 0., t1x = 0., t1y = 0.;
+//    for (int i=0; i<Nsf; ++i) {
+//      t0x += dsf0[i][0]*edgeVert[i]->x();
+//      t0y += dsf0[i][0]*edgeVert[i]->y();
+//      t1x += dsf1[i][0]*edgeVert[i]->x();
+//      t1y += dsf1[i][0]*edgeVert[i]->y();
+//    }
+//    n0[0] = -t0y; n0[1] = t0x; n0[2] = 0.;
+//    n0.normalize();
+//    n1[0] = -t1y; n1[1] = t1x; n1[2] = 0.;
+//    n1.normalize();
+//  }
+//
+//}
+
+
+
+//void getBaseVertsAndNormals(std::set<MElement*> badElts, std::map<MElement*,std::vector<MVertex*> > &faces,
+//                                 std::map<MVertex*,SVector3> &normals) {
+//
+//  typedef std::map<MVertex*,std::vector<SVector3> > normalMap;
+//  normalMap allNormals;
+//
+//  for (std::set<MElement*>::iterator itBE = badElts.begin(); itBE != badElts.end(); itBE++) {
+//    std::vector<MVertex*> baseVert;
+//    for (int i=0; i<(*itBE)->getNumEdges(); i++) {
+//      (*itBE)->getEdgeVertices(i,baseVert);
+//      if (isEdgeCurved(baseVert)) {
+//        const SPoint3 p0 = baseVert[0]->point(), p1 = baseVert[1]->point(), pC = (*itBE)->barycenter(true);
+//        SVector3 n(p0.y()-p1.y(), p1.x()-p0.x(), 0.), p0C(p0,pC);
+//        if (dot(n,p0C) < 0.) n *= -1.;
+//        n.normalize();
+//        allNormals[baseVert[0]].push_back(n);
+//        allNormals[baseVert[1]].push_back(n);
+//        faces[*itBE] = baseVert;
+//        break;
+//      }
+//    }
+//  }
+//
+//  for(normalMap::iterator itAN = allNormals.begin(); itAN != allNormals.end(); itAN++) {
+//    std::vector<SVector3> &normSet = itAN->second;
+////    std::cout << "DBGTT: Considering node " << itAN->first->getNum() << "\n";
+//    SVector3 norm(0.);
+//    for (std::vector<SVector3>::iterator itN = normSet.begin(); itN != normSet.end(); itN++) {
+////      std::cout << "DBGTT:  -> accumulating normal (" << itN->x() << ", "<< itN->y() << ", "<< itN->z() << ")\n";
+//      norm += *itN;
+//    }
+//    norm *= 1./double(normSet.size());
+//    normals[itAN->first] = norm;
+//  }
+//
+//}
+
+
+
+//bool isEdgeCurved(const std::vector<MVertex*> &edgeVert) {
+//
+//  static const double eps = 1.e-8;
+//
+//  const double n = edgeVert.size();
+//  const MVertex *vS = edgeVert[0];
+//  const double vSEx = edgeVert[1]->x()-vS->x(), vSEy = edgeVert[1]->y()-vS->y();
+//  const double normSE = vSEx*vSEx+vSEy*vSEy;
+//
+//  double distSq = 0.;
+//  for (int i=2; i<n; ++i) {
+//    const double vSix = edgeVert[i]->x()-vS->x(), vSiy = edgeVert[i]->y()-vS->y();
+//    const double fact = (vSix*vSEx+vSiy*vSEy)/normSE;
+//    const double dvx = vSix-fact*vSEx, dvy = vSiy-fact*vSEy;
+//    distSq += dvx*dvx+dvy*dvy;
+//  }
+//
+//  return (distSq > eps*normSE);
+//
+//}
+
+
+
+// Among edges connected to a given vertex, return the direction of the one that is closest to the given normal
+// Return the given normal if no edge is sufficiently close
+SVector3 getNormalEdge(MVertex *vert, const SVector3 &n,
+                       const std::map<MVertex*, std::vector<MElement*> > &vertex2elements) {
+
+//  static const double spLimit = 0.70711;                          // Limit in dot product below which we return the normal
+  static const double spLimit = 0.5;                          // Limit in dot product below which we return the normal
+
+  const std::vector<MElement*> &elts = (*vertex2elements.find(vert)).second;     // All elements connected to vertex
+
+  double spMax = 0.;
+  SVector3 normalEdge;
+//  std::cout << "DBGTT: Looking for normal edge at vertex " << vert->getNum()
+//              << " with n = (" << n.x() << ", " << n.y() << ", " << n.z() << ")\n";
+
+  for (std::vector<MElement*>::const_iterator itEl = elts.begin(); itEl != elts.end(); ++itEl)
+    for (int i=0; i<(*itEl)->getNumEdges(); i++) {
+      std::vector<MVertex*> edgeVert;
+      (*itEl)->getEdgeVertices(i,edgeVert);
+      SVector3 edge;
+      if (edgeVert[0] == vert) edge = SVector3(vert->point(),edgeVert[1]->point());
+      else if (edgeVert[1] == vert) edge = SVector3(vert->point(),edgeVert[0]->point());
+      else continue;
+      edge.normalize();
+      double sp = dot(edge,n);
+      if (sp > spMax) {                                           // Retain the edge giving max. dot product with normal
+        spMax = sp;
+        normalEdge = edge;
+      }
+//      std::cout << "DBGTT:   -> checking edge " << edgeVert[0]->getNum() << " - " << edgeVert[1]->getNum()
+//                  << ", sp = " << sp << "\n";
+    }
+
+  if (spMax < spLimit) { std::cout << "DBGTT: no normal edge\n"; normalEdge = n; }                            // If max. dot product is below limit, just take normal
+
+  return normalEdge;
+
+}
+
+
+
+//// Detect whether edge/face is curved, and give (non-oriented) normal
+//bool isCurvedAndNormal(int type, int order, const std::vector<MVertex*> &faceVert, SVector3 &normal) {
+//
+//  static const double eps = 1.e-8;
+//
+//  const nodalBasis *lagrange = BasisFactory::getNodalBasis(ElementType::getTag(type,order,false));
+//  const nodalBasis *lagrange1 = BasisFactory::getNodalBasis(ElementType::getTag(type,1,false));
+//  int nV = lagrange->points.size1();
+//  int nV1 = lagrange1->points.size1();
+//  SPoint3 sxyz[256];
+//  for (int i = 0; i < nV1; ++i) sxyz[i] = faceVert[i]->point();
+//  for (int i = nV1; i < nV; ++i) {
+//    double f[256];
+//    lagrange1->f(lagrange->points(i, 0), lagrange->points(i, 1), lagrange->points(i, 2), f);
+//    for (int j = 0; j < nV1; ++j)
+//      sxyz[i] += sxyz[j] * f[j];
+//  }
+//
+//  double sumDistSq = 0.;
+//  for (int iV = nV1; iV < nV; iV++) sumDistSq += SVector3(sxyz[iV],faceVert[iV]->point()).normSq();
+//
+//  double scale;
+//  if (type == TYPE_LIN) {
+//    const SPoint3 &p0 = sxyz[0], &p1 = sxyz[1];
+//    normal = SVector3(p0.y()-p1.y(),p1.x()-p0.x(),0.);
+//    scale = normal.normSq();
+//  }
+//  else {
+//    const SPoint3 &p0 = sxyz[0], &p1 = sxyz[1], &p2 = sxyz[2];
+//    SVector3 p01(p0,p1), p02(p0,p2);
+//    normal = crossprod(p01,p02);
+//    scale = normal.norm();
+//  }
+//  normal.normalize();
+//
+////  std::cout << "DBGTT: v0 is " << faceVert[0]->getNum() << ", v1 is " << faceVert[1]->getNum() << ", sumDistSq = " << sumDistSq << ", scale = " << scale << ", test = " << (sumDistSq > eps*double(nV)*scale) << "\n";
+//  return (sumDistSq > eps*double(nV)*scale);
+//
+//}
+//
+//
+//
+//void getBaseVertsAndNormals(std::set<MElement*> badElts,
+//                              const std::map<MVertex*, std::vector<MElement*> > &vertex2elements,
+//                              std::map<MElement*,int> &baseType,
+//                              std::map<MElement*,std::vector<MVertex*> > &baseVert,
+//                              std::map<MElement*,std::vector<SVector3> > &normals) {
+//
+//  for (std::set<MElement*>::iterator itBE = badElts.begin(); itBE != badElts.end(); itBE++) {
+//    const bool is2D = ((*itBE)->getDim() == 2);
+//    const int order = (*itBE)->getPolynomialOrder();
+//    const int numFaces = is2D ? (*itBE)->getNumEdges() : (*itBE)->getNumFaces();
+//    for (int iFace=0; iFace<numFaces; iFace++) {            // Loop on edges/faces
+//      std::vector<MVertex*> bv;                             // Vertices of edge/face
+//      int type, numPrimVert;
+//      if (is2D) {
+//        (*itBE)->getEdgeVertices(iFace,bv);
+//        type = TYPE_LIN;
+//        numPrimVert = 2;
+//      }
+//      else {
+//        (*itBE)->getFaceVertices(iFace,bv);
+//        MFace face = (*itBE)->getFace(iFace);
+//        numPrimVert = face.getNumVertices();
+//        type = (numPrimVert == 3) ? TYPE_TRI : TYPE_QUA;
+//      }
+//      SVector3 n;                                           // Normal to straight edge/face
+//      if (isCurvedAndNormal(type,order,bv,n)) {             // Check whether edge/face is curved and compute straight normal
+//        std::cout << "DBGTT: Curved edge/face in el. " << (*itBE)->getNum() << "\n";
+//        if (baseVert.find(*itBE) != baseVert.end()) {       // If more than 1 curved edge/face in bad el., drop it
+//          baseVert.erase(*itBE);
+//          normals.erase(*itBE);
+//          std::cout << "DBGTT: More than 1 curved edge/face detected in el. " << (*itBE)->getNum() << "\n";
+//          break;
+//        }
+//        baseType[*itBE] = type;
+//        baseVert[*itBE] = bv;
+//        const SPoint3 p0 = bv[0]->point(), pC = (*itBE)->barycenter(true);
+//        SVector3 p0C(p0,pC);
+//        if (dot(n,p0C) < 0.) n *= -1.;                                      // Make straight normal in-going
+//        for (int iV=0; iV<numPrimVert; iV++)                                // Compute normals to prim. vert. of edge/face
+//          normals[*itBE].push_back(getNormalEdge(bv[iV],n,vertex2elements));
+//      }
+//    }
+//  }
+//
+//}
+
+
+
+// Detect whether edge/face is curved, and give normal
+bool isCurvedAndNormal(int type, int order, const std::vector<MVertex*> &faceVert,
+                       const SPoint3 pBar, SVector3 &normal, double &maxDist) {
+
+//  static const double eps = 1.e-10;
+  static const double eps = 1.e-6;
+
+  // Compute HO points in straight edge/face
+  const nodalBasis *lagrange = BasisFactory::getNodalBasis(ElementType::getTag(type,order,false));
+  const nodalBasis *lagrange1 = BasisFactory::getNodalBasis(ElementType::getTag(type,1,false));
+  int nV = lagrange->points.size1();
+  int nV1 = lagrange1->points.size1();
+  SPoint3 sxyz[256];
+  for (int i = 0; i < nV1; ++i) sxyz[i] = faceVert[i]->point();
+  for (int i = nV1; i < nV; ++i) {
+    double f[256];
+    lagrange1->f(lagrange->points(i, 0), lagrange->points(i, 1), lagrange->points(i, 2), f);
+    for (int j = 0; j < nV1; ++j)
+      sxyz[i] += sxyz[j] * f[j];
+  }
+
+  // Compute (non-oriented) unit normal to straight edge/face and its scale [length]
+  double scale;
+  const SPoint3 &p0 = sxyz[0], &p1 = sxyz[1];
+  if (type == TYPE_LIN) {
+    normal = SVector3(p0.y()-p1.y(),p1.x()-p0.x(),0.);
+    scale = normal.normalize();
+  }
+  else {
+    const SPoint3 &p2 = sxyz[2];
+    SVector3 p01(p0,p1), p02(p0,p2);
+    normal = crossprod(p01,p02);
+    scale = sqrt(normal.normalize());
+  }
+
+  // Orient normal with help of pBar
+  SVector3 p0C(p0,pBar);
+  if (dot(normal,p0C) < 0.) normal *= -1.;                                      // Make straight normal in-going
+
+  // Calc max. normal dist. from straight to HO points
+  maxDist = 0.;
+  for (int iV = nV1; iV < nV; iV++) {
+    const double normalDisp = dot(SVector3(sxyz[iV],faceVert[iV]->point()),normal);
+    maxDist = std::max(maxDist,fabs(normalDisp));
+  }
+
+  std::cout << "DBGTT: v0 is " << faceVert[0]->getNum() << ", v1 is " << faceVert[1]->getNum()
+            << ", v2 is " << faceVert[2]->getNum() << ", maxDist = " << maxDist
+            << ", scale = " << scale << ", test = " << (maxDist > eps*scale) << "\n";
+  return (maxDist > eps*scale);
+
+}
+
+
+
+bool getCurvedFace(MElement* badEl,
+                   const std::map<MVertex*, std::vector<MElement*> > &vertex2elements,
+                   std::vector<MVertex*> &baseVert, std::vector<SVector3> &normals,
+                   int &baseType, double &baseMaxDist) {
+
+  bool found = false;
+  const int dim = badEl->getDim();
+  const int order = badEl->getPolynomialOrder();
+  const int numFaces = (dim == 2) ? badEl->getNumEdges() : badEl->getNumFaces();
+
+  for (int iFace=0; iFace<numFaces; iFace++) {            // Loop on edges/faces
+
+    // Get face info (type, vertices)
+    std::vector<MVertex*> bv;                             // Vertices of edge/face
+    int numPrimVert, type;
+    if (dim == 2) {
+      badEl->getEdgeVertices(iFace,bv);
+      type = TYPE_LIN;
+      numPrimVert = 2;
+    }
+    else {
+      badEl->getFaceVertices(iFace,bv);
+      MFace face = badEl->getFace(iFace);
+      numPrimVert = face.getNumVertices();
+      type = (numPrimVert == 3) ? TYPE_TRI : TYPE_QUA;
+    }
+
+    // Skip face if at least 1 vertex not on boundary
+    bool inDom = false;
+    for (int iV=0; iV<bv.size(); ++iV)
+      if (bv[iV]->onWhat()->dim() == dim) inDom = true;
+    if (inDom) continue;
+
+    //    std::cout << "DBGTT: Checking edge/face " << iFace << " in el. " << badEl->getNum() << ": "
+//              << numPrimVert << " vert., type " << type << "\n";
+    // If face curved, mark it and compute normals to its primary vertices
+    SVector3 n;                                                           // Normal to straight edge/face
+    double maxDist;                                                       // TOFIX: Max. normal. dist. to straight in curved face
+    const SPoint3 pBar = badEl->barycenter(true);                         // Bary. of el. to help orienting normal
+    if (isCurvedAndNormal(type,order,bv,pBar,n,maxDist)) {                // Check whether edge/face is curved and compute straight normal
+      std::cout << "DBGTT: Curved edge/face in el. " << badEl->getNum() << "\n";
+      if (found) {                                                        // If more than 1 curved edge/face in bad el., drop it
+        std::cout << "DBGTT: More than 1 curved edge/face detected in el. " << badEl->getNum() << "\n";
+        return false;
+      }
+      for (int iV=0; iV<numPrimVert; iV++)                                // Compute normals to prim. vert. of edge/face
+        normals.push_back(getNormalEdge(bv[iV],n,vertex2elements));
+      baseVert = bv;
+      baseType = type;
+      baseMaxDist = maxDist;
+      found = true;
+    }
+
+  }
+
+  return found;
+
+}
+
+
+
+void makeStraight(MElement *el, const std::set<MVertex*> &movedVert) {
+
+  const nodalBasis *nb = el->getFunctionSpace();
+  const fullMatrix<double> &pts = nb->points;
+
+  SPoint3 p;
+
+  for(int iPt = el->getNumPrimaryVertices(); iPt < el->getNumVertices(); ++iPt) {
+    MVertex *vert = el->getVertex(iPt);
+    if (movedVert.find(vert) == movedVert.end()) {
+      el->primaryPnt(pts(iPt,0),pts(iPt,1),pts(iPt,2),p);
+      vert->setXYZ(p.x(),p.y(),p.z());
+    }
+  }
+
+}
+
+
+
+std::set<MElement*> getSuperElBlob(MElement *el, const std::map<MVertex*,
+                                   std::vector<MElement*> > &vertex2elements,
+                                   const SuperEl *sEl)
+{
+
+  static const int depth = 100;
+
+  std::set<MElement*> blob;
+  std::list<MElement*> currentLayer, lastLayer;
+
+  blob.insert(el);
+  lastLayer.push_back(el);
+  for (int d = 0; d < depth; ++d) {
+    currentLayer.clear();
+    for (std::list<MElement*>::iterator it = lastLayer.begin();
+         it != lastLayer.end(); ++it) {
+      for (int i = 0; i < (*it)->getNumPrimaryVertices(); ++i) {
+        const std::vector<MElement*> &neighbours = vertex2elements.find
+          ((*it)->getVertex(i))->second;
+        for (std::vector<MElement*>::const_iterator itN = neighbours.begin();
+             itN != neighbours.end(); ++itN){
+          if (sEl->isPointIn((*itN)->barycenter(true))) {
+            // Assume that if an el is too far, its neighbours are too far as well
+            if (blob.insert(*itN).second) currentLayer.push_back(*itN);
+          }
+        }
+      }
+    }
+    lastLayer = currentLayer;
+    if (currentLayer.empty()) break;
+  }
+
+  return blob;
+}
+
+
+
+void curveMesh(std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
+               std::set<MElement*> &badElements, FastCurvingParameters &p, int samples)
+{
+
+  const int nbBadElts = badElements.size();
+  std::vector<MElement*> badElts;
+//  std::vector<int> types;
+//  std::vector<std::vector<MVertex*> > faces;
+//  std::vector<std::vector<SVector3> > &normals;
+//  std::vector<double> &maxDist;
+  std::vector<SuperEl*> superElts;
+  badElts.reserve(nbBadElts);
+//  types.reserve(nbBadElts);
+//  faces.reserve(nbBadElts);
+//  normals.reserve(nbBadElts);
+//  maxDist.reserve(nbBadElts);
+  superElts.reserve(nbBadElts);
+
+  std::ofstream of("dum.pos");
+  of << "View \" \"{\n";
+
+  for (std::set<MElement*>::const_iterator itBE = badElements.begin(); itBE != badElements.end(); ++itBE) {
+    std::vector<MVertex*> faceBE;
+    std::vector<SVector3> normalsBE;
+    int typeBE;
+    double maxDistBE;
+    if (getCurvedFace(*itBE,vertex2elements,faceBE,normalsBE,typeBE,maxDistBE)) {
+      badElts.push_back(*itBE);
+//      types.push_back(typeBE);
+//      faces.push_back(faceBE);
+//      normals.push_back(normalsBE);
+//      maxDist.push_back(maxDistBE);
+      superElts.push_back(new SuperEl(*itBE,maxDistBE*p.distanceFactor,typeBE,faceBE,normalsBE));
+//      superElts.push_back(new SuperEl(*itBE,distMaxStraight(*itBE)*p.distanceFactor,typeBE,faceBE,normalsBE));
+//      movedVert.insert(faceBE.begin(),faceBE.end());                                                // Do not touch vert. of curved face
+      of << superElts.back()->printPOS();
+    }
+  }
+
+  of << "};\n";
+  of.close();
+
+  std::set<MVertex*> movedVert;
+  for (int iBE=0; iBE<badElts.size(); ++iBE) {
+//    std::set<MElement*> blob = getSurroundingBlob(badElts[iBE], p.nbLayers, vertex2elements, p.distanceFactor);
+    std::set<MElement*> blob = getSuperElBlob(badElts[iBE], vertex2elements, superElts[iBE]);
+    std::cout << "DBGTT: Blob of bad el. " << badElts[iBE]->getNum() << " contains elts.";
+    for (std::set<MElement*>::iterator itE = blob.begin(); itE != blob.end(); ++itE) std::cout << " " << (*itE)->getNum();
+    std::cout << "\n";
+    makeStraight(badElts[iBE],movedVert);                                             // Make bad. el. straight
+    for (std::set<MElement*>::iterator itE = blob.begin(); itE != blob.end(); ++itE)
+      for (int i = 0; i < (*itE)->getNumVertices(); ++i) {                            // For each vert. of each el. in blob
+        MVertex* vert = (*itE)->getVertex(i);
+        if (movedVert.find(vert) == movedVert.end()) {                                // If vert. not already moved
+          double xyzS[3] = {vert->x(), vert->y(), vert->z()}, xyzC[3];
+          if (superElts[iBE]->straightToCurved(xyzS,xyzC)) {
+            std::cout << "DBGTT: moving vertex " << vert->getNum() << " from (" << xyzS[0] << "," << xyzS[1] << "," << xyzS[2] << ") to (" << xyzC[0] << "," << xyzC[1] << "," << xyzC[2] << ")\n";
+            vert->setXYZ(xyzC[0],xyzC[1],xyzC[2]);
+            movedVert.insert(vert);
+          }
+          else std::cout << "DBGTT: Failed to move vertex " << vert->getNum() << " with bad. el " << badElts[iBE]->getNum() << "\n";
+        }
+//        else std::cout << "DBGTT: Already moved vertex " << vert->getNum() << " with bad. el " << badElts[iBE]->getNum() << "\n";
+      }
+  }
+
+}
+
+
+
+}
+
+
+
+//static void optimizeBL(std::map<MVertex*, std::vector<MElement *> > &vertex2elements,
+//                       std::set<MElement*> &badElements, OptHomParameters &p, int samples)
+//{
+//
+//  const int nbBadElts = badElements.size();
+//  std::vector<MElement*> &badElts();
+//  std::map<MElement*,int> types;                                 // Type (LIN, TRI, QUA) of curved face in bad. el.
+//  std::map<MElement*,std::vector<MVertex*> > faces;              // Vertices of curved face in bad. el.
+//  std::map<MElement*,std::vector<SVector3> > normals;            // Normal to each prim. vertex of curved face in bad. el.
+//  getBaseVertsAndNormals(badElements,vertex2elements,types,faces,normals);
+//
+//  std::ofstream of("dum.pos");
+//  of << "View \" \"{\n";
+//
+//  for (std::set<MElement*>::const_iterator itBE = badElements.begin(); itBE != badElements.end(); ++itBE) {
+//
+//    if (faces.find(*itBE) == faces.end()) continue;           // No base edge/face -> no super el. for this bad. el.
+//
+//    SuperEl se(*itBE,distMaxStraight(*itBE)*p.distanceFactor,types[*itBE],faces[*itBE],normals[*itBE]);
+//
+//    std::set<MElement*> blob = getSurroundingBlob(*itBE, p.nbLayers, vertex2elements, p.distanceFactor);
+//    std::set<MVertex*> toMove;
+//    for (std::set<MElement*>::iterator itE = blob.begin(); itE != blob.end(); ++itE)
+//      for (int i = 0; i < (*itE)->getNumVertices(); ++i) toMove.insert((*itE)->getVertex(i));
+//
+////    std::cout << "DBGTT: Before makeStraight:\n";
+////    se.printCoord();
+//    makeStraight(*itBE);                                      // Make bad. el. straight
+////    for (std::set<MElement*>::iterator itE = blob.begin(); itE != blob.end(); ++itE) makeStraight(*itE);
+////    std::cout << "DBGTT: After makeStraight:\n";
+////    se.printCoord();
+//
+//    // Apply transformation straight -> curved to all vertices in super el.
+//    for (std::set<MVertex*>::iterator itV = toMove.begin(); itV != toMove.end(); ++itV) {
+//      double xyzS[3] = {(*itV)->x(), (*itV)->y(), (*itV)->z()}, xyzC[3];
+//      if (se.straightToCurved(xyzS,xyzC)) {
+////        std::cout << "DBGTT: moving vertex " << (*itV)->getNum() << " from (" << xyzS[0] << "," << xyzS[1] << "," << xyzS[2] << ") to (" << xyzC[0] << "," << xyzC[1] << "," << xyzC[2] << ")\n";
+//        (*itV)->setXYZ(xyzC[0],xyzC[1],xyzC[2]);
+//      }
+//      else std::cout << "DBGTT: Failed to move vertex " << (*itV)->getNum() << " with bad. el " << (*itBE)->getNum() << "\n";
+//    }
+//
+////    std::cout << se.printPOS();
+//    of << se.printPOS();
+//
+//  }
+//
+//  of << "};\n";
+//  of.close();
+//
+//}
+
+
+
+void HighOrderMeshFastCurving(GModel *gm, FastCurvingParameters &p)
+{
+
+  double t1 = Cpu();
+
+  int samples = 30;
+
+  double tf1 = Cpu();
+
+  Msg::StatusBar(true, "Optimizing high order mesh...");
+  std::vector<GEntity*> entities;
+  gm->getEntities(entities);
+
+  std::map<MVertex*, std::vector<MElement *> > vertex2elements;
+  std::set<MElement*> badasses;
+  for (int iEnt = 0; iEnt < entities.size(); ++iEnt) {
+    GEntity* &entity = entities[iEnt];
+    if (entity->dim() != p.dim || (p.onlyVisible && !entity->getVisibility())) continue;
+    Msg::Info("Computing connectivity and bad elements for entity %d...",
+              entity->tag());
+    calcVertex2Elements(p.dim,entity,vertex2elements); // Compute vert. -> elt. connectivity
+//    std::cout << "DBGTT: num. el. = " << entity->getNumMeshElements() << "\n";
+    for (int iEl = 0; iEl < entity->getNumMeshElements();iEl++) { // Detect bad elements
+      double jmin, jmax;
+      MElement *el = entity->getMeshElement(iEl);
+      if (el->getDim() == p.dim) {
+        el->scaledJacRange(jmin, jmax);
+//        std::cout << "El. " << iEl << ", jmin = " << jmin << ", jmax = " << jmax << "\n";
+//        Msg::Info("El %i, jmin = %g, jmax = %g\n",iEl,jmin,jmax);
+        if (jmin < p.BARRIER_MIN || jmax > p.BARRIER_MAX) badasses.insert(el);
+      }
+    }
+  }
+
+  curveMesh(vertex2elements, badasses, p, samples);
+
+  double t2 = Cpu();
+
+  Msg::StatusBar(true, "Done curving high order mesh (%g s)", t2-t1);
+
+}
diff --git a/contrib/HighOrderMeshOptimizer/OptHomFastCurving.h b/contrib/HighOrderMeshOptimizer/OptHomFastCurving.h
new file mode 100644
index 0000000000000000000000000000000000000000..f9ae4a56b6107f40ccf8c9c0995fa261e144a27f
--- /dev/null
+++ b/contrib/HighOrderMeshOptimizer/OptHomFastCurving.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2013 ULg-UCL
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, and/or sell copies of the
+// Software, and to permit persons to whom the Software is furnished
+// to do so, provided that the above copyright notice(s) and this
+// permission notice appear in all copies of the Software and that
+// both the above copyright notice(s) and this permission notice
+// appear in supporting documentation.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR
+// ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
+// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+// OF THIS SOFTWARE.
+//
+// Please report all bugs and problems to the public mailing list
+// <gmsh@geuz.org>.
+//
+// Contributors: Thomas Toulorge, Jonathan Lambrechts
+
+#ifndef _OPTHOMFASTCURVING_H_
+#define _OPTHOMFASTCURVING_H_
+
+class GModel;
+
+struct FastCurvingParameters {
+  // INPUT ------>
+  double BARRIER_MIN ; // minimum scaled jcaobian
+  double BARRIER_MAX ; // maximum scaled jcaobian
+  int dim ; // which dimension to optimize
+  bool onlyVisible ; // apply optimization to visible entities ONLY
+  double distanceFactor; // filter elements such that no elements further away
+                         // than DistanceFactor times the max distance to
+                         // straight sided version of an element are optimized
+
+  FastCurvingParameters ()
+    : BARRIER_MIN(0.1), BARRIER_MAX(2.0), dim(3) , onlyVisible(true), distanceFactor(12)
+  {
+  }
+};
+
+void HighOrderMeshFastCurving(GModel *gm, FastCurvingParameters &p);
+
+#endif
diff --git a/contrib/HighOrderMeshOptimizer/SuperEl.cpp b/contrib/HighOrderMeshOptimizer/SuperEl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81e8a00bcfd3895807a5437bcbaa6878ade084ee
--- /dev/null
+++ b/contrib/HighOrderMeshOptimizer/SuperEl.cpp
@@ -0,0 +1,324 @@
+// Copyright (C) 2013 ULg-UCL
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, and/or sell copies of the
+// Software, and to permit persons to whom the Software is furnished
+// to do so, provided that the above copyright notice(s) and this
+// permission notice appear in all copies of the Software and that
+// both the above copyright notice(s) and this permission notice
+// appear in supporting documentation.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR
+// ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
+// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+// OF THIS SOFTWARE.
+//
+// Please report all bugs and problems to the public mailing list
+// <gmsh@geuz.org>.
+//
+// Contributor: Thomas Toulorge
+
+#include <iostream>
+#include <sstream>
+#include "GEdge.h"
+#include "MLine.h"
+#include "MQuadrangle.h"
+#include "MPrism.h"
+#include "MHexahedron.h"
+#include "SuperEl.h"
+
+
+
+SuperEl::SuperEl(MElement *badEl, double dist, int type, const std::vector<MVertex*> &baseVert,
+                 const std::vector<SVector3> &normals) {
+
+//  std::cout << "DBGTT: badEl = " << badEl->getNum() << ", _superEl = " << _superEl << std::endl;
+
+  switch (type) {
+    case TYPE_LIN:
+      createSuperElQuad(badEl, dist, baseVert, normals[0], normals[1]);
+      break;
+    case TYPE_TRI:
+      createSuperElPrism(badEl, dist, baseVert, normals[0], normals[1], normals[2]);
+      break;
+    case TYPE_QUA:
+      createSuperElHex(badEl, dist, baseVert, normals[0], normals[1], normals[2], normals[3]);
+      break;
+    default:
+      std::cout << "ERROR: SuperEl not implemented for element of type " << type << std::endl;
+      _superEl = 0;
+      break;
+  }
+
+  if (!_superEl) std::cout << "ERROR: SuperEl not created" << std::endl;
+
+}
+
+
+
+void SuperEl::createSuperElQuad(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+                                  const SVector3 &n0, const SVector3 &n1) {
+
+
+  if (baseVert.empty()) {
+    std::cout << "ERROR: Base edge not given for SuperEl linked to bad. el. " << badEl->getNum() << "\n";
+    _superEl = 0;
+    return;
+  }
+
+  // First-order vertices for super-element
+  MVertex *v0 = new MVertex(*baseVert[0]);
+  MVertex *v1 = new MVertex(*baseVert[1]);
+//  double dist = distFactor*distMaxStraight(badEl);
+  double v2x = v1->x()+n1.x()*dist, v2y = v1->y()+n1.y()*dist;
+  MVertex *v2 = new MVertex(v2x,v2y,0.);
+  double v3x = v0->x()+n0.x()*dist, v3y = v0->y()+n0.y()*dist;
+  MVertex *v3 = new MVertex(v3x,v3y,0.);
+//  std::cout << "DBGTT: createSuperElQuad: v0 = (" << v0->x() << "," << v0->y()
+//              << "), v1 = (" << v1->x() << "," << v1->y()
+//              << "), v2 = (" << v2->x() << "," << v2->y()
+//              << "), v3 = (" << v3->x() << "," << v3->y() << ")" << std::endl;
+
+  // Get basis functions
+  MQuadrangle quad1(v0,v1,v2,v3);
+  const int p = badEl->getPolynomialOrder();
+  const nodalBasis *quadBasis = quad1.getFunctionSpace(p);
+  const int Nv = quadBasis->getNumShapeFunctions();
+
+  // Store/create all vertices for super-element
+  _superVert.resize(Nv);
+  _superVert[0] = v0;                                                     // First-order vertices
+  _superVert[1] = v1;
+  _superVert[2] = v2;
+  _superVert[3] = v3;
+  for (int i=2; i<p+1; ++i) _superVert[2+i] = new MVertex(*baseVert[i]);  // HO vertices on base edge
+  for (int i=3+p; i<Nv; ++i) {                                            // Additional HO vertices
+    SPoint3 p;
+    quad1.pnt(quadBasis->points(i,0),quadBasis->points(i,1),0.,p);
+    _superVert[i] = new MVertex(p.x(),p.y(),0.);
+//    std::cout << "DBGTT: createSuperElQuad: add vertex (" << _superVert[i]->x()
+//                << "," << _superVert[i]->y()<< "," << _superVert[i]->z() << ")" << std::endl;
+  }
+
+  _superEl = new MQuadrangleN(_superVert,p);
+  _superEl0 = new MQuadrangle(_superVert[0],_superVert[1],_superVert[2],_superVert[3]);
+//  std::cout << "Created SuperEl at address " << _superEl << std::endl;
+
+}
+
+
+
+void SuperEl::createSuperElPrism(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+                                  const SVector3 &n0, const SVector3 &n1, const SVector3 &n2) {
+
+
+  if (baseVert.empty()) {
+    std::cout << "ERROR: Base edge not given for SuperEl linked to bad. el. " << badEl->getNum() << "\n";
+    _superEl = 0;
+    _superEl0 = 0;
+    return;
+  }
+
+  // First-order vertices for super-element
+  MVertex *v0 = new MVertex(*baseVert[0]);
+  MVertex *v1 = new MVertex(*baseVert[1]);
+  MVertex *v2 = new MVertex(*baseVert[2]);
+  double v3x = v0->x()+n0.x()*dist, v3y = v0->y()+n0.y()*dist,  v3z = v0->z()+n0.z()*dist;
+  MVertex *v3 = new MVertex(v3x,v3y,v3z);
+  double v4x = v1->x()+n1.x()*dist, v4y = v1->y()+n1.y()*dist,  v4z = v1->z()+n1.z()*dist;
+  MVertex *v4 = new MVertex(v4x,v4y,v4z);
+  double v5x = v2->x()+n2.x()*dist, v5y = v2->y()+n2.y()*dist,  v5z = v2->z()+n2.z()*dist;
+  MVertex *v5 = new MVertex(v5x,v5y,v5z);
+
+  // Get basis functions
+  MPrism prism1(v0,v1,v2,v3,v4,v5);
+  const int p = badEl->getPolynomialOrder();
+  const nodalBasis *prismBasis = prism1.getFunctionSpace(p);
+  const int Nv = prismBasis->getNumShapeFunctions();                        // Number of vertices in HO prism
+
+  // Store/create all vertices for super-element
+  if (badEl->getTypeForMSH() == MSH_PRI_18) {
+    _superVert.resize(18);
+    _superVert[0] = v0;                                                     // First-order vertices
+    _superVert[1] = v1;
+    _superVert[2] = v2;
+    _superVert[3] = v3;
+    _superVert[4] = v4;
+    _superVert[5] = v5;
+    _superVert[6] = new MVertex(*baseVert[3]);                              // HO vertices on base face
+    _superVert[9] = new MVertex(*baseVert[4]);
+    _superVert[7] = new MVertex(*baseVert[5]);
+    const int indAddVerts[9] = {8,10,11,12,13,14,15,16,17};                 // Additional HO vertices
+    SPoint3 p;
+    for (int i=0; i<9; ++i) {
+      const int &ind = indAddVerts[i];
+      prism1.pnt(prismBasis->points(ind,0),prismBasis->points(ind,1),prismBasis->points(ind,2),p);
+      _superVert[ind] = new MVertex(p.x(),p.y(),p.z());
+    }
+    _superEl = new MPrism18(_superVert);
+  }
+  else {
+    std::cout << "ERROR: Type of prism not supported for bad. el. " << badEl->getNum() << "\n";
+    _superEl = 0;
+    _superEl0 = 0;
+    return;
+  }
+
+  _superEl0 = new MPrism(_superVert[0],_superVert[1],_superVert[2],
+                         _superVert[3],_superVert[4],_superVert[5]);
+
+}
+
+
+
+void SuperEl::createSuperElHex(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+                               const SVector3 &n0, const SVector3 &n1, const SVector3 &n2, const SVector3 &n3) {
+
+
+  if (baseVert.empty()) {
+    std::cout << "ERROR: Base edge not given for SuperEl linked to bad. el. " << badEl->getNum() << "\n";
+    _superEl = 0;
+    _superEl0 = 0;
+    return;
+  }
+
+  // First-order vertices for super-element
+  MVertex *v0 = new MVertex(*baseVert[0]);
+  MVertex *v1 = new MVertex(*baseVert[1]);
+  MVertex *v2 = new MVertex(*baseVert[2]);
+  MVertex *v3 = new MVertex(*baseVert[3]);
+  double v4x = v0->x()+n0.x()*dist, v4y = v0->y()+n0.y()*dist,  v4z = v0->z()+n0.z()*dist;
+  MVertex *v4 = new MVertex(v4x,v4y,v4z);
+  double v5x = v1->x()+n1.x()*dist, v5y = v1->y()+n1.y()*dist,  v5z = v1->z()+n1.z()*dist;
+  MVertex *v5 = new MVertex(v5x,v5y,v5z);
+  double v6x = v2->x()+n2.x()*dist, v6y = v2->y()+n2.y()*dist,  v6z = v2->z()+n2.z()*dist;
+  MVertex *v6 = new MVertex(v6x,v6y,v6z);
+  double v7x = v3->x()+n3.x()*dist, v7y = v3->y()+n3.y()*dist,  v7z = v3->z()+n3.z()*dist;
+  MVertex *v7 = new MVertex(v7x,v7y,v7z);
+
+  // Get basis functions
+  MHexahedron *hex1 = new MHexahedron(v0,v1,v2,v3,v4,v5,v6,v7);
+  const int p = badEl->getPolynomialOrder();
+  const nodalBasis *prismBasis = hex1->getFunctionSpace(p);
+  const int Nv = prismBasis->getNumShapeFunctions();                         // Number of vertices in HO hex
+
+  // Store/create all vertices for super-element
+  if (badEl->getTypeForMSH() == MSH_HEX_27) {
+    _superVert.resize(27);
+    _superVert[0] = v0;                                                      // First-order vertices
+    _superVert[1] = v1;
+    _superVert[2] = v2;
+    _superVert[3] = v3;
+    _superVert[4] = v4;
+    _superVert[5] = v5;
+    _superVert[6] = v6;
+    _superVert[7] = v7;
+    _superVert[8] = new MVertex(*baseVert[4]);                                // HO vertices on base face
+    _superVert[11] = new MVertex(*baseVert[5]);                               // HO vertices on base face
+    _superVert[13] = new MVertex(*baseVert[6]);                               // HO vertices on base face
+    _superVert[9] = new MVertex(*baseVert[7]);
+    _superVert[20] = new MVertex(*baseVert[8]);
+    const int indAddVerts[14] = {10,12,14,15,16,17,18,19,21,22,23,24,25,26};  // Additional HO vertices
+    SPoint3 p;
+    for (int i=0; i<14; ++i) {
+      const int &ind = indAddVerts[i];
+      hex1->pnt(prismBasis->points(ind,0),prismBasis->points(ind,1),prismBasis->points(ind,2),p);
+      _superVert[ind] = new MVertex(p.x(),p.y(),p.z());
+    }
+    _superEl = new MHexahedron27(_superVert);
+  }
+  else {
+    std::cout << "ERROR: Type of prism not supported for bad. el. " << badEl->getNum() << "\n";
+    _superEl = 0;
+    _superEl0 = 0;
+    return;
+  }
+
+  _superEl0 = hex1;
+
+}
+
+
+
+bool SuperEl::isPointIn(const SPoint3 p) const {
+
+  double xyz[3] = {p.x(), p.y(), p.z()}, uvw[3];
+  _superEl0->xyz2uvw(xyz,uvw);
+  return _superEl0->isInside(uvw[0],uvw[1],uvw[2]);
+
+}
+
+
+
+bool SuperEl::straightToCurved(double *xyzS, double *xyzC) const {
+
+  double uvw[3];
+  _superEl0->xyz2uvw(xyzS,uvw);
+  if (!_superEl0->isInside(uvw[0],uvw[1],uvw[2])) return false;
+
+  SPoint3 pC;
+  _superEl->pnt(uvw[0],uvw[1],uvw[2],pC);
+  xyzC[0] = pC[0];
+  xyzC[1] = pC[1];
+  xyzC[2] = pC[2];
+
+  return true;
+
+}
+
+
+
+//std::string SuperEl::printPOS() {
+//
+//  std::vector<MVertex*> verts;
+//  _superEl0->getVertices(verts);
+//  std::string posStr(_superEl0->getStringForPOS());
+//  int n = _superEl0->getNumVertices();
+//
+//  std::ostringstream oss;
+//
+//  oss << posStr << "(";
+//  for(int i = 0; i < n; i++){
+//    if(i) oss << ",";
+//    oss << _superVert[i]->x() << "," <<  _superVert[i]->y() << "," <<  _superVert[i]->z();
+//  }
+//  oss << "){0";
+//  for(int i = 0; i < n; i++) oss << ",0.";
+//  oss << "};\n";
+//
+//  return oss.str();
+//
+//}
+
+
+
+std::string SuperEl::printPOS() {
+
+  std::vector<MVertex*> verts;
+  _superEl0->getVertices(verts);
+  std::string posStr(_superEl0->getStringForPOS());
+  int n = _superEl0->getNumVertices();
+
+  std::ostringstream oss;
+
+  oss << posStr << "(";
+  for(int i = 0; i < n; i++){
+    if(i) oss << ",";
+    oss << _superVert[i]->x() << "," <<  _superVert[i]->y() << "," <<  _superVert[i]->z();
+  }
+  oss << "){0";
+  for(int i = 0; i < n; i++) oss << ",0.";
+  oss << "};\n";
+
+  return oss.str();
+
+}
diff --git a/contrib/HighOrderMeshOptimizer/SuperEl.h b/contrib/HighOrderMeshOptimizer/SuperEl.h
new file mode 100644
index 0000000000000000000000000000000000000000..595394011fd9242d1c6f5066ea438165e86ecdfc
--- /dev/null
+++ b/contrib/HighOrderMeshOptimizer/SuperEl.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2013 ULg-UCL
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use, copy,
+// modify, merge, publish, distribute, and/or sell copies of the
+// Software, and to permit persons to whom the Software is furnished
+// to do so, provided that the above copyright notice(s) and this
+// permission notice appear in all copies of the Software and that
+// both the above copyright notice(s) and this permission notice
+// appear in supporting documentation.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR
+// ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY
+// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+// OF THIS SOFTWARE.
+//
+// Please report all bugs and problems to the public mailing list
+// <gmsh@geuz.org>.
+//
+// Contributor: Thomas Toulorge
+
+#ifndef _SUPEREL_H_
+#define _SUPEREL_H_
+
+#include <string>
+#include "MElement.h"
+
+
+
+class SuperEl
+{
+public:
+
+  SuperEl(MElement *badEl, double dist, int type, const std::vector<MVertex*> &baseVert,
+          const std::vector<SVector3> &normals);
+  ~SuperEl() { _superVert.clear(); delete _superEl, _superEl0; }
+
+  bool isOK() const { return _superEl; }
+  bool isPointIn(const SPoint3 p) const;
+  bool straightToCurved(double *xyzS, double *xyzC) const;
+
+  std::string printPOS();
+
+  void printCoord() {
+    std::cout << "DBGTT: superEl -> ";
+    for(int i = 0; i < _superVert.size(); i++){
+      std::cout << "v" << i << " = (" << _superVert[i]->x() << "," <<  _superVert[i]->y() << "," <<  _superVert[i]->z() << ")";
+      if (i == _superVert.size()-1) std::cout << "\n"; else std::cout << ", ";
+    }
+
+  }
+
+private:
+
+  std::vector<MVertex*> _superVert;
+  MElement *_superEl, *_superEl0;
+
+  void createSuperElQuad(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+                         const SVector3 &n0, const SVector3 &n1);
+  void createSuperElPrism(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+                          const SVector3 &n0, const SVector3 &n1, const SVector3 &n2);
+  void createSuperElHex(MElement *badEl, double dist, const std::vector<MVertex*> &baseVert,
+                        const SVector3 &n0, const SVector3 &n1, const SVector3 &n2, const SVector3 &n3);
+
+
+};
+
+
+#endif