Skip to content
Snippets Groups Projects
Forked from gmsh / gmsh
16202 commits behind the upstream repository.
HighOrder.cpp 64.79 KiB
// Gmsh - Copyright (C) 1997-2008 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to <gmsh@geuz.org>.

#include "HighOrder.h"
#include "meshGFaceOptimize.h"
#include "MElement.h"
#include "Message.h"
#include "OS.h"
#include "Numeric.h"
#include "Context.h"
#include "GmshMatrix.h"
#include "FunctionSpace.h"

#define SQU(a)      ((a)*(a))

extern Context_T CTX;

// for each pair of vertices (an edge), we build a list of vertices
// that are the high order representation of the edge. The ordering of
// vertices in the list is supposed to be (by construction) consistent
// with the ordering of the pair.
typedef std::map<std::pair<MVertex*,MVertex*>, std::vector<MVertex*> > edgeContainer;

// for each face (a list of vertices) we build a list of vertices that
// are the high order representation of the face
// typedef std::map<std::vector<MVertex*>, std::vector<MVertex*>> faceContainer;
typedef std::map<MFace, std::vector<MVertex*>,Less_Face>       faceContainer;

bool mappingIsInvertible (MTetrahedron *e)
{
  if (e->getPolynomialOrder() == 1)return 1.0;
  
  double mat[3][3];
  e->getPrimaryJacobian(0.,0.,0., mat);  
  double det0 = det3x3(mat);

  IntPt *pts;
  int npts;

  e->getIntegrationPoints(e->getPolynomialOrder(),&npts, &pts);
  
  for (int i=0;i<npts;i++){
    
    const double u = pts[i].pt[0];
    const double v = pts[i].pt[1];
    const double w = pts[i].pt[2];
    
    e->getJacobian(u, v, w, mat);

    double detN = det3x3(mat);
    
    if (det0 * detN <= 0.) return false;
  }

  const Double_Matrix& points = e->getFunctionSpace()->points;

  for (int i=0;i<e->getNumPrimaryVertices();i++) {

    const double u = points(i,0);
    const double v = points(i,1);
    const double w = points(i,2);

    e->getJacobian(u,v,w,mat);

    double detN = det3x3(mat);

    if (det0 * detN <= 0.) return false;
  }
  
  return true;
}

bool deformElement(MElement *ele,GEntity* ge,const std::set<MVertex*>& blocked) {
  
  int nbNodes = ele->getNumVertices();  

  double _E = 1.;
  double _nu = 0.1;
  
  double FACT = _E / (1. + _nu) / (1. - 2. * _nu);  
  double C11 = FACT * (1. - _nu);
  double C12 = FACT * _nu;
  double C44 = (C11 - C12) / 2; 
  
  Double_Matrix stiffness(3*nbNodes,3*nbNodes);
  Double_Vector rhs(3*nbNodes);

  int npts;
  IntPt *pts;
  ele->getIntegrationPoints(2*ele->getPolynomialOrder(),&npts, &pts);
  double d0;

  const gmshFunctionSpace* fs = ele->getFunctionSpace();

  const Double_Matrix& points = fs->points;
  

  double jac[3][3];
  ele->getPrimaryJacobian(0,0,0,jac);
  double invjac[3][3];
  inv3x3(jac,invjac);

  double gsf[256][3];


  // cheaper version : quadrature only used for parametric stiffness
  // paramGrads should be stored in functionspace.
  
//   for (int j=0;j<nbNodes;j++) {
//     for (int k=0;k<nbNodes;k++) {

//       double xx = 0;
//       double xy = 0;
//       double xz = 0;
//       double yx = 0;
//       double yy = 0;
//       double yz = 0;
//       double zx = 0;
//       double zy = 0;
//       double zz = 0;
      

//       for (int m=0;m<3;m++) {
//         for (int n=0;n<3;n++) {

//           double dd = paramGrads[m][n](j,k);
          
//           xx += dd * invJac[0][m] * invJac[0][n];
//           xy += dd * invJac[0][m] * invJac[1][n];
//           xz += dd * invJac[0][m] * invJac[2][n];
//           yx += dd * invJac[1][m] * invJac[0][n];
//           yy += dd * invJac[1][m] * invJac[1][n];
//           yz += dd * invJac[1][m] * invJac[2][n];
//           zx += dd * invJac[2][m] * invJac[0][n];
//           zy += dd * invJac[2][m] * invJac[1][n];
//           zz += dd * invJac[2][m] * invJac[2][n];
//         }
//       }

//       stiffness(3*j,3*k)     += (C11 * xx +  // dphidx . tauxx
//                                  C44 * yy +  // dphidy . tauxy
//                                  C44 * zz);  // dphidz . tauxz
      
//       stiffness(3*j,3*k+1)   += (C12 * xy +  // dphidx . tauxx 
//                                  C44 * yx);  // dphidy . tauxy
      
//       stiffness(3*j,3*k+2)   += (C12 * xz +  // dphidx . tauxx
//                                  C44 * zx);  // dphidz . tauxz
      
//       stiffness(3*j+1,3*k)   += (C44 * xy +  // dphidx . tauxy
//                                  C12 * yx);  // dphidy . tauyy
      
//       stiffness(3*j+1,3*k+1) += (C44 * xx +  // dphidx . tauxy 
//                                  C11 * yy +  // dphidy . tauyy
//                                  C44 * zz ); // dphidz . tauzy
      
//       stiffness(3*j+1,3*k+2) += (C12 * yz +  // dphidy . tauyy
//                                  C44 * zy);  // dphidz . tauzy
      
//       stiffness(3*j+2,3*k)   += (C44 * xz +  // dphidx . tauxz
//                                  C12 * zx);  // dphidz . tauzz
      
//       stiffness(3*j+2,3*k+1) += (C44 * yz +  // dphidy . tauyz
//                                  C12 * zy);  // dphidz . tauzz
      
//       stiffness(3*j+2,3*k+2) += (C44 * xx +  // dphidx . tauxz
//                                  C44 * yy +  // dphidy . tauyz
//                                  C11 * zz);  // dphidz . tauzz
//     }
//   }
  
      
  // more expensive version

  double Grads[256][3];

  stiffness.scale(0.);
  
  for (int i=0;i<npts;i++){
    
    const double u = pts[i].pt[0];
    const double v = pts[i].pt[1];
    const double w = pts[i].pt[2];

    const double weight = pts[i].weight;
    
    fs->df(u,v,w,gsf);

    for (int j=0;j<nbNodes;j++) {
      Grads[j][0] = invjac[0][0] * gsf[j][0] + invjac[0][1] * gsf[j][1] + invjac[0][2] * gsf[j][2];
      Grads[j][1] = invjac[1][0] * gsf[j][0] + invjac[1][1] * gsf[j][1] + invjac[1][2] * gsf[j][2];
      Grads[j][2] = invjac[2][0] * gsf[j][0] + invjac[2][1] * gsf[j][1] + invjac[2][2] * gsf[j][2];
    }
    
    for (int j=0;j<nbNodes;j++) {
      for (int k=0;k<nbNodes;k++) {

        // R_x = dphidx . tauxx + dphidy . tauxy + dphidz . tauxz
        // tau_xx = C11 . e_xx + C12 . (e_yy + e_zz)
        // tau_xy = C44 . e_xy 
        // e_xy = 0.5 * (dX/dx + dY/dy)

        double xx = Grads[j][0] * Grads[k][0];
        double xy = Grads[j][0] * Grads[k][1];
        double xz = Grads[j][0] * Grads[k][2];
        
        double yx = Grads[j][1] * Grads[k][0];
        double yy = Grads[j][1] * Grads[k][1];
        double yz = Grads[j][1] * Grads[k][2];
        
        double zx = Grads[j][2] * Grads[k][0];
        double zy = Grads[j][2] * Grads[k][1];
        double zz = Grads[j][2] * Grads[k][2];


        // Poisson
        
        stiffness(3*j,3*k)     += weight * (xx + yy + zz);
        stiffness(3*j+1,3*k+1) += weight * (xx + yy + zz);
        stiffness(3*j+2,3*k+2) += weight * (xx + yy + zz);
        

        // Elasticity

        // stiffness(3*j  ,3*k  ) += weight * (C11 * xx + C44 * yy + C44 * zz);
//         stiffness(3*j  ,3*k+1) += weight * (C12 * xy + C44 * yx);
//         stiffness(3*j  ,3*k+2) += weight * (C12 * xz + C44 * zx);

//         stiffness(3*j+1,3*k  ) += weight * (C12 * yx + C44 * xy);
//         stiffness(3*j+1,3*k+1) += weight * (C44 * xx + C11 * yy + C44 * zz);
//         stiffness(3*j+1,3*k+2) += weight * (C12 * yz + C44 * zy);
        
//         stiffness(3*j+2,3*k  ) += weight * (C12 * zx + C44 * xz);
//         stiffness(3*j+2,3*k+1) += weight * (C12 * zy + C44 * yz);
//         stiffness(3*j+2,3*k+2) += weight * (C44 * xx + C44 * yy + C11 * zz);
        
      }
    }
  }

  int nbDirichlet = 0;
  
  Double_Vector original(nbNodes*3);
  
  for (int i=0;i<nbNodes;i++) {

    MVertex* v = ele->getVertex(i);
      
    SPoint3 primary;
    ele->primaryPnt(points(i,0),points(i,1),points(i,2),primary);
    original(3*i  ) = primary.x();
    original(3*i+1) = primary.y();
    original(3*i+2) = primary.z();
    
    MFaceVertex* vf = dynamic_cast<MFaceVertex*> (v);
    MFaceVertex* ve = dynamic_cast<MFaceVertex*> (v);

    if (v->onWhat() != ge || blocked.find(v) != blocked.end()) {

      nbDirichlet++;
        
      double dx = v->x() - original(3*i  );
      double dy = v->y() - original(3*i+1);
      double dz = v->z() - original(3*i+2);

      for (int j=0;j<3*nbNodes;j++) {
        stiffness(3*i  ,j) = 0;
        stiffness(3*i+1,j) = 0;
        stiffness(3*i+2,j) = 0;
      }
      
      for (int k=0;k<3;k++) stiffness(i*3+k,i*3+k) = 1.;
      
      rhs(i*3  ) = dx;
      rhs(i*3+1) = dy;
      rhs(i*3+2) = dz;
    }
    else {
      rhs(i*3  ) = 0.;
      rhs(i*3+1) = 0.;
      rhs(i*3+2) = 0.;
    }
  }

  if (nbDirichlet < 3) {
    Msg::Warning("Could not deform element due to lack of constraints\n");
    return false;
  }

  if (nbDirichlet == nbNodes) {
    Msg::Warning("Could not deform element because fully constrained\n");
    return false;
  }

  
  Msg::Warning("Deforming element - have %d constrained points of which %d from previous positioning",nbDirichlet,(int) blocked.size());
  

  Double_Vector displacement(nbNodes*3);

  
  stiffness.lu_solve(rhs,displacement);

  for (int i=0;i<nbNodes;i++) {
    MVertex* v = ele->getVertex(i);
    
    v->x() = original(3*i)   + displacement(3*i  );
    v->y() = original(3*i+1) + displacement(3*i+1);
    v->z() = original(3*i+2) + displacement(3*i+2);
    
  }
  
  return true;
}


