From ba745816add4916fa19fcc5c2c3e1da573bc339d Mon Sep 17 00:00:00 2001
From: Sebastien Blaise <sebastien.blaise@uclouvain.be>
Date: Fri, 27 Nov 2015 12:49:25 +0000
Subject: [PATCH] gmsh: hex recombination: relaxed constraint on quad face
 generation to get higher hex ratios

---
 Mesh/Generator.cpp |   2 +-
 Mesh/yamakawa.cpp  | 254 ++++++++++++++++++++++-----------------------
 2 files changed, 125 insertions(+), 131 deletions(-)

diff --git a/Mesh/Generator.cpp b/Mesh/Generator.cpp
index 82b2008ab7..2b6476758c 100644
--- a/Mesh/Generator.cpp
+++ b/Mesh/Generator.cpp
@@ -535,7 +535,7 @@ static void Mesh3D(GModel *m)
         Recombinator rec;
         rec.execute(gr);
         Supplementary sup;
-        //        sup.execute(gr);
+        sup.execute(gr);
         PostOp post;
         post.execute(gr,0, true); //0: no pyramid, 1: single-step, 2: two-steps (conforming), true: fill non-conformities with trihedra
         nb_elements_recombination += post.get_nb_elements();
diff --git a/Mesh/yamakawa.cpp b/Mesh/yamakawa.cpp
index 7a22b59c94..d7b82b4549 100644
--- a/Mesh/yamakawa.cpp
+++ b/Mesh/yamakawa.cpp
@@ -2302,64 +2302,119 @@ bool Recombinator::valid(Hex &hex,const std::set<MElement*>& parts){
   }
 }
 
