diff --git a/CMakeLists.txt b/CMakeLists.txt
index bc25e6bb451aec142fff5fb10180ffa462ef1113..97b9e3b0ec634458b5f8646d7c39a09ed938a3b5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -151,6 +151,8 @@ set(GMSH_API
   contrib/MeshOptimizer/MeshOptCommon.h contrib/MeshOptimizer/MeshOptimizer.h
   contrib/MeshOptimizer/MeshOptObjContribFunc.h contrib/MeshOptimizer/MeshOptObjContrib.h
   contrib/MeshOptimizer/MeshOptObjectiveFunction.h contrib/MeshOptimizer/MeshOptVertexCoord.h 
+  contrib/MeshQualityOptimizer/MeshQualityObjContribNCJ.h
+  contrib/MeshQualityOptimizer/MeshQualityOptimizer.h
   contrib/MathEx/mathex.h)
 
 get_property(IAMCHILD DIRECTORY  PROPERTY PARENT_DIRECTORY)
@@ -645,6 +647,8 @@ if(ENABLE_OPTHOM)
   include_directories(contrib/HighOrderMeshOptimizer)
   add_subdirectory(contrib/MeshOptimizer)
   include_directories(contrib/MeshOptimizer)
+  add_subdirectory(contrib/MeshQualityOptimizer)
+  include_directories(contrib/MeshQualityOptimizer)
   set_config_option(HAVE_OPTHOM "OptHom")
 endif(ENABLE_OPTHOM)
 
diff --git a/Mesh/qualityMeasures.cpp b/Mesh/qualityMeasures.cpp
index ea9615bf1255aeeefb0af65b8eb4ff62c92f2d73..37bd90050bc09511bf2a5a66c9a4d7b46898f2fe 100644
--- a/Mesh/qualityMeasures.cpp
+++ b/Mesh/qualityMeasures.cpp
@@ -295,6 +295,16 @@ double qmTriangle::angles(MTriangle *e)
 }
 
 
+void qmTriangle::NCJRange(const MTriangle *el, double &valMin, double &valMax)
+{
+  fullVector<double> ncj(3);
+  const MVertex *v0 = el->getVertex(0), *v1 = el->getVertex(1), *v2 = el->getVertex(2);
+  NCJ(v0->x(), v0->y(), v0->z(), v1->x(), v1->y(), v1->z(), v2->x(), v2->y(), v2->z(), ncj);
+  valMin = *std::min_element(ncj.getDataPtr(), ncj.getDataPtr()+3);
+  valMax = *std::max_element(ncj.getDataPtr(), ncj.getDataPtr()+3);
+}
+
+
 void qmTriangle::NCJ(const double &x0, const double &y0, const double &z0,
                      const double &x1, const double &y1, const double &z1,
                      const double &x2, const double &y2, const double &z2,
@@ -466,6 +476,18 @@ double qmQuadrangle::angles(MQuadrangle *e)
 }
 
 