bool reparamOnFace(MVertex *v, GFace *gf, SPoint2 &param)
{
  if(v->onWhat()->dim() == 0){
    GVertex *gv = (GVertex*)v->onWhat();

    // abort if we could be on a seam
    /*
    std::list<GEdge*> ed = gv->edges();
    for(std::list<GEdge*>::iterator it = ed.begin(); it != ed.end(); it++)
      if((*it)->isSeam(gf)) return false;
    */
    param = gv->reparamOnFace(gf, 1);
  }
  else if(v->onWhat()->dim() == 1){
    GEdge *ge = (GEdge*)v->onWhat();

    // abort if we are on a seam (todo: try dir=-1 and compare)
    //    if(ge->isSeam(gf)){
    //      printf("oops : %d %d\n",ge->tag(),gf->tag());
      //    }

    double UU;
    v->getParameter(0, UU);
    param = ge->reparamOnFace(gf, UU, 1);
  }
  else{
    double UU, VV;
    if(v->onWhat() == gf && v->getParameter(0, UU) && v->getParameter(1, VV))
      param = SPoint2(UU, VV);
    else 
      return false;
    //      param = gf->parFromPoint(SPoint3(v->x(), v->y(), v->z()));
  }
  return true;
}

// The aim here is to build a polynomial representation that consist
// in polynomial segments of equal length

static double mylength(GEdge *ge, int i, double *u)
{
  return ge->length(u[i], u[i+1], 10);
}

static void myresid(int N, GEdge *ge, double *u, Double_Vector &r)
{
  double L[100];
  for (int i = 0; i < N - 1; i++) L[i] = mylength(ge, i, u);
  for (int i = 0; i < N - 2; i++) r(i) = L[i + 1] - L[i];
}

bool computeEquidistantParameters(GEdge *ge, double u0, double uN, int N, double *u, double underRelax)
{
  const double PRECISION = 1.e-6;
  const int MAX_ITER = 50;
  const double eps = (uN - u0) * 1.e-5;

  // newton algorithm
  // N is the total number of points (3 for quadratic, 4 for cubic ...)
  // u[0] = u0;
  // u[N-1] = uN;
  // initialize as equidistant in parameter space
  u[0] = u0;
  double du = (uN - u0) / (N - 1);
  for (int i = 1 ; i < N ; i++){
    u[i] = u[i - 1] + du;
  }

  // create the tangent matrix
  const int M = N - 2;
  Double_Matrix J(M, M);
  Double_Vector DU(M);
  Double_Vector R(M);
  Double_Vector Rp(M);
  
  int iter = 1;

  while (iter < MAX_ITER){
    iter++;
    myresid(N, ge, u, R);

    for (int i = 0; i < M; i++){
      u[i + 1] += eps;
      myresid(N, ge, u, Rp);
      for (int j = 0; j < M; j++){
        J(i, j) = (Rp(j) - R(j)) / eps;
      }
      u[i+1] -= eps;
    }

    if (M == 1)
      DU(0) = R(0) / J(0, 0);
    else
      J.lu_solve(R, DU);
    
    for (int i = 0; i < M; i++){
      u[i+1] -= underRelax*DU(i);
    }
    // printf("N %d M %d u1 = %g u0 = %g uN1 = %22.15E uN = %22.15E\n",
    //        N, M, u[1], u0, u[N - 1], uN);

    if (u[1] < u0) break;
    if (u[N - 2] > uN) break;

    double newt_norm = DU.norm();      
    // printf("%22.15E\n",newt_norm);
    if (newt_norm < PRECISION) { /*printf("ok %g\n",underRelax);*/ return true; }
  }
  // FAILED, use equidistant in param space
  // printf("failed %g\n",underRelax);
  // for (int i = 1; i < N; i++){
  //   u[i] = u[i - 1] + du;
  // }
  return false;
}

static double mylength(GFace *gf, int i, double *u, double *v)
{
  return gf->length(SPoint2(u[i], v[i]), SPoint2(u[i + 1], v[i + 1]), 10);
}

static void myresid(int N, GFace *gf, double *u, double *v, Double_Vector &r)
{
  double L[100];
  for (int i = 0; i < N - 1; i++) L[i] = mylength(gf, i, u, v);  
  for (int i = 0; i < N - 2; i++) r(i) = L[i + 1] - L[i];
}

bool computeEquidistantParameters(GFace *gf, double u0, double uN, double v0, double vN, 
                                  int N, double *u, double *v)
{
  const double PRECISION = 1.e-6;
  const int MAX_ITER = 50;
  const double eps = 1.e-4;

  double t[100];

  // initialize the points by equal subdivision of geodesics
  u[0] = u0;
  v[0] = v0;
  t[0] = 0;
  for (int i = 1; i < N ; i++){
    t[i] = (double)i / (N - 1);
    SPoint2 p = gf->geodesic(SPoint2(u0, v0), SPoint2(uN, vN), t[i]);
    u[i] = p.x();
    v[i] = p.y();
  }
  u[N] = uN;
  v[N] = vN;
  t[N] = 1.0;

  return true;

  // create the tangent matrix
  const int M = N - 2;
  Double_Matrix J(M, M);
  Double_Vector DU(M);
  Double_Vector R(M);
  Double_Vector Rp(M);
  
  int iter = 1 ;

  while (iter < MAX_ITER){
    iter++ ;
    myresid(N, gf, u, v, R); 

    for (int i = 0; i < M; i++){
      t[i + 1] += eps;
      double tempu = u[i + 1];
      double tempv = v[i + 1];
      SPoint2 p = gf->geodesic(SPoint2(u0, v0), SPoint2(uN, vN), t[i + 1]);
      u[i + 1] = p.x();
      v[i + 1] = p.y();
      myresid(N, gf, u, v, Rp);
      for (int j = 0; j < M; j++){
        J(i, j) = (Rp(j) - R(j)) / eps;
      }
      t[i + 1] -= eps;
      u[i + 1] = tempu;
      v[i + 1] = tempv;
    }

    if (M == 1)
      DU(0) = R(0) / J(0, 0);
    else
      J.lu_solve(R, DU);
    
    for (int i = 0; i < M; i++){
      t[i + 1] -= DU(i);
      SPoint2 p = gf->geodesic(SPoint2(u0, v0), SPoint2(uN, vN), t[i + 1]);
      u[i + 1] = p.x();
      v[i + 1] = p.y();
    }
    double newt_norm = DU.norm();      
    if (newt_norm < PRECISION) return true;
  }
  // FAILED, use equidistant in param space
   for (int i = 1; i < N; i++){
     t[i] = (double)i / (N - 1);
     SPoint2 p = gf->geodesic(SPoint2(u0, v0), SPoint2(uN, vN), t[i]);
     u[i] = p.x();
     v[i] = p.y();
   }
  return false;
}

bool reparamOnEdge(MVertex *v, GEdge *ge, double &param)
{
  param = 1.e6;
  Range<double> bounds = ge->parBounds(0);
  if(ge->getBeginVertex() && ge->getBeginVertex()->mesh_vertices[0] == v) 
    param = bounds.low();
  else if(ge->getEndVertex() && ge->getEndVertex()->mesh_vertices[0] == v) 
    param = bounds.high();
  else 
    v->getParameter(0, param);

  if(param < 1.e6) return true;
  return false;
}

void getEdgeVertices(GEdge *ge, MElement *ele, std::vector<MVertex*> &ve,
                     edgeContainer &edgeVertices, bool linear, int nPts = 1)
{
  for(int i = 0; i < ele->getNumEdges(); i++){
    MEdge edge = ele->getEdge(i);
    std::pair<MVertex*, MVertex*> p(edge.getMinVertex(), edge.getMaxVertex());
    if(edgeVertices.count(p)){
      if(edge.getVertex(0) == edge.getMinVertex())
        ve.insert(ve.end(), edgeVertices[p].begin(), edgeVertices[p].end());
      else
        ve.insert(ve.end(), edgeVertices[p].rbegin(), edgeVertices[p].rend());
    }
    else{
      MVertex *v0 = edge.getVertex(0), *v1 = edge.getVertex(1);            

      double u0 = 0., u1 = 0.;
      bool reparamOK = true;
      if(!linear && ge->geomType() != GEntity::DiscreteCurve &&
         ge->geomType() != GEntity::BoundaryLayerCurve){
        reparamOK &= reparamOnEdge(v0, ge, u0);
        if (ge->periodic(0) &&  v1 == ge->getEndVertex()->mesh_vertices[0]){
          Range<double> par = ge->parBounds(0);
          u1 = par.high();
        }         
        else
          reparamOK &= reparamOnEdge(v1, ge, u1);
      }
      double US[100];
      if(reparamOK && !linear && ge->geomType() != GEntity::DiscreteCurve){
        double relax = 1.;
        while (1){
          if (computeEquidistantParameters(ge, u0, u1, nPts + 2, US,relax))break;
          relax /= 2.0;
          if (relax < 1.e-2)break;
        } 
        if (relax < 1.e-2)Msg::Warning("failure in computing equidistant parameters %g",relax);
      }
      std::vector<MVertex*> temp;      
      for(int j = 0; j < nPts; j++){
        const double t = (double)(j + 1)/(nPts + 1);
        double uc = (1. - t) * u0 + t * u1;
        MVertex *v;
        if(!reparamOK || linear || ge->geomType() == GEntity::DiscreteCurve || 
           uc < u0 || uc > u1){ // need to treat periodic curves properly!
          SPoint3 pc = edge.interpolate(t);
          v = new MVertex(pc.x(), pc.y(), pc.z(), ge);
          v->setParameter (0,t);
        }
        else {
          GPoint pc = ge->point(US[j+1]);
          v = new MEdgeVertex(pc.x(), pc.y(), pc.z(), ge, US[j+1]);
        }
        temp.push_back(v);
        ge->mesh_vertices.push_back(v);
        ve.push_back(v);
      }
      if(edge.getVertex(0) == edge.getMinVertex())
        edgeVertices[p].insert(edgeVertices[p].end(), temp.begin(), temp.end());
      else
        edgeVertices[p].insert(edgeVertices[p].end(), temp.rbegin(), temp.rend());
    }
  }
}

