diff --git a/Geo/CMakeLists.txt b/Geo/CMakeLists.txt
index f69ac0a98c9c01f50022c629d4bbbbb6c30a92fc..087d6bcc53b81051cb02d337bd95189a4bc0f1ef 100644
--- a/Geo/CMakeLists.txt
+++ b/Geo/CMakeLists.txt
@@ -19,16 +19,13 @@ set(SRC
   GModel.cpp
     GModelVertexArrays.cpp
     GModelFactory.cpp
-    GModelIO_Geo.cpp
-    GModelIO_Mesh.cpp
-    GModelIO_MSH.cpp
-    GModelIO_MSH2.cpp
-    GModelIO_OCC.cpp 
-      OCC_Connect.cpp
-    GModelIO_ACIS.cpp
-    GModelIO_Fourier.cpp
-    GModelIO_CGNS.cpp
-    GModelIO_MED.cpp
+    GModelIO_GEO.cpp GModelIO_ACIS.cpp GModelIO_OCC.cpp 
+      GModelIO_Fourier.cpp OCC_Connect.cpp
+    GModelIO_MSH.cpp GModelIO_MSH2.cpp GModelIO_VTK.cpp GModelIO_POS.cpp 
+      GModelIO_CGNS.cpp GModelIO_MED.cpp GModelIO_MESH.cpp GModelIO_STL.cpp 
+      GModelIO_PLY.cpp GModelIO_VRML.cpp GModelIO_UNV.cpp GModelIO_BDF.cpp 
+      GModelIO_IR3.cpp GModelIO_DIFF.cpp GModelIO_GEOM.cpp GModelIO_INP.cpp
+      GModelIO_MAIL.cpp GModelIO_P3D.cpp 
   ExtrudeParams.cpp
   Geo.cpp
   GeoStringInterface.cpp GeoInterpolation.cpp
diff --git a/Geo/GModel.cpp b/Geo/GModel.cpp
index eb287525527793787d4dd3174ef6fcda649be943..e1cf2e06b12e3b85df5f74bbd750da14d1c2f4f1 100644
--- a/Geo/GModel.cpp
+++ b/Geo/GModel.cpp
@@ -1081,7 +1081,8 @@ void GModel::_storeElementsInEntities(std::map< int, std::vector<MElement* > >&
 }
 
 template<class T>
-static void _associateEntityWithElementVertices(GEntity *ge, std::vector<T*> &elements, bool force=false)
+static void _associateEntityWithElementVertices(GEntity *ge, std::vector<T*> &elements,
+                                                bool force=false)
 {
   for(unsigned int i = 0; i < elements.size(); i++){
     for(int j = 0; j < elements[i]->getNumVertices(); j++){
@@ -1183,6 +1184,237 @@ void GModel::_pruneMeshVertexAssociations()
   _storeVerticesInEntities(vertices);
 }
 
+void GModel::_storePhysicalTagsInEntities(int dim,
+                                          std::map<int, std::map<int, std::string> > &map)
+{
+  std::map<int, std::map<int, std::string> >::const_iterator it = map.begin();
+  for(; it != map.end(); ++it){
+    GEntity *ge = 0;
+    switch(dim){
+    case 0: ge = getVertexByTag(it->first); break;
+    case 1: ge = getEdgeByTag(it->first); break;
+    case 2: ge = getFaceByTag(it->first); break;
+    case 3: ge = getRegionByTag(it->first); break;
+    }
+    if(ge){
+      std::map<int, std::string>::const_iterator it2 = it->second.begin();
+      for(; it2 != it->second.end(); ++it2){
+        if(std::find(ge->physicals.begin(), ge->physicals.end(), it2->first) ==
+           ge->physicals.end()){
+          ge->physicals.push_back(it2->first);
+	}
+      }
+    }
+  }
+}
+
+static bool getVertices(int num, int *indices, std::map<int, MVertex*> &map,
+                        std::vector<MVertex*> &vertices)
+{
+  for(int i = 0; i < num; i++){
+    if(!map.count(indices[i])){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(map[indices[i]]);
+  }
+  return true;
+}
+
+static bool getVertices(int num, int *indices, std::vector<MVertex*> &vec,
+                        std::vector<MVertex*> &vertices, int minVertex = 0)
+{
+  for(int i = 0; i < num; i++){
+    if(indices[i] < minVertex || indices[i] > (int)(vec.size() - 1 + minVertex)){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(vec[indices[i]]);
+  }
+  return true;
+}
+
+MElement *GModel::createElementMSH(int num, int typeMSH, int physical,
+                                   int reg, int part, std::vector<MVertex*> &v,
+                                   std::map<int, std::vector<MElement*> > elements[10],
+                                   std::map<int, std::map<int, std::string> > physicals[4])
+{
+  if(CTX::instance()->mesh.switchElementTags) {
+    int tmp = reg;
+    reg = physical;
+    physical = tmp;
+  }
+
+  MElementFactory factory;
+  MElement *e = factory.create(typeMSH, v, num, part);
+  if(!e){
+    Msg::Error("Unknown type of element %d", typeMSH);
+    return 0;
+  }
+
+  switch(e->getType()){
+  case TYPE_PNT : elements[0][reg].push_back(e); break;
+  case TYPE_LIN : elements[1][reg].push_back(e); break;
+  case TYPE_TRI : elements[2][reg].push_back(e); break;
+  case TYPE_QUA : elements[3][reg].push_back(e); break;
+  case TYPE_TET : elements[4][reg].push_back(e); break;
+  case TYPE_HEX : elements[5][reg].push_back(e); break;
+  case TYPE_PRI : elements[6][reg].push_back(e); break;
+  case TYPE_PYR : elements[7][reg].push_back(e); break;
+  default : Msg::Error("Wrong type of element"); return 0;
+  }
+
+  int dim = e->getDim();
+  if(physical && (!physicals[dim].count(reg) || !physicals[dim][reg].count(physical)))
+    physicals[dim][reg][physical] = "unnamed";
+
+  if(part) getMeshPartitions().insert(part);
+  return e;
+}
+
+GModel *GModel::createGModel(std::map<int, MVertex*> &vertexMap,
+                             std::vector<int> &elementNum,
+                             std::vector<std::vector<int> > &vertexIndices,
+                             std::vector<int> &elementType,
+                             std::vector<int> &physical,
+                             std::vector<int> &elementary,
+                             std::vector<int> &partition)
+{
+  GModel *gm = new GModel();
+  std::map<int, std::vector<MElement*> > elements[10];
+  std::map<int, std::map<int, std::string> > physicals[4];
+  std::vector<MVertex*> vertexVector;
+
+  int numVertices = (int)vertexMap.size();
+  int numElement = (int)elementNum.size();
+
+  if(numElement != (int)vertexIndices.size())
+    Msg::Error("Dimension in vertices numbers");
+  if(numElement != (int)elementType.size())
+    Msg::Error("Dimension in elementType numbers");
+  if(numElement != (int)physical.size())
+    Msg::Error("Dimension in physical numbers");
+  if(numElement != (int)elementary.size())
+    Msg::Error("Dimension in elementary numbers");
+  if(numElement != (int)partition.size())
+    Msg::Error("Dimension in partition numbers");
+
+  std::map<int, MVertex*>::const_iterator it = vertexMap.begin();
+  std::map<int, MVertex*>::const_iterator end = vertexMap.end();
+
+  int maxVertex = std::numeric_limits<int>::min();
+  int minVertex = std::numeric_limits<int>::max();
+  int num;
+
+  for(; it != end; ++it){
+    num = it->first;
+    minVertex = std::min(minVertex, num);
+    maxVertex = std::max(maxVertex, num);
+  }
+  if(minVertex == std::numeric_limits<int>::max())
+    Msg::Error("Could not determine the min index of vertices");
+
+  // If the vertex numbering is dense, transfer the map into a
+  // vector to speed up element creation
+  if((minVertex == 1 && maxVertex == numVertices) ||
+     (minVertex == 0 && maxVertex == numVertices - 1)){
+    Msg::Info("Vertex numbering is dense");
+    vertexVector.resize(vertexMap.size() + 1);
+    if(minVertex == 1)
+      vertexVector[0] = 0;
+    else
+      vertexVector[numVertices] = 0;
+    std::map<int, MVertex*>::const_iterator it = vertexMap.begin();
+    for(; it != vertexMap.end(); ++it)
+      vertexVector[it->first] = it->second;
+    vertexMap.clear();
+  }
+
+  int *indices;
+  int nbVertices;
+  for(int i = 0; i < numElement; ++i){
+    num = elementNum[i];
+    std::vector<MVertex*> vertices;
+    nbVertices = (int)vertexIndices[i].size();
+    indices = &vertexIndices[i][0];
+    if(vertexVector.size()){
+      if(!getVertices(nbVertices, indices, vertexVector, vertices)){
+        Msg::Error("Vertex not found aborting");
+        delete gm;
+        return 0;
+      }
+    }
+    else{
+      if(!getVertices(nbVertices, indices, vertexMap, vertices)){
+        Msg::Error("Vertex not found aborting");
+        delete gm;
+        return 0;
+      }
+    }
+
+    gm->createElementMSH(num, elementType[i], physical[i], elementary[i],
+                         partition[i], vertices, elements, physicals);
+  }
+
+  // store the elements in their associated elementary entity. If the
+  // entity does not exist, create a new (discrete) one.
+  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
+    gm->_storeElementsInEntities(elements[i]);
+
+  // associate the correct geometrical entity with each mesh vertex
+  gm->_associateEntityWithMeshVertices();
+
+  // store the vertices in their associated geometrical entity
+  if(vertexVector.size())
+    gm->_storeVerticesInEntities(vertexVector);
+  else
+    gm->_storeVerticesInEntities(vertexMap);
+
+  // store the physical tags
+  for(int i = 0; i < 4; i++)
+    gm->_storePhysicalTagsInEntities(i, physicals[i]);
+
+  return gm;
+}
+
+GModel *GModel::createGModel
+(std::map<int, std::vector<MElement*> > &entityToElementsMap,
+ std::map<int, std::vector<int> > &entityToPhysicalsMap)
+{
+  GModel* gm = new GModel();
+
+  std::map<int, MVertex*> vertexMap;
+  std::map<int, std::map<int, std::string> > physicals[4];
+  for(std::map<int, std::vector<MElement*> >::iterator it =
+        entityToElementsMap.begin(); it != entityToElementsMap.end();
+      it++) {
+    int entity = it->first;
+    for(unsigned int iE = 0; iE < it->second.size(); iE++) {
+      MElement* me = it->second[iE];
+      for(int iV = 0; iV < me->getNumVertices(); iV++) {
+        vertexMap[me->getVertex(iV)->getNum()] = me->getVertex(iV);
+      }
+      if(me->getPartition()) {
+        gm->getMeshPartitions().insert(me->getPartition());
+      }
+      std::vector<int> entityPhysicals = entityToPhysicalsMap[entity];
+      for(unsigned int i = 0; i < entityPhysicals.size(); i++) {
+        physicals[me->getDim()][entity][entityPhysicals[i]] = "unnamed";
+      }
+    }
+  }
+
+  gm->_storeElementsInEntities(entityToElementsMap);
+  gm->_associateEntityWithMeshVertices();
+  gm->_storeVerticesInEntities(vertexMap);
+  for(int i = 0; i < 4; i++)
+    gm->_storePhysicalTagsInEntities(i, physicals[i]);
+
+  return gm;
+}
+
 void GModel::checkMeshCoherence(double tolerance)
 {
   int numEle = getNumMeshElements();
diff --git a/Geo/GModel.h b/Geo/GModel.h
index e6d5fb46e771a54fa73a963daeeaa3ce511c5cd2..aee48a484631483e11c2f7130a35528a9d31b29e 100644
--- a/Geo/GModel.h
+++ b/Geo/GModel.h
@@ -488,6 +488,12 @@ class GModel
   // if cutElem is set to false, split the model without cutting the elements
   GModel *buildCutGModel(gLevelset *ls, bool cutElem=true, bool saveTri=false);
 
+  // utility function to create a mesh element in the I/O routines
+  MElement *createElementMSH(int num, int typeMSH, int physical,
+                             int reg, int part, std::vector<MVertex*> &v,
+                             std::map<int, std::vector<MElement*> > elements[10],
+                             std::map<int, std::map<int, std::string> > physicals[4]);
+
   // create a GModel by importing a mesh (vertexMap has a dim equal to
   // the number of vertices and all the other vectors have a dim equal
   // to the number of elements)
diff --git a/Geo/GModelIO_BDF.cpp b/Geo/GModelIO_BDF.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..23fee9d620a2dca1c81e45a717cf1950ffe535a8
--- /dev/null
+++ b/Geo/GModelIO_BDF.cpp
@@ -0,0 +1,343 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "MPoint.h"
+#include "MLine.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+#include "MTetrahedron.h"
+#include "MHexahedron.h"
+#include "MPrism.h"
+#include "MPyramid.h"
+
+static bool getVertices(int num, int *indices, std::map<int, MVertex*> &map,
+                        std::vector<MVertex*> &vertices)
+{
+  for(int i = 0; i < num; i++){
+    if(!map.count(indices[i])){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(map[indices[i]]);
+  }
+  return true;
+}
+
+static int getFormatBDF(char *buffer, int &keySize)
+{
+  if(buffer[keySize] == '*'){ keySize++; return 2; } // long fields
+  for(unsigned int i = 0; i < strlen(buffer); i++)
+    if(buffer[i] == ',') return 0; // free fields
+  return 1; // small fields;
+}
+
+static double atofBDF(char *str)
+{
+  int len = strlen(str);
+  // classic numbers with E or D exponent notation
+  for(int i = 0; i < len; i++){
+    if(str[i] == 'E' || str[i] == 'e') {
+      return atof(str);
+    }
+    else if(str[i] == 'D' || str[i] == 'd'){
+      str[i] = 'E';
+      return atof(str);
+    }
+  }
+  // special Nastran floating point format (e.g. "-7.-1" instead of
+  // "-7.E-01" or "2.3+2" instead of "2.3E+02")
+  char tmp[32];
+  int j = 0, leading_minus = 1;
+  for(int i = 0; i < len; i++){
+    if(leading_minus && str[i] != ' '  && str[i] != '-') leading_minus = 0;
+    if(!leading_minus && str[i] == '-') tmp[j++] = 'E';
+    if(str[i] == '+') tmp[j++] = 'E';
+    tmp[j++] = str[i];
+  }
+  tmp[j] = '\0';
+  return atof(tmp);
+}
+
+static int readVertexBDF(FILE *fp, char *buffer, int keySize,
+                         int *num, double *x, double *y, double *z)
+{
+  char tmp[5][32];
+  int j = keySize;
+
+  switch(getFormatBDF(buffer, keySize)){
+  case 0: // free field
+    for(int i = 0; i < 5; i++){
+      tmp[i][16] = '\0';
+      strncpy(tmp[i], &buffer[j + 1], 16);
+      for(int k = 0; k < 16; k++){ if(tmp[i][k] == ',') tmp[i][k] = '\0'; }
+      j++;
+      while(j < (int)strlen(buffer) && buffer[j] != ',') j++;
+    }
+    break;
+  case 1: // small field
+    for(int i = 0; i < 5; i++) tmp[i][8] = '\0';
+    strncpy(tmp[0], &buffer[8], 8);
+    strncpy(tmp[2], &buffer[24], 8);
+    strncpy(tmp[3], &buffer[32], 8);
+    strncpy(tmp[4], &buffer[40], 8);
+    break;
+  case 2: // long field
+    for(int i = 0; i < 5; i++) tmp[i][16] = '\0';
+    strncpy(tmp[0], &buffer[8], 16);
+    strncpy(tmp[2], &buffer[40], 16);
+    strncpy(tmp[3], &buffer[56], 16);
+    char buffer2[256];
+    for(unsigned int i = 0; i < sizeof(buffer2); i++) buffer2[i] = '\0';
+    if(!fgets(buffer2, sizeof(buffer2), fp)) return 0;
+    strncpy(tmp[4], &buffer2[8], 16);
+    break;
+  }
+
+  *num = atoi(tmp[0]);
+  *x = atofBDF(tmp[2]);
+  *y = atofBDF(tmp[3]);
+  *z = atofBDF(tmp[4]);
+  return 1;
+}
+
+static bool emptyFieldBDF(char *field, int length)
+{
+  for(int i = 0; i < length; i++)
+    if(field[i] != '\0' && field[i] != ' ' && field[i] != '\n' && field[i] != '\r')
+      return false;
+  return true;
+}
+
+static void readLineBDF(char *buffer, int format, std::vector<char*> &fields)
+{
+  int cmax = (format == 2) ? 16 : 8; // max char per (center) field
+  int nmax = (format == 2) ? 4 : 8; // max num of (center) fields per line
+
+  if(format == 0){ // free fields
+    for(unsigned int i = 0; i < strlen(buffer); i++){
+      if(buffer[i] == ',') fields.push_back(&buffer[i + 1]);
+    }
+  }
+  else{ // small or long fields
+    for(int i = 0; i < nmax + 1; i++){
+      if(!emptyFieldBDF(&buffer[8 + cmax * i], cmax))
+        fields.push_back(&buffer[8 + cmax * i]);
+    }
+  }
+}
+
+static int readElementBDF(FILE *fp, char *buffer, int keySize, int numVertices,
+                          int &num, int &region, std::vector<MVertex*> &vertices,
+                          std::map<int, MVertex*> &vertexMap)
+{
+  char buffer2[256], buffer3[256];
+  std::vector<char*> fields;
+  int format = getFormatBDF(buffer, keySize);
+
+  for(unsigned int i = 0; i < sizeof(buffer2); i++) buffer2[i] = buffer3[i] = '\0';
+
+  readLineBDF(buffer, format, fields);
+
+  if(((int)fields.size() - 2 < abs(numVertices)) ||
+     (numVertices < 0 && (fields.size() == 9))){
+    if(fields.size() == 9) fields.pop_back();
+    if(!fgets(buffer2, sizeof(buffer2), fp)) return 0;
+    readLineBDF(buffer2, format, fields);
+  }
+
+  if(((int)fields.size() - 2 < abs(numVertices)) ||
+     (numVertices < 0 && (fields.size() == 17))){
+    if(fields.size() == 17) fields.pop_back();
+    if(!fgets(buffer3, sizeof(buffer3), fp)) return 0;
+    readLineBDF(buffer3, format, fields);
+  }
+
+  // negative 'numVertices' gives the minimum required number of vertices
+  if((int)fields.size() - 2 < abs(numVertices)){
+    Msg::Error("Wrong number of vertices %d for element", fields.size() - 2);
+    return 0;
+  }
+
+  int n[30], cmax = (format == 2) ? 16 : 8; // max char per (center) field
+  char tmp[32];
+  tmp[cmax] = '\0';
+  strncpy(tmp, fields[0], cmax); num = atoi(tmp);
+  strncpy(tmp, fields[1], cmax); region = atoi(tmp);
+  for(unsigned int i = 2; i < fields.size(); i++){
+    strncpy(tmp, fields[i], cmax); n[i - 2] = atoi(tmp);
+  }
+
+  // ignore the extra fields when we know how many vertices we need
+  int numCheck = (numVertices > 0) ? numVertices : fields.size() - 2;
+  if(!getVertices(numCheck, n, vertexMap, vertices)) return 0;
+  return 1;
+}
+
+int GModel::readBDF(const std::string &name)
+{
+  FILE *fp = fopen(name.c_str(), "r");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  char buffer[256];
+  std::map<int, MVertex*> vertexMap;
+  std::map<int, std::vector<MElement*> > elements[7];
+
+  // nodes can be defined after elements, so parse the file twice
+
+  while(!feof(fp)) {
+    for(unsigned int i = 0; i < sizeof(buffer); i++) buffer[i] = '\0';
+    if(!fgets(buffer, sizeof(buffer), fp)) break;
+    if(buffer[0] != '$'){ // skip comments
+      if(!strncmp(buffer, "GRID", 4)){
+        int num;
+        double x, y, z;
+        if(!readVertexBDF(fp, buffer, 4, &num, &x, &y, &z)) break;
+        vertexMap[num] = new MVertex(x, y, z, 0, num);
+      }
+    }
+  }
+  Msg::Info("%d vertices", vertexMap.size());
+
+  rewind(fp);
+  while(!feof(fp)) {
+    for(unsigned int i = 0; i < sizeof(buffer); i++) buffer[i] = '\0';
+    if(!fgets(buffer, sizeof(buffer), fp)) break;
+    if(buffer[0] != '$'){ // skip comments
+      int num, region;
+      std::vector<MVertex*> vertices;
+      if(!strncmp(buffer, "CBAR", 4)){
+        if(readElementBDF(fp, buffer, 4, 2, num, region, vertices, vertexMap))
+          elements[0][region].push_back(new MLine(vertices, num));
+      }
+      else if(!strncmp(buffer, "CROD", 4)){
+        if(readElementBDF(fp, buffer, 4, 2, num, region, vertices, vertexMap))
+          elements[0][region].push_back(new MLine(vertices, num));
+      }
+      else if(!strncmp(buffer, "CBEAM", 5)){
+        if(readElementBDF(fp, buffer, 5, 2, num, region, vertices, vertexMap))
+          elements[0][region].push_back(new MLine(vertices, num));
+      }
+      else if(!strncmp(buffer, "CTRIA3", 6)){
+        if(readElementBDF(fp, buffer, 6, 3, num, region, vertices, vertexMap))
+          elements[1][region].push_back(new MTriangle(vertices, num));
+      }
+      else if(!strncmp(buffer, "CTRIA6", 6)){
+        if(readElementBDF(fp, buffer, 6, 6, num, region, vertices, vertexMap))
+          elements[1][region].push_back(new MTriangle6(vertices, num));
+      }
+      else if(!strncmp(buffer, "CQUAD4", 6)){
+        if(readElementBDF(fp, buffer, 6, 4, num, region, vertices, vertexMap))
+          elements[2][region].push_back(new MQuadrangle(vertices, num));
+      }
+      else if(!strncmp(buffer, "CQUAD8", 6)){
+        if(readElementBDF(fp, buffer, 6, 8, num, region, vertices, vertexMap))
+          elements[2][region].push_back(new MQuadrangle8(vertices, num));
+      }
+      else if(!strncmp(buffer, "CQUAD", 5)){
+        if(readElementBDF(fp, buffer, 5, -4, num, region, vertices, vertexMap)){
+          if(vertices.size() == 9)
+            elements[2][region].push_back(new MQuadrangle9(vertices, num));
+          else if(vertices.size() == 8)
+            elements[2][region].push_back(new MQuadrangle8(vertices, num));
+          else
+            elements[2][region].push_back(new MQuadrangle(vertices, num));
+        }
+      }
+      else if(!strncmp(buffer, "CTETRA", 6)){
+        if(readElementBDF(fp, buffer, 6, -4, num, region, vertices, vertexMap)){
+          if(vertices.size() == 10)
+            elements[3][region].push_back
+              (new MTetrahedron10(vertices[0], vertices[1], vertices[2], vertices[3],
+                                  vertices[4], vertices[5], vertices[6], vertices[7],
+                                  vertices[9], vertices[8], num));
+          else
+            elements[3][region].push_back(new MTetrahedron(vertices, num));
+        }
+      }
+      else if(!strncmp(buffer, "CHEXA", 5)){
+        if(readElementBDF(fp, buffer, 5, -8, num, region, vertices, vertexMap)){
+          if(vertices.size() == 20)
+            elements[4][region].push_back
+              (new MHexahedron20(vertices[0], vertices[1], vertices[2], vertices[3],
+                                 vertices[4], vertices[5], vertices[6], vertices[7],
+                                 vertices[8], vertices[11], vertices[12], vertices[9],
+                                 vertices[13], vertices[10], vertices[14], vertices[15],
+                                 vertices[16], vertices[19], vertices[17], vertices[18],
+                                 num));
+          else
+            elements[4][region].push_back(new MHexahedron(vertices, num));
+        }
+      }
+      else if(!strncmp(buffer, "CPENTA", 6)){
+        if(readElementBDF(fp, buffer, 6, -6, num, region, vertices, vertexMap)){
+          if(vertices.size() == 15)
+            elements[5][region].push_back
+              (new MPrism15(vertices[0], vertices[1], vertices[2], vertices[3],
+                            vertices[4], vertices[5], vertices[6], vertices[8],
+                            vertices[9], vertices[7], vertices[10], vertices[11],
+                            vertices[12], vertices[14], vertices[13], num));
+          else
+            elements[5][region].push_back(new MPrism(vertices, num));
+        }
+      }
+      else if(!strncmp(buffer, "CPYRAM", 6)){
+        if(readElementBDF(fp, buffer, 6, 5, num, region, vertices, vertexMap))
+          elements[6][region].push_back(new MPyramid(vertices, num));
+      }
+    }
+  }
+
+  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
+    _storeElementsInEntities(elements[i]);
+  _associateEntityWithMeshVertices();
+  _storeVerticesInEntities(vertexMap);
+
+  fclose(fp);
+  return 1;
+}
+
+int GModel::writeBDF(const std::string &name, int format, int elementTagType,
+                     bool saveAll, double scalingFactor)
+{
+  FILE *fp = fopen(name.c_str(), "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  if(noPhysicalGroups()) saveAll = true;
+
+  indexMeshVertices(saveAll);
+
+  fprintf(fp, "$ Created by Gmsh\n");
+
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+
+  // nodes
+  for(unsigned int i = 0; i < entities.size(); i++)
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
+      entities[i]->mesh_vertices[j]->writeBDF(fp, format, scalingFactor);
+
+  // elements
+  for(unsigned int i = 0; i < entities.size(); i++)
+    for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
+      int numPhys = entities[i]->physicals.size();
+      if(saveAll || numPhys)
+        entities[i]->getMeshElement(j)->writeBDF
+          (fp, format, elementTagType, entities[i]->tag(),
+           numPhys ? entities[i]->physicals[0] : 0);
+    }
+
+  fprintf(fp, "ENDDATA\n");
+
+  fclose(fp);
+  return 1;
+}
diff --git a/Geo/GModelIO_CGNS.cpp b/Geo/GModelIO_CGNS.cpp
index b6d9a16900e073c1e5e7292630d6ca81bd073417..93223fa539c2dc224e966a7111a5093078fb7835 100644
--- a/Geo/GModelIO_CGNS.cpp
+++ b/Geo/GModelIO_CGNS.cpp
@@ -121,7 +121,7 @@ class CGNSNameStr
     std::strncpy(name, s.c_str(), 32);
     name[32] = '\0';
   }
-  CGNSNameStr(const int d, const char *const fmt = "%d") 
+  CGNSNameStr(const int d, const char *const fmt = "%d")
   {
     std::sprintf(name, fmt, d);
   }
@@ -225,7 +225,7 @@ int write_CGNS_zones(GModel &model, const int zoneDefinition, const int numZone,
 
 
 static MElement *createElementMSH(GModel *m, int num, int typeMSH, int reg, int part,
-                                     std::vector<MVertex*> &v, std::map<int, std::vector<MElement*> > elements[10])
+                                  std::vector<MVertex*> &v, std::map<int, std::vector<MElement*> > elements[10])
 {
   /*
   if(CTX::instance()->mesh.switchElementTags) {
@@ -279,7 +279,7 @@ static int to1D(int i, int j, int k, int max_i, int max_j, int max_k) {
   return k*max_i*max_j + j*max_i + i;
 }
 
-static int getIndicesQuad(int i1, int i2, int i3, int i4, 
+static int getIndicesQuad(int i1, int i2, int i3, int i4,
 			  int j1, int j2, int j3, int j4,
 			  int k1, int k2, int k3, int k4,
 			  std::vector<int>& ind_i, std::vector<int>& ind_j, std::vector<int>& ind_k, int order, int f) {
@@ -291,7 +291,7 @@ static int getIndicesQuad(int i1, int i2, int i3, int i4,
     {{ 1, 0, 1}, { 1, 0,-1}, {-1, 0,-1}, {-1, 0, 1}},
     {{ 0, 1, 1}, { 0, 1,-1}, { 0,-1,-1}, { 0,-1, 1}},
     {{ 0, 1, 1}, { 0,-1, 1}, { 0,-1,-1}, { 0, 1,-1}}
-  };  
+  };
 
   int added = 0;
 
@@ -304,44 +304,44 @@ static int getIndicesQuad(int i1, int i2, int i3, int i4,
   ind_i.push_back(i1); ind_j.push_back(j1); ind_k.push_back(k1);
   ind_i.push_back(i2); ind_j.push_back(j2); ind_k.push_back(k2);
   ind_i.push_back(i3); ind_j.push_back(j3); ind_k.push_back(k3);
-  ind_i.push_back(i4); ind_j.push_back(j4); ind_k.push_back(k4);  
-  
+  ind_i.push_back(i4); ind_j.push_back(j4); ind_k.push_back(k4);
+
   added += 4;
-  
+
   // edge points
   if (order > 1) {
     // edge 1
     for (int v = 1; v < order; v++) {
-      ind_i.push_back(i1+v*(i2-i1)/order); 
-      ind_j.push_back(j1+v*(j2-j1)/order); 
+      ind_i.push_back(i1+v*(i2-i1)/order);
+      ind_j.push_back(j1+v*(j2-j1)/order);
       ind_k.push_back(k1+v*(k2-k1)/order);
       added++;
     }
 
     // edge 2
     for (int v = 1; v < order; v++) {
-      ind_i.push_back(i2+v*(i3-i2)/order); 
-      ind_j.push_back(j2+v*(j3-j2)/order); 
+      ind_i.push_back(i2+v*(i3-i2)/order);
+      ind_j.push_back(j2+v*(j3-j2)/order);
       ind_k.push_back(k2+v*(k3-k2)/order);
       added++;
     }
 
     // edge 3
     for (int v = 1; v < order; v++) {
-      ind_i.push_back(i3+v*(i4-i3)/order); 
-      ind_j.push_back(j3+v*(j4-j3)/order); 
+      ind_i.push_back(i3+v*(i4-i3)/order);
+      ind_j.push_back(j3+v*(j4-j3)/order);
       ind_k.push_back(k3+v*(k4-k3)/order);
       added++;
     }
 
     // edge 4
     for (int v = 1; v < order; v++) {
-      ind_i.push_back(i4+v*(i1-i4)/order); 
-      ind_j.push_back(j4+v*(j1-j4)/order); 
+      ind_i.push_back(i4+v*(i1-i4)/order);
+      ind_j.push_back(j4+v*(j1-j4)/order);
       ind_k.push_back(k4+v*(k1-k4)/order);
       added++;
     }
-  
+
   /*
   int ioffset = (i3-i1)/abs(i2-i1);
   int joffset = (j3-j1)/abs(j2-j1);
@@ -365,7 +365,7 @@ static int getIndicesFace(int i1, int i2, int i3, int i4, int j1, int j2, int j3
     {{ 0, 1, 1}, { 0,-1, 1}, { 0,-1,-1}, { 0, 1,-1}},
     {{-1, 0, 1}, { 1, 0, 1}, { 1, 0,-1}, {-1, 0,-1}},
     {{ 1, 1 ,0}, {-1, 1, 0}, {-1,-1, 0}, { 1,-1, 0}}
-  };  
+  };
 
   int added = 0;
 
@@ -378,40 +378,40 @@ static int getIndicesFace(int i1, int i2, int i3, int i4, int j1, int j2, int j3
   ind_i.push_back(i1); ind_j.push_back(j1); ind_k.push_back(k1);
   ind_i.push_back(i2); ind_j.push_back(j2); ind_k.push_back(k2);
   ind_i.push_back(i3); ind_j.push_back(j3); ind_k.push_back(k3);
-  ind_i.push_back(i4); ind_j.push_back(j4); ind_k.push_back(k4);  
-  
+  ind_i.push_back(i4); ind_j.push_back(j4); ind_k.push_back(k4);
+
   added += 4;
-  
+
   // edge points
   if (order > 1) {
     // edge 1
     for (int v = 1; v < order; v++) {
-      ind_i.push_back(i1+(i2-i1)/order); 
-      ind_j.push_back(j1+(j2-j1)/order); 
+      ind_i.push_back(i1+(i2-i1)/order);
+      ind_j.push_back(j1+(j2-j1)/order);
       ind_k.push_back(k1+(k2-k1)/order);
       added++;
     }
 
     // edge 2
     for (int v = 1; v < order; v++) {
-      ind_i.push_back(i2+(i3-i2)/order); 
-      ind_j.push_back(j2+(j3-j2)/order); 
+      ind_i.push_back(i2+(i3-i2)/order);
+      ind_j.push_back(j2+(j3-j2)/order);
       ind_k.push_back(k2+(k3-k2)/order);
       added++;
     }
 
     // edge 3
     for (int v = 1; v < order; v++) {
-      ind_i.push_back(i3+(i4-i3)/order); 
-      ind_j.push_back(j3+(j4-j3)/order); 
+      ind_i.push_back(i3+(i4-i3)/order);
+      ind_j.push_back(j3+(j4-j3)/order);
       ind_k.push_back(k3+(k4-k3)/order);
       added++;
     }
 
     // edge 4
     for (int v = 1; v < order; v++) {
-      ind_i.push_back(i4+(i1-i4)/order); 
-      ind_j.push_back(j4+(j1-j4)/order); 
+      ind_i.push_back(i4+(i1-i4)/order);
+      ind_j.push_back(j4+(j1-j4)/order);
       ind_k.push_back(k4+(k1-k4)/order);
       added++;
     }
@@ -461,7 +461,7 @@ static void getIndices(int i, int j, int k, std::vector<int>& ind_i, std::vector
     {{ 0, 1, 1}, { 0,-1, 1}, { 0,-1,-1}, { 0, 1,-1}},
     {{-1, 0, 1}, { 1, 0, 1}, { 1, 0,-1}, {-1, 0,-1}},
     {{ 1, 1 ,0}, {-1, 1, 0}, {-1,-1, 0}, { 1,-1, 0}}
-  };  
+  };
 
 
   if (order == 0) {
@@ -491,7 +491,7 @@ static void getIndices(int i, int j, int k, std::vector<int>& ind_i, std::vector
       int p1_i = ind_i[startpoint+edges[e][1]];
       int p1_j = ind_j[startpoint+edges[e][1]];
       int p1_k = ind_k[startpoint+edges[e][1]];
-      
+
       for (int v = 1; v < order; v++) {
 	ind_i.push_back(p0_i + v*((p1_i-p0_i)/order));
 	ind_j.push_back(p0_j + v*((p1_j-p0_j)/order));
@@ -506,12 +506,12 @@ static void getIndices(int i, int j, int k, std::vector<int>& ind_i, std::vector
       int i2 = ind_i[startpoint+faces[f][1]] + offset[f][1][0];
       int i3 = ind_i[startpoint+faces[f][2]] + offset[f][2][0];
       int i4 = ind_i[startpoint+faces[f][3]] + offset[f][3][0];
-      
+
       int j1 = ind_j[startpoint+faces[f][0]] + offset[f][0][1];
       int j2 = ind_j[startpoint+faces[f][1]] + offset[f][1][1];
       int j3 = ind_j[startpoint+faces[f][2]] + offset[f][2][1];
       int j4 = ind_j[startpoint+faces[f][3]] + offset[f][3][1];
-      
+
       int k1 = ind_k[startpoint+faces[f][0]] + offset[f][0][2];
       int k2 = ind_k[startpoint+faces[f][1]] + offset[f][1][2];
       int k3 = ind_k[startpoint+faces[f][2]] + offset[f][2][2];
@@ -522,10 +522,10 @@ static void getIndices(int i, int j, int k, std::vector<int>& ind_i, std::vector
 				     ind_i, ind_j, ind_k,
 				     order-2, f);
     }
-    
+
     // interior
     getIndices(i+1,j+1,k+1, ind_i, ind_j, ind_k, order-2, initial_point);
-  
+
   }
 }
 
@@ -536,7 +536,7 @@ static void getIndices(int i, int j, int k, std::vector<int>& ind_i, std::vector
  * Purpose
  * =======
  *
- *   
+ *
  *
  * I/O
  * ===
@@ -548,7 +548,7 @@ static void getIndices(int i, int j, int k, std::vector<int>& ind_i, std::vector
 int GModel::readCGNS(const std::string &name)
 {
 
-  cgsize_t isize[9];   
+  cgsize_t isize[9];
   char basename[33],zonename[33];
   std::map<int, std::vector<MElement*> > elements[10];
 
@@ -618,7 +618,7 @@ int GModel::readCGNS(const std::string &name)
 	kelem = kelem / 2.0;
     }
     max_order = min(order, max_order);
-    
+
   }
 
   Msg::Debug("Maximum import order : %i", max_order);
@@ -650,7 +650,7 @@ int GModel::readCGNS(const std::string &name)
       Msg::Debug("Unstructured zone detected, skipping.");
       continue;
     }
-    
+
     cgsize_t irmin[3];
     cgsize_t irmax[3];
     cgsize_t zoneSizes[9];
@@ -667,7 +667,7 @@ int GModel::readCGNS(const std::string &name)
     double ielem = irmax[0] - 1;
     double jelem = irmax[1] - 1;
     double kelem = irmax[2] - 1;
-    
+
     int nnodesZone;
     int nelements;
     int nBoundaryVertices;
@@ -679,19 +679,19 @@ int GModel::readCGNS(const std::string &name)
     // Reading the coordinates
     int nCoords;
     cg_ncoords(index_file, index_base, index_zone, &nCoords);
-    
+
     DataType_t dataType;
     char coordName[35];
     void* coord;
     double nodes[nnodesZone][nCoords];
-    
+
     for ( int iCoord = 0; iCoord < nCoords; iCoord++ ) {
       if ( cg_coord_info(index_file, index_base, index_zone, iCoord+1, &dataType, coordName) ) {
 	Msg::Error("Could not read coordinate %i.", iCoord+1);
 	cg_close (index_file);
 	return 0;
       }
-      
+
       Msg::Debug("Reading coordinate %i : %s.", iCoord+1, coordName);
 
       switch(dataType) {
@@ -773,7 +773,7 @@ int GModel::readCGNS(const std::string &name)
 	}
       }
     }
-    
+
     // Create surface mesh
     std::map<int, std::vector<int*> > forbidden;
 
@@ -791,7 +791,7 @@ int GModel::readCGNS(const std::string &name)
       int transform[3];
       cg_1to1_read(index_file, index_base, index_zone,index_section,
 		   ConnectionName, DonorName, range, donor_range, transform);
-      
+
 
       // Checking on which face it is
       int face = 0;
@@ -809,7 +809,7 @@ int GModel::readCGNS(const std::string &name)
         if (range[2] == 1)
 	  face = 0;
 	else
-	  face = 1;	
+	  face = 1;
       }
 
       printf("Face %i\n", face);
@@ -825,8 +825,8 @@ int GModel::readCGNS(const std::string &name)
 	continue;
       }
 
-      
-      for (int r = 0; r < 6; r++) {	
+
+      for (int r = 0; r < 6; r++) {
 	range_int[r] = (int)range[r];
 	printf("%i ", range_int[r]);
       }
@@ -835,7 +835,7 @@ int GModel::readCGNS(const std::string &name)
       printf("\npong\n");
     }
 
-    
+
 
 
     for(int face = 0; face < 6; face++) {
@@ -894,7 +894,7 @@ int GModel::readCGNS(const std::string &name)
 	move[2][0] = 0; move[2][1] = 0;     move[2][2] = kgrow;     move[2][3] = kgrow;
 	break;
       }
-      
+
       GRegion* gr = getRegionByTag(elementary_region);
       elementary_face++;
       num = 1;
@@ -902,7 +902,7 @@ int GModel::readCGNS(const std::string &name)
 	for (int j = jmin; j < jmax;  j += order) {
 	  //printf("Creating surface element\n");
 	  for (int k = kmin; k < kmax; k += order) {
-	    
+
 	    bool ok = true;
 	    for (int ff=0; ff < forbidden[face].size(); ff++) {
 	      int* lim = forbidden[face][ff];
@@ -921,7 +921,7 @@ int GModel::readCGNS(const std::string &name)
 
 	    std::vector<MVertex*> vertices;
 	    std::vector<int> ind_i, ind_j, ind_k;
-	    
+
 	    getIndicesQuad(i+move[0][0],i+move[0][1], i+move[0][2], i+move[0][3],
 			   j+move[1][0],j+move[1][1], j+move[1][2], j+move[1][3],
 			   k+move[2][0],k+move[2][1], k+move[2][2], k+move[2][3],
@@ -933,7 +933,7 @@ int GModel::readCGNS(const std::string &name)
 	    MElement* e = createElementMSH(this, num, type_quad, elementary_face,
 					   partition, vertices, elements);
 	    num_elements++;
-	    num++;	    
+	    num++;
 	  }
 	}
       }
@@ -1148,8 +1148,8 @@ int GModel::writeCGNS(const std::string &name, int zoneDefinition,
 
   // write the base node
   int cgIndexBase=0;
-  if(cg_base_write(cgIndexFile, options.baseName.c_str(), meshDim, meshDim, 
-                   &cgIndexBase)) 
+  if(cg_base_write(cgIndexFile, options.baseName.c_str(), meshDim, meshDim,
+                   &cgIndexBase))
     return cgnsErr();
 
   // write information about who generated the mesh
@@ -1160,8 +1160,8 @@ int GModel::writeCGNS(const std::string &name, int zoneDefinition,
   case 2:
     MZone<2>::preInit();
     MZoneBoundary<2>::preInit();
-    write_CGNS_zones<2>(*this, zoneDefinition, numZone, options, 
-                        scalingFactor, vectorDim, groups[face], 
+    write_CGNS_zones<2>(*this, zoneDefinition, numZone, options,
+                        scalingFactor, vectorDim, groups[face],
                         cgIndexFile, cgIndexBase);
     MZone<2>::postDestroy();
     MZoneBoundary<2>::postDestroy();
@@ -1169,8 +1169,8 @@ int GModel::writeCGNS(const std::string &name, int zoneDefinition,
   case 3:
     MZone<3>::preInit();
     MZoneBoundary<3>::preInit();
-    write_CGNS_zones<3>(*this, zoneDefinition, numZone, options, 
-                        scalingFactor, vectorDim, groups[region], 
+    write_CGNS_zones<3>(*this, zoneDefinition, numZone, options,
+                        scalingFactor, vectorDim, groups[region],
                         cgIndexFile, cgIndexBase);
     MZone<3>::postDestroy();
     MZoneBoundary<3>::postDestroy();
@@ -1200,7 +1200,7 @@ int GModel::writeCGNS(const std::string &name, int zoneDefinition,
  *   zoneElemConn       - (I) connectivity for the zone using gmsh node ordering
  *                            (see MElement.h)
  *                        (O) connectivity using CGNS node ordering
- *   
+ *
  ******************************************************************************/
 
 void translateElementMSH2CGNS(ElementConnectivity *const zoneElemConn)
@@ -1311,7 +1311,7 @@ void translateElementMSH2CGNS(ElementConnectivity *const zoneElemConn)
  ******************************************************************************/
 
 void expand_name(std::string &s, const int index, const char *const name)
-{  
+{
   std::string::size_type r1 = s.find('&');
   // Look for and replace "&-&" sub-strings
   while(r1 != std::string::npos) {
@@ -1457,7 +1457,7 @@ int get_zone_definition(GModel &model, const int zoneDefinition,
  * I/O
  * ===
  *
- *   model              - (I) 
+ *   model              - (I)
  *   zoneDefinition     - (I) how to define a zone
  *   options            - (I) CGNS specific options
  *   scalingFactor
@@ -1476,7 +1476,7 @@ int get_zone_definition(GModel &model, const int zoneDefinition,
 //--A task for a thread
 
 template <unsigned DIM>
-struct ZoneTask 
+struct ZoneTask
 {
   MZone<DIM> zone;
   CGNSNameStr zoneName;
@@ -1488,7 +1488,7 @@ struct ZoneTask
                                         //     processed
   int indexInOwner;
   ZoneTask() : status(0), indexInOwner(0) { }
-  void change_status(const int _status) 
+  void change_status(const int _status)
   {
 #pragma omp atomic
     status = _status;
@@ -1497,7 +1497,7 @@ struct ZoneTask
 
 //--Information about a zone
 
-struct ZoneInfo 
+struct ZoneInfo
 {
   CGNSNameStr name;
   int cgIndex;
@@ -1589,7 +1589,7 @@ int write_CGNS_zones(GModel &model, const int zoneDefinition, const int numZone,
           // Manually maintain the size of the 'zoneInfo vector'.  'push_back'
           // is not used because the elements are in order of 'zoneIndex'
           if(writeTask->zoneIndex >= zoneInfo.size()) {
-            zoneInfo.resize(std::max(2*static_cast<int>(zoneInfo.size()), 
+            zoneInfo.resize(std::max(2*static_cast<int>(zoneInfo.size()),
                             writeTask->zoneIndex));
           }
           zoneInfo[writeTask->zoneIndex].name = writeTask->zoneName;
@@ -1725,7 +1725,7 @@ int write_CGNS_zones(GModel &model, const int zoneDefinition, const int numZone,
           // Write the connectivity for each zone pair
           const ZoneConnMap::const_iterator gCEnd = zoneConnMap.end();
           for(ZoneConnMap::const_iterator gCIt = zoneConnMap.begin();
-              gCIt != gCEnd; ++gCIt) 
+              gCIt != gCEnd; ++gCIt)
           {
             const int nVert = gCIt->second.vertexPairVec.size();
             iBuffer1.resize(nVert);
@@ -1788,7 +1788,7 @@ int write_CGNS_zones(GModel &model, const int zoneDefinition, const int numZone,
           if(get_zone_definition
              (model, zoneDefinition, numZone, options, DIM, group,
               globalZoneIndex, globalPhysicalIt, zoneTask.zoneIndex, partition,
-              physicalItBegin, physicalItEnd, zoneTask.zoneName)) 
+              physicalItBegin, physicalItEnd, zoneTask.zoneName))
           {
             omp_set_lock(&threadWLock);
             --threadsWorking;
@@ -1798,7 +1798,7 @@ int write_CGNS_zones(GModel &model, const int zoneDefinition, const int numZone,
             zoneTask.zone.clear();
             // Loop through all physical in the zone definition
             for(PhysGroupMap::const_iterator physicalIt = physicalItBegin;
-                physicalIt != physicalItEnd; ++physicalIt) 
+                physicalIt != physicalItEnd; ++physicalIt)
             {
               // Add elements from all entities in the physical with defined
               // partition number
@@ -1830,15 +1830,15 @@ int write_CGNS_zones(GModel &model, const int zoneDefinition, const int numZone,
  * Write the remaining unconnected vertices as boundary conditions
  *--------------------------------------------------------------------*/
 
-      if(options.writeBC) 
+      if(options.writeBC)
       {
         ZoneBoVec zoneBoVec;            // from 'MZoneBoundary.h'
         if(mZoneBoundary.exteriorBoundaryVertices
-           (options.normalSource, zoneBoVec) == 0) 
+           (options.normalSource, zoneBoVec) == 0)
         {
           // Sort by zone index and then entity index
           const int numBoVert = zoneBoVec.size();
-          if (numBoVert>=1) 
+          if (numBoVert>=1)
           {
             Msg::Info("writing BoVerts...");
 
@@ -1858,7 +1858,7 @@ int write_CGNS_zones(GModel &model, const int zoneDefinition, const int numZone,
               const int iVertStart = iVert;
               while(iVert != numBoVert &&
                     zoneBoVec[iZBV[iVert]].zoneIndex == zoneIndex &&
-                    zoneBoVec[iZBV[iVert]].bcPatchIndex == patchIndex) 
+                    zoneBoVec[iZBV[iVert]].bcPatchIndex == patchIndex)
               {
                 VertexBoundary &vertBo = zoneBoVec[iZBV[iVert]];
                 dBuffer.push_back(vertBo.normal[0]);
@@ -1898,7 +1898,7 @@ int write_CGNS_zones(GModel &model, const int zoneDefinition, const int numZone,
         }
       }
 
-      // NBN: MZoneBoundary defines no destructor, so force 
+      // NBN: MZoneBoundary defines no destructor, so force
       // the deallocation of the new pointers with clear()
       mZoneBoundary.clear();
       Msg::Info("Leaving master thread");  // DBG
diff --git a/Geo/GModelIO_DIFF.cpp b/Geo/GModelIO_DIFF.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..58cee819d805bdef88c6cea7bc561931232d1ea5
--- /dev/null
+++ b/Geo/GModelIO_DIFF.cpp
@@ -0,0 +1,462 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "MElement.h"
+
+static bool getVertices(int num, int *indices, std::map<int, MVertex*> &map,
+                        std::vector<MVertex*> &vertices)
+{
+  for(int i = 0; i < num; i++){
+    if(!map.count(indices[i])){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(map[indices[i]]);
+  }
+  return true;
+}
+
+static bool getVertices(int num, int *indices, std::vector<MVertex*> &vec,
+                        std::vector<MVertex*> &vertices, int minVertex = 0)
+{
+  for(int i = 0; i < num; i++){
+    if(indices[i] < minVertex || indices[i] > (int)(vec.size() - 1 + minVertex)){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(vec[indices[i]]);
+  }
+  return true;
+}
+
+int GModel::readDIFF(const std::string &name)
+{
+  FILE *fp = fopen(name.c_str(), "r");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  char str[256] = "XXX";
+  std::map<int, std::vector<MElement*> > elements[10];
+  std::map<int, std::map<int, std::string> > physicals[4];
+  std::map<int, MVertex*> vertexMap;
+  std::vector<MVertex*> vertexVector;
+
+  {
+    while(strstr(str, "Number of space dim. =") == NULL){
+      if(!fgets(str, sizeof(str), fp) || feof(fp))
+        break;
+    }
+
+    int dim;
+    if(sscanf(str, "%*s %*s %*s %*s %*s %d", &dim) != 1) return 0;
+    Msg::Info("dimension %d", dim);
+
+    int numElements;
+    if(!fgets(str, sizeof(str), fp) || feof(fp)) return 0;
+    while(strstr(str, "Number of elements   =") == NULL){
+      if(!fgets(str, sizeof(str), fp) || feof(fp))
+        break;
+    }
+    if(sscanf(str, "%*s %*s %*s %*s %d", &numElements) != 1) return 0;
+    Msg::Info("%d elements", numElements);
+
+    int numVertices;
+    if(!fgets(str, sizeof(str), fp) || feof(fp)) return 0;
+    while(strstr(str, "Number of nodes      =") == NULL){
+      if(!fgets(str, sizeof(str), fp) || feof(fp))
+        break;
+    }
+    if(sscanf(str, "%*s %*s %*s %*s %d", &numVertices) != 1) return 0;
+    Msg::Info("%d vertices", numVertices);
+
+    int numVerticesPerElement;
+    if(!fgets(str, sizeof(str), fp)||feof(fp)) return 0;
+    while(strstr(str, "Max number of nodes in an element:")==NULL){
+      if(!fgets(str, sizeof(str), fp) || feof(fp))
+        break;
+    }
+    if(sscanf(str, "%*s %*s %*s %*s %*s %*s %*s %d", &numVerticesPerElement) != 1)
+      return 0;
+    Msg::Info("numVerticesPerElement %d", numVerticesPerElement);
+
+    bool several_subdomains;
+    if(!fgets(str, sizeof(str), fp) || feof(fp)) return 0;
+    if(!strncmp(&str[2], "Only one material", 17) ||
+       !strncmp(&str[2], "Only one subdomain", 18)){
+      if(!strncmp(&str[37], "dpTRUE", 6) || !strncmp(&str[37], "true", 4) ||
+         !strncmp(&str[36], "dpTRUE", 6) || !strncmp(&str[36], "true", 4)){
+        several_subdomains = false;
+      }
+      else{
+        several_subdomains = true;
+      }
+      Msg::Info("several_subdomains %x %s", several_subdomains, str);
+    }
+
+    int nbi;
+    std::vector<int> bi;
+    if(!fgets(str, sizeof(str), fp) || feof(fp)) return 0;
+    while(strstr(str, "Boundary indicators:") == NULL &&
+          strstr(str, "boundary indicators:") == NULL){
+      if(!fgets(str, sizeof(str), fp) || feof(fp))
+        break;
+    }
+    if(sscanf(str, "%d %*s %*s", &nbi) != 1) return 0;
+    Msg::Info("nbi %d", nbi);
+    if(nbi != 0)
+      bi.resize(nbi);
+    std::string format_read_bi = "%*d %*s %*s";
+    for(int i = 0; i < nbi; i++){
+      if(format_read_bi[format_read_bi.size()-1] == 'd') {
+        format_read_bi[format_read_bi.size()-1] = '*';
+        format_read_bi += "d %d";
+      }
+      else
+        format_read_bi += " %d";
+      if(sscanf(str, format_read_bi.c_str(), &bi[i]) != 1) return 0;
+      Msg::Info("bi[%d]=%d", i, bi[i]);
+    }
+
+    while(str[0] != '#'){
+      if(!fgets(str, sizeof(str), fp) || feof(fp))
+        break;
+    }
+    vertexVector.clear();
+    vertexMap.clear();
+    int minVertex = numVertices + 1, maxVertex = -1;
+    int num = 0;
+    std::vector<std::vector<int> > elementary(numVertices);
+
+    Msg::ResetProgressMeter();
+    for(int i = 0; i < numVertices; i++){
+      if(!fgets(str, sizeof(str), fp)) return 0;
+      double xyz[3];
+      int tmp;
+      if(sscanf(str, "%d ( %lf , %lf , %lf ) [%d]", &num,
+                &xyz[0], &xyz[1], &xyz[2], &tmp) != 5) return 0;
+      elementary[i].resize(tmp + 1);
+      elementary[i][0] = tmp;
+      minVertex = std::min(minVertex, num);
+      maxVertex = std::max(maxVertex, num);
+      if(vertexMap.count(num))
+        Msg::Warning("Skipping duplicate vertex %d", num);
+      else
+        vertexMap[num] = new MVertex(xyz[0], xyz[1], xyz[2], 0, num);
+      if(numVertices > 100000)
+        Msg::ProgressMeter(i + 1, numVertices, true, "Reading nodes");
+      // If the vertex numbering is dense, tranfer the map into a
+      // vector to speed up element creation
+      if((int)vertexMap.size() == numVertices &&
+         ((minVertex == 1 && maxVertex == numVertices) ||
+          (minVertex == 0 && maxVertex == numVertices - 1))){
+        Msg::Info("Vertex numbering is dense");
+        vertexVector.resize(vertexMap.size() + 1);
+        if(minVertex == 1)
+          vertexVector[0] = 0;
+        else
+          vertexVector[numVertices] = 0;
+        std::map<int, MVertex*>::const_iterator it = vertexMap.begin();
+        for(; it != vertexMap.end(); ++it)
+          vertexVector[it->first] = it->second;
+        vertexMap.clear();
+      }
+      Msg::Info("%d ( %lf , %lf , %lf ) [%d]",i, xyz[0], xyz[1], xyz[2],
+                elementary[i][0]);
+      std::string format_read_bi = "%*d ( %*lf , %*lf , %*lf ) [%*d]";
+      for(int j = 0; j < elementary[i][0]; j++){
+        if(format_read_bi[format_read_bi.size() - 1] == 'd') {
+          format_read_bi[format_read_bi.size() - 1] = '*';
+          format_read_bi += "d %d";
+        }
+        else
+          format_read_bi += " %d";
+        if(sscanf(str, format_read_bi.c_str(), &(elementary[i][j + 1])) != 1)
+          return 0;
+        Msg::Info("elementary[%d][%d]=%d", i + 1, j + 1, elementary[i][j + 1]);
+      }
+    }
+    while(str[0] != '#'){
+      if(!fgets(str, sizeof(str), fp) || feof(fp))
+        break;
+    }
+    std::vector<int> material(numElements);
+    std::vector<std::vector<int> > ElementsNodes(numElements);
+    for(int i = 0; i < numElements; i++){
+      ElementsNodes[i].resize(numVerticesPerElement);
+    }
+    char eleTypec[20]="";
+    std::string eleType;
+    Msg::ResetProgressMeter();
+    std::vector<int> mapping;
+    for(int i = 1; i <= numElements; i++){
+      if(!fgets(str, sizeof(str), fp)) return 0;
+      int num = 0, type, physical = 0, partition = 0;
+      int indices[60];
+      if(sscanf(str, "%*d %s %d", eleTypec, &material[i-1]) != 2) return 0;
+      eleType = std::string(eleTypec);
+      int k2; // local number for the element
+      int NoVertices; // number of vertices per element
+      if(eleType == "ElmT3n2D"){
+        NoVertices = 3;
+        static int map[3] = {0, 1, 2}; // identical to gmsh
+        mapping=std::vector<int>(map, map + sizeof(map) / sizeof(int));
+        type = MSH_TRI_3;
+      }
+      else if(eleType == "ElmT6n2D"){
+        NoVertices = 6;
+        static int map[6] = {0, 1, 2, 3, 4, 5}; // identical to gmsh
+        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
+        type = MSH_TRI_6;
+      }
+      else if(eleType == "ElmB4n2D"){
+        NoVertices = 4;
+        static int map[4] = {0, 1, 3, 2}; // local numbering
+        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
+        type = MSH_QUA_4;
+      }
+      else if(eleType == "ElmB8n2D"){
+        NoVertices = 8;
+        static int map[8] = {0, 1, 3, 2, 4, 6, 7, 5}; // local numbering
+        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
+        type = MSH_QUA_8;
+      }
+      else if(eleType == "ElmB9n2D"){
+        NoVertices = 9;
+        static int map[9] = {0, 4, 1, 7, 8, 5, 3, 6, 2}; // local numbering
+        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
+        type = MSH_QUA_9;
+      }
+      else if(eleType == "ElmT4n3D"){
+        NoVertices = 4;
+        static int map[4] = {0, 1, 2, 3}; // identical to gmsh
+        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
+        type = MSH_TET_4;
+      }
+      else if(eleType == "ElmT10n3D"){
+        NoVertices = 10;
+        static int map[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // local numbering
+        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
+        type = MSH_TET_10;
+      }
+      else if(eleType == "ElmB8n3D"){
+        NoVertices = 8;
+        static int map[8] = {4, 5, 0, 1, 7, 6, 3, 2};
+        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
+        type = MSH_HEX_8;
+      }
+      else if(eleType == "ElmB20n3D"){
+        NoVertices = 20;
+        static int map[20] = {4, 5, 0, 1, 7, 6, 3, 2, 16, 8, 19, 13, 15, 12,
+                              14, 17, 18, 9, 11};
+        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
+        type = MSH_HEX_20;
+      }
+      else if(eleType == "ElmB27n3D"){
+        NoVertices = 27;
+        static int map[27] = {4, 16, 5, 10, 21, 12, 0, 8, 1, 17, 25, 18, 22,
+                              26, 23, 9, 20, 11, 7, 19, 6, 15, 24, 14, 3, 13, 2};
+        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
+        type = MSH_HEX_27;
+      }
+      else{
+        return 0;
+      }
+      std::string format_read_vertices = "%*d %*s %*d";
+      for(int k = 0; k < NoVertices; k++){
+        if(format_read_vertices[format_read_vertices.size()-2] != '*') {
+          format_read_vertices[format_read_vertices.size()-1] = '*';
+          format_read_vertices += "d %d";
+        }
+        else
+          format_read_vertices += " %d";
+        k2 = mapping[k];
+        if(sscanf(str, format_read_vertices.c_str(), &ElementsNodes[i-1][k2]) != 1)
+          return 0;
+      }
+      mapping.clear();
+      for(int j = 0; j < NoVertices; j++)
+        indices[j] = ElementsNodes[i - 1][j];
+      std::vector<MVertex*> vertices;
+      if(vertexVector.size()){
+        if(!getVertices(numVerticesPerElement, indices, vertexVector, vertices))
+          return 0;
+      }
+      else{
+        if(!getVertices(numVerticesPerElement, indices, vertexMap, vertices))
+          return 0;
+      }
+      createElementMSH(num, type, physical, elementary[i-1][1], partition,
+                       vertices, elements, physicals);
+      // trouble if elementary[i-1][0]>1 nodal post-processing needed ?
+      if(numElements > 100000)
+        Msg::ProgressMeter(i + 1, numElements, true, "Reading elements");
+    }
+  }
+
+  // store the elements in their associated elementary entity. If the
+  // entity does not exist, create a new (discrete) one.
+  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
+    _storeElementsInEntities(elements[i]);
+
+  // associate the correct geometrical entity with each mesh vertex
+  _associateEntityWithMeshVertices();
+
+  // store the vertices in their associated geometrical entity
+  if(vertexVector.size())
+    _storeVerticesInEntities(vertexVector);
+  else
+    _storeVerticesInEntities(vertexMap);
+
+  // store the physical tags
+  for(int i = 0; i < 4; i++)
+    _storePhysicalTagsInEntities(i, physicals[i]);
+
+  fclose(fp);
+  return 1;
+}
+
+int GModel::writeDIFF(const std::string &name, bool binary, bool saveAll,
+                      double scalingFactor)
+{
+  if(binary){
+    Msg::Error("Binary DIFF output is not implemented");
+    return 0;
+  }
+
+  FILE *fp = fopen(name.c_str(), binary ? "wb" : "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  if(noPhysicalGroups()) saveAll = true;
+
+  // get the number of vertices and index the vertices in a continuous
+  // sequence
+  int numVertices = indexMeshVertices(saveAll);
+
+  // tag the vertices according to which surface they belong to (Note
+  // that we use a brute force approach here, so that we can deal with
+  // models with incomplete topology. For example, when we merge 2 STL
+  // triangulations we don't have the boundary information between the
+  // faces, and the vertices would end up categorized on either one.)
+  std::vector<std::list<int> > vertexTags(numVertices);
+  std::list<int> boundaryIndicators;
+  for(riter it = firstRegion(); it != lastRegion(); it++){
+    std::list<GFace*> faces = (*it)->faces();
+    for(std::list<GFace*>::iterator itf = faces.begin(); itf != faces.end(); itf++){
+      GFace *gf = *itf;
+      boundaryIndicators.push_back(gf->tag());
+      for(unsigned int i = 0; i < gf->getNumMeshElements(); i++){
+        MElement *e = gf->getMeshElement(i);
+        for(int j = 0; j < e->getNumVertices(); j++){
+          MVertex *v = e->getVertex(j);
+          if(v->getIndex() > 0)
+            vertexTags[v->getIndex() - 1].push_back(gf->tag());
+        }
+      }
+    }
+  }
+  boundaryIndicators.sort();
+  boundaryIndicators.unique();
+  for(int i = 0; i < numVertices; i++){
+    vertexTags[i].sort();
+    vertexTags[i].unique();
+  }
+
+  // get all the entities in the model
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+
+  // find max dimension of mesh elements we need to save
+  int dim = 0;
+  for(unsigned int i = 0; i < entities.size(); i++)
+    if(entities[i]->physicals.size() || saveAll)
+      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++)
+        dim = std::max(dim, entities[i]->getMeshElement(j)->getDim());
+
+  // loop over all elements we need to save
+  int numElements = 0, maxNumNodesPerElement = 0;
+  for(unsigned int i = 0; i < entities.size(); i++){
+    if(entities[i]->physicals.size() || saveAll){
+      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
+        MElement *e = entities[i]->getMeshElement(j);
+        if(e->getStringForDIFF() && e->getDim() == dim){
+          numElements++;
+          maxNumNodesPerElement = std::max(maxNumNodesPerElement, e->getNumVertices());
+        }
+      }
+    }
+  }
+
+  fprintf(fp, "\n\n");
+  fprintf(fp, " Finite element mesh (GridFE):\n\n");
+  fprintf(fp, " Number of space dim. =   3\n");
+  fprintf(fp, " Number of elements   =  %d\n", numElements);
+  fprintf(fp, " Number of nodes      =  %d\n\n", numVertices);
+  fprintf(fp, " All elements are of the same type : dpTRUE\n");
+  fprintf(fp, " Max number of nodes in an element: %d \n", maxNumNodesPerElement);
+  fprintf(fp, " Only one subdomain               : dpFALSE\n");
+  fprintf(fp, " Lattice data                     ? 0\n\n\n\n");
+  fprintf(fp, " %d Boundary indicators:  ", (int)boundaryIndicators.size());
+  for(std::list<int>::iterator it = boundaryIndicators.begin();
+      it != boundaryIndicators.end(); it++)
+    fprintf(fp, " %d", *it);
+
+  fprintf(fp, "\n\n\n");
+  fprintf(fp,"  Nodal coordinates and nodal boundary indicators,\n");
+  fprintf(fp,"  the columns contain:\n");
+  fprintf(fp,"   - node number\n");
+  fprintf(fp,"   - coordinates\n");
+  fprintf(fp,"   - no of boundary indicators that are set (ON)\n");
+  fprintf(fp,"   - the boundary indicators that are set (ON) if any.\n");
+  fprintf(fp,"#\n");
+
+  // write mesh vertices
+  for(unsigned int i = 0; i < entities.size(); i++){
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++){
+      MVertex *v = entities[i]->mesh_vertices[j];
+      if(v->getIndex() > 0){
+        v->writeDIFF(fp, binary, scalingFactor);
+        fprintf(fp, " [%d] ", (int)vertexTags[v->getIndex() - 1].size());
+        for(std::list<int>::iterator it = vertexTags[v->getIndex() - 1].begin();
+            it != vertexTags[v->getIndex() - 1].end(); it++)
+          fprintf(fp," %d ", *it);
+        fprintf(fp,"\n");
+      }
+    }
+  }
+
+  fprintf(fp, "\n");
+  fprintf(fp, "\n");
+  fprintf(fp,     "  Element types and connectivity\n");
+  fprintf(fp,     "  the columns contain:\n");
+  fprintf(fp,     "   - element number\n");
+  fprintf(fp,     "   - element type\n");
+  fprintf(fp,     "   - subdomain number \n");
+  fprintf(fp,     "   - the global node numbers of the nodes in the element.\n");
+  fprintf(fp,     "#\n");
+
+  // write mesh elements
+  int num = 0;
+  for(unsigned int i = 0; i < entities.size(); i++){
+    if(entities[i]->physicals.size() || saveAll){
+      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
+        MElement *e = entities[i]->getMeshElement(j);
+        if(e->getStringForDIFF() && e->getDim() == dim)
+          e->writeDIFF(fp, ++num, binary, entities[i]->tag());
+      }
+    }
+  }
+  fprintf(fp, "\n");
+
+  fclose(fp);
+  return 1;
+}
diff --git a/Geo/GModelIO_Geo.cpp b/Geo/GModelIO_GEO.cpp
similarity index 100%
rename from Geo/GModelIO_Geo.cpp
rename to Geo/GModelIO_GEO.cpp
diff --git a/Geo/GModelIO_GEOM.cpp b/Geo/GModelIO_GEOM.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b3fd5d223f0c3d0a5e38c10fd0d2e7fe6c5a44c6
--- /dev/null
+++ b/Geo/GModelIO_GEOM.cpp
@@ -0,0 +1,80 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "MTriangle.h"
+
+static bool getVertices(int num, int *indices, std::vector<MVertex*> &vec,
+                        std::vector<MVertex*> &vertices, int minVertex = 0)
+{
+  for(int i = 0; i < num; i++){
+    if(indices[i] < minVertex || indices[i] > (int)(vec.size() - 1 + minVertex)){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(vec[indices[i]]);
+  }
+  return true;
+}
+
+int GModel::readGEOM(const std::string &name)
+{
+  // this is a format (from geomview?) that Bruno Levy's Graphite code
+  // can write
+  FILE *fp = fopen(name.c_str(), "r");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  int numNodes, numElements, dummy;
+  if(fscanf(fp, "%d %d %d", &numNodes, &numElements, &dummy) != 3)
+    return 0;
+
+  if(!numNodes || !numElements){
+    Msg::Warning("No vertices or elements found");
+    return 0;
+  }
+
+  Msg::Info("%d vertices, %d elements", numNodes, numElements);
+
+  std::vector<MVertex*> vertexVector;
+  std::map<int, std::vector<MElement*> > elements[1];
+
+  vertexVector.resize(numNodes);
+  for(int i = 0; i < numNodes; i++) {
+    double x, y, z;
+    if(fscanf(fp, "%lf %lf %lf", &x, &y, &z) != 3) break;
+    vertexVector[i] = new MVertex(x, y, z);
+  }
+
+  for(int i = 0; i < numElements; i++) {
+    int N, n[3];
+    if(fscanf(fp, "%d", &N) != 1) break;
+    switch(N){
+    case 3:
+      {
+        if(fscanf(fp, "%d %d %d", &n[0], &n[1], &n[2]) != 3) break;
+        for(int i = 0; i < 3; i++) n[i]--;
+        std::vector<MVertex*> vertices;
+        if(!getVertices(3, n, vertexVector, vertices)) break;
+        elements[0][1].push_back(new MTriangle(vertices));
+      }
+      break;
+    default:
+      Msg::Error("Unknown element type in .geom reader");
+      break;
+    }
+  }
+
+  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
+    _storeElementsInEntities(elements[i]);
+  _associateEntityWithMeshVertices();
+  _storeVerticesInEntities(vertexVector);
+
+  fclose(fp);
+  return 1;
+}
diff --git a/Geo/GModelIO_INP.cpp b/Geo/GModelIO_INP.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ff74c9fba2fa6ec852286ec5a1d9430cebe514bc
--- /dev/null
+++ b/Geo/GModelIO_INP.cpp
@@ -0,0 +1,80 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "MPoint.h"
+#include "MLine.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+#include "MTetrahedron.h"
+#include "MHexahedron.h"
+#include "MPrism.h"
+#include "MPyramid.h"
+
+template <class T>
+static void writeElementsINP(FILE *fp, GEntity *ge, std::vector<T*> &elements,
+                             bool saveAll, int &ne)
+{
+  if(elements.size() && (saveAll || ge->physicals.size())){
+    const char *typ = elements[0]->getStringForINP();
+    if(typ){
+      int np = (saveAll ? 1 : ge->physicals.size());
+      for(int p = 0; p < np; p++){
+        int part = (saveAll ? ge->tag() : ge->physicals[p]);
+        const char *str = (ge->dim() == 3) ? "VOLUME" : (ge->dim() == 2) ?
+          "SURFACE" : (ge->dim() == 1) ? "CURVE" : "POINT";
+        fprintf(fp, "*Element, type=%s, ELSET=%s%d\n", typ, str, part);
+        for(unsigned int i = 0; i < elements.size(); i++)
+          elements[i]->writeINP(fp, ne++);
+      }
+    }
+  }
+}
+
+int GModel::writeINP(const std::string &name, bool saveAll,
+                     double scalingFactor)
+{
+  FILE *fp = fopen(name.c_str(), "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  if(noPhysicalGroups()) saveAll = true;
+
+  indexMeshVertices(saveAll);
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+
+  fprintf(fp, "*Heading\n");
+  fprintf(fp, " %s\n", name.c_str());
+
+  fprintf(fp, "*Node\n");
+  for(unsigned int i = 0; i < entities.size(); i++)
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
+      entities[i]->mesh_vertices[j]->writeINP(fp, scalingFactor);
+
+  int ne = 1;
+  for(viter it = firstVertex(); it != lastVertex(); ++it){
+    writeElementsINP(fp, *it, (*it)->points, saveAll, ne);
+  }
+  for(eiter it = firstEdge(); it != lastEdge(); ++it){
+    writeElementsINP(fp, *it, (*it)->lines, saveAll, ne);
+  }
+  for(fiter it = firstFace(); it != lastFace(); ++it){
+    writeElementsINP(fp, *it, (*it)->triangles, saveAll, ne);
+    writeElementsINP(fp, *it, (*it)->quadrangles, saveAll, ne);
+  }
+  for(riter it = firstRegion(); it != lastRegion(); ++it){
+    writeElementsINP(fp, *it, (*it)->tetrahedra, saveAll, ne);
+    writeElementsINP(fp, *it, (*it)->hexahedra, saveAll, ne);
+    writeElementsINP(fp, *it, (*it)->prisms, saveAll, ne);
+    writeElementsINP(fp, *it, (*it)->pyramids, saveAll, ne);
+  }
+
+  fclose(fp);
+  return 1;
+}
+
diff --git a/Geo/GModelIO_IR3.cpp b/Geo/GModelIO_IR3.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..92e6819c4cc7d93024b9b6f2964ae8b31f0289ed
--- /dev/null
+++ b/Geo/GModelIO_IR3.cpp
@@ -0,0 +1,68 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "MElement.h"
+
+int GModel::writeIR3(const std::string &name, int elementTagType,
+                     bool saveAll, double scalingFactor)
+{
+  FILE *fp = fopen(name.c_str(), "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  if(noPhysicalGroups()) saveAll = true;
+
+  int numVertices = indexMeshVertices(saveAll), num2D = 0, num3D = 0;
+  for(fiter it = firstFace(); it != lastFace(); ++it)
+    if(saveAll || (*it)->physicals.size())
+      num2D += (*it)->getNumMeshElements();
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    if(saveAll || (*it)->physicals.size())
+      num3D += (*it)->getNumMeshElements();
+
+  fprintf(fp,"33\n");
+  if(num2D && num3D)
+    fprintf(fp,"%d %d %d\n", numVertices, num2D, num3D);
+  else
+    fprintf(fp,"%d %d\n", numVertices, num2D ? num2D : num3D);
+
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+
+  for(unsigned int i = 0; i < entities.size(); i++)
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
+      if(entities[i]->mesh_vertices[j]->getIndex() >= 0)
+        fprintf(fp,"%d %.16g %.16g %.16g\n", entities[i]->mesh_vertices[j]->getIndex(),
+                entities[i]->mesh_vertices[j]->x() * scalingFactor,
+                entities[i]->mesh_vertices[j]->y() * scalingFactor,
+                entities[i]->mesh_vertices[j]->z() * scalingFactor);
+
+  int iElement = 1;
+  for(fiter it = firstFace(); it != lastFace(); ++it){
+    int numPhys = (*it)->physicals.size();
+    if(saveAll || numPhys)
+      for(unsigned int i = 0; i < (*it)->getNumMeshElements(); i++)
+        (*it)->getMeshElement(i)->writeIR3
+          (fp, elementTagType, iElement++, (*it)->tag(),
+           numPhys ? (*it)->physicals[0] : 0);
+  }
+
+  iElement = 1;
+  for(riter it = firstRegion(); it != lastRegion(); ++it){
+    int numPhys = (*it)->physicals.size();
+    if(saveAll || numPhys)
+      for(unsigned int i = 0; i < (*it)->getNumMeshElements(); i++)
+        (*it)->getMeshElement(i)->writeIR3
+          (fp, elementTagType, iElement++, (*it)->tag(),
+           numPhys ? (*it)->physicals[0] : 0);
+  }
+
+  fclose(fp);
+  return 1;
+}
+
diff --git a/Geo/GModelIO_MAIL.cpp b/Geo/GModelIO_MAIL.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..be4fc398f3fd2ce4163636967a1b17909e583db7
--- /dev/null
+++ b/Geo/GModelIO_MAIL.cpp
@@ -0,0 +1,61 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "MTriangle.h"
+
+int GModel::writeMAIL(const std::string &name, bool saveAll, double scalingFactor)
+{
+  // CEA triangulation (.mail format) for Eric Darrigrand. Note that
+  // we currently don't save the edges of the triangulation (the last
+  // part of the file).
+  FILE *fp = fopen(name.c_str(), "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  if(noPhysicalGroups()) saveAll = true;
+
+  int numVertices = indexMeshVertices(saveAll), numTriangles = 0;
+  for(fiter it = firstFace(); it != lastFace(); ++it)
+    if(saveAll || (*it)->physicals.size())
+      numTriangles += (*it)->triangles.size();
+
+  fprintf(fp, " %d %d\n", numVertices, numTriangles);
+
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+  for(unsigned int i = 0; i < entities.size(); i++){
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++){
+      MVertex *v = entities[i]->mesh_vertices[j];
+      fprintf(fp, " %19.10E %19.10E %19.10E\n", v->x() * scalingFactor,
+              v->y() * scalingFactor, v->z() * scalingFactor);
+    }
+  }
+
+  for(fiter it = firstFace(); it != lastFace(); ++it){
+    if(saveAll || (*it)->physicals.size()){
+      for(unsigned int i = 0; i < (*it)->triangles.size(); i++){
+        MTriangle *t = (*it)->triangles[i];
+        fprintf(fp, " %d %d %d\n", t->getVertex(0)->getIndex(),
+                t->getVertex(1)->getIndex(), t->getVertex(2)->getIndex());
+      }
+    }
+  }
+
+  // TODO write edges (with signs)
+  for(fiter it = firstFace(); it != lastFace(); ++it){
+    if(saveAll || (*it)->physicals.size()){
+      for(unsigned int i = 0; i < (*it)->triangles.size(); i++){
+        //MTriangle *t = (*it)->triangles[i];
+        fprintf(fp, " %d %d %d\n", 0, 0, 0);
+      }
+    }
+  }
+
+  fclose(fp);
+  return 1;
+}
diff --git a/Geo/GModelIO_MESH.cpp b/Geo/GModelIO_MESH.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dcd0b6ec0e09e64de0f49abbb0e6b5ee616aaf4c
--- /dev/null
+++ b/Geo/GModelIO_MESH.cpp
@@ -0,0 +1,275 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "MLine.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+#include "MTetrahedron.h"
+#include "MHexahedron.h"
+
+static bool getVertices(int num, int *indices, std::vector<MVertex*> &vec,
+                        std::vector<MVertex*> &vertices, int minVertex = 0)
+{
+  for(int i = 0; i < num; i++){
+    if(indices[i] < minVertex || indices[i] > (int)(vec.size() - 1 + minVertex)){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(vec[indices[i]]);
+  }
+  return true;
+}
+
+int GModel::readMESH(const std::string &name)
+{
+  FILE *fp = fopen(name.c_str(), "r");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  char buffer[256];
+  if(!fgets(buffer, sizeof(buffer), fp)) return 0;
+
+  char str[256];
+  int format;
+  sscanf(buffer, "%s %d", str, &format);
+  if(format == 3){
+    Msg::Error("Medit mesh import only available for ASCII files");
+    return 0;
+  }
+
+  std::vector<MVertex*> vertexVector;
+  std::map<int, std::vector<MElement*> > elements[5];
+
+  while(!feof(fp)) {
+    if(!fgets(buffer, 256, fp)) break;
+    if(buffer[0] != '#'){ // skip comments and empty lines
+      str[0]='\0';
+      sscanf(buffer, "%s", str);
+      if(!strcmp(str, "Dimension")){
+        if(!fgets(buffer, sizeof(buffer), fp)) break;
+      }
+      else if(!strcmp(str, "Vertices")){
+        if(!fgets(buffer, sizeof(buffer), fp)) break;
+        int nbv;
+        sscanf(buffer, "%d", &nbv);
+        Msg::Info("%d vertices", nbv);
+        vertexVector.resize(nbv);
+        for(int i = 0; i < nbv; i++) {
+          if(!fgets(buffer, sizeof(buffer), fp)) break;
+          int dum;
+          double x, y, z;
+          sscanf(buffer, "%lf %lf %lf %d", &x, &y, &z, &dum);
+          vertexVector[i] = new MVertex(x, y, z);
+        }
+      }
+      else if(!strcmp(str, "Edges")){
+        if(!fgets(buffer, sizeof(buffer), fp)) break;
+        int nbe;
+        sscanf(buffer, "%d", &nbe);
+        Msg::Info("%d edges", nbe);
+        for(int i = 0; i < nbe; i++) {
+          if(!fgets(buffer, sizeof(buffer), fp)) break;
+          int n[2], cl;
+          sscanf(buffer, "%d %d %d", &n[0], &n[1], &cl);
+          for(int j = 0; j < 2; j++) n[j]--;
+          std::vector<MVertex*> vertices;
+          if(!getVertices(2, n, vertexVector, vertices)) return 0;
+          elements[0][cl].push_back(new MLine(vertices));
+        }
+      }
+      else if(!strcmp(str, "Triangles")){
+        if(!fgets(buffer, sizeof(buffer), fp)) break;
+        int nbe;
+        sscanf(buffer, "%d", &nbe);
+        Msg::Info("%d triangles", nbe);
+        for(int i = 0; i < nbe; i++) {
+          if(!fgets(buffer, sizeof(buffer), fp)) break;
+          int n[3], cl;
+          sscanf(buffer, "%d %d %d %d", &n[0], &n[1], &n[2], &cl);
+          for(int j = 0; j < 3; j++) n[j]--;
+          std::vector<MVertex*> vertices;
+          if(!getVertices(3, n, vertexVector, vertices)) return 0;
+          elements[1][cl].push_back(new MTriangle(vertices));
+        }
+      }
+      else if(!strcmp(str, "Quadrilaterals")) {
+        if(!fgets(buffer, sizeof(buffer), fp)) break;
+        int nbe;
+        sscanf(buffer, "%d", &nbe);
+        Msg::Info("%d quadrangles", nbe);
+        for(int i = 0; i < nbe; i++) {
+          if(!fgets(buffer, sizeof(buffer), fp)) break;
+          int n[4], cl;
+          sscanf(buffer, "%d %d %d %d %d", &n[0], &n[1], &n[2], &n[3], &cl);
+          for(int j = 0; j < 4; j++) n[j]--;
+          std::vector<MVertex*> vertices;
+          if(!getVertices(4, n, vertexVector, vertices)) return 0;
+          elements[2][cl].push_back(new MQuadrangle(vertices));
+        }
+      }
+      else if(!strcmp(str, "Tetrahedra")) {
+        if(!fgets(buffer, sizeof(buffer), fp)) break;
+        int nbe;
+        sscanf(buffer, "%d", &nbe);
+        Msg::Info("%d tetrahedra", nbe);
+        for(int i = 0; i < nbe; i++) {
+          if(!fgets(buffer, sizeof(buffer), fp)) break;
+          int n[4], cl;
+          sscanf(buffer, "%d %d %d %d %d", &n[0], &n[1], &n[2], &n[3], &cl);
+          for(int j = 0; j < 4; j++) n[j]--;
+          std::vector<MVertex*> vertices;
+          if(!getVertices(4, n, vertexVector, vertices)) return 0;
+          elements[3][cl].push_back(new MTetrahedron(vertices));
+        }
+      }
+      else if(!strcmp(str, "Hexahedra")) {
+        if(!fgets(buffer, sizeof(buffer), fp)) break;
+        int nbe;
+        sscanf(buffer, "%d", &nbe);
+        Msg::Info("%d hexahedra", nbe);
+        for(int i = 0; i < nbe; i++) {
+          if(!fgets(buffer, sizeof(buffer), fp)) break;
+          int n[8], cl;
+          sscanf(buffer, "%d %d %d %d %d %d %d %d %d", &n[0], &n[1], &n[2], &n[3],
+                 &n[4], &n[5], &n[6], &n[7], &cl);
+          for(int j = 0; j < 8; j++) n[j]--;
+          std::vector<MVertex*> vertices;
+          if(!getVertices(8, n, vertexVector, vertices)) return 0;
+          elements[4][cl].push_back(new MHexahedron(vertices));
+        }
+      }
+    }
+  }
+
+  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
+    _storeElementsInEntities(elements[i]);
+  _associateEntityWithMeshVertices();
+  _storeVerticesInEntities(vertexVector);
+
+  fclose(fp);
+  return 1;
+}
+
+int GModel::writeMESH(const std::string &name, int elementTagType,
+                      bool saveAll, double scalingFactor)
+{
+  FILE *fp = fopen(name.c_str(), "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  if(noPhysicalGroups()) saveAll = true;
+
+  int numVertices = indexMeshVertices(saveAll);
+
+  fprintf(fp, " MeshVersionFormatted 1\n");
+  fprintf(fp, " Dimension\n");
+  fprintf(fp, " 3\n");
+
+  fprintf(fp, " Vertices\n");
+  fprintf(fp, " %d\n", numVertices);
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+  for(unsigned int i = 0; i < entities.size(); i++)
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
+      entities[i]->mesh_vertices[j]->writeMESH(fp, scalingFactor);
+
+  int numEdges = 0, numTriangles = 0, numQuadrangles = 0;
+  int numTetrahedra = 0, numHexahedra = 0;
+  for(eiter it = firstEdge(); it != lastEdge(); ++it){
+    if(saveAll || (*it)->physicals.size()){
+      numEdges += (*it)->lines.size();
+    }
+  }
+  for(fiter it = firstFace(); it != lastFace(); ++it){
+    if(saveAll || (*it)->physicals.size()){
+      numTriangles += (*it)->triangles.size();
+      numQuadrangles += (*it)->quadrangles.size();
+    }
+  }
+  for(riter it = firstRegion(); it != lastRegion(); ++it){
+    if(saveAll || (*it)->physicals.size()){
+      numTetrahedra += (*it)->tetrahedra.size();
+      numHexahedra += (*it)->hexahedra.size();
+    }
+  }
+
+  if(numEdges){
+    fprintf(fp, " Edges\n");
+    fprintf(fp, " %d\n", numEdges);
+    for(eiter it = firstEdge(); it != lastEdge(); ++it){
+      int numPhys = (*it)->physicals.size();
+      if(saveAll || numPhys){
+        for(unsigned int i = 0; i < (*it)->lines.size(); i++)
+          (*it)->lines[i]->writeMESH(fp, elementTagType, (*it)->tag(),
+                                     numPhys ? (*it)->physicals[0] : 0);
+      }
+    }
+  }
+  if(numTriangles){
+    fprintf(fp, " Triangles\n");
+    fprintf(fp, " %d\n", numTriangles);
+    for(fiter it = firstFace(); it != lastFace(); ++it){
+      int numPhys = (*it)->physicals.size();
+      if(saveAll || numPhys){
+        for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
+          (*it)->triangles[i]->writeMESH(fp, elementTagType, (*it)->tag(),
+                                         numPhys ? (*it)->physicals[0] : 0);
+      }
+    }
+  }
+  if(numQuadrangles){
+    fprintf(fp, " Quadrilaterals\n");
+    fprintf(fp, " %d\n", numQuadrangles);
+    for(fiter it = firstFace(); it != lastFace(); ++it){
+      int numPhys = (*it)->physicals.size();
+      if(saveAll || numPhys){
+        for(unsigned int i = 0; i < (*it)->quadrangles.size(); i++)
+          (*it)->quadrangles[i]->writeMESH(fp, elementTagType, (*it)->tag(),
+                                           numPhys ? (*it)->physicals[0] : 0);
+      }
+    }
+  }
+  if(numTetrahedra){
+    fprintf(fp, " Tetrahedra\n");
+    fprintf(fp, " %d\n", numTetrahedra);
+    for(riter it = firstRegion(); it != lastRegion(); ++it){
+      int numPhys = (*it)->physicals.size();
+      if(saveAll || numPhys){
+        for(unsigned int i = 0; i < (*it)->tetrahedra.size(); i++)
+          (*it)->tetrahedra[i]->writeMESH(fp, elementTagType, (*it)->tag(),
+                                          numPhys ? (*it)->physicals[0] : 0);
+      }
+    }
+  }
+  if(numHexahedra){
+    fprintf(fp, " Hexahedra\n");
+    fprintf(fp, " %d\n", numHexahedra);
+    for(riter it = firstRegion(); it != lastRegion(); ++it){
+      int numPhys = (*it)->physicals.size();
+      if(saveAll || numPhys){
+        for(unsigned int i = 0; i < (*it)->hexahedra.size(); i++)
+          (*it)->hexahedra[i]->writeMESH(fp, elementTagType, (*it)->tag(),
+                                         numPhys ? (*it)->physicals[0] : 0);
+      }
+    }
+  }
+
+  fprintf(fp, " End\n");
+
+  fclose(fp);
+  return 1;
+}
+
+
+
+
+
+
diff --git a/Geo/GModelIO_MSH.cpp b/Geo/GModelIO_MSH.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0401e647b52f451c9eb6f24ae7b475b587620dfe
--- /dev/null
+++ b/Geo/GModelIO_MSH.cpp
@@ -0,0 +1,185 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "GmshMessage.h"
+#include "MElement.h"
+#include "MPoint.h"
+#include "MLine.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+#include "MTetrahedron.h"
+#include "MHexahedron.h"
+#include "MPrism.h"
+#include "MPyramid.h"
+
+int GModel::readMSH(const std::string &name)
+{
+  double version = 2.2;
+  if(version < 3)
+    return _readMSH2(name);
+
+  return 0;
+}
+
+static int getNumElementsMSH(GEntity *ge, bool saveAll, int saveSinglePartition)
+{
+  if(!saveAll && ge->physicals.empty()) return 0;
+
+  int n = 0;
+  if(saveSinglePartition <= 0)
+    n = ge->getNumMeshElements();
+  else
+    for(unsigned int i = 0; i < ge->getNumMeshElements(); i++)
+      if(ge->getMeshElement(i)->getPartition() == saveSinglePartition)
+        n++;
+  return n;
+}
+
+static void writeElementMSH(FILE *fp, GModel *model, MElement *ele, bool binary,
+                            int elementary)
+{
+  if(model->getGhostCells().size()){
+    std::vector<short> ghosts;
+    std::pair<std::multimap<MElement*, short>::iterator,
+              std::multimap<MElement*, short>::iterator> itp =
+      model->getGhostCells().equal_range(ele);
+    for(std::multimap<MElement*, short>::iterator it = itp.first;
+        it != itp.second; it++)
+      ghosts.push_back(it->second);
+    ele->writeMSH(fp, binary, elementary, &ghosts);
+  }
+  else
+    ele->writeMSH(fp, binary, elementary);
+}
+
+template<class T>
+static void writeElementsMSH(FILE *fp, GModel *model, GEntity *ge, std::vector<T*> &ele,
+                             bool saveAll, int saveSinglePartition, bool binary)
+{
+  if(!saveAll && ge->physicals.empty()) return;
+
+  for(unsigned int i = 0; i < ele.size(); i++){
+    if(saveSinglePartition && ele[i]->getPartition() != saveSinglePartition)
+      continue;
+    writeElementMSH(fp, model, ele[i], binary, ge->tag());
+  }
+}
+
+int GModel::writeMSH(const std::string &name, double version, bool binary,
+                     bool saveAll, bool saveParametric,
+                     double scalingFactor, int elementStartNum,
+                     int saveSinglePartition)
+{
+  if(version < 3)
+    return _writeMSH2(name, version, binary, saveAll, saveParametric,
+                      scalingFactor, elementStartNum, saveSinglePartition);
+
+  FILE *fp = fopen(name.c_str(), binary ? "wb" : "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  // if there are no physicals we save all the elements
+  if(noPhysicalGroups()) saveAll = true;
+
+  // get the number of vertices and index the vertices in a continuous
+  // sequence
+  int numVertices = indexMeshVertices(saveAll, saveSinglePartition);
+
+  // get the number of elements we need to save
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+  int numElements = 0;
+  for(unsigned int i = 0; i < entities.size(); i++)
+    numElements += getNumElementsMSH(entities[i], saveAll, saveSinglePartition);
+
+  fprintf(fp, "$MeshFormat\n");
+  fprintf(fp, "%g %d %d\n", version, binary ? 1 : 0, (int)sizeof(double));
+  if(binary){
+    int one = 1;
+    fwrite(&one, sizeof(int), 1, fp);
+    fprintf(fp, "\n");
+  }
+  fprintf(fp, "$EndMeshFormat\n");
+
+  if(numPhysicalNames()){
+    fprintf(fp, "$PhysicalNames\n");
+    fprintf(fp, "%d\n", numPhysicalNames());
+    for(piter it = firstPhysicalName(); it != lastPhysicalName(); it++)
+      fprintf(fp, "%d %d \"%s\"\n", it->first.first, it->first.second,
+              it->second.c_str());
+    fprintf(fp, "$EndPhysicalNames\n");
+  }
+
+  fprintf(fp, "$Entities\n");
+  std::vector<GEntity*> entitiesWithPhysical;
+  if(saveAll)
+    entitiesWithPhysical = entities;
+  else{
+    for(unsigned int i = 0; i < entities.size(); i++){
+      GEntity *ge = entities[i];
+      std::vector<int> physicallist = ge->getPhysicalEntities();
+      if(physicallist.size()) entitiesWithPhysical.push_back(ge);
+    }
+  }
+  fprintf(fp, "%d\n", (int)entitiesWithPhysical.size());
+  for(unsigned int i = 0; i < entitiesWithPhysical.size(); i++){
+    GEntity *ge = entitiesWithPhysical[i];
+    std::vector<int> physicallist = ge->getPhysicalEntities();
+    fprintf(fp, "%d %d %d", ge->tag(), ge->dim(), (int)physicallist.size());
+    for(unsigned int j = 0; j < physicallist.size(); j++)
+      fprintf(fp, " %d", physicallist[j]);
+    fprintf(fp, "\n");
+  }
+  fprintf(fp, "$EndEntities\n");
+
+  fprintf(fp, "$Nodes\n");
+  fprintf(fp, "%d\n", numVertices);
+  for(unsigned int i = 0; i < entities.size(); i++)
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
+      entities[i]->mesh_vertices[j]->writeMSH(fp, binary, scalingFactor);
+
+  if(binary) fprintf(fp, "\n");
+  fprintf(fp, "$EndNodes\n");
+
+  fprintf(fp, "$Elements\n");
+  fprintf(fp, "%d\n", numElements);
+
+  _elementIndexCache.clear();
+
+  for(viter it = firstVertex(); it != lastVertex(); ++it)
+    writeElementsMSH(fp, this, *it, (*it)->points, saveAll, saveSinglePartition,
+                     binary);
+  for(eiter it = firstEdge(); it != lastEdge(); ++it)
+    writeElementsMSH(fp, this, *it, (*it)->lines, saveAll, saveSinglePartition,
+                     binary);
+  for(fiter it = firstFace(); it != lastFace(); ++it)
+    writeElementsMSH(fp, this, *it, (*it)->triangles, saveAll, saveSinglePartition,
+                     binary);
+  for(fiter it = firstFace(); it != lastFace(); ++it)
+    writeElementsMSH(fp, this, *it, (*it)->quadrangles, saveAll, saveSinglePartition,
+                     binary);
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    writeElementsMSH(fp, this, *it, (*it)->tetrahedra, saveAll, saveSinglePartition,
+                     binary);
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    writeElementsMSH(fp, this, *it, (*it)->hexahedra, saveAll, saveSinglePartition,
+                     binary);
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    writeElementsMSH(fp, this, *it, (*it)->prisms, saveAll, saveSinglePartition,
+                     binary);
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    writeElementsMSH(fp, this, *it, (*it)->pyramids, saveAll, saveSinglePartition,
+                     binary);
+
+  if(binary) fprintf(fp, "\n");
+
+  fprintf(fp, "$EndElements\n");
+  fclose(fp);
+
+  return 1;
+}
diff --git a/Geo/GModelIO_MSH2.cpp b/Geo/GModelIO_MSH2.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d2ddaae236f0391b977b02b8e669b280481a033
--- /dev/null
+++ b/Geo/GModelIO_MSH2.cpp
@@ -0,0 +1,1055 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include <stdlib.h>
+#include <string.h>
+#include <map>
+#include <string>
+#include <sstream>
+#include <cassert>
+#include <iomanip>
+#include "GModel.h"
+#include "GmshDefines.h"
+#include "MPoint.h"
+#include "MLine.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+#include "MTetrahedron.h"
+#include "MHexahedron.h"
+#include "MPrism.h"
+#include "MPyramid.h"
+#include "MElementCut.h"
+#include "StringUtils.h"
+#include "GmshMessage.h"
+#include "Context.h"
+#include "OS.h"
+
+#define FAST_ELEMENTS 1
+
+static bool getVertices(int num, int *indices, std::map<int, MVertex*> &map,
+                        std::vector<MVertex*> &vertices)
+{
+  for(int i = 0; i < num; i++){
+    if(!map.count(indices[i])){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(map[indices[i]]);
+  }
+  return true;
+}
+
+static bool getVertices(int num, int *indices, std::vector<MVertex*> &vec,
+                        std::vector<MVertex*> &vertices, int minVertex = 0)
+{
+  for(int i = 0; i < num; i++){
+    if(indices[i] < minVertex || indices[i] > (int)(vec.size() - 1 + minVertex)){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(vec[indices[i]]);
+  }
+  return true;
+}
+
+static MElement *createElementMSH2(GModel *m, int num, int typeMSH, int physical,
+                                   int reg, int part, std::vector<MVertex*> &v,
+                                   std::map<int, std::vector<MElement*> > elements[10],
+                                   std::map<int, std::map<int, std::string> > physicals[4],
+                                   bool owner=false, MElement *parent=0,
+                                   MElement *d1=0, MElement *d2=0)
+{
+  if(CTX::instance()->mesh.switchElementTags) {
+    int tmp = reg;
+    reg = physical;
+    physical = tmp;
+  }
+
+  MElementFactory factory;
+  MElement *e = factory.create(typeMSH, v, num, part, owner, parent, d1, d2);
+
+  if(!e){
+    Msg::Error("Unknown type of element %d", typeMSH);
+    return NULL;
+  }
+
+#if (FAST_ELEMENTS!=1)
+  switch(e->getType()){
+  case TYPE_PNT :
+    elements[0][reg].push_back(e); break;
+  case TYPE_LIN :
+    elements[1][reg].push_back(e); break;
+  case TYPE_TRI :
+    elements[2][reg].push_back(e); break;
+  case TYPE_QUA :
+    elements[3][reg].push_back(e); break;
+  case TYPE_TET :
+    elements[4][reg].push_back(e); break;
+  case TYPE_HEX :
+    elements[5][reg].push_back(e); break;
+  case TYPE_PRI :
+    elements[6][reg].push_back(e); break;
+  case TYPE_PYR :
+    elements[7][reg].push_back(e); break;
+  case TYPE_POLYG :
+    elements[8][reg].push_back(e); break;
+  case TYPE_POLYH :
+    elements[9][reg].push_back(e); break;
+  default : Msg::Error("Wrong type of element"); return NULL;
+  }
+#endif
+
+  int dim = e->getDim();
+  if(physical && (!physicals[dim].count(reg) || !physicals[dim][reg].count(physical)))
+    physicals[dim][reg][physical] = "unnamed";
+
+  if(part) m->getMeshPartitions().insert(part);
+  return e;
+}
+
+#if (FAST_ELEMENTS == 0)
+
+static bool getElementsByNum(int elemNum[], std::map<int, std::vector<MElement*> > &elements,
+                             bool erase, MElement *elems[], int nbElem = 1)
+{
+  int i = 0;
+  std::map<int, std::vector<MElement*> >::iterator it = elements.begin();
+  for(; it != elements.end(); ++it) {
+    std::vector<MElement*>::iterator itE = it->second.begin();
+    for(; itE != it->second.end(); itE++) {
+      for(int j = 0; j < nbElem; j++) {
+        if((*itE)->getNum() == elemNum[j]) {
+          elems[i++] = (*itE);
+          if(erase) itE = it->second.erase(itE);
+          if(i == nbElem) return true;
+        }
+      }
+    }
+  }
+  return false;
+}
+
+static MElement *getParent(int parentNum, int type,
+                           std::map<int, std::vector<MElement*> > elements[10])
+{
+  MElement *parent = NULL;
+  switch(type){
+  case MSH_LIN_C :
+    getElementsByNum(&parentNum, elements[1], true, &parent);
+    return parent;
+  case MSH_POLYG_ : case MSH_POLYG_B :
+    if(getElementsByNum(&parentNum, elements[2], true, &parent)) return parent;
+    if(getElementsByNum(&parentNum, elements[3], true, &parent)) return parent;
+    if(getElementsByNum(&parentNum, elements[8], true, &parent)) return parent;
+    return parent;
+  case MSH_POLYH_ :
+    if(getElementsByNum(&parentNum, elements[4], true, &parent)) return parent;
+    if(getElementsByNum(&parentNum, elements[5], true, &parent)) return parent;
+    if(getElementsByNum(&parentNum, elements[6], true, &parent)) return parent;
+    if(getElementsByNum(&parentNum, elements[7], true, &parent)) return parent;
+    if(getElementsByNum(&parentNum, elements[9], true, &parent)) return parent;
+    return parent;
+  default : return parent;
+  }
+}
+
+static void getDomains(int dom1Num, int dom2Num, int type,
+                      std::map<int, std::vector<MElement*> > elements[10],
+                      MElement *doms[])
+{
+  int domNums[2] = {dom1Num, dom2Num};
+  int nbD = (dom1Num == 0 || dom2Num == 0) ? 1 : 2;
+  if(dom1Num == 0) domNums[0] = dom2Num;
+  switch(type){
+  case MSH_LIN_B :
+    getElementsByNum(domNums, elements[8], false, doms, nbD);
+    return;
+  case MSH_TRI_B : case MSH_POLYG_B :
+    getElementsByNum(domNums, elements[9], false, doms, nbD);
+    return;
+  }
+}
+
+#endif
+
+int GModel::_readMSH2(const std::string &name)
+{
+  FILE *fp = fopen(name.c_str(), "rb");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  char str[256] = "XXX";
+  double version = 1.0;
+  bool binary = false, swap = false, postpro = false;
+  std::map<int, std::vector<MElement*> > elements[10];
+  std::map<int, std::map<int, std::string> > physicals[4];
+  std::map<int, MVertex*> vertexMap;
+  std::vector<MVertex*> vertexVector;
+  int minVertex = 0;
+
+  while(1) {
+
+    while(str[0] != '$'){
+      if(!fgets(str, sizeof(str), fp) || feof(fp))
+        break;
+    }
+
+    if(feof(fp))
+      break;
+
+    if(!strncmp(&str[1], "MeshFormat", 10)) {
+
+      if(!fgets(str, sizeof(str), fp)) return 0;
+      int format, size;
+      if(sscanf(str, "%lf %d %d", &version, &format, &size) != 3) return 0;
+      if(format){
+        binary = true;
+        Msg::Info("Mesh is in binary format");
+        int one;
+        if(fread(&one, sizeof(int), 1, fp) != 1) return 0;
+        if(one != 1){
+          swap = true;
+          Msg::Info("Swapping bytes from binary file");
+        }
+      }
+
+    }
+    else if(!strncmp(&str[1], "PhysicalNames", 13)) {
+
+      if(!fgets(str, sizeof(str), fp)) return 0;
+      int numNames;
+      if(sscanf(str, "%d", &numNames) != 1) return 0;
+      for(int i = 0; i < numNames; i++) {
+        int dim = -1, num;
+        if(version > 2.0){
+          if(fscanf(fp, "%d", &dim) != 1) return 0;
+        }
+        if(fscanf(fp, "%d", &num) != 1) return 0;
+        if(!fgets(str, sizeof(str), fp)) return 0;
+        std::string name = ExtractDoubleQuotedString(str, 256);
+        if(name.size()) setPhysicalName(name, dim, num);
+      }
+
+    }
+    else if(!strncmp(&str[1], "NO", 2) || !strncmp(&str[1], "Nodes", 5) ||
+            !strncmp(&str[1], "ParametricNodes", 15)) {
+
+      const bool parametric = !strncmp(&str[1], "ParametricNodes", 15);
+      if(!fgets(str, sizeof(str), fp)) return 0;
+      int numVertices = -1;
+      if(sscanf(str, "%d", &numVertices) != 1) return 0;
+      Msg::Info("%d vertices", numVertices);
+      Msg::ResetProgressMeter();
+      vertexVector.clear();
+      vertexMap.clear();
+      minVertex = numVertices + 1;
+      int maxVertex = -1;
+      for(int i = 0; i < numVertices; i++) {
+        int num;
+        double xyz[3], uv[2];
+        MVertex *newVertex = 0;
+        if (!parametric){
+          if(!binary){
+            if (fscanf(fp, "%d %lf %lf %lf", &num, &xyz[0], &xyz[1], &xyz[2]) != 4)
+              return 0;
+          }
+          else{
+            if(fread(&num, sizeof(int), 1, fp) != 1) return 0;
+            if(swap) SwapBytes((char*)&num, sizeof(int), 1);
+            if(fread(xyz, sizeof(double), 3, fp) != 3) return 0;
+            if(swap) SwapBytes((char*)xyz, sizeof(double), 3);
+          }
+          newVertex = new MVertex(xyz[0], xyz[1], xyz[2], 0, num);
+        }
+        else{
+          int iClasDim, iClasTag;
+          if(!binary){
+            if (fscanf(fp, "%d %lf %lf %lf %d %d", &num, &xyz[0], &xyz[1], &xyz[2],
+                       &iClasDim, &iClasTag) != 6)
+              return 0;
+          }
+          else{
+            if(fread(&num, sizeof(int), 1, fp) != 1) return 0;
+            if(swap) SwapBytes((char*)&num, sizeof(int), 1);
+            if(fread(xyz, sizeof(double), 3, fp) != 3) return 0;
+            if(swap) SwapBytes((char*)xyz, sizeof(double), 3);
+            if(fread(&iClasDim, sizeof(int), 1, fp) != 1) return 0;
+            if(swap) SwapBytes((char*)&iClasDim, sizeof(int), 1);
+            if(fread(&iClasTag, sizeof(int), 1, fp) != 1) return 0;
+            if(swap) SwapBytes((char*)&iClasTag, sizeof(int), 1);
+          }
+          if (iClasDim == 0){
+            GVertex *gv = getVertexByTag(iClasTag);
+            if (gv) gv->deleteMesh();
+            newVertex = new MVertex(xyz[0], xyz[1], xyz[2], gv, num);
+          }
+          else if (iClasDim == 1){
+            GEdge *ge = getEdgeByTag(iClasTag);
+            if(!binary){
+              if (fscanf(fp, "%lf", &uv[0]) != 1) return 0;
+            }
+            else{
+              if(fread(uv, sizeof(double), 1, fp) != 1) return 0;
+              if(swap) SwapBytes((char*)uv, sizeof(double), 1);
+            }
+            newVertex = new MEdgeVertex(xyz[0], xyz[1], xyz[2], ge, uv[0], -1.0, num);
+          }
+          else if (iClasDim == 2){
+            GFace *gf = getFaceByTag(iClasTag);
+            if(!binary){
+              if (fscanf(fp, "%lf %lf", &uv[0], &uv[1]) != 2) return 0;
+            }
+            else{
+              if(fread(uv, sizeof(double), 2, fp) != 2) return 0;
+              if(swap) SwapBytes((char*)uv, sizeof(double), 2);
+            }
+            newVertex = new MFaceVertex(xyz[0], xyz[1], xyz[2], gf, uv[0], uv[1], num);
+          }
+          else if (iClasDim == 3){
+            GRegion *gr = getRegionByTag(iClasTag);
+            newVertex = new MVertex(xyz[0], xyz[1], xyz[2], gr, num);
+          }
+        }
+        minVertex = std::min(minVertex, num);
+        maxVertex = std::max(maxVertex, num);
+        if(vertexMap.count(num))
+          Msg::Warning("Skipping duplicate vertex %d", num);
+        vertexMap[num] = newVertex;
+        if(numVertices > 100000)
+          Msg::ProgressMeter(i + 1, numVertices, true, "Reading nodes");
+      }
+      // If the vertex numbering is dense, transfer the map into a
+      // vector to speed up element creation
+      if((int)vertexMap.size() == numVertices &&
+         ((minVertex == 1 && maxVertex == numVertices) ||
+          (minVertex == 0 && maxVertex == numVertices - 1))){
+        Msg::Info("Vertex numbering is dense");
+        vertexVector.resize(vertexMap.size() + 1);
+        if(minVertex == 1)
+          vertexVector[0] = 0;
+        else
+          vertexVector[numVertices] = 0;
+        std::map<int, MVertex*>::const_iterator it = vertexMap.begin();
+        for(; it != vertexMap.end(); ++it)
+          vertexVector[it->first] = it->second;
+        vertexMap.clear();
+      }
+
+    }
+    else if(!strncmp(&str[1], "ELM", 3) || !strncmp(&str[1], "Elements", 8)) {
+
+      if(!fgets(str, sizeof(str), fp)) return 0;
+      int numElements;
+
+      std::map<int, MElement*> elems;
+      std::map<int, MElement*> parents;
+      std::map<int, MElement*> domains;
+      std::map<int, int> elemreg;
+      std::map<int, int> elemphy;
+
+      std::set<MElement*> parentsOwned;
+      sscanf(str, "%d", &numElements);
+      Msg::Info("%d elements", numElements);
+      Msg::ResetProgressMeter();
+      if(!binary){
+        for(int i = 0; i < numElements; i++) {
+          int num, type, physical = 0, elementary = 0, partition = 0, parent = 0;
+          int dom1 = 0, dom2 = 0, numVertices;
+          std::vector<short> ghosts;
+          if(version <= 1.0){
+            if(fscanf(fp, "%d %d %d %d %d", &num, &type, &physical, &elementary,
+                      &numVertices) != 5)
+              return 0;
+            if(numVertices != MElement::getInfoMSH(type)) return 0;
+          }
+          else{
+            int numTags;
+            if(fscanf(fp, "%d %d %d", &num, &type, &numTags) != 3) return 0;
+            int numPartitions = 0;
+            for(int j = 0; j < numTags; j++){
+              int tag;
+              if(fscanf(fp, "%d", &tag) != 1) return 0;
+              if(j == 0) physical = tag;
+              else if(j == 1) elementary = tag;
+              else if(version < 2.2 && j == 2) partition = tag;
+              else if(version >= 2.2 && j == 2 && numTags > 3) numPartitions = tag;
+              else if(version >= 2.2 && j == 3) partition = tag;
+              else if(j >= 4 && j < 4 + numPartitions - 1) ghosts.push_back(-tag);
+              else if(j == 3 + numPartitions && (numTags == 4 + numPartitions))
+                parent = tag;
+              else if(j == 3 + numPartitions && (numTags == 5 + numPartitions)) {
+                dom1 = tag; j++;
+                if(fscanf(fp, "%d", &dom2) != 1) return 0;
+              }
+            }
+            if(!(numVertices = MElement::getInfoMSH(type))) {
+              if(type != MSH_POLYG_ && type != MSH_POLYH_ && type != MSH_POLYG_B)
+                return 0;
+              if(fscanf(fp, "%d", &numVertices) != 1) return 0;
+            }
+          }
+          int *indices = new int[numVertices];
+          for(int j = 0; j < numVertices; j++)
+            if(fscanf(fp, "%d", &indices[j]) != 1) return 0;
+          std::vector<MVertex*> vertices;
+          if(vertexVector.size()){
+            if(!getVertices(numVertices, indices, vertexVector, vertices, minVertex))
+              return 0;
+          }
+          else{
+            if(!getVertices(numVertices, indices, vertexMap, vertices))
+              return 0;
+          }
+          MElement *p = NULL;
+          bool own = false;
+
+	  // search parent element
+	  if (parent){
+#if (FAST_ELEMENTS == 1)
+            std::map<int, MElement* >::iterator ite = elems.find(parent);
+            if (ite == elems.end())
+              Msg::Error("Parent element %d not found for element %d", parent, num);
+            else{
+              p = ite->second;
+              parents[parent] = p;
+            }
+            std::set<MElement* >::iterator itpo = parentsOwned.find(p);
+            if (itpo == parentsOwned.end()){
+              own = true;
+              parentsOwned.insert(p);
+            }
+            assert(p != NULL);
+#else
+            std::map<int, MElement* >::iterator itp = parents.find(parent);
+            if (itp == parents.end()){
+              p = getParent(parent, type, elements);
+              if (p)
+                parents[parent] = p;
+              else Msg::Error("Parent element %d not found for element %d", parent, num);
+            }
+            else
+              p = itp->second;
+            std::set<MElement* >::iterator itpo = parentsOwned.find(p);
+            if (itpo == parentsOwned.end()){
+              own = true;
+              parentsOwned.insert(p);
+            }
+#endif
+	  }
+
+          // search domains
+          MElement *doms[2] = {NULL, NULL};
+	  if(dom1){
+#if (FAST_ELEMENTS==1)
+            std::map<int, MElement* >::iterator ite = elems.find(dom1);
+	    if (ite == elems.end())
+              Msg::Error("Domain element %d not found for element %d", dom1, num);
+            else
+              doms[0] = ite->second;
+
+	    ite = elems.find(dom2);
+	    if (ite != elems.end())
+              doms[1] = ite->second;
+
+            if(!doms[0])
+              Msg::Error("Domain element %d not found for element %d", dom1, num);
+            if(dom2 && !doms[1])
+              Msg::Error("Domain element %d not found for element %d", dom2, num);
+#else
+            getDomains(dom1, dom2, type, elements, doms);
+            if(!doms[0])
+              Msg::Error("Domain element %d not found for element %d", dom1, num);
+            if(dom2 && !doms[1])
+              Msg::Error("Domain element %d not found for element %d", dom2, num);
+#endif
+	  }
+          if (CTX::instance()->mesh.ignorePartBound && elementary<0) continue;
+          MElement *e = createElementMSH2(this, num, type, physical, elementary,
+                                          partition, vertices, elements, physicals,
+                                          own, p, doms[0], doms[1]);
+
+#if (FAST_ELEMENTS == 1)
+	  elems[num] = e;
+	  elemreg[num] = elementary;
+	  elemphy[num] = physical;
+#endif
+
+          for(unsigned int j = 0; j < ghosts.size(); j++)
+            _ghostCells.insert(std::pair<MElement*, short>(e, ghosts[j]));
+          if(numElements > 100000)
+            Msg::ProgressMeter(i + 1, numElements, true, "Reading elements");
+          delete [] indices;
+        }
+      }
+      else{
+        int numElementsPartial = 0;
+        while(numElementsPartial < numElements){
+          int header[3];
+          if(fread(header, sizeof(int), 3, fp) != 3) return 0;
+          if(swap) SwapBytes((char*)header, sizeof(int), 3);
+          int type = header[0];
+          int numElms = header[1];
+          int numTags = header[2];
+          int numVertices = MElement::getInfoMSH(type);
+          unsigned int n = 1 + numTags + numVertices;
+          int *data = new int[n];
+          for(int i = 0; i < numElms; i++) {
+            if(fread(data, sizeof(int), n, fp) != n) return 0;
+            if(swap) SwapBytes((char*)data, sizeof(int), n);
+            int num = data[0];
+            int physical = (numTags > 0) ? data[1] : 0;
+            int elementary = (numTags > 1) ? data[2] : 0;
+            int numPartitions = (version >= 2.2 && numTags > 3) ? data[3] : 0;
+            int partition = (version < 2.2 && numTags > 2) ? data[3] :
+              (version >= 2.2 && numTags > 3) ? data[4] : 0;
+            int parent = (version < 2.2 && numTags > 3) ||
+              (version >= 2.2 && numPartitions && numTags > 3 + numPartitions) ||
+              (version >= 2.2 && !numPartitions && numTags > 2) ?
+              data[numTags] : 0;
+            int *indices = &data[numTags + 1];
+            std::vector<MVertex*> vertices;
+            if(vertexVector.size()){
+              if(!getVertices(numVertices, indices, vertexVector, vertices, minVertex))
+                return 0;
+            }
+            else{
+              if(!getVertices(numVertices, indices, vertexMap, vertices))
+                return 0;
+            }
+            MElement *p = NULL;
+            bool own = false;
+            if(parent){
+#if (FAST_ELEMENTS == 1)
+	      std::map<int, MElement* >::iterator ite = elems.find(parent);
+	      if (ite == elems.end())
+                Msg::Error("Parent element %d not found for element %d", parent, num);
+	      else{
+                p = ite->second;
+                parents[parent] = p;
+	      }
+	      std::set<MElement* >::iterator itpo = parentsOwned.find(p);
+	      if (itpo == parentsOwned.end()){
+                own = true;
+                parentsOwned.insert(p);
+	      }
+	      assert(p != NULL);
+#else
+	      std::map<int, MElement* >::iterator itp = parents.find(parent);
+              if(itp == parents.end()){
+                p = getParent(parent, type, elements);
+                if(p) parents[parent] = p;
+                else Msg::Error("Parent element %d not found", parent);
+              }
+              else p = itp->second;
+	      std::set<MElement* >::iterator itpo = parentsOwned.find(p);
+              if(itpo == parentsOwned.end()) {
+                own = true;
+                parentsOwned.insert(p);
+              }
+#endif
+            }
+            MElement *e = createElementMSH2(this, num, type, physical, elementary,
+                                            partition, vertices, elements, physicals,
+                                            own, p);
+
+#if (FAST_ELEMENTS==1)
+            elems[num] = e;
+            elemreg[num] = elementary;
+            elemphy[num] = physical;
+#endif
+            if(numPartitions > 1)
+              for(int j = 0; j < numPartitions - 1; j++)
+                _ghostCells.insert(std::pair<MElement*, short>(e, -data[5 + j]));
+            if(numElements > 100000)
+              Msg::ProgressMeter(numElementsPartial + i + 1, numElements, true,
+                                 "Reading elements");
+          }
+          delete [] data;
+          numElementsPartial += numElms;
+        }
+      }
+#if (FAST_ELEMENTS==1)
+      for(int i = 0; i < 10; i++)
+        elements[i].clear();
+
+      std::map<int, MElement* >::iterator ite;
+      for (ite = elems.begin(); ite != elems.end(); ite++){
+        int num = ite->first;
+        MElement *e = ite->second;
+        if (parents.find(num) == parents.end()){
+          int reg;
+          if (CTX::instance()->mesh.switchElementTags) {
+            reg = elemphy[num];
+          }
+          else{
+            reg = elemreg[num];
+          }
+          switch (e->getType()){
+          case TYPE_PNT : elements[0][reg].push_back(e); break;
+          case TYPE_LIN : elements[1][reg].push_back(e); break;
+          case TYPE_TRI : elements[2][reg].push_back(e); break;
+          case TYPE_QUA : elements[3][reg].push_back(e); break;
+          case TYPE_TET : elements[4][reg].push_back(e); break;
+          case TYPE_HEX : elements[5][reg].push_back(e); break;
+          case TYPE_PRI : elements[6][reg].push_back(e); break;
+          case TYPE_PYR : elements[7][reg].push_back(e); break;
+          case TYPE_POLYG : elements[8][reg].push_back(e); break;
+          case TYPE_POLYH : elements[9][reg].push_back(e); break;
+          default :
+            Msg::Error("Wrong type of element");
+            return false;
+          }
+        }
+      }
+#endif
+    }
+    else if(!strncmp(&str[1], "NodeData", 8)) {
+
+      // there's some nodal post-processing data to read later on, so
+      // cache the vertex indexing data
+      if(vertexVector.size())
+        _vertexVectorCache = vertexVector;
+      else
+        _vertexMapCache = vertexMap;
+      postpro = true;
+      break;
+    }
+    else if(!strncmp(&str[1], "ElementData", 11) ||
+            !strncmp(&str[1], "ElementNodeData", 15)) {
+
+      // there's some element post-processing data to read later on
+      postpro = true;
+      break;
+    }
+
+    do {
+      if(!fgets(str, sizeof(str), fp) || feof(fp))
+        break;
+    } while(str[0] != '$');
+  }
+
+  // store the elements in their associated elementary entity. If the
+  // entity does not exist, create a new (discrete) one.
+  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
+    _storeElementsInEntities(elements[i]);
+
+  // associate the correct geometrical entity with each mesh vertex
+  _associateEntityWithMeshVertices();
+
+  // store the vertices in their associated geometrical entity
+  if(vertexVector.size())
+    _storeVerticesInEntities(vertexVector);
+  else
+    _storeVerticesInEntities(vertexMap);
+
+  // store the physical tags
+  for(int i = 0; i < 4; i++)
+    _storePhysicalTagsInEntities(i, physicals[i]);
+
+  fclose(fp);
+
+  return postpro ? 2 : 1;
+}
+
+template<class T>
+static void writeElementMSH(FILE *fp, GModel *model, T *ele, bool saveAll,
+                            double version, bool binary, int &num, int elementary,
+                            std::vector<int> &physicals, int parentNum = 0,
+                            int dom1Num = 0, int dom2Num = 0)
+{
+  std::vector<short> ghosts;
+  if(model->getGhostCells().size()){
+    std::pair<std::multimap<MElement*, short>::iterator,
+              std::multimap<MElement*, short>::iterator> itp =
+      model->getGhostCells().equal_range(ele);
+    for(std::multimap<MElement*, short>::iterator it = itp.first;
+        it != itp.second; it++)
+      ghosts.push_back(it->second);
+  }
+
+  if(saveAll)
+    ele->writeMSH2(fp, version, binary, ++num, elementary, 0,
+                   parentNum, dom1Num, dom2Num, &ghosts);
+  else{
+    if(parentNum) parentNum = parentNum - physicals.size() + 1;
+    for(unsigned int j = 0; j < physicals.size(); j++){
+      ele->writeMSH2(fp, version, binary, ++num, elementary, physicals[j],
+                     parentNum, dom1Num, dom2Num, &ghosts);
+      if(parentNum) parentNum++;
+    }
+  }
+
+  model->setMeshElementIndex(ele, num); // should really be a multimap...
+
+  if(CTX::instance()->mesh.saveTri && ele->getNumChildren())
+    num += ele->getNumChildren() - 1;
+}
+
+template<class T>
+static void writeElementsMSH(FILE *fp, GModel *model, std::vector<T*> &ele,
+                             bool saveAll, int saveSinglePartition, double version,
+                             bool binary, int &num, int elementary,
+                             std::vector<int> &physicals)
+{
+  // Hack to save each partition as a separate physical entity
+  if(saveSinglePartition < 0){
+    if(ele.empty()) return;
+    int offset = -saveSinglePartition;
+    int maxPhysical = model->getMaxPhysicalNumber(ele[0]->getDim());
+    for(unsigned int i = 0; i < ele.size(); i++){
+      std::vector<int> newPhysicals;
+      int newElementary = elementary;
+      if(elementary > 0){ // classical entity
+        newElementary = elementary * offset + ele[i]->getPartition();
+        for(unsigned int j = 0; j < physicals.size(); j++)
+          newPhysicals.push_back(physicals[j] * offset + ele[i]->getPartition());
+      }
+      else if(elementary < 0){ // partition boundary
+        newPhysicals.push_back((maxPhysical - elementary) * offset);
+      }
+      ele[i]->setPartition(0);
+      writeElementMSH(fp, model, ele[i], saveAll, version, binary, num,
+                      newElementary, newPhysicals);
+    }
+    return;
+  }
+
+  for(unsigned int i = 0; i < ele.size(); i++){
+    if(saveSinglePartition && ele[i]->getPartition() != saveSinglePartition)
+      continue;
+    if(ele[i]->getDomain(0))
+      continue;
+    int parentNum = 0;
+    MElement *parent = ele[i]->getParent();
+    if(parent)
+      parentNum = model->getMeshElementIndex(parent);
+    writeElementMSH(fp, model, ele[i], saveAll, version, binary, num,
+                    elementary, physicals, parentNum);
+  }
+}
+
+static int getNumElementsMSH(GEntity *ge, bool saveAll, int saveSinglePartition)
+{
+  int n = 0, p = saveAll ? 1 : ge->physicals.size();
+
+  if(saveSinglePartition < 0 && ge->tag() < 0) p = 1; // partition boundary
+
+  if(saveSinglePartition <= 0)
+    n = p * ge->getNumMeshElements();
+  else
+    for(unsigned int i = 0; i < ge->getNumMeshElements(); i++)
+      if(ge->getMeshElement(i)->getPartition() == saveSinglePartition)
+        n += p;
+  return n;
+}
+
+static int getNumElementsMSH(GModel *m, bool saveAll, int saveSinglePartition)
+{
+
+  int n = 0;
+  for(GModel::viter it = m->firstVertex(); it != m->lastVertex(); ++it)
+  {
+    n += getNumElementsMSH(*it, saveAll, saveSinglePartition);
+    if(!CTX::instance()->mesh.saveTri){
+      for(unsigned int i = 0; i < (*it)->points.size(); i++)
+        if((*it)->points[i]->ownsParent())
+          n += (saveAll ? 1 : (*it)->physicals.size());
+    }
+  }
+  for(GModel::eiter it = m->firstEdge(); it != m->lastEdge(); ++it)
+  {
+    n += getNumElementsMSH(*it, saveAll, saveSinglePartition);
+    if(!CTX::instance()->mesh.saveTri){
+      for(unsigned int i = 0; i < (*it)->lines.size(); i++)
+        if((*it)->lines[i]->ownsParent())
+          n += (saveAll ? 1 : (*it)->physicals.size());
+    }
+  }
+  for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it)
+  {
+    n += getNumElementsMSH(*it, saveAll, saveSinglePartition);
+    if(CTX::instance()->mesh.saveTri){
+      for(unsigned int i = 0; i < (*it)->polygons.size(); i++)
+      {
+        int nbC = (*it)->polygons[i]->getNumChildren()-1;
+        n += (saveAll ? nbC : nbC * (*it)->physicals.size());
+      }
+    }
+    else
+    {
+      for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
+        if((*it)->triangles[i]->ownsParent())
+          n += (saveAll ? 1 : (*it)->physicals.size());
+      for(unsigned int i = 0; i < (*it)->polygons.size(); i++)
+        if((*it)->polygons[i]->ownsParent())
+          n += (saveAll ? 1 : (*it)->physicals.size());
+    }
+  }
+  for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it)
+  {
+    n += getNumElementsMSH(*it, saveAll, saveSinglePartition);
+    if(CTX::instance()->mesh.saveTri){
+      for(unsigned int i = 0; i < (*it)->polyhedra.size(); i++)
+      {
+        int nbC = (*it)->polyhedra[i]->getNumChildren()-1;
+        n += (saveAll ? nbC : nbC * (*it)->physicals.size());
+      }
+    }
+    else
+    {
+      for(unsigned int i = 0; i < (*it)->tetrahedra.size(); i++)
+        if((*it)->tetrahedra[i]->ownsParent())
+          n += (saveAll ? 1 : (*it)->physicals.size());
+      for(unsigned int i = 0; i < (*it)->polyhedra.size(); i++)
+        if((*it)->polyhedra[i]->ownsParent())
+          n += (saveAll ? 1 : (*it)->physicals.size());
+    }
+  }
+  return n;
+}
+
+int GModel::_writeMSH2(const std::string &name, double version, bool binary,
+                       bool saveAll, bool saveParametric, double scalingFactor,
+                       int elementStartNum, int saveSinglePartition)
+{
+  FILE *fp = fopen(name.c_str(), binary ? "wb" : "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  // binary format exists only in version 2
+  if(version > 1 || binary)
+    version = 2.2;
+  else
+    version = 1.0;
+
+  // if there are no physicals we save all the elements
+  if(noPhysicalGroups()) saveAll = true;
+
+  // get the number of vertices and index the vertices in a continuous
+  // sequence
+  int numVertices = indexMeshVertices(saveAll, saveSinglePartition);
+
+  // get the number of elements we need to save
+  int numElements = getNumElementsMSH(this, saveAll, saveSinglePartition);
+
+  if(version >= 2.0){
+    fprintf(fp, "$MeshFormat\n");
+    fprintf(fp, "%g %d %d\n", version, binary ? 1 : 0, (int)sizeof(double));
+    if(binary){
+      int one = 1;
+      fwrite(&one, sizeof(int), 1, fp);
+      fprintf(fp, "\n");
+    }
+    fprintf(fp, "$EndMeshFormat\n");
+
+    if(numPhysicalNames()){
+      fprintf(fp, "$PhysicalNames\n");
+      fprintf(fp, "%d\n", numPhysicalNames());
+      for(piter it = firstPhysicalName(); it != lastPhysicalName(); it++)
+        fprintf(fp, "%d %d \"%s\"\n", it->first.first, it->first.second,
+                it->second.c_str());
+      fprintf(fp, "$EndPhysicalNames\n");
+    }
+
+    if (saveParametric)
+      fprintf(fp, "$ParametricNodes\n");
+    else
+      fprintf(fp, "$Nodes\n");
+  }
+  else
+    fprintf(fp, "$NOD\n");
+
+  fprintf(fp, "%d\n", numVertices);
+
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+  for(unsigned int i = 0; i < entities.size(); i++)
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
+      entities[i]->mesh_vertices[j]->writeMSH2(fp, binary, saveParametric,
+                                               scalingFactor);
+
+  if(binary) fprintf(fp, "\n");
+
+  if(version >= 2.0){
+    if(saveParametric)
+      fprintf(fp, "$EndParametricNodes\n");
+    else
+      fprintf(fp, "$EndNodes\n");
+    fprintf(fp, "$Elements\n");
+  }
+  else{
+    fprintf(fp, "$ENDNOD\n");
+    fprintf(fp, "$ELM\n");
+  }
+
+  fprintf(fp, "%d\n", numElements);
+  int num = elementStartNum;
+
+  _elementIndexCache.clear();
+
+  //parents
+  if (!CTX::instance()->mesh.saveTri){
+   for(viter it = firstVertex(); it != lastVertex(); ++it)
+     for(unsigned int i = 0; i < (*it)->points.size(); i++)
+       if((*it)->points[i]->ownsParent())
+         writeElementMSH(fp, this, (*it)->points[i]->getParent(),
+                         saveAll, version, binary, num, (*it)->tag(), (*it)->physicals);
+   for(eiter it = firstEdge(); it != lastEdge(); ++it)
+     for(unsigned int i = 0; i < (*it)->lines.size(); i++)
+       if((*it)->lines[i]->ownsParent())
+         writeElementMSH(fp, this, (*it)->lines[i]->getParent(),
+                         saveAll, version, binary, num, (*it)->tag(), (*it)->physicals);
+   for(fiter it = firstFace(); it != lastFace(); ++it)
+     for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
+       if((*it)->triangles[i]->ownsParent())
+         writeElementMSH(fp, this, (*it)->triangles[i]->getParent(),
+                         saveAll, version, binary, num, (*it)->tag(), (*it)->physicals);
+   for(riter it = firstRegion(); it != lastRegion(); ++it)
+     for(unsigned int i = 0; i < (*it)->tetrahedra.size(); i++)
+       if((*it)->tetrahedra[i]->ownsParent())
+         writeElementMSH(fp, this, (*it)->tetrahedra[i]->getParent(),
+                         saveAll, version, binary, num, (*it)->tag(), (*it)->physicals);
+   for(fiter it = firstFace(); it != lastFace(); ++it)
+     for(unsigned int i = 0; i < (*it)->polygons.size(); i++)
+       if((*it)->polygons[i]->ownsParent())
+         writeElementMSH(fp, this, (*it)->polygons[i]->getParent(),
+                         saveAll, version, binary, num, (*it)->tag(), (*it)->physicals);
+   for(riter it = firstRegion(); it != lastRegion(); ++it)
+     for(unsigned int i = 0; i < (*it)->polyhedra.size(); i++)
+       if((*it)->polyhedra[i]->ownsParent())
+         writeElementMSH(fp, this, (*it)->polyhedra[i]->getParent(),
+                         saveAll, version, binary, num, (*it)->tag(), (*it)->physicals);
+  }
+  // points
+  for(viter it = firstVertex(); it != lastVertex(); ++it)
+    writeElementsMSH(fp, this, (*it)->points, saveAll, saveSinglePartition,
+                     version, binary, num, (*it)->tag(), (*it)->physicals);
+  // lines
+  for(eiter it = firstEdge(); it != lastEdge(); ++it)
+    writeElementsMSH(fp, this, (*it)->lines, saveAll, saveSinglePartition,
+                     version, binary, num, (*it)->tag(), (*it)->physicals);
+  // triangles
+  for(fiter it = firstFace(); it != lastFace(); ++it)
+    writeElementsMSH(fp, this, (*it)->triangles, saveAll, saveSinglePartition,
+                     version, binary, num, (*it)->tag(), (*it)->physicals);
+
+  // quads
+  for(fiter it = firstFace(); it != lastFace(); ++it)
+    writeElementsMSH(fp, this, (*it)->quadrangles, saveAll, saveSinglePartition,
+                     version, binary, num, (*it)->tag(), (*it)->physicals);
+  // polygons
+  for(fiter it = firstFace(); it != lastFace(); it++)
+    writeElementsMSH(fp, this, (*it)->polygons, saveAll, saveSinglePartition,
+                     version, binary, num, (*it)->tag(), (*it)->physicals);
+  // tets
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    writeElementsMSH(fp, this, (*it)->tetrahedra, saveAll, saveSinglePartition,
+                     version, binary, num, (*it)->tag(), (*it)->physicals);
+
+  // hexas
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    writeElementsMSH(fp, this, (*it)->hexahedra, saveAll, saveSinglePartition,
+                     version, binary, num, (*it)->tag(), (*it)->physicals);
+
+  // prisms
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    writeElementsMSH(fp, this, (*it)->prisms, saveAll, saveSinglePartition,
+                     version, binary, num, (*it)->tag(), (*it)->physicals);
+
+  // pyramids
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    writeElementsMSH(fp, this, (*it)->pyramids, saveAll, saveSinglePartition,
+                     version, binary, num, (*it)->tag(), (*it)->physicals);
+
+  // polyhedra
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    writeElementsMSH(fp, this, (*it)->polyhedra, saveAll, saveSinglePartition,
+                     version, binary, num, (*it)->tag(), (*it)->physicals);
+
+  // level set faces
+  for(fiter it = firstFace(); it != lastFace(); ++it) {
+    for(unsigned int i = 0; i < (*it)->triangles.size(); i++) {
+      MTriangle *t = (*it)->triangles[i];
+      if(t->getDomain(0))
+        writeElementMSH(fp, this, t, saveAll, version, binary, num,
+                        (*it)->tag(), (*it)->physicals, 0,
+                        getMeshElementIndex(t->getDomain(0)),
+                        getMeshElementIndex(t->getDomain(1)));
+    }
+    for(unsigned int i = 0; i < (*it)->polygons.size(); i++) {
+      MPolygon *p = (*it)->polygons[i];
+      if(p->getDomain(0))
+        writeElementMSH(fp, this, p, saveAll, version, binary, num,
+                        (*it)->tag(), (*it)->physicals, 0,
+                        getMeshElementIndex(p->getDomain(0)),
+                        getMeshElementIndex(p->getDomain(1)));
+    }
+  }
+  //level set lines
+  for(eiter it = firstEdge(); it != lastEdge(); ++it) {
+    for(unsigned int i = 0; i < (*it)->lines.size(); i++) {
+      MLine *l = (*it)->lines[i];
+      if(l->getDomain(0))
+        writeElementMSH(fp, this, l, saveAll, version, binary, num,
+                        (*it)->tag(), (*it)->physicals, 0,
+                        getMeshElementIndex(l->getDomain(0)),
+                        getMeshElementIndex(l->getDomain(1)));
+    }
+  }
+
+  if(binary) fprintf(fp, "\n");
+
+  if(version >= 2.0){
+    fprintf(fp, "$EndElements\n");
+  }
+  else{
+    fprintf(fp, "$ENDELM\n");
+  }
+
+  fclose(fp);
+
+  return 1;
+}
+
+int GModel::writePartitionedMSH(const std::string &baseName, bool binary,
+                                bool saveAll, bool saveParametric,
+                                double scalingFactor)
+{
+  int index = 0;
+  for(std::set<int>::iterator it = meshPartitions.begin();
+      it != meshPartitions.end(); it++){
+    int partition = *it;
+
+    std::ostringstream sstream;
+    sstream << baseName << "_" << std::setw(6) << std::setfill('0') << partition;
+
+    int startNum = index ? getNumElementsMSH(this, saveAll, partition) : 0;
+    Msg::Info("Writing partition %d in file '%s'", partition, sstream.str().c_str());
+    _writeMSH2(sstream.str(), 2.2, binary, saveAll, saveParametric,
+               scalingFactor, startNum, partition);
+    index++;
+  }
+
+#if 0
+  if(_ghostCells.size()){
+    Msg::Info("Writing ghost cells in debug file 'ghosts.pos'");
+    FILE *fp = fopen("ghosts.pos", "w");
+    fprintf(fp, "View \"ghosts\"{\n");
+    for(std::multimap<MElement*, short>::iterator it = _ghostCells.begin();
+        it != _ghostCells.end(); it++)
+      it->first->writePOS(fp, false, true, false, false, false, false);
+    fprintf(fp, "};\n");
+    fclose(fp);
+  }
+#endif
+
+  return 1;
+}
diff --git a/Geo/GModelIO_Mesh.cpp b/Geo/GModelIO_Mesh.cpp
deleted file mode 100644
index 6beaa787df126fbe71a04bfaa03349e85dfaf740..0000000000000000000000000000000000000000
--- a/Geo/GModelIO_Mesh.cpp
+++ /dev/null
@@ -1,2930 +0,0 @@
-// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
-//
-// See the LICENSE.txt file for license information. Please report all
-// bugs and problems to <gmsh@geuz.org>.
-
-#include <limits>
-#include <stdlib.h>
-#include <string.h>
-#include <map>
-#include <string>
-#include "GModel.h"
-#include "GmshDefines.h"
-#include "MPoint.h"
-#include "MLine.h"
-#include "MTriangle.h"
-#include "MQuadrangle.h"
-#include "MTetrahedron.h"
-#include "MHexahedron.h"
-#include "MPrism.h"
-#include "MPyramid.h"
-#include "MElementCut.h"
-#include "SBoundingBox3d.h"
-#include "StringUtils.h"
-#include "GmshMessage.h"
-#include "discreteVertex.h"
-#include "discreteEdge.h"
-#include "discreteFace.h"
-#include "discreteRegion.h"
-#include "MVertexPositionSet.h"
-#include "Context.h"
-#include "OS.h"
-
-#if defined(HAVE_POST)
-#include "PView.h"
-#include "PViewData.h"
-#include "PViewDataList.h"
-#endif
-
-void GModel::_storePhysicalTagsInEntities(int dim,
-                                          std::map<int, std::map<int, std::string> > &map)
-{
-  std::map<int, std::map<int, std::string> >::const_iterator it = map.begin();
-  for(; it != map.end(); ++it){
-    GEntity *ge = 0;
-    switch(dim){
-    case 0: ge = getVertexByTag(it->first); break;
-    case 1: ge = getEdgeByTag(it->first); break;
-    case 2: ge = getFaceByTag(it->first); break;
-    case 3: ge = getRegionByTag(it->first); break;
-    }
-    if(ge){
-      std::map<int, std::string>::const_iterator it2 = it->second.begin();
-      for(; it2 != it->second.end(); ++it2){
-        if(std::find(ge->physicals.begin(), ge->physicals.end(), it2->first) ==
-           ge->physicals.end()){
-          ge->physicals.push_back(it2->first);
-	}
-      }
-    }
-  }
-}
-
-static bool getVertices(int num, int *indices, std::map<int, MVertex*> &map,
-                        std::vector<MVertex*> &vertices)
-{
-  for(int i = 0; i < num; i++){
-    if(!map.count(indices[i])){
-      Msg::Error("Wrong vertex index %d", indices[i]);
-      return false;
-    }
-    else
-      vertices.push_back(map[indices[i]]);
-  }
-  return true;
-}
-
-static bool getVertices(int num, int *indices, std::vector<MVertex*> &vec,
-                        std::vector<MVertex*> &vertices, int minVertex = 0)
-{
-  for(int i = 0; i < num; i++){
-    if(indices[i] < minVertex || indices[i] > (int)(vec.size() - 1 + minVertex)){
-      Msg::Error("Wrong vertex index %d", indices[i]);
-      return false;
-    }
-    else
-      vertices.push_back(vec[indices[i]]);
-  }
-  return true;
-}
-
-static MElement *createElementMSH(GModel *m, int num, int typeMSH, int physical,
-                                  int reg, int part, std::vector<MVertex*> &v,
-                                  std::map<int, std::vector<MElement*> > elements[10],
-                                  std::map<int, std::map<int, std::string> > physicals[4])
-{
-  if(CTX::instance()->mesh.switchElementTags) {
-    int tmp = reg;
-    reg = physical;
-    physical = tmp;
-  }
-
-  MElementFactory factory;
-  MElement *e = factory.create(typeMSH, v, num, part);
-  if(!e){
-    Msg::Error("Unknown type of element %d", typeMSH);
-    return 0;
-  }
-
-  switch(e->getType()){
-  case TYPE_PNT : elements[0][reg].push_back(e); break;
-  case TYPE_LIN : elements[1][reg].push_back(e); break;
-  case TYPE_TRI : elements[2][reg].push_back(e); break;
-  case TYPE_QUA : elements[3][reg].push_back(e); break;
-  case TYPE_TET : elements[4][reg].push_back(e); break;
-  case TYPE_HEX : elements[5][reg].push_back(e); break;
-  case TYPE_PRI : elements[6][reg].push_back(e); break;
-  case TYPE_PYR : elements[7][reg].push_back(e); break;
-  default : Msg::Error("Wrong type of element"); return 0;
-  }
-
-  int dim = e->getDim();
-  if(physical && (!physicals[dim].count(reg) || !physicals[dim][reg].count(physical)))
-    physicals[dim][reg][physical] = "unnamed";
-
-  if(part) m->getMeshPartitions().insert(part);
-  return e;
-}
-
-int GModel::writePOS(const std::string &name, bool printElementary,
-                     bool printElementNumber, bool printGamma, bool printEta,
-                     bool printRho, bool printDisto,
-                     bool saveAll, double scalingFactor)
-{
-  FILE *fp = fopen(name.c_str(), "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  /*
-  bool printVertices = true;
-  if(printVertices){
-    fprintf(fp, "View \"Vertices\" {\n");
-    std::vector<GEntity*> entities;
-    getEntities(entities);
-    for(unsigned int i = 0; i < entities.size(); i++)
-      for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++){
-        MVertex *v = entities[i]->mesh_vertices[j];
-        fprintf(fp, "SP(%g,%g,%g){1};\n", v->x(), v->y(), v->z());
-      }
-    fprintf(fp, "};\n");
-    fclose(fp);
-    return 1;
-  }
-  */
-
-  bool f[6] = {printElementary, printElementNumber, printGamma, printEta, printRho,
-               printDisto};
-
-  bool first = true;
-  std::string names;
-  if(f[0]){
-    if(first) first = false; else names += ",";
-    names += "\"Elementary Entity\"";
-  }
-  if(f[1]){
-    if(first) first = false; else names += ",";
-    names += "\"Element Number\"";
-  }
-  if(f[2]){
-    if(first) first = false; else names += ",";
-    names += "\"Gamma\"";
-  }
-  if(f[3]){
-    if(first) first = false; else names += ",";
-    names += "\"Eta\"";
-  }
-  if(f[4]){
-    if(first) first = false; else names += ",";
-    names += "\"Rho\"";
-  }
-  if(f[5]){
-    if(first) first = false; else names += ",";
-    names += "\"Disto\"";
-  }
-
-  if(names.empty()) return 0;
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  fprintf(fp, "View \"Statistics\" {\n");
-  fprintf(fp, "T2(1.e5,30,%d){%s};\n", (1<<16)|(4<<8), names.c_str());
-
-  std::vector<GEntity*> entities;
-  getEntities(entities);
-  for(unsigned int i = 0; i < entities.size(); i++)
-    if(saveAll || entities[i]->physicals.size())
-      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++)
-        entities[i]->getMeshElement(j)->writePOS
-          (fp, f[0], f[1], f[2], f[3], f[4], f[5], scalingFactor, entities[i]->tag());
-  fprintf(fp, "};\n");
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::readSTL(const std::string &name, double tolerance)
-{
-  FILE *fp = fopen(name.c_str(), "rb");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  // store triplets of points for each solid found in the file
-  std::vector<std::vector<SPoint3> > points;
-  SBoundingBox3d bbox;
-
-  // "solid", or binary data header
-  char buffer[256];
-  if(!fgets(buffer, sizeof(buffer), fp)) return 0;
-
-  bool binary = strncmp(buffer, "solid", 5);
-
-  // ASCII STL
-  if(!binary){
-    points.resize(1);
-    while(!feof(fp)) {
-      // "facet normal x y z" or "endsolid"
-      if(!fgets(buffer, sizeof(buffer), fp)) break;
-      if(!strncmp(buffer, "endsolid", 8)){
-        // "solid"
-        if(!fgets(buffer, sizeof(buffer), fp)) break;
-        if(!strncmp(buffer, "solid", 5)){
-          points.resize(points.size() + 1);
-          // "facet normal x y z"
-          if(!fgets(buffer, sizeof(buffer), fp)) break;
-        }
-      }
-      // "outer loop"
-      if(!fgets(buffer, sizeof(buffer), fp)) break;
-      // "vertex x y z"
-      for(int i = 0; i < 3; i++){
-        if(!fgets(buffer, sizeof(buffer), fp)) break;
-        char s1[256];
-        double x, y, z;
-        if(sscanf(buffer, "%s %lf %lf %lf", s1, &x, &y, &z) != 4) break;
-        SPoint3 p(x, y, z);
-        points.back().push_back(p);
-        bbox += p;
-      }
-      // "endloop"
-      if(!fgets(buffer, sizeof(buffer), fp)) break;
-      // "endfacet"
-      if(!fgets(buffer, sizeof(buffer), fp)) break;
-    }
-   }
-
-  // check if we could parse something
-  bool empty = true;
-  for(unsigned int i = 0; i < points.size(); i++){
-    if(points[i].size()){
-      empty = false;
-      break;
-    }
-  }
-  if(empty) points.clear();
-
-  // binary STL (we also try to read in binary mode if the header told
-  // us the format was ASCII but we could not read any vertices)
-  if(binary || empty){
-    if(binary)
-      Msg::Info("Mesh is in binary format");
-    else
-      Msg::Info("Wrong ASCII header or empty file: trying binary read");
-    rewind(fp);
-    while(!feof(fp)) {
-      char header[80];
-      if(!fread(header, sizeof(char), 80, fp)) break;
-      unsigned int nfacets = 0;
-      size_t ret = fread(&nfacets, sizeof(unsigned int), 1, fp);
-      bool swap = false;
-      if(nfacets > 100000000){
-        Msg::Info("Swapping bytes from binary file");
-        swap = true;
-        SwapBytes((char*)&nfacets, sizeof(unsigned int), 1);
-      }
-      if(ret && nfacets){
-        points.resize(points.size() + 1);
-        char *data = new char[nfacets * 50 * sizeof(char)];
-        ret = fread(data, sizeof(char), nfacets * 50, fp);
-        if(ret == nfacets * 50){
-          for(unsigned int i = 0; i < nfacets; i++) {
-            float *xyz = (float *)&data[i * 50 * sizeof(char)];
-            if(swap) SwapBytes((char*)xyz, sizeof(float), 12);
-            for(int j = 0; j < 3; j++){
-              SPoint3 p(xyz[3 + 3 * j], xyz[3 + 3 * j + 1], xyz[3 + 3 * j + 2]);
-              points.back().push_back(p);
-              bbox += p;
-            }
-          }
-        }
-        delete [] data;
-      }
-    }
-  }
-
-  std::vector<GFace*> faces;
-  for(unsigned int i = 0; i < points.size(); i++){
-    if(points[i].empty()){
-      Msg::Error("No facets found in STL file for solid %d", i);
-      return 0;
-    }
-    if(points[i].size() % 3){
-      Msg::Error("Wrong number of points (%d) in STL file for solid %d",
-                 points[i].size(), i);
-      return 0;
-    }
-    Msg::Info("%d facets in solid %d", points[i].size() / 3, i);
-    // create face
-    GFace *face = new discreteFace(this, getMaxElementaryNumber(2) + 1);
-    faces.push_back(face);
-    add(face);
-  }
-
-  // create triangles using unique vertices
-  double eps = norm(SVector3(bbox.max(), bbox.min())) * tolerance;
-  std::vector<MVertex*> vertices;
-  for(unsigned int i = 0; i < points.size(); i++)
-    for(unsigned int j = 0; j < points[i].size(); j++)
-      vertices.push_back(new MVertex(points[i][j].x(), points[i][j].y(),
-                                     points[i][j].z()));
-  MVertexPositionSet pos(vertices);
-
-  std::set<MFace,Less_Face> unique;
-  int nbDuplic = 0;
-  for(unsigned int i = 0; i < points.size(); i ++){
-    for(unsigned int j = 0; j < points[i].size(); j += 3){
-      MVertex *v[3];
-      for(int k = 0; k < 3; k++){
-        double x = points[i][j + k].x();
-        double y = points[i][j + k].y();
-        double z = points[i][j + k].z();
-        v[k] = pos.find(x, y, z, eps);
-      }
-      MFace mf (v[0], v[1], v[2]);
-      if (unique.find(mf) == unique.end()){
-	faces[i]->triangles.push_back(new MTriangle(v[0], v[1], v[2]));
-	unique.insert(mf);
-      }
-      else{
-	nbDuplic++;
-      }
-    }
-  }
-  if (nbDuplic)
-    Msg::Warning("%d duplicate triangles in STL file", nbDuplic);
-
-  _associateEntityWithMeshVertices();
-  _storeVerticesInEntities(vertices); // will delete unused vertices
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writeSTL(const std::string &name, bool binary, bool saveAll,
-                     double scalingFactor)
-{
-  FILE *fp = fopen(name.c_str(), binary ? "wb" : "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  if(!binary){
-    fprintf(fp, "solid Created by Gmsh\n");
-  }
-  else{
-    char header[80];
-    strncpy(header, "Created by Gmsh", 80);
-    fwrite(header, sizeof(char), 80, fp);
-    unsigned int nfacets = 0;
-    for(fiter it = firstFace(); it != lastFace(); ++it){
-      if(saveAll || (*it)->physicals.size()){
-        nfacets += (*it)->triangles.size() + 2 * (*it)->quadrangles.size();
-      }
-    }
-    fwrite(&nfacets, sizeof(unsigned int), 1, fp);
-  }
-
-  for(fiter it = firstFace(); it != lastFace(); ++it) {
-    if(saveAll || (*it)->physicals.size()){
-      for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
-        (*it)->triangles[i]->writeSTL(fp, binary, scalingFactor);
-      for(unsigned int i = 0; i < (*it)->quadrangles.size(); i++)
-        (*it)->quadrangles[i]->writeSTL(fp, binary, scalingFactor);
-    }
-  }
-
-  if(!binary)
-    fprintf(fp, "endsolid Created by Gmsh\n");
-
-  fclose(fp);
-  return 1;
-}
-
-static void replaceCommaByDot(const std::string name)
-{
-  char myCommand[1000], myCommand2[1000];
-  sprintf(myCommand, "sed 's/,/./g' %s > temp.txt", name.c_str());
-  SystemCall(myCommand, true);
-  sprintf(myCommand2, "mv temp.txt %s ", name.c_str());
-  SystemCall(myCommand2, true);
-}
-
-static bool getProperties(int num, int *indices, std::vector<double> &vec,
-                        std::vector<double> &properties)
-{
-  for(int i = 0; i < num; i++){
-    if(indices[i] < 0 || indices[i] > (int)(vec.size() - 1)){
-      Msg::Error("Wrong vertex index %d", indices[i]);
-      return false;
-    }
-    else
-      properties.push_back(vec[indices[i]]);
-  }
-  return true;
-}
-
-int GModel::readPLY(const std::string &name)
-{
-  replaceCommaByDot(name);
-
-  FILE *fp = fopen(name.c_str(), "r");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  std::vector<MVertex*> vertexVector;
-  std::map<int, std::vector<MElement*> > elements[5];
-  std::map<int, std::vector<double> > properties;
-
-  char buffer[256], str[256], str2[256], str3[256];
-  std::string s1;
-  int elementary = getMaxElementaryNumber(-1) + 1;
-  int nbv, nbf;
-  int nbprop = 0;
-  int nbView = 0;
-  std::vector<std::string> propName;
-  while(!feof(fp)) {
-    if(!fgets(buffer, sizeof(buffer), fp)) break;
-    if(buffer[0] != '#'){ // skip comments
-      sscanf(buffer, "%s %s", str, str2);
-      if(!strcmp(str, "element") && !strcmp(str2, "vertex")){
-	sscanf(buffer, "%s %s %d", str, str2, &nbv);
-      }
-      if(!strcmp(str, "format") && strcmp(str2, "ascii")){
-	Msg::Error("Only reading of ascii PLY files implemented");
-	return 0;
-      }
-      if(!strcmp(str, "property") && strcmp(str2, "list")){
-	nbprop++;
-	sscanf(buffer, "%s %s %s", str, str2, str3);
-	if (nbprop > 3) propName.push_back(s1+str3);
-      }
-      if(!strcmp(str, "element") && !strcmp(str2, "face")){
-	sscanf(buffer, "%s %s %d", str, str2, &nbf);
-      }
-      if(!strcmp(str, "end_header")){
-	nbView = nbprop -3;
-	Msg::Info("%d elements", nbv);
-	Msg::Info("%d triangles", nbf);
-	Msg::Info("%d properties", nbView);
-
-	vertexVector.resize(nbv);
-	for(int i = 0; i < nbv; i++) {
-	  double x,y,z;
-	  char line[10000], *pEnd, *pEnd2, *pEnd3;
-	  if(!fgets(line, sizeof(line), fp)) return 0;
-	  x = strtod(line, &pEnd);
-	  y = strtod(pEnd, &pEnd2);
-	  z = strtod(pEnd2, &pEnd3);
-	  vertexVector[i] = new MVertex(x, y, z);
-
-	  pEnd = pEnd3;
-          std::vector<double> prop(nbView);
-	  for (int k = 0; k < nbView; k++){
-	    prop[k]=strtod(pEnd, &pEnd2);
-	    pEnd = pEnd2;
-	    properties[k].push_back(prop[k]);
-	  }
-	}
-
-	for(int i = 0; i < nbf; i++) {
-	  if(!fgets(buffer, sizeof(buffer), fp)) break;
-	  int n[3], nbe;
-	  sscanf(buffer, "%d %d %d %d", &nbe, &n[0], &n[1], &n[2]);
-	  std::vector<MVertex*> vertices;
-	  if(!getVertices(3, n, vertexVector, vertices)) return 0;
-	  elements[0][elementary].push_back(new MTriangle(vertices));
-	}
-
-      }
-
-    }
-  }
-
-  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
-    _storeElementsInEntities(elements[i]);
-  _associateEntityWithMeshVertices();
-  _storeVerticesInEntities(vertexVector);
-
-#if defined(HAVE_POST)
-  // create PViews here
-  std::vector<GEntity*> _entities;
-  getEntities(_entities);
-  for (int iV=0; iV< nbView; iV++){
-    PView *view = new PView();
-    PViewDataList *data = dynamic_cast<PViewDataList*>(view->getData());
-    for(unsigned int ii = 0; ii < _entities.size(); ii++){
-	for(unsigned int i = 0; i < _entities[ii]->getNumMeshElements(); i++){
-	  MElement *e = _entities[ii]->getMeshElement(i);
-	  int numNodes = e->getNumVertices();
-	  std::vector<double> x(numNodes), y(numNodes), z(numNodes);
-	  std::vector<double> *out = data->incrementList(1, e->getType());
-	  for(int nod = 0; nod < numNodes; nod++) out->push_back((e->getVertex(nod))->x());
-	  for(int nod = 0; nod < numNodes; nod++) out->push_back((e->getVertex(nod))->y());
-	  for(int nod = 0; nod < numNodes; nod++) out->push_back((e->getVertex(nod))->z());
-	  std::vector<double> props;
-	  int n[3];
-	  n[0] = e->getVertex(0)->getNum()-1;
-	  n[1] = e->getVertex(1)->getNum()-1;
-	  n[2] = e->getVertex(2)->getNum()-1;
-	  if(!getProperties(3, n, properties[iV], props)) return 0;
-	  for(int nod = 0; nod < numNodes; nod++) out->push_back(props[nod]);
-	}
-    }
-    data->setName(propName[iV]);
-    data->Time.push_back(0);
-    data->setFileName("property.pos");
-    data->finalize();
-  }
-#endif
-
-  fclose(fp);
-
-  return 1;
-}
-
-int GModel::readPLY2(const std::string &name)
-{
-  FILE *fp = fopen(name.c_str(), "r");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  std::vector<MVertex*> vertexVector;
-  std::map<int, std::vector<MElement*> > elements[5];
-
-  char buffer[256];
-  int elementary = getMaxElementaryNumber(-1) + 1;
-  int nbv, nbf;
-  while(!feof(fp)) {
-    if(!fgets(buffer, sizeof(buffer), fp)) break;
-    if(buffer[0] != '#'){ // skip comments
-      sscanf(buffer, "%d", &nbv);
-      if(!fgets(buffer, sizeof(buffer), fp)) break;
-      sscanf(buffer, "%d", &nbf);
-      Msg::Info("%d vertices", nbv);
-      Msg::Info("%d triangles", nbf);
-      vertexVector.resize(nbv);
-      for(int i = 0; i < nbv; i++) {
-	if(!fgets(buffer, sizeof(buffer), fp)) break;
-	double x, y, z;
-	int nb = sscanf(buffer, "%lf %lf %lf", &x, &y, &z);
-	if (nb !=3){
-	  if(!fgets(buffer, sizeof(buffer), fp)) break;
-	  sscanf(buffer, "%lf",  &y);
-	  if(!fgets(buffer, sizeof(buffer), fp)) break;
-	  sscanf(buffer, "%lf",  &z);
-	}
-	vertexVector[i] = new MVertex(x, y, z);
-      }
-      for(int i = 0; i < nbf; i++) {
-	if(!fgets(buffer, sizeof(buffer), fp)) break;
-	int n[3], nbe;
-	int nb = sscanf(buffer, "%d %d %d %d", &nbe, &n[0], &n[1], &n[2]);
-	if (nb !=4){
-	  if(!fgets(buffer, sizeof(buffer), fp)) break;
-	  sscanf(buffer, "%d",  &n[0]);
-	  if(!fgets(buffer, sizeof(buffer), fp)) break;
-	  sscanf(buffer, "%d",  &n[1]);
-	  if(!fgets(buffer, sizeof(buffer), fp)) break;
-	  sscanf(buffer, "%d",  &n[2]);
-	}
-	std::vector<MVertex*> vertices;
-	if(!getVertices(3, n, vertexVector, vertices)) return 0;
-	elements[0][elementary].push_back(new MTriangle(vertices));
-      }
-    }
-  }
-
-  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
-    _storeElementsInEntities(elements[i]);
-  _associateEntityWithMeshVertices();
-  _storeVerticesInEntities(vertexVector);
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writePLY2(const std::string &name)
-{
-  FILE *fp = fopen(name.c_str(), "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  int numVertices = indexMeshVertices(true);
-  int numTriangles = 0;
-  for(fiter it = firstFace(); it != lastFace(); ++it){
-    numTriangles += (*it)->triangles.size();
-  }
-
-  fprintf(fp, "%d\n", numVertices);
-  fprintf(fp, "%d\n", numTriangles);
-
-  std::vector<GEntity*> entities;
-  getEntities(entities);
-  for(unsigned int i = 0; i < entities.size(); i++)
-    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
-      entities[i]->mesh_vertices[j]->writePLY2(fp);
-
-  for(fiter it = firstFace(); it != lastFace(); ++it){
-      for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
-        (*it)->triangles[i]->writePLY2(fp);
-  }
-
-  fclose(fp);
-  return 1;
-}
-
-static int skipUntil(FILE *fp, const char *key)
-{
-  char str[256], key_bracket[256];
-  strcpy(key_bracket, key);
-  strcat(key_bracket, "[");
-  while(fscanf(fp, "%s", str)){
-    if(!strcmp(str, key)){
-      while(!feof(fp) && fgetc(fp) != '['){}
-      return 1;
-    }
-    if(!strcmp(str, key_bracket)){
-      return 1;
-    }
-  }
-  return 0;
-}
-
-static int readVerticesVRML(FILE *fp, std::vector<MVertex*> &vertexVector,
-                            std::vector<MVertex*> &allVertexVector)
-{
-  double x, y, z;
-  if(fscanf(fp, "%lf %lf %lf", &x, &y, &z) != 3) return 0;
-  vertexVector.push_back(new MVertex(x, y, z));
-  while(fscanf(fp, " , %lf %lf %lf", &x, &y, &z) == 3)
-    vertexVector.push_back(new MVertex(x, y, z));
-  for(unsigned int i = 0; i < vertexVector.size(); i++)
-    allVertexVector.push_back(vertexVector[i]);
-  Msg::Info("%d vertices", vertexVector.size());
-  return 1;
-}
-
-static int readElementsVRML(FILE *fp, std::vector<MVertex*> &vertexVector, int region,
-                            std::map<int, std::vector<MElement*> > elements[3],
-                            bool strips=false)
-{
-  int i;
-  std::vector<int> idx;
-  if(fscanf(fp, "%d", &i) != 1) return 0;
-  idx.push_back(i);
-
-  // check if vertex indices are separated by commas
-  char tmp[256];
-  const char *format;
-  fpos_t position;
-  fgetpos(fp, &position);
-  if(!fgets(tmp, sizeof(tmp), fp)) return 0;
-  fsetpos(fp, &position);
-  if(strstr(tmp, ","))
-    format = " , %d";
-  else
-    format = " %d";
-
-  while(fscanf(fp, format, &i) == 1){
-    if(i != -1){
-      idx.push_back(i);
-    }
-    else{
-      std::vector<MVertex*> vertices;
-      if(!getVertices(idx.size(), &idx[0], vertexVector, vertices)) return 0;
-      idx.clear();
-      if(vertices.size() < 2){
-        Msg::Info("Skipping %d-vertex element", (int)vertices.size());
-      }
-      else if(vertices.size() == 2){
-        elements[0][region].push_back(new MLine(vertices));
-      }
-      else if(vertices.size() == 3){
-        elements[1][region].push_back(new MTriangle(vertices));
-      }
-      else if(!strips && vertices.size() == 4){
-        elements[2][region].push_back(new MQuadrangle(vertices));
-      }
-      else if(strips){ // triangle strip
-        for(unsigned int j = 2; j < vertices.size(); j++){
-          if(j % 2)
-            elements[1][region].push_back
-              (new MTriangle(vertices[j], vertices[j - 1], vertices[j - 2]));
-          else
-            elements[1][region].push_back
-              (new MTriangle(vertices[j - 2], vertices[j - 1], vertices[j]));
-        }
-      }
-      else{ // import polygon as triangle fan
-        for(unsigned int j = 2; j < vertices.size(); j++){
-          elements[1][region].push_back
-            (new MTriangle(vertices[0], vertices[j-1], vertices[j]));
-        }
-      }
-    }
-  }
-  if(idx.size()){
-    Msg::Error("Prematured end of VRML file");
-    return 0;
-  }
-  Msg::Info("%d elements", elements[0][region].size() +
-            elements[1][region].size() + elements[2][region].size());
-  return 1;
-}
-
-int GModel::readVRML(const std::string &name)
-{
-  FILE *fp = fopen(name.c_str(), "r");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  // This is by NO means a complete VRML/Inventor parser (but it's
-  // sufficient for reading simple Inventor files... which is all I
-  // need)
-  std::vector<MVertex*> vertexVector, allVertexVector;
-  std::map<int, std::vector<MElement*> > elements[3];
-  int region = getMaxElementaryNumber(-1);
-  char buffer[256], str[256];
-  while(!feof(fp)) {
-    if(!fgets(buffer, sizeof(buffer), fp)) break;
-    if(buffer[0] != '#'){ // skip comments
-      sscanf(buffer, "%s", str);
-      if(!strcmp(str, "Coordinate3")){
-        vertexVector.clear();
-        if(!skipUntil(fp, "point")) break;
-        if(!readVerticesVRML(fp, vertexVector, allVertexVector)) break;
-      }
-      else if(!strcmp(str, "coord")){
-        region++;
-        vertexVector.clear();
-        if(!skipUntil(fp, "point")) break;
-        if(!readVerticesVRML(fp, vertexVector, allVertexVector)) break;
-        if(!skipUntil(fp, "coordIndex")) break;
-        if(!readElementsVRML(fp, vertexVector, region, elements, true)) break;
-      }
-      else if(!strcmp(str, "IndexedTriangleStripSet")){
-        region++;
-        vertexVector.clear();
-        if(!skipUntil(fp, "vertex")) break;
-        if(!readVerticesVRML(fp, vertexVector, allVertexVector)) break;
-        if(!skipUntil(fp, "coordIndex")) break;
-        if(!readElementsVRML(fp, vertexVector, region, elements, true)) break;
-      }
-      else if(!strcmp(str, "IndexedFaceSet") || !strcmp(str, "IndexedLineSet")){
-        region++;
-        if(!skipUntil(fp, "coordIndex")) break;
-        if(!readElementsVRML(fp, vertexVector, region, elements)) break;
-      }
-      else if(!strcmp(str, "DEF")){
-        char str1[256], str2[256];
-        if(!sscanf(buffer, "%s %s %s", str1, str2, str)) break;
-        if(!strcmp(str, "Coordinate")){
-          vertexVector.clear();
-          if(!skipUntil(fp, "point")) break;
-          if(!readVerticesVRML(fp, vertexVector, allVertexVector)) break;
-        }
-        else if(!strcmp(str, "IndexedFaceSet") || !strcmp(str, "IndexedLineSet")){
-          region++;
-          if(!skipUntil(fp, "coordIndex")) break;
-          if(!readElementsVRML(fp, vertexVector, region, elements)) break;
-        }
-      }
-    }
-  }
-
-  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
-    _storeElementsInEntities(elements[i]);
-  _associateEntityWithMeshVertices();
-  _storeVerticesInEntities(allVertexVector);
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writeVRML(const std::string &name, bool saveAll, double scalingFactor)
-{
-  FILE *fp = fopen(name.c_str(), "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  indexMeshVertices(saveAll);
-
-  fprintf(fp, "#VRML V1.0 ascii\n");
-  fprintf(fp, "#created by Gmsh\n");
-  fprintf(fp, "Coordinate3 {\n");
-  fprintf(fp, "  point [\n");
-
-  for(viter it = firstVertex(); it != lastVertex(); ++it)
-    for(unsigned int i = 0; i < (*it)->mesh_vertices.size(); i++)
-      (*it)->mesh_vertices[i]->writeVRML(fp, scalingFactor);
-  for(eiter it = firstEdge(); it != lastEdge(); ++it)
-    for(unsigned int i = 0; i < (*it)->mesh_vertices.size(); i++)
-      (*it)->mesh_vertices[i]->writeVRML(fp, scalingFactor);
-  for(fiter it = firstFace(); it != lastFace(); ++it)
-    for(unsigned int i = 0; i < (*it)->mesh_vertices.size(); i++)
-      (*it)->mesh_vertices[i]->writeVRML(fp, scalingFactor);
-
-  fprintf(fp, "  ]\n");
-  fprintf(fp, "}\n");
-
-  for(eiter it = firstEdge(); it != lastEdge(); ++it){
-    if(saveAll || (*it)->physicals.size()){
-      fprintf(fp, "DEF Curve%d IndexedLineSet {\n", (*it)->tag());
-      fprintf(fp, "  coordIndex [\n");
-      for(unsigned int i = 0; i < (*it)->lines.size(); i++)
-        (*it)->lines[i]->writeVRML(fp);
-      fprintf(fp, "  ]\n");
-      fprintf(fp, "}\n");
-    }
-  }
-
-  for(fiter it = firstFace(); it != lastFace(); ++it){
-    if(saveAll || (*it)->physicals.size()){
-      fprintf(fp, "DEF Surface%d IndexedFaceSet {\n", (*it)->tag());
-      fprintf(fp, "  coordIndex [\n");
-      for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
-        (*it)->triangles[i]->writeVRML(fp);
-      for(unsigned int i = 0; i < (*it)->quadrangles.size(); i++)
-        (*it)->quadrangles[i]->writeVRML(fp);
-      fprintf(fp, "  ]\n");
-      fprintf(fp, "}\n");
-    }
-  }
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::readUNV(const std::string &name)
-{
-  FILE *fp = fopen(name.c_str(), "r");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  char buffer[256];
-  std::map<int, MVertex*> vertexMap;
-  std::map<int, std::vector<MElement*> > elements[7];
-  std::map<int, std::map<int, std::string> > physicals[4];
-
-  while(!feof(fp)) {
-    if(!fgets(buffer, sizeof(buffer), fp)) break;
-    if(!strncmp(buffer, "    -1", 6)){
-      if(!fgets(buffer, sizeof(buffer), fp)) break;
-      if(!strncmp(buffer, "    -1", 6))
-        if(!fgets(buffer, sizeof(buffer), fp)) break;
-      int record = 0;
-      sscanf(buffer, "%d", &record);
-      if(record == 2411){ // nodes
-        while(fgets(buffer, sizeof(buffer), fp)){
-          if(!strncmp(buffer, "    -1", 6)) break;
-          int num, dum;
-          if(sscanf(buffer, "%d %d %d %d", &num, &dum, &dum, &dum) != 4) break;
-          if(!fgets(buffer, sizeof(buffer), fp)) break;
-          double x, y, z;
-          for(unsigned int i = 0; i < strlen(buffer); i++)
-            if(buffer[i] == 'D') buffer[i] = 'E';
-          if(sscanf(buffer, "%lf %lf %lf", &x, &y, &z) != 3) break;
-          vertexMap[num] = new MVertex(x, y, z, 0, num);
-        }
-      }
-      else if(record == 2412){ // elements
-        while(fgets(buffer, sizeof(buffer), fp)){
-          if(strlen(buffer) < 3) continue; // possible line ending after last fscanf
-          if(!strncmp(buffer, "    -1", 6)) break;
-          int num, type, elementary, physical, color, numNodes;
-	  if(!CTX::instance()->mesh.switchElementTags) {
-            if(sscanf(buffer, "%d %d %d %d %d %d", &num, &type, &elementary, &physical,
-                      &color, &numNodes) != 6) break;
-	  }
-          else {
-            if(sscanf(buffer, "%d %d %d %d %d %d", &num, &type, &physical, &elementary,
-                      &color, &numNodes) != 6) break;
-	  }
-          if(elementary < 0) elementary = getMaxElementaryNumber(-1) + 1;
-          if(physical < 0) physical = 0;
-          switch(type){
-          case 11: case 21: case 22: case 31:
-          case 23: case 24: case 32:
-            // beam elements
-            if(!fgets(buffer, sizeof(buffer), fp)) break;
-            int dum;
-            if(sscanf(buffer, "%d %d %d", &dum, &dum, &dum) != 3) break;
-            break;
-          }
-          int n[30];
-          for(int i = 0; i < numNodes; i++) if(!fscanf(fp, "%d", &n[i])) return 0;
-          std::vector<MVertex*> vertices;
-          if(!getVertices(numNodes, n, vertexMap, vertices)) return 0;
-
-          int dim = -1;
-          switch(type){
-          case 11: case 21: case 22: case 31:
-            elements[0][elementary].push_back(new MLine(vertices, num));
-            dim = 1;
-            break;
-          case 23: case 24: case 32:
-            elements[0][elementary].push_back
-              (new MLine3(vertices[0], vertices[2], vertices[1], num));
-            dim = 1;
-            break;
-          case 41: case 51: case 61: case 74: case 81: case 91:
-            elements[1][elementary].push_back(new MTriangle(vertices, num));
-            dim = 2;
-            break;
-          case 42: case 52: case 62: case 72: case 82: case 92:
-            elements[1][elementary].push_back
-              (new MTriangle6(vertices[0], vertices[2], vertices[4], vertices[1],
-                              vertices[3], vertices[5], num));
-            dim = 2;
-            break;
-          case 44: case 54: case 64: case 71: case 84: case 94:
-            elements[2][elementary].push_back(new MQuadrangle(vertices, num));
-            dim = 2;
-            break;
-          case 45: case 55: case 65: case 75: case 85: case 95:
-            elements[2][elementary].push_back
-              (new MQuadrangle8(vertices[0], vertices[2], vertices[4], vertices[6],
-                                vertices[1], vertices[3], vertices[5], vertices[7],
-                                num));
-            dim = 2;
-            break;
-          case 111:
-            elements[3][elementary].push_back(new MTetrahedron(vertices, num));
-            dim = 3;
-            break;
-          case 118:
-            elements[3][elementary].push_back
-              (new MTetrahedron10(vertices[0], vertices[2], vertices[4], vertices[9],
-                                  vertices[1], vertices[3], vertices[5], vertices[6],
-                                  vertices[8], vertices[7], num));
-            dim = 3;
-            break;
-          case 104: case 115:
-            elements[4][elementary].push_back(new MHexahedron(vertices, num));
-            dim = 3;
-            break;
-          case 105: case 116:
-            elements[4][elementary].push_back
-              (new MHexahedron20(vertices[0], vertices[2], vertices[4], vertices[6],
-                                 vertices[12], vertices[14], vertices[16], vertices[18],
-                                 vertices[1], vertices[7], vertices[8], vertices[3],
-                                 vertices[9], vertices[5], vertices[10], vertices[11],
-                                 vertices[13], vertices[19], vertices[15], vertices[17],
-                                 num));
-            dim = 3;
-            break;
-          case 101: case 112:
-            elements[5][elementary].push_back(new MPrism(vertices, num));
-            dim = 3;
-            break;
-          case 102: case 113:
-            elements[5][elementary].push_back
-              (new MPrism15(vertices[0], vertices[2], vertices[4], vertices[9],
-                            vertices[11], vertices[13], vertices[1], vertices[5],
-                            vertices[6], vertices[3], vertices[7], vertices[8],
-                            vertices[10], vertices[14], vertices[12], num));
-            dim = 3;
-            break;
-          }
-
-          if(dim >= 0 && physical && (!physicals[dim].count(elementary) ||
-                                      !physicals[dim][elementary].count(physical)))
-            physicals[dim][elementary][physical] = "unnamed";
-        }
-      }
-    }
-  }
-
-  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
-    _storeElementsInEntities(elements[i]);
-  _associateEntityWithMeshVertices();
-  _storeVerticesInEntities(vertexMap);
-
-  for(int i = 0; i < 4; i++)
-    _storePhysicalTagsInEntities(i, physicals[i]);
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writeUNV(const std::string &name, bool saveAll, bool saveGroupsOfNodes,
-                     double scalingFactor)
-{
-  FILE *fp = fopen(name.c_str(), "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  indexMeshVertices(saveAll);
-
-  std::vector<GEntity*> entities;
-  getEntities(entities);
-
-  // nodes
-  fprintf(fp, "%6d\n", -1);
-  fprintf(fp, "%6d\n", 2411);
-  for(unsigned int i = 0; i < entities.size(); i++)
-    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
-      entities[i]->mesh_vertices[j]->writeUNV(fp, scalingFactor);
-  fprintf(fp, "%6d\n", -1);
-
-  // elements
-  fprintf(fp, "%6d\n", -1);
-  fprintf(fp, "%6d\n", 2412);
-  int num = 0;
-  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);
-      if(saveAll)
-        e->writeUNV(fp, ++num, entities[i]->tag(), 0);
-      else
-        for(unsigned int k = 0; k < entities[i]->physicals.size(); k++)
-          e->writeUNV(fp, ++num, entities[i]->tag(), entities[i]->physicals[k]);
-    }
-  }
-  fprintf(fp, "%6d\n", -1);
-
-  // groups of nodes for physical groups
-  if(saveGroupsOfNodes){
-    fprintf(fp, "%6d\n", -1);
-    fprintf(fp, "%6d\n", 2477);
-    std::map<int, std::vector<GEntity*> > groups[4];
-    getPhysicalGroups(groups);
-    int gr = 1;
-    for(int dim = 1; dim <= 3; dim++){
-      for(std::map<int, std::vector<GEntity*> >::iterator it = groups[dim].begin();
-          it != groups[dim].end(); it++){
-        std::set<MVertex*> nodes;
-        std::vector<GEntity *> &entities = it->second;
-        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);
-            for (int k = 0; k < e->getNumVertices(); k++)
-              nodes.insert(e->getVertex(k));
-          }
-        }
-        fprintf(fp, "%10d%10d%10d%10d%10d%10d%10d%10d\n",
-                gr, 0, 0, 0, 0, 0, 0, (int)nodes.size());
-        fprintf(fp, "PERMANENT GROUP%d\n", gr++);
-        int row = 0;
-        for(std::set<MVertex*>::iterator it2 = nodes.begin(); it2 != nodes.end(); it2++){
-          if(row == 2) {
-            fprintf(fp, "\n");
-            row = 0;
-          }
-          fprintf(fp, "%10d%10d%10d%10d", 7, (*it2)->getIndex(), 0, 0);
-          row++;
-        }
-        fprintf(fp, "\n");
-      }
-    }
-    fprintf(fp, "%6d\n", -1);
-  }
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::readMESH(const std::string &name)
-{
-  FILE *fp = fopen(name.c_str(), "r");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  char buffer[256];
-  if(!fgets(buffer, sizeof(buffer), fp)) return 0;
-
-  char str[256];
-  int format;
-  sscanf(buffer, "%s %d", str, &format);
-  if(format == 3){
-    Msg::Error("Medit mesh import only available for ASCII files");
-    return 0;
-  }
-
-  std::vector<MVertex*> vertexVector;
-  std::map<int, std::vector<MElement*> > elements[5];
-
-  while(!feof(fp)) {
-    if(!fgets(buffer, 256, fp)) break;
-    if(buffer[0] != '#'){ // skip comments and empty lines
-      str[0]='\0';
-      sscanf(buffer, "%s", str);
-      if(!strcmp(str, "Dimension")){
-        if(!fgets(buffer, sizeof(buffer), fp)) break;
-      }
-      else if(!strcmp(str, "Vertices")){
-        if(!fgets(buffer, sizeof(buffer), fp)) break;
-        int nbv;
-        sscanf(buffer, "%d", &nbv);
-        Msg::Info("%d vertices", nbv);
-        vertexVector.resize(nbv);
-        for(int i = 0; i < nbv; i++) {
-          if(!fgets(buffer, sizeof(buffer), fp)) break;
-          int dum;
-          double x, y, z;
-          sscanf(buffer, "%lf %lf %lf %d", &x, &y, &z, &dum);
-          vertexVector[i] = new MVertex(x, y, z);
-        }
-      }
-      else if(!strcmp(str, "Edges")){
-        if(!fgets(buffer, sizeof(buffer), fp)) break;
-        int nbe;
-        sscanf(buffer, "%d", &nbe);
-        Msg::Info("%d edges", nbe);
-        for(int i = 0; i < nbe; i++) {
-          if(!fgets(buffer, sizeof(buffer), fp)) break;
-          int n[2], cl;
-          sscanf(buffer, "%d %d %d", &n[0], &n[1], &cl);
-          for(int j = 0; j < 2; j++) n[j]--;
-          std::vector<MVertex*> vertices;
-          if(!getVertices(2, n, vertexVector, vertices)) return 0;
-          elements[0][cl].push_back(new MLine(vertices));
-        }
-      }
-      else if(!strcmp(str, "Triangles")){
-        if(!fgets(buffer, sizeof(buffer), fp)) break;
-        int nbe;
-        sscanf(buffer, "%d", &nbe);
-        Msg::Info("%d triangles", nbe);
-        for(int i = 0; i < nbe; i++) {
-          if(!fgets(buffer, sizeof(buffer), fp)) break;
-          int n[3], cl;
-          sscanf(buffer, "%d %d %d %d", &n[0], &n[1], &n[2], &cl);
-          for(int j = 0; j < 3; j++) n[j]--;
-          std::vector<MVertex*> vertices;
-          if(!getVertices(3, n, vertexVector, vertices)) return 0;
-          elements[1][cl].push_back(new MTriangle(vertices));
-        }
-      }
-      else if(!strcmp(str, "Quadrilaterals")) {
-        if(!fgets(buffer, sizeof(buffer), fp)) break;
-        int nbe;
-        sscanf(buffer, "%d", &nbe);
-        Msg::Info("%d quadrangles", nbe);
-        for(int i = 0; i < nbe; i++) {
-          if(!fgets(buffer, sizeof(buffer), fp)) break;
-          int n[4], cl;
-          sscanf(buffer, "%d %d %d %d %d", &n[0], &n[1], &n[2], &n[3], &cl);
-          for(int j = 0; j < 4; j++) n[j]--;
-          std::vector<MVertex*> vertices;
-          if(!getVertices(4, n, vertexVector, vertices)) return 0;
-          elements[2][cl].push_back(new MQuadrangle(vertices));
-        }
-      }
-      else if(!strcmp(str, "Tetrahedra")) {
-        if(!fgets(buffer, sizeof(buffer), fp)) break;
-        int nbe;
-        sscanf(buffer, "%d", &nbe);
-        Msg::Info("%d tetrahedra", nbe);
-        for(int i = 0; i < nbe; i++) {
-          if(!fgets(buffer, sizeof(buffer), fp)) break;
-          int n[4], cl;
-          sscanf(buffer, "%d %d %d %d %d", &n[0], &n[1], &n[2], &n[3], &cl);
-          for(int j = 0; j < 4; j++) n[j]--;
-          std::vector<MVertex*> vertices;
-          if(!getVertices(4, n, vertexVector, vertices)) return 0;
-          elements[3][cl].push_back(new MTetrahedron(vertices));
-        }
-      }
-      else if(!strcmp(str, "Hexahedra")) {
-        if(!fgets(buffer, sizeof(buffer), fp)) break;
-        int nbe;
-        sscanf(buffer, "%d", &nbe);
-        Msg::Info("%d hexahedra", nbe);
-        for(int i = 0; i < nbe; i++) {
-          if(!fgets(buffer, sizeof(buffer), fp)) break;
-          int n[8], cl;
-          sscanf(buffer, "%d %d %d %d %d %d %d %d %d", &n[0], &n[1], &n[2], &n[3],
-                 &n[4], &n[5], &n[6], &n[7], &cl);
-          for(int j = 0; j < 8; j++) n[j]--;
-          std::vector<MVertex*> vertices;
-          if(!getVertices(8, n, vertexVector, vertices)) return 0;
-          elements[4][cl].push_back(new MHexahedron(vertices));
-        }
-      }
-    }
-  }
-
-  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
-    _storeElementsInEntities(elements[i]);
-  _associateEntityWithMeshVertices();
-  _storeVerticesInEntities(vertexVector);
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writeMESH(const std::string &name, int elementTagType,
-                      bool saveAll, double scalingFactor)
-{
-  FILE *fp = fopen(name.c_str(), "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  int numVertices = indexMeshVertices(saveAll);
-
-  fprintf(fp, " MeshVersionFormatted 1\n");
-  fprintf(fp, " Dimension\n");
-  fprintf(fp, " 3\n");
-
-  fprintf(fp, " Vertices\n");
-  fprintf(fp, " %d\n", numVertices);
-  std::vector<GEntity*> entities;
-  getEntities(entities);
-  for(unsigned int i = 0; i < entities.size(); i++)
-    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
-      entities[i]->mesh_vertices[j]->writeMESH(fp, scalingFactor);
-
-  int numEdges = 0, numTriangles = 0, numQuadrangles = 0;
-  int numTetrahedra = 0, numHexahedra = 0;
-  for(eiter it = firstEdge(); it != lastEdge(); ++it){
-    if(saveAll || (*it)->physicals.size()){
-      numEdges += (*it)->lines.size();
-    }
-  }
-  for(fiter it = firstFace(); it != lastFace(); ++it){
-    if(saveAll || (*it)->physicals.size()){
-      numTriangles += (*it)->triangles.size();
-      numQuadrangles += (*it)->quadrangles.size();
-    }
-  }
-  for(riter it = firstRegion(); it != lastRegion(); ++it){
-    if(saveAll || (*it)->physicals.size()){
-      numTetrahedra += (*it)->tetrahedra.size();
-      numHexahedra += (*it)->hexahedra.size();
-    }
-  }
-
-  if(numEdges){
-    fprintf(fp, " Edges\n");
-    fprintf(fp, " %d\n", numEdges);
-    for(eiter it = firstEdge(); it != lastEdge(); ++it){
-      int numPhys = (*it)->physicals.size();
-      if(saveAll || numPhys){
-        for(unsigned int i = 0; i < (*it)->lines.size(); i++)
-          (*it)->lines[i]->writeMESH(fp, elementTagType, (*it)->tag(),
-                                     numPhys ? (*it)->physicals[0] : 0);
-      }
-    }
-  }
-  if(numTriangles){
-    fprintf(fp, " Triangles\n");
-    fprintf(fp, " %d\n", numTriangles);
-    for(fiter it = firstFace(); it != lastFace(); ++it){
-      int numPhys = (*it)->physicals.size();
-      if(saveAll || numPhys){
-        for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
-          (*it)->triangles[i]->writeMESH(fp, elementTagType, (*it)->tag(),
-                                         numPhys ? (*it)->physicals[0] : 0);
-      }
-    }
-  }
-  if(numQuadrangles){
-    fprintf(fp, " Quadrilaterals\n");
-    fprintf(fp, " %d\n", numQuadrangles);
-    for(fiter it = firstFace(); it != lastFace(); ++it){
-      int numPhys = (*it)->physicals.size();
-      if(saveAll || numPhys){
-        for(unsigned int i = 0; i < (*it)->quadrangles.size(); i++)
-          (*it)->quadrangles[i]->writeMESH(fp, elementTagType, (*it)->tag(),
-                                           numPhys ? (*it)->physicals[0] : 0);
-      }
-    }
-  }
-  if(numTetrahedra){
-    fprintf(fp, " Tetrahedra\n");
-    fprintf(fp, " %d\n", numTetrahedra);
-    for(riter it = firstRegion(); it != lastRegion(); ++it){
-      int numPhys = (*it)->physicals.size();
-      if(saveAll || numPhys){
-        for(unsigned int i = 0; i < (*it)->tetrahedra.size(); i++)
-          (*it)->tetrahedra[i]->writeMESH(fp, elementTagType, (*it)->tag(),
-                                          numPhys ? (*it)->physicals[0] : 0);
-      }
-    }
-  }
-  if(numHexahedra){
-    fprintf(fp, " Hexahedra\n");
-    fprintf(fp, " %d\n", numHexahedra);
-    for(riter it = firstRegion(); it != lastRegion(); ++it){
-      int numPhys = (*it)->physicals.size();
-      if(saveAll || numPhys){
-        for(unsigned int i = 0; i < (*it)->hexahedra.size(); i++)
-          (*it)->hexahedra[i]->writeMESH(fp, elementTagType, (*it)->tag(),
-                                         numPhys ? (*it)->physicals[0] : 0);
-      }
-    }
-  }
-
-  fprintf(fp, " End\n");
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writeMAIL(const std::string &name, bool saveAll, double scalingFactor)
-{
-  // CEA triangulation (.mail format) for Eric Darrigrand. Note that
-  // we currently don't save the edges of the triangulation (the last
-  // part of the file).
-  FILE *fp = fopen(name.c_str(), "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  int numVertices = indexMeshVertices(saveAll), numTriangles = 0;
-  for(fiter it = firstFace(); it != lastFace(); ++it)
-    if(saveAll || (*it)->physicals.size())
-      numTriangles += (*it)->triangles.size();
-
-  fprintf(fp, " %d %d\n", numVertices, numTriangles);
-
-  std::vector<GEntity*> entities;
-  getEntities(entities);
-  for(unsigned int i = 0; i < entities.size(); i++){
-    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++){
-      MVertex *v = entities[i]->mesh_vertices[j];
-      fprintf(fp, " %19.10E %19.10E %19.10E\n", v->x() * scalingFactor,
-              v->y() * scalingFactor, v->z() * scalingFactor);
-    }
-  }
-
-  for(fiter it = firstFace(); it != lastFace(); ++it){
-    if(saveAll || (*it)->physicals.size()){
-      for(unsigned int i = 0; i < (*it)->triangles.size(); i++){
-        MTriangle *t = (*it)->triangles[i];
-        fprintf(fp, " %d %d %d\n", t->getVertex(0)->getIndex(),
-                t->getVertex(1)->getIndex(), t->getVertex(2)->getIndex());
-      }
-    }
-  }
-
-  // TODO write edges (with signs)
-  for(fiter it = firstFace(); it != lastFace(); ++it){
-    if(saveAll || (*it)->physicals.size()){
-      for(unsigned int i = 0; i < (*it)->triangles.size(); i++){
-        //MTriangle *t = (*it)->triangles[i];
-        fprintf(fp, " %d %d %d\n", 0, 0, 0);
-      }
-    }
-  }
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::readGEOM(const std::string &name)
-{
-  // this is a format (from geomview?) that Bruno Levy's Graphite code
-  // can write
-  FILE *fp = fopen(name.c_str(), "r");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  int numNodes, numElements, dummy;
-  if(fscanf(fp, "%d %d %d", &numNodes, &numElements, &dummy) != 3)
-    return 0;
-
-  if(!numNodes || !numElements){
-    Msg::Warning("No vertices or elements found");
-    return 0;
-  }
-
-  Msg::Info("%d vertices, %d elements", numNodes, numElements);
-
-  std::vector<MVertex*> vertexVector;
-  std::map<int, std::vector<MElement*> > elements[1];
-
-  vertexVector.resize(numNodes);
-  for(int i = 0; i < numNodes; i++) {
-    double x, y, z;
-    if(fscanf(fp, "%lf %lf %lf", &x, &y, &z) != 3) break;
-    vertexVector[i] = new MVertex(x, y, z);
-  }
-
-  for(int i = 0; i < numElements; i++) {
-    int N, n[3];
-    if(fscanf(fp, "%d", &N) != 1) break;
-    switch(N){
-    case 3:
-      {
-        if(fscanf(fp, "%d %d %d", &n[0], &n[1], &n[2]) != 3) break;
-        for(int i = 0; i < 3; i++) n[i]--;
-        std::vector<MVertex*> vertices;
-        if(!getVertices(3, n, vertexVector, vertices)) break;
-        elements[0][1].push_back(new MTriangle(vertices));
-      }
-      break;
-    default:
-      Msg::Error("Unknown element type in .geom reader");
-      break;
-    }
-  }
-
-  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
-    _storeElementsInEntities(elements[i]);
-  _associateEntityWithMeshVertices();
-  _storeVerticesInEntities(vertexVector);
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writeIR3(const std::string &name, int elementTagType,
-                     bool saveAll, double scalingFactor)
-{
-  FILE *fp = fopen(name.c_str(), "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  int numVertices = indexMeshVertices(saveAll), num2D = 0, num3D = 0;
-  for(fiter it = firstFace(); it != lastFace(); ++it)
-    if(saveAll || (*it)->physicals.size())
-      num2D += (*it)->getNumMeshElements();
-  for(riter it = firstRegion(); it != lastRegion(); ++it)
-    if(saveAll || (*it)->physicals.size())
-      num3D += (*it)->getNumMeshElements();
-
-  fprintf(fp,"33\n");
-  if(num2D && num3D)
-    fprintf(fp,"%d %d %d\n", numVertices, num2D, num3D);
-  else
-    fprintf(fp,"%d %d\n", numVertices, num2D ? num2D : num3D);
-
-  std::vector<GEntity*> entities;
-  getEntities(entities);
-
-  for(unsigned int i = 0; i < entities.size(); i++)
-    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
-      if(entities[i]->mesh_vertices[j]->getIndex() >= 0)
-        fprintf(fp,"%d %.16g %.16g %.16g\n", entities[i]->mesh_vertices[j]->getIndex(),
-                entities[i]->mesh_vertices[j]->x() * scalingFactor,
-                entities[i]->mesh_vertices[j]->y() * scalingFactor,
-                entities[i]->mesh_vertices[j]->z() * scalingFactor);
-
-  int iElement = 1;
-  for(fiter it = firstFace(); it != lastFace(); ++it){
-    int numPhys = (*it)->physicals.size();
-    if(saveAll || numPhys)
-      for(unsigned int i = 0; i < (*it)->getNumMeshElements(); i++)
-        (*it)->getMeshElement(i)->writeIR3
-          (fp, elementTagType, iElement++, (*it)->tag(),
-           numPhys ? (*it)->physicals[0] : 0);
-  }
-
-  iElement = 1;
-  for(riter it = firstRegion(); it != lastRegion(); ++it){
-    int numPhys = (*it)->physicals.size();
-    if(saveAll || numPhys)
-      for(unsigned int i = 0; i < (*it)->getNumMeshElements(); i++)
-        (*it)->getMeshElement(i)->writeIR3
-          (fp, elementTagType, iElement++, (*it)->tag(),
-           numPhys ? (*it)->physicals[0] : 0);
-  }
-
-  fclose(fp);
-  return 1;
-}
-
-static int getFormatBDF(char *buffer, int &keySize)
-{
-  if(buffer[keySize] == '*'){ keySize++; return 2; } // long fields
-  for(unsigned int i = 0; i < strlen(buffer); i++)
-    if(buffer[i] == ',') return 0; // free fields
-  return 1; // small fields;
-}
-
-static double atofBDF(char *str)
-{
-  int len = strlen(str);
-  // classic numbers with E or D exponent notation
-  for(int i = 0; i < len; i++){
-    if(str[i] == 'E' || str[i] == 'e') {
-      return atof(str);
-    }
-    else if(str[i] == 'D' || str[i] == 'd'){
-      str[i] = 'E';
-      return atof(str);
-    }
-  }
-  // special Nastran floating point format (e.g. "-7.-1" instead of
-  // "-7.E-01" or "2.3+2" instead of "2.3E+02")
-  char tmp[32];
-  int j = 0, leading_minus = 1;
-  for(int i = 0; i < len; i++){
-    if(leading_minus && str[i] != ' '  && str[i] != '-') leading_minus = 0;
-    if(!leading_minus && str[i] == '-') tmp[j++] = 'E';
-    if(str[i] == '+') tmp[j++] = 'E';
-    tmp[j++] = str[i];
-  }
-  tmp[j] = '\0';
-  return atof(tmp);
-}
-
-static int readVertexBDF(FILE *fp, char *buffer, int keySize,
-                         int *num, double *x, double *y, double *z)
-{
-  char tmp[5][32];
-  int j = keySize;
-
-  switch(getFormatBDF(buffer, keySize)){
-  case 0: // free field
-    for(int i = 0; i < 5; i++){
-      tmp[i][16] = '\0';
-      strncpy(tmp[i], &buffer[j + 1], 16);
-      for(int k = 0; k < 16; k++){ if(tmp[i][k] == ',') tmp[i][k] = '\0'; }
-      j++;
-      while(j < (int)strlen(buffer) && buffer[j] != ',') j++;
-    }
-    break;
-  case 1: // small field
-    for(int i = 0; i < 5; i++) tmp[i][8] = '\0';
-    strncpy(tmp[0], &buffer[8], 8);
-    strncpy(tmp[2], &buffer[24], 8);
-    strncpy(tmp[3], &buffer[32], 8);
-    strncpy(tmp[4], &buffer[40], 8);
-    break;
-  case 2: // long field
-    for(int i = 0; i < 5; i++) tmp[i][16] = '\0';
-    strncpy(tmp[0], &buffer[8], 16);
-    strncpy(tmp[2], &buffer[40], 16);
-    strncpy(tmp[3], &buffer[56], 16);
-    char buffer2[256];
-    for(unsigned int i = 0; i < sizeof(buffer2); i++) buffer2[i] = '\0';
-    if(!fgets(buffer2, sizeof(buffer2), fp)) return 0;
-    strncpy(tmp[4], &buffer2[8], 16);
-    break;
-  }
-
-  *num = atoi(tmp[0]);
-  *x = atofBDF(tmp[2]);
-  *y = atofBDF(tmp[3]);
-  *z = atofBDF(tmp[4]);
-  return 1;
-}
-
-static bool emptyFieldBDF(char *field, int length)
-{
-  for(int i = 0; i < length; i++)
-    if(field[i] != '\0' && field[i] != ' ' && field[i] != '\n' && field[i] != '\r')
-      return false;
-  return true;
-}
-
-static void readLineBDF(char *buffer, int format, std::vector<char*> &fields)
-{
-  int cmax = (format == 2) ? 16 : 8; // max char per (center) field
-  int nmax = (format == 2) ? 4 : 8; // max num of (center) fields per line
-
-  if(format == 0){ // free fields
-    for(unsigned int i = 0; i < strlen(buffer); i++){
-      if(buffer[i] == ',') fields.push_back(&buffer[i + 1]);
-    }
-  }
-  else{ // small or long fields
-    for(int i = 0; i < nmax + 1; i++){
-      if(!emptyFieldBDF(&buffer[8 + cmax * i], cmax))
-        fields.push_back(&buffer[8 + cmax * i]);
-    }
-  }
-}
-
-static int readElementBDF(FILE *fp, char *buffer, int keySize, int numVertices,
-                          int &num, int &region, std::vector<MVertex*> &vertices,
-                          std::map<int, MVertex*> &vertexMap)
-{
-  char buffer2[256], buffer3[256];
-  std::vector<char*> fields;
-  int format = getFormatBDF(buffer, keySize);
-
-  for(unsigned int i = 0; i < sizeof(buffer2); i++) buffer2[i] = buffer3[i] = '\0';
-
-  readLineBDF(buffer, format, fields);
-
-  if(((int)fields.size() - 2 < abs(numVertices)) ||
-     (numVertices < 0 && (fields.size() == 9))){
-    if(fields.size() == 9) fields.pop_back();
-    if(!fgets(buffer2, sizeof(buffer2), fp)) return 0;
-    readLineBDF(buffer2, format, fields);
-  }
-
-  if(((int)fields.size() - 2 < abs(numVertices)) ||
-     (numVertices < 0 && (fields.size() == 17))){
-    if(fields.size() == 17) fields.pop_back();
-    if(!fgets(buffer3, sizeof(buffer3), fp)) return 0;
-    readLineBDF(buffer3, format, fields);
-  }
-
-  // negative 'numVertices' gives the minimum required number of vertices
-  if((int)fields.size() - 2 < abs(numVertices)){
-    Msg::Error("Wrong number of vertices %d for element", fields.size() - 2);
-    return 0;
-  }
-
-  int n[30], cmax = (format == 2) ? 16 : 8; // max char per (center) field
-  char tmp[32];
-  tmp[cmax] = '\0';
-  strncpy(tmp, fields[0], cmax); num = atoi(tmp);
-  strncpy(tmp, fields[1], cmax); region = atoi(tmp);
-  for(unsigned int i = 2; i < fields.size(); i++){
-    strncpy(tmp, fields[i], cmax); n[i - 2] = atoi(tmp);
-  }
-
-  // ignore the extra fields when we know how many vertices we need
-  int numCheck = (numVertices > 0) ? numVertices : fields.size() - 2;
-  if(!getVertices(numCheck, n, vertexMap, vertices)) return 0;
-  return 1;
-}
-
-int GModel::readBDF(const std::string &name)
-{
-  FILE *fp = fopen(name.c_str(), "r");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  char buffer[256];
-  std::map<int, MVertex*> vertexMap;
-  std::map<int, std::vector<MElement*> > elements[7];
-
-  // nodes can be defined after elements, so parse the file twice
-
-  while(!feof(fp)) {
-    for(unsigned int i = 0; i < sizeof(buffer); i++) buffer[i] = '\0';
-    if(!fgets(buffer, sizeof(buffer), fp)) break;
-    if(buffer[0] != '$'){ // skip comments
-      if(!strncmp(buffer, "GRID", 4)){
-        int num;
-        double x, y, z;
-        if(!readVertexBDF(fp, buffer, 4, &num, &x, &y, &z)) break;
-        vertexMap[num] = new MVertex(x, y, z, 0, num);
-      }
-    }
-  }
-  Msg::Info("%d vertices", vertexMap.size());
-
-  rewind(fp);
-  while(!feof(fp)) {
-    for(unsigned int i = 0; i < sizeof(buffer); i++) buffer[i] = '\0';
-    if(!fgets(buffer, sizeof(buffer), fp)) break;
-    if(buffer[0] != '$'){ // skip comments
-      int num, region;
-      std::vector<MVertex*> vertices;
-      if(!strncmp(buffer, "CBAR", 4)){
-        if(readElementBDF(fp, buffer, 4, 2, num, region, vertices, vertexMap))
-          elements[0][region].push_back(new MLine(vertices, num));
-      }
-      else if(!strncmp(buffer, "CROD", 4)){
-        if(readElementBDF(fp, buffer, 4, 2, num, region, vertices, vertexMap))
-          elements[0][region].push_back(new MLine(vertices, num));
-      }
-      else if(!strncmp(buffer, "CBEAM", 5)){
-        if(readElementBDF(fp, buffer, 5, 2, num, region, vertices, vertexMap))
-          elements[0][region].push_back(new MLine(vertices, num));
-      }
-      else if(!strncmp(buffer, "CTRIA3", 6)){
-        if(readElementBDF(fp, buffer, 6, 3, num, region, vertices, vertexMap))
-          elements[1][region].push_back(new MTriangle(vertices, num));
-      }
-      else if(!strncmp(buffer, "CTRIA6", 6)){
-        if(readElementBDF(fp, buffer, 6, 6, num, region, vertices, vertexMap))
-          elements[1][region].push_back(new MTriangle6(vertices, num));
-      }
-      else if(!strncmp(buffer, "CQUAD4", 6)){
-        if(readElementBDF(fp, buffer, 6, 4, num, region, vertices, vertexMap))
-          elements[2][region].push_back(new MQuadrangle(vertices, num));
-      }
-      else if(!strncmp(buffer, "CQUAD8", 6)){
-        if(readElementBDF(fp, buffer, 6, 8, num, region, vertices, vertexMap))
-          elements[2][region].push_back(new MQuadrangle8(vertices, num));
-      }
-      else if(!strncmp(buffer, "CQUAD", 5)){
-        if(readElementBDF(fp, buffer, 5, -4, num, region, vertices, vertexMap)){
-          if(vertices.size() == 9)
-            elements[2][region].push_back(new MQuadrangle9(vertices, num));
-          else if(vertices.size() == 8)
-            elements[2][region].push_back(new MQuadrangle8(vertices, num));
-          else
-            elements[2][region].push_back(new MQuadrangle(vertices, num));
-        }
-      }
-      else if(!strncmp(buffer, "CTETRA", 6)){
-        if(readElementBDF(fp, buffer, 6, -4, num, region, vertices, vertexMap)){
-          if(vertices.size() == 10)
-            elements[3][region].push_back
-              (new MTetrahedron10(vertices[0], vertices[1], vertices[2], vertices[3],
-                                  vertices[4], vertices[5], vertices[6], vertices[7],
-                                  vertices[9], vertices[8], num));
-          else
-            elements[3][region].push_back(new MTetrahedron(vertices, num));
-        }
-      }
-      else if(!strncmp(buffer, "CHEXA", 5)){
-        if(readElementBDF(fp, buffer, 5, -8, num, region, vertices, vertexMap)){
-          if(vertices.size() == 20)
-            elements[4][region].push_back
-              (new MHexahedron20(vertices[0], vertices[1], vertices[2], vertices[3],
-                                 vertices[4], vertices[5], vertices[6], vertices[7],
-                                 vertices[8], vertices[11], vertices[12], vertices[9],
-                                 vertices[13], vertices[10], vertices[14], vertices[15],
-                                 vertices[16], vertices[19], vertices[17], vertices[18],
-                                 num));
-          else
-            elements[4][region].push_back(new MHexahedron(vertices, num));
-        }
-      }
-      else if(!strncmp(buffer, "CPENTA", 6)){
-        if(readElementBDF(fp, buffer, 6, -6, num, region, vertices, vertexMap)){
-          if(vertices.size() == 15)
-            elements[5][region].push_back
-              (new MPrism15(vertices[0], vertices[1], vertices[2], vertices[3],
-                            vertices[4], vertices[5], vertices[6], vertices[8],
-                            vertices[9], vertices[7], vertices[10], vertices[11],
-                            vertices[12], vertices[14], vertices[13], num));
-          else
-            elements[5][region].push_back(new MPrism(vertices, num));
-        }
-      }
-      else if(!strncmp(buffer, "CPYRAM", 6)){
-        if(readElementBDF(fp, buffer, 6, 5, num, region, vertices, vertexMap))
-          elements[6][region].push_back(new MPyramid(vertices, num));
-      }
-    }
-  }
-
-  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
-    _storeElementsInEntities(elements[i]);
-  _associateEntityWithMeshVertices();
-  _storeVerticesInEntities(vertexMap);
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writeBDF(const std::string &name, int format, int elementTagType,
-                     bool saveAll, double scalingFactor)
-{
-  FILE *fp = fopen(name.c_str(), "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  indexMeshVertices(saveAll);
-
-  fprintf(fp, "$ Created by Gmsh\n");
-
-  std::vector<GEntity*> entities;
-  getEntities(entities);
-
-  // nodes
-  for(unsigned int i = 0; i < entities.size(); i++)
-    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
-      entities[i]->mesh_vertices[j]->writeBDF(fp, format, scalingFactor);
-
-  // elements
-  for(unsigned int i = 0; i < entities.size(); i++)
-    for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
-      int numPhys = entities[i]->physicals.size();
-      if(saveAll || numPhys)
-        entities[i]->getMeshElement(j)->writeBDF
-          (fp, format, elementTagType, entities[i]->tag(),
-           numPhys ? entities[i]->physicals[0] : 0);
-    }
-
-  fprintf(fp, "ENDDATA\n");
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::readP3D(const std::string &name)
-{
-  FILE *fp = fopen(name.c_str(), "r");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  int numBlocks = 0;
-  if(fscanf(fp, "%d", &numBlocks) != 1 || numBlocks <= 0) return 0;
-
-  std::vector<int> Ni(numBlocks), Nj(numBlocks), Nk(numBlocks);
-  for(int n = 0; n < numBlocks; n++)
-    if(fscanf(fp, "%d %d %d", &Ni[n], &Nj[n], &Nk[n]) != 3) return 0;
-
-  for(int n = 0; n < numBlocks; n++){
-    if(Nk[n] == 1){
-      GFace *gf = new discreteFace(this, getMaxElementaryNumber(2) + 1);
-      add(gf);
-      gf->transfinite_vertices.resize(Ni[n]);
-      for(int i = 0; i < Ni[n]; i++)
-        gf->transfinite_vertices[i].resize(Nj[n]);
-      for(int coord = 0; coord < 3; coord++){
-        for(int i = 0; i < Ni[n]; i++){
-          for(int j = 0; j < Nj[n]; j++){
-            double d;
-            if(fscanf(fp, "%lf", &d) != 1) return 0;
-            if(coord == 0){
-              MVertex *v = new MVertex(d, 0., 0., gf);
-              gf->transfinite_vertices[i][j] = v;
-              gf->mesh_vertices.push_back(v);
-            }
-            else if(coord == 1){
-              gf->transfinite_vertices[i][j]->y() = d;
-            }
-            else if(coord == 2){
-              gf->transfinite_vertices[i][j]->z() = d;
-            }
-          }
-        }
-      }
-      for(unsigned int i = 0; i < gf->transfinite_vertices.size() - 1; i++)
-        for(unsigned int j = 0; j < gf->transfinite_vertices[0].size() - 1; j++)
-          gf->quadrangles.push_back
-            (new MQuadrangle(gf->transfinite_vertices[i    ][j    ],
-                             gf->transfinite_vertices[i + 1][j    ],
-                             gf->transfinite_vertices[i + 1][j + 1],
-                             gf->transfinite_vertices[i    ][j + 1]));
-    }
-    else{
-      GRegion *gr = new discreteRegion(this,  getMaxElementaryNumber(3) + 1);
-      add(gr);
-      gr->transfinite_vertices.resize(Ni[n]);
-      for(int i = 0; i < Ni[n]; i++){
-        gr->transfinite_vertices[i].resize(Nj[n]);
-        for(int j = 0; j < Nj[n]; j++){
-          gr->transfinite_vertices[i][j].resize(Nk[n]);
-        }
-      }
-      for(int coord = 0; coord < 3; coord++){
-        for(int i = 0; i < Ni[n]; i++){
-          for(int j = 0; j < Nj[n]; j++){
-            for(int k = 0; k < Nk[n]; k++){
-              double d;
-              if(fscanf(fp, "%lf", &d) != 1) return 0;
-              if(coord == 0){
-                MVertex *v = new MVertex(d, 0., 0., gr);
-                gr->transfinite_vertices[i][j][k] = v;
-                gr->mesh_vertices.push_back(v);
-              }
-              else if(coord == 1){
-                gr->transfinite_vertices[i][j][k]->y() = d;
-              }
-              else if(coord == 2){
-                gr->transfinite_vertices[i][j][k]->z() = d;
-              }
-            }
-          }
-        }
-      }
-      for(unsigned int i = 0; i < gr->transfinite_vertices.size() - 1; i++)
-        for(unsigned int j = 0; j < gr->transfinite_vertices[0].size() - 1; j++)
-          for(unsigned int k = 0; k < gr->transfinite_vertices[0][0].size() - 1; k++)
-            gr->hexahedra.push_back
-              (new MHexahedron(gr->transfinite_vertices[i    ][j    ][k    ],
-                               gr->transfinite_vertices[i + 1][j    ][k    ],
-                               gr->transfinite_vertices[i + 1][j + 1][k    ],
-                               gr->transfinite_vertices[i    ][j + 1][k    ],
-                               gr->transfinite_vertices[i    ][j    ][k + 1],
-                               gr->transfinite_vertices[i + 1][j    ][k + 1],
-                               gr->transfinite_vertices[i + 1][j + 1][k + 1],
-                               gr->transfinite_vertices[i    ][j + 1][k + 1]));
-    }
-  }
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writeP3D(const std::string &name, bool saveAll, double scalingFactor)
-{
-  FILE *fp = fopen(name.c_str(), "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  std::vector<GFace*> faces;
-  for(fiter it = firstFace(); it != lastFace(); ++it)
-    if((*it)->transfinite_vertices.size() &&
-       (*it)->transfinite_vertices[0].size() &&
-       ((*it)->physicals.size() || saveAll)) faces.push_back(*it);
-
-  std::vector<GRegion*> regions;
-  for(riter it = firstRegion(); it != lastRegion(); ++it)
-    if((*it)->transfinite_vertices.size() &&
-       (*it)->transfinite_vertices[0].size() &&
-       (*it)->transfinite_vertices[0][0].size() &&
-       ((*it)->physicals.size() || saveAll)) regions.push_back(*it);
-
-  if(faces.empty() && regions.empty()){
-    Msg::Warning("No structured grids to save");
-    fclose(fp);
-    return 0;
-  }
-
-  fprintf(fp, "%d\n", (int)(faces.size() + regions.size()));
-
-  for(unsigned int i = 0; i < faces.size(); i++)
-    fprintf(fp, "%d %d 1\n",
-            (int)faces[i]->transfinite_vertices.size(),
-            (int)faces[i]->transfinite_vertices[0].size());
-
-  for(unsigned int i = 0; i < regions.size(); i++)
-    fprintf(fp, "%d %d %d\n",
-            (int)regions[i]->transfinite_vertices.size(),
-            (int)regions[i]->transfinite_vertices[0].size(),
-            (int)regions[i]->transfinite_vertices[0][0].size());
-
-  for(unsigned int i = 0; i < faces.size(); i++){
-    GFace *gf = faces[i];
-    for(int coord = 0; coord < 3; coord++){
-      for(unsigned int j = 0; j < gf->transfinite_vertices.size(); j++){
-        for(unsigned int k = 0; k < gf->transfinite_vertices[j].size(); k++){
-          MVertex *v = gf->transfinite_vertices[j][k];
-          double d = (coord == 0) ? v->x() : (coord == 1) ? v->y() : v->z();
-          fprintf(fp, "%.16g ", d * scalingFactor);
-        }
-        fprintf(fp, "\n");
-      }
-    }
-  }
-
-  for(unsigned int i = 0; i < regions.size(); i++){
-    GRegion *gr = regions[i];
-    for(int coord = 0; coord < 3; coord++){
-      for(unsigned int j = 0; j < gr->transfinite_vertices.size(); j++){
-        for(unsigned int k = 0; k < gr->transfinite_vertices[j].size(); k++){
-          for(unsigned int l = 0; l < gr->transfinite_vertices[j][k].size(); l++){
-            MVertex *v = gr->transfinite_vertices[j][k][l];
-            double d = (coord == 0) ? v->x() : (coord == 1) ? v->y() : v->z();
-            fprintf(fp, "%.16g ", d * scalingFactor);
-          }
-          fprintf(fp, "\n");
-        }
-      }
-    }
-  }
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writeVTK(const std::string &name, bool binary, bool saveAll,
-                     double scalingFactor, bool bigEndian)
-{
-  FILE *fp = fopen(name.c_str(), binary ? "wb" : "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  // get the number of vertices and index the vertices in a continuous
-  // sequence
-  int numVertices = indexMeshVertices(saveAll);
-
-  fprintf(fp, "# vtk DataFile Version 2.0\n");
-  fprintf(fp, "%s, Created by Gmsh\n", getName().c_str());
-  if(binary)
-    fprintf(fp, "BINARY\n");
-  else
-    fprintf(fp, "ASCII\n");
-  fprintf(fp, "DATASET UNSTRUCTURED_GRID\n");
-
-  // get all the entities in the model
-  std::vector<GEntity*> entities;
-  getEntities(entities);
-
-  // write mesh vertices
-  fprintf(fp, "POINTS %d double\n", numVertices);
-  for(unsigned int i = 0; i < entities.size(); i++)
-    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
-      entities[i]->mesh_vertices[j]->writeVTK(fp, binary, scalingFactor, bigEndian);
-  fprintf(fp, "\n");
-
-  // loop over all elements we need to save and count vertices
-  int numElements = 0, totalNumInt = 0;
-  for(unsigned int i = 0; i < entities.size(); i++){
-    if(entities[i]->physicals.size() || saveAll){
-      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
-        if(entities[i]->getMeshElement(j)->getTypeForVTK()){
-          numElements++;
-          totalNumInt += entities[i]->getMeshElement(j)->getNumVertices() + 1;
-        }
-      }
-    }
-  }
-
-  // print vertex indices in ascii or binary
-  fprintf(fp, "CELLS %d %d\n", numElements, totalNumInt);
-  for(unsigned int i = 0; i < entities.size(); i++){
-    if(entities[i]->physicals.size() || saveAll){
-      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
-        if(entities[i]->getMeshElement(j)->getTypeForVTK())
-          entities[i]->getMeshElement(j)->writeVTK(fp, binary, bigEndian);
-      }
-    }
-  }
-  fprintf(fp, "\n");
-
-  // print element types in ascii or binary
-  fprintf(fp, "CELL_TYPES %d\n", numElements);
-  for(unsigned int i = 0; i < entities.size(); i++){
-    if(entities[i]->physicals.size() || saveAll){
-      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
-        int type = entities[i]->getMeshElement(j)->getTypeForVTK();
-        if(type){
-          if(binary){
-            // VTK always expects big endian binary data
-            if(!bigEndian) SwapBytes((char*)&type, sizeof(int), 1);
-            fwrite(&type, sizeof(int), 1, fp);
-          }
-          else
-            fprintf(fp, "%d\n", type);
-        }
-      }
-    }
-  }
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::readVTK(const std::string &name, bool bigEndian)
-{
-  FILE *fp = fopen(name.c_str(), "rb");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  char buffer[256], buffer2[256];
-  std::map<int, std::map<int, std::string> > physicals[4];
-
-  if(!fgets(buffer, sizeof(buffer), fp)) return 0; // version line
-  if(!fgets(buffer, sizeof(buffer), fp)) return 0; // title
-
-  if(fscanf(fp, "%s", buffer) != 1) // ASCII or BINARY
-    Msg::Error("Failed reading buffer");
-  bool binary = false;
-  if(!strcmp(buffer, "BINARY")) binary = true;
-
-  if(fscanf(fp, "%s %s", buffer, buffer2) != 2) return 0;
-
-  bool unstructured = false;
-  if(!strcmp(buffer, "DATASET") && !strcmp(buffer2, "UNSTRUCTURED_GRID"))
-    unstructured = true;
-
-  if((strcmp(buffer, "DATASET") &&  strcmp(buffer2, "UNSTRUCTURED_GRID")) ||
-     (strcmp(buffer, "DATASET") &&  strcmp(buffer2, "POLYDATA"))){
-    Msg::Error("VTK reader can only read unstructured or polydata datasets");
-    return 0;
-  }
-
-  // read mesh vertices
-  int numVertices;
-  if(fscanf(fp, "%s %d %s\n", buffer, &numVertices, buffer2) != 3) return 0;
-  if(strcmp(buffer, "POINTS") || !numVertices){
-    Msg::Warning("No points in dataset");
-    return 0;
-  }
-  int datasize;
-  if(!strcmp(buffer2, "double"))
-    datasize = sizeof(double);
-  else if(!strcmp(buffer2, "float"))
-    datasize = sizeof(float);
-  else{
-    Msg::Warning("VTK reader only accepts float or double datasets");
-    return 0;
-  }
-  Msg::Info("Reading %d points", numVertices);
-  std::vector<MVertex*> vertices(numVertices);
-  for(int i = 0 ; i < numVertices; i++){
-    double xyz[3];
-    if(binary){
-      if(datasize == sizeof(float)){
-        float f[3];
-        if(fread(f, sizeof(float), 3, fp) != 3) return 0;
-        if(!bigEndian) SwapBytes((char*)f, sizeof(float), 3);
-        for(int j = 0; j < 3; j++) xyz[j] = f[j];
-      }
-      else{
-        if(fread(xyz, sizeof(double), 3, fp) != 3) return 0;
-        if(!bigEndian) SwapBytes((char*)xyz, sizeof(double), 3);
-      }
-    }
-    else{
-      if(fscanf(fp, "%lf %lf %lf", &xyz[0], &xyz[1], &xyz[2]) != 3) return 0;
-    }
-    vertices[i] = new MVertex(xyz[0], xyz[1], xyz[2]);
-  }
-
-  // read mesh elements
-  int numElements, totalNumInt;
-  if(fscanf(fp, "%s %d %d\n", buffer, &numElements, &totalNumInt) != 3) return 0;
-
-  bool haveCells = true;
-  bool haveLines = false;
-  if( !strcmp(buffer, "CELLS") && numElements > 0)
-    Msg::Info("Reading %d cells", numElements);
-  else if (!strcmp(buffer, "POLYGONS") && numElements > 0)
-    Msg::Info("Reading %d polygons", numElements);
-  else if (!strcmp(buffer, "LINES") && numElements > 0){
-    haveCells = false;
-    haveLines = true;
-    Msg::Info("Reading %d lines", numElements);
-  }
-  else{
-    Msg::Warning("No cells or polygons in dataset");
-    return 0;
-  }
-
-  std::map<int, std::vector<MElement*> > elements[8];
-
-  if (haveCells){
-    std::vector<std::vector<MVertex*> > cells(numElements);
-    for(unsigned int i = 0; i < cells.size(); i++){
-      int numVerts, n[100];
-      if(binary){
-	if(fread(&numVerts, sizeof(int), 1, fp) != 1) return 0;
-	if(!bigEndian) SwapBytes((char*)&numVerts, sizeof(int), 1);
-	if((int)fread(n, sizeof(int), numVerts, fp) != numVerts) return 0;
-	if(!bigEndian) SwapBytes((char*)n, sizeof(int), numVerts);
-      }
-      else{
-	if(fscanf(fp, "%d", &numVerts) != 1) return 0;
-	for(int j = 0; j < numVerts; j++){
-	  if(fscanf(fp, "%d", &n[j]) != 1) return 0;
-	}
-      }
-      for(int j = 0; j < numVerts; j++){
-	if(n[j] >= 0 && n[j] < (int)vertices.size())
-	  cells[i].push_back(vertices[n[j]]);
-	else
-	  Msg::Error("Bad vertex index");
-      }
-    }
-
-    if (unstructured){
-      if(fscanf(fp, "%s %d\n", buffer, &numElements) != 2 ) return 0;
-      if(strcmp(buffer, "CELL_TYPES") || numElements != (int)cells.size()){
-	Msg::Error("No or invalid number of cells types");
-	return 0;
-      }
-      for(unsigned int i = 0; i < cells.size(); i++){
-	int type;
-	if(binary){
-	  if(fread(&type, sizeof(int), 1, fp) != 1) return 0;
-	  if(!bigEndian) SwapBytes((char*)&type, sizeof(int), 1);
-	}
-	else{
-	  if(fscanf(fp, "%d", &type) != 1) return 0;
-	}
-	switch(type){
-	case 1: elements[0][1].push_back(new MPoint(cells[i])); break;
-	case 3: elements[1][1].push_back(new MLine(cells[i])); break;
-	case 5: elements[2][1].push_back(new MTriangle(cells[i])); break;
-	case 9: elements[3][1].push_back(new MQuadrangle(cells[i])); break;
-	case 10: elements[4][1].push_back(new MTetrahedron(cells[i])); break;
-	case 12: elements[5][1].push_back(new MHexahedron(cells[i])); break;
-	case 13: elements[6][1].push_back(new MPrism(cells[i])); break;
-	case 14: elements[7][1].push_back(new MPyramid(cells[i])); break;
-	default:
-	  Msg::Error("Unknown type of cell %d", type);
-	  break;
-	}
-      }
-    }
-    else{
-      for(unsigned int i = 0; i < cells.size(); i++){
-	int nbNodes = (int)cells[i].size();
-	switch(nbNodes){
-	case 1: elements[0][1].push_back(new MPoint(cells[i])); break;
-	case 2: elements[1][1].push_back(new MLine(cells[i])); break;
-	case 3: elements[2][1].push_back(new MTriangle(cells[i])); break;
-	case 4: elements[3][1].push_back(new MQuadrangle(cells[i])); break;
-	default:
-	  Msg::Error("Unknown type of mesh element with %d nodes", nbNodes);
-	  break;
-	}
-      }
-    }
-  }
-  else if (haveLines){
-    if(!binary){
-      int v0, v1;
-      char line[100000], *p, *pEnd, *pEnd2;
-      int iLine = 1;
-      for (int k= 0; k < numElements; k++){
-	physicals[1][iLine][1] = "centerline";
-	if(!fgets(line, sizeof(line), fp)) return 0;
-	v0 = (int)strtol(line, &pEnd, 10); //ignore first line
-	v0 = (int)strtol(pEnd, &pEnd2, 10);
-	p=pEnd2;
-	while(1){
-	  v1 = strtol(p, &pEnd, 10);
-	  if (p == pEnd )  break;
-	  elements[1][iLine].push_back(new MLine(vertices[v0],vertices[v1]));
-	  p = pEnd;
-	  v0 = v1;
-	}
-	iLine++;
-      }
-    }
-    else{
-      Msg::Error("TODO: implement reading lines for binary files \n");
-    }
-  }
-
-  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
-    _storeElementsInEntities(elements[i]);
-  _associateEntityWithMeshVertices();
-  _storeVerticesInEntities(vertices);
-
-  // store the physical tags
-  for(int i = 0; i < 4; i++)
-    _storePhysicalTagsInEntities(i, physicals[i]);
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::readDIFF(const std::string &name)
-{
-  FILE *fp = fopen(name.c_str(), "r");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  char str[256] = "XXX";
-  std::map<int, std::vector<MElement*> > elements[10];
-  std::map<int, std::map<int, std::string> > physicals[4];
-  std::map<int, MVertex*> vertexMap;
-  std::vector<MVertex*> vertexVector;
-
-  {
-    while(strstr(str, "Number of space dim. =") == NULL){
-      if(!fgets(str, sizeof(str), fp) || feof(fp))
-        break;
-    }
-
-    int dim;
-    if(sscanf(str, "%*s %*s %*s %*s %*s %d", &dim) != 1) return 0;
-    Msg::Info("dimension %d", dim);
-
-    int numElements;
-    if(!fgets(str, sizeof(str), fp) || feof(fp)) return 0;
-    while(strstr(str, "Number of elements   =") == NULL){
-      if(!fgets(str, sizeof(str), fp) || feof(fp))
-        break;
-    }
-    if(sscanf(str, "%*s %*s %*s %*s %d", &numElements) != 1) return 0;
-    Msg::Info("%d elements", numElements);
-
-    int numVertices;
-    if(!fgets(str, sizeof(str), fp) || feof(fp)) return 0;
-    while(strstr(str, "Number of nodes      =") == NULL){
-      if(!fgets(str, sizeof(str), fp) || feof(fp))
-        break;
-    }
-    if(sscanf(str, "%*s %*s %*s %*s %d", &numVertices) != 1) return 0;
-    Msg::Info("%d vertices", numVertices);
-
-    int numVerticesPerElement;
-    if(!fgets(str, sizeof(str), fp)||feof(fp)) return 0;
-    while(strstr(str, "Max number of nodes in an element:")==NULL){
-      if(!fgets(str, sizeof(str), fp) || feof(fp))
-        break;
-    }
-    if(sscanf(str, "%*s %*s %*s %*s %*s %*s %*s %d", &numVerticesPerElement) != 1)
-      return 0;
-    Msg::Info("numVerticesPerElement %d", numVerticesPerElement);
-
-    bool several_subdomains;
-    if(!fgets(str, sizeof(str), fp) || feof(fp)) return 0;
-    if(!strncmp(&str[2], "Only one material", 17) ||
-       !strncmp(&str[2], "Only one subdomain", 18)){
-      if(!strncmp(&str[37], "dpTRUE", 6) || !strncmp(&str[37], "true", 4) ||
-         !strncmp(&str[36], "dpTRUE", 6) || !strncmp(&str[36], "true", 4)){
-        several_subdomains = false;
-      }
-      else{
-        several_subdomains = true;
-      }
-      Msg::Info("several_subdomains %x %s", several_subdomains, str);
-    }
-
-    int nbi;
-    std::vector<int> bi;
-    if(!fgets(str, sizeof(str), fp) || feof(fp)) return 0;
-    while(strstr(str, "Boundary indicators:") == NULL &&
-          strstr(str, "boundary indicators:") == NULL){
-      if(!fgets(str, sizeof(str), fp) || feof(fp))
-        break;
-    }
-    if(sscanf(str, "%d %*s %*s", &nbi) != 1) return 0;
-    Msg::Info("nbi %d", nbi);
-    if(nbi != 0)
-      bi.resize(nbi);
-    std::string format_read_bi = "%*d %*s %*s";
-    for(int i = 0; i < nbi; i++){
-      if(format_read_bi[format_read_bi.size()-1] == 'd') {
-        format_read_bi[format_read_bi.size()-1] = '*';
-        format_read_bi += "d %d";
-      }
-      else
-        format_read_bi += " %d";
-      if(sscanf(str, format_read_bi.c_str(), &bi[i]) != 1) return 0;
-      Msg::Info("bi[%d]=%d", i, bi[i]);
-    }
-
-    while(str[0] != '#'){
-      if(!fgets(str, sizeof(str), fp) || feof(fp))
-        break;
-    }
-    vertexVector.clear();
-    vertexMap.clear();
-    int minVertex = numVertices + 1, maxVertex = -1;
-    int num = 0;
-    std::vector<std::vector<int> > elementary(numVertices);
-
-    Msg::ResetProgressMeter();
-    for(int i = 0; i < numVertices; i++){
-      if(!fgets(str, sizeof(str), fp)) return 0;
-      double xyz[3];
-      int tmp;
-      if(sscanf(str, "%d ( %lf , %lf , %lf ) [%d]", &num,
-                &xyz[0], &xyz[1], &xyz[2], &tmp) != 5) return 0;
-      elementary[i].resize(tmp + 1);
-      elementary[i][0] = tmp;
-      minVertex = std::min(minVertex, num);
-      maxVertex = std::max(maxVertex, num);
-      if(vertexMap.count(num))
-        Msg::Warning("Skipping duplicate vertex %d", num);
-      else
-        vertexMap[num] = new MVertex(xyz[0], xyz[1], xyz[2], 0, num);
-      if(numVertices > 100000)
-        Msg::ProgressMeter(i + 1, numVertices, true, "Reading nodes");
-      // If the vertex numbering is dense, tranfer the map into a
-      // vector to speed up element creation
-      if((int)vertexMap.size() == numVertices &&
-         ((minVertex == 1 && maxVertex == numVertices) ||
-          (minVertex == 0 && maxVertex == numVertices - 1))){
-        Msg::Info("Vertex numbering is dense");
-        vertexVector.resize(vertexMap.size() + 1);
-        if(minVertex == 1)
-          vertexVector[0] = 0;
-        else
-          vertexVector[numVertices] = 0;
-        std::map<int, MVertex*>::const_iterator it = vertexMap.begin();
-        for(; it != vertexMap.end(); ++it)
-          vertexVector[it->first] = it->second;
-        vertexMap.clear();
-      }
-      Msg::Info("%d ( %lf , %lf , %lf ) [%d]",i, xyz[0], xyz[1], xyz[2],
-                elementary[i][0]);
-      std::string format_read_bi = "%*d ( %*lf , %*lf , %*lf ) [%*d]";
-      for(int j = 0; j < elementary[i][0]; j++){
-        if(format_read_bi[format_read_bi.size() - 1] == 'd') {
-          format_read_bi[format_read_bi.size() - 1] = '*';
-          format_read_bi += "d %d";
-        }
-        else
-          format_read_bi += " %d";
-        if(sscanf(str, format_read_bi.c_str(), &(elementary[i][j + 1])) != 1)
-          return 0;
-        Msg::Info("elementary[%d][%d]=%d", i + 1, j + 1, elementary[i][j + 1]);
-      }
-    }
-    while(str[0] != '#'){
-      if(!fgets(str, sizeof(str), fp) || feof(fp))
-        break;
-    }
-    std::vector<int> material(numElements);
-    std::vector<std::vector<int> > ElementsNodes(numElements);
-    for(int i = 0; i < numElements; i++){
-      ElementsNodes[i].resize(numVerticesPerElement);
-    }
-    char eleTypec[20]="";
-    std::string eleType;
-    Msg::ResetProgressMeter();
-    std::vector<int> mapping;
-    for(int i = 1; i <= numElements; i++){
-      if(!fgets(str, sizeof(str), fp)) return 0;
-      int num = 0, type, physical = 0, partition = 0;
-      int indices[60];
-      if(sscanf(str, "%*d %s %d", eleTypec, &material[i-1]) != 2) return 0;
-      eleType = std::string(eleTypec);
-      int k2; // local number for the element
-      int NoVertices; // number of vertices per element
-      if(eleType == "ElmT3n2D"){
-        NoVertices = 3;
-        static int map[3] = {0, 1, 2}; // identical to gmsh
-        mapping=std::vector<int>(map, map + sizeof(map) / sizeof(int));
-        type = MSH_TRI_3;
-      }
-      else if(eleType == "ElmT6n2D"){
-        NoVertices = 6;
-        static int map[6] = {0, 1, 2, 3, 4, 5}; // identical to gmsh
-        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
-        type = MSH_TRI_6;
-      }
-      else if(eleType == "ElmB4n2D"){
-        NoVertices = 4;
-        static int map[4] = {0, 1, 3, 2}; // local numbering
-        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
-        type = MSH_QUA_4;
-      }
-      else if(eleType == "ElmB8n2D"){
-        NoVertices = 8;
-        static int map[8] = {0, 1, 3, 2, 4, 6, 7, 5}; // local numbering
-        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
-        type = MSH_QUA_8;
-      }
-      else if(eleType == "ElmB9n2D"){
-        NoVertices = 9;
-        static int map[9] = {0, 4, 1, 7, 8, 5, 3, 6, 2}; // local numbering
-        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
-        type = MSH_QUA_9;
-      }
-      else if(eleType == "ElmT4n3D"){
-        NoVertices = 4;
-        static int map[4] = {0, 1, 2, 3}; // identical to gmsh
-        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
-        type = MSH_TET_4;
-      }
-      else if(eleType == "ElmT10n3D"){
-        NoVertices = 10;
-        static int map[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // local numbering
-        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
-        type = MSH_TET_10;
-      }
-      else if(eleType == "ElmB8n3D"){
-        NoVertices = 8;
-        static int map[8] = {4, 5, 0, 1, 7, 6, 3, 2};
-        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
-        type = MSH_HEX_8;
-      }
-      else if(eleType == "ElmB20n3D"){
-        NoVertices = 20;
-        static int map[20] = {4, 5, 0, 1, 7, 6, 3, 2, 16, 8, 19, 13, 15, 12,
-                              14, 17, 18, 9, 11};
-        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
-        type = MSH_HEX_20;
-      }
-      else if(eleType == "ElmB27n3D"){
-        NoVertices = 27;
-        static int map[27] = {4, 16, 5, 10, 21, 12, 0, 8, 1, 17, 25, 18, 22,
-                              26, 23, 9, 20, 11, 7, 19, 6, 15, 24, 14, 3, 13, 2};
-        mapping = std::vector<int>(map, map + sizeof(map) / sizeof(int));
-        type = MSH_HEX_27;
-      }
-      else{
-        return 0;
-      }
-      std::string format_read_vertices = "%*d %*s %*d";
-      for(int k = 0; k < NoVertices; k++){
-        if(format_read_vertices[format_read_vertices.size()-2] != '*') {
-          format_read_vertices[format_read_vertices.size()-1] = '*';
-          format_read_vertices += "d %d";
-        }
-        else
-          format_read_vertices += " %d";
-        k2 = mapping[k];
-        if(sscanf(str, format_read_vertices.c_str(), &ElementsNodes[i-1][k2]) != 1)
-          return 0;
-      }
-      mapping.clear();
-      for(int j = 0; j < NoVertices; j++)
-        indices[j] = ElementsNodes[i - 1][j];
-      std::vector<MVertex*> vertices;
-      if(vertexVector.size()){
-        if(!getVertices(numVerticesPerElement, indices, vertexVector, vertices))
-          return 0;
-      }
-      else{
-        if(!getVertices(numVerticesPerElement, indices, vertexMap, vertices))
-          return 0;
-      }
-      createElementMSH(this, num, type, physical, elementary[i-1][1], partition,
-                       vertices, elements, physicals);
-      // trouble if elementary[i-1][0]>1 nodal post-processing needed ?
-      if(numElements > 100000)
-        Msg::ProgressMeter(i + 1, numElements, true, "Reading elements");
-    }
-  }
-
-  // store the elements in their associated elementary entity. If the
-  // entity does not exist, create a new (discrete) one.
-  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
-    _storeElementsInEntities(elements[i]);
-
-  // associate the correct geometrical entity with each mesh vertex
-  _associateEntityWithMeshVertices();
-
-  // store the vertices in their associated geometrical entity
-  if(vertexVector.size())
-    _storeVerticesInEntities(vertexVector);
-  else
-    _storeVerticesInEntities(vertexMap);
-
-  // store the physical tags
-  for(int i = 0; i < 4; i++)
-    _storePhysicalTagsInEntities(i, physicals[i]);
-
-  fclose(fp);
-  return 1;
-}
-
-int GModel::writeDIFF(const std::string &name, bool binary, bool saveAll,
-                      double scalingFactor)
-{
-  if(binary){
-    Msg::Error("Binary DIFF output is not implemented");
-    return 0;
-  }
-
-  FILE *fp = fopen(name.c_str(), binary ? "wb" : "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  // get the number of vertices and index the vertices in a continuous
-  // sequence
-  int numVertices = indexMeshVertices(saveAll);
-
-  // tag the vertices according to which surface they belong to (Note
-  // that we use a brute force approach here, so that we can deal with
-  // models with incomplete topology. For example, when we merge 2 STL
-  // triangulations we don't have the boundary information between the
-  // faces, and the vertices would end up categorized on either one.)
-  std::vector<std::list<int> > vertexTags(numVertices);
-  std::list<int> boundaryIndicators;
-  for(riter it = firstRegion(); it != lastRegion(); it++){
-    std::list<GFace*> faces = (*it)->faces();
-    for(std::list<GFace*>::iterator itf = faces.begin(); itf != faces.end(); itf++){
-      GFace *gf = *itf;
-      boundaryIndicators.push_back(gf->tag());
-      for(unsigned int i = 0; i < gf->getNumMeshElements(); i++){
-        MElement *e = gf->getMeshElement(i);
-        for(int j = 0; j < e->getNumVertices(); j++){
-          MVertex *v = e->getVertex(j);
-          if(v->getIndex() > 0)
-            vertexTags[v->getIndex() - 1].push_back(gf->tag());
-        }
-      }
-    }
-  }
-  boundaryIndicators.sort();
-  boundaryIndicators.unique();
-  for(int i = 0; i < numVertices; i++){
-    vertexTags[i].sort();
-    vertexTags[i].unique();
-  }
-
-  // get all the entities in the model
-  std::vector<GEntity*> entities;
-  getEntities(entities);
-
-  // find max dimension of mesh elements we need to save
-  int dim = 0;
-  for(unsigned int i = 0; i < entities.size(); i++)
-    if(entities[i]->physicals.size() || saveAll)
-      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++)
-        dim = std::max(dim, entities[i]->getMeshElement(j)->getDim());
-
-  // loop over all elements we need to save
-  int numElements = 0, maxNumNodesPerElement = 0;
-  for(unsigned int i = 0; i < entities.size(); i++){
-    if(entities[i]->physicals.size() || saveAll){
-      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
-        MElement *e = entities[i]->getMeshElement(j);
-        if(e->getStringForDIFF() && e->getDim() == dim){
-          numElements++;
-          maxNumNodesPerElement = std::max(maxNumNodesPerElement, e->getNumVertices());
-        }
-      }
-    }
-  }
-
-  fprintf(fp, "\n\n");
-  fprintf(fp, " Finite element mesh (GridFE):\n\n");
-  fprintf(fp, " Number of space dim. =   3\n");
-  fprintf(fp, " Number of elements   =  %d\n", numElements);
-  fprintf(fp, " Number of nodes      =  %d\n\n", numVertices);
-  fprintf(fp, " All elements are of the same type : dpTRUE\n");
-  fprintf(fp, " Max number of nodes in an element: %d \n", maxNumNodesPerElement);
-  fprintf(fp, " Only one subdomain               : dpFALSE\n");
-  fprintf(fp, " Lattice data                     ? 0\n\n\n\n");
-  fprintf(fp, " %d Boundary indicators:  ", (int)boundaryIndicators.size());
-  for(std::list<int>::iterator it = boundaryIndicators.begin();
-      it != boundaryIndicators.end(); it++)
-    fprintf(fp, " %d", *it);
-
-  fprintf(fp, "\n\n\n");
-  fprintf(fp,"  Nodal coordinates and nodal boundary indicators,\n");
-  fprintf(fp,"  the columns contain:\n");
-  fprintf(fp,"   - node number\n");
-  fprintf(fp,"   - coordinates\n");
-  fprintf(fp,"   - no of boundary indicators that are set (ON)\n");
-  fprintf(fp,"   - the boundary indicators that are set (ON) if any.\n");
-  fprintf(fp,"#\n");
-
-  // write mesh vertices
-  for(unsigned int i = 0; i < entities.size(); i++){
-    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++){
-      MVertex *v = entities[i]->mesh_vertices[j];
-      if(v->getIndex() > 0){
-        v->writeDIFF(fp, binary, scalingFactor);
-        fprintf(fp, " [%d] ", (int)vertexTags[v->getIndex() - 1].size());
-        for(std::list<int>::iterator it = vertexTags[v->getIndex() - 1].begin();
-            it != vertexTags[v->getIndex() - 1].end(); it++)
-          fprintf(fp," %d ", *it);
-        fprintf(fp,"\n");
-      }
-    }
-  }
-
-  fprintf(fp, "\n");
-  fprintf(fp, "\n");
-  fprintf(fp,     "  Element types and connectivity\n");
-  fprintf(fp,     "  the columns contain:\n");
-  fprintf(fp,     "   - element number\n");
-  fprintf(fp,     "   - element type\n");
-  fprintf(fp,     "   - subdomain number \n");
-  fprintf(fp,     "   - the global node numbers of the nodes in the element.\n");
-  fprintf(fp,     "#\n");
-
-  // write mesh elements
-  int num = 0;
-  for(unsigned int i = 0; i < entities.size(); i++){
-    if(entities[i]->physicals.size() || saveAll){
-      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
-        MElement *e = entities[i]->getMeshElement(j);
-        if(e->getStringForDIFF() && e->getDim() == dim)
-          e->writeDIFF(fp, ++num, binary, entities[i]->tag());
-      }
-    }
-  }
-  fprintf(fp, "\n");
-
-  fclose(fp);
-  return 1;
-}
-
-template <class T>
-static void writeElementsINP(FILE *fp, GEntity *ge, std::vector<T*> &elements,
-                             bool saveAll, int &ne)
-{
-  if(elements.size() && (saveAll || ge->physicals.size())){
-    const char *typ = elements[0]->getStringForINP();
-    if(typ){
-      int np = (saveAll ? 1 : ge->physicals.size());
-      for(int p = 0; p < np; p++){
-        int part = (saveAll ? ge->tag() : ge->physicals[p]);
-        const char *str = (ge->dim() == 3) ? "VOLUME" : (ge->dim() == 2) ?
-          "SURFACE" : (ge->dim() == 1) ? "CURVE" : "POINT";
-        fprintf(fp, "*Element, type=%s, ELSET=%s%d\n", typ, str, part);
-        for(unsigned int i = 0; i < elements.size(); i++)
-          elements[i]->writeINP(fp, ne++);
-      }
-    }
-  }
-}
-
-int GModel::writeINP(const std::string &name, bool saveAll,
-                     double scalingFactor)
-{
-  FILE *fp = fopen(name.c_str(), "w");
-  if(!fp){
-    Msg::Error("Unable to open file '%s'", name.c_str());
-    return 0;
-  }
-
-  if(noPhysicalGroups()) saveAll = true;
-
-  indexMeshVertices(saveAll);
-  std::vector<GEntity*> entities;
-  getEntities(entities);
-
-  fprintf(fp, "*Heading\n");
-  fprintf(fp, " %s\n", name.c_str());
-
-  fprintf(fp, "*Node\n");
-  for(unsigned int i = 0; i < entities.size(); i++)
-    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
-      entities[i]->mesh_vertices[j]->writeINP(fp, scalingFactor);
-
-  int ne = 1;
-  for(viter it = firstVertex(); it != lastVertex(); ++it){
-    writeElementsINP(fp, *it, (*it)->points, saveAll, ne);
-  }
-  for(eiter it = firstEdge(); it != lastEdge(); ++it){
-    writeElementsINP(fp, *it, (*it)->lines, saveAll, ne);
-  }
-  for(fiter it = firstFace(); it != lastFace(); ++it){
-    writeElementsINP(fp, *it, (*it)->triangles, saveAll, ne);
-    writeElementsINP(fp, *it, (*it)->quadrangles, saveAll, ne);
-  }
-  for(riter it = firstRegion(); it != lastRegion(); ++it){
-    writeElementsINP(fp, *it, (*it)->tetrahedra, saveAll, ne);
-    writeElementsINP(fp, *it, (*it)->hexahedra, saveAll, ne);
-    writeElementsINP(fp, *it, (*it)->prisms, saveAll, ne);
-    writeElementsINP(fp, *it, (*it)->pyramids, saveAll, ne);
-  }
-
-  fclose(fp);
-  return 1;
-}
-
-GModel *GModel::createGModel(std::map<int, MVertex*> &vertexMap,
-                             std::vector<int> &elementNum,
-                             std::vector<std::vector<int> > &vertexIndices,
-                             std::vector<int> &elementType,
-                             std::vector<int> &physical,
-                             std::vector<int> &elementary,
-                             std::vector<int> &partition)
-{
-  GModel *gm = new GModel();
-  std::map<int, std::vector<MElement*> > elements[10];
-  std::map<int, std::map<int, std::string> > physicals[4];
-  std::vector<MVertex*> vertexVector;
-
-  int numVertices = (int)vertexMap.size();
-  int numElement = (int)elementNum.size();
-
-  if(numElement != (int)vertexIndices.size())
-    Msg::Error("Dimension in vertices numbers");
-  if(numElement != (int)elementType.size())
-    Msg::Error("Dimension in elementType numbers");
-  if(numElement != (int)physical.size())
-    Msg::Error("Dimension in physical numbers");
-  if(numElement != (int)elementary.size())
-    Msg::Error("Dimension in elementary numbers");
-  if(numElement != (int)partition.size())
-    Msg::Error("Dimension in partition numbers");
-
-  std::map<int, MVertex*>::const_iterator it = vertexMap.begin();
-  std::map<int, MVertex*>::const_iterator end = vertexMap.end();
-
-  int maxVertex = std::numeric_limits<int>::min();
-  int minVertex = std::numeric_limits<int>::max();
-  int num;
-
-  for(; it != end; ++it){
-    num = it->first;
-    minVertex = std::min(minVertex, num);
-    maxVertex = std::max(maxVertex, num);
-  }
-  if(minVertex == std::numeric_limits<int>::max())
-    Msg::Error("Could not determine the min index of vertices");
-
-  // If the vertex numbering is dense, transfer the map into a
-  // vector to speed up element creation
-  if((minVertex == 1 && maxVertex == numVertices) ||
-     (minVertex == 0 && maxVertex == numVertices - 1)){
-    Msg::Info("Vertex numbering is dense");
-    vertexVector.resize(vertexMap.size() + 1);
-    if(minVertex == 1)
-      vertexVector[0] = 0;
-    else
-      vertexVector[numVertices] = 0;
-    std::map<int, MVertex*>::const_iterator it = vertexMap.begin();
-    for(; it != vertexMap.end(); ++it)
-      vertexVector[it->first] = it->second;
-    vertexMap.clear();
-  }
-
-  int *indices;
-  int nbVertices;
-  for(int i = 0; i < numElement; ++i){
-    num = elementNum[i];
-    std::vector<MVertex*> vertices;
-    nbVertices = (int)vertexIndices[i].size();
-    indices = &vertexIndices[i][0];
-    if(vertexVector.size()){
-      if(!getVertices(nbVertices, indices, vertexVector, vertices)){
-        Msg::Error("Vertex not found aborting");
-        delete gm;
-        return 0;
-      }
-    }
-    else{
-      if(!getVertices(nbVertices, indices, vertexMap, vertices)){
-        Msg::Error("Vertex not found aborting");
-        delete gm;
-        return 0;
-      }
-    }
-
-    createElementMSH(gm, num, elementType[i], physical[i], elementary[i],
-                     partition[i], vertices, elements, physicals);
-  }
-
-  // store the elements in their associated elementary entity. If the
-  // entity does not exist, create a new (discrete) one.
-  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
-    gm->_storeElementsInEntities(elements[i]);
-
-  // associate the correct geometrical entity with each mesh vertex
-  gm->_associateEntityWithMeshVertices();
-
-  // store the vertices in their associated geometrical entity
-  if(vertexVector.size())
-    gm->_storeVerticesInEntities(vertexVector);
-  else
-    gm->_storeVerticesInEntities(vertexMap);
-
-  // store the physical tags
-  for(int i = 0; i < 4; i++)
-    gm->_storePhysicalTagsInEntities(i, physicals[i]);
-
-  return gm;
-}
-
-GModel *GModel::createGModel
-(std::map<int, std::vector<MElement*> > &entityToElementsMap,
- std::map<int, std::vector<int> > &entityToPhysicalsMap)
-{
-  GModel* gm = new GModel();
-
-  std::map<int, MVertex*> vertexMap;
-  std::map<int, std::map<int, std::string> > physicals[4];
-  for(std::map<int, std::vector<MElement*> >::iterator it =
-        entityToElementsMap.begin(); it != entityToElementsMap.end();
-      it++) {
-    int entity = it->first;
-    for(unsigned int iE = 0; iE < it->second.size(); iE++) {
-      MElement* me = it->second[iE];
-      for(int iV = 0; iV < me->getNumVertices(); iV++) {
-        vertexMap[me->getVertex(iV)->getNum()] = me->getVertex(iV);
-      }
-      if(me->getPartition()) {
-        gm->getMeshPartitions().insert(me->getPartition());
-      }
-      std::vector<int> entityPhysicals = entityToPhysicalsMap[entity];
-      for(unsigned int i = 0; i < entityPhysicals.size(); i++) {
-        physicals[me->getDim()][entity][entityPhysicals[i]] = "unnamed";
-      }
-    }
-  }
-
-  gm->_storeElementsInEntities(entityToElementsMap);
-  gm->_associateEntityWithMeshVertices();
-  gm->_storeVerticesInEntities(vertexMap);
-  for(int i = 0; i < 4; i++)
-    gm->_storePhysicalTagsInEntities(i, physicals[i]);
-
-  return gm;
-}
diff --git a/Geo/GModelIO_P3D.cpp b/Geo/GModelIO_P3D.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2fcfd3d49af7032d923d672bda2c9da4f9ef75f
--- /dev/null
+++ b/Geo/GModelIO_P3D.cpp
@@ -0,0 +1,186 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "MQuadrangle.h"
+#include "MHexahedron.h"
+#include "discreteFace.h"
+#include "discreteRegion.h"
+
+int GModel::readP3D(const std::string &name)
+{
+  FILE *fp = fopen(name.c_str(), "r");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  int numBlocks = 0;
+  if(fscanf(fp, "%d", &numBlocks) != 1 || numBlocks <= 0) return 0;
+
+  std::vector<int> Ni(numBlocks), Nj(numBlocks), Nk(numBlocks);
+  for(int n = 0; n < numBlocks; n++)
+    if(fscanf(fp, "%d %d %d", &Ni[n], &Nj[n], &Nk[n]) != 3) return 0;
+
+  for(int n = 0; n < numBlocks; n++){
+    if(Nk[n] == 1){
+      GFace *gf = new discreteFace(this, getMaxElementaryNumber(2) + 1);
+      add(gf);
+      gf->transfinite_vertices.resize(Ni[n]);
+      for(int i = 0; i < Ni[n]; i++)
+        gf->transfinite_vertices[i].resize(Nj[n]);
+      for(int coord = 0; coord < 3; coord++){
+        for(int i = 0; i < Ni[n]; i++){
+          for(int j = 0; j < Nj[n]; j++){
+            double d;
+            if(fscanf(fp, "%lf", &d) != 1) return 0;
+            if(coord == 0){
+              MVertex *v = new MVertex(d, 0., 0., gf);
+              gf->transfinite_vertices[i][j] = v;
+              gf->mesh_vertices.push_back(v);
+            }
+            else if(coord == 1){
+              gf->transfinite_vertices[i][j]->y() = d;
+            }
+            else if(coord == 2){
+              gf->transfinite_vertices[i][j]->z() = d;
+            }
+          }
+        }
+      }
+      for(unsigned int i = 0; i < gf->transfinite_vertices.size() - 1; i++)
+        for(unsigned int j = 0; j < gf->transfinite_vertices[0].size() - 1; j++)
+          gf->quadrangles.push_back
+            (new MQuadrangle(gf->transfinite_vertices[i    ][j    ],
+                             gf->transfinite_vertices[i + 1][j    ],
+                             gf->transfinite_vertices[i + 1][j + 1],
+                             gf->transfinite_vertices[i    ][j + 1]));
+    }
+    else{
+      GRegion *gr = new discreteRegion(this,  getMaxElementaryNumber(3) + 1);
+      add(gr);
+      gr->transfinite_vertices.resize(Ni[n]);
+      for(int i = 0; i < Ni[n]; i++){
+        gr->transfinite_vertices[i].resize(Nj[n]);
+        for(int j = 0; j < Nj[n]; j++){
+          gr->transfinite_vertices[i][j].resize(Nk[n]);
+        }
+      }
+      for(int coord = 0; coord < 3; coord++){
+        for(int i = 0; i < Ni[n]; i++){
+          for(int j = 0; j < Nj[n]; j++){
+            for(int k = 0; k < Nk[n]; k++){
+              double d;
+              if(fscanf(fp, "%lf", &d) != 1) return 0;
+              if(coord == 0){
+                MVertex *v = new MVertex(d, 0., 0., gr);
+                gr->transfinite_vertices[i][j][k] = v;
+                gr->mesh_vertices.push_back(v);
+              }
+              else if(coord == 1){
+                gr->transfinite_vertices[i][j][k]->y() = d;
+              }
+              else if(coord == 2){
+                gr->transfinite_vertices[i][j][k]->z() = d;
+              }
+            }
+          }
+        }
+      }
+      for(unsigned int i = 0; i < gr->transfinite_vertices.size() - 1; i++)
+        for(unsigned int j = 0; j < gr->transfinite_vertices[0].size() - 1; j++)
+          for(unsigned int k = 0; k < gr->transfinite_vertices[0][0].size() - 1; k++)
+            gr->hexahedra.push_back
+              (new MHexahedron(gr->transfinite_vertices[i    ][j    ][k    ],
+                               gr->transfinite_vertices[i + 1][j    ][k    ],
+                               gr->transfinite_vertices[i + 1][j + 1][k    ],
+                               gr->transfinite_vertices[i    ][j + 1][k    ],
+                               gr->transfinite_vertices[i    ][j    ][k + 1],
+                               gr->transfinite_vertices[i + 1][j    ][k + 1],
+                               gr->transfinite_vertices[i + 1][j + 1][k + 1],
+                               gr->transfinite_vertices[i    ][j + 1][k + 1]));
+    }
+  }
+
+  fclose(fp);
+  return 1;
+}
+
+int GModel::writeP3D(const std::string &name, bool saveAll, double scalingFactor)
+{
+  FILE *fp = fopen(name.c_str(), "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  if(noPhysicalGroups()) saveAll = true;
+
+  std::vector<GFace*> faces;
+  for(fiter it = firstFace(); it != lastFace(); ++it)
+    if((*it)->transfinite_vertices.size() &&
+       (*it)->transfinite_vertices[0].size() &&
+       ((*it)->physicals.size() || saveAll)) faces.push_back(*it);
+
+  std::vector<GRegion*> regions;
+  for(riter it = firstRegion(); it != lastRegion(); ++it)
+    if((*it)->transfinite_vertices.size() &&
+       (*it)->transfinite_vertices[0].size() &&
+       (*it)->transfinite_vertices[0][0].size() &&
+       ((*it)->physicals.size() || saveAll)) regions.push_back(*it);
+
+  if(faces.empty() && regions.empty()){
+    Msg::Warning("No structured grids to save");
+    fclose(fp);
+    return 0;
+  }
+
+  fprintf(fp, "%d\n", (int)(faces.size() + regions.size()));
+
+  for(unsigned int i = 0; i < faces.size(); i++)
+    fprintf(fp, "%d %d 1\n",
+            (int)faces[i]->transfinite_vertices.size(),
+            (int)faces[i]->transfinite_vertices[0].size());
+
+  for(unsigned int i = 0; i < regions.size(); i++)
+    fprintf(fp, "%d %d %d\n",
+            (int)regions[i]->transfinite_vertices.size(),
+            (int)regions[i]->transfinite_vertices[0].size(),
+            (int)regions[i]->transfinite_vertices[0][0].size());
+
+  for(unsigned int i = 0; i < faces.size(); i++){
+    GFace *gf = faces[i];
+    for(int coord = 0; coord < 3; coord++){
+      for(unsigned int j = 0; j < gf->transfinite_vertices.size(); j++){
+        for(unsigned int k = 0; k < gf->transfinite_vertices[j].size(); k++){
+          MVertex *v = gf->transfinite_vertices[j][k];
+          double d = (coord == 0) ? v->x() : (coord == 1) ? v->y() : v->z();
+          fprintf(fp, "%.16g ", d * scalingFactor);
+        }
+        fprintf(fp, "\n");
+      }
+    }
+  }
+
+  for(unsigned int i = 0; i < regions.size(); i++){
+    GRegion *gr = regions[i];
+    for(int coord = 0; coord < 3; coord++){
+      for(unsigned int j = 0; j < gr->transfinite_vertices.size(); j++){
+        for(unsigned int k = 0; k < gr->transfinite_vertices[j].size(); k++){
+          for(unsigned int l = 0; l < gr->transfinite_vertices[j][k].size(); l++){
+            MVertex *v = gr->transfinite_vertices[j][k][l];
+            double d = (coord == 0) ? v->x() : (coord == 1) ? v->y() : v->z();
+            fprintf(fp, "%.16g ", d * scalingFactor);
+          }
+          fprintf(fp, "\n");
+        }
+      }
+    }
+  }
+
+  fclose(fp);
+  return 1;
+}
+
diff --git a/Geo/GModelIO_PLY.cpp b/Geo/GModelIO_PLY.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..44bd8817719aa0591f6123de3276bed11a32a15e
--- /dev/null
+++ b/Geo/GModelIO_PLY.cpp
@@ -0,0 +1,269 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GmshConfig.h"
+#include "GModel.h"
+#include "MTriangle.h"
+#include "OS.h"
+
+#if defined(HAVE_POST)
+#include "PView.h"
+#include "PViewDataList.h"
+#endif
+
+static bool getVertices(int num, int *indices, std::vector<MVertex*> &vec,
+                        std::vector<MVertex*> &vertices, int minVertex = 0)
+{
+  for(int i = 0; i < num; i++){
+    if(indices[i] < minVertex || indices[i] > (int)(vec.size() - 1 + minVertex)){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(vec[indices[i]]);
+  }
+  return true;
+}
+
+static void replaceCommaByDot(const std::string name)
+{
+  char myCommand[1000], myCommand2[1000];
+  sprintf(myCommand, "sed 's/,/./g' %s > temp.txt", name.c_str());
+  SystemCall(myCommand, true);
+  sprintf(myCommand2, "mv temp.txt %s ", name.c_str());
+  SystemCall(myCommand2, true);
+}
+
+static bool getProperties(int num, int *indices, std::vector<double> &vec,
+                        std::vector<double> &properties)
+{
+  for(int i = 0; i < num; i++){
+    if(indices[i] < 0 || indices[i] > (int)(vec.size() - 1)){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      properties.push_back(vec[indices[i]]);
+  }
+  return true;
+}
+
+int GModel::readPLY(const std::string &name)
+{
+  // this is crazy!?
+  replaceCommaByDot(name);
+
+  FILE *fp = fopen(name.c_str(), "r");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  std::vector<MVertex*> vertexVector;
+  std::map<int, std::vector<MElement*> > elements[5];
+  std::map<int, std::vector<double> > properties;
+
+  char buffer[256], str[256], str2[256], str3[256];
+  std::string s1;
+  int elementary = getMaxElementaryNumber(-1) + 1;
+  int nbv, nbf;
+  int nbprop = 0;
+  int nbView = 0;
+  std::vector<std::string> propName;
+  while(!feof(fp)) {
+    if(!fgets(buffer, sizeof(buffer), fp)) break;
+    if(buffer[0] != '#'){ // skip comments
+      sscanf(buffer, "%s %s", str, str2);
+      if(!strcmp(str, "element") && !strcmp(str2, "vertex")){
+	sscanf(buffer, "%s %s %d", str, str2, &nbv);
+      }
+      if(!strcmp(str, "format") && strcmp(str2, "ascii")){
+	Msg::Error("Only reading of ascii PLY files implemented");
+	return 0;
+      }
+      if(!strcmp(str, "property") && strcmp(str2, "list")){
+	nbprop++;
+	sscanf(buffer, "%s %s %s", str, str2, str3);
+	if (nbprop > 3) propName.push_back(s1+str3);
+      }
+      if(!strcmp(str, "element") && !strcmp(str2, "face")){
+	sscanf(buffer, "%s %s %d", str, str2, &nbf);
+      }
+      if(!strcmp(str, "end_header")){
+	nbView = nbprop -3;
+	Msg::Info("%d elements", nbv);
+	Msg::Info("%d triangles", nbf);
+	Msg::Info("%d properties", nbView);
+
+	vertexVector.resize(nbv);
+	for(int i = 0; i < nbv; i++) {
+	  double x,y,z;
+	  char line[10000], *pEnd, *pEnd2, *pEnd3;
+	  if(!fgets(line, sizeof(line), fp)) return 0;
+	  x = strtod(line, &pEnd);
+	  y = strtod(pEnd, &pEnd2);
+	  z = strtod(pEnd2, &pEnd3);
+	  vertexVector[i] = new MVertex(x, y, z);
+
+	  pEnd = pEnd3;
+          std::vector<double> prop(nbView);
+	  for (int k = 0; k < nbView; k++){
+	    prop[k]=strtod(pEnd, &pEnd2);
+	    pEnd = pEnd2;
+	    properties[k].push_back(prop[k]);
+	  }
+	}
+
+	for(int i = 0; i < nbf; i++) {
+	  if(!fgets(buffer, sizeof(buffer), fp)) break;
+	  int n[3], nbe;
+	  sscanf(buffer, "%d %d %d %d", &nbe, &n[0], &n[1], &n[2]);
+	  std::vector<MVertex*> vertices;
+	  if(!getVertices(3, n, vertexVector, vertices)) return 0;
+	  elements[0][elementary].push_back(new MTriangle(vertices));
+	}
+
+      }
+
+    }
+  }
+
+  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
+    _storeElementsInEntities(elements[i]);
+  _associateEntityWithMeshVertices();
+  _storeVerticesInEntities(vertexVector);
+
+#if defined(HAVE_POST)
+  // create PViews here
+  std::vector<GEntity*> _entities;
+  getEntities(_entities);
+  for (int iV=0; iV< nbView; iV++){
+    PView *view = new PView();
+    PViewDataList *data = dynamic_cast<PViewDataList*>(view->getData());
+    for(unsigned int ii = 0; ii < _entities.size(); ii++){
+	for(unsigned int i = 0; i < _entities[ii]->getNumMeshElements(); i++){
+	  MElement *e = _entities[ii]->getMeshElement(i);
+	  int numNodes = e->getNumVertices();
+	  std::vector<double> x(numNodes), y(numNodes), z(numNodes);
+	  std::vector<double> *out = data->incrementList(1, e->getType());
+	  for(int nod = 0; nod < numNodes; nod++) out->push_back((e->getVertex(nod))->x());
+	  for(int nod = 0; nod < numNodes; nod++) out->push_back((e->getVertex(nod))->y());
+	  for(int nod = 0; nod < numNodes; nod++) out->push_back((e->getVertex(nod))->z());
+	  std::vector<double> props;
+	  int n[3];
+	  n[0] = e->getVertex(0)->getNum()-1;
+	  n[1] = e->getVertex(1)->getNum()-1;
+	  n[2] = e->getVertex(2)->getNum()-1;
+	  if(!getProperties(3, n, properties[iV], props)) return 0;
+	  for(int nod = 0; nod < numNodes; nod++) out->push_back(props[nod]);
+	}
+    }
+    data->setName(propName[iV]);
+    data->Time.push_back(0);
+    data->setFileName("property.pos");
+    data->finalize();
+  }
+#endif
+
+  fclose(fp);
+
+  return 1;
+}
+
+int GModel::readPLY2(const std::string &name)
+{
+  FILE *fp = fopen(name.c_str(), "r");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  std::vector<MVertex*> vertexVector;
+  std::map<int, std::vector<MElement*> > elements[5];
+
+  char buffer[256];
+  int elementary = getMaxElementaryNumber(-1) + 1;
+  int nbv, nbf;
+  while(!feof(fp)) {
+    if(!fgets(buffer, sizeof(buffer), fp)) break;
+    if(buffer[0] != '#'){ // skip comments
+      sscanf(buffer, "%d", &nbv);
+      if(!fgets(buffer, sizeof(buffer), fp)) break;
+      sscanf(buffer, "%d", &nbf);
+      Msg::Info("%d vertices", nbv);
+      Msg::Info("%d triangles", nbf);
+      vertexVector.resize(nbv);
+      for(int i = 0; i < nbv; i++) {
+	if(!fgets(buffer, sizeof(buffer), fp)) break;
+	double x, y, z;
+	int nb = sscanf(buffer, "%lf %lf %lf", &x, &y, &z);
+	if (nb !=3){
+	  if(!fgets(buffer, sizeof(buffer), fp)) break;
+	  sscanf(buffer, "%lf",  &y);
+	  if(!fgets(buffer, sizeof(buffer), fp)) break;
+	  sscanf(buffer, "%lf",  &z);
+	}
+	vertexVector[i] = new MVertex(x, y, z);
+      }
+      for(int i = 0; i < nbf; i++) {
+	if(!fgets(buffer, sizeof(buffer), fp)) break;
+	int n[3], nbe;
+	int nb = sscanf(buffer, "%d %d %d %d", &nbe, &n[0], &n[1], &n[2]);
+	if (nb !=4){
+	  if(!fgets(buffer, sizeof(buffer), fp)) break;
+	  sscanf(buffer, "%d",  &n[0]);
+	  if(!fgets(buffer, sizeof(buffer), fp)) break;
+	  sscanf(buffer, "%d",  &n[1]);
+	  if(!fgets(buffer, sizeof(buffer), fp)) break;
+	  sscanf(buffer, "%d",  &n[2]);
+	}
+	std::vector<MVertex*> vertices;
+	if(!getVertices(3, n, vertexVector, vertices)) return 0;
+	elements[0][elementary].push_back(new MTriangle(vertices));
+      }
+    }
+  }
+
+  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
+    _storeElementsInEntities(elements[i]);
+  _associateEntityWithMeshVertices();
+  _storeVerticesInEntities(vertexVector);
+
+  fclose(fp);
+  return 1;
+}
+
+int GModel::writePLY2(const std::string &name)
+{
+  FILE *fp = fopen(name.c_str(), "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  int numVertices = indexMeshVertices(true);
+  int numTriangles = 0;
+  for(fiter it = firstFace(); it != lastFace(); ++it){
+    numTriangles += (*it)->triangles.size();
+  }
+
+  fprintf(fp, "%d\n", numVertices);
+  fprintf(fp, "%d\n", numTriangles);
+
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+  for(unsigned int i = 0; i < entities.size(); i++)
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
+      entities[i]->mesh_vertices[j]->writePLY2(fp);
+
+  for(fiter it = firstFace(); it != lastFace(); ++it){
+      for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
+        (*it)->triangles[i]->writePLY2(fp);
+  }
+
+  fclose(fp);
+  return 1;
+}
+
diff --git a/Geo/GModelIO_STL.cpp b/Geo/GModelIO_STL.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4e3a4f48b27598edfc1fa6a824852ce91d3b0618
--- /dev/null
+++ b/Geo/GModelIO_STL.cpp
@@ -0,0 +1,216 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include <stdio.h>
+#include "GModel.h"
+#include "MLine.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+#include "MVertexPositionSet.h"
+#include "discreteFace.h"
+#include "StringUtils.h"
+
+int GModel::readSTL(const std::string &name, double tolerance)
+{
+  FILE *fp = fopen(name.c_str(), "rb");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  // store triplets of points for each solid found in the file
+  std::vector<std::vector<SPoint3> > points;
+  SBoundingBox3d bbox;
+
+  // "solid", or binary data header
+  char buffer[256];
+  if(!fgets(buffer, sizeof(buffer), fp)) return 0;
+
+  bool binary = strncmp(buffer, "solid", 5);
+
+  // ASCII STL
+  if(!binary){
+    points.resize(1);
+    while(!feof(fp)) {
+      // "facet normal x y z" or "endsolid"
+      if(!fgets(buffer, sizeof(buffer), fp)) break;
+      if(!strncmp(buffer, "endsolid", 8)){
+        // "solid"
+        if(!fgets(buffer, sizeof(buffer), fp)) break;
+        if(!strncmp(buffer, "solid", 5)){
+          points.resize(points.size() + 1);
+          // "facet normal x y z"
+          if(!fgets(buffer, sizeof(buffer), fp)) break;
+        }
+      }
+      // "outer loop"
+      if(!fgets(buffer, sizeof(buffer), fp)) break;
+      // "vertex x y z"
+      for(int i = 0; i < 3; i++){
+        if(!fgets(buffer, sizeof(buffer), fp)) break;
+        char s1[256];
+        double x, y, z;
+        if(sscanf(buffer, "%s %lf %lf %lf", s1, &x, &y, &z) != 4) break;
+        SPoint3 p(x, y, z);
+        points.back().push_back(p);
+        bbox += p;
+      }
+      // "endloop"
+      if(!fgets(buffer, sizeof(buffer), fp)) break;
+      // "endfacet"
+      if(!fgets(buffer, sizeof(buffer), fp)) break;
+    }
+   }
+
+  // check if we could parse something
+  bool empty = true;
+  for(unsigned int i = 0; i < points.size(); i++){
+    if(points[i].size()){
+      empty = false;
+      break;
+    }
+  }
+  if(empty) points.clear();
+
+  // binary STL (we also try to read in binary mode if the header told
+  // us the format was ASCII but we could not read any vertices)
+  if(binary || empty){
+    if(binary)
+      Msg::Info("Mesh is in binary format");
+    else
+      Msg::Info("Wrong ASCII header or empty file: trying binary read");
+    rewind(fp);
+    while(!feof(fp)) {
+      char header[80];
+      if(!fread(header, sizeof(char), 80, fp)) break;
+      unsigned int nfacets = 0;
+      size_t ret = fread(&nfacets, sizeof(unsigned int), 1, fp);
+      bool swap = false;
+      if(nfacets > 100000000){
+        Msg::Info("Swapping bytes from binary file");
+        swap = true;
+        SwapBytes((char*)&nfacets, sizeof(unsigned int), 1);
+      }
+      if(ret && nfacets){
+        points.resize(points.size() + 1);
+        char *data = new char[nfacets * 50 * sizeof(char)];
+        ret = fread(data, sizeof(char), nfacets * 50, fp);
+        if(ret == nfacets * 50){
+          for(unsigned int i = 0; i < nfacets; i++) {
+            float *xyz = (float *)&data[i * 50 * sizeof(char)];
+            if(swap) SwapBytes((char*)xyz, sizeof(float), 12);
+            for(int j = 0; j < 3; j++){
+              SPoint3 p(xyz[3 + 3 * j], xyz[3 + 3 * j + 1], xyz[3 + 3 * j + 2]);
+              points.back().push_back(p);
+              bbox += p;
+            }
+          }
+        }
+        delete [] data;
+      }
+    }
+  }
+
+  std::vector<GFace*> faces;
+  for(unsigned int i = 0; i < points.size(); i++){
+    if(points[i].empty()){
+      Msg::Error("No facets found in STL file for solid %d", i);
+      return 0;
+    }
+    if(points[i].size() % 3){
+      Msg::Error("Wrong number of points (%d) in STL file for solid %d",
+                 points[i].size(), i);
+      return 0;
+    }
+    Msg::Info("%d facets in solid %d", points[i].size() / 3, i);
+    // create face
+    GFace *face = new discreteFace(this, getMaxElementaryNumber(2) + 1);
+    faces.push_back(face);
+    add(face);
+  }
+
+  // create triangles using unique vertices
+  double eps = norm(SVector3(bbox.max(), bbox.min())) * tolerance;
+  std::vector<MVertex*> vertices;
+  for(unsigned int i = 0; i < points.size(); i++)
+    for(unsigned int j = 0; j < points[i].size(); j++)
+      vertices.push_back(new MVertex(points[i][j].x(), points[i][j].y(),
+                                     points[i][j].z()));
+  MVertexPositionSet pos(vertices);
+
+  std::set<MFace,Less_Face> unique;
+  int nbDuplic = 0;
+  for(unsigned int i = 0; i < points.size(); i ++){
+    for(unsigned int j = 0; j < points[i].size(); j += 3){
+      MVertex *v[3];
+      for(int k = 0; k < 3; k++){
+        double x = points[i][j + k].x();
+        double y = points[i][j + k].y();
+        double z = points[i][j + k].z();
+        v[k] = pos.find(x, y, z, eps);
+      }
+      MFace mf (v[0], v[1], v[2]);
+      if (unique.find(mf) == unique.end()){
+	faces[i]->triangles.push_back(new MTriangle(v[0], v[1], v[2]));
+	unique.insert(mf);
+      }
+      else{
+	nbDuplic++;
+      }
+    }
+  }
+  if (nbDuplic)
+    Msg::Warning("%d duplicate triangles in STL file", nbDuplic);
+
+  _associateEntityWithMeshVertices();
+  _storeVerticesInEntities(vertices); // will delete unused vertices
+
+  fclose(fp);
+  return 1;
+}
+
+int GModel::writeSTL(const std::string &name, bool binary, bool saveAll,
+                     double scalingFactor)
+{
+  FILE *fp = fopen(name.c_str(), binary ? "wb" : "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  if(noPhysicalGroups()) saveAll = true;
+
+  if(!binary){
+    fprintf(fp, "solid Created by Gmsh\n");
+  }
+  else{
+    char header[80];
+    strncpy(header, "Created by Gmsh", 80);
+    fwrite(header, sizeof(char), 80, fp);
+    unsigned int nfacets = 0;
+    for(fiter it = firstFace(); it != lastFace(); ++it){
+      if(saveAll || (*it)->physicals.size()){
+        nfacets += (*it)->triangles.size() + 2 * (*it)->quadrangles.size();
+      }
+    }
+    fwrite(&nfacets, sizeof(unsigned int), 1, fp);
+  }
+
+  for(fiter it = firstFace(); it != lastFace(); ++it) {
+    if(saveAll || (*it)->physicals.size()){
+      for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
+        (*it)->triangles[i]->writeSTL(fp, binary, scalingFactor);
+      for(unsigned int i = 0; i < (*it)->quadrangles.size(); i++)
+        (*it)->quadrangles[i]->writeSTL(fp, binary, scalingFactor);
+    }
+  }
+
+  if(!binary)
+    fprintf(fp, "endsolid Created by Gmsh\n");
+
+  fclose(fp);
+  return 1;
+}
+
diff --git a/Geo/GModelIO_VRML.cpp b/Geo/GModelIO_VRML.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e17d956a7e81e490ea3683641639732a915d0bf9
--- /dev/null
+++ b/Geo/GModelIO_VRML.cpp
@@ -0,0 +1,251 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "MLine.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+
+static bool getVertices(int num, int *indices, std::vector<MVertex*> &vec,
+                        std::vector<MVertex*> &vertices, int minVertex = 0)
+{
+  for(int i = 0; i < num; i++){
+    if(indices[i] < minVertex || indices[i] > (int)(vec.size() - 1 + minVertex)){
+      Msg::Error("Wrong vertex index %d", indices[i]);
+      return false;
+    }
+    else
+      vertices.push_back(vec[indices[i]]);
+  }
+  return true;
+}
+
+static int skipUntil(FILE *fp, const char *key)
+{
+  char str[256], key_bracket[256];
+  strcpy(key_bracket, key);
+  strcat(key_bracket, "[");
+  while(fscanf(fp, "%s", str)){
+    if(!strcmp(str, key)){
+      while(!feof(fp) && fgetc(fp) != '['){}
+      return 1;
+    }
+    if(!strcmp(str, key_bracket)){
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static int readVerticesVRML(FILE *fp, std::vector<MVertex*> &vertexVector,
+                            std::vector<MVertex*> &allVertexVector)
+{
+  double x, y, z;
+  if(fscanf(fp, "%lf %lf %lf", &x, &y, &z) != 3) return 0;
+  vertexVector.push_back(new MVertex(x, y, z));
+  while(fscanf(fp, " , %lf %lf %lf", &x, &y, &z) == 3)
+    vertexVector.push_back(new MVertex(x, y, z));
+  for(unsigned int i = 0; i < vertexVector.size(); i++)
+    allVertexVector.push_back(vertexVector[i]);
+  Msg::Info("%d vertices", vertexVector.size());
+  return 1;
+}
+
+static int readElementsVRML(FILE *fp, std::vector<MVertex*> &vertexVector, int region,
+                            std::map<int, std::vector<MElement*> > elements[3],
+                            bool strips=false)
+{
+  int i;
+  std::vector<int> idx;
+  if(fscanf(fp, "%d", &i) != 1) return 0;
+  idx.push_back(i);
+
+  // check if vertex indices are separated by commas
+  char tmp[256];
+  const char *format;
+  fpos_t position;
+  fgetpos(fp, &position);
+  if(!fgets(tmp, sizeof(tmp), fp)) return 0;
+  fsetpos(fp, &position);
+  if(strstr(tmp, ","))
+    format = " , %d";
+  else
+    format = " %d";
+
+  while(fscanf(fp, format, &i) == 1){
+    if(i != -1){
+      idx.push_back(i);
+    }
+    else{
+      std::vector<MVertex*> vertices;
+      if(!getVertices(idx.size(), &idx[0], vertexVector, vertices)) return 0;
+      idx.clear();
+      if(vertices.size() < 2){
+        Msg::Info("Skipping %d-vertex element", (int)vertices.size());
+      }
+      else if(vertices.size() == 2){
+        elements[0][region].push_back(new MLine(vertices));
+      }
+      else if(vertices.size() == 3){
+        elements[1][region].push_back(new MTriangle(vertices));
+      }
+      else if(!strips && vertices.size() == 4){
+        elements[2][region].push_back(new MQuadrangle(vertices));
+      }
+      else if(strips){ // triangle strip
+        for(unsigned int j = 2; j < vertices.size(); j++){
+          if(j % 2)
+            elements[1][region].push_back
+              (new MTriangle(vertices[j], vertices[j - 1], vertices[j - 2]));
+          else
+            elements[1][region].push_back
+              (new MTriangle(vertices[j - 2], vertices[j - 1], vertices[j]));
+        }
+      }
+      else{ // import polygon as triangle fan
+        for(unsigned int j = 2; j < vertices.size(); j++){
+          elements[1][region].push_back
+            (new MTriangle(vertices[0], vertices[j-1], vertices[j]));
+        }
+      }
+    }
+  }
+  if(idx.size()){
+    Msg::Error("Prematured end of VRML file");
+    return 0;
+  }
+  Msg::Info("%d elements", elements[0][region].size() +
+            elements[1][region].size() + elements[2][region].size());
+  return 1;
+}
+
+int GModel::readVRML(const std::string &name)
+{
+  FILE *fp = fopen(name.c_str(), "r");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  // This is by NO means a complete VRML/Inventor parser (but it's
+  // sufficient for reading simple Inventor files... which is all I
+  // need)
+  std::vector<MVertex*> vertexVector, allVertexVector;
+  std::map<int, std::vector<MElement*> > elements[3];
+  int region = getMaxElementaryNumber(-1);
+  char buffer[256], str[256];
+  while(!feof(fp)) {
+    if(!fgets(buffer, sizeof(buffer), fp)) break;
+    if(buffer[0] != '#'){ // skip comments
+      sscanf(buffer, "%s", str);
+      if(!strcmp(str, "Coordinate3")){
+        vertexVector.clear();
+        if(!skipUntil(fp, "point")) break;
+        if(!readVerticesVRML(fp, vertexVector, allVertexVector)) break;
+      }
+      else if(!strcmp(str, "coord")){
+        region++;
+        vertexVector.clear();
+        if(!skipUntil(fp, "point")) break;
+        if(!readVerticesVRML(fp, vertexVector, allVertexVector)) break;
+        if(!skipUntil(fp, "coordIndex")) break;
+        if(!readElementsVRML(fp, vertexVector, region, elements, true)) break;
+      }
+      else if(!strcmp(str, "IndexedTriangleStripSet")){
+        region++;
+        vertexVector.clear();
+        if(!skipUntil(fp, "vertex")) break;
+        if(!readVerticesVRML(fp, vertexVector, allVertexVector)) break;
+        if(!skipUntil(fp, "coordIndex")) break;
+        if(!readElementsVRML(fp, vertexVector, region, elements, true)) break;
+      }
+      else if(!strcmp(str, "IndexedFaceSet") || !strcmp(str, "IndexedLineSet")){
+        region++;
+        if(!skipUntil(fp, "coordIndex")) break;
+        if(!readElementsVRML(fp, vertexVector, region, elements)) break;
+      }
+      else if(!strcmp(str, "DEF")){
+        char str1[256], str2[256];
+        if(!sscanf(buffer, "%s %s %s", str1, str2, str)) break;
+        if(!strcmp(str, "Coordinate")){
+          vertexVector.clear();
+          if(!skipUntil(fp, "point")) break;
+          if(!readVerticesVRML(fp, vertexVector, allVertexVector)) break;
+        }
+        else if(!strcmp(str, "IndexedFaceSet") || !strcmp(str, "IndexedLineSet")){
+          region++;
+          if(!skipUntil(fp, "coordIndex")) break;
+          if(!readElementsVRML(fp, vertexVector, region, elements)) break;
+        }
+      }
+    }
+  }
+
+  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
+    _storeElementsInEntities(elements[i]);
+  _associateEntityWithMeshVertices();
+  _storeVerticesInEntities(allVertexVector);
+
+  fclose(fp);
+  return 1;
+}
+
+int GModel::writeVRML(const std::string &name, bool saveAll, double scalingFactor)
+{
+  FILE *fp = fopen(name.c_str(), "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  if(noPhysicalGroups()) saveAll = true;
+
+  indexMeshVertices(saveAll);
+
+  fprintf(fp, "#VRML V1.0 ascii\n");
+  fprintf(fp, "#created by Gmsh\n");
+  fprintf(fp, "Coordinate3 {\n");
+  fprintf(fp, "  point [\n");
+
+  for(viter it = firstVertex(); it != lastVertex(); ++it)
+    for(unsigned int i = 0; i < (*it)->mesh_vertices.size(); i++)
+      (*it)->mesh_vertices[i]->writeVRML(fp, scalingFactor);
+  for(eiter it = firstEdge(); it != lastEdge(); ++it)
+    for(unsigned int i = 0; i < (*it)->mesh_vertices.size(); i++)
+      (*it)->mesh_vertices[i]->writeVRML(fp, scalingFactor);
+  for(fiter it = firstFace(); it != lastFace(); ++it)
+    for(unsigned int i = 0; i < (*it)->mesh_vertices.size(); i++)
+      (*it)->mesh_vertices[i]->writeVRML(fp, scalingFactor);
+
+  fprintf(fp, "  ]\n");
+  fprintf(fp, "}\n");
+
+  for(eiter it = firstEdge(); it != lastEdge(); ++it){
+    if(saveAll || (*it)->physicals.size()){
+      fprintf(fp, "DEF Curve%d IndexedLineSet {\n", (*it)->tag());
+      fprintf(fp, "  coordIndex [\n");
+      for(unsigned int i = 0; i < (*it)->lines.size(); i++)
+        (*it)->lines[i]->writeVRML(fp);
+      fprintf(fp, "  ]\n");
+      fprintf(fp, "}\n");
+    }
+  }
+
+  for(fiter it = firstFace(); it != lastFace(); ++it){
+    if(saveAll || (*it)->physicals.size()){
+      fprintf(fp, "DEF Surface%d IndexedFaceSet {\n", (*it)->tag());
+      fprintf(fp, "  coordIndex [\n");
+      for(unsigned int i = 0; i < (*it)->triangles.size(); i++)
+        (*it)->triangles[i]->writeVRML(fp);
+      for(unsigned int i = 0; i < (*it)->quadrangles.size(); i++)
+        (*it)->quadrangles[i]->writeVRML(fp);
+      fprintf(fp, "  ]\n");
+      fprintf(fp, "}\n");
+    }
+  }
+
+  fclose(fp);
+  return 1;
+}
diff --git a/Geo/GModelIO_VTK.cpp b/Geo/GModelIO_VTK.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..131620279ba04abc36140e6f91108fbd99d9e2aa
--- /dev/null
+++ b/Geo/GModelIO_VTK.cpp
@@ -0,0 +1,296 @@
+// Gmsh - Copyright (C) 1997-2012 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "MPoint.h"
+#include "MLine.h"
+#include "MTriangle.h"
+#include "MQuadrangle.h"
+#include "MTetrahedron.h"
+#include "MHexahedron.h"
+#include "MPrism.h"
+#include "MPyramid.h"
+#include "StringUtils.h"
+
+int GModel::writeVTK(const std::string &name, bool binary, bool saveAll,
+                     double scalingFactor, bool bigEndian)
+{
+  FILE *fp = fopen(name.c_str(), binary ? "wb" : "w");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  if(noPhysicalGroups()) saveAll = true;
+
+  // get the number of vertices and index the vertices in a continuous
+  // sequence
+  int numVertices = indexMeshVertices(saveAll);
+
+  fprintf(fp, "# vtk DataFile Version 2.0\n");
+  fprintf(fp, "%s, Created by Gmsh\n", getName().c_str());
+  if(binary)
+    fprintf(fp, "BINARY\n");
+  else
+    fprintf(fp, "ASCII\n");
+  fprintf(fp, "DATASET UNSTRUCTURED_GRID\n");
+
+  // get all the entities in the model
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+
+  // write mesh vertices
+  fprintf(fp, "POINTS %d double\n", numVertices);
+  for(unsigned int i = 0; i < entities.size(); i++)
+    for(unsigned int j = 0; j < entities[i]->mesh_vertices.size(); j++)
+      entities[i]->mesh_vertices[j]->writeVTK(fp, binary, scalingFactor, bigEndian);
+  fprintf(fp, "\n");
+
+  // loop over all elements we need to save and count vertices
+  int numElements = 0, totalNumInt = 0;
+  for(unsigned int i = 0; i < entities.size(); i++){
+    if(entities[i]->physicals.size() || saveAll){
+      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
+        if(entities[i]->getMeshElement(j)->getTypeForVTK()){
+          numElements++;
+          totalNumInt += entities[i]->getMeshElement(j)->getNumVertices() + 1;
+        }
+      }
+    }
+  }
+
+  // print vertex indices in ascii or binary
+  fprintf(fp, "CELLS %d %d\n", numElements, totalNumInt);
+  for(unsigned int i = 0; i < entities.size(); i++){
+    if(entities[i]->physicals.size() || saveAll){
+      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
+        if(entities[i]->getMeshElement(j)->getTypeForVTK())
+          entities[i]->getMeshElement(j)->writeVTK(fp, binary, bigEndian);
+      }
+    }
+  }
+  fprintf(fp, "\n");
+
+  // print element types in ascii or binary
+  fprintf(fp, "CELL_TYPES %d\n", numElements);
+  for(unsigned int i = 0; i < entities.size(); i++){
+    if(entities[i]->physicals.size() || saveAll){
+      for(unsigned int j = 0; j < entities[i]->getNumMeshElements(); j++){
+        int type = entities[i]->getMeshElement(j)->getTypeForVTK();
+        if(type){
+          if(binary){
+            // VTK always expects big endian binary data
+            if(!bigEndian) SwapBytes((char*)&type, sizeof(int), 1);
+            fwrite(&type, sizeof(int), 1, fp);
+          }
+          else
+            fprintf(fp, "%d\n", type);
+        }
+      }
+    }
+  }
+
+  fclose(fp);
+  return 1;
+}
+
+int GModel::readVTK(const std::string &name, bool bigEndian)
+{
+  FILE *fp = fopen(name.c_str(), "rb");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+    return 0;
+  }
+
+  char buffer[256], buffer2[256];
+  std::map<int, std::map<int, std::string> > physicals[4];
+
+  if(!fgets(buffer, sizeof(buffer), fp)) return 0; // version line
+  if(!fgets(buffer, sizeof(buffer), fp)) return 0; // title
+
+  if(fscanf(fp, "%s", buffer) != 1) // ASCII or BINARY
+    Msg::Error("Failed reading buffer");
+  bool binary = false;
+  if(!strcmp(buffer, "BINARY")) binary = true;
+
+  if(fscanf(fp, "%s %s", buffer, buffer2) != 2) return 0;
+
+  bool unstructured = false;
+  if(!strcmp(buffer, "DATASET") && !strcmp(buffer2, "UNSTRUCTURED_GRID"))
+    unstructured = true;
+
+  if((strcmp(buffer, "DATASET") &&  strcmp(buffer2, "UNSTRUCTURED_GRID")) ||
+     (strcmp(buffer, "DATASET") &&  strcmp(buffer2, "POLYDATA"))){
+    Msg::Error("VTK reader can only read unstructured or polydata datasets");
+    return 0;
+  }
+
+  // read mesh vertices
+  int numVertices;
+  if(fscanf(fp, "%s %d %s\n", buffer, &numVertices, buffer2) != 3) return 0;
+  if(strcmp(buffer, "POINTS") || !numVertices){
+    Msg::Warning("No points in dataset");
+    return 0;
+  }
+  int datasize;
+  if(!strcmp(buffer2, "double"))
+    datasize = sizeof(double);
+  else if(!strcmp(buffer2, "float"))
+    datasize = sizeof(float);
+  else{
+    Msg::Warning("VTK reader only accepts float or double datasets");
+    return 0;
+  }
+  Msg::Info("Reading %d points", numVertices);
+  std::vector<MVertex*> vertices(numVertices);
+  for(int i = 0 ; i < numVertices; i++){
+    double xyz[3];
+    if(binary){
+      if(datasize == sizeof(float)){
+        float f[3];
+        if(fread(f, sizeof(float), 3, fp) != 3) return 0;
+        if(!bigEndian) SwapBytes((char*)f, sizeof(float), 3);
+        for(int j = 0; j < 3; j++) xyz[j] = f[j];
+      }
+      else{
+        if(fread(xyz, sizeof(double), 3, fp) != 3) return 0;
+        if(!bigEndian) SwapBytes((char*)xyz, sizeof(double), 3);
+      }
+    }
+    else{
+      if(fscanf(fp, "%lf %lf %lf", &xyz[0], &xyz[1], &xyz[2]) != 3) return 0;
+    }
+    vertices[i] = new MVertex(xyz[0], xyz[1], xyz[2]);
+  }
+
+  // read mesh elements
+  int numElements, totalNumInt;
+  if(fscanf(fp, "%s %d %d\n", buffer, &numElements, &totalNumInt) != 3) return 0;
+
+  bool haveCells = true;
+  bool haveLines = false;
+  if( !strcmp(buffer, "CELLS") && numElements > 0)
+    Msg::Info("Reading %d cells", numElements);
+  else if (!strcmp(buffer, "POLYGONS") && numElements > 0)
+    Msg::Info("Reading %d polygons", numElements);
+  else if (!strcmp(buffer, "LINES") && numElements > 0){
+    haveCells = false;
+    haveLines = true;
+    Msg::Info("Reading %d lines", numElements);
+  }
+  else{
+    Msg::Warning("No cells or polygons in dataset");
+    return 0;
+  }
+
+  std::map<int, std::vector<MElement*> > elements[8];
+
+  if (haveCells){
+    std::vector<std::vector<MVertex*> > cells(numElements);
+    for(unsigned int i = 0; i < cells.size(); i++){
+      int numVerts, n[100];
+      if(binary){
+	if(fread(&numVerts, sizeof(int), 1, fp) != 1) return 0;
+	if(!bigEndian) SwapBytes((char*)&numVerts, sizeof(int), 1);
+	if((int)fread(n, sizeof(int), numVerts, fp) != numVerts) return 0;
+	if(!bigEndian) SwapBytes((char*)n, sizeof(int), numVerts);
+      }
+      else{
+	if(fscanf(fp, "%d", &numVerts) != 1) return 0;
+	for(int j = 0; j < numVerts; j++){
+	  if(fscanf(fp, "%d", &n[j]) != 1) return 0;
+	}
+      }
+      for(int j = 0; j < numVerts; j++){
+	if(n[j] >= 0 && n[j] < (int)vertices.size())
+	  cells[i].push_back(vertices[n[j]]);
+	else
+	  Msg::Error("Bad vertex index");
+      }
+    }
+
+    if (unstructured){
+      if(fscanf(fp, "%s %d\n", buffer, &numElements) != 2 ) return 0;
+      if(strcmp(buffer, "CELL_TYPES") || numElements != (int)cells.size()){
+	Msg::Error("No or invalid number of cells types");
+	return 0;
+      }
+      for(unsigned int i = 0; i < cells.size(); i++){
+	int type;
+	if(binary){
+	  if(fread(&type, sizeof(int), 1, fp) != 1) return 0;
+	  if(!bigEndian) SwapBytes((char*)&type, sizeof(int), 1);
+	}
+	else{
+	  if(fscanf(fp, "%d", &type) != 1) return 0;
+	}
+	switch(type){
+	case 1: elements[0][1].push_back(new MPoint(cells[i])); break;
+	case 3: elements[1][1].push_back(new MLine(cells[i])); break;
+	case 5: elements[2][1].push_back(new MTriangle(cells[i])); break;
+	case 9: elements[3][1].push_back(new MQuadrangle(cells[i])); break;
+	case 10: elements[4][1].push_back(new MTetrahedron(cells[i])); break;
+	case 12: elements[5][1].push_back(new MHexahedron(cells[i])); break;
+	case 13: elements[6][1].push_back(new MPrism(cells[i])); break;
+	case 14: elements[7][1].push_back(new MPyramid(cells[i])); break;
+	default:
+	  Msg::Error("Unknown type of cell %d", type);
+	  break;
+	}
+      }
+    }
+    else{
+      for(unsigned int i = 0; i < cells.size(); i++){
+	int nbNodes = (int)cells[i].size();
+	switch(nbNodes){
+	case 1: elements[0][1].push_back(new MPoint(cells[i])); break;
+	case 2: elements[1][1].push_back(new MLine(cells[i])); break;
+	case 3: elements[2][1].push_back(new MTriangle(cells[i])); break;
+	case 4: elements[3][1].push_back(new MQuadrangle(cells[i])); break;
+	default:
+	  Msg::Error("Unknown type of mesh element with %d nodes", nbNodes);
+	  break;
+	}
+      }
+    }
+  }
+  else if (haveLines){
+    if(!binary){
+      int v0, v1;
+      char line[100000], *p, *pEnd, *pEnd2;
+      int iLine = 1;
+      for (int k= 0; k < numElements; k++){
+	physicals[1][iLine][1] = "centerline";
+	if(!fgets(line, sizeof(line), fp)) return 0;
+	v0 = (int)strtol(line, &pEnd, 10); //ignore first line
+	v0 = (int)strtol(pEnd, &pEnd2, 10);
+	p=pEnd2;
+	while(1){
+	  v1 = strtol(p, &pEnd, 10);
+	  if (p == pEnd )  break;
+	  elements[1][iLine].push_back(new MLine(vertices[v0],vertices[v1]));
+	  p = pEnd;
+	  v0 = v1;
+	}
+	iLine++;
+      }
+    }
+    else{
+      Msg::Error("TODO: implement reading lines for binary files \n");
+    }
+  }
+
+  for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++)
+    _storeElementsInEntities(elements[i]);
+  _associateEntityWithMeshVertices();
+  _storeVerticesInEntities(vertices);
+
+  // store the physical tags
+  for(int i = 0; i < 4; i++)
+    _storePhysicalTagsInEntities(i, physicals[i]);
+
+  fclose(fp);
+  return 1;
+}