From e917b465c3b9e69ce7b58bd74691357262d6aea0 Mon Sep 17 00:00:00 2001
From: SYLVAND Guillaume <guillaume.sylvand@airbus.com>
Date: Mon, 10 Feb 2025 18:25:02 +0100
Subject: [PATCH] sync with test_FEMBEM@airbus + cosmetics

- different way to create pipe mesh to have a correct (=conformal) mesh
  Note: it changes (lowers) the number of non-zeros in the FEM matrices
---
 include/main.h     |  7 +++-
 include/mpf_simd.h |  4 +++
 src/cylinder.c     | 75 +++++++++++++++++++++-----------------
 src/hchameleon.c   |  2 ++
 src/help.c         |  3 +-
 src/kernel.c       |  6 ++--
 src/main.c         | 12 ++++++-
 src/pipe.c         | 89 ++++++++++++++++++++++++++++------------------
 src/prepareTEST.c  |  2 +-
 src/rhs.c          |  5 +--
 src/testHMAT.c     |  2 +-
 11 files changed, 131 insertions(+), 76 deletions(-)
 create mode 100644 include/mpf_simd.h

diff --git a/include/main.h b/include/main.h
index aab2feb..5716f25 100644
--- a/include/main.h
+++ b/include/main.h
@@ -12,6 +12,7 @@
 #endif
 /* ================================================================================= */
 #include <math.h>
+#include <complex.h>
 #include "mpi.h"
 #ifdef HAVE_HMAT
 #include "hmat/hmat.h"
@@ -127,6 +128,11 @@ extern int writeMesh;
 /*! \brief  Write a UNV file of the mesh. Enabled with option --write-mesh-unv. */
 extern int writeMeshUNV;
 
+/*! \brief  Create a conformal volume mesh by using alternatively 2 ways of splitting hexa into 5 tetra.
+
+  Disabled with --no-conformal-mesh, enabled with --conformal-mesh. Default is enabled. */
+extern int conformalMesh;
+
 /*! \brief  List of  algorithms that we can test. */
 enum algo {
   _undefined,
@@ -246,7 +252,6 @@ int computeRelativeError(void *sol, void *ref, double *eps) ;
 int computeVecNorm(void *ref, double *norm);
 int sommePara(double *buf) ;
 int computeCoordCylinder(int i, double *coord) ;
-int computeCoordCylinderDetail(int i, double *coord) ;
 int initCylinder(int *argc, char ***argv) ;
 int initCylinderDetail(int *argc, char ***argv) ;
 int getMeshStep(double *m) ;
diff --git a/include/mpf_simd.h b/include/mpf_simd.h
new file mode 100644
index 0000000..7889386
--- /dev/null
+++ b/include/mpf_simd.h
@@ -0,0 +1,4 @@
+// Replacement for MPF macros & routines for SIMD optimisations
+#define MPF_SIMD_DISP(x) x
+#define mpf_simd_cexp cexp
+#define mpf_simd_sincos(theta,s,c) {*s=sin(theta);*(c)=cos(theta);}
diff --git a/src/cylinder.c b/src/cylinder.c
index d50087c..ccc9e67 100644
--- a/src/cylinder.c
+++ b/src/cylinder.c
@@ -1,6 +1,5 @@
 #include "main.h"
-
-/* ============================================================================================================ */
+#include <mpf_simd.h>
 
 /*! \brief Radius of the cylinder */
 double radius = 2.0 ;
@@ -49,40 +48,25 @@ inline static int computeCoordCylinderMain(int i, double *coord) {
     /* zStep and angleStep are defined by :
        angleStep*radius = zStep*nbPtsLoop (= meshStep)
        angleStep*nbPtsLoop = 2.pi */
-    zStep=height/(double)nbPtsMain ;
+      zStep=height/(double)(nbPtsMain-1) ;
     angleStep=sqrt(zStep*2.*M_PI/radius) ;
     meshStep=zStep*2.*M_PI/angleStep ;
 
     nbPtsLoop=floor(2.*M_PI/angleStep) ; // Used to write VTK file
+    // Once I have 'nbPtsLoop' as an integer, I recompute meshStep and angleStep
+    angleStep = 2.*M_PI/(double)nbPtsLoop;
+    meshStep = angleStep*radius;
   }
 
   theta = (double)i * angleStep ;
-  coord[0] = radius * sin(theta) ;
-  coord[1] = radius * cos(theta) ;
+  mpf_simd_sincos(theta, coord, coord+1);
+  coord[0] *= radius;
+  coord[1] *= radius;
   coord[2] = (double)i*zStep ;
 
   return 0 ;
 }
 