void getEdgeVertices(GFace *gf, MElement *ele, std::vector<MVertex*> &ve,
                     edgeContainer &edgeVertices, bool linear, int nPts = 1)
{
  for(int i = 0; i < ele->getNumEdges(); i++){
    MEdge edge = ele->getEdge(i);    
    std::pair<MVertex*, MVertex*> p(edge.getMinVertex(), edge.getMaxVertex());
    if(edgeVertices.count(p)){
      if(edge.getVertex(0) == edge.getMinVertex())
        ve.insert(ve.end(), edgeVertices[p].begin(), edgeVertices[p].end());
      else
        ve.insert(ve.end(), edgeVertices[p].rbegin(), edgeVertices[p].rend());
    }
    else{
      MVertex *v0 = edge.getVertex(0), *v1 = edge.getVertex(1);
      SPoint2 p0, p1;
      bool reparamOK = true;
      if(!linear && 
         gf->geomType() != GEntity::DiscreteSurface &&
         gf->geomType() != GEntity::BoundaryLayerSurface){
        reparamOK &= reparamOnFace(v0, gf, p0);
        reparamOK &= reparamOnFace(v1, gf, p1);
      }
      double US[100], VS[100];
      if(reparamOK && !linear && gf->geomType() != GEntity::DiscreteCurve){
        computeEquidistantParameters(gf, p0[0], p1[0], p0[1], p1[1], nPts + 2, US, VS);
      }
      std::vector<MVertex*> temp;
      for(int j = 0; j < nPts; j++){
        const double t = (double)(j + 1) / (nPts + 1);
        MVertex *v;
        if(!reparamOK || linear || gf->geomType() == GEntity::DiscreteSurface){
          SPoint3 pc = edge.interpolate(t);
          v = new MVertex(pc.x(), pc.y(), pc.z(), gf);
        }
        else{
          GPoint pc = gf->point(US[j+1], VS[j+1]);
          v = new MFaceVertex(pc.x(), pc.y(), pc.z(), gf, US[j+1], VS[j+1]);
        }
        temp.push_back(v);
        gf->mesh_vertices.push_back(v);
        ve.push_back(v);
      }
      if(edge.getVertex(0) == edge.getMinVertex())
        edgeVertices[p].insert(edgeVertices[p].end(), temp.begin(), temp.end());
      else
        edgeVertices[p].insert(edgeVertices[p].end(), temp.rbegin(), temp.rend());
    }
  }
}

void getEdgeVertices(GRegion *gr, MElement *ele,
                     std::vector<MVertex*> &ve,
                     std::set<MVertex*>& blocked,
                     edgeContainer &edgeVertices, bool linear,
                     int nPts = 1)
{
  for(int i = 0; i < ele->getNumEdges(); i++){
    MEdge edge = ele->getEdge(i);
    std::pair<MVertex*, MVertex*> p(edge.getMinVertex(), edge.getMaxVertex());
    if(edgeVertices.count(p)){
      if(edge.getVertex(0) == edge.getMinVertex())
        ve.insert(ve.end(), edgeVertices[p].begin(), edgeVertices[p].end());
      else
        ve.insert(ve.end(), edgeVertices[p].rbegin(), edgeVertices[p].rend());
      blocked.insert(edgeVertices[p].begin(),edgeVertices[p].end());
      blocked.insert(edge.getMinVertex());
      blocked.insert(edge.getMaxVertex());
    }
    else{
      std::vector<MVertex*> temp;
      for(int j = 0; j < nPts; j++){
        double t = (double)(j + 1) / (nPts + 1);
        SPoint3 pc = edge.interpolate(t);
        MVertex *v = new MVertex(pc.x(), pc.y(), pc.z(), gr);
        temp.push_back(v);
        gr->mesh_vertices.push_back(v);
        ve.push_back(v);
      }
      if(edge.getVertex(0) == edge.getMinVertex())
        edgeVertices[p].insert(edgeVertices[p].end(), temp.begin(), temp.end());
      else
        edgeVertices[p].insert(edgeVertices[p].end(), temp.rbegin(), temp.rend());
    }
  }
}

void getFaceVertices(GFace *gf, 
		     MElement *incomplete, 
		     MElement *ele, 
		     std::vector<MVertex*> &vf,
                     faceContainer &faceVertices, 
		     bool linear, int nPts = 1) {
  
  Double_Matrix points;
  int start = 0;
  
  switch (nPts){
  case 2 :
    points = gmshFunctionSpaces::find(MSH_TRI_10).points;
    start = 9;
    break;
  case 3 :
    points = gmshFunctionSpaces::find(MSH_TRI_15).points;
    start = 12;
    break;
  case 4 :
    points = gmshFunctionSpaces::find(MSH_TRI_21).points;
    start = 15;
    break;
  default :  
    // do nothing (e.g. for quad faces)
    break;
  }

  for(int i = 0; i < ele->getNumFaces(); i++){
    MFace face = ele->getFace(i);
    //std::vector<MVertex*> p;
    // face.getOrderedVertices(p);
    //if(faceVertices.count(p)){
    //       vf.insert(vf.end(), faceVertices[p].begin(), faceVertices[p].end());
    //     }

    faceContainer::iterator fIter = faceVertices.find(face);
    
    if(fIter!=faceVertices.end()){
      vf.insert(vf.end(), fIter->second.begin(), fIter->second.end());
    }
    else{
      SPoint2 pts[20];
      bool reparamOK = true;
      if(!linear && 
         gf->geomType() != GEntity::DiscreteSurface &&
         gf->geomType() != GEntity::BoundaryLayerSurface){
	for (int k=0;k<incomplete->getNumVertices(); k++){
	  reparamOK &= reparamOnFace(incomplete->getVertex(k), gf, pts[k]);
	}
      }
      if(face.getNumVertices() == 3){ // triangles

        std::vector<MVertex*>& vtcs = faceVertices[face];
          
        for(int k = start ; k < points.size1() ; k++){
          MVertex *v;
          const double t1 = points(k, 0);
          const double t2 = points(k, 1);
          if(linear || gf->geomType() == GEntity::DiscreteSurface){
            SPoint3 pc = face.interpolate(t1, t2);
            v = new MVertex(pc.x(), pc.y(), pc.z(), gf);
          }
          else{

            //             SPoint3 pos;
            //             incomplete->pnt(t1,t2,0.,pos);
            //             double X(pos.x());
            //             double Y(pos.y());
            //             double Z(pos.z());
            
            
	    double X(0),Y(0),Z(0),GUESS[2]={0,0};

	    for (int j=0; j<incomplete->getNumVertices(); j++){
              
	      double sf ; incomplete->getShapeFunction(j,t1,t2,0,sf);
	      MVertex *vt = incomplete->getVertex(j);
              
	      X += sf * vt->x();
	      Y += sf * vt->y();
	      Z += sf * vt->z();
	      if (reparamOK){
		GUESS[0] += sf * pts[j][0];
		GUESS[1] += sf * pts[j][1];
	      }
	    }
	    if (reparamOK){
	      GPoint gp = gf->closestPoint(SPoint3(X,Y,Z),GUESS);
	      if (gp.g())
		v = new MFaceVertex(gp.x(), gp.y(), gp.z(), gf, gp.u(), gp.v());
	      else
		v = new MVertex(X,Y,Z, gf);
	    }
	    else{
	      v = new MVertex(X,Y,Z, gf);
	    }
          }
          // should be expensive -> induces a new search each time
          // faceVertices[p].push_back(v);
          vtcs.push_back(v);
          gf->mesh_vertices.push_back(v);
          vf.push_back(v);
        }
      }
      else if(face.getNumVertices() == 4){ // quadrangles

        std::vector<MVertex*>& vtcs = faceVertices[face];
        
        for(int j = 0; j < nPts; j++){
          for(int k = 0; k < nPts; k++){
            MVertex *v;
            // parameters are between -1 and 1
            double t1 = 2. * (double)(j + 1) / (nPts + 1) - 1.;
            double t2 = 2. * (double)(k + 1) / (nPts + 1) - 1.;
            if(!reparamOK || linear || gf->geomType() == GEntity::DiscreteSurface){
              SPoint3 pc = face.interpolate(t1, t2);
              v = new MVertex(pc.x(), pc.y(), pc.z(), gf);
            }
            else{
              double uc = 0.25 * ((1 - t1) * (1 - t2) * pts[0][0] + 
                                  (1 + t1) * (1 - t2) * pts[1][0] + 
                                  (1 + t1) * (1 + t2) * pts[2][0] + 
                                  (1 - t1) * (1 + t2) * pts[3][0]); 
              double vc = 0.25 * ((1 - t1) * (1 - t2) * pts[0][1] + 
                                  (1 + t1) * (1 - t2) * pts[1][1] + 
                                  (1 + t1) * (1 + t2) * pts[2][1] + 
                                  (1 - t1) * (1 + t2) * pts[3][1]); 
              GPoint pc = gf->point(uc, vc);
              v = new MFaceVertex(pc.x(), pc.y(), pc.z(), gf, uc, vc);
            }
            // faceVertices[p].push_back(v);
            vtcs.push_back(v);
            gf->mesh_vertices.push_back(v);
            vf.push_back(v);
          }
        }
      }
    }
  }  
}