-//A face with 4 nodes on the boundary should be close to planar
-//A face with 3 nodes on the boundary should not exist
-bool validFace(MVertex *a, MVertex *b, MVertex *c, MVertex *d){
-    //A face should not have exactly 3 nodes on the same surface
-    //or the face tag is undefined
-  //A better option would be to topologically check that the fase has not  4 nodes
-  //on the surface while the face is not no the surface (face2elems map)
-    MVertex *v[4] = {a, b, c, d};
-    std::map<GFace*, int> nbTagOccurences;
-    int maxOccurences = 0;
-    for (int iV = 0; iV < 4; iV++){
-      if (v[iV]->onWhat()->dim() < 3){
-        std::list<GFace*> faces;
-        if (v[iV]->onWhat()->dim() == 2)
-          faces.push_back((GFace*) v[iV]->onWhat());
-        else
-          faces = v[iV]->onWhat()->faces();
-        //        printf("nbFaces %i\n",faces.size());
-        for (std::list<GFace*>::iterator it=faces.begin(); it != faces.end(); it++){
-          GFace* idFace = (*it);
-          //          printf("Id is %p %i\n",idFace,idFace->getNativeInt());
-          if (nbTagOccurences.find(idFace)==nbTagOccurences.end())
-            nbTagOccurences[idFace] = 1;
-          else
-            nbTagOccurences[idFace]++;
-          maxOccurences = std::max(nbTagOccurences[idFace], maxOccurences);
-        } 
-      }
-    }
-    if (maxOccurences == 3) return false;
-
-
-
-    //Geometry: A face with 4 nodes on the boundary should be close enough to planar
-    int nbBndNodes = 0;
-    if (a->onWhat()->dim() < 3) nbBndNodes++;
-    if (b->onWhat()->dim() < 3) nbBndNodes++;
-    if (c->onWhat()->dim() < 3) nbBndNodes++;
-    if (d->onWhat()->dim() < 3) nbBndNodes++;
-    if (nbBndNodes == 4){
-      SVector3 vec1 = SVector3(b->x()-a->x(),b->y()-a->y(),b->z()-a->z()).unit();
-      SVector3 vec2 = SVector3(c->x()-a->x(),c->y()-a->y(),c->z()-a->z()).unit();
-      SVector3 vec3 = SVector3(d->x()-a->x(),d->y()-a->y(),d->z()-a->z()).unit();
-      
-      SVector3 crossVec1Vec2 = crossprod(vec1, vec2);
-      double angle = fabs(acos(dot(crossVec1Vec2, vec3))*180/M_PI);
-      double maxAngle = 15;
-      if (fabs(angle-90) > maxAngle) return false;
-    }
-    //A face with 3 nodes on the boundary should not exist
-    //Constraint should be relaxed
-    if (nbBndNodes == 3){
-      return false;
-    }
-  return true;
-}
+bool validFace(MVertex *a, MVertex *b, MVertex *c, MVertex *d, std::map<MVertex*,std::set<MElement*> > &vertexToElements){
+  std::map<MVertex*,std::set<MElement*> >::iterator itElV[4];
+  MVertex *faceV[4] = {a, b, c, d};
+  for (int iV = 0; iV < 4; iV++){
+    itElV[iV] = vertexToElements.find(faceV[iV]);
+  }
+  size_t tris[4][3] = {{0, 1, 2}, {0, 2, 3}, {0, 1, 3}, {1, 2, 3}};
+  size_t other[4] = {3, 1, 2, 0};
+  size_t nbTris[4];
+  std::set<MElement*> buf1, buf2;
+  for (int iTri = 0; iTri < 4; iTri++){
+    //We count the number of elements sharing the considered triangle
+    buf1.clear();
+    std::set_intersection(itElV[tris[iTri][0]]->second.begin(),itElV[tris[iTri][0]]->second.end(),itElV[tris[iTri][1]]->second.begin(),itElV[tris[iTri][1]]->second.end(),std::inserter(buf1,buf1.end()));
+    buf2.clear();
+    std::set_intersection(buf1.begin(),buf1.end(),itElV[tris[iTri][2]]->second.begin(),itElV[tris[iTri][2]]->second.end(),std::inserter(buf2,buf2.end()));
+    buf1.clear();
+    std::set_difference(buf2.begin(),buf2.end(),itElV[other[iTri]]->second.begin(),itElV[other[iTri]]->second.end(),std::inserter(buf1,buf1.end()));
+    nbTris[iTri] = buf1.size();
+  }
+
+
+  bool valid = false;
+  // All sub-faces inside (2 elements)
+  if (nbTris[0]== 2 && nbTris[1] == 2 && nbTris[2] == 0 && nbTris[3] == 0) valid = true;
+  else if (nbTris[0]== 0 && nbTris[1] == 0 && nbTris[2] == 2 && nbTris[3] == 2) valid = true;
+  // All sub-faces on the surface or facing a quad face (1 element)
+  else if (nbTris[0]== 1 && nbTris[1] == 1 && nbTris[2] == 0 && nbTris[3] == 0) valid = true;
+  else if (nbTris[0]== 0 && nbTris[1] == 0 && nbTris[2] == 1 && nbTris[3] == 1) valid = true;
+  // Face is made of triangles on each side but they are nonconforming (OK for recombination, but why do these exist??)
+  else if (nbTris[0]== 1 && nbTris[1] == 1 && nbTris[2] == 1 && nbTris[3] == 1) valid = true;
+
+  //Geometry: A face with 4 nodes on the boundary should be close enough to planar
+  int nbBndNodes = 0;
+  if (a->onWhat()->dim() < 3) nbBndNodes++;
+  if (b->onWhat()->dim() < 3) nbBndNodes++;
+  if (c->onWhat()->dim() < 3) nbBndNodes++;
+  if (d->onWhat()->dim() < 3) nbBndNodes++;
+  if (nbBndNodes == 4){
+    SVector3 vec1 = SVector3(b->x()-a->x(),b->y()-a->y(),b->z()-a->z()).unit();
+    SVector3 vec2 = SVector3(c->x()-a->x(),c->y()-a->y(),c->z()-a->z()).unit();
+    SVector3 vec3 = SVector3(d->x()-a->x(),d->y()-a->y(),d->z()-a->z()).unit();
+    
+    SVector3 crossVec1Vec2 = crossprod(vec1, vec2);
+    double angle = fabs(acos(dot(crossVec1Vec2, vec3))*180/M_PI);
+    double maxAngle = 15;
+    if (fabs(angle-90) > maxAngle) valid=false;
+  }
+  
+  return valid;
+
+
+}
+
+
+////A face with 4 nodes on the boundary should be close to planar
+////A face with 3 nodes on the boundary should not exist
+//bool validFace(MVertex *a, MVertex *b, MVertex *c, MVertex *d, std::map<MVertex*,std::set<MElement*> > &vertexToElements){
+//    //A face should not have exactly 3 nodes on the same surface
+//    //or the face tag is undefined
+//  //A better option would be to topologically check that the fase has not  4 nodes
+//  //on the surface while the face is not no the surface (face2elems map)
+//    MVertex *v[4] = {a, b, c, d};
+//    std::map<GFace*, int> nbTagOccurences;
+//    int maxOccurences = 0;
+//    for (int iV = 0; iV < 4; iV++){
+//      if (v[iV]->onWhat()->dim() < 3){
+//        std::list<GFace*> faces;
+//        if (v[iV]->onWhat()->dim() == 2)
+//          faces.push_back((GFace*) v[iV]->onWhat());
+//        else
+//          faces = v[iV]->onWhat()->faces();
+//        //        printf("nbFaces %i\n",faces.size());
+//        for (std::list<GFace*>::iterator it=faces.begin(); it != faces.end(); it++){
+//          GFace* idFace = (*it);
+//          //          printf("Id is %p %i\n",idFace,idFace->getNativeInt());
+//          if (nbTagOccurences.find(idFace)==nbTagOccurences.end())
+//            nbTagOccurences[idFace] = 1;
+//          else
+//            nbTagOccurences[idFace]++;
+//          maxOccurences = std::max(nbTagOccurences[idFace], maxOccurences);
+//        } 
+//      }
+//    }
+//    if (maxOccurences == 3) return false;
+//
+//
+//
+//    //Geometry: A face with 4 nodes on the boundary should be close enough to planar
+//    int nbBndNodes = 0;
+//    if (a->onWhat()->dim() < 3) nbBndNodes++;
+//    if (b->onWhat()->dim() < 3) nbBndNodes++;
+//    if (c->onWhat()->dim() < 3) nbBndNodes++;
+//    if (d->onWhat()->dim() < 3) nbBndNodes++;
+//    if (nbBndNodes == 4){
+//      SVector3 vec1 = SVector3(b->x()-a->x(),b->y()-a->y(),b->z()-a->z()).unit();
+//      SVector3 vec2 = SVector3(c->x()-a->x(),c->y()-a->y(),c->z()-a->z()).unit();
+//      SVector3 vec3 = SVector3(d->x()-a->x(),d->y()-a->y(),d->z()-a->z()).unit();
+//      
+//      SVector3 crossVec1Vec2 = crossprod(vec1, vec2);
+//      double angle = fabs(acos(dot(crossVec1Vec2, vec3))*180/M_PI);
+//      double maxAngle = 15;
+//      if (fabs(angle-90) > maxAngle) return false;
+//    }
+//    //A face with 3 nodes on the boundary should not exist
+//    //Constraint should be relaxed
+//    if (nbBndNodes == 3){
+//      return false;
+//    }
+//  return true;
+//}
 
