diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp
index 886e474f09210e722c879f5fb33b342785301dcb..0fca4b945c169faad9c93e51989c03be79851ab3 100644
--- a/Common/CommandLine.cpp
+++ b/Common/CommandLine.cpp
@@ -407,6 +407,10 @@ void GetOptions(int argc, char *argv[])
               i++;
               opt_mesh_partition_pyr_weight(0,GMSH_SET,atoi(argv[i]));
             }
+            else if (!strcmp(argv[i],"trihedron")) {
+              i++;
+              opt_mesh_partition_trih_weight(0,GMSH_SET,atoi(argv[i]));
+            }
             else if (!strcmp(argv[i],"hex")) {
               i++;
               opt_mesh_partition_hex_weight(0,GMSH_SET,atoi(argv[i]));
diff --git a/Common/Context.cpp b/Common/Context.cpp
index 67f4277662693f53de6ce49569066ec47a065c6d..2657781376285aca2282c75b1dad3b59a9ec004e 100644
--- a/Common/Context.cpp
+++ b/Common/Context.cpp
@@ -83,7 +83,7 @@ CTX::CTX() : gamepad(0)
   mesh.qualityInf = mesh.qualitySup = mesh.qualityType = 0;
   mesh.radiusInf = mesh.radiusSup = 0;
   mesh.lines = mesh.triangles = mesh.tetrahedra = mesh.quadrangles = 0;
-  mesh.prisms = mesh.pyramids = mesh.hexahedra = 0;
+  mesh.prisms = mesh.pyramids = mesh.hexahedra = mesh.trihedra = 0;
   mesh.volumesEdges = mesh.volumesFaces = mesh.surfacesEdges = mesh.surfacesFaces = 0;
   mesh.volumesFaces = mesh.surfacesEdges = mesh.surfacesFaces = 0;
   mesh.hoOptimize = mesh.smoothNormals = mesh.reverseAllNormals = 0;
@@ -93,7 +93,7 @@ CTX::CTX() : gamepad(0)
   mesh.ignorePartBound = 0;
   mesh.saveTri = 0;
   color.mesh.tangents = color.mesh.tetrahedron = color.mesh.triangle = 0;
-  color.mesh.prism = color.mesh.pyramid = color.mesh.hexahedron = 0;
+  color.mesh.prism = color.mesh.pyramid = color.mesh.hexahedron = color.mesh.trihedron = 0;
   color.mesh.tangents = color.mesh.line = color.mesh.quadrangle = 0;
   for (int i = 0; i < 20; i++)
     color.mesh.carousel[i] = 0;
diff --git a/Common/Context.h b/Common/Context.h
index 0913deddcc1c0c5cd7624ae3fc4fc12e38c6a362..c8dad77afbebd6b9776b8992cd2dae111d7ba924 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -20,7 +20,7 @@ class GamePad;
 
 struct contextMeshOptions {
   int draw, changed, light, lightTwoSide, lightLines, pointType;
-  int points, lines, triangles, quadrangles, tetrahedra, hexahedra, prisms, pyramids;
+  int points, lines, triangles, quadrangles, tetrahedra, hexahedra, prisms, pyramids, trihedra;
   int surfacesEdges, surfacesFaces, volumesEdges, volumesFaces, numSubEdges;
   int pointsNum, linesNum, surfacesNum, volumesNum, qualityType, labelType;
   int optimize, optimizeNetgen, optimizeLloyd, smoothCrossField, refineSteps;
@@ -293,7 +293,7 @@ class CTX {
     } geom;
     struct{
       unsigned int vertex, vertexSup, line, triangle, quadrangle;
-      unsigned int tetrahedron, hexahedron, prism, pyramid;
+      unsigned int tetrahedron, hexahedron, prism, pyramid, trihedron;
       unsigned int carousel[20];
       unsigned int tangents, normals;
     } mesh;
diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h
index 5a203ea8b323a6722544367a24100723d9a15ee9..10c36cba74e01445cba47b66e5813dabb7756f0c 100644
--- a/Common/DefaultOptions.h
+++ b/Common/DefaultOptions.h
@@ -1096,6 +1096,8 @@ StringXNumber MeshOptions_Number[] = {
     "Weight of prismatic element (wedge) for METIS load balancing" },
   { F|O, "PartitionPyramidWeight" , opt_mesh_partition_pyr_weight , 1 ,
     "Weight of pyramidal element for METIS load balancing" },
+  { F|O, "PartitionTrihedronWeight" , opt_mesh_partition_trih_weight , 0 ,
+    "Weight of trihedron element for METIS load balancing" },
   { F|O, "PartitionQuadWeight"    , opt_mesh_partition_qua_weight , 1 ,
     "Weight of quadrangle for METIS load balancing" },
   { F|O, "PartitionTetWeight"     , opt_mesh_partition_tet_weight , 1 ,
@@ -1113,6 +1115,8 @@ StringXNumber MeshOptions_Number[] = {
     "Number of prisms in the current mesh (read-only)" },
   { F, "NbPyramids" , opt_mesh_nb_pyramids , 0. ,
     "Number of pyramids in the current mesh (read-only)" },
+  { F, "NbTrihedra" , opt_mesh_nb_trihedra , 0. ,
+    "Number of trihedra in the current mesh (read-only)" },
   { F, "NbQuadrangles" , opt_mesh_nb_quadrangles , 0. ,
     "Number of quadrangles in the current mesh (read-only)" },
   { F, "NbTetrahedra" , opt_mesh_nb_tetrahedra , 0. ,
@@ -1144,6 +1148,8 @@ StringXNumber MeshOptions_Number[] = {
     "Display mesh prisms?" },
   { F|O, "Pyramids" , opt_mesh_pyramids , 1. ,
     "Display mesh pyramids?" },
+  { F|O, "Trihedra" , opt_mesh_trihedra , 1. ,
+    "Display mesh trihedra?" },
 
   { F|O, "Quadrangles" , opt_mesh_quadrangles , 1. ,
     "Display mesh quadrangles?" },
@@ -1445,6 +1451,8 @@ StringXNumber ViewOptions_Number[] = {
     "Display post-processing prisms?" },
   { F|O, "DrawPyramids" , opt_view_draw_pyramids , 1. ,
     "Display post-processing pyramids?" },
+  { F|O, "DrawTrihedra" , opt_view_draw_trihedra , 1. ,
+    "Display post-processing trihedra?" },
   { F|O, "DrawQuadrangles" , opt_view_draw_quadrangles , 1. ,
     "Display post-processing quadrangles?" },
   { F|O, "DrawScalars" , opt_view_draw_scalars , 1. ,
@@ -1798,6 +1806,7 @@ StringXColor GeometryOptions_Color[] = {
 #define COLQ  {130, 120, 225, 255}
 #define COLP  {232, 210, 23, 255}
 #define COLY  {217, 113, 38, 255}
+#define COLR  {20, 255, 0, 255}
 
 #define COL0  {255, 120, 0, 255}
 #define COL2  {255, 160, 0, 255}
@@ -1848,6 +1857,9 @@ StringXColor MeshOptions_Color[] = {
   { F|O, "Pyramids" , opt_mesh_color_pyramid ,
     COLY, COLY, COLW, COLY,
      "Mesh pyramid color (if Mesh.ColorCarousel=0)" },
+  { F|O, "Trihedra" , opt_mesh_color_trihedron ,
+    COLR, COLR, COLW, COLR,
+     "Mesh trihedron color (if Mesh.ColorCarousel=0)" },
   { F|O, "Tangents" , opt_mesh_color_tangents ,
     {255, 255, 0, 255}, {255, 255, 0, 255}, {0, 0, 0, 255}, {255, 255, 0, 255},
     "Tangent mesh vector color" },
@@ -1916,6 +1928,7 @@ StringXColor ViewOptions_Color[] = {
   { F|O, "Hexahedra" , opt_view_color_hexahedra , ELECOL, "Hexahedron color" },
   { F|O, "Prisms" , opt_view_color_prisms , ELECOL, "Prism color" },
   { F|O, "Pyramids" , opt_view_color_pyramids , ELECOL, "Pyramid color" },
+  { F|O, "Trihedra" , opt_view_color_trihedra , ELECOL, "Trihedron color" },
   { F|O, "Tangents" , opt_view_color_tangents ,
     {255, 255, 0, 255}, {255, 255, 0, 255}, {0, 0, 0, 255}, {255, 255, 0, 255},
     "Tangent vector color" },
diff --git a/Common/GmshDefines.h b/Common/GmshDefines.h
index a36ee969052d2b394d6e1cae439d98d8a966ca93..d68ec33f65abc395e944a4cd0780eaa6e24b05c2 100644
--- a/Common/GmshDefines.h
+++ b/Common/GmshDefines.h
@@ -67,6 +67,7 @@
 #define TYPE_POLYH   10
 #define TYPE_XFEM    11
 #define TYPE_MINI    12
+#define TYPE_TRIH    13
 
 // Element types in .msh file format (numbers should not be changed)
 #define MSH_LIN_2    1
@@ -215,8 +216,9 @@
 #define MSH_TET_16  137
 #define MSH_TRI_MINI 138
 #define MSH_TET_MINI 139
+#define MSH_TRIH_4   140
 
-#define MSH_NUM_TYPE 139
+#define MSH_NUM_TYPE 140
 
 // Geometric entities
 #define ENT_NONE     0
diff --git a/Common/Options.cpp b/Common/Options.cpp
index e4ec17a867f81c1d007dcf44ebfd7406139e0148..5e0170cc31ce5eb55b7dcdcc31347259c7b3cbea 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -5251,6 +5251,25 @@ double opt_mesh_pyramids(OPT_ARGS_NUM)
   return CTX::instance()->mesh.pyramids;
 }
 
+double opt_mesh_trihedra(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET) {
+    if(CTX::instance()->mesh.trihedra != val)
+      CTX::instance()->mesh.changed |= ENT_VOLUME;
+    CTX::instance()->mesh.trihedra = (int)val;
+  }
+#if defined(HAVE_FLTK)
+  if(FlGui::available() && (action & GMSH_GUI)){
+    if(CTX::instance()->mesh.trihedra)
+      ((Fl_Menu_Item*)FlGui::instance()->options->mesh.menu->menu())[6].set();
+    else
+      ((Fl_Menu_Item*)FlGui::instance()->options->mesh.menu->menu())[6].clear();
+  }
+#endif
+  return CTX::instance()->mesh.trihedra;
+}
+
+
 double opt_mesh_surfaces_edges(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET) {
@@ -5562,6 +5581,13 @@ double opt_mesh_partition_pyr_weight(OPT_ARGS_NUM)
   return CTX::instance()->partitionOptions.pyrWeight;
 }
 
+double opt_mesh_partition_trih_weight(OPT_ARGS_NUM)
+{
+  if (action & GMSH_SET)
+    CTX::instance()->partitionOptions.trihWeight = (int) val;
+  return CTX::instance()->partitionOptions.trihWeight;
+}
+
 double opt_mesh_partition_qua_weight(OPT_ARGS_NUM)
 {
   if (action & GMSH_SET)
@@ -6129,11 +6155,18 @@ double opt_mesh_nb_pyramids(OPT_ARGS_NUM)
   return s[12];
 }
 
+double opt_mesh_nb_trihedra(OPT_ARGS_NUM)
+{
+  double s[50];
+  GetStatistics(s);
+  return s[13];
+}
+
 double opt_mesh_cpu_time(OPT_ARGS_NUM)
 {
   double s[50];
   GetStatistics(s);
-  return s[13] + s[14] + s[15];
+  return s[14] + s[15] + s[16];
 }
 
 double opt_mesh_partition_partitioner(OPT_ARGS_NUM)
@@ -8134,6 +8167,28 @@ double opt_view_draw_pyramids(OPT_ARGS_NUM)
 #endif
 }
 
+double opt_view_draw_trihedra(OPT_ARGS_NUM)
+{
+#if defined(HAVE_POST)
+  GET_VIEWo(0.);
+  if(action & GMSH_SET) {
+    opt->drawTrihedra = (int)val;
+    if(view) view->setChanged(true);
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num)){
+    if(opt->drawTrihedra)
+      ((Fl_Menu_Item*)FlGui::instance()->options->view.menu[1]->menu())[8].set();
+    else
+      ((Fl_Menu_Item*)FlGui::instance()->options->view.menu[1]->menu())[8].clear();
+  }
+#endif
+  return opt->drawTrihedra;
+#else
+  return 0.;
+#endif
+}
+
 double opt_view_draw_scalars(OPT_ARGS_NUM)
 {
 #if defined(HAVE_POST)
@@ -9428,13 +9483,29 @@ unsigned int opt_mesh_color_pyramid(OPT_ARGS_COL)
   return CTX::instance()->color.mesh.pyramid;
 }
 
+unsigned int opt_mesh_color_trihedron(OPT_ARGS_COL)
+{
+  if(action & GMSH_SET) {
+    // vertex arrays need to be regenerated only when we color by
+    // element type
+    if(CTX::instance()->color.mesh.trihedron != val &&
+       CTX::instance()->mesh.colorCarousel == 0)
+      CTX::instance()->mesh.changed |= ENT_VOLUME;
+    CTX::instance()->color.mesh.trihedron = val;
+  }
+#if defined(HAVE_FLTK)
+  CCC(CTX::instance()->color.mesh.trihedron, FlGui::instance()->options->mesh.color[9]);
+#endif
+  return CTX::instance()->color.mesh.trihedron;
+}
+
 unsigned int opt_mesh_color_tangents(OPT_ARGS_COL)
 {
   if(action & GMSH_SET) {
     CTX::instance()->color.mesh.tangents = val;
   }
 #if defined(HAVE_FLTK)
-  CCC(CTX::instance()->color.mesh.tangents, FlGui::instance()->options->mesh.color[9]);
+  CCC(CTX::instance()->color.mesh.tangents, FlGui::instance()->options->mesh.color[10]);
 #endif
   return CTX::instance()->color.mesh.tangents;
 }
@@ -9445,7 +9516,7 @@ unsigned int opt_mesh_color_normals(OPT_ARGS_COL)
     CTX::instance()->color.mesh.normals = val;
   }
 #if defined(HAVE_FLTK)
-  CCC(CTX::instance()->color.mesh.normals, FlGui::instance()->options->mesh.color[10]);
+  CCC(CTX::instance()->color.mesh.normals, FlGui::instance()->options->mesh.color[11]);
 #endif
   return CTX::instance()->color.mesh.normals;
 }
@@ -9461,7 +9532,7 @@ unsigned int opt_mesh_color_(int i, OPT_ARGS_COL)
     CTX::instance()->color.mesh.carousel[i] = val;
   }
 #if defined(HAVE_FLTK)
-  CCC(CTX::instance()->color.mesh.carousel[i], FlGui::instance()->options->mesh.color[11+i]);
+  CCC(CTX::instance()->color.mesh.carousel[i], FlGui::instance()->options->mesh.color[12+i]);
 #endif
   return CTX::instance()->color.mesh.carousel[i];
 }
@@ -9639,6 +9710,25 @@ unsigned int opt_view_color_pyramids(OPT_ARGS_COL)
 #endif
 }
 
+unsigned int opt_view_color_trihedra(OPT_ARGS_COL)
+{
+#if defined(HAVE_POST)
+  GET_VIEWo(0);
+  if(action & GMSH_SET) {
+    opt->color.trihedron = val;
+    if(view) view->setChanged(true);
+  }
+#if defined(HAVE_FLTK)
+  if(_gui_action_valid(action, num)){
+    CCC(opt->color.trihedron, FlGui::instance()->options->view.color[8]);
+  }
+#endif
+  return opt->color.trihedron;
+#else
+  return 0;
+#endif
+}
+
 unsigned int opt_view_color_tangents(OPT_ARGS_COL)
 {
 #if defined(HAVE_POST)
@@ -9649,7 +9739,7 @@ unsigned int opt_view_color_tangents(OPT_ARGS_COL)
   }
 #if defined(HAVE_FLTK)
   if(_gui_action_valid(action, num)){
-    CCC(opt->color.tangents, FlGui::instance()->options->view.color[8]);
+    CCC(opt->color.tangents, FlGui::instance()->options->view.color[9]);
   }
 #endif
   return opt->color.tangents;
@@ -9668,7 +9758,7 @@ unsigned int opt_view_color_normals(OPT_ARGS_COL)
   }
 #if defined(HAVE_FLTK)
   if(_gui_action_valid(action, num)){
-    CCC(opt->color.normals, FlGui::instance()->options->view.color[9]);
+    CCC(opt->color.normals, FlGui::instance()->options->view.color[10]);
   }
 #endif
   return opt->color.normals;
diff --git a/Common/Options.h b/Common/Options.h
index 1da21151e4e0935df324ce360264f850f4195214..0f3f076fad9436b46c723eb566feaa11683fc7e3 100644
--- a/Common/Options.h
+++ b/Common/Options.h
@@ -423,6 +423,7 @@ double opt_mesh_tetrahedra(OPT_ARGS_NUM);
 double opt_mesh_hexahedra(OPT_ARGS_NUM);
 double opt_mesh_prisms(OPT_ARGS_NUM);
 double opt_mesh_pyramids(OPT_ARGS_NUM);
+double opt_mesh_trihedra(OPT_ARGS_NUM);
 double opt_mesh_surfaces_edges(OPT_ARGS_NUM);
 double opt_mesh_surfaces_faces(OPT_ARGS_NUM);
 double opt_mesh_volumes_edges(OPT_ARGS_NUM);
@@ -449,6 +450,7 @@ double opt_mesh_msh_file_partitioned(OPT_ARGS_NUM);
 double opt_mesh_partition_hex_weight(OPT_ARGS_NUM);
 double opt_mesh_partition_pri_weight(OPT_ARGS_NUM);
 double opt_mesh_partition_pyr_weight(OPT_ARGS_NUM);
+double opt_mesh_partition_trih_weight(OPT_ARGS_NUM);
 double opt_mesh_partition_qua_weight(OPT_ARGS_NUM);
 double opt_mesh_partition_tet_weight(OPT_ARGS_NUM);
 double opt_mesh_partition_tri_weight(OPT_ARGS_NUM);
@@ -503,6 +505,7 @@ double opt_mesh_nb_tetrahedra(OPT_ARGS_NUM);
 double opt_mesh_nb_hexahedra(OPT_ARGS_NUM);
 double opt_mesh_nb_prisms(OPT_ARGS_NUM);
 double opt_mesh_nb_pyramids(OPT_ARGS_NUM);
+double opt_mesh_nb_trihedra(OPT_ARGS_NUM);
 double opt_mesh_cpu_time(OPT_ARGS_NUM);
 double opt_mesh_partition_partitioner(OPT_ARGS_NUM);
 double opt_mesh_partition_num(OPT_ARGS_NUM);
@@ -652,6 +655,7 @@ double opt_view_draw_tetrahedra(OPT_ARGS_NUM);
 double opt_view_draw_hexahedra(OPT_ARGS_NUM);
 double opt_view_draw_prisms(OPT_ARGS_NUM);
 double opt_view_draw_pyramids(OPT_ARGS_NUM);
+double opt_view_draw_trihedra(OPT_ARGS_NUM);
 double opt_view_draw_scalars(OPT_ARGS_NUM);
 double opt_view_draw_vectors(OPT_ARGS_NUM);
 double opt_view_draw_tensors(OPT_ARGS_NUM);