void getFaceVertices(GFace *gf, MElement *ele, std::vector<MVertex*> &vf,
                     faceContainer &faceVertices, bool linear, int nPts = 1)
{
  
  Double_Matrix points;
  int start = 0;
  switch (nPts){
  case 2 :
    points = gmshFunctionSpaces::find(MSH_TRI_10).points;
    start = 9;
    break;
  case 3 :
    points = gmshFunctionSpaces::find(MSH_TRI_15).points;
    start = 12;
    break;
  case 4 :
    points = gmshFunctionSpaces::find(MSH_TRI_21).points;
    start = 15;
    break;
  default :  
    // do nothing (e.g. for quad faces)
    break;
  }

  for(int i = 0; i < ele->getNumFaces(); i++){
    MFace face = ele->getFace(i);
    // std::vector<MVertex*> p;
//     face.getOrderedVertices(p);
//     if(faceVertices.count(p)){
//       vf.insert(vf.end(), faceVertices[p].begin(), faceVertices[p].end());
//     }

    faceContainer::iterator fIter = faceVertices.find(face);

    if (fIter!=faceVertices.end()) {
      vf.insert(vf.end(), fIter->second.begin(), fIter->second.end());
    }
    else{

      std::vector<MVertex*>& vtcs = faceVertices[face];
      
      SPoint2 p0, p1, p2, p3;
      bool reparamOK = true;
      if(!linear && 
         gf->geomType() != GEntity::DiscreteSurface &&
         gf->geomType() != GEntity::BoundaryLayerSurface){
        reparamOK &= reparamOnFace(ele->getVertex(0), gf, p0);
        reparamOK &= reparamOnFace(ele->getVertex(1), gf, p1);
        reparamOK &= reparamOnFace(ele->getVertex(2), gf, p2);
        if(face.getNumVertices() == 4)
          reparamOK &= reparamOnFace(ele->getVertex(3), gf, p3);
      }
      if(face.getNumVertices() == 3){ // triangles
        for(int k = start ; k < points.size1() ; k++){
          MVertex *v;
          const double t1 = points(k, 0);
          const double t2 = points(k, 1);
          if(!reparamOK || linear || gf->geomType() == GEntity::DiscreteSurface){
            SPoint3 pc = face.interpolate(t1, t2);
            v = new MVertex(pc.x(), pc.y(), pc.z(), gf);
          }
          else{
            double uc = (1. - t1 - t2) * p0[0] + t1 * p1[0] + t2 * p2[0];
            double vc = (1. - t1 - t2) * p0[1] + t1 * p1[1] + t2 * p2[1];
            GPoint pc = gf->point(uc, vc);
            v = new MFaceVertex(pc.x(), pc.y(), pc.z(), gf, uc, vc);
            v->setParameter (0,uc);
            v->setParameter (1,vc);
          }
          // faceVertices[p].push_back(v);
          vtcs.push_back(v);
          gf->mesh_vertices.push_back(v);
          vf.push_back(v);
        }
      }
      else if(face.getNumVertices() == 4){ // quadrangles
        for(int j = 0; j < nPts; j++){
          for(int k = 0; k < nPts; k++){
            MVertex *v;
            // parameters are between -1 and 1
            double t1 = 2. * (double)(j + 1) / (nPts + 1) - 1.;
            double t2 = 2. * (double)(k + 1) / (nPts + 1) - 1.;
            if(!reparamOK || linear || gf->geomType() == GEntity::DiscreteSurface){
              SPoint3 pc = face.interpolate(t1, t2);
              v = new MVertex(pc.x(), pc.y(), pc.z(), gf);
            }
            else{
              double uc = 0.25 * ((1 - t1) * (1 - t2) * p0[0] + 
                                  (1 + t1) * (1 - t2) * p1[0] + 
                                  (1 + t1) * (1 + t2) * p2[0] + 
                                  (1 - t1) * (1 + t2) * p3[0]); 
              double vc = 0.25 * ((1 - t1) * (1 - t2) * p0[1] + 
                                  (1 + t1) * (1 - t2) * p1[1] + 
                                  (1 + t1) * (1 + t2) * p2[1] + 
                                  (1 - t1) * (1 + t2) * p3[1]); 
              GPoint pc = gf->point(uc, vc);
              v = new MFaceVertex(pc.x(), pc.y(), pc.z(), gf, uc, vc);
            }
            // faceVertices[p].push_back(v);
            vtcs.push_back(v);
            gf->mesh_vertices.push_back(v);
            vf.push_back(v);
          }
        }
      }
    }
  }
}

void reorientTrianglePoints(std::vector<MVertex*>& vtcs,int orientation,bool swap) {

  if (vtcs.size() == 1) return;
  
  size_t nbPts = vtcs.size();

  if (nbPts > 3) Msg::Error("Interior face nodes reorientation not supported for order > 4");
  std::vector<MVertex*> tmp(nbPts);

  // rotation
  // --------

  // --- interior "principal vertices"
  
  // for (int i=0;i<3;i++) tmp[(i+orientation)%3] = vtcs[i];
  
  for (int i=0;i<3;i++) tmp[(i+orientation)%3] = vtcs[i];

  
  // normal swap
  // -----------
  
  if (swap) {
    // --- interior "principal vertices"


    vtcs[orientation]           = tmp[orientation];
    vtcs[(orientation + 1) % 3] = tmp[(orientation+2)%3];
    vtcs[(orientation + 2) % 3] = tmp[(orientation+1)%3];
    
    // for (int i=0;i<2;i++) for (int j=0;j<2-i;j++) vtcs[i*2+j] = tmp[i+j*2];
  }
  
  // no swap
  // -------
  
  else vtcs = tmp;
} 


// KH: check face orientation wrt element ... 

void getFaceVertices(GRegion *gr, MElement *ele,
                     std::vector<MVertex*> &vf,
                     std::set<MVertex*>& blocked,
                     faceContainer &faceVertices,
                     edgeContainer& edgeVertices,
                     bool linear, int nPts = 1)
{
  
  
  Double_Matrix points;
  int start = 0;
  
  switch (nPts){
  case 2 :
    points = gmshFunctionSpaces::find(MSH_TRI_10).points;
    start = 9;
    break;
  case 3 :
    points = gmshFunctionSpaces::find(MSH_TRI_15).points;
    start = 12;
    break;
  case 4 :
    points = gmshFunctionSpaces::find(MSH_TRI_21).points;
    start = 15;
    break;
  default :  
    // do nothing (e.g. for quad faces)
    break;
  }
  
  
  for(int i = 0; i < ele->getNumFaces(); i++){
    MFace face = ele->getFace(i);

    faceContainer::iterator fIter = faceVertices.find(face);

    if (fIter!=faceVertices.end()) {

      int orientation;
      bool swap;
      
      std::vector<MVertex*> vtcs = fIter->second;
      if (fIter->first.computeCorrespondence(face,orientation,swap)) {
        reorientTrianglePoints(vtcs,orientation,swap);
      }
      else Msg::Error("Error in face lookup for recuperation of high order face nodes");
      vf.insert(vf.end(), vtcs.begin(), vtcs.end());

      blocked.insert(vtcs.begin(),vtcs.end());
      blocked.insert(face.getVertex(0));
      blocked.insert(face.getVertex(1));
      blocked.insert(face.getVertex(2));
    }
    else{
      std::vector<MVertex*>& vtcs = faceVertices[face];        
      
      if(face.getNumVertices() == 3){ // triangles

        // construct incomplete element to take into account curved edges on surface boundaries

        std::vector<MVertex*> hoEdgeNodes;
        
        for (int i=0;i<3;i++) {

          MVertex* v0 = face.getVertex(i);
          MVertex* v1 = face.getVertex((i+1)%3);
          
          edgeContainer::iterator eIter = edgeVertices.find(std::pair<MVertex*,MVertex*>(std::min(v0,v1),std::max(v0,v1)));

          if (eIter == edgeVertices.end()) {
            printf("Could not find ho nodes for an edge");
          }

          if (v0 == eIter->first.first) hoEdgeNodes.insert(hoEdgeNodes.end(),eIter->second.begin(),eIter->second.end());
          else                          hoEdgeNodes.insert(hoEdgeNodes.end(),eIter->second.rbegin(),eIter->second.rend());
        }

        MTriangleN incomplete(face.getVertex(0),
                              face.getVertex(1),
                              face.getVertex(2),
                              hoEdgeNodes,nPts+1);

        double X(0),Y(0),Z(0);
        
        for (int k=start;k<points.size1();k++) {
          
          double t1 = points(k,0);
          double t2 = points(k,1);

          SPoint3 pos;
          incomplete.pnt(t1,t2,0,pos);
          MVertex* v = new MVertex(pos.x(),pos.y(),pos.z(),gr);
          
          
          // for (int j=0; j<incomplete.getNumVertices(); j++){
            
//             double sf ; incomplete.getShapeFunction(j,t1,t2,0,sf);
//             MVertex *vt = incomplete.getVertex(j);
            
//             X += sf * vt->x();
//             Y += sf * vt->y();
//             Z += sf * vt->z();
//           }

//           MVertex* v = new MVertex(X,Y,Z,gr);
  
          
//           SPoint3 pc = face.interpolate(t1, t2);
//           MVertex *v = new MVertex(pc.x(), pc.y(), pc.z(), gr);
          // faceVertices[p].push_back(v);
          vtcs.push_back(v);
          gr->mesh_vertices.push_back(v);
          vf.push_back(v);
        } 
        
//         for(int j = 0; j < nPts; j++){
//           for(int k = 0 ; k < nPts - j - 1; k++){
            
//             // KH: inverted direction to stick with triangle vertex numbering
//             // is not consistent with function space definitions for p>4
//             // 
//             // 2
//             // | \
//             // 0 - 1
//             // 
//             // double t1 = (double)(j + 1) / (nPts + 1);
//             // double t2 = (double)(k + 1) / (nPts + 1);
//             double t1 = (double)(k + 1) / (nPts + 1);
//             double t2 = (double)(j + 1) / (nPts + 1);

//             SPoint3 pc = face.interpolate(t1, t2);
//             MVertex *v = new MVertex(pc.x(), pc.y(), pc.z(), gr);
//             // faceVertices[p].push_back(v);
//             vtcs.push_back(v);
//             gr->mesh_vertices.push_back(v);
//             vf.push_back(v);
//           }
//         }
      }
      else if(face.getNumVertices() == 4){ // quadrangles
        for(int j = 0; j < nPts; j++){
          for(int k = 0; k < nPts; k++){
            // parameters are between -1 and 1
            double t1 = 2. * (double)(j + 1) / (nPts + 1) - 1.;
            double t2 = 2. * (double)(k + 1) / (nPts + 1) - 1.;
            SPoint3 pc = face.interpolate(t1, t2);
            MVertex *v = new MVertex(pc.x(), pc.y(), pc.z(), gr);
            // faceVertices[p].push_back(v);
            vtcs.push_back(v);
            gr->mesh_vertices.push_back(v);
            vf.push_back(v);
          }
        }
      }
    }
  }
}