-/*! \brief Computes coordinates of point number 'i' on the object (main cylinder or detail).
-
-  \a i should be in [0, nbPts[.
-  \param i the number of the point in the detail (input)
-  \param coord the coordinates x y z of this point (output)
-  \return 0 for success
-*/
-int computeCoordCylinder(int i, double *coord) {
-  ASSERTQ(i<nbPtsBEM && i>=0);
-  if (testedModel == _fembem)
-    return computeCoordPipe(i, coord);
-  if (useDetail && i >= nbPtsMain) {
-    i = i - nbPtsMain;
-    return computeCoordCylinderDetail(i, coord);
-  } else {
-    return computeCoordCylinderMain(i, coord);
-  }
-}
-
 /*! \brief Computes coordinates of point number 'i' of the detail placed on the main cylinder.
 
   The points are computed on a cylinder defined by 'heightDetail' and 'radiusDetail' (values in number of wavelength).
@@ -93,39 +77,63 @@ int computeCoordCylinder(int i, double *coord) {
   \param coord the coordinates x y z of this point (output)
   \return 0 for success
 */
-int computeCoordCylinderDetail(int i, double *coord) {
+inline static int computeCoordCylinderDetail(int i, double *coord) {
   ASSERTQ(testedModel == _bem);
   static double angleStep = 0. ;
   static double zStep = 0. ;
+  static double heightD = 0.;
+  static double radiusD = 0.;
   double theta ;
 
-  double heightD = lambda * heightDetail ;
-  double radiusD = lambda * radiusDetail ;
-
   if (i<0 || i>=(nbPts - nbPtsMain)) {
     SETERRQ(1, "Incorrect unknown number %d, should be in [0, %d[\n", i, nbPts - nbPtsMain) ;
   }
 
   if (angleStep == 0.) {
+      heightD = lambda * heightDetail ;
+      radiusD = lambda * radiusDetail ;
     /* zStep and angleStep are defined by :
        angleStep*radius = zStep*nbPtsLoopDetail (= meshStep)
        angleStep*nbPtsLoopDetail = 2.pi */
-    zStep = heightD/(double)(nbPts - nbPtsMain) ;
+    zStep = heightD/(double)(nbPts - nbPtsMain-1) ;
     angleStep = sqrt(zStep*2.*M_PI/radiusD) ;
     meshStepDetail = zStep*2.*M_PI/angleStep ;
 
     nbPtsLoopDetail=floor(2.*M_PI/angleStep) ; // Used to write VTK file
+    // Once I have 'nbPtsLoopDetail' as an integer, I recompute meshStepDetail and angleStep
+    angleStep = 2.*M_PI/(double)nbPtsLoopDetail;
+    meshStepDetail = angleStep*radius;
   }
 
   /* Le detail est oriente selon y, perpendiculaire au cylindre principal, a mi-hauteur */
   theta = (double)i * angleStep ;
-  coord[0] = radiusD * sin(theta) ;
-  coord[1] = zStep * (double)(i+1) + radius ;
-  coord[2] = radiusD * cos(theta) + 0.5 * height ;
+  mpf_simd_sincos(theta, coord, coord+2);
+  coord[0] *= radiusD ;
+  coord[1] = zStep * (double)(i+1) + radius ; // 'i+1' instead of 'i' to avoid that the detail touches the main cylinder
+  coord[2] = radiusD * coord[2] + 0.5 * height ;
 
   return 0 ;
 }
 
+/*! \brief Computes coordinates of point number 'i' on the object (main cylinder or detail).
+
+  \a i should be in [0, nbPts[.
+  \param i the number of the point in the detail (input)
+  \param coord the coordinates x y z of this point (output)
+  \return 0 for success
+*/
+MPF_SIMD_DISP(int computeCoordCylinder(int i, double *coord)) {
+    ASSERTQ(i<nbPtsBEM && i>=0);
+    if (testedModel == _fembem)
+        return computeCoordPipe(i, coord);
+    if (useDetail && i >= nbPtsMain) {
+        i = i - nbPtsMain;
+        return computeCoordCylinderDetail(i, coord);
+    } else {
+        return computeCoordCylinderMain(i, coord);
+    }
+}
+
 double* createCylinder(void) {
   double* result = MpfCalloc((size_t)3 * nbPts, sizeof(double));
   int i, ierr;
@@ -325,8 +333,9 @@ int initCylinderDetail(int *argc, char ***argv) {
 
   /* Nombre de points sur le detail = superficie du cylindre (2.pi.R.h.lambda^2) divise par superficie d'une "maille" (step^2) */
   nbPts += ceil(2*M_PI*lambda*lambda*heightDetail*radiusDetail/(step*step));
+  nbPtsBEM = nbPts ;
 
-  /* Calcule les coordonnees d'un point, afin de fixer meshStepDetail (qui peut servir a fixer lambda) */
+  /* Calcule les coordonnees d'un point, afin de fixer meshStepDetail */
   ierr = computeCoordCylinder(nbPtsMain, &(coord[0]) ) ; CHKERRQ(ierr) ;
 
   return 0 ;
diff --git a/src/hchameleon.c b/src/hchameleon.c
index f812833..04c6c83 100644
--- a/src/hchameleon.c
+++ b/src/hchameleon.c
@@ -1,3 +1,5 @@
+#include "config.h"
+
 #if defined(HAVE_MKL_H)
 #include <mkl.h>
 #else
diff --git a/src/help.c b/src/help.c
index 9ed0ba4..41006ee 100644
--- a/src/help.c
+++ b/src/help.c
@@ -10,6 +10,7 @@ int printHelp() {
          "     -nbpts: Number of unknowns of the global linear system. Default: 16000 (can be in floating point notation, like 1.2e4).\n"
          "     -nbrhs: Number of right-hand sides of the global linear system. Default: 1.\n"
          "     -s/-d/-c/-z: Scalar type of the computation (real computation uses 1/R kernel in BEM, complex computation uses oscillatory exp(ikr)/r kernel). Default: z.\n"
+         "     --partial-fembem: Using partial interactions between BEM and FEM unknowns. Default is NO."
          "     -v: Verbose mode, to display values of data (extremely verbose). Default is OFF.\n"
          "     -sparserhs: Sparsity of the right hand sides (1 non-zero value every 'n' values). Default is nbpts/80.log10(nbpts).\n"
          "     --new-rhs/--no-new-rhs: Use (or not) new way to compute RHS (old way was producing low rank RHS). Default is ON.\n"
@@ -24,7 +25,7 @@ int printHelp() {
          "     -height: Height of the cylinder. Default is 4.\n"
          "     -with_detail: add a finely meshed cylionder detail. Default is NO.\n"
          "     -radius_detail: Radius of the cylinder detail. Default is 0.02.\n"
-         "     -height: Height of the cylinder detail. Default is 0.5.\n"
+         "     -height_detail: Height of the cylinder detail. Default is 0.5.\n"
          "     -ptsperlambda_detail: Points per wavelength on the detail. Default is '10 times finer than on the main cylinder'.\n"
          "\n"
          "     FEM and FEM/BEM tests use a volumic cylinder, defined with:\n"
diff --git a/src/kernel.c b/src/kernel.c
index 5415bd0..ae28e7a 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -1,6 +1,8 @@
 #include "main.h"
+#include <mpf_simd.h>
+
 extern double meshStep;
-int computeKernelBEM(double *coord1, double *coord2, int self, double complex *kernel) {
+MPF_SIMD_DISP(int computeKernelBEM(double *coord1, double *coord2, int self, double complex *kernel)) {
   double r, v[3], k=0. ;
 
   v[0]=coord1[0]-coord2[0] ;
@@ -11,7 +13,7 @@ int computeKernelBEM(double *coord1, double *coord2, int self, double complex *k
 
   if (complexALGO) {
     k=2*M_PI/lambda;
-    *kernel = cexp(I * k * r) / (4 * M_PI * r);
+    *kernel = mpf_simd_cexp(I * k * r) / (4 * M_PI * r);
   } else
     *kernel = 1 / (4 * M_PI * r);
 
diff --git a/src/main.c b/src/main.c
index 0d6efda..6182774 100644
--- a/src/main.c
+++ b/src/main.c
@@ -38,6 +38,7 @@ double ptsPerLambdaDetail = -1.;
 double radiusLeaf = 0.;
 int writeMesh = 0;
 int writeMeshUNV = 0;
+int conformalMesh=1;
 enum algo testedAlgo = _undefined;
 int use_simple_assembly = 0;
 int divider = 2;
@@ -221,6 +222,15 @@ int main(int argc, char **argv) {
     writeMeshUNV=1;
     Mpf_printf(MPI_COMM_WORLD, "Activate writing of UNV file 'mesh.unv'\n") ;
   }
+  /* --- Create (or not) a conformal volume mesh in pipe.c --- */
+  if (MpfArgHasName(&argc, argv, 1, "--conformal-mesh")) {
+      conformalMesh=1;
+      Mpf_printf(MPI_COMM_WORLD, "Create a conformal volume mesh.\n") ;
+  }
+  if (MpfArgHasName(&argc, argv, 1, "--no-conformal-mesh")) {
+      conformalMesh=0;
+      Mpf_printf(MPI_COMM_WORLD, "Do not create a conformal volume mesh (old way)\n") ;
+  }
 
   switch(testedModel){
     case _bem: // 2D mesh
@@ -254,7 +264,7 @@ int main(int argc, char **argv) {
   if (MpfArgHasName(&argc, argv, 1, "-with_detail")) {
     useDetail = 1;
     ASSERTQ(testedModel==_bem); // detail is not (yet) compatible with FEM
-    Mpf_printf(MPI_COMM_WORLD, "Add an overmeshed detail");
+    Mpf_printf(MPI_COMM_WORLD, "Add an overmeshed detail\n");
   }
 
   /* Wavelength */
diff --git a/src/pipe.c b/src/pipe.c
index b96e6f7..0041763 100644
--- a/src/pipe.c
+++ b/src/pipe.c
@@ -50,9 +50,10 @@ int computeCoordPipe(int i, double *coord) {
 
   if (angleStep==0.) {
     /* The pipe is defined as follows: its height is 'height', the points are located between 'radius'
-       and 'radius/2'. We have n_r, n_z, n_l points in the direction z, radial and orthoradial.
+       and 'radius/2'. We have n_r, n_z, n_l points in the direction radial, z and orthoradial.
        total number of points nbPts=n_r.n_z.n_l
        2.pi*radius/n_l = heigth/n_z = (radius/2)/n_r = meshStep
+       (rigorously, we should solve: 2.pi*radius/n_l = heigth/(n_z-1) = (radius/2)/(n_r-1) = meshStep)
        We compute the number of points as double and round them up to integers.
        angleStep*n_l = 2.pi
        zStep=heigth/(n_z.n_l)
@@ -61,8 +62,10 @@ int computeCoordPipe(int i, double *coord) {
     double dn_r=radius*dn_z/2./height;
     double dn_l=4.*M_PI*dn_r;
     n_l=(int)ceil(dn_l);
+    if (conformalMesh && n_l%2 == 0) n_l++; // To create a conformal mesh, n_l must be odd
     n_r=(int)ceil(dn_r);
     n_z=(int)ceil(dn_z);
+    while(n_l*n_r*n_z < nbPts) n_z++;
     meshStep=height/(double)n_z;
     zStep=meshStep/(double)n_l;
     angleStep=2.*M_PI/(double)n_l;
@@ -142,11 +145,15 @@ int freeFemMatrix() {
 }
 
 int createFemMatrix() {
-  int nbHexa=0, ierr, rank, iVtk;
-  int defTetra[5][4]= { {0,1,2,4}, {1,4,5,7}, {1,2,3,7}, {2,4,6,7}, {1,2,4,7}};
+  int ierr, rank, iVtk;
+  // in a conformal mesh, we alternatively have 'A' and 'B' hexa to match diagonal edges between consecutive hexa
+  // in a non-conformal mesh (the old way), we use only 'A' hexa
+  // we can switch between the 2 types with --conformal-mesh and --no-conformal-mesh, conformal is the default
+  int defTetra[2][5][4]= { { {0,1,2,4}, {1,4,5,7}, {1,2,3,7}, {2,4,6,7}, {1,2,4,7}}, // 'A' hexa: face 0246 is split along 24
+                            { {0,1,3,5}, {0,2,3,6}, {0,4,5,6}, {3,5,6,7}, {0,3,5,6}} }; // 'B' hexa: face 0246 is split along 06
   FILE *fvtk=NULL, *funv=NULL;
   int nbTetra=0, nbTria=0;
-
+  fpos_t posNbTetra;
   ierr = MPI_Comm_rank(MPI_COMM_WORLD, &rank);
   if (rank==0) {
     if (writeMesh) {
@@ -156,14 +163,16 @@ int createFemMatrix() {
       fprintf(fvtk, "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" "
                     "byte_order=\"LittleEndian\" compressor=\"vtkZLibDataCompressor\">\n");
       fprintf(fvtk, "\t<UnstructuredGrid>\n");
-      fprintf(fvtk, "\t\t<Piece NumberOfPoints=\"%d\" NumberOfCells=\"%d\">\n",
-              n_r*n_z*n_l, 5*(n_r*n_l*n_z-n_l*n_z-(n_r-1)*(n_l+1)));
+      fprintf(fvtk, "\t\t<Piece NumberOfPoints=\"%d\" NumberOfCells=\"", nbPts);
+      fgetpos(fvtk, &posNbTetra); // we will write the exact number of tetra later, within the empty space left below
+      fprintf(fvtk, "                    >\n");
+
       // Write the vertices
       fprintf(fvtk, "\t\t\t<Points>\n");
       fprintf(fvtk, "\t\t\t\t<DataArray type=\"Float32\""
                     " NumberOfComponents=\"3\" format=\"ascii\">\n");
       double coord[3];
-      for (iVtk = 0; iVtk < n_r*n_z*n_l; iVtk++) {
+      for (iVtk = 0; iVtk < nbPts; iVtk++) {
         ierr = computeCoordPipe(iVtk, coord); CHKERRQ(ierr);
         fprintf(fvtk, "%g %g %g\n", (float) (coord[0]), (float)(coord[1]), (float)(coord[2]));
       }
@@ -191,7 +200,7 @@ int createFemMatrix() {
       fprintf(funv, "%s\n",sUNV_SEPARATOR ) ;
       fprintf(funv, "%s\n", sNODE_UNV_ID );
       double coord[3];
-      for(int i = 1; i<=n_r*n_z*n_l; i++ ) {
+      for(int i = 1; i<=nbPts; i++ ) {
         ierr = computeCoordPipe(i-1, coord); CHKERRQ(ierr);
         fprintf(funv, "%10d%s\n", i, sNODE_UNV_DESCR );
         fprintf(funv, "%25.16E%25.16E%25.16E\n", coord[0], coord[1], coord[2]) ;
@@ -219,10 +228,10 @@ int createFemMatrix() {
       // the outer face is based on points i_face, i_face+1, i_face+n_l, i_face+n_l+1, they must be in [0, n_z.n_l-1]
       if (i_face+n_l+1 > n_z*n_l-1) continue;
 
-      nbHexa++;
+      // In a conformal mesh, we use alternatively the 2 types of hexa defined in defTetra[]
+      int typeHexa = conformalMesh ? (i_face+i_helix)%2 : 0;
 
-      /* Vertices of the current hexaedre, in [0..nbPts[
-    */
+      /* Vertices of the current hexaedre, in [0..nbPts[ */
       int i_vert[8];
       i_vert[0] = i_helix * n_z*n_l + i_face; // actually it is i_hexa
       i_vert[1] = i_helix * n_z*n_l + i_face+1;
@@ -240,34 +249,33 @@ int createFemMatrix() {
         int i_vert_t[4]; // vertices of the current tetraedron, in [0..nbPts[
         int i, j;
         for (i=0 ; i<4 ; i++)
-          i_vert_t[i] = i_vert[defTetra[i_tetra][i]];
+          i_vert_t[i] = i_vert[defTetra[typeHexa][i_tetra][i]];
 
         // Loop on the unkowns (=vertices) in this tetrahedron
         // Each unknown interacts with the other unknown in the same tetrahedron
         // Value of the interaction is 0.1 for self, 0.05 otherwise
-        for (i=0 ; i<4 ; i++)
-          if (i_vert_t[i] >= 0 && i_vert_t[i] < nbPts)
+        if (i_vert_t[0] >= 0 && i_vert_t[0] < nbPts && i_vert_t[1] >= 0 && i_vert_t[1] < nbPts && i_vert_t[2] >= 0 && i_vert_t[2] < nbPts && i_vert_t[3] >= 0 && i_vert_t[3] < nbPts) {
+          nbTetra++;
+          for (i=0 ; i<4 ; i++)
             for (j=0 ; j<4 ; j++)
-              if (i_vert_t[j] >= 0 && i_vert_t[j] < nbPts)
-                addValue(i_vert_t[i], i_vert_t[j], i==j ? 0.1 : 0.05);
-
-        if (writeMesh)
-          fprintf(fvtk, "%d %d %d %d\n", i_vert_t[0], i_vert_t[1], i_vert_t[2], i_vert_t[3]);
-        if (writeMeshUNV) {
-          fprintf(funv, "%10d%s\n", ++nbTetra, sELT_TETRA4_DESC ) ;
-          fprintf(funv, "%10d%10d%10d%10d\n", i_vert_t[0]+1, i_vert_t[1]+1, i_vert_t[2]+1, i_vert_t[3]+1);
+              addValue(i_vert_t[i], i_vert_t[j], i==j ? 0.1 : 0.05);
+
+          if (writeMesh)
+            fprintf(fvtk, "%d %d %d %d\n", i_vert_t[0], i_vert_t[1], i_vert_t[2], i_vert_t[3]);
+          if (writeMeshUNV) {
+            fprintf(funv, "%10d%s\n", nbTetra, sELT_TETRA4_DESC ) ;
+            fprintf(funv, "%10d%10d%10d%10d\n", i_vert_t[0]+1, i_vert_t[1]+1, i_vert_t[2]+1, i_vert_t[3]+1);
+          }
         }
       }
     }
 
     if (writeMesh) {
-      // Check that the number of hexas created matches the one computed at the beginning and written in the vtk header
-      ASSERTQ(nbHexa == n_r*n_l*n_z-n_l*n_z-(n_r-1)*(n_l+1));
       fprintf(fvtk, "\t\t\t\t</DataArray>\n");
       /* Ecriture des offsets */
       fprintf(fvtk, "\t\t\t\t<DataArray "
                     "type=\"Int32\" Name=\"offsets\" format=\"ascii\">\n");
-      for (iVtk = 0; iVtk < nbHexa*5; iVtk++) {
+      for (iVtk = 0; iVtk < nbTetra; iVtk++) {
         fprintf(fvtk, "%d ", iVtk*4+4);
         if (iVtk % 10 == 9)
           fprintf(fvtk, "\n");
@@ -277,7 +285,7 @@ int createFemMatrix() {
       /* Ecriture des types d'elements */
       fprintf(fvtk, "\t\t\t\t<DataArray "
                     "type=\"UInt8\" Name=\"types\" format=\"ascii\">\n");
-      for (iVtk = 0; iVtk < nbHexa*5; iVtk++) {
+      for (iVtk = 0; iVtk < nbTetra; iVtk++) {
         fprintf(fvtk, "%d ", 10); // For VTK_TETRA
         if (iVtk % 10 == 9)
           fprintf(fvtk, "\n");
@@ -285,27 +293,40 @@ int createFemMatrix() {
       fprintf(fvtk, "\t\t\t\t</DataArray>\n");
       fprintf(fvtk, "\t\t\t</Cells>\n");
       fprintf(fvtk, "\t\t</Piece>\n\t</UnstructuredGrid>\n</VTKFile>\n");
+
+      fsetpos(fvtk, &posNbTetra); // Write a-posteriori the exact number of hexa
+      fprintf(fvtk, "%d\"", nbTetra) ;
+
       fclose(fvtk);
       Mpf_printf(MPI_COMM_WORLD, "Done writing 'mesh.vtu'\n") ;
     }
     if (writeMeshUNV) {
       // Add the triangles on the outer helix
+      // Like for tetra, the shape of triangles depends on conformal/non-conformal mesh
+      int defTria[2][2][3]= { { {0,1,2}, {2,1,3}}, // 'A' hexa
+                              { {0,1,3}, {2,0,3}} }; // 'B' hexa
       for (int i_face=0 ; i_face<n_z*n_l ; i_face++) {
         // the outer face is based on points i_face, i_face+1, i_face+n_l, i_face+n_l+1, they must be in [0, n_z.n_l-1]
         if (i_face+n_l+1 > n_z*n_l-1) continue;
 
-        /* Vertices of the current outer triangles in [0..n_z*n_l[
-    */
+        // In a conformal mesh, we use alternatively the 2 types of hexa defined in defTetra[]
+        int typeHexa = conformalMesh ? i_face%2 : 0;
+
+        /* Vertices of the current outer triangles in [0..n_z*n_l[ */
         int i_vert[4];
         i_vert[0] = i_face;
         i_vert[1] = i_face+1;
         i_vert[2] = i_face+n_l;
         i_vert[3] = i_face+n_l+1;
-        // Add the 2 triangles using vertices 0,1,2 and 2,1,3 (inward normal)
-        fprintf(funv, "%10d%s\n", ++nbTria + nbTetra, sELT_TRIA3_DESC ) ;
-        fprintf(funv, "%10d%10d%10d\n", i_vert[0]+1, i_vert[1]+1, i_vert[2]+1);
-        fprintf(funv, "%10d%s\n", ++nbTria + nbTetra, sELT_TRIA3_DESC ) ;
-        fprintf(funv, "%10d%10d%10d\n", i_vert[2]+1, i_vert[1]+1, i_vert[3]+1);
+
+        // Add the 2 triangles using vertices defined in defTria
+        for (int i_tria=0 ; i_tria<2 ; i_tria++) {
+          int i_vert_t[3]; // vertices of the current triangle, in [0..n_z*n_l[
+          for (int i=0 ; i<3 ; i++)
+            i_vert_t[i] = i_vert[defTria[typeHexa][i_tria][i]];
+          fprintf(funv, "%10d%s\n", ++nbTria + nbTetra, sELT_TRIA3_DESC ) ;
+          fprintf(funv, "%10d%10d%10d\n", i_vert_t[0]+1, i_vert_t[1]+1, i_vert_t[2]+1);
+        }
       }
       fprintf(funv, "%s\n",sUNV_SEPARATOR ) ;
       /* ------ */
@@ -334,7 +355,7 @@ int createFemMatrix() {
       Mpf_printf(MPI_COMM_WORLD, "Done.\n") ;
     }
 
-    Mpf_printf(MPI_COMM_WORLD, "Number of hexaedres = %d\n", nbHexa);
+    Mpf_printf(MPI_COMM_WORLD, "Number of Tetra = %d\n", nbTetra);
     Mpf_printf(MPI_COMM_WORLD, "Number of NNZ before sort = %zu\n", nnz_FEM);
     // Passer en multithread ?
     ierr=Z_sortAndCompactL(z_matComp_FEM, &nnz_FEM) ; CHKERRQ(ierr) ;
diff --git a/src/prepareTEST.c b/src/prepareTEST.c
index 4c7950d..4344511 100644
--- a/src/prepareTEST.c
+++ b/src/prepareTEST.c
@@ -13,6 +13,7 @@ int prepareTEST(void) {
   Mpf_printf(MPI_COMM_WORLD, "<PERFTESTS> StepMesh = %e\n" , step);
   Mpf_printf(MPI_COMM_WORLD, "<PERFTESTS> NbPts = %d \n", nbPts);
   Mpf_printf(MPI_COMM_WORLD, "<PERFTESTS> NbPtsBEM = %d \n", nbPtsBEM);
+  Mpf_printf(MPI_COMM_WORLD, "<PERFTESTS> NbPtsFEM = %d \n", nbPts-nbPtsBEM);
   Mpf_printf(MPI_COMM_WORLD, "<PERFTESTS> NbRhs = %d \n", nbRHS);
   Mpf_printf(MPI_COMM_WORLD, "<PERFTESTS> nbPtLambda = %e \n", ptsPerLambda);
   if (useDetail)
@@ -48,4 +49,3 @@ int displayArray(char *s, void *f, int m, int n) {
         Mpf_printf(MPI_COMM_WORLD, "%s[%d, %d]=%e\n", s, i, j, d_f[(size_t)j*m+i]) ;
   return 0 ;
 }
-
diff --git a/src/rhs.c b/src/rhs.c
index 7b9716a..59831a7 100644
--- a/src/rhs.c
+++ b/src/rhs.c
@@ -1,8 +1,9 @@
 #include "main.h"
+#include <mpf_simd.h>
 
 extern double height;
 
-int computeRhs(void) {
+MPF_SIMD_DISP(int computeRhs(void)) {
   double coord[3], OP[3] ;
   int i, j, ierr ;
   double complex *z_rhs=rhs ;
@@ -21,7 +22,7 @@ int computeRhs(void) {
             // OP = fake incident plane wave coming from direction rotating around the object
             ierr = computeCoord( (size_t)j*nbPts/nbRHS%nbPts , &(OP[0])) ; CHKERRQ(ierr);
             OP[2] -= height/2.; // to have plane waves coming from below too
-            z_rhs[(size_t)j * nbPts + i] = cexp(
+            z_rhs[(size_t)j * nbPts + i] = mpf_simd_cexp(
               I * k * (coord[0] * OP[0] + coord[1] * OP[1] + coord[2] * OP[2]));
           } else {
             d_rhs[(size_t)j*nbPts+i]=cos(coord[0]/(j+1)+coord[1]+sin(j)*coord[2])+sin(j) ;
diff --git a/src/testHMAT.c b/src/testHMAT.c
index b3ff1d9..86e9fda 100644
--- a/src/testHMAT.c
+++ b/src/testHMAT.c
@@ -386,10 +386,10 @@ _refineLoop: // Iterative Refinement loop
       break;
   } /* switch(testedAlgo) */
 
-  /* Destroys the HMAT structure */
 #ifdef TESTFEMBEM_DUMP_HMAT
   hi->dump_info(hmatrix, "testHMAT_matrix");
 #endif
+  /* Destroys the HMAT structure */
   HMAT_destroy_matrix( hi, hdesc );
   MpfFree(solCLA);
   hmat_tracing_dump("testHMAT_trace.json");
-- 
GitLab