-bool validFaces(Hex &hex){
+bool validFaces(Hex &hex, std::map<MVertex*,std::set<MElement*> > &vertexToElements){
   bool v1, v2, v3, v4, v5, v6;
   MVertex *a,*b,*c,*d;
   MVertex *e,*f,*g,*h;
@@ -2373,40 +2428,16 @@ bool validFaces(Hex &hex){
   g = hex.get_g();
   h = hex.get_h();
 
-  v1 = validFace(a,b,c,d);  //SHOULD CHECK GEOMETRY
-  v2 = validFace(e,f,g,h);
-  v3 = validFace(a,b,f,e);
-  v4 = validFace(b,c,g,f);
-  v5 = validFace(d,c,g,h);
-  v6 = validFace(d,a,e,h);
+  v1 = validFace(a,b,c,d,vertexToElements);  //SHOULD CHECK GEOMETRY
+  v2 = validFace(e,f,g,h,vertexToElements);
+  v3 = validFace(a,b,f,e,vertexToElements);
+  v4 = validFace(b,c,g,f,vertexToElements);
+  v5 = validFace(d,c,g,h,vertexToElements);
+  v6 = validFace(d,a,e,h,vertexToElements);
 
   return v1 && v2 && v3 && v4 && v5 && v6;
 }
 
-bool validCondNum(Hex &hex){
-  return true;
-  CondNumBasis basis(MSH_HEX_8);
-  fullMatrix<double> nodesXYZ(8,3);
-  for (int iV = 0; iV < 8; iV++){
-    nodesXYZ(iV,0) = hex.getVertex(iV)->x();
-    nodesXYZ(iV,1) = hex.getVertex(iV)->y();
-    nodesXYZ(iV,2) = hex.getVertex(iV)->z();
-  }
-  fullMatrix<double> normals(8,3);
-  fullVector<double> invCondNum(basis.getNumCondNumNodes());
-  basis.getSignedInvCondNum(nodesXYZ, normals, invCondNum);
-  for (int i=0; i<invCondNum.size(); i++){
-    if (invCondNum(i) * invCondNum(0) <= 0){
-      //      nodesXYZ.print("HEX nodes");
-      //      invCondNum.print("HEX invCondNum");
-      //      Msg::Error("Wrong Hex");
-      return false;
-    }
-  }
-  return true;
-}
-
-
 // renvoie true si le "MQuadrangle::etaShapeMeasure" des 6 faces est plus grand que 0.000001
 bool Recombinator::valid(Hex &hex){
   double k;
@@ -2434,7 +2465,7 @@ bool Recombinator::valid(Hex &hex){
   eta6 = eta(d,c,g,h);
 
   if(eta1>k && eta2>k && eta3>k && eta4>k && eta5>k && eta6>k){
-    return (validFaces(hex) && validCondNum(hex));
+    return validFaces(hex,vertex_to_elements);
   }
   else{
     return false;
@@ -4093,7 +4124,7 @@ bool Supplementary::valid(Prism prism,const std::set<MElement*>& parts){
 }
 
 
-bool validFaces(Prism &prism){
+bool validFaces(Prism &prism, std::map<MVertex*,std::set<MElement*> > &vertexToElements){
   bool v1, v2, v3;
   MVertex *a,*b,*c;
   MVertex *d, *e,*f;
@@ -4105,51 +4136,14 @@ bool validFaces(Prism &prism){
   e = prism.get_e();
   f = prism.get_f();
 
-  v1 = validFace(a,d,f,c);  //SHOULD CHECK GEOMETRY
-  v2 = validFace(a,d,e,b);
-  v3 = validFace(b,c,e,f);
+  v1 = validFace(a,d,f,c,vertexToElements);  //SHOULD CHECK GEOMETRY
+  v2 = validFace(a,d,e,b,vertexToElements);
+  v3 = validFace(b,c,e,f,vertexToElements);
 
   return v1 && v2 && v3;
 }
 
 
-bool validCondNum(Prism &prism){
-  return true;
-  CondNumBasis basis(MSH_PRI_6);
-  fullMatrix<double> nodesXYZ(6,3);
-  nodesXYZ(0,0) = prism.get_a()->x();
-  nodesXYZ(0,1) = prism.get_a()->y();
-  nodesXYZ(0,2) = prism.get_a()->z();
-  nodesXYZ(1,0) = prism.get_b()->x();
-  nodesXYZ(1,1) = prism.get_b()->y();
-  nodesXYZ(1,2) = prism.get_b()->z();
-  nodesXYZ(2,0) = prism.get_c()->x();
-  nodesXYZ(2,1) = prism.get_c()->y();
-  nodesXYZ(2,2) = prism.get_c()->z();
-  nodesXYZ(3,0) = prism.get_d()->x();
-  nodesXYZ(3,1) = prism.get_d()->y();
-  nodesXYZ(3,2) = prism.get_d()->z();
-  nodesXYZ(4,0) = prism.get_e()->x();
-  nodesXYZ(4,1) = prism.get_e()->y();
-  nodesXYZ(4,2) = prism.get_e()->z();
-  nodesXYZ(5,0) = prism.get_f()->x();
-  nodesXYZ(5,1) = prism.get_f()->y();
-  nodesXYZ(5,2) = prism.get_f()->z();
-
-  fullMatrix<double> normals(6,3);
-  fullVector<double> invCondNum(basis.getNumCondNumNodes());
-  basis.getSignedInvCondNum(nodesXYZ, normals, invCondNum);
-  for (int i=0; i<invCondNum.size(); i++){
-    if (invCondNum(i) * invCondNum(0) <= 0){
-      //      nodesXYZ.print("HEX nodes");
-      //      invCondNum.print("HEX invCondNum");
-      //      Msg::Error("Wrong PRISM");
-      return false;
-    }
-  }
-  return true;
-}
-
 
 bool Supplementary::valid(Prism prism){
   double k;
@@ -4171,7 +4165,7 @@ bool Supplementary::valid(Prism prism){
   eta3 = eta(b,c,f,e);
 
   if(eta1>k && eta2>k && eta3>k){
-    return (validFaces(prism) && validCondNum(prism));
+    return validFaces(prism, vertex_to_tetrahedra);
   }
   else{
     return 0;
-- 
GitLab