void getRegionVertices(GRegion *gr, 
		       MElement *incomplete, 
		       MElement *ele, 
		       std::vector<MVertex*> &vr,
		       bool linear, int nPts = 1)
{

  Double_Matrix points;
  int start = 0;

  switch (nPts){
  case 3 :
    points = gmshFunctionSpaces::find(MSH_TET_35).points;
    start = 34;
    break;
  case 4 :
    points = gmshFunctionSpaces::find(MSH_TET_56).points;
    start = 52;
    break;
  default :  
    return;
    break;
  }

  for(int k = start ; k < points.size1() ; k++){
    MVertex *v;
    const double t1 = points(k, 0);
    const double t2 = points(k, 1);
    const double t3 = points(k, 2);
    
    SPoint3 pos;
    incomplete->pnt(t1,t2,t3,pos);
    v = new MVertex(pos.x(),pos.y(),pos.z(),gr);
    
    // FIXME: KOEN - I had to comment this out (MElement does not have
    // pnt() member) -- CG

    // SPoint3 pos;
    // incomplete->pnt(t1,t2,t3,pos);
    // v = new MVertex(pos.x(),pos.y(),pos.z(),gr);
    
    //     double X(0),Y(0),Z(0);
    //     for (int j=0; j<incomplete->getNumVertices(); j++){
    //       double sf ; incomplete->getShapeFunction(j,t1,t2,t3,sf);
    //       MVertex *vt = incomplete->getVertex(j);
    //       X += sf * vt->x();
    //       Y += sf * vt->y();
    //       Z += sf * vt->z();
    //     }
    //    v = new MVertex(X,Y,Z, gr);
    
    gr->mesh_vertices.push_back(v);
    vr.push_back(v);
  }
}


void setHighOrder(GEdge *ge, edgeContainer &edgeVertices, bool linear, 
                  int nbPts = 1)
{
  std::vector<MLine*> lines2;
  for(unsigned int i = 0; i < ge->lines.size(); i++){
    MLine *l = ge->lines[i];
    std::vector<MVertex*> ve;
    getEdgeVertices(ge, l, ve, edgeVertices, linear, nbPts);
    if(nbPts == 1)
      lines2.push_back(new MLine3(l->getVertex(0), l->getVertex(1), ve[0]));
    else
      lines2.push_back(new MLineN(l->getVertex(0), l->getVertex(1), ve));      
    delete l;
  }
  ge->lines = lines2;
  ge->deleteVertexArrays();
}

void setHighOrder(GFace *gf, edgeContainer &edgeVertices, 
                  faceContainer &faceVertices, bool linear, bool incomplete,
                  int nPts = 1)
{
  std::vector<MTriangle*> triangles2;
  for(unsigned int i = 0; i < gf->triangles.size(); i++){
    MTriangle *t = gf->triangles[i];
    std::vector<MVertex*> ve, vf;
    getEdgeVertices(gf, t, ve, edgeVertices, linear, nPts);
    if(nPts == 1){
      triangles2.push_back
        (new MTriangle6(t->getVertex(0), t->getVertex(1), t->getVertex(2),
                        ve[0], ve[1], ve[2]));
    }
    else{
      if(incomplete){
        triangles2.push_back
          (new MTriangleN(t->getVertex(0), t->getVertex(1), t->getVertex(2),
                          ve, nPts + 1));
      }
      else{
	if (0 && gf->geomType() == GEntity::Plane){
	  getFaceVertices(gf, t, vf, faceVertices, linear, nPts);
	}
	else{
	  MTriangleN incpl (t->getVertex(0), t->getVertex(1), t->getVertex(2),
			    ve, nPts + 1);
	  getFaceVertices(gf, &incpl, t, vf, faceVertices, linear, nPts);
	}
        ve.insert(ve.end(), vf.begin(), vf.end());
        triangles2.push_back
          (new MTriangleN(t->getVertex(0), t->getVertex(1), t->getVertex(2),
                          ve, nPts + 1));
      }
    }
    delete t;
  }
  gf->triangles = triangles2;
  
  std::vector<MQuadrangle*> quadrangles2;
  for(unsigned int i = 0; i < gf->quadrangles.size(); i++){
    MQuadrangle *q = gf->quadrangles[i];
    std::vector<MVertex*> ve, vf;
    getEdgeVertices(gf, q, ve, edgeVertices, linear, nPts);
    if(incomplete){
      quadrangles2.push_back
        (new MQuadrangle8(q->getVertex(0), q->getVertex(1), q->getVertex(2),
                          q->getVertex(3), ve[0], ve[1], ve[2], ve[3]));
    }
    else{
      getFaceVertices(gf, q, vf, faceVertices, linear, nPts);
      quadrangles2.push_back
        (new MQuadrangle9(q->getVertex(0), q->getVertex(1), q->getVertex(2),
                          q->getVertex(3), ve[0], ve[1], ve[2], ve[3], vf[0]));
    }
    delete q;
  }
  gf->quadrangles = quadrangles2;
  gf->deleteVertexArrays();
}

void setHighOrder(GRegion *gr, edgeContainer &edgeVertices, 
                  faceContainer &faceVertices, bool linear, bool incomplete,
                  int nPts = 1)
{
  int nbCorr = 0;
  
  std::vector<MTetrahedron*> tetrahedra2;
  for(unsigned int i = 0; i < gr->tetrahedra.size(); i++){
    MTetrahedron *t = gr->tetrahedra[i];

    std::set<MVertex*> blocked;
    
    std::vector<MVertex*> ve,vf,vr;
    getEdgeVertices(gr, t, ve, blocked, edgeVertices, linear, nPts);
    if (nPts == 1){
      tetrahedra2.push_back
	(new MTetrahedron10(t->getVertex(0), t->getVertex(1), t->getVertex(2), 
			    t->getVertex(3), ve[0], ve[1], ve[2], ve[3], ve[4], ve[5]));
    }
    else{
      getFaceVertices(gr, t, vf, blocked, faceVertices, edgeVertices, linear, nPts);
      ve.insert(ve.end(), vf.begin(), vf.end());     
      MTetrahedronN incpl (t->getVertex(0), t->getVertex(1), t->getVertex(2), t->getVertex(3),
			   ve, nPts + 1);
      getRegionVertices(gr, &incpl, t, vr, linear, nPts); 
      ve.insert(ve.end(), vr.begin(), vr.end());

      MTetrahedron* n = new MTetrahedronN(t->getVertex(0), t->getVertex(1), t->getVertex(2), t->getVertex(3),ve, nPts + 1);

      if (!mappingIsInvertible(n)) {
        Msg::Warning("Found invalid curved volume element (# %d in list) ",i);
        
        // if (deformElement(n,gr,blocked)) {
//           if (mappingIsInvertible(n)) printf(" - corrected using Poisson smoothing\n");
//           else                        printf(" - could not correct the mapping\n");
//           nbCorr++;
//         }
      }
      tetrahedra2.push_back (n);
    }
    delete t;
  }
  gr->tetrahedra = tetrahedra2;

  std::vector<int> invalid;
  
  if (nbCorr != 0) {
    for(unsigned int i = 0; i < gr->tetrahedra.size(); i++){
      if (!mappingIsInvertible(gr->tetrahedra[i])) invalid.push_back(i);
    }

    if (invalid.size()!=0) {
      Msg::Warning("We have %d invalid elements remaining\n",(int) invalid.size());
      std::vector<int>::iterator iIter = invalid.begin();
      for (;iIter!=invalid.end();++iIter) printf("%d ",*iIter);
      printf("\n");
    }
  }
  

  std::vector<MHexahedron*> hexahedra2;
  for(unsigned int i = 0; i < gr->hexahedra.size(); i++){
    MHexahedron *h = gr->hexahedra[i];
    std::vector<MVertex*> ve, vf;
    std::set<MVertex*> blocked;
    getEdgeVertices(gr, h, ve, blocked, edgeVertices, linear, nPts);
    if(incomplete){
      hexahedra2.push_back
        (new MHexahedron20(h->getVertex(0), h->getVertex(1), h->getVertex(2), 
                           h->getVertex(3), h->getVertex(4), h->getVertex(5), 
                           h->getVertex(6), h->getVertex(7), ve[0], ve[1], ve[2], 
                           ve[3], ve[4], ve[5], ve[6], ve[7], ve[8], ve[9], ve[10], 
                           ve[11]));
    }
    else{
      getFaceVertices(gr, h, vf, blocked, faceVertices, edgeVertices, linear, nPts);
      SPoint3 pc = h->barycenter();
      MVertex *v = new MVertex(pc.x(), pc.y(), pc.z(), gr);
      gr->mesh_vertices.push_back(v);
      hexahedra2.push_back
        (new MHexahedron27(h->getVertex(0), h->getVertex(1), h->getVertex(2), 
                           h->getVertex(3), h->getVertex(4), h->getVertex(5), 
                           h->getVertex(6), h->getVertex(7), ve[0], ve[1], ve[2], 
                           ve[3], ve[4], ve[5], ve[6], ve[7], ve[8], ve[9], ve[10], 
                           ve[11], vf[0], vf[1], vf[2], vf[3], vf[4], vf[5], v));
    }
    delete h;
  }
  gr->hexahedra = hexahedra2;

  std::vector<MPrism*> prisms2;
  for(unsigned int i = 0; i < gr->prisms.size(); i++){
    MPrism *p = gr->prisms[i];
    std::vector<MVertex*> ve, vf;
    std::set<MVertex*> blocked;
    getEdgeVertices(gr, p, ve, blocked,edgeVertices, linear, nPts);
    if(incomplete){
      prisms2.push_back
        (new MPrism15(p->getVertex(0), p->getVertex(1), p->getVertex(2), 
                      p->getVertex(3), p->getVertex(4), p->getVertex(5), 
                      ve[0], ve[1], ve[2], ve[3], ve[4], ve[5], ve[6], ve[7], ve[8]));
    }
    else{
      getFaceVertices(gr, p, vf, blocked, faceVertices, edgeVertices, linear, nPts);
      prisms2.push_back
        (new MPrism18(p->getVertex(0), p->getVertex(1), p->getVertex(2), 
                      p->getVertex(3), p->getVertex(4), p->getVertex(5), 
                      ve[0], ve[1], ve[2], ve[3], ve[4], ve[5], ve[6], ve[7], ve[8],
                      vf[0], vf[1], vf[2]));
    }
    delete p;
  }
  gr->prisms = prisms2;

  std::vector<MPyramid*> pyramids2;
  for(unsigned int i = 0; i < gr->pyramids.size(); i++){
    MPyramid *p = gr->pyramids[i];
    std::vector<MVertex*> ve, vf;
    std::set<MVertex*> blocked;
    getEdgeVertices(gr, p, ve, blocked, edgeVertices, linear, nPts);
    if(incomplete){
      pyramids2.push_back
        (new MPyramid13(p->getVertex(0), p->getVertex(1), p->getVertex(2), 
                        p->getVertex(3), p->getVertex(4), ve[0], ve[1], ve[2], 
                        ve[3], ve[4], ve[5], ve[6], ve[7]));
    }
    else{
      getFaceVertices(gr, p, vf, blocked, faceVertices, edgeVertices, linear, nPts);
      pyramids2.push_back
        (new MPyramid14(p->getVertex(0), p->getVertex(1), p->getVertex(2), 
                        p->getVertex(3), p->getVertex(4), ve[0], ve[1], ve[2], 
                        ve[3], ve[4], ve[5], ve[6], ve[7], vf[0]));
    }
    delete p;
  }
  gr->pyramids = pyramids2;
  gr->deleteVertexArrays();
}