+void qmQuadrangle::NCJRange(const MQuadrangle *el, double &valMin, double &valMax)
+{
+  fullVector<double> ncj(4);
+  const MVertex *v0 = el->getVertex(0), *v1 = el->getVertex(1),
+                *v2 = el->getVertex(2), *v3 = el->getVertex(3);
+  NCJ(v0->x(), v0->y(), v0->z(), v1->x(), v1->y(), v1->z(),
+      v2->x(), v2->y(), v2->z(), v3->x(), v3->y(), v3->z(), ncj);
+  valMin = *std::min_element(ncj.getDataPtr(), ncj.getDataPtr()+4);
+  valMax = *std::max_element(ncj.getDataPtr(), ncj.getDataPtr()+4);
+}
+
+
 void qmQuadrangle::NCJ(const double &x0, const double &y0, const double &z0,
                        const double &x1, const double &y1, const double &z1,
                        const double &x2, const double &y2, const double &z2,
diff --git a/Mesh/qualityMeasures.h b/Mesh/qualityMeasures.h
index 8086d20856051492719b78ec61de90f5402f64dc..324f9745a249c6afcbe7d8d08b7981b621a997b9 100644
--- a/Mesh/qualityMeasures.h
+++ b/Mesh/qualityMeasures.h
@@ -33,6 +33,8 @@ public:
   static double eta(MTriangle *el);
   static double angles(MTriangle *e);
   static double minNCJ(const MTriangle *e);
+  static void NCJRange(const MTriangle *e, double &valMin, double &valMax);
+  static inline int numNCJVal() { return 3; }
   static void NCJ(const double &x0, const double &y0, const double &z0,
                   const double &x1, const double &y1, const double &z1,
                   const double &x2, const double &y2, const double &z2,
@@ -51,6 +53,8 @@ public:
   static double eta(MQuadrangle *el);
   static double angles(MQuadrangle *e);
   static double minNCJ(const MQuadrangle *e);
+  static void NCJRange(const MQuadrangle *e, double &valMin, double &valMax);
+  static inline int numNCJVal() { return 4; }
   static void NCJ(const double &x0, const double &y0, const double &z0,
                   const double &x1, const double &y1, const double &z1,
                   const double &x2, const double &y2, const double &z2,
@@ -93,6 +97,8 @@ public:
                      const double &x4, const double &y4, const double &z4,
                      double *volume = 0);
   static double minNCJ(const MTetrahedron *e);
+//  static void NCJRange(const MTetrahedron *e, double &valMin, double &valMax);
+  static inline int numNCJVal() { return 4; }
   static void NCJ(const double &x0, const double &y0, const double &z0,
                   const double &x1, const double &y1, const double &z1,
                   const double &x2, const double &y2, const double &z2,
@@ -110,6 +116,7 @@ class qmPrism
 {
 public:
   static double minNCJ(const MPrism *el);
+  static inline int numNCJVal() { return 6; }
 //  static void NCJ(const double &x0, const double &y0, const double &z0,
 //                  const double &x1, const double &y1, const double &z1,
 //                  const double &x2, const double &y2, const double &z2,
@@ -123,6 +130,7 @@ class qmHexahedron
 {
 public:
   static double angles(MHexahedron *el);
+  static inline int numNCJVal() { return 8; }
 //  static void NCJ(const double &x0, const double &y0, const double &z0,
 //                  const double &x1, const double &y1, const double &z1,
 //                  const double &x2, const double &y2, const double &z2,
diff --git a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
index 8cc3573451f308d0f85ed6afba3be70934b6cbeb..94b970e882ace3609b6a80c130ee0546d4cdf2c5 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
@@ -758,7 +758,7 @@ struct HOPatchDefParameters : public MeshOptParameters::PatchDefParameters
   HOPatchDefParameters(const OptHomParameters &p);
   virtual ~HOPatchDefParameters() {}
   virtual double elBadness(MElement *el);
-  virtual double maxDistance(const MElement *el);
+  virtual double maxDistance(MElement *el);
 private:
   double jacMin, jacMax;
   double distanceFactor;
@@ -802,7 +802,7 @@ double HOPatchDefParameters::elBadness(MElement *el) {
 }
 
 
-double HOPatchDefParameters::maxDistance(const MElement *el) {
+double HOPatchDefParameters::maxDistance(MElement *el) {
   return distanceFactor * el->maxDistToStraight();
 }
 
diff --git a/contrib/MeshOptimizer/MeshOptCommon.h b/contrib/MeshOptimizer/MeshOptCommon.h
index 80fd94edaeb51806ed740562133ea0633641116e..9f895e84f9c5ede16ae4f6847aa42dbcae194a1e 100644
--- a/contrib/MeshOptimizer/MeshOptCommon.h
+++ b/contrib/MeshOptimizer/MeshOptCommon.h
@@ -56,7 +56,7 @@ struct MeshOptParameters {                             // Parameters controlling
       bool weakMerge;                                   // If connected strategy: weak or strong merging of patches
     };
     virtual double elBadness(MElement *el) = 0;         // Pointer to function returning "badness" of element (for patch creation)
-    virtual double maxDistance(const MElement *el) = 0; // Pointer to function checking the patch distance criterion for a given bad element
+    virtual double maxDistance(MElement *el) = 0; // Pointer to function checking the patch distance criterion for a given bad element
   };
   int dim ;                                             // Which dimension to optimize
   bool onlyVisible ;                                    // Apply optimization to visible entities ONLY
diff --git a/contrib/MeshOptimizer/MeshOptPatch.cpp b/contrib/MeshOptimizer/MeshOptPatch.cpp
index 0577a2525783f9f93454297eed8bc8d6918a9a4e..cfed7a6823dc30c971bf9b53a1d46d1239c27294 100644
--- a/contrib/MeshOptimizer/MeshOptPatch.cpp
+++ b/contrib/MeshOptimizer/MeshOptPatch.cpp
@@ -35,6 +35,7 @@
 #include "MTetrahedron.h"
 #include "BasisFactory.h"
 #include "OptHomIntegralBoundaryDist.h"
+#include "qualityMeasures.h"
 #include "MeshOptPatch.h"
 
 
@@ -496,3 +497,98 @@ bool Patch::bndDistAndGradients(int iEl, double &f, std::vector<double> &gradF,
   }
   return edgeFound;
 }
+
+
+void Patch::initNCJ()
+{
+  // Initialize _nBezEl
+  if (_nNCJEl.empty()) {
+    _nNCJEl.resize(nEl());
+    for (int iEl=0; iEl<nEl(); iEl++)
+      switch(_el[iEl]->getType()) {                                             // TODO: Complete with other types?
+      case TYPE_TRI: _nNCJEl[iEl] = qmTriangle::numNCJVal(); break;
+      case TYPE_QUA: _nNCJEl[iEl] = qmQuadrangle::numNCJVal(); break;
+      }
+  }
+}
+
+
+void Patch::NCJ(int iEl, std::vector<double> &NCJ)
+{
+  const int &numNCJVal = _nNCJEl[iEl];
+  const int &numNodes = _nNodEl[iEl];
+
+  std::vector<int> &iVEl = _el2V[iEl];
+  fullVector<double> val(numNCJVal);
+  switch(_el[iEl]->getType()) {                                                 // TODO: Complete with other types?
+  case TYPE_TRI: {
+    const int &iV0 = _el2V[iEl][0], &iV1 = _el2V[iEl][1], &iV2 = _el2V[iEl][2];
+    qmTriangle::NCJ(_xyz[iV0].x(), _xyz[iV0].y(), _xyz[iV0].z(),
+                    _xyz[iV1].x(), _xyz[iV1].y(), _xyz[iV1].z(),
+                    _xyz[iV2].x(), _xyz[iV2].y(), _xyz[iV2].z(), val);
+    break;
+  }
+  case TYPE_QUA: {
+    const int &iV0 = _el2V[iEl][0], &iV1 = _el2V[iEl][1],
+              &iV2 = _el2V[iEl][2], &iV3 = _el2V[iEl][3];
+    qmQuadrangle::NCJ(_xyz[iV0].x(), _xyz[iV0].y(), _xyz[iV0].z(),
+                      _xyz[iV1].x(), _xyz[iV1].y(), _xyz[iV1].z(),
+                      _xyz[iV2].x(), _xyz[iV2].y(), _xyz[iV2].z(),
+                      _xyz[iV3].x(), _xyz[iV3].y(), _xyz[iV3].z(), val);
+    break;
+  }
+  }
+
+  for (int l = 0; l < numNCJVal; l++) NCJ[l] = val(l);
+}
+
+
+void Patch::NCJAndGradients(int iEl, std::vector<double> &NCJ, std::vector<double> &gNCJ)
+{
+  const int &numNCJVal = _nNCJEl[iEl];
+  const int &numNodes = _nNodEl[iEl];
+
+  std::vector<int> &iVEl = _el2V[iEl];
+  fullMatrix<double> gradVal(numNCJVal, 3*numNodes+1);
+  switch(_el[iEl]->getType()) {                                                 // TODO: Complete with other types?
+  case TYPE_TRI: {
+    const int &iV0 = _el2V[iEl][0], &iV1 = _el2V[iEl][1], &iV2 = _el2V[iEl][2];
+    qmTriangle::NCJAndGradients(_xyz[iV0].x(), _xyz[iV0].y(), _xyz[iV0].z(),
+                                _xyz[iV1].x(), _xyz[iV1].y(), _xyz[iV1].z(),
+                                _xyz[iV2].x(), _xyz[iV2].y(), _xyz[iV2].z(), gradVal);
+    break;
+  }
+  case TYPE_QUA: {
+    const int &iV0 = _el2V[iEl][0], &iV1 = _el2V[iEl][1],
+              &iV2 = _el2V[iEl][2], &iV3 = _el2V[iEl][3];
+    qmQuadrangle::NCJAndGradients(_xyz[iV0].x(), _xyz[iV0].y(), _xyz[iV0].z(),
+                                  _xyz[iV1].x(), _xyz[iV1].y(), _xyz[iV1].z(),
+                                  _xyz[iV2].x(), _xyz[iV2].y(), _xyz[iV2].z(),
+                                  _xyz[iV3].x(), _xyz[iV3].y(), _xyz[iV3].z(), gradVal);
+    break;
+  }
+  }
+
+  // NCJ
+  for (int l = 0; l < numNCJVal; l++) NCJ[l] = gradVal(l,3*numNodes);
+
+  // Gradients of the NCJ
+  int iPC = 0;
+  std::vector<SPoint3> gXyzV(numNCJVal);
+  std::vector<SPoint3> gUvwV(numNCJVal);
+  for (int i = 0; i < numNodes; i++) {
+    int &iFVi = _el2FV[iEl][i];
+    if (iFVi >= 0) {
+      for (int l = 0; l < numNCJVal; l++)
+        gXyzV [l] = SPoint3(gradVal(l,i+0*numNodes), gradVal(l,i+1*numNodes),
+                            gradVal(l,i+2*numNodes));
+      _coordFV[iFVi]->gXyz2gUvw(_uvw[iFVi],gXyzV,gUvwV);
+      for (int l = 0; l < numNCJVal; l++) {
+        gNCJ[indGNCJ(iEl,l,iPC)] = gUvwV[l][0];
+        if (_nPCFV[iFVi] >= 2) gNCJ[indGNCJ(iEl,l,iPC+1)] = gUvwV[l][1];
+        if (_nPCFV[iFVi] == 3) gNCJ[indGNCJ(iEl,l,iPC+2)] = gUvwV[l][2];
+      }
+      iPC += _nPCFV[iFVi];
+    }
+  }
+}
diff --git a/contrib/MeshOptimizer/MeshOptPatch.h b/contrib/MeshOptimizer/MeshOptPatch.h
index 209f173c0a5b30d7df0625a3d08d82db79de7a9b..5a2f0613860caedf002d24a4a5a6301e38540a5d 100644
--- a/contrib/MeshOptimizer/MeshOptPatch.h
+++ b/contrib/MeshOptimizer/MeshOptPatch.h
@@ -92,6 +92,13 @@ public:
   void metricMinAndGradients(int iEl, std::vector<double> &sJ, std::vector<double> &gSJ);
   bool bndDistAndGradients(int iEl, double &f , std::vector<double> &gradF, double eps);
 
+  // Mesh quality
+  inline const int &nNCJEl(int iEl) { return _nNCJEl[iEl]; }
+  inline int indGNCJ(int iEl, int l, int iPC) { return iPC*_nNCJEl[iEl]+l; }
+  void initNCJ();
+  void NCJ(int iEl, std::vector<double> &NCJ);
+  void NCJAndGradients(int iEl, std::vector<double> &NCJ, std::vector<double> &gNCJ);
+
 private:
 
   // Mesh entities and variables
@@ -133,11 +140,14 @@ private:
   double _invLengthScaleSq;                             // Square inverse of a length for node displacement scaling
 
   // High-order: scaled Jacobian and metric measures
-  std::vector<int> _nBezEl;                             // Number of Bezier poly. and for an el.
+  std::vector<int> _nBezEl;                             // Number of Bezier poly. for an el.
   std::vector<fullMatrix<double> > _scaledNormEl;       // Normals to 2D elements for Jacobian regularization and scaling
   std::vector<double> _invStraightJac;                  // Initial Jacobians for 3D elements
 //  void calcScaledNormalEl2D(const std::map<MElement*,GEntity*> &element2entity, int iEl);
   void calcScaledNormalEl2D(int iEl);
+
+  // Mesh quality
+  std::vector<int> _nNCJEl;                             // Number of NCJ values for an el.
 };
 
 
diff --git a/contrib/MeshQualityOptimizer/CMakeLists.txt b/contrib/MeshQualityOptimizer/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d958b468cce1bf9c6a38b7113413ef5adb099508
--- /dev/null
+++ b/contrib/MeshQualityOptimizer/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Gmsh - Copyright (C) 1997-2014 C. Geuzaine, J.-F. Remacle
+#
+# See the LICENSE.txt file for license information. Please report all
+# bugs and problems to the public mailing list <gmsh@geuz.org>.
+
+set(SRC
+  MeshQualityOptimizer.cpp
+)
+
+file(GLOB_RECURSE HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.hpp)
+append_gmsh_src(contrib/MeshQualityOptimizer "${SRC};${HDR}")
diff --git a/contrib/MeshQualityOptimizer/MeshQualityObjContribNCJ.h b/contrib/MeshQualityOptimizer/MeshQualityObjContribNCJ.h
new file mode 100644
index 0000000000000000000000000000000000000000..369ff30b54a145b4e7febc3b1b4c9659b8796861
--- /dev/null
+++ b/contrib/MeshQualityOptimizer/MeshQualityObjContribNCJ.h
@@ -0,0 +1,97 @@
+// TODO: Copyright
+
+#ifndef _MESHQUALITYCONTRIBNCJ_H_
+#define _MESHQUALITYCONTRIBNCJ_H_
+
+#include "MeshOptPatch.h"
+#include "MeshOptObjContrib.h"
+
+
+template<class FuncType>
+class ObjContribNCJ : public ObjContrib, public FuncType
+{
+public:
+  ObjContribNCJ(double weight);
+  virtual ~ObjContribNCJ() {}
+  virtual ObjContrib *copy() const;
+  virtual void initialize(Patch *mesh);
+  virtual bool fail() { return _min <= 0.; }
+  virtual bool addContrib(double &Obj, alglib::real_1d_array &gradObj);
+  virtual void updateParameters() { FuncType::updateParameters(_min, _max); }
+  virtual bool targetReached() { return FuncType::targetReached(_min, _max); }
+  virtual bool stagnated() { return FuncType::stagnated(_min, _max); }
+  virtual void updateMinMax();
+
+protected:
+  Patch *_mesh;
+  double _weight;
+};
+
+
+template<class FuncType>
+ObjContribNCJ<FuncType>::ObjContribNCJ(double weight) :
+  ObjContrib("NCJ", FuncType::getNamePrefix()+"NCJ"),
+  _mesh(0), _weight(weight)
+{
+}
+
+
+template<class FuncType>
+ObjContrib *ObjContribNCJ<FuncType>::copy() const
+{
+  return new ObjContribNCJ<FuncType>(*this);
+}
+
+
+template<class FuncType>
+void ObjContribNCJ<FuncType>::initialize(Patch *mesh)
+{
+  _mesh = mesh;
+  _mesh->initNCJ();
+  updateMinMax();
+  FuncType::initialize(_min, _max);
+}
+
+
+template<class FuncType>
+bool ObjContribNCJ<FuncType>::addContrib(double &Obj, alglib::real_1d_array &gradObj)
+{
+  _min = BIGVAL;
+  _max = -BIGVAL;
+
+  for (int iEl = 0; iEl < _mesh->nEl(); iEl++) {
+    std::vector<double> NCJ(_mesh->nNCJEl(iEl));                                        // Scaled Jacobians
+    std::vector<double> gNCJ(_mesh->nNCJEl(iEl)*_mesh->nPCEl(iEl));                     // Gradients of scaled Jacobians
+    _mesh->NCJAndGradients(iEl, NCJ, gNCJ);
+    for (int l = 0; l < _mesh->nNCJEl(iEl); l++) {                                      // Add contribution for each Bezier coeff.
+      Obj += _weight * FuncType::compute(NCJ[l]);
+      const double dfact = _weight * FuncType::computeDiff(NCJ[l]);
+      for (int iPC = 0; iPC < _mesh->nPCEl(iEl); iPC++)
+        gradObj[_mesh->indPCEl(iEl, iPC)] += dfact * gNCJ[_mesh->indGNCJ(iEl, l, iPC)];
+      _min = std::min(_min, NCJ[l]);
+      _max = std::max(_max, NCJ[l]);
+    }
+  }
+
+  return true;
+}
+
+
+template<class FuncType>
+void ObjContribNCJ<FuncType>::updateMinMax()
+{
+  _min = BIGVAL;
+  _max = -BIGVAL;
+
+  for (int iEl = 0; iEl < _mesh->nEl(); iEl++) {
+    std::vector<double> NCJ(_mesh->nNCJEl(iEl));                         // Scaled Jacobians
+    _mesh->NCJ(iEl, NCJ);
+    for (int l = 0; l < _mesh->nBezEl(iEl); l++) {                      // Check each Bezier coeff.
+      _min = std::min(_min, NCJ[l]);
+      _max = std::max(_max, NCJ[l]);
+    }
+  }
+}
+
+
+#endif /* _MESHQUALITYCONTRIBNCJ_H_ */
diff --git a/contrib/MeshQualityOptimizer/MeshQualityOptimizer.cpp b/contrib/MeshQualityOptimizer/MeshQualityOptimizer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c0c502457c2f62b4674a13ffdbae02af87318110
--- /dev/null
+++ b/contrib/MeshQualityOptimizer/MeshQualityOptimizer.cpp
@@ -0,0 +1,113 @@
+// TODO: Copyright
+
+//#include "GModel.h"
+#include "GEntity.h"
+#include "MElement.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+#include "qualityMeasures.h"
+#include "MeshOptCommon.h"
+#include "MeshOptObjContribFunc.h"
+#include "MeshOptObjContrib.h"
+#include "MeshOptObjContribScaledNodeDispSq.h"
+#include "MeshQualityObjContribNCJ.h"
+#include "MeshOptimizer.h"
+#include "MeshQualityOptimizer.h"
+
+
+struct QualPatchDefParameters : public MeshOptParameters::PatchDefParameters
+{
+  QualPatchDefParameters(const MeshQualOptParameters &p);
+  virtual ~QualPatchDefParameters() {}
+  virtual double elBadness(MElement *el);
+  virtual double maxDistance(MElement *el);
+private:
+  double NCJMin, NCJMax;
+  double distanceFactor;
+};
+
+
+QualPatchDefParameters::QualPatchDefParameters(const MeshQualOptParameters &p)
+{
+  NCJMin = p.minTargetNCJ;
+  NCJMax = (p.maxTargetNCJ > 0.) ? p.maxTargetNCJ : 1.e300;
+  strategy = (p.strategy == 1) ? MeshOptParameters::STRAT_ONEBYONE :
+                                        MeshOptParameters::STRAT_CONNECTED;
+  minLayers = (p.dim == 3) ? 1 : 0;
+  maxLayers = p.nbLayers;
+  distanceFactor = p.distanceFactor;
+  if (strategy == MeshOptParameters::STRAT_CONNECTED)
+    weakMerge = (p.strategy == 2);
+  else {
+    maxAdaptPatch = p.maxAdaptBlob;
+    maxLayersAdaptFact = p.adaptBlobLayerFact;
+    distanceAdaptFact = p.adaptBlobDistFact;
+  }
+}
+
+
+double QualPatchDefParameters::elBadness(MElement *el) {
+  double valMin, valMax;
+  switch(el->getType()) {                                                 // TODO: Complete with other types?
+  case TYPE_TRI: {
+     qmTriangle::NCJRange(static_cast<MTriangle*>(el), valMin, valMax);
+    break;
+  }
+  case TYPE_QUA: {
+    qmQuadrangle::NCJRange(static_cast<MQuadrangle*>(el), valMin, valMax);
+    break;
+  }
+  }
+  double badness = std::min(valMin-NCJMin, 0.) + std::min(NCJMin-valMax, 0.);
+  return badness;
+}
+
+
+double QualPatchDefParameters::maxDistance(MElement *el) {
+  return distanceFactor * el->getOuterRadius();
+}
+
+
+void MeshQualityOptimizer(GModel *gm, MeshQualOptParameters &p)
+{
+  Msg::StatusBar(true, "Optimizing mesh quality...");
+
+  MeshOptParameters par;
+  par.dim = p.dim;
+  par.onlyVisible = p.onlyVisible;
+  par.fixBndNodes = p.fixBndNodes;
+  QualPatchDefParameters patchDef(p);
+  par.patchDef = &patchDef;
+  par.optDisplay = 30;
+  par.verbose = 4;
+
+  ObjContribScaledNodeDispSq<ObjContribFuncSimple> nodeDistFunc(p.weightFixed, p.weightFree);
+  ObjContribNCJ<ObjContribFuncBarrierMovMin> minNCJBarFunc(1.);
+  minNCJBarFunc.setTarget(p.minTargetNCJ, 1.);
+  ObjContribNCJ<ObjContribFuncBarrierFixMinMovMax> minMaxNCJBarFunc(1.);
+  minMaxNCJBarFunc.setTarget(p.maxTargetNCJ, 1.);
+
+  MeshOptParameters::PassParameters minJacPass;
+  minJacPass.barrierIterMax = p.optPassMax;
+  minJacPass.optIterMax = p.itMax;
+  minJacPass.contrib.push_back(&nodeDistFunc);
+  minJacPass.contrib.push_back(&minNCJBarFunc);
+  par.pass.push_back(minJacPass);
+
+  if (p.maxTargetNCJ > 0.) {
+    MeshOptParameters::PassParameters minMaxJacPass;
+    minMaxJacPass.barrierIterMax = p.optPassMax;
+    minMaxJacPass.optIterMax = p.itMax;
+    minMaxJacPass.contrib.push_back(&nodeDistFunc);
+    minMaxJacPass.contrib.push_back(&minMaxNCJBarFunc);
+    par.pass.push_back(minMaxJacPass);
+  }
+
+  meshOptimizer(gm, par);
+
+  p.CPU = par.CPU;
+  p.minNCJ = minMaxNCJBarFunc.getMin();
+  p.maxNCJ = minMaxNCJBarFunc.getMax();
+
+  Msg::StatusBar(true, "Done optimizing high order mesh (%g s)", p.CPU);
+}
diff --git a/contrib/MeshQualityOptimizer/MeshQualityOptimizer.h b/contrib/MeshQualityOptimizer/MeshQualityOptimizer.h
new file mode 100644
index 0000000000000000000000000000000000000000..287b3a418559943d25e7099178616392a1877d39
--- /dev/null
+++ b/contrib/MeshQualityOptimizer/MeshQualityOptimizer.h
@@ -0,0 +1,68 @@
+// 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 _MESHQUALITYOPTIMIZER_H_
+#define _MESHQUALITYOPTIMIZER_H_
+
+class GModel;
+
+struct MeshQualOptParameters {
+  double minTargetNCJ, maxTargetNCJ;
+  double weightFixed ; // weight of the energy for fixed nodes
+  double weightFree ; // weight of the energy for free nodes
+  int nbLayers ; // number of layers taken around a bad element
+  int dim ; // which dimension to optimize
+  int itMax ; // max number of iterations in the optimization process
+  int optPassMax ; // max number of optimization passes
+  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
+  bool fixBndNodes;  // how jacobians are computed and if points can move on boundaries
+  int strategy; // 0 = connected blobs, 1 = adaptive one-by-one
+  int maxAdaptBlob; // Max. nb. of blob adaptation interations
+  int adaptBlobLayerFact; // Growth factor in number of layers for blob adaptation
+  double adaptBlobDistFact; // Growth factor in distance factor for blob adaptation
+
+  int SUCCESS ; // 0 --> success , 1 --> Not converged
+  double minNCJ, maxNCJ; // after optimization, range of jacobians
+  double CPU; // Time for optimization
+
+  MeshQualOptParameters ()
+    : minTargetNCJ(0.5), maxTargetNCJ(1.5), weightFixed(1000.),
+      weightFree (1.), nbLayers (6) , dim(3) , itMax(300), optPassMax(50),
+      onlyVisible(true), distanceFactor(12), fixBndNodes(false), strategy(0),
+      maxAdaptBlob(3), adaptBlobLayerFact(2.), adaptBlobDistFact(2.)
+  {
+  }
+};
+
+void MeshQualityOptimizer(GModel *gm, MeshQualOptParameters &p);
+
+#endif
diff --git a/wrappers/gmshpy/CMakeLists.txt b/wrappers/gmshpy/CMakeLists.txt
index 7a34421ddafebd817a4722ad8bb1266c3a80fb55..687dc25866416fe2e87fab0362cc8380d0da7b77 100644
--- a/wrappers/gmshpy/CMakeLists.txt
+++ b/wrappers/gmshpy/CMakeLists.txt
@@ -141,6 +141,7 @@ set (GMSH_PYTHON_EXTRA_INCLUDE
   Mesh/meshGFaceLloyd.h
   contrib/HighOrderMeshOptimizer/OptHomRun.h
   contrib/HighOrderMeshOptimizer/OptHomElastic.h
+  contrib/MeshQualityOptimizer/MeshQualityOptimizer.h
   Post/PViewAsSimpleFunction.h
   Post/PViewDataList.h
   Post/PViewFactory.h
diff --git a/wrappers/gmshpy/gmshMesh.i b/wrappers/gmshpy/gmshMesh.i
index 8974de1a4d9fba4b7c334ed3c5fe2eeba429b854..361cfed3e9c4ef1aa06fb98b0df7cb4bda1579fb 100644
--- a/wrappers/gmshpy/gmshMesh.i
+++ b/wrappers/gmshpy/gmshMesh.i
@@ -18,6 +18,7 @@
   #include "OptHomRun.h"
   #include "OptHomElastic.h"
   #include "OptHomFastCurving.h"
+  #include "MeshQualityOptimizer.h"
 #endif
 #if defined(HAVE_METIS) || defined(HAVE_CHACO)
   #include "meshPartition.h"
@@ -57,6 +58,7 @@ namespace std {
 %include "OptHomRun.h"
 %include "OptHomElastic.h"
 %include "OptHomFastCurving.h"
+%include "MeshQualityOptimizer.h"
 #endif
 #if defined(HAVE_METIS) || defined(HAVE_CHACO)
 %include "meshPartition.h"