@@ -747,6 +751,7 @@ unsigned int opt_mesh_color_tetrahedra(OPT_ARGS_COL);
 unsigned int opt_mesh_color_hexahedra(OPT_ARGS_COL);
 unsigned int opt_mesh_color_prisms(OPT_ARGS_COL);
 unsigned int opt_mesh_color_pyramid(OPT_ARGS_COL);
+unsigned int opt_mesh_color_trihedron(OPT_ARGS_COL);
 unsigned int opt_mesh_color_tangents(OPT_ARGS_COL);
 unsigned int opt_mesh_color_normals(OPT_ARGS_COL);
 unsigned int opt_mesh_color_0(OPT_ARGS_COL);
@@ -777,6 +782,7 @@ unsigned int opt_view_color_tetrahedra(OPT_ARGS_COL);
 unsigned int opt_view_color_hexahedra(OPT_ARGS_COL);
 unsigned int opt_view_color_prisms(OPT_ARGS_COL);
 unsigned int opt_view_color_pyramids(OPT_ARGS_COL);
+unsigned int opt_view_color_trihedra(OPT_ARGS_COL);
 unsigned int opt_view_color_tangents(OPT_ARGS_COL);
 unsigned int opt_view_color_normals(OPT_ARGS_COL);
 unsigned int opt_view_color_text2d(OPT_ARGS_COL);
diff --git a/Fltk/optionWindow.cpp b/Fltk/optionWindow.cpp
index dbf7ae25cddd3e687206ebf67d3b2568b2610d00..77df50ea95b7c64514da31968e18b78895bea766 100644
--- a/Fltk/optionWindow.cpp
+++ b/Fltk/optionWindow.cpp
@@ -496,6 +496,7 @@ static void mesh_options_ok_cb(Fl_Widget *w, void *data)
   opt_mesh_hexahedra(0, GMSH_SET, o->mesh.menu->menu()[3].value() ? 1 : 0);
   opt_mesh_prisms(0, GMSH_SET, o->mesh.menu->menu()[4].value() ? 1 : 0);
   opt_mesh_pyramids(0, GMSH_SET, o->mesh.menu->menu()[5].value() ? 1 : 0);
+  opt_mesh_trihedra(0, GMSH_SET, o->mesh.menu->menu()[6].value() ? 1 : 0);
   opt_mesh_surfaces_edges(0, GMSH_SET, o->mesh.butt[8]->value());
   opt_mesh_surfaces_faces(0, GMSH_SET, o->mesh.butt[9]->value());
   opt_mesh_volumes_edges(0, GMSH_SET, o->mesh.butt[10]->value());
@@ -2503,6 +2504,7 @@ optionWindow::optionWindow(int deltaFontSize)
         {"Hexahedra",   0, 0, 0, FL_MENU_TOGGLE},
         {"Prisms",      0, 0, 0, FL_MENU_TOGGLE},
         {"Pyramids",    0, 0, 0, FL_MENU_TOGGLE},
+        {"Trihedra",    0, 0, 0, FL_MENU_TOGGLE},
         {0}
       };
 
@@ -3687,6 +3689,7 @@ void optionWindow::updateViewGroup(int index)
   opt_view_draw_hexahedra(index, GMSH_GUI, 0);
   opt_view_draw_prisms(index, GMSH_GUI, 0);
   opt_view_draw_pyramids(index, GMSH_GUI, 0);
+  opt_view_draw_trihedra(index, GMSH_GUI, 0);
   opt_view_draw_scalars(index, GMSH_GUI, 0);
   opt_view_draw_vectors(index, GMSH_GUI, 0);
   opt_view_draw_tensors(index, GMSH_GUI, 0);
@@ -3792,6 +3795,7 @@ void optionWindow::updateViewGroup(int index)
   opt_view_color_hexahedra(index, GMSH_GUI, 0);
   opt_view_color_prisms(index, GMSH_GUI, 0);
   opt_view_color_pyramids(index, GMSH_GUI, 0);
+  opt_view_color_trihedra(index, GMSH_GUI, 0);
   opt_view_color_tangents(index, GMSH_GUI, 0);
   opt_view_color_normals(index, GMSH_GUI, 0);
   opt_view_color_text2d(index, GMSH_GUI, 0);
diff --git a/Fltk/statisticsWindow.cpp b/Fltk/statisticsWindow.cpp
index 42d552c343496f6874c4ae006a5c341200d8873f..439a30ab3c357f46cd0f51983815de2316d1e138 100644
--- a/Fltk/statisticsWindow.cpp
+++ b/Fltk/statisticsWindow.cpp
@@ -89,7 +89,7 @@ statisticsWindow::statisticsWindow(int deltaFontSize)
 
   int num = 0;
   int width = 26 * FL_NORMAL_SIZE;
-  int height = 5 * WB + 17 * BH;
+  int height = 5 * WB + 18 * BH;
 
   win = new paletteWindow
     (width, height, CTX::instance()->nonModalWindows ? true : false, "Statistics");
@@ -118,26 +118,27 @@ statisticsWindow::statisticsWindow(int deltaFontSize)
       value[num++] = new Fl_Output(2 * WB, 2 * WB + 7 * BH, IW, BH, "Hexahedra");
       value[num++] = new Fl_Output(2 * WB, 2 * WB + 8 * BH, IW, BH, "Prisms");
       value[num++] = new Fl_Output(2 * WB, 2 * WB + 9 * BH, IW, BH, "Pyramids");
+      value[num++] = new Fl_Output(2 * WB, 2 * WB + 10 * BH, IW, BH, "Trihedra");
 
-      value[num++] = new Fl_Output(2 * WB, 2 * WB + 10 * BH, IW, BH, "Time for 1D mesh");
-      value[num++] = new Fl_Output(2 * WB, 2 * WB + 11 * BH, IW, BH, "Time for 2D mesh");
-      value[num++] = new Fl_Output(2 * WB, 2 * WB + 12 * BH, IW, BH, "Time for 3D mesh");
+      value[num++] = new Fl_Output(2 * WB, 2 * WB + 11 * BH, IW, BH, "Time for 1D mesh");
+      value[num++] = new Fl_Output(2 * WB, 2 * WB + 12 * BH, IW, BH, "Time for 2D mesh");
+      value[num++] = new Fl_Output(2 * WB, 2 * WB + 13 * BH, IW, BH, "Time for 3D mesh");
 
-      value[num] = new Fl_Output(2 * WB, 2 * WB + 13 * BH, IW, BH, "SICN");
+      value[num] = new Fl_Output(2 * WB, 2 * WB + 14 * BH, IW, BH, "SICN");
       value[num]->tooltip("~ signed inverse condition number"); num++;
-      value[num] = new Fl_Output(2 * WB, 2 * WB + 14 * BH, IW, BH, "Gamma");
+      value[num] = new Fl_Output(2 * WB, 2 * WB + 15 * BH, IW, BH, "Gamma");
       value[num]->tooltip("~ inscribed_radius / circumscribed_radius (simplices)"); num++;
-      value[num] = new Fl_Output(2 * WB, 2 * WB + 15 * BH, IW, BH, "Rho");
+      value[num] = new Fl_Output(2 * WB, 2 * WB + 16 * BH, IW, BH, "Rho");
       value[num]->tooltip("~ min_edge_length / max_edge_length"); num++;
 
       for(int i = 0; i < 3; i++){
         int ww = 3 * FL_NORMAL_SIZE;
         new Fl_Box
-          (FL_NO_BOX, width - 3 * ww - 2 * WB, 2 * WB + (13 + i) * BH, ww, BH, "Plot");
+          (FL_NO_BOX, width - 3 * ww - 2 * WB, 2 * WB + (14 + i) * BH, ww, BH, "Plot");
         butt[2 * i] = new Fl_Button
-          (width - 2 * ww - 2 * WB, 2 * WB + (13 + i) * BH, ww, BH, "X-Y");
+          (width - 2 * ww - 2 * WB, 2 * WB + (14 + i) * BH, ww, BH, "X-Y");
         butt[2 * i + 1] = new Fl_Button
-          (width - ww - 2 * WB, 2 * WB + (13 + i) * BH, ww, BH, "3D");
+          (width - ww - 2 * WB, 2 * WB + (14 + i) * BH, ww, BH, "3D");
       }
       static const QM_HISTO qmh0 = QMH_SICN_XY, qmh1 = QMH_SICN_3D, qmh2 = QMH_GAMMA_XY,
                             qmh3 = QMH_GAMMA_3D, qmh4 = QMH_RHO_XY, qmh5 = QMH_RHO_3D;
@@ -162,7 +163,8 @@ statisticsWindow::statisticsWindow(int deltaFontSize)
       value[num++] = new Fl_Output(2 * WB, 2 * WB + 7 * BH, IW, BH, "Hexahedra");
       value[num++] = new Fl_Output(2 * WB, 2 * WB + 8 * BH, IW, BH, "Prisms");
       value[num++] = new Fl_Output(2 * WB, 2 * WB + 9 * BH, IW, BH, "Pyramids");
-      value[num++] = new Fl_Output(2 * WB, 2 * WB + 10 * BH, IW, BH, "Strings");
+      value[num++] = new Fl_Output(2 * WB, 2 * WB + 10 * BH, IW, BH, "Trihedra");
+      value[num++] = new Fl_Output(2 * WB, 2 * WB + 11 * BH, IW, BH, "Strings");
       group[2]->end();
     }
     o->end();
@@ -216,10 +218,11 @@ void statisticsWindow::compute(bool elementQuality)
   sprintf(label[num], "%g", s[10]); value[num]->value(label[num]); num++;
   sprintf(label[num], "%g", s[11]); value[num]->value(label[num]); num++;
   sprintf(label[num], "%g", s[12]); value[num]->value(label[num]); num++;
-
   sprintf(label[num], "%g", s[13]); value[num]->value(label[num]); num++;
+
   sprintf(label[num], "%g", s[14]); value[num]->value(label[num]); num++;
   sprintf(label[num], "%g", s[15]); value[num]->value(label[num]); num++;
+  sprintf(label[num], "%g", s[16]); value[num]->value(label[num]); num++;
 
   if(!elementQuality){
     for(int i = 0; i < 6; i += 2) butt[i]->deactivate();
@@ -235,19 +238,18 @@ void statisticsWindow::compute(bool elementQuality)
   }
   else{
     for(int i = 0; i < 6; i += 2) butt[i]->activate();
-    sprintf(label[num], "%.4g (%.4g->%.4g)", s[17], s[18], s[19]);
+    sprintf(label[num], "%.4g (%.4g->%.4g)", s[18], s[19], s[20]);
     value[num]->activate();
     value[num]->value(label[num]); num++;
-    sprintf(label[num], "%.4g (%.4g->%.4g)", s[20], s[21], s[22]);
+    sprintf(label[num], "%.4g (%.4g->%.4g)", s[21], s[22], s[23]);
     value[num]->activate();
     value[num]->value(label[num]); num++;
-    sprintf(label[num], "%.4g (%.4g->%.4g)", s[23], s[24], s[25]);
+    sprintf(label[num], "%.4g (%.4g->%.4g)", s[24], s[25], s[26]);
     value[num]->activate();
     value[num]->value(label[num]); num++;
   }
 
   // post
-  sprintf(label[num], "%g", s[26]); value[num]->value(label[num]); num++;
   sprintf(label[num], "%g", s[27]); value[num]->value(label[num]); num++;
   sprintf(label[num], "%g", s[28]); value[num]->value(label[num]); num++;
   sprintf(label[num], "%g", s[29]); value[num]->value(label[num]); num++;
@@ -257,6 +259,8 @@ void statisticsWindow::compute(bool elementQuality)
   sprintf(label[num], "%g", s[33]); value[num]->value(label[num]); num++;
   sprintf(label[num], "%g", s[34]); value[num]->value(label[num]); num++;
   sprintf(label[num], "%g", s[35]); value[num]->value(label[num]); num++;
+  sprintf(label[num], "%g", s[36]); value[num]->value(label[num]); num++;
+  sprintf(label[num], "%g", s[37]); value[num]->value(label[num]); num++;
 
   static char mem[256];
   long m = GetMemoryUsage();
diff --git a/Geo/CMakeLists.txt b/Geo/CMakeLists.txt
index e91f0bef8da156e8b2a2187a0db76e0cd4dbd04e..b1d3a1d9d2d281e3058bc801e92700aed7b49124 100644
--- a/Geo/CMakeLists.txt
+++ b/Geo/CMakeLists.txt
@@ -43,7 +43,7 @@ set(SRC
   MFace.cpp
   MElement.cpp MElementOctree.cpp
     MLine.cpp MTriangle.cpp MQuadrangle.cpp MTetrahedron.cpp
-    MHexahedron.cpp MPrism.cpp MPyramid.cpp MElementCut.cpp MSubElement.cpp
+    MHexahedron.cpp MPrism.cpp MPyramid.cpp MTrihedron.cpp MElementCut.cpp MSubElement.cpp
   MZone.cpp MZoneBoundary.cpp
   Cell.cpp CellComplex.cpp ChainComplex.cpp Homology.cpp Chain.cpp
   Curvature.cpp
diff --git a/Geo/GModel.cpp b/Geo/GModel.cpp
index 36309fa2cb8bebf5dc041ac5a57683f8890fb378..b11f31a513ac45dcf7a9e6c9471152de8bebbb2b 100644
--- a/Geo/GModel.cpp
+++ b/Geo/GModel.cpp
@@ -21,6 +21,7 @@
 #include "MHexahedron.h"
 #include "MPrism.h"
 #include "MPyramid.h"
+#include "MTrihedron.h"
 #include "MElementCut.h"
 #include "MElementOctree.h"
 #include "discreteRegion.h"
@@ -587,6 +588,128 @@ bool GModel::setAllVolumesPositive()
   return ok;
 }
 
+static void addToMap(std::multimap< MFace , MElement *, Less_Face> &faceToElement, std::map< MElement *, std::vector < std::pair <MElement *, bool> > > &elToNeighbors, const MFace &face,  MElement *el){
+  std::map< MFace , MElement *, Less_Face>::iterator fit = faceToElement.find(face);
+  if (fit == faceToElement.end()){
+    faceToElement.insert(std::pair< MFace , MElement *>(face, el));
+  } else { //We found the neighbor face outFace
+    faceToElement.insert(std::pair< MFace , MElement *>(face, el));
+    if (faceToElement.count(face) > 2)
+      Msg::Fatal("Topological fault: Face sharing two other faces. Element %i. Number of nodes %i. Count of faces: %i Three first nodes %i %i %i",el->getNum(),face.getNumVertices(),faceToElement.count(face),face.getVertex(0)->getNum(),face.getVertex(1)->getNum(),face.getVertex(2)->getNum());
+    MFace outFace = fit->first;
+    std::vector<std::pair<MElement *, bool> > &neigh = elToNeighbors[el];
+    for (size_t iN = 0; iN < neigh.size(); iN++)
+      if (neigh[iN].first == fit->second)
+        return;
+    int i0 = -1;
+    while (face.getVertex(0) != outFace.getVertex(++i0));
+    bool sameOrientation = face.getVertex(1) == outFace.getVertex((i0+1)%face.getNumVertices());
+    //    if (sameOrientation) printf("Non-matching orientation between el %i and el %i for face IN %i %i %i out %i %i %i\n",el->getNum(),fit->second->getNum(),face.getVertex(0)->getNum(),face.getVertex(1)->getNum(),face.getVertex(2)->getNum(),outFace.getVertex(0)->getNum(),outFace.getVertex(1)->getNum(),outFace.getVertex(2)->getNum());
+    neigh.push_back(std::make_pair(fit->second, !sameOrientation));
+    elToNeighbors[fit->second].push_back(std::make_pair(el, !sameOrientation));
+  }
+}
+
+//static void checkConformity(std::multimap< MFace , MElement *, Less_Face> &faceToElement, std::map< MElement *, std::vector < std::pair <MElement *, bool> > > &elToNeighbors, const MFace &face,  MElement *el){
+//  //Check conformity
+//  if (face.getNumVertices() == 4){
+//    MVertex *v0  =  face.getVertex(0);
+//    MVertex *v1  =  face.getVertex(1);
+//    MVertex *v2  =  face.getVertex(2);
+//    MVertex *v3  =  face.getVertex(3);
+//    MFace f[5] = {MFace(v0, v1, v2), MFace(v0, v2, v3), MFace(v0, v1, v3), MFace(v1, v2, v3)};
+//    if (faceToElement.find(f[0])!= faceToElement.end() || faceToElement.find(f[1])!= faceToElement.end() || faceToElement.find(f[2])!= faceToElement.end() || faceToElement.find(f[3])!= faceToElement.end() ){
+//      //Check if the element is a trihedron used to recover conformity:
+//      //A single element contains the two triangles, the quad and no other face
+//      std::multiset<size_t> facesNeighborsId;
+//      int nFaces=0;
+//      for (int iFace = 0; iFace<4; iFace++){
+//        int nbCount = faceToElement.count(f[iFace]);
+//        if (nbCount != 0){
+//          nFaces++;
+//          if (nFaces > 3) Msg::Fatal("Topological fault: A quad face shares more than 3 triangles (element %i)",el->getNum());
+//          std::pair <std::multimap< MFace , MElement *, Less_Face>::iterator, std::multimap< MFace , MElement *, Less_Face>::iterator> faceNeighbors;
+//          faceNeighbors = faceToElement.equal_range(f[iFace]);
+//          for (std::multimap< MFace , MElement *, Less_Face>::iterator itEl=faceNeighbors.first; itEl!=faceNeighbors.second; ++itEl)
+//            facesNeighborsId.insert(itEl->second->getNum());
+//        }
+//      }
+//      //In some cases, there can be 3 triangles matching a quad face and result in a valid trihedron (1 or 4 triangles not allowed)
+//      if ((nFaces != 2 || nFaces != 3) && faceToElement.count(face)!= 2) Msg::Fatal("Nonconforming element which is not a trihedron (element %i  nFaces %i nodes %i %i %i %i)",el->getNum(),nFaces,v0->getNum(),v1->getNum(),v2->getNum(),v3->getNum());
+//      size_t maxOccurence=0;
+//      for (std::multiset<size_t>::iterator iElId = facesNeighborsId.begin(); iElId != facesNeighborsId.end(); ++iElId){
+//        maxOccurence = std::max(facesNeighborsId.count(*iElId), maxOccurence);
+//      }
+//      if (maxOccurence != 3) //A trihedron should have 3 and only 3 neighbors      
+//        Msg::Fatal("Nonconforming face for element: %i vertices %i %i %i %i. Number of neighbors: %i",el->getNum(), v0->getNum(), v1->getNum(), v2->getNum(), v3->getNum(),maxOccurence);
+//    }
+//  }
+//}
+
+static void checkConformity(std::multimap< MFace , MElement *, Less_Face> &faceToElement, std::map< MElement *, std::vector < std::pair <MElement *, bool> > > &elToNeighbors, MFace face, MElement *el){
+  int connectivity = faceToElement.count(face);
+  if (ElementType::ParentTypeFromTag(el->getType()) == TYPE_TRIH){
+    //Each face of a trihedron should exist twice (no face on the boundary)
+    if (connectivity != 2) Msg::Fatal("Non conforming trihedron %i (nb connections for a face %i)",el->getNum(), faceToElement.count(face));
+  }else{
+    //A face can exist  twice (inside) or once (boundary)
+    if (connectivity != 2){
+      for (int iV = 0; iV < face.getNumVertices(); iV++)
+        if (face.getVertex(iV)->onWhat()->dim() == 3 || connectivity != 1){
+          for (int jV = 0; jV < face.getNumVertices(); jV++)
+            Msg::Info("Vertex %i",face.getVertex(jV)->getNum());
+          Msg::Fatal("Non conforming element %i (nb connections for a face %i vertex %i)",el->getNum(), connectivity, face.getVertex(iV)->getNum());
+        }
+    }
+  }
+}
+
+void GModel::setAllVolumesPositiveTopology()
+{
+  Msg::Info("Orienting volumes according to topology");
+  std::map< MElement *, std::vector < std::pair < MElement *, bool> > > elToNeighbors;
+  std::multimap< MFace , MElement *, Less_Face> faceToElement;
+
+  MElement *el;
+  for(riter it = regions.begin(); it != regions.end(); ++it){
+    for (unsigned int iEl = 0; iEl < (*it)->getNumMeshElements(); ++iEl) {
+      el = (*it)->getMeshElement(iEl);
+      for (int iFace = 0; iFace < el->getNumFaces(); iFace++){
+        addToMap(faceToElement, elToNeighbors, el->getFace(iFace), el);
+      }
+    }
+  }
+  for(riter it = regions.begin(); it != regions.end(); ++it){
+    for (unsigned int iEl = 0; iEl < (*it)->getNumMeshElements(); ++iEl) {
+      el = (*it)->getMeshElement(iEl);
+      for (int iFace = 0; iFace < el->getNumFaces(); iFace++){
+        checkConformity(faceToElement, elToNeighbors, el->getFace(iFace), el);
+      }
+    }
+  }
+  std::vector< std::pair <MElement *, bool > > queue;
+  std::set<MElement*> queued;
+  if ( (*regions.begin())->tetrahedra.size() == 0)
+    Msg::Fatal("setAllVolumePositiveTopology needs at least one tetrahedron to start");
+  el = (*regions.begin())->tetrahedra[0];
+  queue.push_back(std::make_pair(el, true));
+  for (size_t i = 0; i < queue.size(); i++){
+    el = queue[i].first;
+    if (!queue[i].second){
+      el->reverse();
+      //      Msg::Info("Reverted element %i of type %i", el->getNum(), el->getType());
+    }
+    const std::vector < std::pair <MElement *, bool> > &neigh = elToNeighbors[el];
+    for (size_t iN = 0; iN < neigh.size(); iN++)
+      if (queued.count(neigh[iN].first) == 0){
+        queue.push_back(std::make_pair(neigh[iN].first, neigh[iN].second == queue[i].second));
+        //        if (!(neigh[iN].second == queue[i].second))
+        //          Msg::Info("Queuing  element %i (%i) from el %i (%i)", neigh[iN].first->getNum(), neigh[iN].second, el->getNum(),queue[i].second);
+        queued.insert(neigh[iN].first);
+      }
+  }
+}
+
 int GModel::adaptMesh(std::vector<int> technique,
                       std::vector<simpleFunction<double>* > f,
                       std::vector<std::vector<double> > parameters,
@@ -758,7 +881,7 @@ int GModel::getMeshStatus(bool countDiscrete)
                           (*it)->meshAttributes.method != MESH_NONE)) &&
        ((*it)->tetrahedra.size() ||(*it)->hexahedra.size() ||
         (*it)->prisms.size() || (*it)->pyramids.size() ||
-        (*it)->polyhedra.size())) return 3;
+        (*it)->polyhedra.size() || (*it)->trihedra.size())) return 3;
   for(fiter it = firstFace(); it != lastFace(); ++it)
     if((countDiscrete || ((*it)->geomType() != GEntity::DiscreteSurface &&
                           (*it)->meshAttributes.method != MESH_NONE)) &&
@@ -803,12 +926,12 @@ int GModel::getNumMeshParentElements()
   return n;
 }
 