template<class T>
void setFirstOrder(GEntity *e, std::vector<T*> &elements)
{
  std::vector<T*> elements1;
  for(unsigned int i = 0; i < elements.size(); i++){
    T *ele = elements[i];
    int n = ele->getNumPrimaryVertices();
    std::vector<MVertex*> v1;
    for(int j = 0; j < n; j++)
      v1.push_back(ele->getVertex(j));
    for(int j = n; j < ele->getNumVertices(); j++)
      ele->getVertex(j)->setVisibility(-1);
    elements1.push_back(new T(v1));
    delete ele;
  }
  elements = elements1;
  e->deleteVertexArrays();
}

void removeHighOrderVertices(GEntity *e)
{
  std::vector<MVertex*> v1;
  for(unsigned int i = 0; i < e->mesh_vertices.size(); i++){
    if(e->mesh_vertices[i]->getVisibility() < 0)
      delete e->mesh_vertices[i];
    else
      v1.push_back(e->mesh_vertices[i]);
  }
  e->mesh_vertices = v1;
}

void SetOrder1(GModel *m)
{
  // replace all elements with first order elements and mark all
  // unused vertices with a -1 visibility flag
  for(GModel::eiter it = m->firstEdge(); it != m->lastEdge(); ++it){
    setFirstOrder(*it, (*it)->lines);
  }
  for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){
    setFirstOrder(*it, (*it)->triangles);
    setFirstOrder(*it, (*it)->quadrangles);
  }
  for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it){
    setFirstOrder(*it, (*it)->tetrahedra);
    setFirstOrder(*it, (*it)->hexahedra);
    setFirstOrder(*it, (*it)->prisms);
    setFirstOrder(*it, (*it)->pyramids);
  }

  // remove all vertices with a -1 visibility flag
  for(GModel::eiter it = m->firstEdge(); it != m->lastEdge(); ++it)
    removeHighOrderVertices(*it);
  for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it)
    removeHighOrderVertices(*it);
  for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it)
    removeHighOrderVertices(*it);
}

bool straightLine(std::vector<MVertex*> &l, MVertex *n1, MVertex *n2)
{
  // x = a * t + b
  // x1 = b
  // x2 = a + b
  for(unsigned int i = 0; i < l.size(); i++){
    MVertex *v = l[i];
    double b = n1->x();
    double a = n2->x() - b;
    double t = (v->x() - b) / a;
    double by = n1->y();
    double ay = n2->y() - by;
    double y = ay * t + by;
    if(fabs(y-v->y()) > 1.e-07 * CTX.lc){
      return false;      
    }
  }
  return true;
}

static double mesh_functional_distorsion(MTriangle *t, double u, double v)
{
  // compute uncurved element jacobian d_u x and d_v x
  double mat[2][3];  
  // t->getPrimaryJacobian(0, 0, 0, mat);
  t->getJacobian(0, 0, 0, mat);
  
  double v1[3] = {mat[0][0], mat[0][1], mat[0][2]};
  double v2[3] = {mat[1][0], mat[1][1], mat[1][2]};
  double normal1[3];
  prodve(v1, v2, normal1);
  double nn = sqrt(SQU(normal1[0]) + SQU(normal1[1]) + SQU(normal1[2]));
  
  // compute uncurved element jacobian d_u x and d_v x
  t->getJacobian(u, v, 0, mat);
  double v1b[3] = {mat[0][0], mat[0][1], mat[0][2]};
  double v2b[3] = {mat[1][0], mat[1][1], mat[1][2]};
  double normal[3];
  prodve(v1b, v2b, normal);
  
  double sign;
  prosca(normal1, normal, &sign);
  double det = norm3(normal) * (sign > 0 ? 1. : -1.) / nn;  

  // compute distorsion
  double dist = std::min(1. / det, det); 
  return dist;
}

void getMinMaxJac (MTriangle *t, double &minJ, double &maxJ)
{
  double mat[2][3];  
  int n = 3;
  // t->getPrimaryJacobian(0, 0, 0, mat);
  t->getJacobian(0, 0, 0, mat);
  double v1[3] = {mat[0][0], mat[0][1], mat[0][2]};
  double v2[3] = {mat[1][0], mat[1][1], mat[1][2]};
  double normal1[3], normal[3];
  prodve(v1, v2, normal1);
  double nn = sqrt(SQU(normal1[0]) + SQU(normal1[1]) + SQU(normal1[2]));
  for(int i = 0; i < n; i++){
    for(int k = 0; k < n - i; k++){
      t->getJacobian((double)i / (n - 1), (double)k / (n - 1), 0, mat);
      double v1b[3] = {mat[0][0], mat[0][1], mat[0][2]};
      double v2b[3] = {mat[1][0], mat[1][1], mat[1][2]};
      prodve(v1b, v2b, normal);
      double sign; 
      prosca(normal1, normal, &sign);
      double det = norm3(normal) * (sign > 0 ? 1. : -1.) / nn;
      minJ = std::min(1. / det, std::min(det, minJ));
      maxJ = std::max(det, maxJ);
    }
  }
}

struct smoothVertexDataHO{
  MVertex *v;
  GFace *gf;
  std::vector<MTriangle*> ts;
}; 

struct smoothVertexDataHON{
  std::vector<MVertex*> v;
  GFace *gf;
  std::vector<MTriangle*> ts;
}; 

double smoothing_objective_function_HighOrder(double U, double V, MVertex *v, 
                                              std::vector<MTriangle*> &ts, GFace *gf)
{
  GPoint gp = gf->point(U, V);
  const double oldX = v->x();
  const double oldY = v->y();
  const double oldZ = v->z();

  v->x() = gp.x();
  v->y() = gp.y();
  v->z() = gp.z();

  double minJ =  1.e22;
  double maxJ = -1.e22;
  for (unsigned int i = 0; i < ts.size(); i++){
    getMinMaxJac (ts[i], minJ, maxJ);
  }
  v->x() = oldX;
  v->y() = oldY;
  v->z() = oldZ;
  
  return -minJ;
}

void deriv_smoothing_objective_function_HighOrder(double U, double V, 
                                                  double &F, double &dFdU,
                                                  double &dFdV, void *data)
{
  smoothVertexDataHO *svd = (smoothVertexDataHO*)data;
  MVertex *v = svd->v;
  const double LARGE = -1.e5;
  const double SMALL = 1./LARGE;
  F   = smoothing_objective_function_HighOrder(U, V, v, svd->ts, svd->gf);
  double F_U = smoothing_objective_function_HighOrder(U + SMALL, V, v, svd->ts, svd->gf);
  double F_V = smoothing_objective_function_HighOrder(U, V + SMALL, v, svd->ts, svd->gf);
  dFdU = (F_U - F) * LARGE;
  dFdV = (F_V - F) * LARGE;
}

