Skip to content
Snippets Groups Projects
Select Git revision
  • fce8a10fa0a01d5314b3fa6f4d69ba00c87df237
  • master default
  • cgnsUnstructured
  • partitioning
  • poppler
  • HighOrderBLCurving
  • gmsh_3_0_4
  • gmsh_3_0_3
  • gmsh_3_0_2
  • gmsh_3_0_1
  • gmsh_3_0_0
  • gmsh_2_16_0
  • gmsh_2_15_0
  • gmsh_2_14_1
  • gmsh_2_14_0
  • gmsh_2_13_2
  • gmsh_2_13_1
  • gmsh_2_12_0
  • gmsh_2_11_0
  • gmsh_2_10_1
  • gmsh_2_10_0
  • gmsh_2_9_3
  • gmsh_2_9_2
  • gmsh_2_9_1
  • gmsh_2_9_0
  • gmsh_2_8_6
26 results

tutorial.html

Blame
  • Forked from gmsh / gmsh
    Source project has a limited visibility.
    OptHomFastCurving.cpp 41.09 KiB
    // 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@onelab.info>.
    //
    // Contributors: Thomas Toulorge, Jonathan Lambrechts
    
    #include <cstdio>
    #include <sstream>
    #include <fstream>
    #include <string>
    #include <cmath>
    #include "OptHomFastCurving.h"
    #include "GmshConfig.h"
    #include "GModel.h"
    #include "Gmsh.h"
    #include "MLine.h"
    #include "MTriangle.h"
    #include "MQuadrangle.h"
    #include "MTetrahedron.h"
    #include "MHexahedron.h"
    #include "MPrism.h"
    #include "MEdge.h"
    #include "MFace.h"
    #include "OS.h"
    #include "SVector3.h"
    #include "BasisFactory.h"
    #include "MetaEl.h"
    #include "qualityMeasuresJacobian.h"
    #include "CADDistances.h"
    
    
    
    namespace {
    
    
    
    typedef std::map<MEdge, std::vector<MElement*>, Less_Edge> MEdgeVecMEltMap;
    typedef std::map<MFace, std::vector<MElement*>, Less_Face> MFaceVecMEltMap;
    
    
    
    // Compute edge -> element connectivity (for 2D elements)
    void calcEdge2Elements(GEntity *entity, MEdgeVecMEltMap &ed2el)
    {
      for (size_t iEl = 0; iEl < entity->getNumMeshElements(); iEl++) {
        MElement *elt = entity->getMeshElement(iEl);
        if (elt->getDim() == 2)
          for (int iEdge = 0; iEdge < elt->getNumEdges(); iEdge++) {
            ed2el[elt->getEdge(iEdge)].push_back(elt);
          }
      }
    }
    
    
    
    // Compute face -> element connectivity (for 3D elements)
    void calcFace2Elements(GEntity *entity, MFaceVecMEltMap &face2el)
    {
      for (size_t iEl = 0; iEl < entity->getNumMeshElements(); iEl++) {
        MElement *elt = entity->getMeshElement(iEl);
        if (elt->getDim() == 3)
          for (int iFace = 0; iFace < elt->getNumFaces(); iFace++)
            face2el[elt->getFace(iFace)].push_back(elt);
      }
    }
    
    
    
    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());
        }
      }
    }
    
    
    
    inline void insertIfCurved(MElement *el, std::list<MElement*> &bndEl)
    {
      static const double curvedTol = 1.e-3;                                        // Tolerance to consider element as curved
    
      const double normalDispCurved = curvedTol*el->getInnerRadius();               // Threshold in normal displacement to consider element as curved
      const int dim = el->getDim();
    
      // Compute unit normal to straight edge/face
      SVector3 normal = (dim == 1) ? el->getEdge(0).normal() :
                                     el->getFace(0).normal();
    
      // Get functional spaces
      const nodalBasis *lagBasis = el->getFunctionSpace();
      const fullMatrix<double> &uvw = lagBasis->points;
      const int &nV = uvw.size1();
      const nodalBasis *lagBasis1 = el->getFunctionSpace(1);
      const int &nV1 = lagBasis1->points.size1();
    
      // Get first-order vertices
      std::vector<SPoint3> xyz1(nV1);
      for (int iV = 0; iV < nV1; ++iV) xyz1[iV] = el->getVertex(iV)->point();
    
      // Check normal displacement at each HO vertex
      for (int iV = nV1; iV < nV; ++iV) {                                           // Loop over HO nodes
        double f[256];
        lagBasis1->f(uvw(iV, 0), (dim > 1) ? uvw(iV, 1) : 0., 0., f);
        SPoint3 xyzS(0.,0.,0.);
        for (int iSF = 0; iSF < nV1; ++iSF) xyzS += xyz1[iSF]*f[iSF];               // Compute location of node in straight element
        const SVector3 vec(xyzS, el->getVertex(iV)->point());
        const double normalDisp = fabs(dot(vec, normal));                           // Normal component of displacement
        if (normalDisp > normalDispCurved) {
          bndEl.push_back(el);
          break;
        }
      }
    }
    
    
    
    void getOppositeEdgeQuad(MElement *el, const MEdge &elBaseEd, MEdge &elTopEd,
                             double &edLenMin, double &edLenMax)
    {
      // Find base face
      int iElBaseEd, elBaseEdOrient;
      el->getEdgeInfo(elBaseEd, iElBaseEd, elBaseEdOrient);
    
      // Determine opposite and side edges
      int iOppEd, iSideEd0, iSideEd1;
      if (iElBaseEd == 0) { iOppEd = 2; iSideEd0 = 1; iSideEd1 = 3;}
      else if (iElBaseEd == 1) { iOppEd = 3; iSideEd0 = 0; iSideEd1 = 2;}
      else if (iElBaseEd == 2) { iOppEd = 0; iSideEd0 = 1; iSideEd1 = 3;}
      else { iOppEd = 1; iSideEd0 = 0; iSideEd1 = 2;}
      const MEdge elOppEd = el->getEdge(iOppEd);
    
      // Create top edge
      if (elBaseEdOrient > 0)
        elTopEd = MEdge(elOppEd.getVertex(1), elOppEd.getVertex(0));
      else
        elTopEd = elOppEd;
    
      // Compute max. and min. edge lengths
      edLenMax = elTopEd.length();
      edLenMin = std::min(el->getEdge(iSideEd0).length(),
                          el->getEdge(iSideEd1).length());
    }
    
    
    
    void getOppositeEdgeTri(MElement *el, const MEdge &elBaseEd, MEdge &elTopEd,
                            double &edLenMin, double &edLenMax)
    {
      static const double BIG = 1e300;
    
      // Find base face
      int iElBaseEd, iDum;
      el->getEdgeInfo(elBaseEd, iElBaseEd, iDum);
      edLenMin = BIG;
      edLenMax = -BIG;
      MEdge elMaxEd;
    
      // Look for largest edge except base one
      for (int iElEd = 0; iElEd < el->getNumEdges(); iElEd++) {
        if (iElEd != iElBaseEd) {
          MEdge edTest = el->getEdge(iElEd);
          double edLenTest = edTest.length();
          if (edLenTest < edLenMin) edLenMin = edLenTest;
          if (edLenTest > edLenMax) {
            edLenMax = edLenTest;
            elMaxEd = edTest;
          }
        }
      }
    
      // Build top edge from max edge (with right orientation)
      if (elBaseEd.getVertex(0) == elMaxEd.getVertex(0)) elTopEd = elMaxEd;
      else elTopEd = MEdge(elMaxEd.getVertex(1), elMaxEd.getVertex(0));
    }
    
    
    
    void getColumnQuad(MEdgeVecMEltMap &ed2el, const FastCurvingParameters &p,
                       MEdge &elBaseEd, std::vector<MElement*> &blob,
                       MElement* &aboveElt)
    {
      const double maxDP = std::cos(p.maxAngle);
    
      MElement *el = 0;
    
      for (int iLayer = 0; iLayer < p.maxNumLayers; iLayer++) {
        std::vector<MElement*> newElts = ed2el[elBaseEd];
        if ((iLayer > 0) && (newElts.size() < 2)) { aboveElt = 0; break; }
        el = (newElts[0] == el) ? newElts[1] : newElts[0];
        aboveElt = el;
        if (el->getType() != TYPE_QUA) break;
        MEdge elTopEd;
        double edLenMin, edLenMax;
        getOppositeEdgeQuad(el, elBaseEd, elTopEd, edLenMin, edLenMax);
    
        if (edLenMin > edLenMax*p.maxRho) break;
        const double dp = dot(elBaseEd.normal(), elTopEd.normal());
        if (std::abs(dp) < maxDP) break;
    
        blob.push_back(el);
        elBaseEd = elTopEd;
      }
    }
    
    
    
    void getColumnTri(MEdgeVecMEltMap &ed2el, const FastCurvingParameters &p,
                      MEdge &elBaseEd, std::vector<MElement*> &blob,
                      MElement* &aboveElt)
    {
      const double maxDP = std::cos(p.maxAngle);
      const double maxDPIn = std::cos(p.maxAngleInner);
    
      MElement *el0 = 0, *el1 = 0;
    
      for (int iLayer = 0; iLayer < p.maxNumLayers; iLayer++) {
        // Get first element in layer
        std::vector<MElement*> newElts0 = ed2el[elBaseEd];
        if ((iLayer > 0) && (newElts0.size() < 2)) { aboveElt = 0; break; }
        el0 = (newElts0[0] == el1) ? newElts0[1] : newElts0[0];
        aboveElt = el0;
        if (el0->getType() != TYPE_TRI) break;
        MEdge elTopEd0;
        double edLenMin0, edLenMax0;
        getOppositeEdgeTri(el0, elBaseEd, elTopEd0, edLenMin0, edLenMax0);
        const SVector3 normBase = elBaseEd.normal();
        const SVector3 normTop0 = elTopEd0.normal();
        if (std::abs(dot(normBase, normTop0)) < maxDPIn) break;
    
        // Get second element in layer
        std::vector<MElement*> newElts1 = ed2el[elTopEd0];
        if (newElts1.size() < 2) { aboveElt = 0; break; }
        el1 = (newElts1[0] == el0) ? newElts1[1] : newElts1[0];
        if (el1->getType() != TYPE_TRI) break;
        MEdge elTopEd1;
        double edLenMin1, edLenMax1;
        getOppositeEdgeTri(el1, elTopEd0, elTopEd1, edLenMin1, edLenMax1);
        const SVector3 normTop1 = elTopEd1.normal();
        if (std::abs(dot(normTop0, normTop1)) < maxDPIn) break;
    
        // Check stop criteria
        const double edLenMin = std::min(edLenMin0, edLenMin1);
        const double edLenMax = std::max(edLenMax0, edLenMax1);
        if (edLenMin > edLenMax*p.maxRho) break;
        if (std::abs(dot(normBase, normTop1)) < maxDP) break;
    
        // Add elements to blob and pass top edge to next layer
        blob.push_back(el0);
        blob.push_back(el1);
        elBaseEd = elTopEd1;
      }
    }
    
    
    
    bool getColumn2D(MEdgeVecMEltMap &ed2el, const FastCurvingParameters &p,
                     const MEdge &baseEd, std::vector<MVertex*> &baseVert,
                     std::vector<MVertex*> &topPrimVert,
                     std::vector<MElement*> &blob, MElement* &aboveElt)
    {
      // Get first element and base vertices
      std::vector<MElement*> firstElts = ed2el[baseEd];
      MElement *el = firstElts[0];
      int iFirstElEd, iDum;
      el->getEdgeInfo(baseEd, iFirstElEd, iDum);
      el->getEdgeVertices(iFirstElEd, baseVert);
      MEdge elBaseEd(baseVert[0], baseVert[1]);
    
      // Sweep column upwards by choosing largest edges in each element
      if (el->getType() == TYPE_TRI)
        getColumnTri(ed2el, p, elBaseEd, blob, aboveElt);
      else getColumnQuad(ed2el, p, elBaseEd, blob, aboveElt);
    
      topPrimVert.resize(2);
      topPrimVert[0] = elBaseEd.getVertex(0);
      topPrimVert[1] = elBaseEd.getVertex(1);
      return true;
    }
    
    
    
    void getOppositeFacePrism(MElement *el, const MFace &elBaseFace,
                              MFace &elTopFace, double &faceSurfMin,
                              double &faceSurfMax)
    {
      // Find base face
      int iElBaseFace = -1, iDum;
      el->getFaceInfo(elBaseFace, iElBaseFace, iDum, iDum);
    
      // Check side edges to find opposite vertices
      const int sideEd[3] = {2, 4, 5};
      std::vector<MVertex*> topVert(3);
      for (int iEd = 0; iEd < 3; iEd++) {
        MEdge ed = el->getEdge(sideEd[iEd]);
        for (int iV = 0; iV < 3; iV++) {
          if (elBaseFace.getVertex(iV) == ed.getVertex(0))
            topVert[iV] = ed.getVertex(1);
          else if (elBaseFace.getVertex(iV) == ed.getVertex(1))
            topVert[iV] = ed.getVertex(0);
        }
      }
      elTopFace = MFace(topVert);
    
      // Compute min. (side faces) and max. (top face) face areas
      faceSurfMax = elTopFace.approximateArea();
      MFace sideFace0 = el->getFace(2);
      faceSurfMin = sideFace0.approximateArea();
      MFace sideFace1 = el->getFace(3);
      faceSurfMin = std::min(faceSurfMin, sideFace1.approximateArea());
      MFace sideFace2 = el->getFace(4);
      faceSurfMin = std::min(faceSurfMin, sideFace2.approximateArea());
    }
    
    
    
    void getOppositeFaceHex(MElement *el, const MFace &elBaseFace, MFace &elTopFace,
                            double &faceSurfMin, double &faceSurfMax)
    {
      // Find base face
      int iElBaseFace = -1, iDum;
      el->getFaceInfo(elBaseFace, iElBaseFace, iDum, iDum);
    
      // Determine side edges
      int sideEd[4], sideFace[4];
      if ((iElBaseFace == 0) || (iElBaseFace == 5)) {
        sideEd[0] = 2; sideEd[1] = 4; sideEd[2] = 6; sideEd[3] = 7;
        sideFace[0] = 1; sideFace[1] = 2; sideFace[2] = 3; sideFace[3] = 4;
      }
      else if ((iElBaseFace == 1) || (iElBaseFace == 4)) {
        sideEd[0] = 1; sideEd[1] = 3; sideEd[2] = 10; sideEd[3] = 9;
        sideFace[0] = 0; sideFace[1] = 2; sideFace[2] = 3; sideFace[3] = 5;
      }
      else if ((iElBaseFace == 2) || (iElBaseFace == 3)) {
        sideEd[0] = 0; sideEd[1] = 5; sideEd[2] = 11; sideEd[3] = 8;
        sideFace[0] = 0; sideFace[1] = 1; sideFace[2] = 4; sideFace[3] = 5;
      }
    
      // Check side edges to find opposite vertices
      std::vector<MVertex*> topVert(4);
      for (int iEd = 0; iEd < 4; iEd++) {
        MEdge ed = el->getEdge(sideEd[iEd]);
        for (int iV = 0; iV < 4; iV++) {
          if (elBaseFace.getVertex(iV) == ed.getVertex(0))
            topVert[iV] = ed.getVertex(1);
          else if (elBaseFace.getVertex(iV) == ed.getVertex(1))
            topVert[iV] = ed.getVertex(0);
        }
      }
      elTopFace = MFace(topVert);
    
      // Compute min. (side faces) and max. (top face) face areas
      faceSurfMax = elTopFace.approximateArea();
      MFace sideFace0 = el->getFace(sideFace[0]);
      faceSurfMin = sideFace0.approximateArea();
      MFace sideFace1 = el->getFace(sideFace[1]);
      faceSurfMin = std::min(faceSurfMin, sideFace1.approximateArea());
      MFace sideFace2 = el->getFace(sideFace[2]);
      faceSurfMin = std::min(faceSurfMin, sideFace2.approximateArea());
      MFace sideFace3 = el->getFace(sideFace[3]);
      faceSurfMin = std::min(faceSurfMin, sideFace3.approximateArea());
    }
    
    
    
    void getOppositeFaceTet(MElement *el, const MFace &elBaseFace, MFace &elTopFace,
                            double &faceSurfMin, double &faceSurfMax)
    {
      static const double BIG = 1e300;
    
      int iElBaseFace = -1, iDum;
      el->getFaceInfo(elBaseFace, iElBaseFace, iDum, iDum);
      faceSurfMin = BIG;
      faceSurfMax = -BIG;
      MFace elMaxFace;
    
      // Look for largest face except base one
      for (int iElFace = 0; iElFace < el->getNumFaces(); iElFace++) {
        if (iElFace != iElBaseFace) {
          MFace faceTest = el->getFace(iElFace);
          const double faceSurfTest = faceTest.approximateArea();
          if (faceSurfTest < faceSurfMin) faceSurfMin = faceSurfTest;
          if (faceSurfTest > faceSurfMax) {
            faceSurfMax = faceSurfTest;
            elMaxFace = faceTest;
          }
        }
      }
    
      // Build top face from max face (with right correspondance)
      MVertex *maxVert[3] = {elMaxFace.getVertex(0),
                             elMaxFace.getVertex(1), elMaxFace.getVertex(2)};
      std::vector<MVertex*> topVert(3, static_cast<MVertex*>(0));
      for (int iBaseV = 0; iBaseV < 3; iBaseV++)                                    // Two vertices of elTopFace are those of elMaxFace coinciding with elBaseFace
        for (int iMaxV = 0; iMaxV < 3; iMaxV++)
          if (elBaseFace.getVertex(iBaseV) == maxVert[iMaxV]) {
            topVert[iBaseV] = maxVert[iMaxV];
            maxVert[iMaxV] = 0;
          }
      MVertex *thirdMaxVert = (maxVert[0] != 0) ? maxVert[0] :                      // Set last vertex of elTopFace as remaining vertex in elMaxFace
                              (maxVert[1] != 0) ? maxVert[1] : maxVert[2];
      if (topVert[0] == 0) topVert[0] = thirdMaxVert;
      else if (topVert[1] == 0) topVert[1] = thirdMaxVert;
      else topVert[2] = thirdMaxVert;
      elTopFace = MFace(topVert);
    }
    
    
    
    // Column of tets: assume tets obtained from subdivision of prism
    void getColumnTet(MFaceVecMEltMap &face2el,
                      const FastCurvingParameters &p, MFace &elBaseFace,
                      std::vector<MElement*> &blob, MElement* &aboveElt)
    {
      const double maxDP = std::cos(p.maxAngle);
      const double maxDPIn = std::cos(p.maxAngleInner);
    
      MElement *el0 = 0, *el1 = 0, *el2 = 0;
    
      for (int iLayer = 0; iLayer < p.maxNumLayers; iLayer++) {
        // Get first element in layer
        std::vector<MElement*> newElts0 = face2el[elBaseFace];
        if ((iLayer > 0) && (newElts0.size() < 2)) { aboveElt = 0; break; }
        el0 = (newElts0[0] == el2) ? newElts0[1] : newElts0[0];
        aboveElt = el0;
        if (el0->getType() != TYPE_TET) break;
        MFace elTopFace0;
        double faceSurfMin0, faceSurfMax0;
        getOppositeFaceTet(el0, elBaseFace, elTopFace0, faceSurfMin0, faceSurfMax0);
        const SVector3 normBase = elBaseFace.normal();
        const SVector3 normTop0 = elTopFace0.normal();
        if (std::abs(dot(normBase, normTop0)) < maxDPIn) break;
    
        // Get second element in layer
        std::vector<MElement*> newElts1 = face2el[elTopFace0];
        if (newElts1.size() < 2) { aboveElt = 0; break; }
        el1 = (newElts1[0] == el0) ? newElts1[1] : newElts1[0];
        if (el1->getType() != TYPE_TET) break;
        MFace elTopFace1;
        double faceSurfMin1, faceSurfMax1;
        getOppositeFaceTet(el1, elTopFace0, elTopFace1, faceSurfMin1, faceSurfMax1);
        const SVector3 normTop1 = elTopFace1.normal();
        if (std::abs(dot(normTop0, normTop1)) < maxDPIn) break;
    
        // Get third element in layer
        std::vector<MElement*> newElts2 = face2el[elTopFace1];
        if (newElts2.size() < 2) { aboveElt = 0; break; }
        el2 = (newElts2[0] == el1) ? newElts2[1] : newElts2[0];
        if (el2->getType() != TYPE_TET) break;
        MFace elTopFace2;
        double faceSurfMin2, faceSurfMax2;
        getOppositeFaceTet(el2, elTopFace1, elTopFace2, faceSurfMin2, faceSurfMax2);
        const SVector3 normTop2 = elTopFace2.normal();
        if (std::abs(dot(normTop1, normTop2)) < maxDPIn) break;
    
        // Check stop criteria
        const double faceSurfMin = std::min(faceSurfMin0,
                                            std::min(faceSurfMin1, faceSurfMin2));
        const double faceSurfMax = std::max(faceSurfMax0,
                                            std::min(faceSurfMax1, faceSurfMax2));
        if (faceSurfMin > faceSurfMax*p.maxRho) break;
        if (std::abs(dot(normBase, normTop2)) < maxDP) break;
    
        // Add elements to blob and pass top face to next layer
        blob.push_back(el0);
        blob.push_back(el1);
        blob.push_back(el2);
        elBaseFace = elTopFace2;
      }
    }
    
    
    
    void getColumnPrismHex(int elType, MFaceVecMEltMap &face2el,
                           const FastCurvingParameters &p, MFace &elBaseFace,
                           std::vector<MElement*> &blob, MElement* &aboveElt)
    {
      const double maxDP = std::cos(p.maxAngle);
    
      MElement *el = 0;
    
      for (int iLayer = 0; iLayer < p.maxNumLayers; iLayer++) {
        std::vector<MElement*> newElts = face2el[elBaseFace];
        if ((iLayer > 0) && (newElts.size() < 2)) { aboveElt = 0; break; }
        el = (newElts[0] == el) ? newElts[1] : newElts[0];
        aboveElt = el;
        if (el->getType() != elType) break;
    
        MFace elTopFace;
        double faceSurfMin, faceSurfMax;
        if (el->getType() == TYPE_PRI)
          getOppositeFacePrism(el, elBaseFace, elTopFace, faceSurfMin, faceSurfMax);
        else if (el->getType() == TYPE_HEX)
          getOppositeFaceHex(el, elBaseFace, elTopFace, faceSurfMin, faceSurfMax);
    
        if (faceSurfMin > faceSurfMax*p.maxRho) break;
        const double dp = dot(elBaseFace.normal(), elTopFace.normal());
        if (std::abs(dp) < maxDP) break;
    
        blob.push_back(el);
        elBaseFace = elTopFace;
      }
    }
    
    
    
    bool getColumn3D(MFaceVecMEltMap &face2el, const FastCurvingParameters &p,
                     const MFace &baseFace, std::vector<MVertex*> &baseVert,
                     std::vector<MVertex*> &topPrimVert,
                     std::vector<MElement*> &blob, MElement* &aboveElt)
    {
      // Get first element and base vertices
      const int nbBaseFaceVert = baseFace.getNumVertices();
      std::vector<MElement*> firstElts = face2el[baseFace];
      MElement *el = firstElts[0];
      int iFirstElFace = -1, iDum;
      el->getFaceInfo(baseFace, iFirstElFace, iDum, iDum);
      el->getFaceVertices(iFirstElFace, baseVert);
      MFace elBaseFace(baseVert[0], baseVert[1], baseVert[2],
                       (nbBaseFaceVert == 3) ? 0 : baseVert[3]);
    
      // Sweep column upwards by choosing largest faces in each element
      if (nbBaseFaceVert == 3) {
        if (el->getType() == TYPE_PRI)                                              // Get BL column of prisms
          getColumnPrismHex(TYPE_PRI, face2el, p, elBaseFace, blob, aboveElt);
        else if (el->getType() == TYPE_TET)
          getColumnTet(face2el, p, elBaseFace, blob, aboveElt);
      }
      else if ((nbBaseFaceVert == 4) && (el->getType() == TYPE_HEX))                // Get BL column of hexas
        getColumnPrismHex(TYPE_HEX, face2el, p, elBaseFace, blob, aboveElt);
      else return false;                                                            // Not a BL
      if (blob.size() == 0) return false;                                           // Could not find column (may not be a BL)
    
      // Create top face of column with last face vertices
      topPrimVert.resize(nbBaseFaceVert);
      topPrimVert[0] = elBaseFace.getVertex(0);
      topPrimVert[1] = elBaseFace.getVertex(1);
      topPrimVert[2] = elBaseFace.getVertex(2);
      if (nbBaseFaceVert == 4) topPrimVert[3] = elBaseFace.getVertex(3);
      return true;
    }
    
    
    
    class DbgOutputMeta {
    public:
      void addMetaEl(MetaEl &mEl) {
        MElement *elt = mEl.getMElement();
        elType_.push_back(elt->getTypeForMSH());
        nbVertEl_.push_back(elt->getNumVertices());
        for (int iV = 0; iV < elt->getNumVertices(); iV++)
          point_.push_back(elt->getVertex(iV)->point());
      }
      void write(std::string fNameBase, int tag) {
        std::ostringstream oss;
        oss << fNameBase << "_" << tag << ".msh";
        std::string fName = oss.str();
        FILE *fp = fopen(fName.c_str(), "w");
        if(!fp) return;
        fprintf(fp, "$MeshFormat\n2.2 0 8\n$EndMeshFormat\n");
        fprintf(fp, "$Nodes\n");
        fprintf(fp, "%d\n", point_.size());
        for (int iV = 0; iV < point_.size(); iV++)
          fprintf(fp, "%i %g %g %g\n", iV+1, point_[iV].x(),
                  point_[iV].y(), point_[iV].z());
        fprintf(fp, "$EndNodes\n");
        fprintf(fp, "$Elements\n");
        fprintf(fp, "%d\n", elType_.size());
        int iV = 0;
        for (int iEl = 0; iEl < elType_.size(); iEl++) {
          fprintf(fp, "%i %i 2 0 0 ", iEl+1, elType_[iEl]);
          for (int iVEl = 1; iVEl <= nbVertEl_[iEl]; iVEl++)
            fprintf(fp, " %i", iV+iVEl);
          fprintf(fp, "\n");
          iV += nbVertEl_[iEl];
        }
        fprintf(fp, "$EndElements\n");
        fclose(fp);
      }
    
    private:
      std::vector<int> elType_;
      std::vector<int> nbVertEl_;
      std::vector<SPoint3> point_;
    };
    
    
    
    class DbgOutputCol {
    public:
      void addBlob(const std::vector<MElement*> &blob) {
        for (int iEl = 0; iEl < blob.size(); iEl++) addElement(blob[iEl]);
      }
      void addElement(MElement* elt) {
        elt_.push_back(elt);
        for (int iV = 0; iV < elt->getNumVertices(); iV++)
          vert_.insert(elt->getVertex(iV));
      }
      void write(std::string fNameBase, int tag) {
        std::ostringstream oss;
        oss << fNameBase << "_" << tag << ".msh";
        std::string fName = oss.str();
        FILE *fp = fopen(fName.c_str(), "w");
        if(!fp) return;
        fprintf(fp, "$MeshFormat\n2.2 0 8\n$EndMeshFormat\n");
        fprintf(fp, "$Nodes\n");
        fprintf(fp, "%d\n", vert_.size());
        for (std::set<MVertex*>::iterator itV = vert_.begin();
             itV != vert_.end(); ++itV) {
          SPoint3 p = (*itV)->point();
          fprintf(fp, "%i %g %g %g\n", (*itV)->getNum(), p.x(), p.y(), p.z());
        }
        fprintf(fp, "$EndNodes\n");
        fprintf(fp, "$Elements\n");
        fprintf(fp, "%d\n", elt_.size());
        int iV = 0;
        for (int iEl = 0; iEl < elt_.size(); iEl++) {
          fprintf(fp, "%i %i 2 0 0 ", elt_[iEl]->getNum(),
                                      elt_[iEl]->getTypeForMSH());
          for (int iVEl = 0; iVEl < elt_[iEl]->getNumVertices(); iVEl++)
            fprintf(fp, " %i", elt_[iEl]->getVertex(iVEl)->getNum());
          fprintf(fp, "\n");
        }
        fprintf(fp, "$EndElements\n");
        fclose(fp);
      }
    
    private:
      std::set<MVertex*> vert_;
      std::vector<MElement*> elt_;
    };
    
    
    
    void curveElement(const MetaEl &metaElt, std::set<MVertex*> &movedVert,
                      MElement *elt)
    {
        makeStraight(elt, movedVert);
        for (int iV = elt->getNumPrimaryVertices();
             iV < elt->getNumVertices(); iV++) {                                  // Loop over HO vert. of each el. in blob
          MVertex* vert = elt->getVertex(iV);
          if (movedVert.find(vert) == movedVert.end()) {                          // Try to move vert. not already moved
            double xyzS[3] = {vert->x(), vert->y(), vert->z()}, xyzC[3];
            if (metaElt.straightToCurved(xyzS, xyzC)) {
              vert->setXYZ(xyzC[0], xyzC[1], xyzC[2]);
              movedVert.insert(vert);
            }
          }
        }
    }
    
    
    
    double calcCADDistSq2D(GEntity *geomEnt, MElement *bndElt)
    {
      const int nbVert = bndElt->getNumVertices();
      const int nbPrimVert = bndElt->getNumPrimaryVertices();
      const GradientBasis *gb = BasisFactory::getGradientBasis(FuncSpaceData(bndElt));
    
      // Coordinates of nodes
      fullMatrix<double> nodesXYZ(nbVert, 3);
      for (int iV = 0; iV < nbVert; iV++) {
        MVertex *vert = bndElt->getVertex(iV);
        nodesXYZ(iV, 0) = vert->x();
        nodesXYZ(iV, 1) = vert->y();
        nodesXYZ(iV, 2) = vert->z();
      }
    
      // Compute distance
      const GEdge *ge = geomEnt->cast2Edge();
      const Range<double> parBounds = ge->parBounds(0);
      std::vector<SVector3> tanCAD(nbVert);
      for (int iV = 0; iV < nbVert; iV++) {
        MVertex *vert = bndElt->getVertex(iV);
        double tCAD;
        if (vert->onWhat() == geomEnt)                                                       // If HO vertex, ...
          vert->getParameter(0, tCAD);                                                       // ... 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(vert->point());
        }
        tanCAD[iV] = ge->firstDer(tCAD);                                                     // Compute tangent at vertex
        tanCAD[iV].normalize();                                                              // Normalize tangent
      }
      return taylorDistanceSq1D(gb, nodesXYZ, tanCAD);
    }
    
    
    
    void optimizeCADDist2DP2(GEntity *geomEnt, std::vector<MVertex*> &baseVert)
    {
      static const int NPTS = 1000;
      MLine3 bndMetaElt(baseVert);
      MVertex* &vert = baseVert[2];
      const double xS = vert->x(), yS = vert->y();
      const GEdge *ge = geomEnt->cast2Edge();
      const Range<double> parBounds = ge->parBounds(0);
      const double du = (parBounds.high()-parBounds.low())/double(NPTS-1);
      double uMin = 0., distSqMin = 1e300;
      double uDbg;
      vert->getParameter(0, uDbg);
      for (double u = parBounds.low(); u < parBounds.high(); u += du) {
        vert->setParameter(0, u);
        GPoint gp = ge->point(u);
        vert->setXYZ(gp.x(), gp.y(), gp.z());
        const double distSq = calcCADDistSq2D(geomEnt, &bndMetaElt);
        if (distSq < distSqMin) { uMin = u; distSqMin = distSq; }
      }
      vert->setParameter(0, uMin);
      GPoint gp = ge->point(uMin);
      vert->setXYZ(gp.x(), gp.y(), gp.z());
    }
    
    
    
    // void calcCADDistSqAndGradients(int dim, GEntity *geomEnt, MElement *bndElt,
    //                                double &dist, std::vector<double> &gradDist)
    // {
    //   const int nbVert = bndElt->getNumVertices();
    //   const int nbPrimVert = bndElt->getNumPrimaryVertices();
    //   const GradientBasis *gb = BasisFactory::getGradientBasis(FuncSpaceData(bndElt));
    
    //   // Coordinates of nodes
    //   fullMatrix<double> nodesXYZ(nbVert, 3);
    //   for (int iV = 0; iV < nbVert; iV++) {
    //     MVertex *vert = bndElt->getVertex(iV);
    //     nodesXYZ(iV, 0) = vert->x();
    //     nodesXYZ(iV, 1) = vert->y();
    //     nodesXYZ(iV, 2) = vert->z();
    //   }
    
    //   // Compute distance and gradients
    //   if (dim == 2) {                                                                        // 2D
    //     const GEdge *ge = geomEnt->cast2Edge();
    //     const Range<double> parBounds = ge->parBounds(0);
    //     const double eps = 1.e-6 * (parBounds.high()-parBounds.low());
    //     std::vector<SVector3> tanCAD(nbVert);
    //     for (int iV = 0; iV < nbVert; iV++) {
    //       MVertex *vert = bndElt->getVertex(iV);
    //       double tCAD;
    //       if (iV >= nbPrimVert)                                                                // If HO vertex, ...
    //         vert->getParameter(0, tCAD);                                                       // ... 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(vert->point());
    //       }
    //       tanCAD[iV] = ge->firstDer(tCAD);                                                     // Compute tangent at vertex
    //       tanCAD[iV].normalize();                                                              // Normalize tangent
    //     }
    //     dist = taylorDistanceSq1D(gb, nodesXYZ, tanCAD);
    //     for (int iV = nbPrimVert; iV < nbVert; iV++) {
    //       const double xS = nodesXYZ(iV, 0), yS = nodesXYZ(iV, 1),
    //                    zS = nodesXYZ(iV, 2);                                                  // Save coord. of perturbed node for FD
    //       const SVector3 tanCADS = tanCAD[iV];                                                // Save tangent to CAD at perturbed node
    //       double tCAD;
    //       bndElt->getVertex(iV)->getParameter(0, tCAD);
    //       tCAD += eps;                                                                        // New param. coord. of perturbed node
    //       GPoint gp = ge->point(tCAD);                                                        // New coord. of perturbed node
    //       nodesXYZ(iV, 0) = gp.x();
    //       nodesXYZ(iV, 1) = gp.y();
    //       nodesXYZ(iV, 2) = gp.z();
    //       tanCAD[iV] = ge->firstDer(tCAD);                                                    // New tangent to CAD at perturbed node
    //       tanCAD[iV].normalize();                                                             // Normalize new tangent
    //       const double sDistDiff = taylorDistanceSq1D(gb, nodesXYZ, tanCAD);                  // Compute distance with perturbed node
    //       gradDist[iV-nbPrimVert] = (sDistDiff-dist) / eps;                       // Compute gradient
    //       nodesXYZ(iV, 0) = xS; nodesXYZ(iV, 1) = yS; nodesXYZ(iV, 2) = zS;                   // Restore coord. of perturbed node
    //       tanCAD[iV] = tanCADS;                                                               // Restore tan. to CAD at perturbed node
    //     }
    //   }
    //   else {                                                                                  // 3D
    //     const GFace *gf = geomEnt->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(nbVert);
    //     for (int iV = 0; iV < nbVert; iV++) {
    //       MVertex *vert = bndElt->getVertex(iV);
    //       SPoint2 pCAD;
    //       if (iV >= nbPrimVert) {                                                               // If HO vertex, get parameters
    //         vert->getParameter(0, pCAD[0]);
    //         vert->getParameter(1, pCAD[1]);
    //       }
    //       else
    //         reparamMeshVertexOnFace(vert, gf, pCAD);                                          // If not free vertex, reparametrize on surface
    //       normCAD[iV] = gf->normal(pCAD);                                                     // Compute normal at vertex
    //       normCAD[iV].normalize();                                                            // Normalize normal
    //     }
    //     dist = taylorDistanceSq2D(gb, nodesXYZ, normCAD);
    //     for (int iV = nbPrimVert; iV < nbVert; iV++) {
    //       MVertex *vert = bndElt->getVertex(iV);
    //       const double xS = nodesXYZ(iV, 0), yS = nodesXYZ(iV, 1),
    //                    zS = nodesXYZ(iV, 2);                                                  // Save coord. of perturbed node for FD
    //       const SVector3 normCADS = normCAD[iV];                                              // Save normal to CAD at perturbed node
    //       SPoint2 pCAD0;                                                                      // New param. coord. of perturbed node in 1st dir.
    //       vert->getParameter(0, pCAD0[0]);
    //       pCAD0 += eps0;
    //       vert->getParameter(1, pCAD0[1]);
    //       GPoint gp0 = gf->point(pCAD0);                                                    // New coord. of perturbed node in 1st dir.
    //       nodesXYZ(iV, 0) = gp0.x();
    //       nodesXYZ(iV, 1) = gp0.y();
    //       nodesXYZ(iV, 2) = gp0.z();
    //       normCAD[iV] = gf->normal(pCAD0);                                                  // New normal to CAD at perturbed node in 1st dir.
    //       normCAD[iV].normalize();                                                          // Normalize new normal
    //       const double sDistDiff0 = taylorDistanceSq2D(gb, nodesXYZ, normCAD);              // Compute distance with perturbed node in 1st dir.
    //       gradDist[2*(iV-nbPrimVert)] = (sDistDiff0-dist) / eps0;                           // Compute gradient in 1st dir.
    //       SPoint2 pCAD1;                                                                    // New param. coord. of perturbed node in 2nd dir.
    //       vert->getParameter(0, pCAD1[0]);
    //       vert->getParameter(1, pCAD1[1]);
    //       pCAD1 += eps1;
    //       GPoint gp1 = gf->point(pCAD1);                                                    // New coord. of perturbed node in 2nd dir.
    //       nodesXYZ(iV, 0) = gp1.x();
    //       nodesXYZ(iV, 1) = gp1.y();
    //       nodesXYZ(iV, 2) = gp1.z();
    //       normCAD[iV] = gf->normal(pCAD1);                                                   // New normal to CAD at perturbed node in 2nd dir.
    //       normCAD[iV].normalize();                                                           // Normalize new normal
    //       double sDistDiff1 = taylorDistanceSq2D(gb, nodesXYZ, normCAD);                     // Compute distance with perturbed node in 2nd dir.
    //       gradDist[2*(iV-nbPrimVert)+1] = (sDistDiff1-dist) / eps1;                          // Compute gradient in 2nd dir.
    //       nodesXYZ(iV, 0) = xS;                                                              // Restore coord. of perturbed node
    //       nodesXYZ(iV, 1) = yS;
    //       nodesXYZ(iV, 2) = zS;
    //       normCAD[iV] = normCADS;                                                            // Restore tan. to CAD at perturbed node
    //     }
    //   }
    // }
    
    
    
    double curveAndMeasureAboveEl(MetaEl &metaElt, MElement *lastElt,
                                  MElement *aboveElt, double deformFact)
    {
      metaElt.setCurvedTop(deformFact);
      std::set<MVertex*> movedVertDum;
      curveElement(metaElt, movedVertDum, lastElt);
      if (aboveElt == 0) return 1.;
      double minJacDet, maxJacDet;
      jacobianBasedQuality::minMaxJacobianDeterminant(aboveElt, minJacDet, maxJacDet);
      return minJacDet/maxJacDet;
    }
    
    
    
    void curveColumn(const FastCurvingParameters &p, GEntity *geomEnt,
                     int metaElType, std::vector<MVertex*> &baseVert,
                     const std::vector<MVertex*> &topPrimVert, MElement *aboveElt,
                     std::vector<MElement*> &blob, std::set<MVertex*> &movedVert,
                     DbgOutputMeta &dbgOut)
    {
      static const double MINQUAL = 0.01, TOL = 0.01, MAXITER = 10;
    
      // Order
      const int order = blob[0]->getPolynomialOrder();
    
      // If 2D P2 and allowed, modify base vertices to minimize distance between wall edge and CAD
      if ((p.optimizeGeometry) && (metaElType == TYPE_QUA) && (order == 2))
        optimizeCADDist2DP2(geomEnt, baseVert);
    
      // Create meta-element
      MetaEl metaElt(metaElType, order, baseVert, topPrimVert);
    
      // If allowed, curve top face of meta-element while avoiding breaking the element above
      if (p.curveOuterBL) {
        MElement* &lastElt = blob.back();
        double minJacDet, maxJacDet;
        double deformMin = 0., qualDeformMin = 1.;
        double deformMax = 1.;
        double qualDeformMax = curveAndMeasureAboveEl(metaElt, lastElt, aboveElt,
                                                      deformMax);
        if (qualDeformMax < MINQUAL) {                                                // Max deformation makes element above invalid
          for (int iter = 0; iter < MAXITER; iter++) {                                // Bisection to find max. deformation that makes element above valid
            const double deformMid = 0.5 * (deformMin + deformMax);
            const double qualDeformMid = curveAndMeasureAboveEl(metaElt, lastElt,
                                                                aboveElt, deformMid);
            if (std::abs(deformMax-deformMin) < TOL) break;
            const bool signDeformMax = (qualDeformMax < MINQUAL);
            const bool signDeformMid = (qualDeformMid < MINQUAL);
            if (signDeformMid == signDeformMax) deformMax = deformMid;
            else deformMin = deformMid;
          }
          metaElt.setFlatTop();
        }
        for (int iV = 0; iV < lastElt->getNumVertices(); iV++)
          movedVert.insert(lastElt->getVertex(iV));
      }
    
      // Curve elements
      dbgOut.addMetaEl(metaElt);
      for (int iEl = 0; iEl < blob.size(); iEl++)
        curveElement(metaElt, movedVert, blob[iEl]);
    }
    
    
    
    void curveMeshFromBndElt(MEdgeVecMEltMap &ed2el, MFaceVecMEltMap &face2el,
                             GEntity *bndEnt, MElement *bndElt,
                             std::set<MVertex*> movedVert,
                             const FastCurvingParameters &p,
                             DbgOutputMeta &dbgOut)
    {
      const int bndType = bndElt->getType();
      int metaElType;
      bool foundCol;
      std::vector<MVertex*> baseVert, topPrimVert;
      std::vector<MElement*> blob;
      MElement *aboveElt = 0;
      if (bndType == TYPE_LIN) { // 1D boundary
        MVertex *vb0 = bndElt->getVertex(0);
        MVertex *vb1 = bndElt->getVertex(1);
        metaElType = TYPE_QUA;
        MEdge baseEd(vb0, vb1);
        foundCol = getColumn2D(ed2el, p, baseEd, baseVert,
                               topPrimVert, blob, aboveElt);
      }
      else { // 2D boundary
        MVertex *vb0 = bndElt->getVertex(0);
        MVertex *vb1 = bndElt->getVertex(1);
        MVertex *vb2 = bndElt->getVertex(2);
        MVertex *vb3;
        if (bndType == TYPE_QUA) {
          vb3 = bndElt->getVertex(3);
          metaElType = TYPE_HEX;
        }
        else {
          vb3 = 0;
          metaElType = TYPE_PRI;
        }
        MFace baseFace(vb0, vb1, vb2, vb3);
        foundCol = getColumn3D(face2el, p, baseFace, baseVert, topPrimVert,
                               blob, aboveElt);
      }
      if (!foundCol || blob.empty()) return; // Skip bnd. el. if top vertices not found
      DbgOutputCol dbgOutCol;
      dbgOutCol.addBlob(blob);
      dbgOutCol.write("col_KO", bndElt->getNum());
      if (aboveElt == 0) std::cout << "DBGTT: aboveElt = 0 for bnd. elt. " << bndElt->getNum() << std::endl;
      curveColumn(p, bndEnt, metaElType, baseVert, topPrimVert, aboveElt, blob,
                  movedVert, dbgOut);
      dbgOutCol.write("col_OK", bndElt->getNum());
    }
    
    
    
    void curveMeshFromBnd(MEdgeVecMEltMap &ed2el, MFaceVecMEltMap &face2el,
                          GEntity *bndEnt, const FastCurvingParameters &p)
    {
      // Build list of bnd. elements to consider
      std::list<MElement*> bndEl;
      if (bndEnt->dim() == 1) {
        GEdge *gEd = bndEnt->cast2Edge();
        for(unsigned int i = 0; i< gEd->lines.size(); i++)
          insertIfCurved(gEd->lines[i], bndEl);
      }
      else if (bndEnt->dim() == 2) {
        GFace *gFace = bndEnt->cast2Face();
        for(unsigned int i = 0; i< gFace->triangles.size(); i++)
          insertIfCurved(gFace->triangles[i], bndEl);
        for(unsigned int i = 0; i< gFace->quadrangles.size(); i++)
          insertIfCurved(gFace->quadrangles[i], bndEl);
      }
      else
        Msg::Error("Cannot process model entity %i of dim %i", bndEnt->tag(),
                                                               bndEnt->dim());
    
      // Loop over boundary elements to curve them by columns
      DbgOutputMeta dbgOut;
      std::set<MVertex*> movedVert;
      for(std::list<MElement*>::iterator itBE = bndEl.begin();
          itBE != bndEl.end(); itBE++) // Loop over bnd. elements
        curveMeshFromBndElt(ed2el, face2el, bndEnt, *itBE, movedVert, p, dbgOut);
      dbgOut.write("meta-elements", bndEnt->tag());
    }
    
    
    
    }
    
    
    
    // Main function for fast curving
    void HighOrderMeshFastCurving(GModel *gm, FastCurvingParameters &p)
    {
      double t1 = Cpu();
      Msg::StatusBar(true, "Curving high order boundary layer mesh...");
    
      // Retrieve geometric entities
      std::vector<GEntity*> allGEnt;
      gm->getEntities(allGEnt);
    
      // Curve mesh for non-straight boundary entities
      for (int iEnt = 0; iEnt < allGEnt.size(); ++iEnt) {
        // Retrive entity
        GEntity* &gEnt = allGEnt[iEnt];
        if (gEnt->dim() != p.dim) continue;
    
        // Compute edge/face -> elt. connectivity
        Msg::Info("Computing connectivity for entity %i...", gEnt->tag());
        MEdgeVecMEltMap ed2el;
        MFaceVecMEltMap face2el;
        if (p.dim == 2) calcEdge2Elements(allGEnt[iEnt], ed2el);
        else calcFace2Elements(allGEnt[iEnt], face2el);
    
        // Retrieve boundary entities
        std::vector<GEntity*> bndEnts;
        if (p.dim == 2) {
          std::list<GEdge*> gEds = gEnt->edges();
          bndEnts = std::vector<GEntity*>(gEds.begin(), gEds.end());
        }
        else {
          std::list<GFace*> gFaces = gEnt->faces();
          bndEnts = std::vector<GEntity*>(gFaces.begin(), gFaces.end());
        }
    
        // Curve mesh from each boundary entity
        for (int iBndEnt = 0; iBndEnt < bndEnts.size(); iBndEnt++) {
          GEntity* &bndEnt = bndEnts[iBndEnt];
          if (p.onlyVisible && !bndEnt->getVisibility()) continue;
          const GEntity::GeomType bndType = bndEnt->geomType();
          if ((bndType == GEntity::Line) || (bndType == GEntity::Plane)) continue;
          Msg::Info("Curving elements in entity %d for boundary entity %d...",
                    gEnt->tag(), bndEnt->tag());
          curveMeshFromBnd(ed2el, face2el, bndEnt, p);
        }
      }
    
      double t2 = Cpu();
      Msg::StatusBar(true, "Done curving high order boundary layer mesh (%g s)", t2-t1);
    }