Skip to content
Snippets Groups Projects
Select Git revision
  • 32d540750b23cf9248229e8faae05104c5e75016
  • master default protected
  • revert-ef4a3a4f
  • patch_releases_4_14
  • overlaps_tags_and_distributed_export
  • overlaps_tags_and_distributed_export_rebased
  • relaying
  • alphashapes
  • steplayer
  • bl
  • pluginMeshQuality
  • fixBugsAmaury
  • hierarchical-basis
  • new_export_boris
  • oras_vs_osm
  • reassign_partitions
  • distributed_fwi
  • rename-classes
  • fix/fortran-api-example-t4
  • robust_partitions
  • reducing_files
  • gmsh_4_14_0
  • gmsh_4_13_1
  • gmsh_4_13_0
  • gmsh_4_12_2
  • gmsh_4_12_1
  • gmsh_4_12_0
  • gmsh_4_11_1
  • gmsh_4_11_0
  • gmsh_4_10_5
  • gmsh_4_10_4
  • gmsh_4_10_3
  • gmsh_4_10_2
  • gmsh_4_10_1
  • gmsh_4_10_0
  • gmsh_4_9_5
  • gmsh_4_9_4
  • gmsh_4_9_3
  • gmsh_4_9_2
  • gmsh_4_9_1
  • gmsh_4_9_0
41 results

Navigator.cpp