double smooth_obj_HighOrder(double U, double V, void *data)
{
  smoothVertexDataHO *svd = (smoothVertexDataHO*)data;
  return  smoothing_objective_function_HighOrder(U, V, svd->v, svd->ts, svd->gf); 
}

double smooth_obj_HighOrderN(double *uv, void *data)
{
  smoothVertexDataHON *svd = (smoothVertexDataHON*)data;
  double oldX[10],oldY[10],oldZ[10];
  for (unsigned int i = 0; i < svd->v.size(); i++){
    GPoint gp = svd->gf->point(uv[2 * i], uv[2 * i + 1]);
    oldX[i] = svd->v[i]->x();
    oldY[i] = svd->v[i]->y();
    oldZ[i] = svd->v[i]->z();
    svd->v[i]->x() = gp.x();
    svd->v[i]->y() = gp.y();
    svd->v[i]->z() = gp.z();
  }
  double minJ =  1.e22;
  double maxJ = -1.e22;
  for(unsigned int i = 0; i < svd->ts.size(); i++){
    getMinMaxJac (svd->ts[i], minJ, maxJ);
  }
  for(unsigned int i = 0; i < svd->v.size(); i++){
    svd->v[i]->x() = oldX[i];
    svd->v[i]->y() = oldY[i];
    svd->v[i]->z() = oldZ[i];
  }
  return -minJ;
}

void deriv_smoothing_objective_function_HighOrderN(double *uv, double *dF, 
                                                   double &F, void *data)
{
  const double LARGE = -1.e2;
  const double SMALL = 1. / LARGE;
  smoothVertexDataHON *svd = (smoothVertexDataHON*)data;
  F = smooth_obj_HighOrderN(uv, data);
  for (unsigned int i = 0; i < svd->v.size(); i++){
    uv[i] += SMALL;
    dF[i] = (smooth_obj_HighOrderN(uv, data) - F) * LARGE;
    uv[i] -= SMALL;
  }
}

void optimizeNodeLocations(GFace *gf, smoothVertexDataHON &vdN, double eps = .2)
{
  if(!vdN.v.size()) return;
  double uv[20];
  for (unsigned int i = 0; i < vdN.v.size(); i++){
    if (!vdN.v[i]->getParameter(0, uv[2 * i])){
      Msg::Error("Node location optimization failed");
      return;
    }
    if (!vdN.v[i]->getParameter(1, uv[2 * i + 1])){
      Msg::Error("Node location optimization failed");
      return;
    }
  }

  double F = -smooth_obj_HighOrderN(uv, &vdN);
  if (F < eps){
    double val;
    minimize_N(2 * vdN.v.size(), 
               smooth_obj_HighOrderN, 
               deriv_smoothing_objective_function_HighOrderN, 
               &vdN, 1, uv,val);
    double Fafter = -smooth_obj_HighOrderN(uv, &vdN);
    printf("%12.5E %12.5E\n", F, Fafter);
    if (F < Fafter){
      for (unsigned int i = 0; i < vdN.v.size(); i++){
        vdN.v[i]->setParameter(0, uv[2 * i]);
        vdN.v[i]->setParameter(1, uv[2 * i + 1]);
        GPoint gp = gf->point(uv[2 * i], uv[2 * i + 1]);
        vdN.v[i]->x() = gp.x();
        vdN.v[i]->y() = gp.y();
        vdN.v[i]->z() = gp.z();
      }
    }     
  }
}

void optimizeHighOrderMeshInternalNodes(GFace *gf)
{
  for(unsigned int i = 0; i < gf->triangles.size(); i++){
    MTriangle *t = gf->triangles[i];
    smoothVertexDataHON vdN;
    int start = t->getNumVertices() - t->getNumFaceVertices();
    for (int j=start;j<t->getNumVertices();j++)
      vdN.v.push_back(t->getVertex(j));
    vdN.gf = gf;
    vdN.ts.push_back(t);
    optimizeNodeLocations(gf, vdN, .9);
  }
}

bool optimizeHighOrderMesh(GFace *gf, edgeContainer &edgeVertices)
{
  v2t_cont adjv;
  buildVertexToTriangle(gf->triangles, adjv);

  typedef std::map<std::pair<MVertex*, MVertex*>, std::vector<MElement*> > edge2tris;
  edge2tris e2t;
  for(unsigned int i = 0; i < gf->triangles.size(); i++){
    MTriangle *t = gf->triangles[i];
    for(int j = 0; j < t->getNumEdges(); j++){
      MEdge edge = t->getEdge(j);
      std::pair<MVertex*, MVertex*> p(edge.getMinVertex(), edge.getMaxVertex());
      e2t[p].push_back(t);
    }
  }
  /*
  v2t_cont :: iterator it = adjv.begin();      
  while (it != adjv.end()){
    MVertex *ver= it->first;
    GEntity *ge = ver->onWhat();
    if (ge->dim() == 2){
      double initu,initv;
      ver->getParameter(0, initu);
      ver->getParameter(1, initv);        

      smoothVertexDataHON vdN;
      vdN.ts = it->second;
      for (int i=0;i<vdN.ts.size();i++){
        MTriangle *t = vdN.ts[i];
      }

      vdN.v = e;
      vdN.gf = gf;

      double val;      
      double F = -smooth_obj_HighOrder(initu,initv, &vd);
      if (F < .2){
        minimize_2(smooth_obj_HighOrder, 
             deriv_smoothing_objective_function_HighOrder, &vd, 1, initu,initv,val);
        double Fafter = -smooth_obj_HighOrder(initu,initv, &vd);
        if (F < Fafter){
          success = true;
          ver->setParameter(0,initu);
          ver->setParameter(1,initv);
          GPoint gp = gf->point(initu,initv);
          ver->x() = gp.x();
          ver->y() = gp.y();
          ver->z() = gp.z();  
        }
      }                         
    }
    ++it;
  }
  */
  bool success = false;
  
  for(edge2tris::iterator it = e2t.begin(); it != e2t.end(); ++it){
    std::pair<MVertex*, MVertex*> edge = it->first;
    std::vector<MVertex*> e;
    std::vector<MElement*> triangles = it->second;
    if(triangles.size() == 2){
      MVertex *n2 = edge.first; 
      MVertex *n4 = edge.second;
      MTriangle *t1 = (MTriangle*)triangles[0];
      MTriangle *t2 = (MTriangle*)triangles[1];
      if(n2 < n4)
        e = edgeVertices[std::make_pair<MVertex*, MVertex*> (n2, n4)];
      else
        e = edgeVertices[std::make_pair<MVertex*, MVertex*> (n4, n2)];

      if (e.size() < 5){
        smoothVertexDataHON vdN;
        vdN.v = e;
        vdN.gf = gf;
        vdN.ts.clear();
        vdN.ts.push_back(t1);
        vdN.ts.push_back(t2);   
        optimizeNodeLocations(gf, vdN);
      }
    }
  }

  return success;
}

double angle3Points ( MVertex *p1, MVertex *p2, MVertex *p3 ){
  SVector3 a(p1->x()-p2->x(),p1->y()-p2->y(),p1->z()-p2->z());
  SVector3 b(p3->x()-p2->x(),p3->y()-p2->y(),p3->z()-p2->z());
  SVector3 c = crossprod(a,b);
  double sinA = c.norm();
  double cosA = dot(a,b);
  //  printf("%d %d %d -> %g %g\n",p1->iD,p2->iD,p3->iD,cosA,sinA);
  return atan2 (sinA,cosA);  
}