-int GModel::getNumMeshElements(unsigned c[5])
+int GModel::getNumMeshElements(unsigned c[6])
 {
-  c[0] = 0; c[1] = 0; c[2] = 0; c[3] = 0; c[4] = 0;
+  c[0] = 0; c[1] = 0; c[2] = 0; c[3] = 0; c[4] = 0; c[5] = 0;
   for(riter it = firstRegion(); it != lastRegion(); ++it)
     (*it)->getNumMeshElements(c);
-  if(c[0] + c[1] + c[2] + c[3] + c[4]) return 3;
+  if(c[0] + c[1] + c[2] + c[3] + c[4] + c[5]) return 3;
   for(fiter it = firstFace(); it != lastFace(); ++it)
     (*it)->getNumMeshElements(c);
   if(c[0] + c[1] + c[2]) return 2;
@@ -970,6 +1093,7 @@ void GModel::removeInvisibleElements()
     removeInvisible((*it)->hexahedra, all);
     removeInvisible((*it)->prisms, all);
     removeInvisible((*it)->pyramids, all);
+    removeInvisible((*it)->trihedra, all);
     removeInvisible((*it)->polyhedra, all);
     (*it)->deleteVertexArrays();
   }
@@ -1148,7 +1272,7 @@ void GModel::_storeElementsInEntities(std::map< int, std::vector<MElement* > >&
         else _addElements(f->polygons, it->second);
       }
       break;
-    case TYPE_TET: case TYPE_HEX: case TYPE_PYR: case TYPE_PRI: case TYPE_POLYH:
+    case TYPE_TET: case TYPE_HEX: case TYPE_PYR: case TYPE_TRIH: case TYPE_PRI: case TYPE_POLYH:
       {
         GRegion *r = getRegionByTag(it->first);
         if(!r){
@@ -1159,6 +1283,7 @@ void GModel::_storeElementsInEntities(std::map< int, std::vector<MElement* > >&
         else if(type == TYPE_HEX) _addElements(r->hexahedra, it->second);
         else if(type == TYPE_PRI) _addElements(r->prisms, it->second);
         else if(type == TYPE_PYR) _addElements(r->pyramids, it->second);
+        else if(type == TYPE_TRIH) _addElements(r->trihedra, it->second);
         else _addElements(r->polyhedra, it->second);
       }
       break;
@@ -1190,6 +1315,7 @@ void GModel::_associateEntityWithMeshVertices()
     _associateEntityWithElementVertices(*it, (*it)->hexahedra);
     _associateEntityWithElementVertices(*it, (*it)->prisms);
     _associateEntityWithElementVertices(*it, (*it)->pyramids);
+    _associateEntityWithElementVertices(*it, (*it)->trihedra);
     _associateEntityWithElementVertices(*it, (*it)->polyhedra);
   }
   for(fiter it = firstFace(); it != lastFace(); ++it){
@@ -1254,6 +1380,7 @@ void GModel::pruneMeshVertexAssociations()
     _associateEntityWithElementVertices(*it, (*it)->hexahedra, true);
     _associateEntityWithElementVertices(*it, (*it)->prisms, true);
     _associateEntityWithElementVertices(*it, (*it)->pyramids, true);
+    _associateEntityWithElementVertices(*it, (*it)->trihedra, true);
     _associateEntityWithElementVertices(*it, (*it)->polyhedra, true);
   }
   for(fiter it = _chainFaces.begin(); it != _chainFaces.end(); ++it){
@@ -1355,7 +1482,7 @@ GModel *GModel::createGModel(std::map<int, MVertex*> &vertexMap,
   }
 
   GModel *gm = new GModel();
-  std::map<int, std::vector<MElement*> > elements[10];
+  std::map<int, std::vector<MElement*> > elements[11];
   std::map<int, std::map<int, std::string> > physicals[4];
   std::vector<MVertex*> vertexVector;
 
@@ -1428,8 +1555,9 @@ GModel *GModel::createGModel(std::map<int, MVertex*> &vertexMap,
     case TYPE_HEX : elements[5][elementary[i]].push_back(e); break;
     case TYPE_PRI : elements[6][elementary[i]].push_back(e); break;
     case TYPE_PYR : elements[7][elementary[i]].push_back(e); break;
-    case TYPE_POLYG: elements[8][elementary[i]].push_back(e); break;
-    case TYPE_POLYH: elements[9][elementary[i]].push_back(e); break;
+    case TYPE_TRIH : elements[8][elementary[i]].push_back(e); break;
+    case TYPE_POLYG: elements[9][elementary[i]].push_back(e); break;
+    case TYPE_POLYH: elements[10][elementary[i]].push_back(e); break;
     default : Msg::Error("Wrong type of element"); delete gm; return 0;
     }
     int dim = e->getDim();
@@ -1578,7 +1706,7 @@ int GModel::removeDuplicateMeshVertices(double tolerance)
     return 0;
   }
 
-  std::map<int, std::vector<MElement*> > elements[10];
+  std::map<int, std::vector<MElement*> > elements[11];
   for(unsigned int i = 0; i < entities.size(); i++){
     for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
       MElement *e = entities[i]->getMeshElement(j);
@@ -1601,8 +1729,9 @@ int GModel::removeDuplicateMeshVertices(double tolerance)
         case TYPE_HEX: elements[5][entities[i]->tag()].push_back(e2); break;
         case TYPE_PRI: elements[6][entities[i]->tag()].push_back(e2); break;
         case TYPE_PYR: elements[7][entities[i]->tag()].push_back(e2); break;
-        case TYPE_POLYG: elements[8][entities[i]->tag()].push_back(e2); break;
-        case TYPE_POLYH: elements[9][entities[i]->tag()].push_back(e2); break;
+        case TYPE_TRIH: elements[8][entities[i]->tag()].push_back(e2); break;
+        case TYPE_POLYG: elements[9][entities[i]->tag()].push_back(e2); break;
+        case TYPE_POLYH: elements[10][entities[i]->tag()].push_back(e2); break;
         }
       }
       else
@@ -1824,6 +1953,7 @@ void GModel::makeDiscreteRegionsSimplyConnected()
         case TYPE_HEX: r->hexahedra.push_back((MHexahedron*)e2); break;
         case TYPE_PRI: r->prisms.push_back((MPrism*)e2); break;
         case TYPE_PYR: r->pyramids.push_back((MPyramid*)e2); break;
+        case TYPE_TRIH: r->trihedra.push_back((MTrihedron*)e2); break;
         }
       }
       r->mesh_vertices.insert
@@ -2299,6 +2429,7 @@ void GModel::createTopologyFromFaces(std::vector<discreteFace*> &discFaces, int
     std::vector<MHexahedron*> newHexahedra;
     std::vector<MPrism*> newPrisms;
     std::vector<MPyramid*> newPyramids;
+    std::vector<MTrihedron*> newTrihedra;
     for (unsigned int i = 0; i < gr->getNumMeshElements(); ++i){
       MElement *e = gr->getMeshElement(i);
       std::vector<MVertex *> v;
@@ -2317,6 +2448,7 @@ void GModel::createTopologyFromFaces(std::vector<discreteFace*> &discFaces, int
       case TYPE_HEX: newHexahedra.push_back((MHexahedron*)e2); break;
       case TYPE_PRI: newPrisms.push_back((MPrism*)e2); break;
       case TYPE_PYR: newPyramids.push_back((MPyramid*)e2); break;
+      case TYPE_TRIH: newTrihedra.push_back((MTrihedron*)e2); break;
       }
     }
     gr->deleteVertexArrays();
@@ -2324,16 +2456,18 @@ void GModel::createTopologyFromFaces(std::vector<discreteFace*> &discFaces, int
     for(unsigned int i = 0; i < gr->hexahedra.size(); i++) delete gr->hexahedra[i];
     for(unsigned int i = 0; i < gr->prisms.size(); i++) delete gr->prisms[i];
     for(unsigned int i = 0; i < gr->pyramids.size(); i++) delete gr->pyramids[i];
+    for(unsigned int i = 0; i < gr->trihedra.size(); i++) delete gr->trihedra[i];
     gr->tetrahedra = newTetrahedra;
     gr->hexahedra = newHexahedra;
     gr->prisms = newPrisms;
     gr->pyramids = newPyramids;
+    gr->trihedra = newTrihedra;
   }
 
   Msg::Debug("Done creating topology for edges");
 }
 
-void makeSimplyConnected(std::map<int, std::vector<MElement*> > elements[10])
+void makeSimplyConnected(std::map<int, std::vector<MElement*> > elements[11])
 {
   //only for tetras and triangles
   Msg::Info("Make simply connected regions and surfaces.");
@@ -2502,7 +2636,7 @@ GModel *GModel::buildCutGModel(gLevelset *ls, bool cutElem, bool saveTri)
   else
     CTX::instance()->mesh.saveTri = 0;
 
-  std::map<int, std::vector<MElement*> > elements[10];
+  std::map<int, std::vector<MElement*> > elements[11];
   std::map<int, std::map<int, std::string> > physicals[4];
   std::map<int, MVertex*> vertexMap;
 
diff --git a/Geo/GModel.h b/Geo/GModel.h
index 9996a42c4149be4115fb99b2e8f46cf65a297c41..11411531bbfee66df6c74329fd7142ca4901d82c 100644
--- a/Geo/GModel.h
+++ b/Geo/GModel.h
@@ -362,7 +362,7 @@ class GModel
 
   // get the number of each type of element in the mesh at the largest
   // dimension and return the dimension
-  int getNumMeshElements(unsigned c[5]);
+  int getNumMeshElements(unsigned c[6]);
 
   // access a mesh element by coordinates (using an octree search)
   MElement *getMeshElementByCoord(SPoint3 &p, int dim=-1, bool strict=true);
@@ -467,6 +467,7 @@ class GModel
 
   // Ensure that the Jacobian of all volume elements is positive
   bool setAllVolumesPositive();
+  void setAllVolumesPositiveTopology();
 
   // make the mesh a high order mesh at order N
   // linear is 1 if the high order points are not placed on the geometry of the model
diff --git a/Geo/GModelIO_MSH.cpp b/Geo/GModelIO_MSH.cpp
index 0ee171ed0ed307882a1d477b6a4ab5c449c500e8..6562f53a3a88089f774b9207d41e24ac801102b3 100644
--- a/Geo/GModelIO_MSH.cpp
+++ b/Geo/GModelIO_MSH.cpp
@@ -17,6 +17,7 @@
 #include "MHexahedron.h"
 #include "MPrism.h"
 #include "MPyramid.h"
+#include "MTrihedron.h"
 #include "StringUtils.h"
 #include "discreteVertex.h"
 #include "discreteEdge.h"
@@ -170,7 +171,7 @@ int GModel::readMSH(const std::string &name)
   double version = 0.;
   bool binary = false, swap = false, postpro = false;
   int minVertex = 0;
-  std::map<int, std::vector<MElement*> > elements[10];
+  std::map<int, std::vector<MElement*> > elements[11];
 
   while(1) {
 
@@ -406,8 +407,9 @@ int GModel::readMSH(const std::string &name)
         case TYPE_HEX: elements[5][entity].push_back(element); break;
         case TYPE_PRI: elements[6][entity].push_back(element); break;
         case TYPE_PYR: elements[7][entity].push_back(element); break;
-        case TYPE_POLYG: elements[8][entity].push_back(element); break;
-        case TYPE_POLYH: elements[9][entity].push_back(element); break;
+        case TYPE_TRIH: elements[8][entity].push_back(element); break;
+        case TYPE_POLYG: elements[9][entity].push_back(element); break;
+        case TYPE_POLYH: elements[10][entity].push_back(element); break;
         }
         if(numElements > 100000)
           Msg::ProgressMeter(i + 1, numElements, true, "Reading elements");
@@ -665,6 +667,9 @@ int GModel::writeMSH(const std::string &name, double version, bool binary,
   for(riter it = firstRegion(); it != lastRegion(); ++it)
     writeElementsMSH(fp, this, *it, (*it)->pyramids, saveAll, saveSinglePartition,
                      binary);
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    writeElementsMSH(fp, this, *it, (*it)->trihedra, saveAll, saveSinglePartition,
+                     binary);
   for(fiter it = firstFace(); it != lastFace(); ++it)
     writeElementsMSH(fp, this, *it, (*it)->triangles, saveAll, saveSinglePartition,
                      binary);
diff --git a/Geo/GModelIO_MSH2.cpp b/Geo/GModelIO_MSH2.cpp
index a99fd2f459fa305b03a2c39abba6fdb867cf5c91..db97baf5987f4c80aca3195f16738ba1a35e41e1 100644
--- a/Geo/GModelIO_MSH2.cpp
+++ b/Geo/GModelIO_MSH2.cpp
@@ -847,6 +847,7 @@ static int getNumElementsMSH(GModel *m, bool saveAll, int saveSinglePartition)
         if((*it)->polyhedra[i]->ownsParent())
           n += (saveAll ? 1 : (*it)->physicals.size());
     }
+    n -= (*it)->trihedra.size();
   }
   return n;
 }
diff --git a/Geo/GModelVertexArrays.cpp b/Geo/GModelVertexArrays.cpp
index d3a6c0992482f700320e8188cdb092e3e851b76e..f45a2f7f1a999fc1a053a604942090cbbdfe31ec 100644
--- a/Geo/GModelVertexArrays.cpp
+++ b/Geo/GModelVertexArrays.cpp
@@ -14,6 +14,7 @@
 #include "MHexahedron.h"
 #include "MPrism.h"
 #include "MPyramid.h"
+#include "MTrihedron.h"
 #include "MElementCut.h"
 #include "Context.h"
 #include "VertexArray.h"
@@ -56,6 +57,7 @@ static unsigned int getColorByElement(MElement *ele)
     case TYPE_HEX: return CTX::instance()->color.mesh.hexahedron;
     case TYPE_PRI: return CTX::instance()->color.mesh.prism;
     case TYPE_PYR: return CTX::instance()->color.mesh.pyramid;
+    case TYPE_TRIH: return CTX::instance()->color.mesh.trihedron;
     default: return CTX::instance()->color.mesh.vertex;
     }
   }
@@ -353,7 +355,7 @@ class initMeshGRegion {
       for(unsigned int i = 0; i < r->polyhedra.size(); i++)
         numLP += 2 * r->polyhedra[i]->getNumEdges();
       num += (12 * r->tetrahedra.size() + 24 * r->hexahedra.size() +
-              18 * r->prisms.size() + 16 * r->pyramids.size() + numLP) / 4;
+              18 * r->prisms.size() + 16 * r->pyramids.size() + 10 * r->trihedra.size() + numLP) / 4;
       num = _estimateIfClipped(num);
       if(CTX::instance()->mesh.explode != 1.) num *= 4;
       if(_curved) num *= 2;
@@ -368,7 +370,7 @@ class initMeshGRegion {
       for(unsigned int i = 0; i < r->polyhedra.size(); i++)
         numFP += r->polyhedra[i]->getNumFaces();
       num += (4 * r->tetrahedra.size() + 12 * r->hexahedra.size() +
-              8 * r->prisms.size() + 6 * r->pyramids.size() + numFP) / 2;
+              8 * r->prisms.size() + 6 * r->pyramids.size() + 4 * r->trihedra.size() + numFP) / 2;
       num = _estimateIfClipped(num);
       if(CTX::instance()->mesh.explode != 1.) num *= 2;
       if(_curved) num *= 4;
@@ -384,7 +386,8 @@ class initMeshGRegion {
       (CTX::instance()->mesh.tetrahedra && areAllElementsVisible(r->tetrahedra) &&
        CTX::instance()->mesh.hexahedra && areAllElementsVisible(r->hexahedra) &&
        CTX::instance()->mesh.prisms && areAllElementsVisible(r->prisms) &&
-       CTX::instance()->mesh.pyramids && areAllElementsVisible(r->pyramids));
+       CTX::instance()->mesh.pyramids && areAllElementsVisible(r->pyramids) &&
+       CTX::instance()->mesh.trihedra && areAllElementsVisible(r->trihedra));
 
     bool edg = CTX::instance()->mesh.volumesEdges;
     bool fac = CTX::instance()->mesh.volumesFaces;