Blame
  • GFaceCompound.cpp 95.79 KiB
    // Gmsh - Copyright (C) 1997-2013 C. Geuzaine, J.-F. Remacle
    //
    // See the LICENSE.txt file for license information. Please report all
    // bugs and problems to the public mailing list <gmsh@geuz.org>.
    //
    // Contributor(s):
    //   Emilie Marchandise
    //
    
    #include "GmshConfig.h"
    #include "GmshDefines.h"
    #include "GFaceCompound.h"
    #include "GEdgeCompound.h"
    #include "intersectCurveSurface.h"
    
    #if defined(HAVE_SOLVER)
    
    #include "Options.h"
    #include "MLine.h"
    #include "MTriangle.h"
    #include "Numeric.h"
    #include "OS.h"
    #include "Octree.h"
    #include "SBoundingBox3d.h"
    #include "SPoint3.h"
    #include "polynomialBasis.h"
    #include "robustPredicates.h"
    #include "MElementCut.h"
    #include "dofManager.h"
    #include "laplaceTerm.h"
    #include "crossConfTerm.h"
    #include "convexCombinationTerm.h"
    #include "diagBCTerm.h"
    #include "linearSystemGMM.h"
    #include "linearSystemCSR.h"
    #include "linearSystemFull.h"
    #include "linearSystemPETSc.h"
    #include "CreateFile.h"
    #include "Context.h"
    #include "discreteFace.h"
    #include "eigenSolver.h"
    #include "multiscaleLaplace.h"
    #include "GRbf.h"
    #include "Curvature.h"
    #include "MPoint.h"
    #include "Numeric.h"
    #include "meshGFace.h"
    #if defined(HAVE_ANN)
    #include <ANN/ANN.h>
    #endif
    
    static void fixEdgeToValue(GEdge *ed, double value, dofManager<double> &myAssembler)
    {
      myAssembler.fixVertex(ed->getBeginVertex()->mesh_vertices[0], 0, 1, value);
      myAssembler.fixVertex(ed->getEndVertex()->mesh_vertices[0], 0, 1, value);
      for(unsigned int i = 0; i < ed->mesh_vertices.size(); i++){
        myAssembler.fixVertex(ed->mesh_vertices[i], 0, 1, value);
      }
    }
    
    /*
    static void printBound(std::vector<MVertex*> &l, int tag)
    {
      char name[256];
      sprintf(name, "myBOUND%d.pos", tag);
      FILE * xyz = fopen(name,"w");
      fprintf(xyz,"View \"\"{\n");
      for(unsigned int i = 0; i < l.size(); i++){
        MVertex *v = l[i];
        fprintf(xyz,"SP(%g,%g,%g){%d};\n", v->x(), v->y(), v->z(), i);
      }
      fprintf(xyz,"};\n");
      fclose(xyz);
    }
    */
    
    static std::vector<MVertex*> getBlob(unsigned int minNbPt, v2t_cont::iterator it, v2t_cont &adj)
    {
    
      std::vector<MVertex*> vv(1,it->first), bvv = vv;                                                      // Vector of vertices in blob and in boundary of blob
      do {
        std::set<MVertex*> nbvv;                                                                            // Set of vertices in new boundary
        for (std::vector<MVertex*>::iterator itBV = bvv.begin(); itBV != bvv.end(); itBV++) {               // For each boundary vertex...
          std::vector<MElement*> &adjBV = adj[*itBV];
          for (std::vector<MElement*>::iterator itBVEl = adjBV.begin(); itBVEl != adjBV.end(); itBVEl++) {
            for (int iV=0; iV<(*itBVEl)->getNumVertices(); iV++){                                           // ... look for adjacent vertices...
              MVertex *v = (*itBVEl)->getVertex(iV);
              if (find(vv.begin(),vv.end(),v) == vv.end()) nbvv.insert(v);                                  // ... and add them in the new boundary if they are not already in the blob
            }
          }
        }
        if (nbvv.empty()) bvv.clear();
        else {
          bvv.assign(nbvv.begin(),nbvv.end());
          vv.insert(vv.end(),nbvv.begin(),nbvv.end());
        }
    //  } while (!bvv.empty());
      } while (vv.size() < minNbPt);                                                                        // Repeat until min. number of points is reached
    
      return vv;
    
    }
    
    static bool orderVertices(const std::list<GEdge*> &e, std::vector<MVertex*> &l,
                              std::vector<double> &coord)
    {
      l.clear();
      coord.clear();
    
      std::list<GEdge*>::const_iterator it = e.begin();
      std::list<MLine*> temp;
      double tot_length = 0;
      for( ; it != e.end(); ++it ){
        for(unsigned int i = 0; i < (*it)->lines.size(); i++ ){
          temp.push_back((*it)->lines[i]);
          MVertex *v0 = (*it)->lines[i]->getVertex(0);
          MVertex *v1 = (*it)->lines[i]->getVertex(1);
          const double length = sqrt((v0->x() - v1->x()) * (v0->x() - v1->x()) +
                                     (v0->y() - v1->y()) * (v0->y() - v1->y()) +
                                     (v0->z() - v1->z()) * (v0->z() - v1->z()));
          tot_length += length;
        }
      }
    
      MVertex *first_v = (*temp.begin())->getVertex(0);
      MVertex *current_v = (*temp.begin())->getVertex(1);
    
      l.push_back(first_v);
      coord.push_back(0.0);
      temp.erase(temp.begin());
    
      while(temp.size()){
        bool found = false;
        for(std::list<MLine*>::iterator itl = temp.begin(); itl != temp.end(); ++itl){
          MLine *ll = *itl;
          MVertex *v0 = ll->getVertex(0);
          MVertex *v1 = ll->getVertex(1);
          if(v0 == current_v){
            found = true;
            l.push_back(current_v);
            current_v = v1;
            temp.erase(itl);
            const double length = sqrt((v0->x() - v1->x()) * (v0->x() - v1->x()) +
                                       (v0->y() - v1->y()) * (v0->y() - v1->y()) +
                                       (v0->z() - v1->z()) * (v0->z() - v1->z()));
            coord.push_back(coord[coord.size()-1] + length / tot_length);
            break;
          }
          else if(v1 == current_v){
            found = true;
            l.push_back(current_v);
            current_v = v0;
            temp.erase(itl);
            const double length = sqrt((v0->x() - v1->x()) * (v0->x() - v1->x()) +
                                       (v0->y() - v1->y()) * (v0->y() - v1->y()) +
                                       (v0->z() - v1->z()) * (v0->z() - v1->z()));
            coord.push_back(coord[coord.size()-1] + length / tot_length);
            break;
          }
        }
        if(!found) return false;
      }
    
      return true;
    }
    
    static bool computeCGKernelPolygon(std::map<MVertex*,SPoint3> &coordinates,
                                       std::vector<MVertex*> &cavV,
                                       double &ucg, double &vcg)
    {
      ucg = 0.0;
      vcg = 0.0;
    
      // place at CG KERNEL polygon
    
      int nbPts = cavV.size();
      fullMatrix<double> u(100,2);
      int ipt = 0;
      for(std::vector<MVertex*>::iterator it = cavV.begin(); it != cavV.end(); it++){
        SPoint3 vsp = coordinates[*it];
        u(ipt,0) = vsp[0];
        u(ipt,1) = vsp[1];
        ucg += u(ipt,0);
        vcg += u(ipt,1);
        ipt++;
      }
      ucg /= ipt;
      vcg /= ipt;
    
      double eps = -5.e-7;
      int N = nbPts;
    
      std::set<int> setP;
      for(int k = 0; k < nbPts; k++) setP.insert(k);
    
      if(nbPts > 3){
        for(int i = 0; i < nbPts - 2; i++){
          int next = i + 1;
          double x1, x2, y1, y2;
          x1 = u(i, 0); y1 = u(i, 1);
          x2 = u(next, 0); y2 = u(next, 1);
          for(int j = i + 2; j < nbPts; j++){
            int jnext = j + 1;
            if(j == nbPts - 1) jnext = 0;
            double x3, x4, y3, y4, x,y;
            double a, b, c, d;
            x3 = u(j, 0); y3 = u(j, 1);
            x4 = u(jnext, 0); y4 = u(jnext, 1);
            a = (y2 - y1) / (x2 - x1);
            c = (y4 - y3) / (x4 - x3);
            b = y1 - a * x1;
            d = y3 - c * x3;
            if(fabs(a - c) > 1.e-5 && x2 !=x1 && x4 != x3 && jnext != i){
              x = (d - b) / (a - c);
              y = a * x + b;
              bool exist= false;
              for(unsigned int k = 1; k < setP.size(); k++){
                if(x == u(k, 0) && y == u(k, 1)) exist = true;
              }
              if(!exist){
                u(N, 0) = x; u(N, 1) = y;
                setP.insert(N);
                N++;
              }
            }
          }
        }
    
        int nbAll = setP.size();
        for(int i = 0; i <nbPts; i++){
          int next = i + 1;
          if(next == nbPts) next = 0;
          double p1[2] = {u(next, 0) - u(i, 0), u(next, 1) - u(i, 1)};
          for(int k = 0; k < nbAll; k++){
            double p2[2] = {u(k, 0) - u(i, 0), u(k, 1) - u(i, 1)};
            double crossProd = p1[0] * p2[1] - p2[0] * p1[1];
            if(crossProd <  eps){
              setP.erase(k);
            }
          }
        }
      }
    
      int nbFinal = setP.size();
      if(nbFinal > 0){
        ucg = 0.0;
        vcg = 0.0;
        for(std::set<int>::iterator it =setP.begin(); it != setP.end(); it++){
          ucg += u(*it,0);
          vcg += u(*it,1);
        }
        ucg /= nbFinal;
        vcg /= nbFinal;
        return true;
      }
      else{
        return false;
      }
    }
    
    static void myPolygon(std::vector<MElement*> &vTri, std::vector<MVertex*> &vPoly)
    {
      std::vector<MEdge> ePoly;
      for(unsigned int i = 0; i  < vTri.size() ; i++) {
        MTriangle *t = (MTriangle*) vTri[i];
        for(int iEdge = 0; iEdge < 3; iEdge++) {
          MEdge tmp_edge =  t->getEdge(iEdge);
          if(std::find(ePoly.begin(), ePoly.end(), tmp_edge) == ePoly.end())
            ePoly.push_back(tmp_edge);
          else
            ePoly.erase(std::find(ePoly.begin(), ePoly.end(),tmp_edge));
        }
      }
    
      std::vector<MEdge>::iterator ite= ePoly.begin() ;
      MVertex *vINIT = ite->getVertex(0);
      vPoly.push_back(vINIT);
      vPoly.push_back(ite->getVertex(1));
      ePoly.erase(ite);
    
      while(!ePoly.empty()){
        ite = ePoly.begin() ;
        while(ite != ePoly.end()){
          MVertex *vB = ite->getVertex(0);
          MVertex *vE = ite->getVertex(1);
          if(vB == vPoly.back()){
    	if(vE != vINIT) vPoly.push_back(vE);
    	ePoly.erase(ite);
          }
          else if(vE == vPoly.back()){
    	if(vB != vINIT) vPoly.push_back(vB);
    	ePoly.erase(ite);
          }
          else ite++;
        }
      }
    }
    
    static double normalUV(MTriangle *t, std::map<MVertex*, SPoint3> &vCoord)
    {
      SPoint3 v0 = vCoord[t->getVertex(0)];
      SPoint3 v1 = vCoord[t->getVertex(1)];
      SPoint3 v2 = vCoord[t->getVertex(2)];
      double p0[2] = {v0[0], v0[1]};
      double p1[2] = {v1[0], v1[1]};
      double p2[2] = {v2[0], v2[1]};
      double normal =  robustPredicates::orient2d(p0, p1, p2);
    
      // SVector3 P0 (v0.x(),v0.y(), 0.0);
      // SVector3 P1 (v1.x(),v1.y(), 0.0);
      // SVector3 P2 (v2.x(),v2.y(), 0.0);
      // double normal2 = crossprod(P1-P0,P2-P1).z();
      // if (normal != 0.0) normal /= std::abs(normal);
    
      return normal;
    }
    
    static bool checkCavity(std::vector<MElement*> &vTri, std::map<MVertex*, SPoint3> &vCoord)
    {
      bool badCavity = false;
    
      unsigned int nbV = vTri.size();
      double a_old = 0.0, a_new;
      for(unsigned int i = 0; i < nbV; ++i){
        a_new = normalUV((MTriangle*) vTri[i], vCoord);
        if(i == 0) a_old=a_new;
        if(a_new*a_old < 0.0) badCavity = true;
      }
      return badCavity;
    }
    
    static bool closedCavity(MVertex *v, std::vector<MElement*> &vTri)
    {
      std::set<MVertex *> vs;
      for (unsigned int i = 0; i < vTri.size(); i++){
        MElement *t = vTri[i];
        for (int j = 0; j < t->getNumVertices(); j++){
          MVertex *vv = t->getVertex(j);
          if (vv != v){
            if (vs.find(vv) == vs.end())vs.insert(vv);
            else vs.erase(vv);
          }
        }
      }
      return vs.empty();
    }
    
    static MVertex* findVertexInTri(v2t_cont &adjv, MVertex *v0, MVertex *v1,
    				std::map<MVertex*, SPoint3> &vCoord, double nTot,
    				bool &inverted)
    {
      MVertex *v2 = 0;
      v2t_cont :: iterator it0 = adjv.find(v0);
      std::vector<MElement*> vTri0 = it0->second;
      MElement *myTri = 0;
      for (unsigned int j = 0; j < vTri0.size(); j++){
        MVertex *vt0  = vTri0[j]->getVertex(0);
        MVertex *vt1  = vTri0[j]->getVertex(1);
        MVertex *vt2  = vTri0[j]->getVertex(2);
        bool found0 = (vt0==v0 || vt1 ==v0 || vt2 ==v0) ? true: false;
        bool found1 = (vt0==v1 || vt1 ==v1 || vt2 ==v1) ? true: false;
        if (found0 && found1) { myTri = vTri0[j]; break; }
      }
      if(!myTri) return 0;
      for (unsigned int j = 0; j < 3; j++){
        MVertex *vj = myTri->getVertex(j);
        if (vj!=v0 && vj!=v1) { v2 = vj; break;}
      }
      double normTri = normalUV((MTriangle*)myTri, vCoord);
      if (nTot*normTri < 0.0) inverted = true;
      else inverted = false;
    
      return v2;
    }
    
    static MTriangle* findInvertedTri(v2t_cont &adjv, MVertex*v0, MVertex*v1, MVertex*v2,
                                      std::map<MVertex*, SPoint3> &vCoord, double nTot)
    {
      v2t_cont::iterator it = adjv.find(v1);
      std::vector<MElement*> vTri = it->second;
      MTriangle *myTri=NULL;
      for (unsigned int j = 0; j < vTri.size(); j++){
        double normTri = normalUV((MTriangle*)vTri[j], vCoord);
        if (nTot * normTri < 0.0) {
          MVertex *vt0  = vTri[j]->getVertex(0);
          MVertex *vt1  = vTri[j]->getVertex(1);
          MVertex *vt2  = vTri[j]->getVertex(2);
          bool found0 = (vt0==v0 || vt1 ==v0 || vt2 ==v0) ? true: false;
          bool found2 = (vt0==v2 || vt1 ==v2 || vt2 ==v2) ? true: false;
          if (!found0 && !found2) { myTri = (MTriangle*)vTri[j]; break; }
        }
      }
    
      return myTri;
    }
    
    void GFaceCompound::orientFillTris(std::list<MTriangle*> loopfillTris) const
    {
      // check normal orientations of loopfillTris
      bool invertTris = false;
      std::map<MEdge, std::set<MTriangle*>, Less_Edge > edge2tris;
      for(std::list<MTriangle*>::iterator t = loopfillTris.begin();
          t != loopfillTris.end(); t++){
        for (int j = 0; j < 3; j++){
          MEdge me = (*t)->getEdge(j);
          std::map<MEdge, std::set<MTriangle*, std::less<MTriangle*> >,
    	       Less_Edge >::iterator it = edge2tris.find(me);
          if (it == edge2tris.end()) {
    	std::set<MTriangle*, std::less<MTriangle*> > mySet;
    	mySet.insert(*t);
    	edge2tris.insert(std::make_pair(me, mySet));
          }
          else{
    	std::set<MTriangle*, std::less<MTriangle*> > mySet = it->second;
    	mySet.insert(*t);
    	it->second = mySet;
          }
        }
      }
    
      int iE, si, iE2, si2;
      std::list<GFace*>::const_iterator itf = _compound.begin();
      for( ; itf != _compound.end(); ++itf){
        for(unsigned int i = 0; i < (*itf)->triangles.size(); ++i){
          MTriangle *t = (*itf)->triangles[i];
          for (int j = 0; j < 3; j++){
    	MEdge me = t->getEdge(j);
    	std::map<MEdge, std::set<MTriangle*>, Less_Edge >::iterator it =
    	  edge2tris.find(me);
    	if(it != edge2tris.end()){
    	  t->getEdgeInfo(me, iE,si);
    	  MTriangle* t2 = *((it->second).begin());
    	  t2->getEdgeInfo(me,iE2,si2);
    	  if(si == si2) {
    	    invertTris = true;
    	    break;
    	  }
    	}
          }
        }
      }
      if (invertTris){
        for (std::list<MTriangle*>::iterator it = loopfillTris.begin();
               it != loopfillTris.end(); it++ )
    	(*it)->reverse();
      }
    
      fillTris.insert(fillTris.begin(),loopfillTris.begin(),loopfillTris.end());
    }
    
    void GFaceCompound::printFillTris() const
    {
      if(!CTX::instance()->mesh.saveAll) return;
    
      if (fillTris.size() > 0){
        char name[256];
        //std::list<GFace*>::const_iterator itf = _compound.begin();
        sprintf(name, "fillTris-%d.pos", tag());
        FILE * ftri = fopen(name,"w");
        fprintf(ftri,"View \"\"{\n");
        for (std::list<MTriangle*>::iterator it2 = fillTris.begin();
    	 it2 !=fillTris.end(); it2++ ){
          MTriangle *t = (*it2);
          fprintf(ftri,"ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g};\n",
    	      t->getVertex(0)->x(), t->getVertex(0)->y(), t->getVertex(0)->z(),
    	      t->getVertex(1)->x(), t->getVertex(1)->y(), t->getVertex(1)->z(),
    	      t->getVertex(2)->x(), t->getVertex(2)->y(), t->getVertex(2)->z(),
    	      1., 1., 1.);
        }
        fprintf(ftri,"};\n");
        fclose(ftri);
      }
    }
    
    void GFaceCompound::fillNeumannBCS_Plane() const
    {
    #if defined(HAVE_MESH)
    
      Msg::Debug("Meshing %d interior holes with planes ", _interior_loops.size()-1);
    
      fillTris.clear();
      fillNodes.clear();
    
      if (_interior_loops.size()==1 && _type != SQUARE) return;
    
      GModel::current()->setFactory("Gmsh");
      //GModel::current()->setFactory("OpenCASCADE");
      std::vector<std::vector<GFace *> > myFaceLoops;
      std::vector<GFace *> myFaces;
      for(std::list<std::list<GEdge*> >::const_iterator iloop = _interior_loops.begin();
          iloop != _interior_loops.end(); iloop++){
        std::list<MTriangle*> loopfillTris;
        std::list<GEdge*> loop = *iloop;
        if (loop != _U0 ){
          std::vector<std::vector<GEdge *> > myEdgeLoops;
          std::vector<GEdge*> myEdges;
          myEdgeLoops.clear();
          myEdges.clear();
          for (std::list<GEdge*>::iterator itl = loop.begin(); itl != loop.end(); itl++){
    	myEdges.push_back(*itl);
          }
          myEdgeLoops.push_back(myEdges);
          GFace *newFace =  GModel::current()->addPlanarFace(myEdgeLoops);
          fillFaces.push_back(newFace);
          int meshingAlgo = CTX::instance()->mesh.algo2d;
          int recombine = CTX::instance()->mesh.recombineAll;
          opt_mesh_algo2d(0, GMSH_SET, 1.0); //mesh adapt
          opt_mesh_recombine_all(0, GMSH_SET, 0.0); //no recombination
          meshGFace mgf;
          mgf(newFace,false);
          opt_mesh_algo2d(0, GMSH_SET, meshingAlgo);
          opt_mesh_recombine_all(0, GMSH_SET, recombine);
          for(unsigned int i = 0; i < newFace->triangles.size(); ++i){
    	loopfillTris.push_back(newFace->triangles[i]);
    	fillNodes.insert(newFace->triangles[i]->getVertex(0));
    	fillNodes.insert(newFace->triangles[i]->getVertex(1));
    	fillNodes.insert(newFace->triangles[i]->getVertex(2));
          }
          orientFillTris(loopfillTris);
        }
      }
    
      printFillTris();
    #endif
    }
    
    void GFaceCompound::fillNeumannBCS() const
    {
      fillTris.clear();
      fillNodes.clear();
    
      // closed interior loops
      for(std::list<std::list<GEdge*> >::const_iterator iloop = _interior_loops.begin();
          iloop != _interior_loops.end(); iloop++){
        std::list<MTriangle*> loopfillTris;
        std::list<GEdge*> loop = *iloop;
        if (loop != _U0 ){
          std::vector<MVertex*> orderedLoop;
          std::vector<double> coordsLoop;
          orderVertices(*iloop, orderedLoop, coordsLoop);
          int nbLoop = orderedLoop.size();
    
          //--- center of Neumann interior loop
          int nb = 0;
          double x=0.;
          double y=0.;
          double z=0.;
          // EMI- TODO FIND KERNEL OF POLYGON AND PLACE AT CG KERNEL !
          // IF NO KERNEL -> DO NOT FILL TRIS
          for(int i = 0; i < nbLoop; ++i){
    	MVertex *v0 = orderedLoop[i];
    	MVertex *v1 = (i==nbLoop-1) ? orderedLoop[0]: orderedLoop[i+1];
    	x += .5*(v0->x() + v1->x());
    	y += .5*(v0->y() + v1->y());
    	z += .5*(v0->z() + v1->z());
    	nb++;
          }
          x/=nb; y/=nb;  z/=nb;
          MVertex *c = new MVertex(x, y, z);
    
          //--- create new triangles
          for(int i = 0; i < nbLoop; ++i){
    	MVertex *v0 = orderedLoop[i];
    	MVertex *v1 = (i==nbLoop-1) ? orderedLoop[0]: orderedLoop[i+1];
    
    	double k = 1. / 3.; double kk = 2. / 3.;
    	MVertex *v2 = new MVertex(kk * v0->x() + k * c->x(),
                                      kk * v0->y() + k * c->y(),
                                      kk * v0->z() + k * c->z());
    	MVertex *v3 = new MVertex(kk * v1->x() + k * c->x(),
                                      kk * v1->y() + k * c->y(),
                                      kk * v1->z() + k * c->z());
    	MVertex *v4 = new MVertex(k * v0->x() + kk * c->x(),
                                      k * v0->y() + kk * c->y(),
                                      k * v0->z() + kk * c->z());
    	MVertex *v5 = new MVertex(k * v1->x() + kk * c->x(),
                                      k * v1->y() + kk * c->y(),
                                      k * v1->z() + kk * c->z());
    	fillNodes.insert(c); fillNodes.insert(v2); fillNodes.insert(v3);
    	fillNodes.insert(v4); fillNodes.insert(v5);
    	loopfillTris.push_back(new MTriangle(v0, v2, v3));
    	loopfillTris.push_back(new MTriangle(v2, v5, v3));
    	loopfillTris.push_back(new MTriangle(v2, v4, v5));
    	loopfillTris.push_back(new MTriangle(v4, c, v5));
    	loopfillTris.push_back(new MTriangle(v0, v3, v1));
          }
        }
        orientFillTris(loopfillTris);
      }
    
      printFillTris();
    }
    
    bool GFaceCompound::trivial() const
    {
      return false;
      if(_compound.size() == 1 &&
         (*(_compound.begin()))->getNativeType() == GEntity::OpenCascadeModel &&
         (*(_compound.begin()))->geomType() != GEntity::DiscreteSurface &&
         _mapping != CONFORMAL){
        if ((*(_compound.begin()))->periodic(0) ||
    	(*(_compound.begin()))->periodic(1) ) return false;
        return true;
      }
      return false;
    }
    
    // For the conformal map the linear system cannot guarantee there is
    // no overlapping of triangles
    bool GFaceCompound::checkOverlap(std::vector<MVertex *> &vert) const
    {
    
      vert.clear();
      bool has_overlap = false;
      double EPS = 1.e-2;
      for(std::list<std::list<GEdge*> >::const_iterator iloop = _interior_loops.begin();
          iloop != _interior_loops.end(); iloop++){
        std::list<GEdge*> loop = *iloop;
        std::vector<MVertex*> orderedLoop;
        if (loop != _U0 ){
          std::vector<double> coordsLoop;
          orderVertices(*iloop, orderedLoop, coordsLoop);
        }
        else orderedLoop = _ordered;
        int nbLoop = orderedLoop.size();
    
        for(int i = 0; i < nbLoop-1; ++i){
          SPoint3 p1 = coordinates[orderedLoop[i]];
          SPoint3 p2 = coordinates[orderedLoop[i+1]];
          int maxSize = (i==0) ? nbLoop-2: nbLoop-1;
          for(int k = i+2; k < maxSize; ++k){
    	SPoint3 q1 = coordinates[orderedLoop[k]];
    	SPoint3 q2 = coordinates[orderedLoop[k+1]];
    	double x[2];
    	int inters = intersection_segments (p1,p2,q1,q2,x);
    	if (inters && x[1] > EPS && x[1] < 1.-EPS){
    	  has_overlap = true;
    	  MVertex *v1 = orderedLoop[i];
    	  MVertex *v2 = orderedLoop[k];
    	  //std::set<MVertex *>::iterator it1 = ov.find(v1);
    	  //std::set<MVertex *>::iterator it2 = ov.find(v2);
    	  vert.push_back(v1);
    	  vert.push_back(v2);
    	  Msg::Info("=== Overlap");
    	  return has_overlap;
    	}
          }
        }
    
      }
    
      return has_overlap;
    }
    
    // check if the discrete harmonic map is correct by checking that all
    // the mapped triangles have the same normal orientation
    bool GFaceCompound::checkOrientation(int iter, bool moveBoundaries) const
    {
      std::list<GFace*>::const_iterator it = _compound.begin();
      double a_old = 0.0, a_new=0.0;
      bool oriented = true;
      int count = 0;
      double nTot = 0.0;
      for( ; it != _compound.end(); ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          a_new = normalUV((*it)->triangles[i], coordinates);
          if(count == 0) a_old=a_new;
          nTot += a_new;
          if(a_new*a_old < 0.0){
            oriented = false;
            break;
          }
          count++;
        }
      }
    
      int iterMax = 15;
      if(!oriented && iter < iterMax){
        if (moveBoundaries){
          if (iter ==0) Msg::Info("--- Flipping : moving boundaries.");
          Msg::Debug("--- Moving Boundary - iter %d -",iter);
          convexBoundary(nTot/fabs(nTot));
          printStuff(iter);
          return checkOrientation(iter+1, moveBoundaries);
        }
        else if (!moveBoundaries){
          if (iter ==0) Msg::Info("--- Flipping : applying cavity checks.");
          Msg::Debug("--- Cavity Check - iter %d -",iter);
          oriented = one2OneMap();
          printStuff(iter);
          iter++;
          if (!oriented) return checkOrientation(iter);
        }
      }
    
      if (iter > 0 && iter < iterMax){
        Msg::Info("--- Flipping : no more flips (%d iter)", iter);
      }
    
      return oriented;
    }
    
    void GFaceCompound::convexBoundary(double nTot) const
    {
    #if defined(HAVE_MESH)
      if(adjv.size() == 0){
        std::vector<MTriangle*> allTri;
        std::list<GFace*>::const_iterator it = _compound.begin();
        for( ; it != _compound.end(); ++it){
          allTri.insert(allTri.end(), (*it)->triangles.begin(), (*it)->triangles.end() );
        }
        buildVertexToTriangle(allTri, adjv);
      }
    
      for(std::list<std::list<GEdge*> >::const_iterator it = _interior_loops.begin();
          it != _interior_loops.end(); it++){
    
        double s = 1.0;
        std::vector<MVertex*> oVert;
        std::vector<double> coords;
        if (*it == _U0){
          oVert = _ordered;
        }
        else {
          s = -1.0;
          orderVertices(*it, oVert,coords);
        }
    
        // find normal of ordered loop
        MVertex *v0 = oVert[0];
        MVertex *v1 = oVert[1];
        bool inverted;
        MVertex *v2 = findVertexInTri(adjv, v0,v1,coordinates, nTot, inverted);
        if (inverted) s *= -1.0 ;
        SPoint3 uv0 = coordinates[v0];
        SPoint3 uv1 = coordinates[v1];
        SPoint3 uv2 = coordinates[v2];
        SVector3 P0 (uv0.x(),uv0.y(), 0.0);
        SVector3 P1 (uv1.x(),uv1.y(), 0.0);
        SVector3 P2 (uv2.x(),uv2.y(), 0.0);
        double myN = s*crossprod(P1-P0,P2-P1).z();
        SVector3 N (0,0,myN/fabs(myN));
    
        // start the loop
        int nbInv = 0;
        int nbInvTri = 0;
        for(unsigned int i = 0; i < oVert.size(); i++){
    
          MVertex *v0 = oVert[i];
          MVertex *v1, *v2;
          if (i == oVert.size()-2){
    	v1 = oVert[i+1];
    	v2 = oVert[0];
          }
          else if (i == oVert.size()-1){
    	v1= oVert[0];
    	v2= oVert[1];
          }
          else{
    	v1 = oVert[i+1];
    	v2 = oVert[i+2];
          }
    
          SPoint3 uv0 = coordinates[v0];
          SPoint3 uv1 = coordinates[v1];
          SPoint3 uv2 = coordinates[v2];
          SVector3 P0 (uv0.x(),uv0.y(), uv0.z());
          SVector3 P1 (uv1.x(),uv1.y(), uv1.z());
          SVector3 P2 (uv2.x(),uv2.y(), uv2.z());
    
          SVector3 dir1 = P1-P0;
          SVector3 dir2 = P2-P1;
          SVector3 dir3 = crossprod(dir1,dir2);
          double rot = dot(N, (1./norm(dir3))*dir3);
    
          bool inverted;
          MVertex *v2t = findVertexInTri(adjv, v0,v1, coordinates, nTot, inverted);
    
          if (rot < 0.0 && inverted){
    	SVector3 a = P1-P0;
    	SVector3 b = P2-P0;
    	double a_para = dot(a,b)/norm(b);
    	SVector3 P3,P1b;
    	P3= P0 + (a_para/norm(b))*b;
    	P1b = P1 + 1.2*(P3-P1);
    	SPoint3 uv1_new(P1b.x(),P1b.y(),P1b.z());
    	coordinates[v1] = uv1_new;
          }
          else if (rot > 0.0 && inverted){
    	nbInv++;
    	SPoint3 uv2t = coordinates[v2t];
    	SVector3 P2t (uv2t.x(),uv2t.y(), uv2t.z());
    	SVector3 a = P1-P0;
    	SVector3 b = P2t-P0;
    	double b_para = dot(a,b)/norm(a);
    	SVector3 P3,P2b;
    	P3= P0 + (b_para/norm(a))*a;
    	P2b = P2t + 1.3*(P3-P2t);
    	SPoint3 uv2_new(P2b.x(),P2b.y(),P2b.z());
    	coordinates[v2t] = uv2_new;
          }
    
          MTriangle *tinv = findInvertedTri(adjv, v0,v1,v2, coordinates, nTot);
          if (tinv){
    	nbInvTri++;
    	MVertex *i0 = NULL;
    	MVertex *i1 = NULL;
    	for (unsigned int j = 0; j < 3; j++){
    	  MVertex *vj = tinv->getVertex(j);
    	  if (vj!=v1 && i0 == NULL) i0 = vj;
    	  else if (vj!=v1 && i1 ==NULL ) i1 = vj;
    	}
    	MVertex *i2 = v1;
    	SPoint3 uv0 = coordinates[i0];
    	SPoint3 uv1 = coordinates[i1];
    	SPoint3 uv2 = coordinates[i2];
    	SVector3 P0 (uv0.x(),uv0.y(), uv0.z());
    	SVector3 P1 (uv1.x(),uv1.y(), uv1.z());
    	SVector3 P2 (uv2.x(),uv2.y(), uv2.z());
    	SVector3 a = P1-P0;
    	SVector3 b = P2-P0;
    	double b_para = dot(a,b)/norm(a);
    	SVector3 P3,P2b;
    	P3= P0 + (b_para/norm(a))*a;
    	P2b = P2 + 1.2*(P3-P2);
    	SPoint3 uv2_new(P2b.x(),P2b.y(),P2b.z());
    	coordinates[i2] = uv2_new;
          }
    
        }
      }
    #endif
    }
    
    bool GFaceCompound::one2OneMap() const
    {
    #if defined(HAVE_MESH)
      if(adjv.size() == 0){
        std::vector<MTriangle*> allTri;
        std::list<GFace*>::const_iterator it = _compound.begin();
        for( ; it != _compound.end(); ++it){
          allTri.insert(allTri.end(), (*it)->triangles.begin(), (*it)->triangles.end() );
        }
        buildVertexToTriangle(allTri, adjv);
      }
    
      int nbRepair = 0;
      int nbCav = 0;
      for(v2t_cont::iterator it = adjv.begin(); it!= adjv.end(); ++it){
        MVertex *v = it->first;
        SPoint2 p = getCoordinates(v);
        std::vector<MElement*> vTri = it->second;
        std::map<MVertex*,SPoint3> vCoord;
        for (unsigned int j = 0; j < vTri.size(); j++){
          for (int k = 0; k < vTri[j]->getNumVertices(); k++){
            MVertex *vk = vTri[j]->getVertex(k);
            SPoint2 pt2 =  getCoordinates(vk);
    	vCoord[vk] = SPoint3(pt2[0], pt2[1], 0.0);
          }
        }
        bool badCavity = checkCavity(vTri, vCoord);
        bool innerCavity = closedCavity(v,vTri);
    
        if( badCavity && innerCavity ){
          nbCav++;
          double u_cg, v_cg;
          std::vector<MVertex*> cavV;
          myPolygon(vTri, cavV);
          //bool success =
             computeCGKernelPolygon(coordinates, cavV, u_cg, v_cg);
          //if (success){ //if not succes compute with CG polygon
    	nbRepair++;
    	SPoint3 p_cg(u_cg,v_cg,0.0);
    	coordinates[v] = p_cg;
          //}
        }
      }
      if (nbRepair == 0) return false;
      else return true;
    #endif
    }
    
    bool GFaceCompound::parametrize() const
    {
      if (_compound.size() > 1) coherencePatches();
    
      bool paramOK = true;
      if(oct) return paramOK;
      if(trivial()) return paramOK;
    
      if (_mapping != RBF)
        coordinates.clear();
    
      computeNormals();
    
      if(allNodes.empty()) buildAllNodes();
    
      if (_type != SQUARE){
        bool success = orderVertices(_U0, _ordered, _coords);
        if(!success) {
          Msg::Error("Could not order vertices on boundary");
          return false;
        }
      }
    
      fillNeumannBCS_Plane();
      //fillNeumannBCS();
    
      // Convex parametrization
      if (_mapping == CONVEX){
        Msg::Info("Parametrizing surface %d with 'convex map'", tag());
        parametrize(ITERU,CONVEX);
        parametrize(ITERV,CONVEX);
        if (_type==MEANPLANE){
          checkOrientation(0, true);
        }
      }
      // Laplace parametrization
      else if (_mapping == HARMONIC){
        Msg::Info("Parametrizing surface %d with 'harmonic map'", tag());
        parametrize(ITERU,HARMONIC);
        parametrize(ITERV,HARMONIC);
        if (_type == MEANPLANE) checkOrientation(0, true);
      }
      // Conformal map parametrization
      else if (_mapping == CONFORMAL){
        std::vector<MVertex *> vert;
        bool oriented, overlap;
        if (_type == SPECTRAL){
          Msg::Info("Parametrizing surface %d with 'spectral conformal map'", tag());
          overlap = parametrize_conformal_spectral();
        }
        else {
          Msg::Info("Parametrizing surface %d with 'FE conformal map'", tag());
          overlap = parametrize_conformal(0, NULL, NULL);
        }
        //printStuff(55);
        oriented = checkOrientation(0);
        //printStuff(77);
        if (_type==SPECTRAL &&  (!oriented || overlap) ){
          Msg::Warning("!!! parametrization switched to 'FE conformal' map");
          overlap = parametrize_conformal(0, NULL, NULL);
          oriented = checkOrientation(0);
        }
        if (!oriented || overlap){
          Msg::Warning("$$$ parametrization switched to 'convex' map");
          _type  = UNITCIRCLE;
          parametrize(ITERU,CONVEX);
          parametrize(ITERV,CONVEX);
        }
      }
      // Radial-Basis Function parametrization
      else if (_mapping == RBF){
        Msg::Debug("Parametrizing surface %d with 'RBF' ", tag());
        int variableEps = 0;
        int radFunInd = 1; // 1 MQ RBF , 0 GA
        double sizeBox = getSizeH();
        /*
        fullMatrix<double> Oper(3*allNodes.size(),3*allNodes.size());
        _rbf = new GRbf(sizeBox, variableEps, radFunInd, _normals, allNodes, _ordered);
        _rbf->RbfLapSurface_local_CPM(false,_rbf->getXYZ(), _rbf->getN(), Oper);
        _rbf->solveHarmonicMap(Oper, _ordered, _coords, coordinates);
        */
        fullMatrix<double> Oper(3*allNodes.size(),3*allNodes.size());
    
        _rbf = new GRbf(sizeBox, variableEps, radFunInd, _normals, allNodes, _ordered);
    
        linearSystemPETSc<double> sys;
    	  printf("system 0 = %p\n", &sys);
        #if 1
        _rbf->RbfLapSurface_local_CPM_sparse(_ordered, false, _rbf->getXYZ(), _rbf->getN(), sys);
        _rbf->solveHarmonicMap_sparse(sys, _rbf->getXYZ().size1()* 3,_ordered, _coords, coordinates);
        #else
        _rbf->RbfLapSurface_local_CPM(false, _rbf->getXYZ(), _rbf->getN(), Oper);
        _rbf->solveHarmonicMap(Oper,_ordered, _coords, coordinates);
        #endif
      }
    
      buildOct();
    
      if (_mapping != RBF){
        if (!checkOrientation(0)){
          printStuff(22);
          Msg::Info("### parametrization switched to 'convex map' onto circle");
          printStuff(33);
          _type = UNITCIRCLE;
          coordinates.clear();
          Octree_Delete(oct);
          parametrize(ITERU,CONVEX);
          parametrize(ITERV,CONVEX);
          checkOrientation(0);
          buildOct();
        }
      }
    
      for (unsigned int i= 0; i< fillFaces.size(); i++){
        GModel::current()->remove(fillFaces[i]);
      }
    
      return paramOK;
    }
    
    void GFaceCompound::getBoundingEdges()
    {
      for(std::list<GFace*>::iterator it = _compound.begin(); it != _compound.end(); ++it){
        (*it)->setCompound(this);
      }
    
      std::set<GEdge*> _unique;
      getUniqueEdges(_unique);
    
      l_edges.clear();
    
      if(_U0.size()){
        // in case the bounding edges are explicitely given
        std::list<GEdge*>::const_iterator it = _U0.begin();
        for( ; it != _U0.end() ; ++it){
          l_edges.push_back(*it);
          (*it)->addFace(this);
        }
        if (_V0.size() && _U1.size() && _V1.size()){
          it = _V0.begin();
          for( ; it != _V0.end() ; ++it){
    	l_edges.push_back(*it);
    	(*it)->addFace(this);
          }
          it = _U1.begin();
          for( ; it != _U1.end() ; ++it){
    	l_edges.push_back(*it);
    	(*it)->addFace(this);
          }
          it = _V1.begin();
          for( ; it != _V1.end() ; ++it){
    	l_edges.push_back(*it);
    	(*it)->addFace(this);
          }
        }
        std::list<GEdge*> loop;
        computeALoop(_unique, loop);
        while(!_unique.empty())  computeALoop(_unique,loop);
      }
      else{
        // in case the bounding edges are NOT explicitely given
        std::set<GEdge*>::iterator itf = _unique.begin();
        for( ; itf != _unique.end(); ++itf){
          l_edges.push_back(*itf);
          (*itf)->addFace(this);
        }
    
        std::list<GEdge*> loop;
        computeALoop(_unique,loop);
        while(!_unique.empty())  computeALoop(_unique, loop);
    
        // assign Derichlet BC (_U0) to boundary with largest size
        double maxSize = 0.0;
        for(std::list<std::list<GEdge*> >::iterator it = _interior_loops.begin();
            it != _interior_loops.end(); it++){
          double size = getSizeBB(*it);
          if (size > maxSize) {
    	_U0 = *it;
    	maxSize = size;
          }
        }
      }
    }
    
    double GFaceCompound::getSizeH() const
    {
      SBoundingBox3d bb;
      for(std::set<MVertex *>::iterator itv = allNodes.begin();
          itv != allNodes.end() ; ++itv){
        SPoint3 pt((*itv)->x(),(*itv)->y(), (*itv)->z());
        bb +=pt;
      }
    
      double H = norm(SVector3(bb.max(), bb.min()));
    
      return H;
    }
    
    double GFaceCompound::getSizeBB(const std::list<GEdge* > &elist) const
    {
      SBoundingBox3d bboxD;
      std::list<GEdge*>::const_iterator it = elist.begin();
      for(; it != elist.end(); it++){
        bboxD += (*it)->bounds();
      }
      double D = norm(SVector3(bboxD.max(), bboxD.min()));
    
      return D;
    }
    
    void GFaceCompound::getUniqueEdges(std::set<GEdge*> &_unique)
    {
      _unique.clear();
    
      // in case the bounding edges are not given Boundary { {} };
      std::multiset<GEdge*> _touched;
      std::list<GFace*>::iterator it = _compound.begin();
      for( ; it != _compound.end(); ++it){
        std::list<GEdge*> ed = (*it)->edges();
        std::list<GEdge*>::iterator ite = ed.begin();
        for( ; ite != ed.end(); ++ite){
          _touched.insert(*ite);
        }
      }
      it = _compound.begin();
      for( ; it != _compound.end(); ++it){
        std::list<GEdge*> ed = (*it)->edges();
        std::list<GEdge*>::iterator ite = ed.begin();
        for( ; ite != ed.end() ; ++ite){
          if(!(*ite)->degenerate(0) && _touched.count(*ite) == 1){
            _unique.insert(*ite);
          }
        }
      }
    }
    
    void GFaceCompound::computeALoop(std::set<GEdge*> &_unique, std::list<GEdge*> &loop)
    {
      std::list<GEdge*> _loop;
    
      if (_unique.empty()) return;
    
      while(!_unique.empty()) {
        std::set<GEdge*>::iterator it = _unique.begin();
        GVertex *vB = (*it)->getBeginVertex();
        GVertex *vE = (*it)->getEndVertex();
        _loop.push_back(*it);
        _unique.erase(it);
    
        bool found = false;
        for(int i = 0; i < 2; i++) {
          for(std::set<GEdge*>::iterator itx = _unique.begin();
              itx != _unique.end(); ++itx){
            GVertex *v1 = (*itx)->getBeginVertex();
            GVertex *v2 = (*itx)->getEndVertex();
    
            std::set<GEdge*>::iterator itp;
            if(v1 == vE){
              _loop.push_back(*itx);
              itp = itx;
              itx++;
              _unique.erase(itp);
              vE = v2;
              i = -1;
            }
            else if(v2 == vE){
              _loop.push_back(*itx);
              itp = itx;
              itx++;
              _unique.erase(itp);
              vE = v1;
              i=-1;
            }
            if(itx == _unique.end()) break;
          }
    
          if(vB == vE) {
            found = true;
            break;
          }
    
          if(_unique.empty())  break;
    
          GVertex *temp = vB;
          vB = vE;
          vE = temp;
        }
    
        if(found == true) break;
    
        it++;
      }
    
      loop = _loop;
      _interior_loops.push_back(loop);
    }
    
    GFaceCompound::GFaceCompound(GModel *m, int tag, std::list<GFace*> &compound,
    			     std::list<GEdge*> &U0, typeOfCompound toc,
                                 int allowPartition,
    			     linearSystem<double>* lsys)
      : GFace(m, tag), _compound(compound), _U0(U0), oct(0), octNew(0),
        _lsys(lsys), _toc(toc), _allowPartition(allowPartition)
    {
      ONE = new simpleFunction<double>(1.0);
      MONE = new simpleFunction<double>(-1.0);
    
      for(std::list<GFace*>::iterator it = _compound.begin(); it != _compound.end(); ++it){
        if(!(*it)){
          Msg::Error("Incorrect face in compound surface %d\n", tag);
          return;
        }
      }
    
      getBoundingEdges();
    
      _mapping = HARMONIC;
      _type = UNITCIRCLE;
      if(toc == RADIAL_BASIS)   _mapping = RBF;
      else if (toc == HARMONIC_PLANE) _type = MEANPLANE;
      else if (toc == CONVEX_CIRCLE)  _mapping = CONVEX;
      else if (toc == CONVEX_PLANE){
        _mapping = CONVEX;
        _type = MEANPLANE;
      }
      else if(toc == CONFORMAL_SPECTRAL) {
        _mapping = CONFORMAL;
        _type = SPECTRAL;
      }
      else if(toc == CONFORMAL_FE) {
        _mapping = CONFORMAL;
        _type = FE;
      }
    
      nbSplit = 0;
      fillTris.clear();
    
    #if defined(HAVE_ANN)
      kdtree = NULL;
      uv_kdtree = NULL;
      uv_nodes = NULL;
      nodes = NULL;
      index = new ANNidx[2];
      dist  = new ANNdist[2];
    #endif
    }
    
    GFaceCompound::GFaceCompound(GModel *m, int tag, std::list<GFace*> &compound,
    			     std::list<GEdge*> &U0, std::list<GEdge*> &V0,
    			     std::list<GEdge*> &U1, std::list<GEdge*> &V1,
    			     typeOfCompound toc,
    			     int allowPartition,
    			     linearSystem<double>* lsys)
      : GFace(m, tag), _compound(compound), _U0(U0), _V0(V0), _U1(U1), _V1(V1),
        oct(0), octNew(0), _lsys(lsys), _toc(toc), _allowPartition(allowPartition)
    {
      ONE = new simpleFunction<double>(1.0);
      MONE = new simpleFunction<double>(-1.0);
    
      for(std::list<GFace*>::iterator it = _compound.begin(); it != _compound.end(); ++it){
        if(!(*it)){
          Msg::Error("Incorrect face in compound surface %d\n", tag);
          return;
        }
      }
    
      getBoundingEdges();
    
      _mapping = HARMONIC;
      _type = UNITCIRCLE;
      if(toc == RADIAL_BASIS)   _mapping = RBF;
      else if (toc == HARMONIC_PLANE) _type = MEANPLANE;
      else if (toc == CONVEX_CIRCLE)  _mapping = CONVEX;
      else if (toc == CONVEX_PLANE){
        _mapping = CONVEX;
        _type = MEANPLANE;
      }
      if(toc == CONFORMAL_SPECTRAL) {
        _mapping = CONFORMAL;
        _type = SPECTRAL;
      }
      else if(toc == CONFORMAL_FE) {
        _mapping = CONFORMAL;
        _type = FE;
      }
      else if(toc == HARMONIC_SQUARE && _U0.size() && _V0.size() && _U1.size() && _V1.size()) {
        _type = SQUARE;
      }
    
      nbSplit = 0;
      fillTris.clear();
    
    #if defined(HAVE_ANN)
      uv_kdtree = NULL;
      kdtree = NULL;
      uv_nodes = NULL;
      nodes = NULL;
      index = new ANNidx[2];
      dist  = new ANNdist[2];
    #endif
    }
    
    GFaceCompound::~GFaceCompound()
    {
      for (unsigned int i = 0; i < myParamVert.size(); i++) delete myParamVert[i];
      for (unsigned int i = 0; i < myParamElems.size(); i++) delete myParamElems[i];
    
      if(oct){
        Octree_Delete(oct);
        delete [] _gfct;
      }
      if(octNew) delete(octNew);
      if (_lsys)delete _lsys;
      delete ONE;
      delete MONE;
    #if defined(HAVE_ANN)
      if(uv_kdtree) delete uv_kdtree;
      if(kdtree) delete kdtree;
      if(uv_nodes) annDeallocPts(uv_nodes);
      if(nodes) annDeallocPts(nodes);
      delete[]index;
      delete[]dist;
    #endif
    
    }
    
    SPoint2 GFaceCompound::getCoordinates(MVertex *v) const
    {
      if(trivial()){
        SPoint2 param;
        reparamMeshVertexOnFace(v, (*(_compound.begin())),param);
        return param;
      }
    
      std::map<MVertex*,SPoint3>::iterator it = coordinates.find(v);
    
      if(it != coordinates.end()){
        return SPoint2(it->second.x(),it->second.y());
      }
      else{
    
        //if(v->onWhat()->dim() == 1){
          double tGlob, tLoc;
          double tL, tR;
          int iEdge;
    
          // getParameter Point
          v->getParameter(0, tGlob);
    
          //find compound Edge
          GEdgeCompound *gec = dynamic_cast<GEdgeCompound*>(v->onWhat());
    
          if(gec){
    	// compute local parameter on Edge
    	gec->getLocalParameter(tGlob,iEdge,tLoc);
    	std::vector<GEdge*> gev = gec->getCompounds();
    	GEdge *ge = gev[iEdge];
    
    	// left and right vertex of the Edge
    	MVertex *v0 = ge->getBeginVertex()->mesh_vertices[0];
    	MVertex *v1 = ge->getEndVertex()->mesh_vertices[0];
    	std::map<MVertex*,SPoint3>::iterator itL = coordinates.find(v0);
    	std::map<MVertex*,SPoint3>::iterator itR = coordinates.find(v1);
    
    	// for the Edge, find the left and right vertices of the initial
    	// 1D mesh and interpolate to find (u,v)
    	//MVertex *vL = v0;
    	MVertex *vR = v1;
    	double tB = ge->parBounds(0).low();
    	double tE = ge->parBounds(0).high();
    	int j = 0;
    	tL=tB;
    	bool found = false;
    	while(j < (int)ge->mesh_vertices.size()){
    	  vR = ge->mesh_vertices[j];
              if(vR->getPolynomialOrder() > 1){ j++; continue; }
    	  vR->getParameter(0,tR);
    	  if(!vR->getParameter(0,tR)) {
    	    Msg::Error("Vertex vr %p not an MEdgeVertex", vR);
    	    return SPoint2();
    	  }
    	  if(tLoc > tL && tLoc < tR){
    	    found = true;
    	    itR = coordinates.find(vR);
    	    if(itR == coordinates.end()){
    	      Msg::Error("Vertex %p (%g %g %g) not found", vR, vR->x(), vR->y(), vR->z());
                  return SPoint2(0,0);
    	    }
    	    break;
    	  }
    	  else{
    	    itL = coordinates.find(vR);
    	    //vL = vR;
    	    tL = tR;
    	  }
    	  j++;
    	}
    	if(!found) {
    	  vR = v1;
    	  tR = tE;
    	}
    
    	// linear interpolation between tL and tR
    	double uloc, vloc;
    	uloc = itL->second.x() + (tLoc-tL)/(tR-tL) * (itR->second.x()-itL->second.x());
    	vloc = itL->second.y() + (tLoc-tL)/(tR-tL) * (itR->second.y()-itL->second.y());
    	return SPoint2(uloc,vloc);
          }
    
        // }
        // else if(v->onWhat()->dim() == 2){
        //   Msg::Debug("Get coordinates of Compound Face cannot find vertex");
        // }
    
      }
    
      // never here
      return SPoint2(0, 0);
    }
    
    void GFaceCompound::parametrize(iterationStep step, typeOfMapping tom) const
    {
      linearSystem<double> *lsys = 0;
      if (_lsys) lsys = _lsys;
      else{
        if (tom==HARMONIC){
    #if defined(HAVE_TAUCS)
          lsys = new linearSystemCSRTaucs<double>;
    #elif defined(HAVE_PETSC) && !defined(HAVE_TAUCS)
          lsys = new linearSystemPETSc<double>;
    #elif defined(HAVE_GMM) && !defined(HAVE_TAUCS)
          linearSystemGmm<double> *lsysb = new linearSystemGmm<double>;
          lsysb->setGmres(1);
          lsys = lsysb;
    #else
          lsys = new linearSystemFull<double>;
    #endif
        }
        else if (tom==CONVEX){
    #if defined(HAVE_PETSC)
          lsys = new linearSystemPETSc<double>;
    #elif defined(HAVE_GMM)
          linearSystemGmm<double> *lsysb = new linearSystemGmm<double>;
          lsysb->setGmres(1);
          lsys = lsysb;
    #else
          lsys = new linearSystemFull<double>;
    #endif
        }
      }
    
      dofManager<double> myAssembler(lsys);
    
      if(_type == UNITCIRCLE){
        for(unsigned int i = 0; i < _ordered.size(); i++){
          MVertex *v = _ordered[i];
          const double theta = 2 * M_PI * _coords[i];
          if(step == ITERU) myAssembler.fixVertex(v, 0, 1, cos(theta));
          else if(step == ITERV) myAssembler.fixVertex(v, 0, 1, sin(theta));
        }
      }
      else if(_type == SQUARE){
        if(step == ITERU){
          std::list<GEdge*>::const_iterator it = _U0.begin();
          for( ; it != _U0.end(); ++it){
    	fixEdgeToValue(*it, 0.0, myAssembler);
          }
          it = _U1.begin();
          for( ; it != _U1.end(); ++it){
    	fixEdgeToValue(*it, 1.0, myAssembler);
          }
        }
        else if(step == ITERV){
          std::list<GEdge*>::const_iterator it = _V0.begin();
          for( ; it != _V0.end(); ++it){
    	fixEdgeToValue(*it, 0.0, myAssembler);
          }
          it = _V1.begin();
          for( ; it != _V1.end(); ++it){
    	fixEdgeToValue(*it, 1.0, myAssembler);
          }
        }
      }
      else if (_type == MEANPLANE){
        std::vector<SPoint3> points, pointsProj, pointsUV;
        SPoint3 ptCG(0.0, 0.0, 0.0);
        for(unsigned int i = 0; i < _ordered.size(); i++){
          MVertex *v = _ordered[i];
          points.push_back(SPoint3(v->x(), v->y(), v->z()));
          ptCG[0] += v->x();
          ptCG[1] += v->y();
          ptCG[2] += v->z();
        }
    
        ptCG /= points.size();
        mean_plane meanPlane;
        computeMeanPlaneSimple(points, meanPlane);
        projectPointsToPlane(points, pointsProj, meanPlane);
        transformPointsIntoOrthoBasis(pointsProj, pointsUV, ptCG, meanPlane);
    
        for(unsigned int i = 0; i < pointsUV.size(); i++){
          MVertex *v = _ordered[i];
          if(step == ITERU) myAssembler.fixVertex(v, 0, 1, pointsUV[i][0]);
          else if(step == ITERV) myAssembler.fixVertex(v, 0, 1, pointsUV[i][1]);
        }
      }
      else if (_type == ALREADYFIXED){
        printf("already fixed boundaries \n");
        for(unsigned int i = 0; i < _ordered.size(); i++){
          MVertex *v = _ordered[i];
          SPoint3 uv = coordinates[v];
          if(step == ITERU) myAssembler.fixVertex(v, 0, 1, uv[0]);
          else if(step == ITERV) myAssembler.fixVertex(v, 0, 1, uv[1]);
        }
      }
      else{
        Msg::Error("Unknown type of parametrization");
        return;
      }
    
      std::list<GFace*>::const_iterator it = _compound.begin();
      for( ; it != _compound.end(); ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          MTriangle *t = (*it)->triangles[i];
          myAssembler.numberVertex(t->getVertex(0), 0, 1);
          myAssembler.numberVertex(t->getVertex(1), 0, 1);
          myAssembler.numberVertex(t->getVertex(2), 0, 1);
        }
      }
    
      for (std::list<MTriangle*>::iterator it2 = fillTris.begin(); it2 !=fillTris.end(); it2++ ){
        MTriangle *t = (*it2);
        myAssembler.numberVertex(t->getVertex(0), 0, 1);
        myAssembler.numberVertex(t->getVertex(1), 0, 1);
        myAssembler.numberVertex(t->getVertex(2), 0, 1);
      }
    
      Msg::Debug("Creating term %d dofs numbered %d fixed",
                 myAssembler.sizeOfR(), myAssembler.sizeOfF());
    
      double t1 = Cpu();
    
      femTerm<double> *mapping;
      if (tom == HARMONIC)
        mapping = new laplaceTerm(0, 1, ONE);
      else
        mapping = new convexCombinationTerm(0, 1, ONE);
    
      it = _compound.begin();
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          SElement se((*it)->triangles[i]);
          mapping->addToMatrix(myAssembler, &se);
        }
      }
    
      for (std::list<MTriangle*>::iterator it2 = fillTris.begin(); it2 !=fillTris.end(); it2++ ){
        SElement se((*it2));
        mapping->addToMatrix(myAssembler, &se);
      }
    
      double t2 = Cpu();
      Msg::Debug("Assembly done in %8.3f seconds", t2 - t1);
      lsys->systemSolve();
      Msg::Debug("System solved");
    
      for(std::set<MVertex *>::iterator itv = allNodes.begin(); itv !=allNodes.end() ; ++itv){
        MVertex *v = *itv;
        double value;
        myAssembler.getDofValue(v, 0, 1, value);
        std::map<MVertex*,SPoint3>::iterator itf = coordinates.find(v);
        if(itf == coordinates.end()){
          SPoint3 p(0, 0, 0);
          p[step] = value;
          coordinates[v] = p;
        }
        else{
          itf->second[step]= value;
        }
      }
    
      if (_lsys)
        lsys->clear();
      else
        delete lsys;
    }
    
    bool GFaceCompound::parametrize_conformal_spectral() const
    {
    #if !defined(HAVE_PETSC) && !defined(HAVE_SLEPC)
      Msg::Warning("Slepc not installed: parametrization switched to 'FE conformal' map");
      return parametrize_conformal(0, NULL, NULL);
    #else
    
      linearSystem <double> *lsysA  = new linearSystemPETSc<double>;
      linearSystem <double> *lsysB  = new linearSystemPETSc<double>;
      dofManager<double> myAssembler(lsysA, lsysB);
    
      myAssembler.setCurrentMatrix("A");
      for(std::set<MVertex *>::iterator itv = allNodes.begin(); itv !=allNodes.end() ; ++itv){
        MVertex *v = *itv;
        myAssembler.numberVertex(v, 0, 1);
        myAssembler.numberVertex(v, 0, 2);
      }
    
      for(std::set<MVertex *>::iterator itv = fillNodes.begin(); itv !=fillNodes.end() ; ++itv){
        MVertex *v = *itv;
        myAssembler.numberVertex(v, 0, 1);
        myAssembler.numberVertex(v, 0, 2);
      }
    
      laplaceTerm laplace1(model(), 1, ONE);
      laplaceTerm laplace2(model(), 2, ONE);
      crossConfTerm cross12(model(), 1, 2, ONE);
      crossConfTerm cross21(model(), 2, 1, MONE);
      std::list<GFace*>::const_iterator it = _compound.begin();
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          SElement se((*it)->triangles[i]);
          laplace1.addToMatrix(myAssembler, &se);
          laplace2.addToMatrix(myAssembler, &se);
          cross12.addToMatrix(myAssembler, &se);
          cross21.addToMatrix(myAssembler, &se);
        }
      }
    
      for (std::list<MTriangle*>::iterator it2 = fillTris.begin(); it2 != fillTris.end(); it2++){
        SElement se((*it2));
        laplace1.addToMatrix(myAssembler, &se);
        laplace2.addToMatrix(myAssembler, &se);
        cross12.addToMatrix(myAssembler, &se);
        cross21.addToMatrix(myAssembler, &se);
      }
    
      double epsilon = 1.e-6;
      for(std::set<MVertex *>::iterator itv = allNodes.begin(); itv !=allNodes.end() ; ++itv){
        MVertex *v = *itv;
        if (std::find(_ordered.begin(), _ordered.end(), v) == _ordered.end() ){
          myAssembler.assemble(v, 0, 1, v, 0, 1,  epsilon);
          myAssembler.assemble(v, 0, 2, v, 0, 2,  epsilon);
        }
      }
      for(std::set<MVertex *>::iterator itv = fillNodes.begin(); itv !=fillNodes.end() ; ++itv){
        MVertex *v = *itv;
        if (std::find(_ordered.begin(), _ordered.end(), v) == _ordered.end() ){
          myAssembler.assemble(v, 0, 1, v, 0, 1,  epsilon);
          myAssembler.assemble(v, 0, 2, v, 0, 2,  epsilon);
        }
      }
    
      myAssembler.setCurrentMatrix("B");
    
      // mettre max NC contraintes par bord
      int NB = _ordered.size();
      int NC = std::min(30,NB);
      int jump = (int) NB/NC;
      int nbLoop = (int) NB/jump ;
      for (int i = 0; i< nbLoop; i++){
        MVertex *v1 = _ordered[i*jump];
        myAssembler.assemble(v1, 0, 1, v1, 0, 1,  1.0);
        myAssembler.assemble(v1, 0, 2, v1, 0, 2,  1.0);
        for (int j = 0; j< nbLoop; j++){
          MVertex *v2 = _ordered[j*jump];
          myAssembler.assemble(v1, 0, 1, v2, 0, 1,  -1./NC);
          myAssembler.assemble(v1, 0, 2, v2, 0, 2,  -1./NC);
        }
      }
    
      // FIXME: force non-hermitian. For some reason (roundoff errors?)
      // on some machines and for some meshes slepc complains about bad IP
      // norm otherwise
      eigenSolver eig(&myAssembler, "B" , "A", false);
    
      bool converged = eig.solve(1, "largest");
    
      if(converged) {
        double Linfty = 0.0;
        int k = 0;
        std::vector<std::complex<double> > &ev = eig.getEigenVector(0);
        for(std::set<MVertex *>::iterator itv = allNodes.begin(); itv !=allNodes.end() ; ++itv){
          double paramu = ev[k].real();
          double paramv = ev[k+1].real();
          Linfty=std::max(Linfty, sqrt(paramu*paramu+paramv*paramv));
          k = k+2;
        }
        k = 0;
        for(std::set<MVertex *>::iterator itv = allNodes.begin(); itv !=allNodes.end() ; ++itv){
          MVertex *v = *itv;
          double paramu = ev[k].real()/Linfty;
          double paramv = ev[k+1].real()/Linfty;
          coordinates[v] = SPoint3(paramu,paramv,0.0);
          k = k+2;
        }
        delete lsysA;
        delete lsysB;
      }
      else{
        Msg::Warning("Slepc not converged: parametrization switched to 'FE conformal' map");
        return parametrize_conformal(0,NULL,NULL);
      }
    
      std::vector<MVertex *> vert;
      return checkOverlap(vert);
    #endif
    }
    
    bool GFaceCompound::parametrize_conformal(int iter, MVertex *v1, MVertex *v2) const
    {
      linearSystem<double> *lsys = 0;
    #if defined(HAVE_PETSC) && !defined(HAVE_TAUCS)
      lsys = new linearSystemPETSc<double>;
    #elif defined(HAVE_GMM) && !defined(HAVE_TAUCS)
      linearSystemGmm<double> *lsysb = new linearSystemGmm<double>;
      lsysb->setGmres(1);
      lsys = lsysb;
    #elif defined(HAVE_TAUCS)
      lsys = new linearSystemCSRTaucs<double>;
    #else
      lsys = new linearSystemFull<double>;
    #endif
    
      dofManager<double> myAssembler(lsys);
    
      if(!v1) v1 = _ordered[0];
      if(!v2) v2 = _ordered[(int)ceil((double)_ordered.size()/2.)];
      myAssembler.fixVertex(v1, 0, 1, 1.);
      myAssembler.fixVertex(v1, 0, 2, 0.);
      myAssembler.fixVertex(v2, 0, 1, -1.);
      myAssembler.fixVertex(v2, 0, 2, 0.);
    
      std::list<GFace*>::const_iterator it = _compound.begin();
      for( ; it != _compound.end(); ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          MTriangle *t = (*it)->triangles[i];
          myAssembler.numberVertex(t->getVertex(0), 0, 1);
          myAssembler.numberVertex(t->getVertex(1), 0, 1);
          myAssembler.numberVertex(t->getVertex(2), 0, 1);
          myAssembler.numberVertex(t->getVertex(0), 0, 2);
          myAssembler.numberVertex(t->getVertex(1), 0, 2);
          myAssembler.numberVertex(t->getVertex(2), 0, 2);
        }
      }
      for (std::list<MTriangle*>::iterator it2 = fillTris.begin();
           it2 !=fillTris.end(); it2++){
        MTriangle *t = (*it2);
        myAssembler.numberVertex(t->getVertex(0), 0, 1);
        myAssembler.numberVertex(t->getVertex(1), 0, 1);
        myAssembler.numberVertex(t->getVertex(2), 0, 1);
        myAssembler.numberVertex(t->getVertex(0), 0, 2);
        myAssembler.numberVertex(t->getVertex(1), 0, 2);
        myAssembler.numberVertex(t->getVertex(2), 0, 2);
      }
    
      laplaceTerm laplace1(model(), 1, ONE);
      laplaceTerm laplace2(model(), 2, ONE);
      crossConfTerm cross12(model(), 1, 2, ONE);
      crossConfTerm cross21(model(), 2, 1, MONE);
      it = _compound.begin();
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          SElement se((*it)->triangles[i]);
          laplace1.addToMatrix(myAssembler, &se);
          laplace2.addToMatrix(myAssembler, &se);
          cross12.addToMatrix(myAssembler, &se);
          cross21.addToMatrix(myAssembler, &se);
        }
      }
      for (std::list<MTriangle*>::iterator it2 = fillTris.begin();
           it2 != fillTris.end(); it2++ ){
        SElement se((*it2));
        laplace1.addToMatrix(myAssembler, &se);
        laplace2.addToMatrix(myAssembler, &se);
        cross12.addToMatrix(myAssembler, &se);
        cross21.addToMatrix(myAssembler, &se);
      }
    
      Msg::Debug("Assembly done");
      lsys->systemSolve();
      Msg::Debug("System solved");
    
      for(std::set<MVertex *>::iterator itv = allNodes.begin();
          itv != allNodes.end() ; ++itv){
        MVertex *v = *itv;
        double value1, value2;
        myAssembler.getDofValue(v, 0, 1, value1);
        myAssembler.getDofValue(v, 0, 2, value2);
        coordinates[v] = SPoint3(value1,value2,0.0);
      }
    
      delete lsys;
    
      // check for overlap and compute new mapping with new pinned
      // vertices
      std::vector<MVertex *> vert;
      bool hasOverlap = checkOverlap(vert);
      return hasOverlap;
      // if ( hasOverlap && iter < 3){
      //   Msg::Info("Loop FE conformal iter (%d) v1=%d v2=%d", iter,
      //             vert[0]->getNum(), vert[1]->getNum());
      //   printStuff(100+iter);
      //   return hasOverlap = parametrize_conformal(iter+1, vert[0],vert[1]);
      // }
      // else{
      //   return hasOverlap;
      // }
    }
    
    void GFaceCompound::computeNormals(std::map<MVertex*,SVector3> &normals) const
    {
      computeNormals ();
      normals = _normals;
    }
    
    void GFaceCompound::computeNormals() const
    {
      _normals.clear();
      std::list<GFace*>::const_iterator it = _compound.begin();
      double J[3][3];
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          MTriangle *t = (*it)->triangles[i];
          t->getJacobian(0, 0, 0, J);
          SVector3 n (J[2][0], J[2][1], J[2][2]);
          n.normalize();
          for(int j = 0; j < 3; j++){
            std::map<MVertex*, SVector3>::iterator itn = _normals.find(t->getVertex(j));
            if(itn == _normals.end())_normals[t->getVertex(j)] = n;
            else itn->second += n;
          }
        }
      }
      std::map<MVertex*,SVector3>::iterator itn = _normals.begin();
      for( ; itn != _normals.end() ; ++itn) itn->second.normalize();
    }
    
    double GFaceCompound::curvatureMax(const SPoint2 &param) const
    {
      if(!oct) parametrize();
      if(trivial()) {
        return (*(_compound.begin()))->curvatureMax(param);
      }
    
      double U, V;
      GFaceCompoundTriangle *lt;
      getTriangle(param.x(), param.y(), &lt, U,V);
    
      if(!lt) return 0.0;
    
      if(lt->gf && lt->gf->geomType() != GEntity::DiscreteSurface) {
        SPoint2 pv = lt->gfp1*(1.-U-V) + lt->gfp2*U + lt->gfp3*V;
        return lt->gf->curvatureMax(pv);
      }
      else if (lt->gf->geomType() == GEntity::DiscreteSurface) {
        Curvature& curvature = Curvature::getInstance();
        if( !Curvature::valueAlreadyComputed() ) {
          Msg::Info("Need to compute discrete curvature for isotropic remesh (in GFace)");
          Curvature::typeOfCurvature type = Curvature::RUSIN;
          curvature.computeCurvature(model(), type);
        }
        double c0,c1,c2;
        curvature.triangleNodalValues(lt->tri,c0, c1, c2, 1);
        double cv = (1-U-V)*c0 + U*c1 + V*c2;
        return cv;
      }
    
      return 0.;
    }
    
    double GFaceCompound::curvatures(const SPoint2 &param, SVector3 *dirMax, SVector3 *dirMin,
                                     double *curvMax, double *curvMin) const
    {
      if(!oct) parametrize();
      if(trivial()) {
        return (*(_compound.begin()))->curvatures(param, dirMax,dirMin, curvMax, curvMin);
      }
    
      double U, V;
      GFaceCompoundTriangle *lt;
      getTriangle(param.x(), param.y(), &lt, U,V);
    
      if(!lt) return 0.0;
    
      if(lt->gf && lt->gf->geomType() != GEntity::DiscreteSurface){
        SPoint2 pv = lt->gfp1*(1.-U-V) + lt->gfp2*U + lt->gfp3*V;
        return lt->gf->curvatures(pv, dirMax,dirMin, curvMax, curvMin);
      }
      else if (lt->gf->geomType() == GEntity::DiscreteSurface){
        Curvature& curvature = Curvature::getInstance();
        if( !Curvature::valueAlreadyComputed() ) {
          Msg::Info("Need to compute discrete curvature for anisotropic remesh (in GFace)");
          Curvature::typeOfCurvature type = Curvature::RUSIN; //RBF
          curvature.computeCurvature(model(), type);
        }
    
        double cMin[3];
        double cMax[3];
        SVector3 dMin[3];
        SVector3 dMax[3];
        curvature.triangleNodalValuesAndDirections(lt->tri, dMax, dMin, cMax, cMin, 0);
        //curvature.triangleNodalValuesAndDirections(lt->tri, dMax, dMin, cMax, cMin, 1);
    
        *dirMax = (1-U-V)*dMax[0] + U*dMax[1] + V*dMax[2];
        *dirMin = (1-U-V)*dMin[0] + U*dMin[1] + V*dMin[2];
        *curvMax = (1-U-V)*cMax[0] + U*cMax[1] + V*cMax[2];
        *curvMin = (1-U-V)*cMin[0] + U*cMin[1] + V*cMin[2];
    
        return *curvMax;
      }
      return 0.;
    }
    
    double GFaceCompound::locCurvature(MTriangle *t, double u, double v) const
    {
      SVector3 n1 = _normals[t->getVertex(0)];
      SVector3 n2 = _normals[t->getVertex(1)];
      SVector3 n3 = _normals[t->getVertex(2)];
      double val[9] = {n1.x(), n2.x(), n3.x(),
                       n1.y(), n2.y(), n3.y(),
                       n1.z(), n2.z(), n3.z()};
      return fabs(t->interpolateDiv(val, u, v, 3));
    }
    
    SPoint2 GFaceCompound::parFromVertex(MVertex *v) const
    {
      double U=0.0,V=0.0;
      if(v->onWhat()->dim()==2){// && meshStatistics.status == GFace::DONE) {
        v->getParameter(0, U);
        v->getParameter(1, V);
      }
      if (v->onWhat()->dim()==1 ||
         (v->onWhat()->dim()==2  && U == -1 && V==-1)){ //if MFaceVertex created on edge in bunin
        SPoint2 sp = getCoordinates(v);
        U = sp.x();
        V = sp.y();
      }
      if (v->onWhat()->dim()==0){
        SPoint2 sp = parFromPoint(SPoint3(v->x(), v->y(), v->z()));
        U = sp.x();
        V = sp.y();
      }
      return SPoint2(U,V);
    
    }
    SPoint2 GFaceCompound::parFromPoint(const SPoint3 &p, bool onSurface) const
    {
      if(!oct) parametrize();
    
      std::map<SPoint3,SPoint3>::const_iterator it = _coordPoints.find(p);
      SPoint3 sp = it->second;
    
      return SPoint2(sp.x(), sp.y());
    }
    
    GPoint GFaceCompound::pointInRemeshedOctree(double par1, double par2) const
    {
    
      //create new octree with new mesh elements
      if(!octNew){
        std::vector<MElement *> myElems;
        for (unsigned int i = 0; i < triangles.size(); i++) myElems.push_back(triangles[i]);
        for (unsigned int i = 0; i < quadrangles.size(); i++) myElems.push_back(quadrangles[i]);
    
        std::set<SPoint2> myBCNodes;
        for (unsigned int i = 0; i < myElems.size(); i++){
          MElement *e = myElems[i];
          MVertex *news[4];
          for (int j = 0; j < e->getNumVertices(); j++){
    	MVertex *v = e->getVertex(j);
    	std::map<MVertex*,MVertex*>::iterator it = _3Dto2D.find(v);
    	MVertex *newv =0;
    	if (it == _3Dto2D.end()){
    	  SPoint2 p = parFromVertex(v);
    	  newv = new MVertex (p.x(), p.y(), 0.0);
    	  myParamVert.push_back(newv);
    	  _3Dto2D[v] = newv;
    	  _2Dto3D[newv] = v;
    	  if(v->onWhat()->dim()<2) myBCNodes.insert(p);
    	}
    	else newv = it->second;
    	news[j] = newv;
          }
    
          if(e->getType() == TYPE_TRI)
      	myParamElems.push_back(new MTriangle(news[0],news[1],news[2],e->getNum()));
          else if (e->getType() == TYPE_QUA) {
      	myParamElems.push_back(new MQuadrangle(news[0],news[1],news[2],news[3],e->getNum()));
          }
        }
    
        octNew = new MElementOctree(myParamElems);
    
        //build kdtree boundary nodes in parametric space
    #if defined(HAVE_ANN)
        uv_nodes = annAllocPts(myBCNodes.size(), 3);
        std::set<SPoint2>::iterator itp = myBCNodes.begin();
        int ind = 0;
        while (itp != myBCNodes.end()){
          SPoint2 pt = *itp;
          uv_nodes[ind][0] = pt.x();
          uv_nodes[ind][1] = pt.y();
          uv_nodes[ind][2] = 0.0;
          itp++; ind++;
        }
        uv_kdtree = new ANNkd_tree(uv_nodes, myBCNodes.size(), 3);
    #endif
      }
    
      //now use new octree to find point
      double uvw[3]={par1,par2, 0.0};
      double UV[3];
      double initialTol = MElement::getTolerance();
      MElement::setTolerance(1.e-2);
      MElement *e = octNew->find(par1,par2, 0.0,-1,false);
      MElement::setTolerance(initialTol);
      if (e){
        e->xyz2uvw(uvw,UV);
        double valX[8], valY[8], valZ[8];
        for (int i=0;i<e->getNumPrimaryVertices();i++){
          MVertex *v = _2Dto3D[e->getVertex(i)];
          valX[i] = v->x();
          valY[i] = v->y();
          valZ[i] = v->z();
        }
        GPoint gp;
        gp.x() = e->interpolate(valX,UV[0],UV[1],UV[2]);
        gp.y() = e->interpolate(valY,UV[0],UV[1],UV[2]);
        gp.z() = e->interpolate(valZ,UV[0],UV[1],UV[2]);
        return gp;
      }
      //if element not found in new octree find closest point
      else{
        GPoint gp(50,50,50);
    #if defined(HAVE_ANN)
        double pt[3] = {par1,par2,0.0};
        uv_kdtree->annkSearch(pt, 2, index, dist);
        SPoint3  p1(uv_nodes[index[0]][0], uv_nodes[index[0]][1], uv_nodes[index[0]][2]);
        SPoint3  p2(uv_nodes[index[1]][0], uv_nodes[index[1]][1], uv_nodes[index[1]][2]);
        SPoint3 pnew; double d;
        signedDistancePointLine(p1,p2, SPoint3(par1,par2,0.0), d, pnew);
    
        double uvw[3]={pnew.x(),pnew.y(), 0.0};
        double UV[3];
        MElement *e = octNew->find(pnew.x(), pnew.y(), 0.0,-1,true);
        if(!e)    e = octNew->find(p1.x(), p1.y(), 0.0,-1,true);
        if( e){
          e->xyz2uvw(uvw,UV);
          double valX[8], valY[8], valZ[8];
          for (int i=0;i<e->getNumPrimaryVertices();i++){
    	MVertex *v = _2Dto3D[e->getVertex(i)];
    	valX[i] = v->x();
    	valY[i] = v->y();
    	valZ[i] = v->z();
          }
          gp.x() = e->interpolate(valX,UV[0],UV[1],UV[2]);
          gp.y() = e->interpolate(valY,UV[0],UV[1],UV[2]);
          gp.z() = e->interpolate(valZ,UV[0],UV[1],UV[2]);
        }
        else{
          gp.setNoSuccess();
        }
    #else
        gp.setNoSuccess();
    #endif
    
        if (gp.succeeded()) return gp;
        else{
          Msg::Error("NOT found point with ANN %g %g", par1, par2);
          GPoint gp (30,30,30,this);
          gp.setNoSuccess();
          return gp;
        }
      }
    }
    
    GPoint GFaceCompound::point(double par1, double par2) const
    {
      if(trivial()){
        return (*(_compound.begin()))->point(par1,par2);
      }
    
      if(!oct) parametrize();
    
      double U,V;
      double par[2] = {par1,par2};
      GFaceCompoundTriangle *lt;
      getTriangle(par1, par2, &lt, U,V);
      if(!lt){
        //printf("POINT (%g %g) NOT FOUND --> find closest \n", par1,par2);
        if (meshStatistics.status != GFace::DONE ) {
          double pt[3] = {par1,par2, 0.0};
          kdtree->annkSearch(pt, 1, index, dist);
          lt = &(_gfct[index[0]]);
    
          SPoint3 pnew_a, pnew_b, pnew_c; double d_a, d_b, d_c;
          signedDistancePointLine(lt->p1,lt->p2, SPoint3(par1,par2,0.0), d_a, pnew_a);
          signedDistancePointLine(lt->p1,lt->p3, SPoint3(par1,par2,0.0), d_b, pnew_b);
          signedDistancePointLine(lt->p2,lt->p3, SPoint3(par1,par2,0.0), d_c, pnew_c);
          double u,v;
          if (d_a <= d_b && d_a <= d_c)      {  u = pnew_a.x(); v= pnew_a.y();}
          else if (d_b <= d_a && d_b <= d_c) {  u = pnew_b.x(); v= pnew_b.y();}
          else                               {  u = pnew_c.x(); v= pnew_c.y();}
          double M[2][2],X[2],R[2];
          const SPoint3 p0 = (lt)->p1;
          const SPoint3 p1 = (lt)->p2;
          const SPoint3 p2 = (lt)->p3;
          M[0][0] = p1.x() - p0.x();
          M[0][1] = p2.x() - p0.x();
          M[1][0] = p1.y() - p0.y();
          M[1][1] = p2.y() - p0.y();
          R[0] = (u - p0.x());
          R[1] = (v - p0.y());
          sys2x2(M, R, X);
          U = X[0];
          V = X[1];
          //printf("found closest point (%g %g) U V =%g %g \n", u,v, U,V);
        }
        else{
          //printf("look in remeshed octree \n");
          GPoint gp = pointInRemeshedOctree(par1,par2);
          gp.setNoSuccess();
          return gp;
        }
      }
    
      if (lt->gf->geomType() != GEntity::DiscreteSurface){
        SPoint2 pParam = lt->gfp1*(1.-U-V) + lt->gfp2*U + lt->gfp3*V;
        GPoint pp = lt->gf->point(pParam);
        return GPoint(pp.x(),pp.y(),pp.z(),this,par);
      }
    
      const bool LINEARMESH = true; //false
      if(LINEARMESH){
        // linear Lagrange mesh
        SPoint3 p = lt->v1*(1.-U-V) + lt->v2*U + lt->v3*V;
        return GPoint(p.x(),p.y(),p.z(),this,par);
      }
      else{
        // curved PN triangle
        const SVector3 n1 = _normals[lt->tri->getVertex(0)];
        const SVector3 n2 = _normals[lt->tri->getVertex(1)];
        const SVector3 n3 = _normals[lt->tri->getVertex(2)];
    
        SVector3 b300, b030, b003;
        SVector3 b210, b120, b021, b012, b102, b201, E, VV, b111;
        b300 = lt->v1;
        b030 = lt->v2;
        b003 = lt->v3;
    
        // tagged PN triangles (sigma=1)
        double theta = 0.0;
        SVector3 d1 = lt->v1+.33*(1-theta)*(lt->v2-lt->v1);
        SVector3 d2 = lt->v2+.33*(1-theta)*(lt->v1-lt->v2);
        SVector3 X1 = 1/norm(n1)*n1;
        SVector3 X2 = 1/norm(n2)*n2;
        b210 = d1 - dot(X1,d1-lt->v1)*X1;
        b120 = d2 - dot(X2,d2-lt->v2)*X2;
        SVector3 d3 = lt->v2+.33*(1-theta)*(lt->v3-lt->v2);
        SVector3 d4 = lt->v3+.33*(1-theta)*(lt->v2-lt->v3);
        SVector3 X3 = 1/norm(n2)*n2;
        SVector3 X4 = 1/norm(n3)*n3;
        b021 = d3 - dot(X3,d3-lt->v2)*X3;
        b012 = d4 - dot(X4,d4-lt->v3)*X4;
        SVector3 d5 = lt->v3+.33*(1-theta)*(lt->v1-lt->v3);
        SVector3 d6 = lt->v1+.33*(1-theta)*(lt->v3-lt->v1);
        SVector3 X5 = 1/norm(n3)*n3;
        SVector3 X6 = 1/norm(n1)*n1;
        b102 = d5 - dot(X5,d5-lt->v3)*X5;
        b201 = d6 - dot(X6,d6-lt->v1)*X6;
    
        E=(b210+b120+b021+b012+b102+b201)*0.16667;
        VV=(lt->v1+lt->v2+lt->v3)*0.333;
        b111=E+(E-VV)*0.5;
    
        double W = 1-U-V;
        SVector3 point = b300*W*W*W+b030*U*U*U+b003*V*V*V+
          b210*3.*W*W*U+b120*3.*W*U*U+b201*3.*W*W*V+
          b021*3.*U*U*V+b102*3.*W*V*V+b012*3.*U*V*V+
          b111*6.*W*U*V;
    
        SPoint3 PP(point.x(), point.y(), point.z());
        return GPoint(PP.x(),PP.y(),PP.z(),this,par);
      }
    }
    
    Pair<SVector3,SVector3> GFaceCompound::firstDer(const SPoint2 &param) const
    {
    
      if(trivial()){
        return (*(_compound.begin()))->firstDer(param);
      }
    
      if(!oct) parametrize();
    
      double U,V;
      GFaceCompoundTriangle *lt;
      getTriangle(param.x(), param.y(), &lt, U,V);
      MTriangle *tri;
      if (lt) tri = lt->tri;
      else {
        //printf("FIRSTDER POINT NOT FOUND --> kdtree \n");
        //printf("uv=%g %g \n", param.x(), param.y());
        double pt[3] = {param.x(), param.y(), 0.0};
        kdtree->annkSearch(pt, 1, index, dist);
        tri = (&_gfct[index[0]])->tri;
      }
    
      SVector3 dXdu1 = firstDerivatives[tri->getVertex(0)].first();
      SVector3 dXdu2 = firstDerivatives[tri->getVertex(1)].first();
      SVector3 dXdu3 = firstDerivatives[tri->getVertex(2)].first();
      SVector3 dXdv1 = firstDerivatives[tri->getVertex(0)].second();
      SVector3 dXdv2 = firstDerivatives[tri->getVertex(1)].second();
      SVector3 dXdv3 = firstDerivatives[tri->getVertex(2)].second();
      SVector3 dXdu = dXdu1*(1.-U-V) + dXdu2*U + dXdu3*V;
      SVector3 dXdv = dXdv1*(1.-U-V) + dXdv2*U + dXdv3*V;
      return Pair<SVector3, SVector3>(dXdu,dXdv);
    
    }
    
    // Pair<SVector3,SVector3> GFaceCompound::firstDer(const SPoint2 &param) const
    // {
    //   if(!oct) parametrize();
    
    //   if(trivial())
    //     return (*(_compound.begin()))->firstDer(param);
    
    //   double U, V;
    //   GFaceCompoundTriangle *lt;
    //   getTriangle(param.x(), param.y(), &lt, U,V);
    //   if(!lt && _mapping != RBF)
    //     return Pair<SVector3, SVector3>(SVector3(1, 0, 0), SVector3(0, 1, 0));
    //   else if (!lt && _mapping == RBF){
    //     double x, y, z;
    //     SVector3 dXdu, dXdv  ;
    //     _rbf->UVStoXYZ(param.x(), param.y(), x, y, z, dXdu, dXdv);
    //     return Pair<SVector3, SVector3>(dXdu, dXdv);
    //   }
    
    //   double mat[2][2] = {{lt->p2.x() - lt->p1.x(), lt->p3.x() - lt->p1.x()},
    //                       {lt->p2.y() - lt->p1.y(), lt->p3.y() - lt->p1.y()}};
    //   double inv[2][2];
    //   double det = inv2x2(mat,inv);
    //   if (!det && _mapping == RBF){
    //     double x, y, z;
    //     SVector3 dXdu, dXdv  ;
    //     _rbf->UVStoXYZ(param.x(), param.y(), x, y, z, dXdu, dXdv);
    //     return Pair<SVector3, SVector3>(dXdu, dXdv);
    //   }
    
    //   SVector3 dXdxi(lt->v2 - lt->v1);
    //   SVector3 dXdeta(lt->v3 - lt->v1);
    
    //   SVector3 dXdu(dXdxi * inv[0][0] + dXdeta * inv[1][0]);
    //   SVector3 dXdv(dXdxi * inv[0][1] + dXdeta * inv[1][1]);
    
    //   return Pair<SVector3, SVector3>(dXdu, dXdv);
    // }
    
    void GFaceCompound::secondDer(const SPoint2 &param,
                                  SVector3 *dudu, SVector3 *dvdv, SVector3 *dudv) const
    {
    #if defined(HAVE_MESH)
    
      if(!oct) parametrize();
    
      if(adjv.size() == 0){
        std::vector<MTriangle*> allTri;
        std::list<GFace*>::const_iterator it = _compound.begin();
        for( ; it != _compound.end(); ++it){
          allTri.insert(allTri.end(), (*it)->triangles.begin(), (*it)->triangles.end() );
        }
        buildVertexToTriangle(allTri, adjv);
      }
    
      if (xuu.size() == 0) computeHessianMapping();
    
      double U, V;
      GFaceCompoundTriangle *lt;
      getTriangle(param.x(), param.y(), &lt, U,V);
      if(!lt) return;
    
      MVertex* v0 = lt->tri->getVertex(0);
      MVertex* v1 = lt->tri->getVertex(1);
      MVertex* v2 = lt->tri->getVertex(2);
    
      *dudu = (1-U-V)*xuu[v0] + U*xuu[v1] + V*xuu[v2];
      *dvdv = (1-U-V)*xvv[v0] + U*xvv[v1] + V*xvv[v2];
      *dudv = (1-U-V)*xuv[v0] + U*xuv[v1] + V*xuv[v2];
    
    #endif
      //Msg::Debug("Computation of the second derivatives is not implemented for compound faces");
    }
    
    void GFaceCompound::computeHessianMapping() const
    {
    
    #if defined(HAVE_MESH)
      unsigned int sysDim = 6; //for 2D
      unsigned int minNbPtBlob = 3*sysDim;
      for (v2t_cont::iterator it = adjv.begin(); it != adjv.end(); it++) {
        MVertex *ver = it->first;
        std::vector<MVertex*> vv = getBlob(minNbPtBlob, it, adjv);
        fullMatrix<double> A(vv.size(),sysDim), ATAx(sysDim,sysDim), ATAy(sysDim,sysDim), ATAz(sysDim,sysDim);
        fullVector<double> bx(vv.size()), ATbx(sysDim), coeffsx(sysDim);
        fullVector<double> by(vv.size()), ATby(sysDim), coeffsy(sysDim);
        fullVector<double> bz(vv.size()), ATbz(sysDim), coeffsz(sysDim);
        for(unsigned int i=0; i<vv.size(); i++) {
          SPoint3 uv = coordinates[vv[i]];
          A(i,0) = uv.x()*uv.x(); A(i,1) = uv.x()*uv.y(); A(i,2) = uv.y()*uv.y();
          A(i,3) = uv.x(); A(i,4) = uv.y(); A(i,5) = 1.;
          bx(i) = vv[i]->x();
          by(i) = vv[i]->y();
          bz(i) = vv[i]->z();
        }
        ATAx.gemmWithAtranspose(A,A,1.,0.);
        ATAy = ATAx; ATAz = ATAx;
        A.multWithATranspose(bx,1.,0.,ATbx);
        A.multWithATranspose(by,1.,0.,ATby);
        A.multWithATranspose(bz,1.,0.,ATbz);
        ATAx.luSolve(ATbx,coeffsx);
        ATAy.luSolve(ATby,coeffsy);
        ATAz.luSolve(ATbz,coeffsz);
        SPoint3 uv = coordinates[ver];
        xuu[ver] = SVector3(2.*coeffsx(0),2.*coeffsy(0),2.*coeffsz(0)) ;
        xvv[ver] = SVector3(2.*coeffsx(2),2.*coeffsy(2),2.*coeffsz(2)) ;
        xuv[ver] = SVector3(coeffsx(1), coeffsy(1),coeffsz(1));
        xu[ver]  = SVector3(2.*coeffsx(0)*uv.x()+coeffsx(1)*uv.y()+coeffsx(3),
    			2.*coeffsy(0)*uv.x()+coeffsy(1)*uv.y()+coeffsy(3),
    			2.*coeffsz(0)*uv.x()+coeffsz(1)*uv.y()+coeffsz(3));
        xv[ver] = SVector3(coeffsx(1)*uv.x()+2.*coeffsx(2)*uv.y()+coeffsx(4),
    		       coeffsy(1)*uv.x()+2.*coeffsy(2)*uv.y()+coeffsy(4),
    		       coeffsz(1)*uv.x()+2.*coeffsz(2)*uv.y()+coeffsz(4));
      }
    
    #endif
    }
    static void GFaceCompoundBB(void *a, double*mmin, double*mmax)
    {
      GFaceCompoundTriangle *t = (GFaceCompoundTriangle *)a;
      mmin[0] = std::min(std::min(t->p1.x(), t->p2.x()), t->p3.x());
      mmin[1] = std::min(std::min(t->p1.y(), t->p2.y()), t->p3.y());
      mmax[0] = std::max(std::max(t->p1.x(), t->p2.x()), t->p3.x());
      mmax[1] = std::max(std::max(t->p1.y(), t->p2.y()), t->p3.y());
      mmin[2] = -1;
      mmax[2] = 1;
    
      const double dx = mmax[0] - mmin[0];
      const double dy = mmax[1] - mmin[1];
      const double eps = 0.0;//1.e-12;
      mmin[0] -= eps*dx;
      mmin[1] -= eps*dy;
      mmax[0] += eps*dx;
      mmax[1] += eps*dy;
    }
    
    static void GFaceCompoundCentroid(void *a, double*c)
    {
      GFaceCompoundTriangle *t = (GFaceCompoundTriangle *)a;
      c[0] = (t->p1.x() + t->p2.x() + t->p3.x()) / 3.0;
      c[1] = (t->p1.y() + t->p2.y() + t->p3.y()) / 3.0;
      c[2] = 0.0;
    }
    
    static int GFaceCompoundInEle(void *a, double*c)
    {
      GFaceCompoundTriangle *t = (GFaceCompoundTriangle *)a;
      double M[2][2], R[2], X[2];
      const double eps = 1.e-8;
      const SPoint3 p0 = t->p1;
      const SPoint3 p1 = t->p2;
      const SPoint3 p2 = t->p3;
      M[0][0] = p1.x() - p0.x();
      M[0][1] = p2.x() - p0.x();
      M[1][0] = p1.y() - p0.y();
      M[1][1] = p2.y() - p0.y();
      R[0] = (c[0] - p0.x());
      R[1] = (c[1] - p0.y());
      sys2x2(M, R, X);
      if(X[0] > -eps && X[1] > -eps && 1. - X[0] - X[1] > -eps){
        return 1;
      }
      return 0;
    }
    
    void GFaceCompound::getTriangle(double u, double v,
                                    GFaceCompoundTriangle **lt,
                                    double &_u, double &_v) const
    {
      double uv[3] = {u, v, 0};
      *lt = (GFaceCompoundTriangle*)Octree_Search(uv, oct);
    
      if(!(*lt)){
        _u = 0.0; _v = 0.0;
        return;
      }
    
      double M[2][2],X[2],R[2];
      const SPoint3 p0 = (*lt)->p1;
      const SPoint3 p1 = (*lt)->p2;
      const SPoint3 p2 = (*lt)->p3;
      M[0][0] = p1.x() - p0.x();
      M[0][1] = p2.x() - p0.x();
      M[1][0] = p1.y() - p0.y();
      M[1][1] = p2.y() - p0.y();
      R[0] = (u - p0.x());
      R[1] = (v - p0.y());
      sys2x2(M, R, X);
      _u = X[0];
      _v = X[1];
    }
    
    void GFaceCompound::buildOct() const
    {
    #if defined(HAVE_MESH)
      SBoundingBox3d bb;
      int count = 0;
      std::list<GFace*>::const_iterator it = _compound.begin();
    
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          MTriangle *t = (*it)->triangles[i];
          //create bounding box
          for(int j = 0; j < 3; j++){
            std::map<MVertex*,SPoint3>::const_iterator itj = coordinates.find(t->getVertex(j));
            _coordPoints.insert(std::make_pair(t->getVertex(j)->point(), itj->second));
            bb += SPoint3(itj->second.x(),itj->second.y(),0.0);
          }
          count++;
        }
      }
    
      //ANN octree
      std::set<MVertex*> allVS;
      nodes = annAllocPts(count, 3);
    
      // make bounding box
      SPoint3 bbmin = bb.min(), bbmax = bb.max();
      double origin[3] = {bbmin.x(), bbmin.y(), bbmin.z()};
      double ssize[3] = {bbmax.x() - bbmin.x(),
                         bbmax.y() - bbmin.y(),
                         bbmax.z() - bbmin.z()};
    
      _gfct = new GFaceCompoundTriangle[count];
      const int maxElePerBucket = 15;
      oct = Octree_Create(maxElePerBucket, origin, ssize, GFaceCompoundBB,
                          GFaceCompoundCentroid, GFaceCompoundInEle);
      std::map<MElement*, Pair<SVector3,SVector3> > firstElemDerivatives;
    
      it = _compound.begin();
      count = 0;
    
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          MTriangle *t = (*it)->triangles[i];
          std::map<MVertex*,SPoint3>::const_iterator it0 =
            coordinates.find(t->getVertex(0));
          std::map<MVertex*,SPoint3>::const_iterator it1 =
            coordinates.find(t->getVertex(1));
          std::map<MVertex*,SPoint3>::const_iterator it2 =
            coordinates.find(t->getVertex(2));
          _gfct[count].p1 = it0->second;
          _gfct[count].p2 = it1->second;
          _gfct[count].p3 = it2->second;
           if((*it)->geomType() != GEntity::DiscreteSurface){
    	// take care of the seam !!!!
    	if (t->getVertex(0)->onWhat()->dim() == 2){
    	  reparamMeshEdgeOnFace(t->getVertex(0), t->getVertex(1), *it,
                                    _gfct[count].gfp1, _gfct[count].gfp2);
    	  reparamMeshEdgeOnFace(t->getVertex(0), t->getVertex(2), *it,
                                    _gfct[count].gfp1, _gfct[count].gfp3);
    	}
    	else if (t->getVertex(1)->onWhat()->dim() == 2){
    	  reparamMeshEdgeOnFace(t->getVertex(1), t->getVertex(0), *it,
                                    _gfct[count].gfp2, _gfct[count].gfp1);
    	  reparamMeshEdgeOnFace(t->getVertex(1), t->getVertex(2), *it,
                                    _gfct[count].gfp2, _gfct[count].gfp3);
    	}
    	else if (t->getVertex(2)->onWhat()->dim() == 2){
    	  reparamMeshEdgeOnFace(t->getVertex(2), t->getVertex(0), *it,
                                    _gfct[count].gfp3, _gfct[count].gfp1);
    	  reparamMeshEdgeOnFace(t->getVertex(2), t->getVertex(1), *it,
                                    _gfct[count].gfp3, _gfct[count].gfp2);
    	}
    	else {
    	  reparamMeshVertexOnFace(t->getVertex(0), *it, _gfct[count].gfp1);
    	  reparamMeshVertexOnFace(t->getVertex(1), *it, _gfct[count].gfp2);
    	  reparamMeshVertexOnFace(t->getVertex(2), *it, _gfct[count].gfp3);
    	}
          }
          _gfct[count].v1 = SPoint3(t->getVertex(0)->x(), t->getVertex(0)->y(),
                                    t->getVertex(0)->z());
          _gfct[count].v2 = SPoint3(t->getVertex(1)->x(), t->getVertex(1)->y(),
                                    t->getVertex(1)->z());
          _gfct[count].v3 = SPoint3(t->getVertex(2)->x(), t->getVertex(2)->y(),
                                    t->getVertex(2)->z());
          _gfct[count].gf = *it;
          _gfct[count].tri = t;
    
          //compute first derivatives for every triangle
          double mat[2][2] = {{_gfct[count].p2.x() - _gfct[count].p1.x(),
    			   _gfct[count].p3.x() - _gfct[count].p1.x()},
    			  {_gfct[count].p2.y() - _gfct[count].p1.y(),
    			   _gfct[count].p3.y() - _gfct[count].p1.y()}};
          double inv[2][2];
          double det = inv2x2(mat,inv);
          SVector3 dXdxi (_gfct[count].v2 - _gfct[count].v1);
          SVector3 dXdeta(_gfct[count].v3 - _gfct[count].v1);
          SVector3 dXdu(dXdxi * inv[0][0] + dXdeta * inv[1][0]);
          SVector3 dXdv(dXdxi * inv[0][1] + dXdeta * inv[1][1]);
          firstElemDerivatives[(MElement*)t] = Pair<SVector3,SVector3>(dXdu,dXdv);
    
          // build ANN kdtree
          nodes[count][0] = (it0->second.x() + it1->second.x() + it2->second.x())/3.0 ;
          nodes[count][1] = (it0->second.y() + it1->second.y() + it2->second.y())/3.0 ;
          nodes[count][2] = 0.0;
    
          Octree_Insert(&_gfct[count], oct);
          count++;
        }
      }
      nbT = count;
      Octree_Arrange(oct);
    
    
      //smooth first derivatives at vertices
      if(adjv.size() == 0){
        std::vector<MTriangle*> allTri;
        std::list<GFace*>::const_iterator it = _compound.begin();
        for( ; it != _compound.end(); ++it){
          allTri.insert(allTri.end(), (*it)->triangles.begin(), (*it)->triangles.end() );
        }
        buildVertexToTriangle(allTri, adjv);
      }
      for(v2t_cont::iterator it = adjv.begin(); it!= adjv.end(); ++it){
        MVertex *v = it->first;
        std::vector<MElement*> vTri = it->second;
        SVector3 dXdu(0.0), dXdv(0.0);
        int nbTri = vTri.size();
        for (unsigned int j = 0; j < nbTri; j++){
          dXdu += firstElemDerivatives[vTri[j]].first();
          dXdv += firstElemDerivatives[vTri[j]].second();
        }
        dXdu*= 1./nbTri;
        dXdv*= 1./nbTri;
        firstDerivatives[v] = Pair<SVector3, SVector3>(dXdu, dXdv);
      }
    
      //build ANN kdtree
      kdtree = new ANNkd_tree(nodes, count, 3);
    
      printStuff();
    #endif
    }
    
    bool GFaceCompound::checkTopology() const
    {
      if (_mapping == RBF) return true;
    
      bool correctTopo = true;
      if(allNodes.empty()) buildAllNodes();
    
      int Nb = _interior_loops.size();
      int G  = genusGeom() ;
    
      double H = getSizeH();
      double D = H;
      if (_interior_loops.size() > 0)    D =  getSizeBB(_U0);
      int AR1 = (int) checkAspectRatio();
      int AR2 = (int) floor(H/D+0.5);
      int AR = std::max(AR1, AR2);
    
      if (G != 0 || Nb < 1){
        correctTopo = false;
        nbSplit = std::max(G+2, 2);
        Msg::Warning("Wrong topology: Genus=%d, Nb boundaries=%d, AR=%g", G, Nb, H/D);
        if (_allowPartition){
          Msg::Info("-----------------------------------------------------------");
          Msg::Info("--- Split surface %d in %d parts with Multilevel Mesh partitioner",
                    tag(), nbSplit);
        }
        else{
          Msg::Fatal("For remeshing your geometry, you should enable the automatic "
                     "remeshing algorithm. Add 'Mesh.RemeshAlgorithm=1;' in your "
                     "geo file or through the Fltk window (Options > Mesh > General)");
        }
      }
      else if (G == 0 && AR > AR_MAX){
        correctTopo = false;
        Msg::Warning("Wrong topology: Aspect ratio is too high AR=%d (AR1=%d AR2=%d)", AR, AR1, AR2);
        if (_allowPartition == 1){
          nbSplit = -2;
          Msg::Info("-----------------------------------------------------------");
          Msg::Info("--- Split surface %d in 2 parts with Laplacian Mesh partitioner",
                    tag());
        }
        else if (_allowPartition == 2){
          nbSplit = 2;
          Msg::Info("-----------------------------------------------------------");
          Msg::Info("--- Split surface %d in %d parts with Multilevel Mesh partitioner",
                    tag(), nbSplit);
        }
        else if (_allowPartition == 0){
          Msg::Debug("The geometrical aspect ratio of your geometry is quite high.\n "
    		 "You should enable partitioning of the mesh by activating the\n"
    		 "automatic remeshing algorithm. Add 'Mesh.RemeshAlgorithm=1;'\n "
    		 "in your geo file or through the Fltk window (Options > Mesh >\n "
    		 "General)");
        }
      }
      else{
        Msg::Debug("Correct topology: Genus=%d and Nb boundaries=%d, AR=%g", G, Nb, H/D);
      }
    
      return correctTopo;
    }
    
    double GFaceCompound::checkAspectRatio() const
    {
    
      if(allNodes.empty()) buildAllNodes();
    
      double limit =  1.e-20;
      double areaMin = 1.e20;
      double area3D = 0.0;
      int nb = 0;
      std::list<GFace*>::const_iterator it = _compound.begin();
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          MTriangle *t = (*it)->triangles[i];
          std::vector<MVertex *> v(3);
          for(int k = 0; k < 3; k++){
            v[k] = t->getVertex(k);
          }
          std::map<MVertex*,SPoint3>::const_iterator it0 = coordinates.find(v[0]);
          std::map<MVertex*,SPoint3>::const_iterator it1 = coordinates.find(v[1]);
          std::map<MVertex*,SPoint3>::const_iterator it2 = coordinates.find(v[2]);
          double p0[3] = {v[0]->x(), v[0]->y(), v[0]->z()};
          double p1[3] = {v[1]->x(), v[1]->y(), v[1]->z()};
          double p2[3] = {v[2]->x(), v[2]->y(), v[2]->z()};
          double a_3D = fabs(triangle_area(p0, p1, p2));
          area3D += a_3D;
          double q0[3] = {it0->second.x(), it0->second.y(), 0.0};
          double q1[3] = {it1->second.x(), it1->second.y(), 0.0};
          double q2[3] = {it2->second.x(), it2->second.y(), 0.0};
          double a_2D = fabs(triangle_area(q0, q1, q2));
          if (a_2D > limit) nb++;
          areaMin = std::min(areaMin,a_2D);
        }
      }
    
      std::list<GEdge*>::const_iterator it0 = _U0.begin();
      double tot_length = 0.0;
      for( ; it0 != _U0.end(); ++it0 )
        for(unsigned int i = 0; i < (*it0)->lines.size(); i++ )
          tot_length += (*it0)->lines[i]->getLength();
    
      double AR = M_PI*area3D/(tot_length*tot_length);
    
      if (areaMin > 0 && areaMin < limit && nb > 3) {
        Msg::Warning("Too small triangles in mapping (a_2D=%g)", areaMin);
      }
      else {
        Msg::Debug("Geometrical aspect ratio is OK  :-)");
      }
    
      return AR;
    }
    
    void GFaceCompound::coherencePatches() const
    {
      if (_mapping == RBF) return;
      Msg::Info("Re-orient all %d compound patches normals coherently",
                _compound.size());
    
      std::map<MEdge, std::set<MElement*>, Less_Edge > edge2elems;
      std::vector<MElement*> allElems;
      std::list<GFace*>::const_iterator it = _compound.begin();
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->getNumMeshElements(); ++i){
          MElement *t =  (*it)->getMeshElement(i);
          allElems.push_back(t);
          for (int j = 0; j <  t->getNumEdges(); j++){
    	MEdge me = t->getEdge(j);
    	std::map<MEdge, std::set<MElement*, std::less<MElement*> >,
    		 Less_Edge >::iterator it = edge2elems.find(me);
    	if (it == edge2elems.end()) {
    	  std::set<MElement*, std::less<MElement*> > mySet;
    	  mySet.insert(t);
    	  edge2elems.insert(std::make_pair(me, mySet));
    	}
    	else{
    	  std::set<MElement*, std::less<MElement*> > mySet = it->second;
    	  mySet.insert(t);
    	  it->second = mySet;
    	}
        }
      }
    }
    
      std::set<MElement* , std::less<MElement*> > touched;
      int iE, si, iE2, si2;
      touched.insert(allElems[0]);
      while(touched.size() != allElems.size()){
        for(unsigned int i = 0; i < allElems.size(); i++){
          MElement *t = allElems[i];
          std::set<MElement*, std::less<MElement*> >::iterator it2 = touched.find(t);
          if(it2 != touched.end()){
    	for (int j = 0; j <  t->getNumEdges(); j++){
    	  MEdge me = t->getEdge(j);
    	  t->getEdgeInfo(me, iE,si);
    	  std::map<MEdge, std::set<MElement*>, Less_Edge >::iterator it =
    	    edge2elems.find(me);
    	  std::set<MElement*, std::less<MElement*> > mySet = it->second;
    	  for(std::set<MElement*, std::less<MElement*> >::iterator itt = mySet.begin();
    	      itt != mySet.end(); itt++){
    	    if (*itt != t){
    	      (*itt)->getEdgeInfo(me,iE2,si2);
    	      if(si == si2) { (*itt)->reverse();}
    	      touched.insert(*itt);
    	    }
    	  }
    	}
          }
        }
      }
    }
    
    void GFaceCompound::coherenceNormals()
    {
      Msg::Info("Re-orient all %d face normals coherently", getNumMeshElements());
    
      std::map<MEdge, std::set<MElement*>, Less_Edge > edge2elems;
      for(unsigned int i = 0; i <  getNumMeshElements(); i++){
        MElement *t =  getMeshElement(i);
        for (int j = 0; j <  t->getNumEdges(); j++){
          MEdge me = t->getEdge(j);
          std::map<MEdge, std::set<MElement*, std::less<MElement*> >,
            Less_Edge >::iterator it = edge2elems.find(me);
          if (it == edge2elems.end()) {
            std::set<MElement*, std::less<MElement*> > mySet;
            mySet.insert(t);
            edge2elems.insert(std::make_pair(me, mySet));
          }
          else{
            std::set<MElement*, std::less<MElement*> > mySet = it->second;
            mySet.insert(t);
            it->second = mySet;
          }
        }
      }
    
      std::set<MElement* , std::less<MElement*> > touched;
      int iE, si, iE2, si2;
      touched.insert(getMeshElement(0));
      while(touched.size() != getNumMeshElements()){
        for(unsigned int i = 0; i < getNumMeshElements(); i++){
          MElement *t = getMeshElement(i);
          std::set<MElement*, std::less<MElement*> >::iterator it2 = touched.find(t);
          if(it2 != touched.end()){
            for (int j = 0; j <  t->getNumEdges(); j++){
              MEdge me = t->getEdge(j);
              t->getEdgeInfo(me, iE,si);
              std::map<MEdge, std::set<MElement*>, Less_Edge >::iterator it =
                edge2elems.find(me);
              std::set<MElement*, std::less<MElement*> > mySet = it->second;
              for(std::set<MElement*, std::less<MElement*> >::iterator itt = mySet.begin();
                  itt != mySet.end(); itt++){
                if (*itt != t){
                  (*itt)->getEdgeInfo(me,iE2,si2);
                  if(si == si2)  (*itt)->reverse();
                  touched.insert(*itt);
                }
              }
            }
          }
        }
      }
    }
    
    void GFaceCompound::buildAllNodes() const
    {
      std::list<GFace*>::const_iterator it = _compound.begin();
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          MTriangle *t = (*it)->triangles[i];
          for(int j = 0; j < 3; j++){
            allNodes.insert(t->getVertex(j));
          }
        }
      }
    }
    
    int GFaceCompound::genusGeom() const
    {
      std::list<GFace*>::const_iterator it = _compound.begin();
      std::set<MEdge, Less_Edge> es;
      std::set<MVertex*> vs;
      int N = 0;
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          N++;
          MTriangle *e = (*it)->triangles[i];
          for(int j = 0; j < e->getNumVertices(); j++){
    	vs.insert(e->getVertex(j));
          }
          for(int j = 0; j < e->getNumEdges(); j++){
    	es.insert(e->getEdge(j));
          }
        }
      }
      int poincare = vs.size() - es.size() + N;
    
      return (int)(-poincare + 2 - _interior_loops.size())/2;
    }
    
    void GFaceCompound::printStuff(int iNewton) const
    {
      if(!CTX::instance()->mesh.saveAll) return;
    
      std::list<GFace*>::const_iterator it = _compound.begin();
    
      char name0[256], name1[256], name2[256], name3[256];
      char name4[256], name5[256], name6[256];
      char name7[256];
      sprintf(name0, "UVAREA-%d.pos", tag()); //(*it)->tag()
      sprintf(name1, "UVX-%d_%d.pos", tag(), iNewton);
      sprintf(name2, "UVY-%d_%d.pos", tag(), iNewton);
      sprintf(name3, "UVZ-%d_%d.pos", tag(), iNewton);
      sprintf(name4, "XYZU-%d_%d.pos", tag(), iNewton);
      sprintf(name5, "XYZV-%d_%d.pos", tag(), iNewton);
      sprintf(name6, "XYZC-%d.pos", tag());
    
      sprintf(name7, "UVM-%d.pos", (*it)->tag());
    
      //FILE * uva = fopen(name0,"w");
      FILE * uvx = fopen(name1,"w");
      FILE * uvy = fopen(name2,"w");
      FILE * uvz = fopen(name3,"w");
      FILE * xyzu = fopen(name4,"w");
      FILE * xyzv = fopen(name5,"w");
      //FILE * xyzc = fopen(name6,"w");
      //FILE * uvm = fopen(name7,"w");
    
      //fprintf(uva, "View \"\"{\n");
      fprintf(uvx, "View \"\"{\n");
      fprintf(uvy, "View \"\"{\n");
      fprintf(uvz, "View \"\"{\n");
      fprintf(xyzu, "View \"\"{\n");
      fprintf(xyzv, "View \"\"{\n");
      //fprintf(xyzc, "View \"\"{\n");
      //fprintf(uvm, "View \"\"{\n");
    
      for( ; it != _compound.end() ; ++it){
        for(unsigned int i = 0; i < (*it)->triangles.size(); ++i){
          MTriangle *t = (*it)->triangles[i];
          std::map<MVertex*,SPoint3>::const_iterator it0 =
            coordinates.find(t->getVertex(0));
          std::map<MVertex*,SPoint3>::const_iterator it1 =
            coordinates.find(t->getVertex(1));
          std::map<MVertex*,SPoint3>::const_iterator it2 =
            coordinates.find(t->getVertex(2));
          fprintf(xyzv, "ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g};\n",
                  t->getVertex(0)->x(), t->getVertex(0)->y(), t->getVertex(0)->z(),
                  t->getVertex(1)->x(), t->getVertex(1)->y(), t->getVertex(1)->z(),
                  t->getVertex(2)->x(), t->getVertex(2)->y(), t->getVertex(2)->z(),
                  it0->second.y(),it1->second.y(),it2->second.y());
          fprintf(xyzu, "ST(%22.15E,%22.15E,%22.15E,%22.15E,%22.15E,%22.15E,%22.15E,"
                  "%22.15E,%22.15E){%22.15E,%22.15E,%22.15E};\n",
                  t->getVertex(0)->x(), t->getVertex(0)->y(), t->getVertex(0)->z(),
                  t->getVertex(1)->x(), t->getVertex(1)->y(), t->getVertex(1)->z(),
                  t->getVertex(2)->x(), t->getVertex(2)->y(), t->getVertex(2)->z(),
                  it0->second.x(),it1->second.x(),it2->second.x());
          // double K1 = locCurvature(t,it0->second.x(),it0->second.y());
          // double K2 = locCurvature(t,it1->second.x(),it1->second.y());
          // double K3 = locCurvature(t,it2->second.x(),it2->second.y());
          // fprintf(xyzc,"ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g};\n",
          //         t->getVertex(0)->x(), t->getVertex(0)->y(), t->getVertex(0)->z(),
          //         t->getVertex(1)->x(), t->getVertex(1)->y(), t->getVertex(1)->z(),
          //         t->getVertex(2)->x(), t->getVertex(2)->y(), t->getVertex(2)->z(),
          //         K1, K2, K3);
    
          // double p0[3] = {t->getVertex(0)->x(), t->getVertex(0)->y(), t->getVertex(0)->z()};
          // double p1[3] = {t->getVertex(1)->x(), t->getVertex(1)->y(), t->getVertex(1)->z()};
          // double p2[3] = {t->getVertex(2)->x(), t->getVertex(2)->y(), t->getVertex(2)->z()};
          // double a_3D = fabs(triangle_area(p0, p1, p2));
          // double q0[3] = {it0->second.x(), it0->second.y(), 0.0};
          // double q1[3] = {it1->second.x(), it1->second.y(), 0.0};
          // double q2[3] = {it2->second.x(), it2->second.y(), 0.0};
          // double a_2D = fabs(triangle_area(q0, q1, q2));
          // double area = (a_3D/a_2D); //*(a_3D/a_2D);
          // Pair<SVector3, SVector3> der = this->firstDer(SPoint2(it0->second.x(),
          //                                                       it0->second.y()));
          // double metric0e = dot(der.first(), der.first());
          // double metric0f = dot(der.second()*(1./norm(der.second())),
          //                       der.first()*(1./norm(der.first())));
          // double metric0g = dot(der.second(), der.second());
          // Pair<SVector3, SVector3> der1 = this->firstDer(SPoint2(it1->second.x(),
          //                                                        it1->second.y()));
          // double metric1e = dot(der1.first(), der1.first());
          // double metric1f = dot(der1.second()*(1/norm(der1.second())),
          //                       der1.first()*(1./norm(der1.first())));
          // double metric1g = dot(der1.second(), der1.second());
          // Pair<SVector3, SVector3> der2 = this->firstDer(SPoint2(it2->second.x(),
          //                                                        it2->second.y()));
          // double metric2e = dot(der2.first(),  der2.first());
          // double metric2f = dot(der2.second()*(1./norm(der2.second())),
          //                       der2.first()*(1./norm(der2.first())));
          // double metric2g = dot(der2.second(), der2.second());
    
          // double mat0[2][2], eig0[2];
          // double mat1[2][2], eig1[2];
          // double mat2[2][2], eig2[2];
          // mat0[0][0]  = metric0e;  mat0[0][1]  =  metric0f;
          // mat0[1][0]  =  metric0f;  mat0[1][1]  =  metric0g;
          // eigenvalue2x2(mat0, eig0);
          // mat1[0][0]  = metric1e;  mat1[0][1]  = metric1f;
          // mat1[1][0]  = metric1f;  mat1[1][1]  = metric1g;
          // eigenvalue2x2(mat1, eig1);
          // mat2[0][0]  = metric2e;  mat2[0][1]  = metric2f;
          // mat2[1][0]  = metric2f;  mat2[1][1]  = metric2g;
          // eigenvalue2x2(mat2, eig2);
    
          // double disp0 = sqrt(.5*(eig0[0]*eig0[0]+ (eig0[1]*eig0[1])));
          // double disp1 = sqrt(.5*(eig1[0]*eig1[0]+ (eig1[1]*eig1[1])));
          // double disp2 = sqrt(.5*(eig2[0]*eig2[0]+ (eig2[1]*eig2[1])));
          // double mdisp = .333*(disp0+disp1+disp2);
          // fprintf(uva, "ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g};\n",
          //         it0->second.x(), it0->second.y(), 0.0,
          //         it1->second.x(), it1->second.y(), 0.0,
          //         it2->second.x(), it2->second.y(), 0.0,
          //         area, area, area);
    
          // fprintf(uvm, "ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g};\n",
          //         it0->second.x(), it0->second.y(), 0.0,
          //         it1->second.x(), it1->second.y(), 0.0,
          //         it2->second.x(), it2->second.y(), 0.0,
          // 	      mdisp, mdisp, mdisp);
    
          fprintf(uvx, "ST(%22.15E,%22.15E,%22.15E,%22.15E,%22.15E,%22.15E,%22.15E,"
                  "%22.15E,%22.15E){%22.15E,%22.15E,%22.15E};\n",
                  it0->second.x(), it0->second.y(), 0.0,
                  it1->second.x(), it1->second.y(), 0.0,
                  it2->second.x(), it2->second.y(), 0.0,
                  t->getVertex(0)->x(), t->getVertex(1)->x(), t->getVertex(2)->x());
          fprintf(uvy, "ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g};\n",
                  it0->second.x(), it0->second.y(), 0.0,
                  it1->second.x(), it1->second.y(), 0.0,
                  it2->second.x(), it2->second.y(), 0.0,
                  t->getVertex(0)->y(), t->getVertex(1)->y(), t->getVertex(2)->y());
          fprintf(uvz, "ST(%g,%g,%g,%g,%g,%g,%g,%g,%g){%g,%g,%g};\n",
                  it0->second.x(), it0->second.y(), 0.0,
                  it1->second.x(), it1->second.y(), 0.0,
                  it2->second.x(), it2->second.y(), 0.0,
                  t->getVertex(0)->z(), t->getVertex(1)->z(), t->getVertex(2)->z());
        }
      }
      // fprintf(uva,"};\n");
      // fclose(uva);
      fprintf(uvx,"};\n");
      fclose(uvx);
      fprintf(uvy,"};\n");
      fclose(uvy);
      fprintf(uvz,"};\n");
      fclose(uvz);
      fprintf(xyzu,"};\n");
      fclose(xyzu);
      fprintf(xyzv,"};\n");
      fclose(xyzv);
      // fprintf(xyzc,"};\n");
      // fclose(xyzc);
      // fprintf(uvm,"};\n");
      // fclose(uvm);
    }
    
    // useful for mesh generators ----------------------------------------
    // Intersection of a circle and a plane
    GPoint GFaceCompound::intersectionWithCircle(const SVector3 &n1, const SVector3 &n2,
                                                 const SVector3 &p, const double &d,
                                                 double uv[2]) const
    {
      SVector3 n = crossprod(n1,n2);
      n.normalize();
      for (int i=-1;i<nbT;i++){
        GFaceCompoundTriangle *ct;
        double U,V;
        if (i == -1) getTriangle(uv[0],uv[1], &ct, U,V);
        else ct = &_gfct[i];
        if (!ct) continue;
        // we have two planes, defined with n1,n2 and t1,t2
        // we have then a direction m = (n1 x n2) x (t1 x t2)
        // and a point p that defines a straight line
        // the point is situated in the half plane defined
        // by direction n1 and point p (exclude the one on the
        // other side)
    
        SVector3 t1  = ct->v2 - ct->v1;
        SVector3 t2  = ct->v3 - ct->v1;
    
        // let us first compute point q to be the intersection
        // of the plane of the triangle with the line L = p + t n1
        // Compute w = t1 x t2 = (a,b,c) and write a point of the plabe as
        // X(x,y,z) = ax + by + cz - (v1_x a + v1_y b + v1_z * c) = 0
        // Then
        // a (p_x + t n1_x) + a (p_y + t n1_y) + c (p_z + t n1_z) - (v1_x a + v1_y b + v1_z * c) = 0
        // gives t
    
        SVector3 w = crossprod(t1,t2);
        double t = dot(ct->v1-p,w)/dot(n1,w);
        SVector3 q = p + n1*t;
    
        // consider the line that intersects both planes in its
        // parametric form
        // X(x,y,z) = q + t m
        // We have now to intersect that line with the sphere
        // (x-p_x)^2 + (y-p_y)^2 + (z-p_z)^2 = d^2
        // Inserting the parametric form of the line into the sphere gives
        // (t m_x + q_x - p_x)^2 + (t m_y + q_y - p_y)^2  + (t m_z + q_z - p_z)^2  = d^2
        //  t^2 (m_x^2 + m_y^2 + m_z^2) + 2 t (m_x (q_x - p_x) + m_y (q_y - p_z) + m_z (q_z - p_z)) + ((q_x - p_x)^2 + (q_y - p_y)^2 + (q_z - p_z)^2 - d^2) = 0
        // t^2 m^2 + 2 t (m . (q-p)) + ((q-p)^2 - d^2) = 0
        // Let us call ta and tb the two roots
        // they correspond to two points s_1 = q + ta m and s_2 = q + tb m
        // we choose the one that corresponds to (s_i - p) . n1 > 0
        SVector3 m = crossprod(w,n);
        const double a = dot(m,m);
        const double b = 2*dot(m,q-p);
        const double c = dot(q-p,q-p) - d*d;
        const double delta = b*b-4*a*c;
    
        if (delta >= 0){
          const double ta = (-b + sqrt(delta)) / (2.*a);
          const double tb = (-b - sqrt(delta)) / (2.*a);
          SVector3 s1 = q + m * ta;
          SVector3 s2 = q + m * tb;
          SVector3 s;
          if (dot(s1-p,n1) > 0){
    	s = s1;
          }
          else if (dot(s2-p,n1) > 0){
    	s = s2;
          }
          else continue;
    
          // we have now to look if the point is inside the triangle
          // s = v1 + u t1 + v t2
          // we know the system has a solution because s is in the plane
          // defined by v1 and v2 we solve then
          // (s - v1) . t1 = u t1^2    + v t2 . t1
          // (s - v1) . t2 = u t1 . t2 + v t2^2
    
          double mat[2][2], b[2],uv[2];
          mat[0][0] = dot(t1,t1);
          mat[1][1] = dot(t2,t2);
          mat[0][1] = mat[1][0] = dot(t1,t2);
          b[0] = dot(s-ct->v1,t1) ;
          b[1] = dot(s-ct->v1,t2) ;
          sys2x2(mat,b,uv);
          // check now if the point is inside the triangle
          if (uv[0] >= -1.e-6 && uv[1] >= -1.e-6 &&
    	  1.-uv[0]-uv[1] >= -1.e-6 ) {
    	SVector3 pp =
    	  ct->p1 * ( 1.-uv[0]-uv[1] ) +
    	  ct->p2 *uv[0] +
    	  ct->p3 *uv[1] ;
    	return GPoint(s.x(),s.y(),s.z(),this,pp);
          }
        }
      }
      GPoint pp(0);
      pp.setNoSuccess();
      Msg::Debug("ARGG no success intersection circle");
      return pp;
    }
    
    #endif