diff --git a/CMakeLists.txt b/CMakeLists.txt
index d23017c98d70f1501841195ce9dfa2a7195936cd..b0341479028e829cb389b2c7dc87385435b05048 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -148,6 +148,8 @@ set(GMSH_API
   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/HighOrderMeshOptimizer/OptHomIntegralBoundaryDist.h
+  contrib/HighOrderMeshOptimizer/OptHomCADDist.h
   contrib/HighOrderMeshOptimizer/OptHomObjContribScaledJac.h
   contrib/HighOrderMeshOptimizer/OptHomObjContribMetricMin.h
   contrib/HighOrderMeshOptimizer/OptHomObjContribCADDist.h
diff --git a/contrib/HighOrderMeshOptimizer/OptHOM.cpp b/contrib/HighOrderMeshOptimizer/OptHOM.cpp
index 1b2f0307c019a5d2fbdb3a1e2ccf2fe840e5d2a3..3200e1bd2f70974a1dabd147b80b0ba48766f8ba 100644
--- a/contrib/HighOrderMeshOptimizer/OptHOM.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHOM.cpp
@@ -283,7 +283,7 @@ bool OptHOM::addBndObjGrad(double factor, double &Obj, alglib::real_1d_array &gr
     for (unsigned int i=0;i<(*it)->triangles.size(); i++){
       // compute the distance from the geometry to the mesh
       if(doWeCompute[i]){
-        const double d = MFaceGFaceDistance ( (*it)->triangles[i] , *it , &gsfT, &normalsToCAD);
+        const double d = MFaceGFaceDistanceOld((*it)->triangles[i], *it, &gsfT, &normalsToCAD);
         dist[(*it)->triangles[i]] = d;
         maxDistCAD = std::max(maxDistCAD,d);
         distCAD += d * factor;
@@ -309,7 +309,7 @@ bool OptHOM::addBndObjGrad(double factor, double &Obj, alglib::real_1d_array &gr
               v->setParameter(0,t_+eps);
               v->setXYZ(gp.x(),gp.y(),gp.z());
               const double distT = dist[t];
-              double deriv =  (MFaceGFaceDistance ( t , *it , &gsfT, &normalsToCAD) - distT)  /eps;
+              double deriv =  (MFaceGFaceDistanceOld(t, *it, &gsfT, &normalsToCAD) - distT) /eps;
               v->setXYZ(pp.x(),pp.y(),pp.z());
               v->setParameter(0,t_);
               gradObj[index] += deriv * factor;
@@ -330,7 +330,7 @@ bool OptHOM::addBndObjGrad(double factor, double &Obj, alglib::real_1d_array &gr
               GPoint gp = (*it)->point(uu+eps,vv);
               v->setParameter(0,uu+eps);
               v->setXYZ(gp.x(),gp.y(),gp.z());
-              double deriv = (MFaceGFaceDistance ( t , *it, &gsfT, &normalsToCAD ) - distT)  /eps;
+              double deriv = (MFaceGFaceDistanceOld(t, *it, &gsfT, &normalsToCAD) - distT) /eps;
               v->setXYZ(pp.x(),pp.y(),pp.z());
               v->setParameter(0,uu);
               gradObj[index] += deriv * factor;
@@ -338,7 +338,7 @@ bool OptHOM::addBndObjGrad(double factor, double &Obj, alglib::real_1d_array &gr
               gp = (*it)->point(uu,vv+eps);
               v->setParameter(1,vv+eps);
               v->setXYZ(gp.x(),gp.y(),gp.z());
-              deriv = (MFaceGFaceDistance ( t , *it, &gsfT, &normalsToCAD ) - distT)  /eps;
+              deriv = (MFaceGFaceDistanceOld(t, *it, &gsfT, &normalsToCAD) - distT) /eps;
               v->setXYZ(pp.x(),pp.y(),pp.z());
               v->setParameter(1,vv);
               gradObj[index+1] += deriv * factor;
diff --git a/contrib/HighOrderMeshOptimizer/OptHomCADDist.cpp b/contrib/HighOrderMeshOptimizer/OptHomCADDist.cpp
index 4644d77435aa66665fc54eef96b489d76a0fc7ad..5e3d16b0076d037f17173129a13f6c44cd3d9b16 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomCADDist.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomCADDist.cpp
@@ -3,12 +3,16 @@
 #include "MLine.h"
 #include "MTriangle.h"
 #include "GModel.h"
+
+#include "GEdge.h"
+#include "JacobianBasis.h"
+#include "BasisFactory.h"
 #include "OptHomCADDist.h"
 
 
-double MFaceGFaceDistance(MTriangle *t, GFace *gf,
-                          std::vector<std::vector<SVector3> > *gsfT,
-                          std::map<MVertex*,SVector3> *normalsToCAD) {
+double MFaceGFaceDistanceOld(MTriangle *t, GFace *gf,
+                             std::vector<std::vector<SVector3> > *gsfT,
+                             std::map<MVertex*,SVector3> *normalsToCAD) {
   const double h = t->maxEdge();
   double jac[3][3];
   double distFace = 0.0;
@@ -48,7 +52,7 @@ double MFaceGFaceDistance(MTriangle *t, GFace *gf,
 }
 
 
-double MLineGEdgeDistance (MLine *l, GEdge *ge, FILE *f) {
+double MLineGEdgeDistanceOld(MLine *l, GEdge *ge, FILE *f) {
   const nodalBasis &elbasis = *l->getFunctionSpace();
   const double h = .25*0.5*distance (l->getVertex (0), l->getVertex (1) ) / (l->getNumVertices()-1);
   double jac[3][3];
@@ -84,6 +88,93 @@ double MLineGEdgeDistance (MLine *l, GEdge *ge, FILE *f) {
 }
 
 
+double distToCAD1D(const GradientBasis *gb, const fullMatrix<double> &nodesXYZ,
+                   const std::vector<SVector3> &tanCAD, double edLength)
+{
+  const int nV = nodesXYZ.size1();
+  const double h = .25*0.5*edLength/(nV-1);
+  fullMatrix<double> dxyzdX(nV, 3);
+  gb->getGradientsFromNodes(nodesXYZ, &dxyzdX, 0, 0);
+  double dist = 0.;
+  for (int i=0; i<nV; i++) {
+    SVector3 tanMesh(dxyzdX(i, 0), dxyzdX(i, 1), dxyzdX(i, 2));
+    tanMesh.normalize();
+    SVector3 diff = (dot(tanCAD[i], tanMesh) > 0) ?
+                    tanCAD[i] - tanMesh : tanCAD[i] + tanMesh;
+    dist += diff.norm();
+  }
+  return h*dist;
+}
+
+
+double distToCAD2D(const GradientBasis *gb, const fullMatrix<double> &nodesXYZ,
+                   const std::vector<SVector3> &normCAD)
+{
+  const int nV = nodesXYZ.size1();
+  fullMatrix<double> dxyzdX(nV, 3),dxyzdY(nV, 3);
+  gb->getGradientsFromNodes(nodesXYZ, &dxyzdX, &dxyzdY, 0);
+  double dist = 0.;
+  for (int i=0; i<nV; i++) {
+    const double nz = dxyzdX(i, 0) * dxyzdY(i, 1) - dxyzdX(i, 1) * dxyzdY(i, 0);
+    const double ny = -dxyzdX(i, 0) * dxyzdY(i, 2) + dxyzdX(i, 2) * dxyzdY(i, 0);
+    const double nx = dxyzdX(i, 1) * dxyzdY(i, 2) - dxyzdX(i, 2) * dxyzdY(i, 1);
+    SVector3 normMesh(nx, ny, nz);
+    normMesh.normalize();
+    SVector3 diff = (dot(normCAD[i], normMesh) > 0) ?
+                    normCAD[i] - normMesh : normCAD[i] + normMesh;
+    dist += diff.norm();
+  }
+  return dist;
+}
+
+
+double MLineGEdgeDistance(MLine *l, GEdge *ge)
+{
+  const int nV = l->getNumVertices();
+  const GradientBasis *gb = BasisFactory::getGradientBasis(FuncSpaceData(l));
+  const double edLength = l->getLength();
+
+  // Coordinates of vertices
+  fullMatrix<double> nodesXYZ(nV, 3);
+  l->getNodesCoord(nodesXYZ);
+
+  // Tangent to CAD at vertices
+  std::vector<SVector3> tanCAD(nV);
+  for (int i=0; i<nV; i++) {
+    double tCAD;
+    reparamMeshVertexOnEdge(l->getVertex(i), ge, tCAD);
+    tanCAD[i] = ge->firstDer(tCAD);
+    tanCAD[i].normalize();
+  }
+
+  // Compute distance
+  return distToCAD1D(gb, nodesXYZ, tanCAD, edLength);
+}
+
+
+double MFaceGFaceDistance(MElement *el, GFace *gf)
+{
+  const int nV = el->getNumVertices();
+  const GradientBasis *gb = BasisFactory::getGradientBasis(FuncSpaceData(el));
+
+  // Coordinates of vertices
+  fullMatrix<double> nodesXYZ(nV, 3);
+  el->getNodesCoord(nodesXYZ);
+
+  // Normal to CAD at vertices
+  std::vector<SVector3> normCAD(nV);
+  for (int i=0; i<nV; i++) {
+    SPoint2 pCAD;
+    reparamMeshVertexOnFace(el->getVertex(i), gf, pCAD);
+    normCAD[i] = gf->normal(pCAD);
+    normCAD[i].normalize();
+  }
+
+  // Compute distance
+  return distToCAD2D(gb, nodesXYZ, normCAD);
+}
+
+
 void distanceFromElementsToGeometry(GModel *gm, int dim, std::map<MElement*,double> &distances){
 
   std::map<MEdge,double,Less_Edge> dist2Edge;
@@ -134,35 +225,22 @@ void distanceFromElementsToGeometry(GModel *gm, int dim, std::map<MElement*,doub
 
 double distanceToGeometry(GModel *gm)
 {
-
-  FILE *f = fopen("toto.pos","w");
-  fprintf(f,"View \"\"{\n");
-
   double Obj = 0.0;
 
-  for (GModel::eiter it = gm->firstEdge(); it != gm->lastEdge(); ++it){
-    if ((*it)->geomType() == GEntity::Line)continue;
-    for (unsigned int i=0;i<(*it)->lines.size(); i++){
-      //Obj += MLineGEdgeDistance ( (*it)->lines[i] , *it,f );
-      Obj = std::max(MLineGEdgeDistance ( (*it)->lines[i] , *it, f ),Obj);
-    }
+  for (GModel::eiter it = gm->firstEdge(); it != gm->lastEdge(); ++it) {
+    if ((*it)->geomType() == GEntity::Line) continue;
+    for (unsigned int i=0;i<(*it)->lines.size(); i++)
+      Obj = std::max(MLineGEdgeDistance((*it)->lines[i], *it), Obj);
   }
+  printf("DISTANCE TO GEOMETRY : 1D PART %22.15E\n",Obj);
 
-    printf("DISTANCE TO GEOMETRY : 1D PART %22.15E\n",Obj);
-
-  for(GModel::fiter it = gm->firstFace(); it != gm->lastFace(); ++it){
-    if ((*it)->geomType() == GEntity::Plane)continue;
-    //    printf("FACE %d with %d triangles\n",(*it)->tag(),(*it)->triangles.size());
-    for (unsigned int i=0;i<(*it)->triangles.size(); i++){
-      //Obj += MFaceGFaceDistance ( (*it)->triangles[i] , *it );
-      Obj = std::max(Obj,MFaceGFaceDistance ( (*it)->triangles[i] , *it ));
+  for(GModel::fiter it = gm->firstFace(); it != gm->lastFace(); ++it) {
+    if ((*it)->geomType() == GEntity::Plane) continue;
+    for (unsigned int i=0;i<(*it)->triangles.size(); i++) {
+      Obj = std::max(Obj,MFaceGFaceDistance( (*it)->triangles[i] , *it ));
     }
   }
-
   printf("DISTANCE TO GEOMETRY : 1D AND 2D PART %22.15E\n",Obj);
-  fprintf(f,"};\n");
-  fclose(f);
+
   return Obj;
 }
-
-
diff --git a/contrib/HighOrderMeshOptimizer/OptHomCADDist.h b/contrib/HighOrderMeshOptimizer/OptHomCADDist.h
index 465335331580b57c2b921c46cdcb6f30d2689031..d91deb6f96d689f90426c076090aa2e2fe962092 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomCADDist.h
+++ b/contrib/HighOrderMeshOptimizer/OptHomCADDist.h
@@ -4,6 +4,7 @@
 #define OPTHOMCADDIST_H_
 
 
+class GradientBasis;
 class GModel;
 class MElement;
 class MLine;
@@ -11,10 +12,17 @@ class MTriangle;
 class GEdge;
 class GFace;
 
-double MFaceGFaceDistance(MTriangle *t, GFace *gf,
-                          std::vector<std::vector<SVector3> > *gsfT=0,
-                          std::map<MVertex*,SVector3> *normalsToCAD=0);
-double MLineGEdgeDistance (MLine *l, GEdge *ge, FILE *f = 0);
+double MFaceGFaceDistanceOld(MTriangle *t, GFace *gf,
+                            std::vector<std::vector<SVector3> > *gsfT=0,
+                            std::map<MVertex*,SVector3> *normalsToCAD=0);
+double MLineGEdgeDistanceOld(MLine *l, GEdge *ge, FILE *f = 0);
+
+double distToCAD1D(const GradientBasis *gb, const fullMatrix<double> &nodesXYZ,
+                   const std::vector<SVector3> &tanCAD, double edLength);
+double distToCAD2D(const GradientBasis *gb, const fullMatrix<double> &nodesXYZ,
+                   const std::vector<SVector3> &normCAD);
+double MLineGEdgeDistance (MLine *l, GEdge *ge);
+double MFaceGFaceDistance(MElement *el, GFace *gf);
 
 void distanceFromElementsToGeometry(GModel *gm, int dim, std::map<MElement*, double> &distances);
 double distanceToGeometry(GModel *gm);
diff --git a/contrib/HighOrderMeshOptimizer/OptHomObjContribCADDist.h b/contrib/HighOrderMeshOptimizer/OptHomObjContribCADDist.h
index aee3baed41b9dfe77e91acf13527713bd8e5ca1f..7c31c4178e78af8372780b2da3bc1c67167d8fe0 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomObjContribCADDist.h
+++ b/contrib/HighOrderMeshOptimizer/OptHomObjContribCADDist.h
@@ -10,7 +10,7 @@ template<class FuncType>
 class ObjContribCADDist : public ObjContrib, public FuncType
 {
 public:
-  ObjContribCADDist(double weight, double geomTol);
+  ObjContribCADDist(double weight, double refDist);
   virtual ~ObjContribCADDist() {}
   virtual ObjContrib *copy() const;
   virtual void initialize(Patch *mesh);
@@ -24,14 +24,14 @@ public:
 protected:
   Patch *_mesh;
   double _weight;
-  double _geomTol;
+  double _refDist;
 };
 
 
 template<class FuncType>
-ObjContribCADDist<FuncType>::ObjContribCADDist(double weight, double geomTol) :
+ObjContribCADDist<FuncType>::ObjContribCADDist(double weight, double refDist) :
   ObjContrib("CADDist", FuncType::getNamePrefix()+"CADDist"),
-  _mesh(0), _weight(weight), _geomTol(geomTol)
+  _mesh(0), _weight(weight), _refDist(refDist)
 {
 }
 
@@ -47,7 +47,7 @@ template<class FuncType>
 void ObjContribCADDist<FuncType>::initialize(Patch *mesh)
 {
   _mesh = mesh;
-
+  _mesh->initScaledCADDist(_refDist);
   updateMinMax();
   FuncType::initialize(_min, _max);
 }
@@ -59,16 +59,26 @@ bool ObjContribCADDist<FuncType>::addContrib(double &Obj, alglib::real_1d_array
   _min = BIGVAL;
   _max = -BIGVAL;
 
-  std::vector<double> gradF;
-  for (int iEl = 0; iEl < _mesh->nEl(); iEl++) {
+  const int bndDim = _mesh->dim()-1;
+
+  for (int iBndEl = 0; iBndEl < _mesh->nBndEl(); iBndEl++) {
+    const int nVEl = _mesh->nNodBndEl(iBndEl);
     double f;
-    if (_mesh->bndDistAndGradients(iEl, f, gradF, _geomTol)) {
-      _min = std::min(_min, f);
-      _max = std::max(_max, f);
-      Obj += FuncType::compute(f) * _weight;
-      const double dFact = _weight * FuncType::computeDiff(f);
-      for (size_t i = 0; i < _mesh->nPCEl(iEl); ++i)
-        gradObj[_mesh->indPCEl(iEl, i)] += gradF[i] * dFact;
+    std::vector<double> gradF(nVEl*bndDim);
+    _mesh->scaledCADDistAndGradients(iBndEl, f, gradF);
+    _min = std::min(_min, f);
+    _max = std::max(_max, f);
+    Obj += FuncType::compute(f) * _weight;
+    const double dFact = _weight * FuncType::computeDiff(f);
+    for (int i=0; i<nVEl; i++) {
+      const int iFVi = _mesh->bndEl2FV(iBndEl, i);
+      if (iFVi >= 0) {                                                                        // Skip if not free vertex
+        if (bndDim == 1) gradObj[_mesh->indPCFV(iFVi, 0)] += gradF[i] * dFact;                // 2D
+        else {                                                                                // 3D
+          gradObj[_mesh->indPCFV(iFVi, 0)] += gradF[2*i] * dFact;
+          gradObj[_mesh->indPCFV(iFVi, 1)] += gradF[2*i+1] * dFact;
+        }
+      }
     }
   }
 
@@ -82,13 +92,15 @@ void ObjContribCADDist<FuncType>::updateMinMax()
   _min = BIGVAL;
   _max = -BIGVAL;
 
-  std::vector<double> dumGradF;
-  for (int iEl = 0; iEl < _mesh->nEl(); iEl++) {
+  const int bndDim = _mesh->dim()-1;
+
+  for (int iBndEl = 0; iBndEl < _mesh->nBndEl(); iBndEl++) {
+    const int nVEl = _mesh->nNodBndEl(iBndEl);
     double f;
-    if (_mesh->bndDistAndGradients(iEl, f, dumGradF, _geomTol)) {
-      _min = std::min(_min, f);
-      _max = std::max(_max, f);
-    }
+    std::vector<double> dumGradF(nVEl*bndDim);
+    _mesh->scaledCADDistAndGradients(iBndEl, f, dumGradF);
+    _min = std::min(_min, f);
+    _max = std::max(_max, f);
   }
 }
 
diff --git a/contrib/HighOrderMeshOptimizer/OptHomObjContribCADDistOld.h b/contrib/HighOrderMeshOptimizer/OptHomObjContribCADDistOld.h
index 944cac46bbb253c870d49a365e58fcacbb9f4e47..8031ba314c51d0a50c622cc07b47608d381271aa 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomObjContribCADDistOld.h
+++ b/contrib/HighOrderMeshOptimizer/OptHomObjContribCADDistOld.h
@@ -30,7 +30,7 @@ protected:
 
 template<class FuncType>
 ObjContribCADDistOld<FuncType>::ObjContribCADDistOld(double weight, double geomTol) :
-  ObjContrib("CADDist", FuncType::getNamePrefix()+"CADDist"),
+  ObjContrib("CADDistOld", FuncType::getNamePrefix()+"CADDistOld"),
   _mesh(0), _weight(weight), _geomTol(geomTol)
 {
 }
diff --git a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
index 69e612606ea0cd6944008cb1aedc45f801bbba7f..0cd51f319212a06a09d96bc917c769f3ad2b992c 100644
--- a/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
+++ b/contrib/HighOrderMeshOptimizer/OptHomRun.cpp
@@ -691,6 +691,7 @@ void HighOrderMeshOptimizer(GModel *gm, OptHomParameters &p)
 #include "GEntity.h"
 //#include "MElement.h"
 //#include "OptHomRun.h"
+#include "OptHomCADDist.h"
 #include "MeshOptCommon.h"
 #include "MeshOptObjContribFunc.h"
 #include "MeshOptObjContrib.h"
@@ -706,6 +707,7 @@ struct HOPatchDefParameters : public MeshOptPatchDef
   HOPatchDefParameters(const OptHomParameters &p);
   virtual ~HOPatchDefParameters() {}
   virtual double elBadness(MElement *el, GEntity* gEnt) const;
+  virtual double bndElBadness(MElement *el, GEntity* gEnt) const;
   virtual double maxDistance(MElement *el) const;
   virtual int inPatch(const SPoint3 &badBary,
                       double limDist, MElement *el,
@@ -714,7 +716,7 @@ private:
   double jacMin, jacMax;
   double distanceFactor;
   bool optCAD;
-  double optCADDistMax, optCADWeight, discrTolerance;
+  double optCADDistMax, optCADWeight;
 };
 
 
@@ -737,7 +739,6 @@ HOPatchDefParameters::HOPatchDefParameters(const OptHomParameters &p)
   optCAD = p.optCAD;
   optCADDistMax = p.optCADDistMax;
   optCADWeight = p.optCADWeight;
-  discrTolerance = p.discrTolerance;
 }
 
 
@@ -746,11 +747,23 @@ double HOPatchDefParameters::elBadness(MElement *el, GEntity* gEnt) const
   double jmin, jmax;
   el->scaledJacRange(jmin, jmax);
   double badness = std::min(jmin-jacMin, 0.) + std::min(jacMax-jmax, 0.);
+  return badness;
+}
+
+
+double HOPatchDefParameters::bndElBadness(MElement *el, GEntity* gEnt) const
+{
   if (optCAD) {
-    const double dist = computeBndDist(el, 2, fabs(discrTolerance));
-    badness += optCADWeight*std::min(optCADDistMax-dist, 0.);
+    if (el->getType() == TYPE_LIN) {                                              // 2D
+      if (gEnt->geomType() != GEntity::Line)                                      // Straight geometric line -> no distance
+        return MLineGEdgeDistance(static_cast<MLine*>(el), gEnt->cast2Edge());
+    }
+    else {                                                                        // 3D
+      if (gEnt->geomType() != GEntity::Plane)                                     // Straight geometric plance -> no distance
+        return MFaceGFaceDistance(el, gEnt->cast2Face());
+    }
   }
-  return badness;
+  return 1.;
 }
 
 
@@ -778,6 +791,7 @@ void HighOrderMeshOptimizerNew(GModel *gm, OptHomParameters &p)
   par.fixBndNodes = p.fixBndNodes;
   par.useGeomForPatches = false;
   par.useGeomForOpt = false;
+  par.useBoundaries = p.optCAD;
   HOPatchDefParameters patchDef(p);
   par.patchDef = &patchDef;
   par.displayInterv = 30;
@@ -789,8 +803,8 @@ void HighOrderMeshOptimizerNew(GModel *gm, OptHomParameters &p)
   minJacBarFunc.setTarget(p.BARRIER_MIN, 1.);
   ObjContribScaledJac<ObjContribFuncBarrierFixMinMovMax> minMaxJacBarFunc(1.);
   minMaxJacBarFunc.setTarget(p.BARRIER_MAX, 1.);
-  ObjContribCADDist<ObjContribFuncSimpleTargetMax> CADDistFunc(p.optCADWeight, p.discrTolerance);
-  CADDistFunc.setTarget(p.optCADDistMax);
+  ObjContribCADDist<ObjContribFuncSimpleTargetMax> CADDistFunc(p.optCADWeight, p.optCADDistMax);
+  CADDistFunc.setTarget(1.);
 
   MeshOptPass minJacPass;
   minJacPass.maxParamUpdates = p.optPassMax;
diff --git a/contrib/MeshOptimizer/MeshOpt.cpp b/contrib/MeshOptimizer/MeshOpt.cpp
index 261a8611f64072142fa116fad56c34090792f753..a35c3125ee64da15a598f9475b07825374068324 100644
--- a/contrib/MeshOptimizer/MeshOpt.cpp
+++ b/contrib/MeshOptimizer/MeshOpt.cpp
@@ -65,9 +65,10 @@ void printProgressFunc(const alglib::real_1d_array &x, double Obj, void *MOInst)
 
 
 MeshOpt::MeshOpt(const std::map<MElement*,GEntity*> &element2entity,
+                 const std::map<MElement*, GEntity*> &bndEl2Ent,
                  const std::set<MElement*> &els, std::set<MVertex*> &toFix,
-                 const MeshOptParameters &par) :
-  patch(element2entity, els, toFix, par.fixBndNodes), _verbose(0),
+                 const std::set<MElement*> &bndEls, const MeshOptParameters &par) :
+  patch(element2entity, bndEl2Ent, els, toFix, bndEls, par.fixBndNodes), _verbose(0),
   _iPass(0), _iter(0), _intervDisplay(0), _initObj(0)
 {
   _allObjFunc.resize(par.pass.size());
diff --git a/contrib/MeshOptimizer/MeshOpt.h b/contrib/MeshOptimizer/MeshOpt.h
index 844d0ca61e54b4e43438277f9a6dde991a6bf380..9c7a21c8085f34b9cf5cb4631d9352ed35305422 100644
--- a/contrib/MeshOptimizer/MeshOpt.h
+++ b/contrib/MeshOptimizer/MeshOpt.h
@@ -48,8 +48,9 @@ class MeshOpt
 public:
   Patch patch;
   MeshOpt(const std::map<MElement*, GEntity*> &element2entity,
+          const std::map<MElement*, GEntity*> &bndEl2Ent,
           const std::set<MElement*> &els, std::set<MVertex*> &toFix,
-          const MeshOptParameters &par);
+          const std::set<MElement*> &bndEls, const MeshOptParameters &par);
   ~MeshOpt();
   int optimize(const MeshOptParameters &par);
   void updateMesh(const alglib::real_1d_array &x);
diff --git a/contrib/MeshOptimizer/MeshOptCommon.h b/contrib/MeshOptimizer/MeshOptCommon.h
index 151d115d1cf00264932ffc9bb0d99488df90476b..eddbcc042fbed34c85bd0f9b6f03a492b22079ec 100644
--- a/contrib/MeshOptimizer/MeshOptCommon.h
+++ b/contrib/MeshOptimizer/MeshOptCommon.h
@@ -55,6 +55,8 @@ public:
   virtual ~MeshOptPatchDef() {}
   virtual double elBadness(MElement *el,                // Determine "badness" of a given element (for patch creation)
                            GEntity* gEnt) const = 0;
+  virtual double bndElBadness(MElement *el,             // Determine "badness" of a given boundary element (for patch creation)
+                              GEntity* gEnt) const = 0;
   virtual double maxDistance(MElement *el) const = 0;   // Compute max. distance to a given bad element for elements in patch
   virtual int inPatch(const SPoint3 &badBary,           // Determine whether a given element should be included in the patch around a...
                       double limDist,                   // ... given bad element barycenter, with a limit distance if needed. Output: ...
@@ -78,6 +80,7 @@ struct MeshOptParameters {                              // Parameters controllin
   bool onlyVisible ;                                    // Apply optimization to visible entities ONLY
   bool fixBndNodes;                                     // If points can move on boundaries
   bool useGeomForPatches, useGeomForOpt;                // Whether to use info from CAD for creation of patches and for optimization
+  bool useBoundaries;                                   // Whether to use boundary elements
   MeshOptPatchDef *patchDef;
   std::vector<MeshOptPass> pass;
   int displayInterv;                                    // Sampling rate in opt. iterations for display
diff --git a/contrib/MeshOptimizer/MeshOptPatch.cpp b/contrib/MeshOptimizer/MeshOptPatch.cpp
index b6d457cd91b1fd49dbf6c91f240b0febd2f45495..0f657ddf023bcc13e72badf983a200173d1d9d4e 100644
--- a/contrib/MeshOptimizer/MeshOptPatch.cpp
+++ b/contrib/MeshOptimizer/MeshOptPatch.cpp
@@ -36,13 +36,15 @@
 #include "BasisFactory.h"
 #include "CondNumBasis.h"
 #include "OptHomIntegralBoundaryDist.h"
+#include "OptHomCADDist.h"
 #include "qualityMeasures.h"
 #include "MeshOptPatch.h"
 
 
 Patch::Patch(const std::map<MElement*, GEntity*> &element2entity,
-           const std::set<MElement*> &els, std::set<MVertex*> &toFix,
-           bool fixBndNodes) :
+             const std::map<MElement*, GEntity*> &bndEl2Ent,
+             const std::set<MElement*> &els, std::set<MVertex*> &toFix,
+             const std::set<MElement*> &bndEls, bool fixBndNodes) :
   _typeLengthScale(LS_NONE), _invLengthScaleSq(0.)
 {
 
@@ -95,6 +97,39 @@ Patch::Patch(const std::map<MElement*, GEntity*> &element2entity,
   if (nonGeoMove) Msg::Warning("Some vertices will be moved along local lines "
                                "or planes, they may not remain on the exact geometry");
 
+  // Initialize boundary elements and related connectivity if required
+  if (!bndEls.empty()) {
+    int nBndElts = bndEls.size();
+    _bndEl.resize(nBndElts);
+    _bndEl2Ent.resize(nBndElts);
+    _bndEl2V.resize(nBndElts);
+    _bndEl2FV.resize(nBndElts);
+    int iBndEl = 0;
+    bool unknownVert = false;
+    for(std::set<MElement*>::iterator it = bndEls.begin();
+        it != bndEls.end(); ++it, ++iBndEl) {
+      MElement* bndEl = *it;
+      _bndEl[iBndEl] = bndEl;
+      std::map<MElement*,GEntity*>::const_iterator itBndEl2Ent = bndEl2Ent.find(bndEl);
+      _bndEl2Ent[iBndEl] = (itBndEl2Ent == bndEl2Ent.end()) ? 0 : itBndEl2Ent->second;
+      int nBndElVerts = bndEl->getNumVertices();
+      _bndEl2V[iBndEl].resize(nBndElVerts);
+      _bndEl2FV[iBndEl].resize(nBndElVerts);
+      for (int iVBndEl=0; iVBndEl<nBndElVerts; iVBndEl++) {
+        MVertex *vert = bndEl->getVertex(iVBndEl);
+        std::vector<MVertex*>::iterator itV = std::find(_vert.begin(), _vert.end(), vert);
+        if (itV == _vert.end()) unknownVert = true;
+        else _bndEl2V[iBndEl][iVBndEl] = std::distance(_vert.begin(), itV);
+        std::vector<MVertex*>::iterator itFV = std::find(_freeVert.begin(),
+                                                         _freeVert.end(), vert);
+        if (itFV == _freeVert.end()) _bndEl2FV[iBndEl][iVBndEl] = -1;
+        else _bndEl2FV[iBndEl][iVBndEl] = std::distance(_freeVert.begin(), itFV);
+      }
+    }
+    if (unknownVert) Msg::Error("Unknown vertices in boundary element "
+                                "at patch initialization");
+  }
+
   // Initial coordinates
   _ixyz.resize(nVert());
   for (int iV = 0; iV < nVert(); iV++) _ixyz[iV] = _vert[iV]->point();
@@ -309,6 +344,12 @@ void Patch::initMetricMin()
 }
 
 
+void Patch::initScaledCADDist(double refCADDist)
+{
+  _invRefCADDist = 1./refCADDist;
+}
+
+
 void Patch::calcNormalEl2D(int iEl, NormalScaling scaling,
                            fullMatrix<double> &elNorm, bool ideal)
 {
@@ -524,6 +565,107 @@ bool Patch::bndDistAndGradients(int iEl, double &f, std::vector<double> &gradF,
 }
 
 
+void Patch::scaledCADDistAndGradients(int iBndEl, double &scaledDist,
+                                      std::vector<double> &gradScaledDist)
+{
+  const std::vector<int> &iV = _bndEl2V[iBndEl], &iFV = _bndEl2FV[iBndEl];
+  const int nV = iV.size();
+  const GradientBasis *gb = BasisFactory::getGradientBasis(FuncSpaceData(_bndEl[iBndEl]));
+
+  // Coordinates of nodes
+  fullMatrix<double> nodesXYZ(nV, 3);
+  for (int i = 0; i < nV; i++) {
+    const int &iVi = iV[i];
+    nodesXYZ(i,0) = _xyz[iVi].x();
+    nodesXYZ(i,1) = _xyz[iVi].y();
+    nodesXYZ(i,2) = _xyz[iVi].z();
+  }
+
+  // Compute distance and gradients (CAUTION: returns gradients w.r.t. vertices, not free vertices)
+  if (_dim == 2) {                                                                        // 2D
+    const GEdge *ge = _bndEl2Ent[iBndEl]->cast2Edge();
+    const Range<double> parBounds = ge->parBounds(0);
+    const double eps = 1.e-6 * (parBounds.high()-parBounds.low());
+    std::vector<SVector3> tanCAD(nV);
+    for (int i=0; i<nV; i++) {
+      const int &iVi = iV[i], &iFVi = iFV[i];
+      MVertex* &vert = _vert[iVi];
+      double tCAD;
+      if (iFVi >= 0)                                                                       // If free vertex, ...
+        tCAD = _uvw[iFVi].x();                                                             // ... get stored param. coord. (can be only line).
+      else {                                                                               // Otherwise, get param. coord. from CAD.
+        if(ge->getBeginVertex() && ge->getBeginVertex()->mesh_vertices[0] == vert)
+          tCAD = parBounds.low();
+        else if(ge->getEndVertex() && ge->getEndVertex()->mesh_vertices[0] == vert)
+          tCAD = parBounds.high();
+        else
+          tCAD = ge->parFromPoint(_xyz[iVi]);
+      }
+      tanCAD[i] = ge->firstDer(tCAD);                                                     // Compute tangent at vertex
+      tanCAD[i].normalize();                                                              // Normalize tangent
+    }
+    const double edLength = _xyz[iV[0]].distance(_xyz[iV[1]]);                            // Get edge length
+    scaledDist = _invRefCADDist * distToCAD1D(gb, nodesXYZ, tanCAD, edLength);
+    for (int i=0; i<nV; i++) {
+      const int &iFVi = iFV[i];
+      if (iFVi < 0) continue;                                                             // Skip if not free vertex
+      const double xS = nodesXYZ(i, 0), yS = nodesXYZ(i, 1), zS = nodesXYZ(i, 2);         // Save coord. of perturbed node for FD
+      const SVector3 tanCADS = tanCAD[i];                                                 // Save tangent to CAD at perturbed node
+      const double tCAD = _uvw[iFVi].x() + eps;                                           // New param. coord. of perturbed node
+      GPoint gp = ge->point(tCAD);                                                        // New coord. of perturbed node
+      nodesXYZ(i, 0) = gp.x(); nodesXYZ(i, 1) = gp.y(); nodesXYZ(i, 2) = gp.z();
+      tanCAD[i] = ge->firstDer(tCAD);                                                     // New tangent to CAD at perturbed node
+      tanCAD[i].normalize();                                                              // Normalize new tangent
+      double sDistDiff = _invRefCADDist * distToCAD1D(gb, nodesXYZ, tanCAD, edLength);    // Compute distance with perturbed node
+      gradScaledDist[i] = (sDistDiff-scaledDist) / eps;                                   // Compute gradient
+      nodesXYZ(i, 0) = xS; nodesXYZ(i, 1) = yS; nodesXYZ(i, 2) = zS;                      // Restore coord. of perturbed node
+      tanCAD[i] = tanCADS;                                                                // Restore tan. to CAD at perturbed node
+    }
+  }
+  else {                                                                                  // 3D
+    const GFace *gf = _bndEl2Ent[iBndEl]->cast2Face();
+    const Range<double> parBounds0 = gf->parBounds(0), parBounds1 = gf->parBounds(1);
+    const double eps0 = 1.e-6 * (parBounds0.high()-parBounds0.low());
+    const double eps1 = 1.e-6 * (parBounds1.high()-parBounds1.low());
+    std::vector<SVector3> normCAD(nV);
+    for (int i=0; i<nV; i++) {
+      const int &iVi = iV[i], &iFVi = iFV[i];
+      MVertex* &vert = _vert[iVi];
+      SPoint2 pCAD;
+      if ((iFVi >= 0) && (vert->onWhat() == gf))                                          // If free vertex and on surface, ...
+        pCAD = SPoint2(_uvw[iFVi].x(), _uvw[iFVi].y());                                   // ... get stored param. coord.
+      else
+        reparamMeshVertexOnFace(vert, gf, pCAD);                                          // Otherwise, get param. coord. from CAD.
+      normCAD[i] = gf->normal(pCAD);                                                      // Compute normal at vertex
+      normCAD[i].normalize();                                                             // Normalize normal
+    }
+    scaledDist = _invRefCADDist * distToCAD2D(gb, nodesXYZ, normCAD);
+    for (int i=0; i<nV; i++) {
+      const int &iFVi = iFV[i];
+      if (iFVi < 0) continue;                                                             // Skip if not free vertex
+      const double xS = nodesXYZ(i, 0), yS = nodesXYZ(i, 1), zS = nodesXYZ(i, 2);         // Save coord. of perturbed node for FD
+      const SVector3 tanCADS = normCAD[i];                                                // Save normal to CAD at perturbed node
+      const SPoint2 pCAD0 = SPoint2(_uvw[iFVi].x()+eps0, _uvw[iFVi].y());                 // New param. coord. of perturbed node in 1st dir.
+      GPoint gp0 = gf->point(pCAD0);                                                      // New coord. of perturbed node in 1st dir.
+      nodesXYZ(i, 0) = gp0.x(); nodesXYZ(i, 1) = gp0.y(); nodesXYZ(i, 2) = gp0.z();
+      normCAD[i] = gf->normal(pCAD0);                                                     // New normal to CAD at perturbed node in 1st dir.
+      normCAD[i].normalize();                                                             // Normalize new normal
+      double sDistDiff0 = _invRefCADDist * distToCAD2D(gb, nodesXYZ, normCAD);            // Compute distance with perturbed node in 1st dir.
+      gradScaledDist[2*i] = (sDistDiff0-scaledDist) / eps0;                               // Compute gradient in 1st dir.
+      const SPoint2 pCAD1 = SPoint2(_uvw[iFVi].x(), _uvw[iFVi].y()+eps1);                 // New param. coord. of perturbed node in 2nd dir.
+      GPoint gp1 = gf->point(pCAD1);                                                      // New coord. of perturbed node in 2nd dir.
+      nodesXYZ(i, 0) = gp1.x(); nodesXYZ(i, 1) = gp1.y(); nodesXYZ(i, 2) = gp1.z();
+      normCAD[i] = gf->normal(pCAD1);                                                     // New normal to CAD at perturbed node in 2nd dir.
+      normCAD[i].normalize();                                                             // Normalize new normal
+      double sDistDiff1 = _invRefCADDist * distToCAD2D(gb, nodesXYZ, normCAD);            // Compute distance with perturbed node in 2nd dir.
+      gradScaledDist[2*i+1] = (sDistDiff1-scaledDist) / eps0;                             // Compute gradient in 2nd dir.
+      nodesXYZ(i, 0) = xS; nodesXYZ(i, 1) = yS; nodesXYZ(i, 2) = zS;                      // Restore coord. of perturbed node
+      normCAD[i] = tanCADS;                                                               // Restore tan. to CAD at perturbed node
+    }
+  }
+}
+
+
 void Patch::initIdealJac()
 {
   // Initialize _nBezEl
diff --git a/contrib/MeshOptimizer/MeshOptPatch.h b/contrib/MeshOptimizer/MeshOptPatch.h
index 6b1c0d16b0aead1816d8468306dfda0086e1cbb0..e0d7fb1bc98647a8be321705d12d204a2565ab99 100644
--- a/contrib/MeshOptimizer/MeshOptPatch.h
+++ b/contrib/MeshOptimizer/MeshOptPatch.h
@@ -47,7 +47,9 @@ class Patch
 {
 public:
   Patch(const std::map<MElement*,GEntity*> &element2entity,
-        const std::set<MElement*> &els, std::set<MVertex*> &toFix, bool fixBndNodes);
+        const std::map<MElement*, GEntity*> &bndEl2Ent,
+        const std::set<MElement*> &els, std::set<MVertex*> &toFix,
+        const std::set<MElement*> &bndEls, bool fixBndNodes);
 
   // Mesh entities and variables
   inline const int &dim() { return _dim; }
@@ -85,11 +87,17 @@ public:
   // High-order: scaled Jacobian and metric measures, distance to CAD
   inline const int &nBezEl(int iEl) { return _nBezEl[iEl]; }
   inline int indGSJ(int iEl, int l, int iPC) { return iPC*_nBezEl[iEl]+l; }
+  inline int nBndEl() { return _bndEl.size(); }
+  inline int nNodBndEl(int iBndEl) { return _bndEl2V[iBndEl].size(); }
+  inline const int &bndEl2FV(int iBndEl, int i) { return _bndEl2FV[iBndEl][i]; }
   void initScaledJac();
   void scaledJacAndGradients(int iEl, std::vector<double> &sJ, std::vector<double> &gSJ);
   void initMetricMin();
   void metricMinAndGradients(int iEl, std::vector<double> &sJ, std::vector<double> &gSJ);
-  bool bndDistAndGradients(int iEl, double &f , std::vector<double> &gradF, double eps);
+  bool bndDistAndGradients(int iEl, double &f, std::vector<double> &gradF, double eps);
+  void initScaledCADDist(double refCADDist);
+  void scaledCADDistAndGradients(int iBndEl, double &scaledDist,
+                                 std::vector<double> &gradScaledDist);
 
   // Mesh quality
   inline const int &nIJacEl(int iEl) { return _nIJacEl[iEl]; }
@@ -147,6 +155,10 @@ private:
   std::vector<int> _nBezEl;                                     // Number of Bezier poly. for an el.
   std::vector<fullMatrix<double> > _JacNormEl;                  // Normals to 2D elements for Jacobian regularization and scaling
   std::vector<double> _invStraightJac;                          // Initial Jacobians for 3D elements
+  std::vector<MElement*> _bndEl;                                // Boundary elements
+  std::vector<std::vector<int> > _bndEl2V, _bndEl2FV;           // Vertices & corresponding free vertices on the boundary elements
+  std::vector<GEntity*> _bndEl2Ent;                             // Geometric entities corresponding to the boundary elements
+  double _invRefCADDist;
   void calcNormalEl2D(int iEl, NormalScaling scaling,
                       fullMatrix<double> &elNorm, bool ideal);
 
diff --git a/contrib/MeshOptimizer/MeshOptimizer.cpp b/contrib/MeshOptimizer/MeshOptimizer.cpp
index f52165efd7323afd704a5971b548251daaf538ef..3365908d13e72f816483ce3abf214adc2ab6b0ae 100644
--- a/contrib/MeshOptimizer/MeshOptimizer.cpp
+++ b/contrib/MeshOptimizer/MeshOptimizer.cpp
@@ -55,6 +55,7 @@ typedef std::set<MElement*> elSet;
 typedef elSet::iterator elSetIter;
 typedef std::set<MVertex*> vertSet;
 typedef std::map<MElement*, GEntity*> elEntMap;
+typedef std::map<MElement*, MElement*> elElMap;
 
 typedef std::map<MVertex*, elVec> vertElVecMap;
 typedef std::map<MElement*, elSet> elElSetMap;
@@ -178,6 +179,96 @@ void calcElement2Entity(GEntity *entity, elEntMap &element2entity)
 }
 
 
+MElement *getFaceInBndElements(const MFace &f, std::list<GFace*> gFaces)
+{
+  for (std::list<GFace*>::iterator itGF = gFaces.begin(); itGF != gFaces.end(); itGF++) {
+    if (f.getNumVertices() == 3) {
+      std::vector<MTriangle*> &tris = (*itGF)->triangles;
+      for (int iEl=0; iEl<tris.size(); iEl++)
+        if (tris[iEl]->getFace(0) == f) return tris[iEl];
+    }
+    else {
+      std::vector<MQuadrangle*> &quads = (*itGF)->quadrangles;
+      for (int iEl=0; iEl<quads.size(); iEl++)
+        if (quads[iEl]->getFace(0) == f) return quads[iEl];
+    }
+  }
+  return 0;
+}
+
+
+MElement *getEdgeInBndElements(const MEdge &e, std::list<GEdge*> gEdges)
+{
+  for (std::list<GEdge*>::iterator itGE = gEdges.begin(); itGE != gEdges.end(); itGE++) {
+    std::vector<MLine*> &lines = (*itGE)->lines;
+    for (int iEl=0; iEl<lines.size(); iEl++)
+      if (lines[iEl]->getEdge(0) == e) return lines[iEl];
+  }
+  return 0;
+}
+
+
+void calcBndInfo(GEntity *entity, elElMap &el2BndEl, elEntMap &bndEl2Ent)
+{
+  typedef std::list<GFace*> GFaceList;
+  typedef std::list<GEdge*> GEdgeList;
+
+  if (entity->dim() == 3) {                                                               // 3D
+
+    // Fill boundary element -> GEntity connectivity
+    GFaceList gFaces = entity->faces();
+    for (GFaceList::iterator itGF = gFaces.begin(); itGF != gFaces.end(); itGF++) {
+      std::vector<MTriangle*> &tris = (*itGF)->triangles;
+      for (int i=0; i<tris.size(); i++)
+        bndEl2Ent.insert(std::pair<MElement*, GEntity*>(tris[i], *itGF));
+      std::vector<MQuadrangle*> &quads = (*itGF)->quadrangles;
+      for (int i=0; i<quads.size(); i++)
+        bndEl2Ent.insert(std::pair<MElement*, GEntity*>(quads[i], *itGF));
+    }
+
+    // Fill element -> boundary element connectivity
+    for (int iEl = 0; iEl < entity->getNumMeshElements(); iEl++) {
+      MElement *el = entity->getMeshElement(iEl);
+      int nBndVert = 0;                                                                   // Compute nb. of bnd. vertices in element
+      for (int iV=0; iV<el->getNumPrimaryVertices(); iV++)
+        if (el->getVertex(iV)->onWhat() != entity) nBndVert++;
+      if (nBndVert >= 3)                                                                  // If more than 3 primary vert. on bnd., look for bnd. face(s)
+        for (int iF=0; iF<el->getNumFaces(); iF++) {
+          MElement *bndEl = getFaceInBndElements(el->getFace(iF), gFaces);
+          if (bndEl != 0)
+            el2BndEl.insert(std::pair<MElement*, MElement*>(el, bndEl));
+        }
+    }
+
+  }
+  else {                                                                                  // 2D
+
+    // Fill boundary element -> GEntity connectivity
+    GEdgeList gEdges = entity->edges();
+    for (GEdgeList::iterator itGE = gEdges.begin(); itGE != gEdges.end(); itGE++) {
+      std::vector<MLine*> &lines = (*itGE)->lines;
+      for (int i=0; i<lines.size(); i++)
+        bndEl2Ent.insert(std::pair<MElement*, GEntity*>(lines[i], *itGE));
+    }
+
+    // Fill element -> boundary element connectivity
+    for (int iEl = 0; iEl < entity->getNumMeshElements(); iEl++) {
+      MElement *el = entity->getMeshElement(iEl);
+      int nBndVert = 0;                                                                   // Compute nb. of bnd. vertices in element
+      for (int iV=0; iV<el->getNumPrimaryVertices(); iV++)
+        if (el->getVertex(iV)->onWhat() != entity) nBndVert++;
+      if (nBndVert >= 2)                                                                  // If more than 2 primary vert. on bnd., look for bnd. edge(s)
+        for (int iE=0; iE<el->getNumEdges(); iE++) {
+          MElement *bndEl = getEdgeInBndElements(el->getEdge(iE), gEdges);
+          if (bndEl != 0)
+            el2BndEl.insert(std::pair<MElement*, MElement*>(el, bndEl));
+        }
+    }
+
+  }
+}
+
+
 std::vector<elSetVertSetPair> getDisjointPatches(const vertElVecMap &vertex2elements,
                                                  const elEntMap &element2entity,
                                                  const elSet &badElements,
@@ -253,8 +344,25 @@ std::vector<elSetVertSetPair> getDisjointPatches(const vertElVecMap &vertex2elem
 }
 
 
+// Get (bad) boundary elements adjacent to patch
+void getAdjacentBndElts(const elElMap &el2BndEl, const elEntMap &bndEl2Ent,
+                        const elSet &elts, elSet &bndElts, MeshOptParameters &par)
+{
+  for (elSetIter itEl=elts.begin(); itEl!=elts.end(); itEl++) {
+    elElMap::const_iterator itBndEl = el2BndEl.find(*itEl);
+    if (itBndEl != el2BndEl.end()) {
+      MElement* bndEl = itBndEl->second;
+      elEntMap::const_iterator itEnt = bndEl2Ent.find(bndEl);
+      if (par.patchDef->bndElBadness(bndEl, itEnt->second) < 0.) bndElts.insert(bndEl);
+    }
+  }
+}
+
+
 void optimizeDisjointPatches(const vertElVecMap &vertex2elements,
                              const elEntMap &element2entity,
+                             const elElMap &el2BndEl,
+                             const elEntMap &bndEl2Ent,
                              elSet &badasses, MeshOptParameters &par)
 {
   par.success = 1;
@@ -262,16 +370,27 @@ void optimizeDisjointPatches(const vertElVecMap &vertex2elements,
   const elEntMap &e2ePatch = par.useGeomForPatches ? element2entity : elEntMap();
   const elEntMap &e2eOpt = par.useGeomForOpt ? element2entity : elEntMap();
 
+  // Get patches
   std::vector<elSetVertSetPair> toOptimize = getDisjointPatches(vertex2elements,
                                                                  e2ePatch, badasses, par);
 
+  // Get boundary elements adjacent to patch if required
+  std::vector<elSet> bndElts;
+  bndElts.resize(toOptimize.size());
+  if (!el2BndEl.empty()) {
+    for (int iPatch = 0; iPatch < toOptimize.size(); ++iPatch)
+      getAdjacentBndElts(el2BndEl, bndEl2Ent, toOptimize[iPatch].first, bndElts[iPatch], par);
+  }
+
   for (int iPatch = 0; iPatch < toOptimize.size(); ++iPatch) {
 
     // Initialize optimization and output if asked
     if (par.verbose > 1)
-      Msg::Info("Optimizing patch %i/%i composed of %i elements", iPatch,
-                      toOptimize.size()-1, toOptimize[iPatch].first.size());
-    MeshOpt opt(e2eOpt, toOptimize[iPatch].first, toOptimize[iPatch].second, par);
+      Msg::Info("Optimizing patch %i/%i composed of %i elements, "
+                "%i boundary elements", iPatch, toOptimize.size()-1,
+                toOptimize[iPatch].first.size(), bndElts[iPatch].size());
+    MeshOpt opt(e2eOpt, bndEl2Ent, toOptimize[iPatch].first,
+                toOptimize[iPatch].second, bndElts[iPatch], par);
     if (par.verbose > 3) {
       std::ostringstream ossI1;
       ossI1 << "initial_patch-" << iPatch << ".msh";
@@ -327,6 +446,8 @@ MElement *getWorstElement(elSet &badElts,
 
 void optimizeOneByOne(const vertElVecMap &vertex2elements,
                       const elEntMap &element2entity,
+                      const elElMap &el2BndEl,
+                      const elEntMap &bndEl2Ent,
                       elSet badElts, MeshOptParameters &par)
 {
   par.success = 1;
@@ -367,11 +488,17 @@ void optimizeOneByOne(const vertElVecMap &vertex2elements,
                           badElts.begin(),badElts.end(),
                           std::inserter(toOptimize, toOptimize.end()));
 
+      // Get boundary elements adjacent to patch if required
+      elSet bndElts;
+      if (!el2BndEl.empty()) {
+        getAdjacentBndElts(el2BndEl, bndEl2Ent, toOptimize, bndElts, par);
+      }
+
       // Initialize optimization and output if asked
       if (par.verbose > 1)
         Msg::Info("Optimizing patch %i (max. %i remaining) composed of %4d elements",
                                             iBadEl, badElts.size(), toOptimize.size());
-      MeshOpt opt(e2eOpt, toOptimize, toFix, par);
+      MeshOpt opt(e2eOpt, bndEl2Ent, toOptimize, toFix, bndElts, par);
       if (par.verbose > 3) {
         std::ostringstream ossI1;
         ossI1 << "initial_patch-" << iBadEl << ".msh";
@@ -442,7 +569,8 @@ void meshOptimizer(GModel *gm, MeshOptParameters &par)
   gm->getEntities(entities);
 
   vertElVecMap vertex2elements;
-  elEntMap element2entity;
+  elEntMap element2entity, bndEl2Ent;
+  elElMap el2BndEl;
   elSet badElts;
   for (int iEnt = 0; iEnt < entities.size(); ++iEnt) {
     GEntity* &entity = entities[iEnt];
@@ -453,18 +581,28 @@ void meshOptimizer(GModel *gm, MeshOptParameters &par)
     calcVertex2Elements(par.dim, entity, vertex2elements);
     if ((par.useGeomForPatches) || (par.useGeomForOpt))
       calcElement2Entity(entity, element2entity);
-    for (int iEl = 0; iEl < entity->getNumMeshElements();iEl++) {                               // Detect bad elements
-      double jmin, jmax;
+    if (par.useBoundaries) calcBndInfo(entity, el2BndEl, bndEl2Ent);
+    for (int iEl = 0; iEl < entity->getNumMeshElements(); iEl++) {                               // Detect bad elements
       MElement *el = entity->getMeshElement(iEl);
-      if ((el->getDim() == par.dim) && (par.patchDef->elBadness(el, entity) < 0.))
-        badElts.insert(el);
+      if (el->getDim() == par.dim) {
+        if (par.patchDef->elBadness(el, entity) < 0.) badElts.insert(el);
+        else if (par.useBoundaries) {
+          elElMap::iterator bndElIt = el2BndEl.find(el);
+          if (bndElIt != el2BndEl.end()) {
+            MElement* &bndEl = bndElIt->second;
+            if (par.patchDef->bndElBadness(bndEl, bndEl2Ent[bndEl])) badElts.insert(el);
+          }
+        }
+      }
     }
   }
 
   if (par.patchDef->strategy == MeshOptPatchDef::STRAT_DISJOINT)
-    optimizeDisjointPatches(vertex2elements, element2entity, badElts, par);
+    optimizeDisjointPatches(vertex2elements, element2entity,
+                            el2BndEl, bndEl2Ent, badElts, par);
   else if (par.patchDef->strategy == MeshOptPatchDef::STRAT_ONEBYONE)
-    optimizeOneByOne(vertex2elements, element2entity, badElts, par);
+    optimizeOneByOne(vertex2elements, element2entity,
+                     el2BndEl, bndEl2Ent, badElts, par);
   else
     Msg::Error("Unknown strategy %d for mesh optimization", par.patchDef->strategy);
 
diff --git a/contrib/MeshQualityOptimizer/MeshQualityOptimizer.cpp b/contrib/MeshQualityOptimizer/MeshQualityOptimizer.cpp
index 0faff2097a02671a7b2fd1df8968f47fe20f94bb..faf6a8452b5e21bdb1c9f625b390fcbde2ef4935 100644
--- a/contrib/MeshQualityOptimizer/MeshQualityOptimizer.cpp
+++ b/contrib/MeshQualityOptimizer/MeshQualityOptimizer.cpp
@@ -23,6 +23,7 @@ struct QualPatchDefParameters : public MeshOptPatchDef
   QualPatchDefParameters(const MeshQualOptParameters &p);
   virtual ~QualPatchDefParameters() {}
   virtual double elBadness(MElement *el, GEntity* gEnt) const;
+  virtual double bndElBadness(MElement *el, GEntity* gEnt) const { return 1.; }
   virtual double maxDistance(MElement *el) const;
   virtual int inPatch(const SPoint3 &badBary, double limDist,
                       MElement *el, GEntity* gEnt) const;
@@ -126,6 +127,7 @@ void MeshQualityOptimizer(GModel *gm, MeshQualOptParameters &p)
   par.fixBndNodes = p.fixBndNodes;
   par.useGeomForPatches = p.excludeBL;
   par.useGeomForOpt = false;
+  par.useBoundaries = false;
   QualPatchDefParameters patchDef(p);
   par.patchDef = &patchDef;
   par.displayInterv = 20;