@@ -392,13 +395,15 @@ class initMeshGRegion {
       _curved = (areSomeElementsCurved(r->tetrahedra) ||
                  areSomeElementsCurved(r->hexahedra) ||
                  areSomeElementsCurved(r->prisms) ||
-                 areSomeElementsCurved(r->pyramids));
+                 areSomeElementsCurved(r->pyramids) ||
+                 areSomeElementsCurved(r->trihedra));
       r->va_lines = new VertexArray(2, _estimateNumLines(r));
       r->va_triangles = new VertexArray(3, _estimateNumTriangles(r));
       if(CTX::instance()->mesh.tetrahedra) addElementsInArrays(r, r->tetrahedra, edg, fac);
       if(CTX::instance()->mesh.hexahedra) addElementsInArrays(r, r->hexahedra, edg, fac);
       if(CTX::instance()->mesh.prisms) addElementsInArrays(r, r->prisms, edg, fac);
       if(CTX::instance()->mesh.pyramids) addElementsInArrays(r, r->pyramids, edg, fac);
+      if(CTX::instance()->mesh.trihedra) addElementsInArrays(r, r->trihedra, edg, fac);
       addElementsInArrays(r, r->polyhedra, edg, fac);
       r->va_lines->finalize();
       r->va_triangles->finalize();
diff --git a/Geo/GRegion.cpp b/Geo/GRegion.cpp
index 08de35b82016fc2aceaa6c6cbf9d20fd4c59048a..778a04dfcd94df1fcac94f0bf4013a361bf65d99 100644
--- a/Geo/GRegion.cpp
+++ b/Geo/GRegion.cpp
@@ -11,6 +11,7 @@
 #include "MHexahedron.h"
 #include "MPrism.h"
 #include "MPyramid.h"
+#include "MTrihedron.h"
 #include "MElementCut.h"
 #include "GmshMessage.h"
 #include "VertexArray.h"
@@ -45,8 +46,11 @@ void GRegion::deleteMesh()
   prisms.clear();
   for(unsigned int i = 0; i < pyramids.size(); i++) delete pyramids[i];
   pyramids.clear();
+  for(unsigned int i = 0; i < trihedra.size(); i++) delete trihedra[i];
+  trihedra.clear();
   for(unsigned int i = 0; i < polyhedra.size(); i++) delete polyhedra[i];
   polyhedra.clear();
+
   deleteVertexArrays();
   model()->destroyMeshCaches();
 }
@@ -54,7 +58,7 @@ void GRegion::deleteMesh()
 unsigned int GRegion::getNumMeshElements()
 {
   return tetrahedra.size() + hexahedra.size() + prisms.size() + pyramids.size() +
-    polyhedra.size();
+    trihedra.size() + polyhedra.size();
 }
 
 unsigned int GRegion::getNumMeshParentElements()
@@ -72,7 +76,8 @@ void GRegion::getNumMeshElements(unsigned *const c) const
   c[1] += hexahedra.size();
   c[2] += prisms.size();
   c[3] += pyramids.size();
-  c[4] += polyhedra.size();
+  c[4] += trihedra.size();
+  c[5] += polyhedra.size();
 }
 
 MElement *const *GRegion::getStartElementType(int type) const
@@ -91,6 +96,9 @@ MElement *const *GRegion::getStartElementType(int type) const
     if(pyramids.empty()) return 0; // msvc would throw an exception
     return reinterpret_cast<MElement *const *>(&pyramids[0]);
   case 4:
+    if(trihedra.empty()) return 0;
+    return reinterpret_cast<MElement *const *>(&trihedra[0]);
+  case 5:
     if(polyhedra.empty()) return 0;
     return reinterpret_cast<MElement *const *>(&polyhedra[0]);
   }
@@ -109,9 +117,14 @@ MElement *GRegion::getMeshElement(unsigned int index)
           pyramids.size())
     return pyramids[index - tetrahedra.size() - hexahedra.size() - prisms.size()];
   else if(index < tetrahedra.size() + hexahedra.size() + prisms.size() +
-          pyramids.size() + polyhedra.size())
+          pyramids.size() + trihedra.size())
+    return trihedra[index - tetrahedra.size() - hexahedra.size() - prisms.size() -
+                    pyramids.size()];
+  else if(index < tetrahedra.size() + hexahedra.size() + prisms.size() +
+          pyramids.size() + trihedra.size() + polyhedra.size())
     return polyhedra[index - tetrahedra.size() - hexahedra.size() - prisms.size() -
-                     pyramids.size()];
+                     pyramids.size() - trihedra.size()];
+
   return 0;
 }
 
diff --git a/Geo/GRegion.h b/Geo/GRegion.h
index d05cdf6e845737cf5277528ae5895e90b32d93a0..2812a8a8f7c8fb253295c24095c65e1401a9a944 100644
--- a/Geo/GRegion.h
+++ b/Geo/GRegion.h
@@ -12,6 +12,7 @@
 #include <stdio.h>
 #include "GEntity.h"
 #include "boundaryLayersData.h"
+#include "GmshDefines.h"
 
 class MElement;
 class MTetrahedron;
@@ -19,6 +20,7 @@ class MHexahedron;
 class MPrism;
 class MPyramid;
 class MPolyhedron;
+class MTrihedron;
 class ExtrudeParams;
 class GRegionCompound;
 class BoundaryLayerColumns;
@@ -90,7 +92,7 @@ class GRegion : public GEntity {
   virtual void writeGEO(FILE *fp);
 
   // number of types of elements
-  int getNumElementTypes() const { return 5; }
+  int getNumElementTypes() const { return 6; }
 
   // get total/by-type number of elements in the mesh
   unsigned int getNumMeshElements();
@@ -131,13 +133,41 @@ class GRegion : public GEntity {
   std::vector<MHexahedron*> hexahedra;
   std::vector<MPrism*> prisms;
   std::vector<MPyramid*> pyramids;
+  std::vector<MTrihedron*> trihedra;
   std::vector<MPolyhedron*> polyhedra;
 
   void addTetrahedron(MTetrahedron *t){ tetrahedra.push_back(t); }
+  
   void addHexahedron(MHexahedron *h){ hexahedra.push_back(h); }
   void addPrism(MPrism *p){ prisms.push_back(p); }
   void addPyramid(MPyramid *p){ pyramids.push_back(p); }
   void addPolyhedron(MPolyhedron *p){ polyhedra.push_back(p); }
+  void addTrihedron(MTrihedron *t){ trihedra.push_back(t); }
+  void addElement(int type, MElement *e){
+    switch (type){
+    case TYPE_TET:
+      addTetrahedron((MTetrahedron*) e);
+      break;
+    case TYPE_HEX:
+      addHexahedron((MHexahedron*) e);
+      break;
+    case TYPE_PRI:
+      addPrism((MPrism*) e);
+      break;
+    case TYPE_PYR:
+      addPyramid((MPyramid*) e);
+      break;
+    case TYPE_TRIH:
+      addTrihedron((MTrihedron*) e);
+      break;
+    case TYPE_POLYH:
+      addPolyhedron((MPolyhedron*) e);
+      break;
+    default:
+      Msg::Fatal("Trying to add unsupported element");
+    }
+  }
+  
   // get the boundary layer columns
   BoundaryLayerColumns *getColumns () {return &_columns;}
 };
diff --git a/Geo/GeomMeshMatcher.cpp b/Geo/GeomMeshMatcher.cpp
index a678a2be8f98d42b05d5b190fadcb884a556ace6..61e6e3d2a5f68a11b209a4f4bcf67e8cf6c0abed 100644
--- a/Geo/GeomMeshMatcher.cpp
+++ b/Geo/GeomMeshMatcher.cpp
@@ -22,6 +22,7 @@
 #include "MVertex.h"
 #include "MLine.h"
 #include "MPyramid.h"
+#include "MTrihedron.h"
 #include "MPrism.h"
 #include "MPoint.h"
 #include "MHexahedron.h"
@@ -722,6 +723,7 @@ void copy_elements (GModel *geom, GModel *mesh, std::map<MVertex*,MVertex*> &_me
     copy_elements<MHexahedron> (dest->hexahedra ,orig->hexahedra ,_mesh_to_geom);
     copy_elements<MPrism>      (dest->prisms    ,orig->prisms    ,_mesh_to_geom);
     copy_elements<MPyramid>    (dest->pyramids  ,orig->pyramids  ,_mesh_to_geom);
+    copy_elements<MTrihedron>  (dest->trihedra  ,orig->trihedra  ,_mesh_to_geom);
   }
 }
 
diff --git a/Geo/MElement.cpp b/Geo/MElement.cpp
index 0424d058c4fadc1b9d3399ce5a6646b3a79c3a91..533d163e7e1fa182c7c355f954270c14f702eac7 100644
--- a/Geo/MElement.cpp
+++ b/Geo/MElement.cpp
@@ -17,6 +17,7 @@
 #include "MHexahedron.h"
 #include "MPrism.h"
 #include "MPyramid.h"
+#include "MTrihedron.h"
 #include "MElementCut.h"
 #include "MSubElement.h"
 #include "GEntity.h"
@@ -116,6 +117,9 @@ MElement* MElement::createElement(int tag, const std::vector<MVertex*> &vertices
     else
       return new MPrismN(vertices, order, num, part);
 
+  case TYPE_TRIH:
+    return new MTrihedron(vertices, num, part);
+    
   case TYPE_HEX:
     if (order == 1)
       return new MHexahedron(vertices, num, part);
@@ -1547,6 +1551,7 @@ int MElement::getInfoMSH(const int typeMSH, const char **const name)
   case MSH_PYR_53  : if(name) *name = "Pyramid 53";       return 5 + 8*6;
   case MSH_PYR_61  : if(name) *name = "Pyramid 61";       return 5 + 8*7;
   case MSH_PYR_69  : if(name) *name = "Pyramid 69";       return 5 + 8*8;
+  case MSH_TRIH_4 : if(name) *name = "Trihedron 4";       return 4;
   case MSH_POLYH_  : if(name) *name = "Polyhedron";       return 0;
   case MSH_PNT_SUB : if(name) *name = "Point Xfem";       return 1;
   case MSH_LIN_SUB : if(name) *name = "Line Xfem";        return 2;
@@ -1757,6 +1762,7 @@ MElement *MElementFactory::create(int type, std::vector<MVertex*> &v,
   case MSH_PYR_204: return new MPyramidN(v, 7, num, part);
   case MSH_PYR_285: return new MPyramidN(v, 8, num, part);
   case MSH_PYR_385: return new MPyramidN(v, 9, num, part);
+  case MSH_TRIH_4: return new MTrihedron(v, num, part);
   default:          return 0;
   }
 }
diff --git a/Geo/MTrihedron.cpp b/Geo/MTrihedron.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d9b2193145fbf1c815421da008665d6f4cf60b09
--- /dev/null
+++ b/Geo/MTrihedron.cpp
@@ -0,0 +1,3 @@
+#include "GmshConfig.h"
+#include "MTrihedron.h"
+
diff --git a/Geo/MTrihedron.h b/Geo/MTrihedron.h
new file mode 100644
index 0000000000000000000000000000000000000000..94a3f9c6d985e60dceedcfb38347dc433c86e32f
--- /dev/null
+++ b/Geo/MTrihedron.h
@@ -0,0 +1,162 @@
+#ifndef _MTRIHEDRON_H_
+#define _MTRIHEDRON_H_
+
+#include "MElement.h"
+
+/*
+ * MTrihedron
+ * A MTrihedron is a plane element composed of 
+ * a quadrangle and two triangles.
+ * It serves as an interface between two non-conforming 
+ * elements
+ *
+ *         v
+ *         ^
+ *         |
+ *   3-----------2
+ *   |'\   |     |
+ *   |  '\ |     |
+ *   |     +---- | --> u
+ *   |      '\   |
+ *   |        '\ |
+ *   0-----------1
+ *
+ */
+
+class MTrihedron : public MElement {
+ protected:
+  MVertex *_v[4];
+  void _getEdgeVertices(const int num, std::vector<MVertex*> &v) const
+  {
+    v[0] = _v[edges_trihedron(num, 0)];
+    v[1] = _v[edges_trihedron(num, 1)];
+  }
+    void _getFaceVertices(const int num, std::vector<MVertex*> &v) const
+  {
+    if(num > 0) {
+      v[0] = _v[faces_trihedron(num, 0)];
+      v[1] = _v[faces_trihedron(num, 1)];
+      v[2] = _v[faces_trihedron(num, 2)];
+    }
+    else {
+      v[0] = _v[0];
+      v[1] = _v[1];
+      v[2] = _v[2];
+      v[3] = _v[3];
+    }
+  }
+
+ public:
+    MTrihedron(MVertex *v0, MVertex *v1, MVertex *v2, MVertex *v3, int num=0, int part=0)
+    : MElement(num, part)
+  {
+    _v[0] = v0; _v[1] = v1; _v[2] = v2; _v[3] = v3;
+  }
+  MTrihedron(const std::vector<MVertex*> &v, int num=0, int part=0)
+    : MElement(num, part)
+  {
+    for(int i = 0; i < 4; i++) _v[i] = v[i];
+  }
+  ~MTrihedron(){}
+  virtual int getDim() const { return 3; } //Can have a volume...
+  virtual int getNumVertices() const { return 4; }
+  virtual MVertex *getVertex(int num){ return _v[num]; }
+  virtual const MVertex *getVertex(int num) const { return _v[num]; }
+  virtual void setVertex(int num,  MVertex *v){ _v[num] = v; }
+  virtual int getNumEdges()const{ return 5; }
+  virtual MEdge getEdge(int num) const
+  {
+    return MEdge(_v[edges_trihedron(num, 0)], _v[edges_trihedron(num, 1)]);
+  }
+  virtual int getNumEdgesRep(bool curved){ return 5; }
+  virtual void getEdgeRep(bool curved, int num, double *x, double *y, double *z, SVector3 *n)
+  {
+    MEdge e(getEdge(num));
+    _getEdgeRep(e.getVertex(0), e.getVertex(1), x, y, z, n, 0);
+  }
+    virtual void getEdgeVertices(const int num, std::vector<MVertex*> &v) const
+  {
+    v.resize(2);
+    _getEdgeVertices(num, v);
+  }
+  virtual int getNumFaces(){ return 3; }
+  virtual MFace getFace(int num)
+  {
+    if(num > 0)
+      return MFace(_v[faces_trihedron(num, 0)],
+                   _v[faces_trihedron(num, 1)],
+                   _v[faces_trihedron(num, 2)]);
+    else
+      return MFace(_v[0], _v[1], _v[2], _v[3]);
+  }
+
+  virtual int getNumFacesRep(bool curved){ return 2; }
+  virtual void getFaceRep(bool curved, int num, double *x, double *y, double *z, SVector3 *n)
+  {
+    static const int f[2][3] = {
+      {0, 1, 3}, {1, 2, 3}
+    };
+    _getFaceRep(getVertex(f[num][0]), getVertex(f[num][1]), getVertex(f[num][2]),
+                x, y, z, n);
+  }
+  virtual void getFaceVertices(const int num, std::vector<MVertex*> &v) const
+  {
+    v.resize((num == 0) ? 4 : 3);
+    _getFaceVertices(num, v);
+  }  
+  virtual int getType() const { return TYPE_TRIH; }
+  virtual int getTypeForMSH() const { return MSH_TRIH_4; }
+  
+  virtual void reverse()
+  {
+    MVertex *tmp = _v[1]; _v[1] = _v[3]; _v[3] = tmp;
+  }
+  virtual int getVolumeSign(){return 0;};
+  virtual double getVolume(){return 0;};
+  virtual bool setVolumePositive(){return false;};
+  virtual void getNode(int num, double &u, double &v, double &w) const
+  {
+    w = 0;
+    switch(num) {
+    case 0 : u = -1.; v = -1.; break;
+    case 1 : u =  1.; v = -1.; break;
+    case 2 : u =  1.; v =  1.; break;
+    case 3 : u = -1.; v =  1.; break;
+    default: u =  0.; v =  0.; break;
+    }
+  }
+  virtual SPoint3 barycenterUVW() const
+  {
+    return SPoint3(0., 0., 0.);
+  }
+  virtual bool isInside(double u, double v, double w) const
+  {
+    double tol = _isInsideTolerance;
+    if(u < -(1. + tol) || v < -(1. + tol) || u > (1. + tol) || v > (1. + tol) ||
+       fabs(w) > tol)
+      return false;
+    return true;
+  }
+  static int edges_trihedron(const int edge, const int vert)
+  {
+    static const int e[5][2] = {
+      {0, 1},
+      {1, 2},
+      {2, 3},
+      {3, 0},
+      {1, 3},
+    };
+    return e[edge][vert];
+  }
+  static int faces_trihedron(const int face, const int vert)
+  {
+    static const int f[3][4] = {
+      {0, 1, 2, 3},
+      {0, 3, 1, -1},
+      {1, 3, 2, -1},
+    };
+    return f[face][vert];
+  }
+};
+
+#endif
diff --git a/Graphics/drawMesh.cpp b/Graphics/drawMesh.cpp
index 13410eeb23f58be3cd1e0de831cbd4588d6d1257..3c91e8517259a58966dba7746321e79a0ef7a381 100644
--- a/Graphics/drawMesh.cpp
+++ b/Graphics/drawMesh.cpp
@@ -17,6 +17,7 @@
 #include "MHexahedron.h"
 #include "MPrism.h"
 #include "MPyramid.h"
+#include "MTrihedron.h"
 #include "MElementCut.h"
 #include "Context.h"
 #include "OS.h"
@@ -585,6 +586,10 @@ class drawMeshGRegion {
         drawElementLabels(_ctx, r, r->pyramids, CTX::instance()->mesh.volumesFaces ||
                           CTX::instance()->mesh.surfacesFaces,
                           CTX::instance()->color.mesh.line);
+      if(CTX::instance()->mesh.trihedra)
+        drawElementLabels(_ctx, r, r->trihedra, CTX::instance()->mesh.volumesFaces ||
+                          CTX::instance()->mesh.surfacesFaces,
+                          CTX::instance()->color.mesh.line);
       drawElementLabels(_ctx, r, r->polyhedra, CTX::instance()->mesh.volumesFaces ||
                         CTX::instance()->mesh.surfacesFaces,
                         CTX::instance()->color.mesh.line);
@@ -598,6 +603,7 @@ class drawMeshGRegion {
         if(CTX::instance()->mesh.hexahedra) drawVerticesPerElement(_ctx, r, r->hexahedra);
         if(CTX::instance()->mesh.prisms) drawVerticesPerElement(_ctx, r, r->prisms);
         if(CTX::instance()->mesh.pyramids) drawVerticesPerElement(_ctx, r, r->pyramids);
+        if(CTX::instance()->mesh.trihedra) drawVerticesPerElement(_ctx, r, r->trihedra);
         drawVerticesPerElement(_ctx, r, r->polyhedra);
       }
     }
@@ -607,6 +613,7 @@ class drawMeshGRegion {
       if(CTX::instance()->mesh.hexahedra) drawBarycentricDual(r->hexahedra);
       if(CTX::instance()->mesh.prisms) drawBarycentricDual(r->prisms);
       if(CTX::instance()->mesh.pyramids) drawBarycentricDual(r->pyramids);
+      if(CTX::instance()->mesh.trihedra) drawBarycentricDual(r->trihedra);
       drawBarycentricDual(r->polyhedra);
     }
 