bool smoothInternalEdges(GFace *gf, edgeContainer &edgeVertices)
{
  typedef std::map<std::pair<MVertex*, MVertex*>, std::vector<MElement*> > edge2tris;
  edge2tris e2t;
  for(unsigned int i = 0; i < gf->triangles.size(); i++){
    MTriangle *t = gf->triangles[i];
    for(int j = 0; j < t->getNumEdges(); j++){
      MEdge edge = t->getEdge(j);
      std::pair<MVertex*, MVertex*> p(edge.getMinVertex(), edge.getMaxVertex());
      e2t[p].push_back(t);
    }
  }

  bool success = false;

  const int NBST = 10;

  for(edge2tris::iterator it = e2t.begin(); it != e2t.end(); ++it){
    std::pair<MVertex*, MVertex*> edge = it->first;
    std::vector<MVertex*> e1, e2, e3, e4, e;
    std::vector<MElement*> triangles = it->second;
    if(triangles.size() == 2){
      MVertex *n2 = edge.first; 
      MVertex *n4 = edge.second;
      MTriangle *t1 = (MTriangle*)triangles[0];
      MTriangle *t2 = (MTriangle*)triangles[1];
      MVertex *n1 = t1->getOtherVertex(n2, n4);
      MVertex *n3 = t2->getOtherVertex(n2, n4);
      
      double ang1 = angle3Points(n1,n2,n3);
      double ang2 = angle3Points(n2,n3,n4);
      double ang3 = angle3Points(n3,n4,n1);
      double ang4 = angle3Points(n4,n1,n2);
      const double angleLimit = 3*M_PI/4.;

      if (ang1 < angleLimit && ang2 < angleLimit && ang3 < angleLimit && ang4 < angleLimit){
	if(n1 < n2)
	  e1 = edgeVertices[std::make_pair<MVertex*, MVertex*>(n1, n2)];
	else
	  e1 = edgeVertices[std::make_pair<MVertex*, MVertex*>(n2, n1)];
	if(n2 < n3)
	  e2 = edgeVertices[std::make_pair<MVertex*, MVertex*>(n2, n3)];
	else
	  e2 = edgeVertices[std::make_pair<MVertex*, MVertex*>(n3, n2)];
	if(n3 < n4)
	  e3 = edgeVertices[std::make_pair<MVertex*, MVertex*>(n3, n4)];
	else
	  e3 = edgeVertices[std::make_pair<MVertex*, MVertex*>(n4, n3)];
	if(n4 < n1)
	  e4 = edgeVertices[std::make_pair<MVertex*, MVertex*>(n4, n1)];
	else
	  e4 = edgeVertices[std::make_pair<MVertex*, MVertex*>(n1, n4)];
	if(n2 < n4)
	  e = edgeVertices[std::make_pair<MVertex*, MVertex*>(n2, n4)];
	else
	  e = edgeVertices[std::make_pair<MVertex*, MVertex*>(n4, n2)];
	
	if((!straightLine(e1, n1, n2) || !straightLine(e2, n2, n3) ||
	    !straightLine(e3, n3, n4) || !straightLine(e4, n4, n1))){
	  
	  double Unew[NBST][10],Vnew[NBST][10];
	  double Xold[10],Yold[10],Zold[10];
	  
	  for(unsigned int i = 0; i < e.size(); i++){
	    Xold[i] = e[i]->x();
	    Yold[i] = e[i]->y();
	    Zold[i] = e[i]->z();
	  }
	  
	  double minJ = 1.e22;
	  double maxJ = -1.e22;       
	  getMinMaxJac (t1, minJ, maxJ);
	  getMinMaxJac (t2, minJ, maxJ);
	  int kopt = -1; 
	  for (int k=0;k<NBST;k++){
	    double relax = (k+1)/(double)NBST;
	    for(unsigned int i = 0; i < e.size(); i++){
	      double v = (double)(i + 1) / (e.size() + 1);
	      double u = 1. - v;
	      MVertex *vert  = (n2 < n4) ? e[i] : e[e.size() - i - 1];
	      MVertex *vert1 = (n1 < n2) ? e1[e1.size() - i - 1] : e1[i];
	      MVertex *vert3 = (n3 < n4) ? e3[i] : e3[e3.size() - i - 1];
	      MVertex *vert4 = (n4 < n1) ? e4[e4.size() - i - 1] : e4[i];
	      MVertex *vert2 = (n2 < n3) ? e2[i] : e2[e2.size() - i - 1];	    
	      double U1,V1,U2,V2,U3,V3,U4,V4,U,V,nU1,nV1,nU2,nV2,nU3,nV3,nU4,nV4;
	      parametricCoordinates(vert , gf, U, V);
	      parametricCoordinates(vert1, gf, U1, V1);
	      parametricCoordinates(vert2, gf, U2, V2);
	      parametricCoordinates(vert3, gf, U3, V3);
	      parametricCoordinates(vert4, gf, U4, V4);
	      parametricCoordinates(n1, gf, nU1, nV1);
	      parametricCoordinates(n2, gf, nU2, nV2);
	      parametricCoordinates(n3, gf, nU3, nV3);
	      parametricCoordinates(n4, gf, nU4, nV4);
	      
	      Unew[k][i] = U + relax * ((1.-u) * U4 + u * U2 +
					(1.-v) * U1 + v * U3 -
					((1.-u)*(1.-v) * nU1 
					 + u * (1.-v) * nU2 
					 + u * v * nU3 
					 + (1.-u) * v * nU4) - U);
	      Vnew[k][i] = V + relax * ((1.-u) * V4 + u * V2 +
					(1.-v) * V1 + v * V3 -
					((1.-u)*(1.-v) * nV1 
					 + u * (1.-v) * nV2 
					 + u * v * nV3 
					 + (1.-u) * v * nV4) - V);
	      GPoint gp = gf->point(Unew[k][i],Vnew[k][i]);
	      vert->x() = gp.x();
	      vert->y() = gp.y();
	      vert->z() = gp.z();
	    }
	    double minJloc = 1.e22;
	    double maxJloc = -1.e22;          
	    getMinMaxJac(t1, minJloc, maxJloc);
	    getMinMaxJac(t2, minJloc, maxJloc);
	    //	  printf("relax = %g min %g max %g\n",relax,minJloc,maxJloc);
	    
	    if (minJloc > minJ){
	      kopt = k;
	      minJ = minJloc;
	    }
	  }
	  //	kopt = 1;
	  if (kopt == -1){
	    for(unsigned int i = 0; i < e.size(); i++){
	      e[i]->x() = Xold[i];
	      e[i]->y() = Yold[i];
	      e[i]->z() = Zold[i];
	    }      
	  }
	  else{
	    success = true;
	    for(unsigned int i = 0; i < e.size(); i++){
	      MVertex *vert  = (n2 < n4) ? e[i] : e[e.size() - i - 1];
	      vert->setParameter(0,Unew[kopt][i]);
	      vert->setParameter(1,Vnew[kopt][i]);
	      GPoint gp = gf->point(Unew[kopt][i],Vnew[kopt][i]);
	      vert->x() = gp.x();
	      vert->y() = gp.y();
	      vert->z() = gp.z();
	    }      
	  }
	}
      }
    }
  }    
  return success;
}

void checkHighOrderTriangles(GModel *m)
{
  double minJGlob = 1.e22;
  double maxJGlob = -1.e22;
  for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){
    double minJ = 1.e22;
    double maxJ = -1.e22;
    for(unsigned int i = 0; i < (*it)->triangles.size(); i++){
      double minJloc = 1.e22;
      double maxJloc = -1.e22;      
      MTriangle *t = (*it)->triangles[i];
      if(t->getPolynomialOrder() > 1 && t->getPolynomialOrder() < 6){
        getMinMaxJac (t, minJloc, maxJloc);
        minJ = std::min(minJ, minJloc);
        maxJ = std::max(maxJ, maxJloc);
      }
    }
    minJGlob = std::min(minJGlob,minJ);
    maxJGlob = std::max(maxJGlob,maxJ);
  }
  if (minJGlob >= 0) Msg::Info("Jacobian Range (%12.5E,%12.5E)", minJGlob, maxJGlob);
  else Msg::Warning("Jacobian Range (%12.5E,%12.5E)", minJGlob, maxJGlob);
}  

void printJacobians(GModel *m, const char *nm)
{
  return;

  const int n = 15;
  double D[n][n];
  double X[n][n];
  double Y[n][n];
  double Z[n][n];

  FILE *f = fopen(nm,"w");
  fprintf(f,"View \"\"{\n");
  for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){
    for(unsigned int i = 0; i < (*it)->triangles.size(); i++){
      MTriangle *t = (*it)->triangles[i];
      for(int i = 0; i < n; i++){
        for(int k = 0; k < n - i; k++){
          SPoint3 pt;
          double u = (double)i / (n - 1);
          double v = (double)k / (n - 1);         
          t->pnt(u, v, 0, pt);
          D[i][k] = mesh_functional_distorsion(t, u, v);
          X[i][k] = pt.x();
          Y[i][k] = pt.y();
          Z[i][k] = pt.z();
        }
      }
      for(int i = 0; i < n -1; i++){
        for(int k = 0; k < n - i -1; k++){
          fprintf(f,"ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%22.15E,%22.15E,%22.15E};\n",
                  X[i][k],Y[i][k],Z[i][k],
                  X[i+1][k],Y[i+1][k],Z[i+1][k],
                  X[i][k+1],Y[i][k+1],Z[i][k+1],
                  D[i][k],
                  D[i+1][k],
                  D[i][k+1]);
          if (i != n-2 && k != n - i -2)
            fprintf(f,"ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%22.15E,%22.15E,%22.15E};\n",
                    X[i+1][k],Y[i+1][k],Z[i+1][k],
                    X[i+1][k+1],Y[i+1][k+1],Z[i+1][k+1],
                    X[i][k+1],Y[i][k+1],Z[i][k+1],
                    D[i+1][k],
                    D[i+1][k+1],
                    D[i][k+1]);
        }
      }
    }
  }
  fprintf(f,"};\n");
  fclose(f);
}

void SetOrderN(GModel *m, int order, bool linear, bool incomplete)
{
  // replace all the elements in the mesh with second order elements
  // by creating unique vertices on the edges/faces of the mesh:
  //
  // - if linear is set to true, new vertices are created by linear
  //   interpolation between existing ones. If not, new vertices are
  //   created on the exact geometry, provided that the geometrical
  //   edges/faces are discretized with 1D/2D elements. (I.e., if
  //   there are only 3D elements in the mesh then any new vertices on
  //   the boundary will always be created by linear interpolation,
  //   whether linear is set or not.)
  //
  // - if incomplete is set to true, we only create new vertices on 
  //   edges (creating 8-node quads, 20-node hexas, etc., instead of
  //   9-node quads, 27-node hexas, etc.)

#if !defined(HAVE_GSL)
  Msg::Error("High order mesh generation requires the GSL");
  return;
#endif

  int nPts = order - 1;

  Msg::StatusBar(1, true, "Generating High Order Nodes (q = %d) ...",order);
  double t1 = Cpu();

  // first, make sure to remove any existsing second order vertices/elements
  SetOrder1(m);

  // then create new second order vertices/elements
  edgeContainer edgeVertices;
  faceContainer faceVertices;
  for(GModel::eiter it = m->firstEdge(); it != m->lastEdge(); ++it)
    setHighOrder(*it, edgeVertices, linear, nPts);
  for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it)
    setHighOrder(*it, edgeVertices, faceVertices, linear, incomplete, nPts);
  for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it)
    setHighOrder(*it, edgeVertices, faceVertices, linear, incomplete, nPts);

  printJacobians(m, "detjIni.pos");  

  if(CTX.mesh.smooth_internal_edges){
    checkHighOrderTriangles(m);
    for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){      
      Msg::Info("Smoothing internal Edges in Surface %d",(*it)->tag());
      for (int i = 0; i < 10; i++) {
        if (!smoothInternalEdges(*it, edgeVertices))break;
        checkHighOrderTriangles(m);
      }
      //      optimizeHighOrderMeshInternalNodes(*it);
    }
    //    for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it){      
    //      for (int i=0;i<CTX.mesh.nb_smoothing;i++){
    //        if(!optimizeHighOrderMesh(*it, edgeVertices))break;
    //        checkHighOrderTriangles(m);
    //      }
    //    }
    printJacobians(m, "detjOpt.pos");  
  }


  checkHighOrderTriangles(m);

  double t2 = Cpu();
  Msg::Info("Meshing order %d complete (%g s)", order, t2 - t1);
  Msg::StatusBar(1, true, "Mesh");
}