diff --git a/Mesh/Generator.cpp b/Mesh/Generator.cpp
index 779b4885ef9f71449497886f2674a778d8b2d4dd..82b2008ab7e7583f51648368cd5d05b2798f01ed 100644
--- a/Mesh/Generator.cpp
+++ b/Mesh/Generator.cpp
@@ -111,11 +111,12 @@ void GetStatistics(double stat[50], double quality[3][100])
     stat[10] += (*it)->hexahedra.size();
     stat[11] += (*it)->prisms.size();
     stat[12] += (*it)->pyramids.size();
+    stat[13] += (*it)->trihedra.size();
   }
 
-  stat[13] = CTX::instance()->meshTimer[0];
-  stat[14] = CTX::instance()->meshTimer[1];
-  stat[15] = CTX::instance()->meshTimer[2];
+  stat[14] = CTX::instance()->meshTimer[0];
+  stat[15] = CTX::instance()->meshTimer[1];
+  stat[16] = CTX::instance()->meshTimer[2];
 
   if(quality){
     for(int i = 0; i < 3; i++)
@@ -125,7 +126,7 @@ void GetStatistics(double stat[50], double quality[3][100])
     double gamma = 0., gammaMin = 1., gammaMax = 0.;
     double rho = 0., rhoMin = 1., rhoMax = 0.;
 
-    double N = stat[9] + stat[10] + stat[11] + stat[12];
+    double N = stat[9] + stat[10] + stat[11] + stat[12] + stat[13];
     if(N){ // if we have 3D elements
       for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it){
         GetQualityMeasure((*it)->tetrahedra, gamma, gammaMin, gammaMax,
@@ -148,25 +149,25 @@ void GetStatistics(double stat[50], double quality[3][100])
       }
     }
     if(N){
-      stat[17] = minSICN / N; stat[18] = minSICNMin; stat[19] = minSICNMax;
-      stat[20] = gamma / N;   stat[21] = gammaMin;   stat[22] = gammaMax;
-      stat[23] = rho / N;     stat[24] = rhoMin;     stat[25] = rhoMax;
+      stat[18] = minSICN / N; stat[19] = minSICNMin; stat[20] = minSICNMax;
+      stat[21] = gamma / N;   stat[22] = gammaMin;   stat[23] = gammaMax;
+      stat[25] = rho / N;     stat[25] = rhoMin;     stat[26] = rhoMax;
     }
   }
 
 #if defined(HAVE_POST)
-  stat[26] = PView::list.size();
+  stat[27] = PView::list.size();
   for(unsigned int i = 0; i < PView::list.size(); i++) {
     PViewData *data = PView::list[i]->getData(true);
-    stat[27] += data->getNumPoints();
-    stat[28] += data->getNumLines();
-    stat[29] += data->getNumTriangles();
-    stat[30] += data->getNumQuadrangles();
-    stat[31] += data->getNumTetrahedra();
-    stat[32] += data->getNumHexahedra();
-    stat[33] += data->getNumPrisms();
-    stat[34] += data->getNumPyramids();
-    stat[35] += data->getNumStrings2D() + data->getNumStrings3D();
+    stat[28] += data->getNumPoints();
+    stat[29] += data->getNumLines();
+    stat[30] += data->getNumTriangles();
+    stat[31] += data->getNumQuadrangles();
+    stat[32] += data->getNumTetrahedra();
+    stat[33] += data->getNumHexahedra();
+    stat[34] += data->getNumPrisms();
+    stat[35] += data->getNumPyramids();
+    stat[36] += data->getNumStrings2D() + data->getNumStrings3D();
   }
 #endif
 }
@@ -534,9 +535,9 @@ static void Mesh3D(GModel *m)
         Recombinator rec;
         rec.execute(gr);
         Supplementary sup;
-        sup.execute(gr);
+        //        sup.execute(gr);
         PostOp post;
-        post.execute(gr,0);
+        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();
         nb_hexa_recombination += post.get_nb_hexahedra();
         vol_element_recombination += post.get_vol_elements();
diff --git a/Mesh/meshPartition.cpp b/Mesh/meshPartition.cpp
index 887b30eb1bd74a90940c5f44601da9bceb41a743..bdb33df6fb0d4fba956e9d5a4b44016e31f443f4 100644
--- a/Mesh/meshPartition.cpp
+++ b/Mesh/meshPartition.cpp
@@ -19,6 +19,7 @@
 #include "MHexahedron.h"
 #include "MPrism.h"
 #include "MPyramid.h"
+#include "MTrihedron.h"
 #include "MElementCut.h"
 #include "MPoint.h"
 #include "partitionVertex.h"
@@ -219,6 +220,8 @@ int RenumberMeshElements(std::vector<MElement*> &elements, meshPartitionOptions
         gr->prisms.push_back((MPrism*)(*it));
       else if  ((*it)->getType() == TYPE_PYR)
         gr->pyramids.push_back((MPyramid*)(*it));
+      else if  ((*it)->getType() == TYPE_TRIH)
+        gr->trihedra.push_back((MTrihedron*)(*it));
     }
     tmp_model->add(gr);
     RenumberMesh(tmp_model, options, elements);
@@ -556,7 +559,8 @@ int MakeGraph(GModel *const model, Graph &graph, meshPartitionOptions &options,
     ElemTypeHexa = 1,
     ElemTypePrism = 2,
     ElemTypePyramid = 3,
-    ElemTypePolyh = 4
+    ElemTypeTrih = 4,
+    ElemTypePolyh = 5
   };
   enum {
     ElemTypeTri = 0,
@@ -574,7 +578,7 @@ int MakeGraph(GModel *const model, Graph &graph, meshPartitionOptions &options,
 
 //--Get the dimension of the mesh and count the numbers of elements
 
-      unsigned numElem[5];
+      unsigned numElem[6];
       const int meshDim = model->getNumMeshElements(numElem);
       if(meshDim < 2) {
         Msg::Error("No mesh elements were found");
@@ -613,11 +617,11 @@ int MakeGraph(GModel *const model, Graph &graph, meshPartitionOptions &options,
             const int numGrVert =
               numElem[ElemTypeTetra] + numElem[ElemTypeHexa] +
               numElem[ElemTypePrism] + numElem[ElemTypePyramid] +
-              numElem[ElemTypePolyh];
+              + numElem[ElemTypeTrih] + numElem[ElemTypePolyh];
             const int maxGrEdge =
               (numElem[ElemTypeTetra]*4 + numElem[ElemTypeHexa]*6 +
                numElem[ElemTypePrism]*5 + numElem[ElemTypePyramid]*5 +
-               numElem[ElemTypePolyh]*5)/2;
+               numElem[ElemTypeTrih]*3 + numElem[ElemTypePolyh]*5)/2;
             graph.allocate(numGrVert, maxGrEdge);
             // Make the graph
             MakeGraphDIM<3>(model->firstRegion(), model->lastRegion(),
@@ -1204,6 +1208,7 @@ int CreatePartitionBoundaries(GModel *model, bool createGhostCells, bool createA
       fillit_(faceToElement, (*it)->hexahedra.begin(), (*it)->hexahedra.end());
       fillit_(faceToElement, (*it)->prisms.begin(), (*it)->prisms.end());
       fillit_(faceToElement, (*it)->pyramids.begin(), (*it)->pyramids.end());
+      fillit_(faceToElement, (*it)->trihedra.begin(), (*it)->trihedra.end());
       fillit_(faceToElement, (*it)->polyhedra.begin(), (*it)->polyhedra.end());
     }
     std::multimap<MFace, MElement*, Less_Face>::iterator it = faceToElement.begin();
@@ -1235,6 +1240,7 @@ int CreatePartitionBoundaries(GModel *model, bool createGhostCells, bool createA
         fillit_(edgeToElement, (*it)->hexahedra.begin(), (*it)->hexahedra.end());
         fillit_(edgeToElement, (*it)->prisms.begin(), (*it)->prisms.end());
         fillit_(edgeToElement, (*it)->pyramids.begin(), (*it)->pyramids.end());
+        fillit_(edgeToElement, (*it)->trihedra.begin(), (*it)->trihedra.end());
         fillit_(edgeToElement, (*it)->polyhedra.begin(), (*it)->polyhedra.end());
       }
     }
@@ -1268,6 +1274,7 @@ int CreatePartitionBoundaries(GModel *model, bool createGhostCells, bool createA
         fillit_(vertexToElement, (*it)->hexahedra.begin(), (*it)->hexahedra.end());
         fillit_(vertexToElement, (*it)->prisms.begin(), (*it)->prisms.end());
         fillit_(vertexToElement, (*it)->pyramids.begin(), (*it)->pyramids.end());
+        fillit_(vertexToElement, (*it)->trihedra.begin(), (*it)->trihedra.end());
         fillit_(vertexToElement, (*it)->polyhedra.begin(), (*it)->polyhedra.end());
       }
     }
diff --git a/Mesh/meshPartitionOptions.h b/Mesh/meshPartitionOptions.h
index a3c96fa8771103c41e37fa2a72dc87fbf4910224..52c05574d1f10f00b1060bb589f144b6a6074616 100644
--- a/Mesh/meshPartitionOptions.h
+++ b/Mesh/meshPartitionOptions.h
@@ -78,6 +78,7 @@ class meshPartitionOptions
   int tetWeight;
   int priWeight;
   int pyrWeight;
+  int trihWeight;
   int hexWeight;
 
   // NODAL WEIGHT
@@ -130,6 +131,7 @@ class meshPartitionOptions
     tetWeight = 1;
     priWeight = 1;
     pyrWeight = 1;
+    trihWeight = 0;
     hexWeight = 1;
   }
 };
diff --git a/Mesh/yamakawa.cpp b/Mesh/yamakawa.cpp
index fcef9661f769f368e5a2ed38092632a8e76fa6e9..7a22b59c944109b04d7ca3556a5b08c3ac3188e6 100644
--- a/Mesh/yamakawa.cpp
+++ b/Mesh/yamakawa.cpp
@@ -19,10 +19,15 @@
 #include "MQuadrangle.h"
 #include "MTriangle.h"
 #include "MPyramid.h"
+#include "MTrihedron.h"
 #include "MPrism.h"
 #include "MTetrahedron.h"
 #include "MHexahedron.h"
 
+#include "CondNumBasis.h"
+
+
+
 void export_gregion_mesh(GRegion *gr, string filename)
 {
   // FIXME: use MElement::writeMSH
@@ -2297,6 +2302,111 @@ 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 validFaces(Hex &hex){
+  bool v1, v2, v3, v4, v5, v6;
+  MVertex *a,*b,*c,*d;
+  MVertex *e,*f,*g,*h;
+
+  a = hex.get_a();
+  b = hex.get_b();
+  c = hex.get_c();
+  d = hex.get_d();
+  e = hex.get_e();
+  f = hex.get_f();
+  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);
+
+  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;
@@ -2324,10 +2434,10 @@ 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 1;
+    return (validFaces(hex) && validCondNum(hex));
   }
   else{
-    return 0;
+    return false;
   }
 }
 
@@ -3982,6 +4092,65 @@ bool Supplementary::valid(Prism prism,const std::set<MElement*>& parts){
   }
 }
 
+
+bool validFaces(Prism &prism){
+  bool v1, v2, v3;
+  MVertex *a,*b,*c;
+  MVertex *d, *e,*f;
+
+  a = prism.get_a();
+  b = prism.get_b();
+  c = prism.get_c();
+  d = prism.get_d();
+  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);
+
+  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;
   double eta1,eta2,eta3;
@@ -4002,7 +4171,7 @@ bool Supplementary::valid(Prism prism){
   eta3 = eta(b,c,f,e);
 
   if(eta1>k && eta2>k && eta3>k){
-    return 1;
+    return (validFaces(prism) && validCondNum(prism));
   }
   else{
     return 0;
@@ -4719,7 +4888,7 @@ PostOp::PostOp(){}
 
 PostOp::~PostOp(){}
 
-void PostOp::execute(bool flag){
+void PostOp::execute(int level, bool insertTrihedra){
   GRegion* gr;
   GModel* model = GModel::current();
   GModel::riter it;
@@ -4728,24 +4897,26 @@ void PostOp::execute(bool flag){
     {
       gr = *it;
       if(gr->getNumMeshElements()>0){
-        execute(gr,flag);
+        execute(gr,level, insertTrihedra);
       }
     }
 }
 
-void PostOp::execute(GRegion* gr,bool flag){
+void PostOp::execute(GRegion* gr,int level,bool insertTrihedra){
   printf("................PYRAMIDS................\n");
   estimate1 = 0;
   estimate2 = 0;
   iterations = 0;
 
   build_tuples(gr);
-  init_markings(gr);
-  build_vertex_to_tetrahedra(gr);
-  pyramids1(gr);
-  rearrange(gr);
+  if (level >= 1){
+    init_markings(gr);
+    build_vertex_to_tetrahedra(gr);
+    pyramids1(gr);
+    rearrange(gr);
+  }
 
-  if(flag){
+  if(level >= 2){
     init_markings(gr);
     build_vertex_to_tetrahedra(gr);
     build_vertex_to_pyramids(gr);
@@ -4753,6 +4924,15 @@ void PostOp::execute(GRegion* gr,bool flag){
     rearrange(gr);
   }
 
+  if (insertTrihedra){
+    init_markings(gr);
+    build_vertex_to_tetrahedra(gr);
+    build_vertex_to_pyramids(gr);
+    trihedra(gr);
+    rearrange(gr);
+  }
+    
+
   statistics(gr);
 
   modify_surfaces(gr);
@@ -4807,11 +4987,11 @@ void PostOp::pyramids1(GRegion* gr){
     g = element->getVertex(6);
     h = element->getVertex(7);
 
-    pyramids1(a,b,c,d,gr);
+    pyramids1(b,a,d,c,gr);
     pyramids1(e,f,g,h,gr);
     pyramids1(a,b,f,e,gr);
     pyramids1(b,c,g,f,gr);
-    pyramids1(d,c,g,h,gr);
+    pyramids1(c,d,h,g,gr);
     pyramids1(d,a,e,h,gr);
   }
 
@@ -4844,6 +5024,7 @@ void PostOp::pyramids1(GRegion* gr){
   }
 }
 
+
 void PostOp::pyramids2(GRegion* gr){
   unsigned int i;
   MVertex *a,*b,*c,*d;
@@ -4870,7 +5051,7 @@ void PostOp::pyramids2(GRegion* gr){
 
   for(i=0;i<hexahedra.size();i++){
     element = hexahedra[i];
-
+  
     a = element->getVertex(0);
     b = element->getVertex(1);
     c = element->getVertex(2);
@@ -4933,36 +5114,53 @@ void PostOp::pyramids2(GRegion* gr){
 void PostOp::pyramids1(MVertex* a,MVertex* b,MVertex* c,MVertex* d,GRegion* gr){
   MVertex* vertex;
   std::set<MElement*> bin;
-  std::set<MElement*> bin1;
-  std::set<MElement*> bin2;
+  std::set<MElement*> bin1a;
+  std::set<MElement*> bin2a;
+  std::set<MElement*> bin1b;
+  std::set<MElement*> bin2b;
   std::set<MElement*>::iterator it;
   std::map<MElement*,bool>::iterator it1;
   std::map<MElement*,bool>::iterator it2;
 
-  bin1.clear();
-  bin2.clear();
-  find_tetrahedra(a,c,bin1);
-  find_tetrahedra(b,d,bin2);
+    bin1a.clear();
+  bin1b.clear();
+  bin2a.clear();
+  bin2b.clear();
+  find_tetrahedra(a,c,d,bin1a);
+  find_tetrahedra(a,b,c,bin1b);
+  find_tetrahedra(b,d,a,bin2a);
+  find_tetrahedra(b,c,d,bin2b);
+
+//  bin1.clear();
+//  bin2.clear();
+//  find_tetrahedra(a,c,bin1);
+//  find_tetrahedra(b,d,bin2);
 
   bin.clear();
-  for(it=bin1.begin();it!=bin1.end();it++){
+  for(it=bin1a.begin();it!=bin1a.end();it++){
     bin.insert(*it);
   }
-  for(it=bin2.begin();it!=bin2.end();it++){
+  for(it=bin1b.begin();it!=bin1b.end();it++){
+    bin.insert(*it);
+  }
+  for(it=bin2a.begin();it!=bin2a.end();it++){
+    bin.insert(*it);
+  }
+  for(it=bin2b.begin();it!=bin2b.end();it++){
     bin.insert(*it);
   }
 
-  if(bin.size()==2){
-    it = bin.begin();
-    it1 = markings.find(*it);
+  if(bin.size()==2){ //2 tetrahedra on face
+    it = bin.begin(); 
+    it1 = markings.find(*it); //1st tetrahedra
     it++;
-    it2 = markings.find(*it);
+    it2 = markings.find(*it); //2nd tetrahedra
 
     if(it1->second==0 && it2->second==0){
       vertex = find(a,b,c,d,*it);
-
-      if(vertex!=0){
-        gr->addPyramid(new MPyramid(a,b,c,d,vertex));
+      MVertex* vertex1 = find(a,b,c,d,*bin.begin());
+      if(vertex!=0 && vertex == vertex1){//Seb added this 2nd condition. Is it necessary??
+        gr->addPyramid(new MPyramid(a,d,c,b,vertex));
         it1->second = 1;
         it2->second = 1;
       }
@@ -4970,6 +5168,93 @@ void PostOp::pyramids1(MVertex* a,MVertex* b,MVertex* c,MVertex* d,GRegion* gr){
   }
 }
 
+
+void PostOp::trihedra(GRegion* gr){
+  unsigned int i;
+  MVertex *a,*b,*c,*d;
+  MVertex *e,*f,*g,*h;
+  MElement* element;
+  std::vector<MElement*> hexahedra;
+  std::vector<MElement*> prisms;
+  std::vector<MTetrahedron*> opt1;
+  std::vector<MPyramid*> opt2;
+  std::map<MElement*,bool>::iterator it;
+
+  hexahedra.clear();
+  prisms.clear();
+
+  for(i=0;i<gr->getNumMeshElements();i++){
+    element = gr->getMeshElement(i);
+    if(eight(element)){
+      hexahedra.push_back(element);
+    }
+    else if(six(element)){
+      prisms.push_back(element);
+    }
+  }
+
+  for(i=0;i<hexahedra.size();i++){
+    element = hexahedra[i];
+  
+    a = element->getVertex(0);
+    b = element->getVertex(1);
+    c = element->getVertex(2);
+    d = element->getVertex(3);
+    e = element->getVertex(4);
+    f = element->getVertex(5);
+    g = element->getVertex(6);
+    h = element->getVertex(7);
+
+    trihedra(a,b,c,d,gr);
+    trihedra(e,f,g,h,gr);
+    trihedra(a,b,f,e,gr);
+    trihedra(b,c,g,f,gr);
+    trihedra(d,c,g,h,gr);
+    trihedra(d,a,e,h,gr);
+  }
+
+  for(i=0;i<prisms.size();i++){
+    element = prisms[i];
+
+    a = element->getVertex(0);
+    b = element->getVertex(1);
+    c = element->getVertex(2);
+    d = element->getVertex(3);
+    e = element->getVertex(4);
+    f = element->getVertex(5);
+
+    trihedra(a,d,f,c,gr);
+    trihedra(a,b,e,d,gr);
+    trihedra(b,c,f,e,gr);
+  }
+
+}
+
+void PostOp::trihedra(MVertex* a,MVertex* b,MVertex* c,MVertex* d,GRegion* gr){
+  std::set<MElement*> diag1a;
+  std::set<MElement*> diag1b;
+  std::set<MElement*> diag2a;
+  std::set<MElement*> diag2b;
+
+  find_tetrahedra(a,b,c,diag1a);
+  find_tetrahedra(a,c,d,diag1b);
+  find_tetrahedra(b,c,d,diag2a);
+  find_tetrahedra(a,b,d,diag2b);
+  find_pyramids_from_tri(a,b,c,diag1a);
+  find_pyramids_from_tri(a,c,d,diag1b);
+  find_pyramids_from_tri(b,c,d,diag2a);
+  find_pyramids_from_tri(a,b,d,diag2b);
+  if(diag1a.size()==1 && diag1b.size()==1){
+    MTrihedron *trih = new MTrihedron(b, c, d, a);
+    //    if (diag1a.size() != 1 || diag1b.size() != 1) Msg::Error("Quad face neighbor with %i+%i triangular faces (other diagonal: %i+%i) Trihedron: %i",diag1a.size(),diag1b.size(),diag2a.size(),diag2b.size(),trih->getNum());
+    gr->addTrihedron(trih);
+  }else if(diag2a.size()==1 && diag2b.size()==1){
+    MTrihedron *trih = new MTrihedron(a, b, c, d);
+    //    if (diag2a.size() != 1 || diag2b.size() != 1) Msg::Error("Quad face neighbor with %i+%i triangular faces (other diagonal: %i+%i) Trihedron: %i",diag2a.size(),diag2b.size(),diag1a.size(),diag1b.size(),trih->getNum());
+    gr->addTrihedron(trih);
+  }
+}
+
 void PostOp::pyramids2(MVertex* a,MVertex* b,MVertex* c,MVertex* d,GRegion* gr){
   bool flag;
   double x,y,z;
@@ -5055,6 +5340,7 @@ void PostOp::pyramids2(MVertex* a,MVertex* b,MVertex* c,MVertex* d,GRegion* gr){
     gr->addMeshVertex(mid);
 
     temp2 = new MPyramid(a,b,c,d,mid);
+    
     gr->addPyramid(temp2);
     markings.insert(std::pair<MElement*,bool>(temp2,false));
     build_vertex_to_pyramids(temp2);
@@ -5173,6 +5459,7 @@ void PostOp::statistics(GRegion* gr){
   nbr6 = 0;
   nbr5 = 0;
   nbr4 = 0;
+  nbr4Trih = 0;
   vol = 0.0;
   vol8 = 0.0;
   vol6 = 0.0;
@@ -5202,6 +5489,10 @@ void PostOp::statistics(GRegion* gr){
       vol4 = vol4 + element->getVolume();
     }
 
+    if(fourTrih(element)){
+      nbr4Trih = nbr4Trih + 1;
+    }
+
     nbr = nbr + 1;
 
     if(!five(element)){
@@ -5218,6 +5509,7 @@ void PostOp::statistics(GRegion* gr){
   printf("  percentage of prisms : %.2f\n",nbr6*100.0/nbr);
   printf("  percentage of pyramids : %.2f\n",nbr5*100.0/nbr);
   printf("  percentage of tetrahedra : %.2f\n",nbr4*100.0/nbr);
+  printf("  percentage of trihedra : %.2f\n",nbr4Trih*100.0/nbr);
   printf("Volume :\n");
   printf("  percentage of hexahedra : %.2f\n",vol8*100.0/vol);
   printf("  percentage of prisms : %.2f\n",vol6*100.0/vol);
@@ -5412,7 +5704,12 @@ void PostOp::modify_surfaces(MVertex* a,MVertex* b,MVertex* c,MVertex* d){
 }
 
 bool PostOp::four(MElement* element){
-  if(element->getNumVertices()==4) return 1;
+  if(element->getNumVertices()==4 && element->getNumFaces()==4) return 1;
+  else return 0;
+}
+
+bool PostOp::fourTrih(MElement* element){
+  if(element->getNumVertices()==4 && element->getNumFaces()==3) return 1;
   else return 0;
 }
 
@@ -5610,6 +5907,72 @@ void PostOp::find_tetrahedra(MVertex* v1,MVertex* v2,std::set<MElement*>& final)
   }
 }
 
+void PostOp::find_tetrahedra(MVertex* v1,MVertex* v2,MVertex* v3,std::set<MElement*>& final){
+  std::map<MVertex*,std::set<MElement*> >::iterator it1;
+  std::map<MVertex*,std::set<MElement*> >::iterator it2;
+  std::map<MVertex*,std::set<MElement*> >::iterator it3;
+
+  it1 = vertex_to_tetrahedra.find(v1);
+  it2 = vertex_to_tetrahedra.find(v2);
+  it3 = vertex_to_tetrahedra.find(v3);
+
+  std::set<MElement*> buf;
+
+  if(it1!=vertex_to_tetrahedra.end() && it2!=vertex_to_tetrahedra.end()  && it3!=vertex_to_tetrahedra.end()){
+    intersection(it1->second,it2->second,buf);
+    intersection(buf,it3->second,final);
+    
+  }
+}
+
+void PostOp::find_pyramids_from_tri(MVertex* v1,MVertex* v2,MVertex* v3,std::set<MElement*>& final){
+  bool flag;
+  std::set<MElement*>::iterator it;
+  std::map<MVertex*,std::set<MElement*> >::iterator it1;
+  std::map<MVertex*,std::set<MElement*> >::iterator it2;
+  std::map<MVertex*,std::set<MElement*> >::iterator it3;
+  std::set<MElement*> temp1, temp2;
+
+  it1 = vertex_to_pyramids.find(v1);
+  it2 = vertex_to_pyramids.find(v2);
+  it3 = vertex_to_pyramids.find(v3);
+
+  temp1.clear();
+  temp2.clear();
+
+  if(it1!=vertex_to_pyramids.end() && it2!=vertex_to_pyramids.end() && it3!=vertex_to_pyramids.end()){
+    intersection(it1->second,it2->second,temp1);
+    intersection(temp1,it3->second,temp2);
+  }
+  //Now temp2 contains pyramids containing the 3 vertices
+  //We check that one vertex is on the summit and the others are neighbors in the base
+  for(it=temp2.begin();it!=temp2.end();it++){
+    flag = false;
+    if (v1 == (*it)->getVertex(4)){
+      flag = (equal(v2,v3,(*it)->getVertex(0),(*it)->getVertex(1)) ||
+              equal(v2,v3,(*it)->getVertex(1),(*it)->getVertex(2)) ||
+              equal(v2,v3,(*it)->getVertex(2),(*it)->getVertex(3)) ||
+              equal(v2,v3,(*it)->getVertex(3),(*it)->getVertex(0)));
+    }      
+    if (v2 == (*it)->getVertex(4)){
+      flag = (equal(v1,v3,(*it)->getVertex(0),(*it)->getVertex(1)) ||
+              equal(v1,v3,(*it)->getVertex(1),(*it)->getVertex(2)) ||
+              equal(v1,v3,(*it)->getVertex(2),(*it)->getVertex(3)) ||
+              equal(v1,v3,(*it)->getVertex(3),(*it)->getVertex(0)));
+    }
+    if (v3 == (*it)->getVertex(4)){
+      flag = (equal(v1,v2,(*it)->getVertex(0),(*it)->getVertex(1)) ||
+              equal(v1,v2,(*it)->getVertex(1),(*it)->getVertex(2)) ||
+              equal(v1,v2,(*it)->getVertex(2),(*it)->getVertex(3)) ||
+              equal(v1,v2,(*it)->getVertex(3),(*it)->getVertex(0)));
+    }
+    if(flag){
+      final.insert(*it);
+    }
+  }
+}
+
+
 void PostOp::find_pyramids(MVertex* v1,MVertex* v2,std::set<MElement*>& final){
   bool flag1,flag2,flag3,flag4;
   bool flag5,flag6,flag7,flag8;
@@ -5642,6 +6005,39 @@ void PostOp::find_pyramids(MVertex* v1,MVertex* v2,std::set<MElement*>& final){
   }
 }
 
+//void PostOp::find_pyramids(MVertex* v1,MVertex* v2,std::set<MElement*>& final){
+//  bool flag1,flag2,flag3,flag4;
+//  bool flag5,flag6,flag7,flag8;
+//  std::set<MElement*>::iterator it;
+//  std::map<MVertex*,std::set<MElement*> >::iterator it1;
+//  std::map<MVertex*,std::set<MElement*> >::iterator it2;
+//  std::set<MElement*> temp;
+//
+//  it1 = vertex_to_pyramids.find(v1);
+//  it2 = vertex_to_pyramids.find(v2);
+//
+//  temp.clear();
+//
+//  if(it1!=vertex_to_pyramids.end() && it2!=vertex_to_pyramids.end()){
+//    intersection(it1->second,it2->second,temp);
+//  }
+//
+//  for(it=temp.begin();it!=temp.end();it++){
+//    flag1 = equal(v1,v2,(*it)->getVertex(0),(*it)->getVertex(1));
+//    flag2 = equal(v1,v2,(*it)->getVertex(1),(*it)->getVertex(2));
+//    flag3 = equal(v1,v2,(*it)->getVertex(2),(*it)->getVertex(3));
+//    flag4 = equal(v1,v2,(*it)->getVertex(3),(*it)->getVertex(0));
+//    flag5 = equal(v1,v2,(*it)->getVertex(0),(*it)->getVertex(4));
+//    flag6 = equal(v1,v2,(*it)->getVertex(1),(*it)->getVertex(4));
+//    flag7 = equal(v1,v2,(*it)->getVertex(2),(*it)->getVertex(4));
+//    flag8 = equal(v1,v2,(*it)->getVertex(3),(*it)->getVertex(4));
+//    if(flag1 || flag2 || flag3 || flag4 || flag5 || flag6 || flag7 || flag8){
+//      final.insert(*it);
+//    }
+//  }
+//}
+
+
 void PostOp::intersection(const std::set<MElement*>& bin1,const std::set<MElement*>& bin2,std::set<MElement*>& final){
   std::set_intersection(bin1.begin(),bin1.end(),bin2.begin(),bin2.end(),std::inserter(final,final.end()));
 }
@@ -6221,7 +6617,7 @@ Recombinator_Graph::~Recombinator_Graph(){
 
 void Recombinator_Graph::createBlossomInfo(){
 
-  throw;
+ throw;
 
 
   GRegion* gr;
@@ -6606,7 +7002,8 @@ void Recombinator_Graph::merge_clique(GRegion* gr, cliques_losses_graph<Hex*> &c
 
       // inserting the hex
       quality = quality + current_hex->get_quality();
-      gr->addHexahedron(new MHexahedron(current_hex->get_a(), current_hex->get_b(),current_hex->get_c(),current_hex->get_d(),current_hex->get_e(),current_hex->get_f(),current_hex->get_g(),current_hex->get_h()));
+      MHexahedron *h=new MHexahedron(current_hex->get_a(), current_hex->get_b(),current_hex->get_c(),current_hex->get_d(),current_hex->get_e(),current_hex->get_f(),current_hex->get_g(),current_hex->get_h());
+      gr->addHexahedron(h);
       count++;
       //      hex_to_export.insert(current_hex);
       //      cout << " inserting " << current_hex << " made of ";
diff --git a/Mesh/yamakawa.h b/Mesh/yamakawa.h
index d3e005603dec6ee34bd040abbf76f2a33e7d3d23..ec7b9b07ae33ba572cd5209c131eea315785a291 100644
--- a/Mesh/yamakawa.h
+++ b/Mesh/yamakawa.h
@@ -609,7 +609,7 @@ public:
 
 class PostOp{
 private:
-  int nbr,nbr8,nbr6,nbr5,nbr4;
+  int nbr,nbr8,nbr6,nbr5,nbr4,nbr4Trih;
   double vol,vol8,vol6,vol5,vol4;
   int estimate1;
   int estimate2;
@@ -623,8 +623,9 @@ public:
   PostOp();
   ~PostOp();
 
-  void execute(bool);
-  void execute(GRegion*,bool);
+  void execute(int, bool);
+  //0: no pyramid, 1: single-step, 2: two-steps (conforming), true: fill non-conformities with trihedra
+  void execute(GRegion*,int level, bool addTrihedra);
 
   inline int get_nb_hexahedra()const{return nbr8;};
   inline double get_vol_hexahedra()const{return vol8;};
@@ -634,8 +635,10 @@ public:
   void init_markings(GRegion*);
   void pyramids1(GRegion*);
   void pyramids2(GRegion*);
+  void trihedra(GRegion*);
   void pyramids1(MVertex*,MVertex*,MVertex*,MVertex*,GRegion*);
   void pyramids2(MVertex*,MVertex*,MVertex*,MVertex*,GRegion*);
+  void trihedra(MVertex*,MVertex*,MVertex*,MVertex*,GRegion*);
   void rearrange(GRegion*);
   void statistics(GRegion*);
   void build_tuples(GRegion*);
@@ -643,6 +646,7 @@ public:
   void modify_surfaces(MVertex*,MVertex*,MVertex*,MVertex*);
 
   bool four(MElement*);
+  bool fourTrih(MElement*);
   bool five(MElement*);
   bool six(MElement*);
   bool eight(MElement*);
@@ -656,6 +660,8 @@ public:
 
   MVertex* find(MVertex*,MVertex*,MVertex*,MVertex*,MElement*);
   void find_tetrahedra(MVertex*,MVertex*,std::set<MElement*>&);
+  void find_tetrahedra(MVertex*,MVertex*,MVertex*,std::set<MElement*>&);
+  void find_pyramids_from_tri(MVertex*,MVertex*,MVertex*,std::set<MElement*>&);
   void find_pyramids(MVertex*,MVertex*,std::set<MElement*>&);
 
   void intersection(const std::set<MElement*>&,const std::set<MElement*>&,std::set<MElement*>&);
diff --git a/Numeric/CondNumBasis.cpp b/Numeric/CondNumBasis.cpp
index cf46578ae835c1f96609b4cb66e8db53e113ece2..06a62aeb856f88946968faff6475f9839f359c37 100644
--- a/Numeric/CondNumBasis.cpp
+++ b/Numeric/CondNumBasis.cpp
@@ -315,10 +315,16 @@ CondNumBasis::CondNumBasis(int tag, int cnOrder) :
     _tag(tag), _dim(ElementType::DimensionFromTag(tag)),
     _condNumOrder(cnOrder >= 0 ? cnOrder : condNumOrder(tag))
 {
+  if ( ElementType::ParentTypeFromTag(tag) == TYPE_TRIH){
+    _nCondNumNodes = 1;
+    _nMapNodes = 4;
+    _nPrimMapNodes = 4;
+    return;
+  }
   const bool serendip = false;
 
-  FuncSpaceData data = ( ElementType::ParentTypeFromTag(tag) == TYPE_PYR )? FuncSpaceData(true, tag, true, _condNumOrder+2, _condNumOrder, &serendip) : FuncSpaceData(true, tag, _condNumOrder, &serendip);
-  _gradBasis = BasisFactory::getGradientBasis(data);
+  FuncSpaceData data = ( ElementType::ParentTypeFromTag(tag) == TYPE_PYR )? FuncSpaceData(true, tag, true, _condNumOrder+1, _condNumOrder, &serendip) : FuncSpaceData(true, tag, _condNumOrder, &serendip);
+
   fullMatrix<double> lagPoints;                                  // Sampling points
   gmshGeneratePoints(data, lagPoints);
   _nCondNumNodes = lagPoints.size1();
@@ -381,6 +387,7 @@ int CondNumBasis::condNumOrder(int parentType, int order)
     case TYPE_PRI : return order;
     case TYPE_HEX : return order;
     case TYPE_PYR : return (order == 1) ? 0 : order;
+    case TYPE_TRIH : return 0;
     default :
       Msg::Error("Unknown element type %d, return order 0", parentType);
       return 0;
@@ -398,7 +405,7 @@ inline void CondNumBasis::getInvCondNumGeneral(int nCondNumNodes,
                                                const fullMatrix<double> &nodesXYZ,
                                                const fullMatrix<double> &normals,
                                                fullVector<double> &condNum) const
-{
+{ 
   switch (_dim) {
 
     case 0 : {
@@ -426,6 +433,10 @@ inline void CondNumBasis::getInvCondNumGeneral(int nCondNumNodes,
     }
 
     case 3 : {
+      if (ElementType::ParentTypeFromTag(_tag) == TYPE_TRIH){
+        for (int i = 0; i < nCondNumNodes; i++) condNum(i) = 1.;
+        break;
+      }
       fullMatrix<double> dxyzdX(nCondNumNodes, 3), dxyzdY(nCondNumNodes, 3), dxyzdZ(nCondNumNodes, 3);
       gSMatX.mult(nodesXYZ, dxyzdX);
       gSMatY.mult(nodesXYZ, dxyzdY);
@@ -519,6 +530,17 @@ inline void CondNumBasis::getInvCondNumAndGradientsGeneral(int nCondNumNodes,
     }
 
     case 3 : {
+      if (ElementType::ParentTypeFromTag(_tag) == TYPE_TRIH){
+        for (int i = 0; i < nCondNumNodes; i++) {
+          for (int j = 0; j < _nMapNodes; j++) {
+            IDI(i,j) = 0.;
+            IDI(i,j+1*_nMapNodes) = 0.;
+            IDI(i,j+2*_nMapNodes) = 0.;
+          }
+          IDI(i,3*_nMapNodes) = 1.;
+        }
+        break;
+      }
       fullMatrix<double> dxyzdX(nCondNumNodes,3), dxyzdY(nCondNumNodes,3), dxyzdZ(nCondNumNodes,3);
       gSMatX.mult(nodesXYZ, dxyzdX);
       gSMatY.mult(nodesXYZ, dxyzdY);
diff --git a/Numeric/ElementType.cpp b/Numeric/ElementType.cpp
index 4cf8a204774617dbe5ecda6fb1d46e6d32773b11..6da8698049deeb417b4548e7e337247d21d19c86 100644
--- a/Numeric/ElementType.cpp
+++ b/Numeric/ElementType.cpp
@@ -93,6 +93,9 @@ int ElementType::ParentTypeFromTag(int tag)
     case(MSH_TRI_MINI):
     case(MSH_TET_MINI):
       return TYPE_MINI;
+    case(MSH_TRIH_4):
+      return TYPE_TRIH;
+
     default:
       Msg::Error("Unknown element tag %i for parent type, returning -1.", tag);
       return -1;
@@ -230,6 +233,7 @@ int ElementType::OrderFromTag(int tag)
   case MSH_PYR_53 : return 7;
   case MSH_PYR_61 : return 8;
   case MSH_PYR_69 : return 9;
+  case MSH_TRIH_4 : return 1;
   case MSH_POLYH_ : return 1;
   default :
     Msg::Warning("Unknown element tag %d, assuming order 1.",tag);
@@ -320,6 +324,8 @@ int ElementType::DimensionFromTag(int tag)
     case(MSH_HEX_68):   case(MSH_HEX_80):
     case(MSH_HEX_92):   case(MSH_HEX_104):
 
+    case(MSH_TRIH_4):
+      
     case(MSH_POLYH_):
       return 3;
 
@@ -393,6 +399,8 @@ int ElementType::SerendipityFromTag(int tag)
   case MSH_PYR_140 : case MSH_PYR_204 :
   case MSH_PYR_285 : case MSH_PYR_385 :
 
+  case MSH_TRIH_4 :
+    
     return 0; // Not Serendipity
 
 
@@ -549,6 +557,8 @@ int ElementType::getTag(int parentTag, int order, bool serendip)
     default : Msg::Error("pyramid order %i unknown", order); return 0;
     }
     break;
+  case TYPE_TRIH :
+    return MSH_TRIH_4;
   default : Msg::Warning("unknown element type %i, returning 0", parentTag); return 0;
   }
 }
diff --git a/Post/PViewData.h b/Post/PViewData.h
index fff10746d1df20541006dd7d088419c52029bdc2..b6112e30d5241b02169d293b3f52117e21e015ce 100644
--- a/Post/PViewData.h
+++ b/Post/PViewData.h
@@ -113,6 +113,7 @@ class PViewData {
   virtual int getNumHexahedra(int step=-1){ return 0; }
   virtual int getNumPrisms(int step=-1){ return 0; }
   virtual int getNumPyramids(int step=-1){ return 0; }
+  virtual int getNumTrihedra(int step=-1){ return 0; }
   virtual int getNumPolyhedra(int step=-1){ return 0; }
 
   // return the number of geometrical entities in the view
diff --git a/Post/PViewDataGModel.cpp b/Post/PViewDataGModel.cpp
index 3b00fc528b30660bd27cacd173390fec3129183e..5fccf53ebf8eedbe3395fe75c401b06d2aa410f3 100644
--- a/Post/PViewDataGModel.cpp
+++ b/Post/PViewDataGModel.cpp
@@ -449,6 +449,16 @@ int PViewDataGModel::getNumPyramids(int step)
   return n;
 }
 
+int PViewDataGModel::getNumTrihedra(int step)
+{
+  if(_steps.empty()) return 0;
+  GModel *m = _steps[0]->getModel(); // to generalize
+  int n = 0;
+  for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it)
+    n += (*it)->trihedra.size();
+  return n;
+}
+
 int PViewDataGModel::getNumPolyhedra(int step)
 {
   if(_steps.empty()) return 0;
diff --git a/Post/PViewDataGModel.h b/Post/PViewDataGModel.h
index 3e4e8dfab55eb6ab1892ca6456681849f294c68c..6ccd0fc8ebb69edfe47d898cdaff59d0c425a97c 100644
--- a/Post/PViewDataGModel.h
+++ b/Post/PViewDataGModel.h
@@ -202,6 +202,7 @@ class PViewDataGModel : public PViewData {
   int getNumHexahedra(int step=-1);
   int getNumPrisms(int step=-1);
   int getNumPyramids(int step=-1);
+  int getNumTrihedra(int step=-1);
   int getNumPolyhedra(int step=-1);
   int getNumEntities(int step=-1);
   int getNumElements(int step=-1, int ent=-1);
diff --git a/Post/PViewDataList.cpp b/Post/PViewDataList.cpp
index fe39970349785d9e39e518f3203732c8177614a6..e971ebeb390d87c99c663959ae9d8903be6f20e0 100644
--- a/Post/PViewDataList.cpp
+++ b/Post/PViewDataList.cpp
@@ -20,12 +20,13 @@ PViewDataList::PViewDataList(bool isAdapted)
     NbSG(0), NbVG(0), NbTG(0),
     NbSS(0), NbVS(0), NbTS(0), NbSH(0), NbVH(0), NbTH(0),
     NbSI(0), NbVI(0), NbTI(0), NbSY(0), NbVY(0), NbTY(0),
-    NbSD(0), NbVD(0), NbTD(0), NbT2(0), NbT3(0),
+    NbSR(0), NbVR(0), NbTR(0), NbSD(0), NbVD(0), NbTD(0),
+    NbT2(0), NbT3(0),
     _lastElement(-1), _lastDimension(-1), _lastNumNodes(-1),
     _lastNumComponents(-1), _lastNumValues(-1), _lastNumEdges(-1),
     _lastType(-1), _lastXYZ(0), _lastVal(0), _isAdapted(isAdapted)
 {
-  for(int i = 0; i < 30; i++) _index[i] = 0;
+  for(int i = 0; i < 33; i++) _index[i] = 0;
   polyTotNumNodes[0] = 0.; polyTotNumNodes[1] = 0.;
   polyAgNumNodes[0].push_back(0.); polyAgNumNodes[1].push_back(0.);
 }
@@ -74,6 +75,8 @@ bool PViewDataList::finalize(bool computeMinMax, const std::string &interpolatio
   _stat(TI, 9, NbTI, 6, TYPE_PRI);
   _stat(SY, 1, NbSY, 5, TYPE_PYR); _stat(VY, 3, NbVY, 5, TYPE_PYR);
   _stat(TY, 9, NbTY, 5, TYPE_PYR);
+  _stat(SY, 1, NbSR, 4, TYPE_TRIH); _stat(VY, 3, NbVR, 4, TYPE_TRIH);
+  _stat(TY, 9, NbTR, 4, TYPE_TRIH);
   _stat(SG, 1, NbSG, 3, TYPE_POLYG); _stat(VG, 3, NbVG, 3, TYPE_POLYG);
   _stat(TG, 9, NbTG, 3, TYPE_POLYG);
   _stat(SD, 1, NbSD, 4, TYPE_POLYH); _stat(VD, 3, NbVD, 4, TYPE_POLYH);
@@ -87,11 +90,11 @@ bool PViewDataList::finalize(bool computeMinMax, const std::string &interpolatio
   }
 
   // compute starting element indices
-  int nb[30] = {NbSP, NbVP, NbTP,  NbSL, NbVL, NbTL,  NbST, NbVT, NbTT,
+  int nb[33] = {NbSP, NbVP, NbTP,  NbSL, NbVL, NbTL,  NbST, NbVT, NbTT,
                 NbSQ, NbVQ, NbTQ,  NbSS, NbVS, NbTS,  NbSH, NbVH, NbTH,
-                NbSI, NbVI, NbTI,  NbSY, NbVY, NbTY,  NbSG, NbVG, NbTG,
-                NbSD, NbVD, NbTD};
-  for(int i = 0; i < 30; i++){
+                NbSI, NbVI, NbTI,  NbSY, NbVY, NbTY,  NbSR, NbVR, NbTR,
+                NbSG, NbVG, NbTG,  NbSD, NbVD, NbTD};
+  for(int i = 0; i < 33; i++){
     _index[i] = 0;
     for(int j = 0; j <= i; j++)
       _index[i] += nb[j];
@@ -104,17 +107,17 @@ bool PViewDataList::finalize(bool computeMinMax, const std::string &interpolatio
 
 int PViewDataList::getNumScalars(int step)
 {
-  return NbSP + NbSL + NbST + NbSQ + NbSS + NbSH + NbSI + NbSY + NbSG + NbSD;
+  return NbSP + NbSL + NbST + NbSQ + NbSS + NbSH + NbSI + NbSY + NbSR + NbSG + NbSD;
 }
 
 int PViewDataList::getNumVectors(int step)
 {
-  return NbVP + NbVL + NbVT + NbVQ + NbVS + NbVH + NbVI + NbVY + NbVG + NbVD;
+  return NbVP + NbVL + NbVT + NbVQ + NbVS + NbVH + NbVI + NbVY + NbVR + NbVG + NbVD;
 }
 
 int PViewDataList::getNumTensors(int step)
 {
-  return NbTP + NbTL + NbTT + NbTQ + NbTS + NbTH + NbTI + NbTY + NbTG + NbTD;
+  return NbTP + NbTL + NbTT + NbTQ + NbTS + NbTH + NbTI + NbTY + NbTR + NbTG + NbTD;
 }
 
 int PViewDataList::getNumElements(int step, int ent)
@@ -334,17 +337,22 @@ void PViewDataList::_setLast(int ele)
     else if(ele < _index[22]) _setLast(ele - _index[21], 3, 5, 3, 8, TYPE_PYR, VY, NbVY);
     else _setLast(ele - _index[22], 3, 5, 9, 8, TYPE_PYR, TY, NbTY);
   }
-  else if(ele < _index[26]){ // polygons
-    int nN = polyNumNodes[0][ele - _index[23]];
-    if(ele < _index[24]) _setLast(ele - _index[23], 2, nN, 1, nN, TYPE_POLYG, SG, NbSG);
-    else if(ele < _index[25]) _setLast(ele - _index[24], 2, nN, 3, nN, TYPE_POLYG, VG, NbVG);
-    else _setLast(ele - _index[25], 2, nN, 9, nN, TYPE_POLYG, TG, NbTG);
+  else if(ele < _index[26]){ // trihedra
+    if(ele < _index[24]) _setLast(ele - _index[23], 3, 4, 1, 5, TYPE_TRIH, SR, NbSR);
+    else if(ele < _index[25]) _setLast(ele - _index[24], 3, 4, 3, 5, TYPE_TRIH, VR, NbVR);
+    else _setLast(ele - _index[25], 3, 4, 9, 5, TYPE_TRIH, TR, NbTR);
   }
-  else if(ele < _index[29]){ // polyhedra
-    int nN = polyNumNodes[1][ele - _index[26]];
-    if(ele < _index[27]) _setLast(ele - _index[26], 3, nN, 1, nN*1.5, TYPE_POLYH, SD, NbSD);
-    else if(ele < _index[28]) _setLast(ele - _index[27], 3, nN, 3, nN*1.5, TYPE_POLYH, VD, NbVD);
-    else _setLast(ele - _index[28], 3, nN, 9, nN*1.5, TYPE_POLYH, TD, NbTD);
+  else if(ele < _index[29]){ // polygons
+    int nN = polyNumNodes[0][ele - _index[26]];
+    if(ele < _index[27]) _setLast(ele - _index[26], 2, nN, 1, nN, TYPE_POLYG, SG, NbSG);
+    else if(ele < _index[28]) _setLast(ele - _index[27], 2, nN, 3, nN, TYPE_POLYG, VG, NbVG);
+    else _setLast(ele - _index[28], 2, nN, 9, nN, TYPE_POLYG, TG, NbTG);
+  }
+  else if(ele < _index[32]){ // polyhedra
+    int nN = polyNumNodes[1][ele - _index[29]];
+    if(ele < _index[30]) _setLast(ele - _index[29], 3, nN, 1, nN*1.5, TYPE_POLYH, SD, NbSD);
+    else if(ele < _index[32]) _setLast(ele - _index[30], 3, nN, 3, nN*1.5, TYPE_POLYH, VD, NbVD);
+    else _setLast(ele - _index[31], 3, nN, 9, nN*1.5, TYPE_POLYH, TD, NbTD);
   }
 }
 
@@ -576,12 +584,12 @@ void PViewDataList::smooth()
 
   std::vector<double> *list = 0;
   int *nbe = 0, nbc, nbn;
-  for(int i = 0; i < 24; i++){
+  for(int i = 0; i < 27; i++){
     _getRawData(i, &list, &nbe, &nbc, &nbn);
     if(nbn > 1)
       generateConnectivities(*list, *nbe, NbTimeStep, nbn, nbc, data);
   }
-  for(int i = 0; i < 24; i++){
+  for(int i = 0; i < 27; i++){
     _getRawData(i, &list, &nbe, &nbc, &nbn);
     if(nbn > 1)
       smoothList(*list, *nbe, NbTimeStep, nbn, nbc, data);
@@ -603,6 +611,7 @@ double PViewDataList::getMemoryInMb()
   b += (SH.size() + VH.size() + TH.size()) * sizeof(double);
   b += (SI.size() + VI.size() + TI.size()) * sizeof(double);
   b += (SY.size() + VY.size() + TY.size()) * sizeof(double);
+  b += (SR.size() + VR.size() + TR.size()) * sizeof(double);
   b += (SD.size() + VD.size() + TD.size()) * sizeof(double);
   b += (T2D.size() + T3D.size()) * sizeof(double);
   return b / 1024. / 1024.;
@@ -656,7 +665,8 @@ bool PViewDataList::combineSpace(nameData &nd)
     dVecMerge(l->SI, SI); NbSI += l->NbSI; dVecMerge(l->VI, VI); NbVI += l->NbVI;
     dVecMerge(l->TI, TI); NbTI += l->NbTI; dVecMerge(l->SY, SY); NbSY += l->NbSY;
     dVecMerge(l->VY, VY); NbVY += l->NbVY; dVecMerge(l->TY, TY); NbTY += l->NbTY;
-
+    dVecMerge(l->VR, VR); NbVR += l->NbVR; dVecMerge(l->TR, TR); NbTR += l->NbTR;
+    
     // merge strings
     for(unsigned int i = 0; i < l->T2D.size(); i += 4){
       T2D.push_back(l->T2D[i]);
@@ -725,7 +735,7 @@ bool PViewDataList::combineTime(nameData &nd)
   std::vector<double> *list = 0, *list2 = 0;
 
   // use the first data set as the reference
-  for(int i = 0; i < 24; i++){
+  for(int i = 0; i < 27; i++){
     _getRawData(i, &list, &nbe, &nbc, &nbn);
     data[0]->_getRawData(i, &list2, &nbe2, &nbc2, &nbn2);
     *nbe = *nbe2;
@@ -739,7 +749,7 @@ bool PViewDataList::combineTime(nameData &nd)
         _interpolation[it->first].push_back(new fullMatrix<double>(*it->second[i]));
 
   // merge values for all element types
-  for(int i = 0; i < 24; i++){
+  for(int i = 0; i < 27; i++){
     _getRawData(i, &list, &nbe, &nbc, &nbn);
     for(int j = 0; j < *nbe; j++){
       for(unsigned int k = 0; k < data.size(); k++){
@@ -853,7 +863,7 @@ int PViewDataList::_getRawData(int idxtype, std::vector<double> **l, int **ne,
 {
   int type = 0;
   // No constant nn for polygons!
-  if(idxtype > 23 && idxtype < 30)
+  if(idxtype > 26 && idxtype < 33)
     Msg::Warning("No constant number of nodes for polygons and polyhedra");
   switch(idxtype){
   case 0 : *l = &SP; *ne = &NbSP; *nc = 1; *nn = 1; type = TYPE_PNT; break;
@@ -880,12 +890,15 @@ int PViewDataList::_getRawData(int idxtype, std::vector<double> **l, int **ne,
   case 21: *l = &SY; *ne = &NbSY; *nc = 1; *nn = 5; type = TYPE_PYR; break;
   case 22: *l = &VY; *ne = &NbVY; *nc = 3; *nn = 5; type = TYPE_PYR; break;
   case 23: *l = &TY; *ne = &NbTY; *nc = 9; *nn = 5; type = TYPE_PYR; break;
-  case 24: *l = &SG; *ne = &NbSG; *nc = 1; *nn = 3; type = TYPE_POLYG; break;
-  case 25: *l = &VG; *ne = &NbVG; *nc = 3; *nn = 3; type = TYPE_POLYG; break;
-  case 26: *l = &TG; *ne = &NbTG; *nc = 9; *nn = 3; type = TYPE_POLYG; break;
-  case 27: *l = &SD; *ne = &NbSD; *nc = 1; *nn = 4; type = TYPE_POLYH; break;
-  case 28: *l = &VD; *ne = &NbVD; *nc = 3; *nn = 4; type = TYPE_POLYH; break;
-  case 29: *l = &TD; *ne = &NbTD; *nc = 9; *nn = 4; type = TYPE_POLYH; break;
+  case 24: *l = &SR; *ne = &NbSR; *nc = 1; *nn = 4; type = TYPE_TRIH; break;
+  case 25: *l = &VR; *ne = &NbVR; *nc = 3; *nn = 4; type = TYPE_TRIH; break;
+  case 26: *l = &TR; *ne = &NbTR; *nc = 9; *nn = 4; type = TYPE_TRIH; break;
+  case 27: *l = &SG; *ne = &NbSG; *nc = 1; *nn = 3; type = TYPE_POLYG; break;
+  case 28: *l = &VG; *ne = &NbVG; *nc = 3; *nn = 3; type = TYPE_POLYG; break;
+  case 29: *l = &TG; *ne = &NbTG; *nc = 9; *nn = 3; type = TYPE_POLYG; break;
+  case 30: *l = &SD; *ne = &NbSD; *nc = 1; *nn = 4; type = TYPE_POLYH; break;
+  case 31: *l = &VD; *ne = &NbVD; *nc = 3; *nn = 4; type = TYPE_POLYH; break;
+  case 32: *l = &TD; *ne = &NbTD; *nc = 9; *nn = 4; type = TYPE_POLYH; break;
   default: Msg::Error("Wrong type in PViewDataList"); break;
   }
 
@@ -963,6 +976,11 @@ std::vector<double> *PViewDataList::incrementList(int numComp, int type, int num
     else if(numComp == 3){ NbVY++; return &VY; }
     else if(numComp == 9){ NbTY++; return &TY; }
     break;
+  case TYPE_TRIH:
+    if     (numComp == 1){ NbSR++; return &SR; }
+    else if(numComp == 3){ NbVR++; return &VR; }
+    else if(numComp == 9){ NbTR++; return &TR; }
+    break;
   case TYPE_POLYG:
     polyNumNodes[0].push_back(numNodes);
     nb = (polyAgNumNodes[0].size()) ? polyAgNumNodes[0].back() : 0;
diff --git a/Post/PViewDataList.h b/Post/PViewDataList.h
index 97c1f74881c71d44b0f7615c0d191388331e71ba..59d340f3165f3561e0440a3a0ec371d1db885ee5 100644
--- a/Post/PViewDataList.h
+++ b/Post/PViewDataList.h
@@ -41,6 +41,8 @@ class PViewDataList : public PViewData {
   std::vector<double> SI, VI, TI; // prisms
   int NbSY, NbVY, NbTY;
   std::vector<double> SY, VY, TY; // pyramids
+  int NbSR, NbVR, NbTR;
+  std::vector<double> SR, VR, TR; // trihedra
   int NbSD, NbVD, NbTD;
   std::vector<double> SD, VD, TD; // polyhedra
   int NbT2, NbT3;
@@ -50,7 +52,7 @@ class PViewDataList : public PViewData {
   std::vector<int> polyAgNumNodes[2];
   int polyTotNumNodes[2];
  private:
-  int _index[30];
+  int _index[33];
   int _lastElement, _lastDimension;
   int _lastNumNodes, _lastNumComponents, _lastNumValues, _lastNumEdges, _lastType;
   double *_lastXYZ, *_lastVal;
@@ -90,6 +92,7 @@ class PViewDataList : public PViewData {
   int getNumHexahedra(int step=-1){ return NbSH + NbVH + NbTH; }
   int getNumPrisms(int step=-1){ return NbSI + NbVI + NbTI; }
   int getNumPyramids(int step=-1){ return NbSY + NbVY + NbTY; }
+  int getNumTrihedra(int step=-1){ return NbSR + NbVR + NbTR; }
   int getNumPolyhedra(int step=-1){ return NbSD + NbVD + NbTD; }
   int getNumEntities(int step=-1){ return 1; }
   int getNumElements(int step=-1, int ent=-1);
diff --git a/Post/PViewOptions.cpp b/Post/PViewOptions.cpp
index 6e602a96255e60d778a9ca45eae55a11727d582b..241df2ee59eb26e68948d2038d2aa1a32e27fcba 100644
--- a/Post/PViewOptions.cpp
+++ b/Post/PViewOptions.cpp
@@ -137,6 +137,7 @@ bool PViewOptions::skipElement(int type)
   case TYPE_HEX: return !drawHexahedra;
   case TYPE_PRI: return !drawPrisms;
   case TYPE_PYR: return !drawPyramids;
+  case TYPE_TRIH: return !drawTrihedra;
   case TYPE_POLYH: return false;
   default: return true;
   }
diff --git a/Post/PViewOptions.h b/Post/PViewOptions.h
index a5ebaa5224415e578fc513d914815c5543c1f653..99c8c25aa6612aa78249f365449c977f67956d5e 100644
--- a/Post/PViewOptions.h
+++ b/Post/PViewOptions.h
@@ -82,7 +82,7 @@ class PViewOptions {
   double currentTime;
   int drawStrings;
   int drawPoints, drawLines, drawTriangles, drawQuadrangles, drawPolygons;
-  int drawTetrahedra, drawHexahedra, drawPrisms, drawPyramids, drawPolyhedra;
+  int drawTetrahedra, drawHexahedra, drawPrisms, drawPyramids, drawTrihedra, drawPolyhedra;
   int drawScalars, drawVectors, drawTensors;
   int boundary, pointType, lineType, drawSkinOnly;
   double pointSize, lineWidth;
@@ -103,7 +103,7 @@ class PViewOptions {
   int closed;
   struct{
     unsigned int point, line, triangle, quadrangle;
-    unsigned int tetrahedron, hexahedron, prism, pyramid;
+    unsigned int tetrahedron, hexahedron, prism, pyramid, trihedron;
     unsigned int tangents, normals;
     unsigned int text2d, text3d, axes, background2d;
   } color;
diff --git a/Post/PViewVertexArrays.cpp b/Post/PViewVertexArrays.cpp
index 8f8970075c68e51df25afff66731fe6acae001ff..e3df2253e539908baa88562a7bd4a5061cc3138a 100644
--- a/Post/PViewVertexArrays.cpp
+++ b/Post/PViewVertexArrays.cpp
@@ -857,6 +857,20 @@ static void addScalarPyramid(PView *p, double **xyz,
     addScalarTetrahedron(p, xyz, val, pre, is[i][0], is[i][1], is[i][2], is[i][3]);
 }
 
+static void addOutlineTrihedron(PView *p, double **xyz,
+                              unsigned int color, bool pre)
+{
+  addOutlineQuadrangle(p, xyz, color, pre, 0, 1, 2, 3);
+}
+
+static void addScalarTrihedron(PView *p, double **xyz,
+                                double **val, bool pre, int i0=0,
+                                int i1=1, int i2=2, int i3=3, bool unique=false)
+{
+  addScalarQuadrangle(p, xyz, val, pre, i0, i1, i2, i3, unique);
+}
+
+
 static void addOutlinePolyhedron(PView *p, double **xyz,
                                  unsigned int color, bool pre, int numNodes)
 {
@@ -915,6 +929,7 @@ static void addOutlineElement(PView *p, int type, double **xyz,
   case TYPE_HEX: addOutlineHexahedron(p, xyz, opt->color.hexahedron, pre); break;
   case TYPE_PRI: addOutlinePrism(p, xyz, opt->color.prism, pre); break;
   case TYPE_PYR: addOutlinePyramid(p, xyz, opt->color.pyramid, pre); break;
+  case TYPE_TRIH: addOutlineTrihedron(p, xyz, opt->color.pyramid, pre); break;
   case TYPE_POLYH: addOutlinePolyhedron(p, xyz, opt->color.pyramid, pre, numNodes); break;
   }
 }
@@ -932,6 +947,7 @@ static void addScalarElement(PView *p, int type, double **xyz,
   case TYPE_HEX: addScalarHexahedron(p, xyz, val, pre); break;
   case TYPE_PRI: addScalarPrism(p, xyz, val, pre); break;
   case TYPE_PYR: addScalarPyramid(p, xyz, val, pre); break;
+  case TYPE_TRIH: addScalarTrihedron(p, xyz, val, pre); break;
   case TYPE_POLYH: addScalarPolyhedron(p, xyz, val, pre, numNodes); break;
   }
 }
@@ -1342,6 +1358,7 @@ class initPView {
     int tets = data->getNumTetrahedra(opt->timeStep);
     int prisms = data->getNumPrisms(opt->timeStep);
     int pyrs = data->getNumPyramids(opt->timeStep);
+    int trihs = data->getNumTrihedra(opt->timeStep);
     int hexas = data->getNumHexahedra(opt->timeStep);
     int polyhs = data->getNumPolyhedra(opt->timeStep);
     int heuristic = 0;
@@ -1349,10 +1366,10 @@ class initPView {
       heuristic = (tets + prisms + pyrs + hexas + polyhs) / 10;
     else if(opt->intervalsType == PViewOptions::Continuous)
       heuristic = (tris + 2 * quads + 3 * polygs + 6 * tets +
-                   8 * prisms + 6 * pyrs + 12 * hexas + 10 * polyhs);
+                   8 * prisms + 6 * pyrs + 2 * trihs  + 12 * hexas + 10 * polyhs);
     else if(opt->intervalsType == PViewOptions::Discrete)
       heuristic = (tris + 2 * quads + 3 * polygs + 6 * tets +
-                   8 * prisms + 6 * pyrs + 12 * hexas + 10 * polyhs) * 2;
+                   8 * prisms + 6 * pyrs + 2 * trihs + 12 * hexas + 10 * polyhs) * 2;
     heuristic = _estimateIfClipped(p, heuristic);
     return heuristic + 10000;
   }
diff --git a/benchmarks/3d/Submarine/Submarine.geo b/benchmarks/3d/Submarine/Submarine.geo
index ec10af692e6dd02ccd235a9f077390f73873829d..e592c80a88f7f5ac337495486b786a565c9ffe3c 100644
--- a/benchmarks/3d/Submarine/Submarine.geo
+++ b/benchmarks/3d/Submarine/Submarine.geo
@@ -6,32 +6,37 @@ Mesh.Algorithm3D=9;
 Mesh.Smoothing=0;
 Mesh.Recombine3DAll=1;
 Mesh.SaveParametric = 1;
-MeshAlgorithm Surface {12}  6;
 
+MeshAlgorithm Surface {12}  5; //Not in final mesh
 MeshAlgorithm Surface {10}  5;
 MeshAlgorithm Surface {11}  5; 
 
 //Mesh.Algorithm=5;
 //Mesh.Algorithm3D=5;
 
-Mesh.Optimize = 1;
+Mesh.Optimize = 0;
 
 //Mesh.CharacteristicLengthMax = 25;
 
-Characteristic Length {14, 13, 15, 16, 10, 9, 11, 12, 6, 5, 19, 8, 4, 3, 7, 2, 1} = 100;
-Characteristic Length {21} = 200;
-Characteristic Length {17, 18} = 100;
-Characteristic Length {20} = 200;
+LcMin = 39;
+LcMax = 78;
+//LcMin = 80;
+//LcMax = 160;
+Characteristic Length {14, 13, 15, 16, 10, 9, 11, 12, 6, 5, 19, 8, 4, 3, 7, 2, 1} = LcMax;
+Characteristic Length {21} = LcMin;
+Characteristic Length {17, 18} = LcMin;
+Characteristic Length {20} = LcMax;
 
 Compound Line(34) = {22, 24, 25};
 Compound Surface(13) = {10};
 Compound Surface(14) = {11};
+Compound Surface(15) = {12};
 
 submarineLine[] = {15, 21, 17, 26, 11, 20, 13, 27, 29, 8, 19, 10, 34};
 Physical Line(1) = submarineLine[];
 submarineSurf[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 13};
 Physical Surface(2) = submarineSurf[];
-Physical Surface(3) = {12,14};
+Physical Surface(3) = {14,15};
 Physical Volume(4) = {1};
 Physical Line(5) = {30, 31};
 
diff --git a/benchmarks/3d/Submarine/optimize.py b/benchmarks/3d/Submarine/optimize.py
index 3093216b562489ee8b59da5356090e195673cb01..929a5c10b645a43d44a8f63de296ec22d9a17d1e 100644
--- a/benchmarks/3d/Submarine/optimize.py
+++ b/benchmarks/3d/Submarine/optimize.py
@@ -1,26 +1,38 @@
 from gmshpy import *
 import sys
 import pickle
-
-name = "Submarine2"
+import preprofix
+name = "Submarine"
 
 g = GModel()
 g.load(name + ".geo")
-#g.mesh(3)
-#g.save(name + ".msh")
-g.load(name + ".msh")
+g.mesh(3)
+g.writeMSH(name + ".msh", 3.0)
+#g.load(name + ".msh")
+g.setAllVolumesPositiveTopology()
+
+g.writeMSH(name + "_topology.msh", 3.0)
+preprofix.fixTetras(g)
+preprofix.fixPyramids(g)
+preprofix.fixPrisms(g)
+preprofix.fixHexs(g)
+g.writeMSH(name + "_FIX.msh", 3.0)
 
+#exit()
+#preprofix.fixPyramids(g)
+
+#g.writeMSH(name + "Fix.msh",3.0)
 
 OH = MeshQualOptParameters()
 OH.onlyVisible = False
 OH.dim = 3
-OH.fixBndNodes = True    # Fix boundary nodes or not
-OH.strategy = 1           # 0 = Connected blobs, 1 = Adaptive one-by-one (recommended in 3D)
+OH.fixBndNodes = False    # Fix boundary nodes or not
+OH.strategy = 0           # 0 = Connected blobs, 1 = Adaptive one-by-one (recommended in 3D)
 
 OH.excludeHex = False
 OH.excludePrism = False
-OH.nbLayers = 2          # Nb. of layers around invalid element to construct blob
-OH.distanceFactor = 2    # Distance factor to construct blob
+OH.nbLayers = 3          # Nb. of layers around invalid element to construct blob
+OH.distanceFactor = 3    # Distance factor to construct blob
 OH.maxPatchAdapt = 5       # Number of blob adaption iteration
 OH.maxLayersAdaptFact = 3 # Factor to multiply nb. of layers when adapting
 OH.distanceAdaptFact = 3 # Factor to multiply distance factor when adapting
@@ -36,7 +48,7 @@ OH.nCurses = 1
 OH.logFileName = "log"
 
 OH.maxOptIter = 20             # Nb of optimixation iterations
-OH.maxBarrierUpdates = 30        # Nb. of optimization passes
+OH.maxBarrierUpdates = 15        # Nb. of optimization passes
 
 #print("minTargetIdealJac = %g" % OH.minTargetIdealJac)
 print("minTargetInvCondNum = %g" % OH.minTargetInvCondNum)
@@ -55,6 +67,11 @@ MeshQualityOptimizer(g, OH)
 print("RESULT: minInvCondNum = %g, maxInvCondNum = %g, CPU = %g" %
       (OH.minInvCondNum, OH.maxInvCondNum, OH.CPU))
 
-g.save(name + "_opt.msh")
+g.writeMSH(name + "_opt.msh", 3.0)
+pOpt = meshPartitionOptions()
+pOpt.setNumOfPartitions(5)
+PartitionMesh(g,  pOpt)
+g.writeMSH(name + "_opt_part.msh", 3.0)
+
 #g.writeMSH(name + "_OK.msh",3.0,False,False,False,1.0,0,0)
 
diff --git a/benchmarks/3d/Submarine/preprofix.py b/benchmarks/3d/Submarine/preprofix.py
index 51264e1b7c12a9b598187ae50217a2c3cc48ba90..51207d5ba5c327cad01e48f74ff7479f617bafd8 100644
--- a/benchmarks/3d/Submarine/preprofix.py
+++ b/benchmarks/3d/Submarine/preprofix.py
@@ -1,48 +1,60 @@
 import gmshpy
-import sys
-m = gmshpy.GModel()
-if (len(sys.argv) < 3) :
-    print("please specify input and output files")
-    exit()
-m.load(sys.argv[1])
+import math
 
-# generate a list of boundary elements
-
-def isBoundaryFace(f) :
-    vent = [f.getVertex(i).onWhat() for i in range(f.getNumVertices())]
-    return  all([e.dim() < 3 for e in vent])
 
 def areCoplanarFaces(f0, f1, tol) :
     n0 = f0.normal()
     n1 = f1.normal()
-    return abs(gmshpy.dot(n0, n1)) > tol
+    return (abs(math.acos(gmshpy.dot(n0, n1))*180/math.pi) < tol)
+
+def nbVerticesOnSurface(el) :
+    vent = [el.getVertex(i).onWhat().dim() for i in range(el.getNumVertices())]
+    return  len([e for e in vent if e < 3])
 
-def isBadPyramid(p) :
-    fs = [p.getFace(i) for i in range(p.getNumFaces())]
-    bndfs = [f for f in fs if (isBoundaryFace(f) and f.getNumVertices() == 3)]
+def isBadElement(el) :
+    fs = [el.getFace(i) for i in range(el.getNumFaces())]
     bad = False
-    for i in range(len(bndfs)) :
+    for i in range(len(fs)) :
         for j in range(i):
-            bad |= areCoplanarFaces(bndfs[0], bndfs[1], 0.9)
-    return bad
-
-
-for r in m.bindingsGetRegions() :
-    badp = [p for p in r.pyramids if isBadPyramid(p)]
-    for p in badp :
-        cog = p.barycenter()
-        nv = gmshpy.MVertex(cog.x(), cog.y(), cog.z(), r);
-        nv.thisown = False
-        r.addMeshVertex(nv)
-        for i in range(4) :
-            f = p.getFace(i)
-            vs = [f.getVertex(i) for i in range(3)] + [nv]
-            t = gmshpy.MTetrahedron(*vs)
-            t.thisown = False
+            bad |= areCoplanarFaces(fs[i], fs[j], 10) #10 degrees min angle allowed
+    return bad    
+
+def fixTetras(m):
+    for r in m.bindingsGetRegions() :
+	goodt = [t for t in r.tetrahedra if (not ((nbVerticesOnSurface(t) == 4) and  isBadElement(t)))]
+        oldSize = r.tetrahedra.size()
+        r.tetrahedra.clear()
+        for t in goodt :
             r.addTetrahedron(t)
-        p.setVertex(4, nv)
-
-m.save(sys.argv[2])
+        print("-- Removed %i tetrahedra over %i"%(oldSize-r.tetrahedra.size(), oldSize))
+
+
+def fixPyramids(m):
+    for r in m.bindingsGetRegions() :
+	goodp = [p for p in r.pyramids if (not ((nbVerticesOnSurface(p) == 5) and  isBadElement(p)))]
+        oldSize = r.pyramids.size()
+        r.pyramids.clear()
+        for p in goodp :
+            r.addPyramid(p)
+        print("-- Removed %i pyramids over %i"%(oldSize-r.pyramids.size(), oldSize))
+
+def fixPrisms(m):
+    for r in m.bindingsGetRegions() :
+	goodp = [p for p in r.prisms if (not ((nbVerticesOnSurface(p) == 6) and  isBadElement(p)))]
+        oldSize = r.prisms.size()
+        r.prisms.clear()
+        for p in goodp :
+            r.addPrism(p)
+        print("-- Removed %i prisms over %i"%(oldSize-r.prisms.size(), oldSize))
+
+def fixHexs(m):
+    for r in m.bindingsGetRegions() :
+	goodh = [h for h in r.hexahedra if (not ((nbVerticesOnSurface(h) == 8) and  isBadElement(p)))]
+        oldSize = r.hexahedra.size()
+        r.hexahedra.clear()
+        for h in goodh :
+            r.addHexahedron(h)
+        print("-- Removed %i hexahedra over %i"%(oldSize-r.hexahedra.size(), oldSize))
 
 
 
diff --git a/demos/title.script b/demos/title.script
index 7e49d3964ab60266cefbd6418a10125216a09a2f..c2ae942435f9aef0dcb03fc91d72baf042e6d299 100644
--- a/demos/title.script
+++ b/demos/title.script
@@ -29,7 +29,7 @@ Plugin(Annotate).Run ;
 
 // if we have a mesh, print some statistics
 nbelm = Mesh.NbTriangles + Mesh.NbQuadrangles + Mesh.NbTetrahedra + 
-        Mesh.NbHexahedra + Mesh.NbPrisms + Mesh.NbPyramids;
+        Mesh.NbHexahedra + Mesh.NbPrisms + Mesh.NbPyramids  + Mesh.NbTrihedra;
 nbnod = Mesh.NbNodes;
 If(nbelm && nbnod)
   y += 20;
diff --git a/wrappers/gmshpy/gmshGeo.i b/wrappers/gmshpy/gmshGeo.i
index 500a3d00992bd9408e603f604761f718b7daded7..d195c204bf5a99ef5a1ff26ff2c77405bf632713 100644
--- a/wrappers/gmshpy/gmshGeo.i
+++ b/wrappers/gmshpy/gmshGeo.i
@@ -31,6 +31,7 @@
   #include "MTetrahedron.h"
   #include "MPrism.h"
   #include "MPyramid.h"
+  #include "MTrihedron.h"
   #include "MHexahedron.h"
   #include "MQuadrangle.h"
   #include "MLine.h"
@@ -58,6 +59,9 @@ namespace std {
   %template(MTetrahedronVector) vector< MTetrahedron *,std::allocator< MTetrahedron * > >;
   %template(MQuadrangleVector) vector< MQuadrangle *,std::allocator< MQuadrangle * > >;
   %template(MPyramidVector) vector< MPyramid *,std::allocator< MPyramid * > >;
+  %template(MTrihedronVector) vector< MTrihedron *,std::allocator< MTrihedron * > >;
+  %template(MHexahedronVector) vector< MHexahedron *,std::allocator< MHexahedron * > >;
+  %template(MPrismVector) vector< MPrism *,std::allocator< MPrism * > >;
   %template(GEdgeVectorVector) vector< std::vector< GEdge *,std::allocator< GEdge * > >,std::allocator< std::vector< GEdge *,std::allocator< GEdge * > > > >;
   %template(GFaceVectorVector) vector< std::vector< GFace *,std::allocator< GFace * > >,std::allocator< std::vector< GFace *,std::allocator< GFace * > > > >;
   %template(GFaceList) list<GFace*, std::allocator<GFace*> >;
@@ -125,6 +129,7 @@ namespace std {
 %include "MTetrahedron.h"
 %include "MPrism.h"
 %include "MPyramid.h"
+%include "MTrihedron.h"
 %include "MHexahedron.h"
 %include "MQuadrangle.h"
 %include "MLine.h"
@@ -195,6 +200,11 @@ namespace std {
     $self->xyz2uvw(xyz, &uvw[0]);
     return uvw;
   }
+  std::vector<MVertex*> getVertices() {
+    std::vector <MVertex*> vertices;
+    $self->getVertices(vertices);
+    return vertices;
+  }
 }
 
 %extend GEdge {
diff --git a/wrappers/gmshpy/gmshNumeric.i b/wrappers/gmshpy/gmshNumeric.i
index 3f3df01d41a0d83362a07cd4dba7186973e781eb..e58e8f7795193f8689736a9cf67fbb9278eda1ff 100644
--- a/wrappers/gmshpy/gmshNumeric.i
+++ b/wrappers/gmshpy/gmshNumeric.i
@@ -15,6 +15,7 @@
   #include "fullMatrix.h"
   #include "nodalBasis.h"
   #include "BasisFactory.h"
+  #include "CondNumBasis.h"
   #include "simpleFunction.h"
   #include "simpleFunctionPython.h"
   #include "polynomialBasis.h"
@@ -37,6 +38,7 @@
 %include "polynomialBasis.h"
 %include "pyramidalBasis.h"
 %include "BasisFactory.h"
+%include "CondNumBasis.h"
 %extend nodalBasis {
   fullMatrix<double> F(const fullMatrix<double> &xi) {
     fullMatrix<double> psi;