diff --git a/CMakeLists.txt b/CMakeLists.txt
index b71bb6e598e4924d81dc24e6a0f8c9947a22209f..cb1c70ae5bc942e5bdc681668e9e25142943e487 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -148,7 +148,7 @@ set(GMSH_API
   contrib/DiscreteIntegration/Integration3D.h
   contrib/HighOrderMeshOptimizer/OptHOM.h contrib/HighOrderMeshOptimizer/OptHomMesh.h
   contrib/HighOrderMeshOptimizer/OptHomRun.h contrib/HighOrderMeshOptimizer/ParamCoord.h
-  contrib/HighOrderMeshOptimizer/OptHomFastCurving.h 
+  contrib/HighOrderMeshOptimizer/OptHomFastCurving.h
   contrib/HighOrderMeshOptimizer/OptHomIntegralBoundaryDist.h
   contrib/HighOrderMeshOptimizer/CADDistances.h
   contrib/HighOrderMeshOptimizer/OptHomObjContribScaledJac.h
@@ -1054,7 +1054,7 @@ if(HAVE_SOLVER)
       message(STATUS "Warning: Disabling Taucs (requires METIS)")
     endif(HAVE_METIS)
   endif(ENABLE_TAUCS AND HAVE_BLAS AND HAVE_LAPACK)
-  
+
   if(ENABLE_MUMPS AND HAVE_BLAS AND HAVE_LAPACK)
     set(MUMPS_LIBS_REQUIRED smumps dmumps cmumps zmumps mumps_common pord)
     if(NOT ENABLE_MPI)
@@ -1154,7 +1154,7 @@ if(ENABLE_OCC)
         unset(OCC_LIB CACHE)
       endforeach(OCC)
       list(LENGTH OCC_LIBS NUM_OCC_LIBS)
-    endif(OCC_LIBS)      
+    endif(OCC_LIBS)
     if(NUM_OCC_LIBS EQUAL NUM_OCC_LIBS_REQUIRED)
       set_config_option(HAVE_OCC "OpenCASCADE")
       list(APPEND EXTERNAL_LIBRARIES ${OCC_LIBS})
diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp
index 4c611ebc2687b6a3facff5ed84f9bb6711e0a737..19c233f24cb160fcbfcc24bc40322283a00c8005 100644
--- a/Common/CommandLine.cpp
+++ b/Common/CommandLine.cpp
@@ -74,7 +74,7 @@ std::vector<std::pair<std::string, std::string> > GetUsage()
   s.push_back(mp("-o file",            "Specify output file name"));
   s.push_back(mp("-format string",     "Select output mesh format (auto (default), msh, "
                                        "msh1, msh2, unv, vrml, ply2, stl, mesh, bdf, cgns, "
-                                       "p3d, diff, med, ...)"));
+                                       "p3d, diff, med, neu, ...)"));
   s.push_back(mp("-bin",               "Use binary format when available"));
   s.push_back(mp("-refine",            "Perform uniform mesh refinement, then exit"));
   s.push_back(mp("-reclassify",        "Reclassify mesh, then exit"));
@@ -106,7 +106,7 @@ std::vector<std::pair<std::string, std::string> > GetUsage()
   s.push_back(mp("-rand float",        "Set random perturbation factor"));
   s.push_back(mp("-bgm file",          "Load background mesh from file"));
   s.push_back(mp("-check",             "Perform various consistency checks on mesh"));
-  s.push_back(mp("-ignorePartBound",   "Ignore partition boundaries")); 
+  s.push_back(mp("-ignorePartBound",   "Ignore partition boundaries"));
   s.push_back(mp("-ignorePeriocity",   "Ignore periodic boundaries"));
   s.push_back(mp("-oneFilePerPart",    "Save mesh partitions in separate files"));
 #if defined(HAVE_FLTK)
@@ -1163,5 +1163,3 @@ void GetOptions(int argc, char *argv[])
   if(CTX::instance()->terminal == 99)
     CTX::instance()->terminal = terminal;
 }
-
-
diff --git a/Common/CreateFile.cpp b/Common/CreateFile.cpp
index 7ec744426dacf7c2091fc665bf8c63b64f8c7d16..532c68dcabf1d64ecf205a15b9bd0f4f93e63b30 100644
--- a/Common/CreateFile.cpp
+++ b/Common/CreateFile.cpp
@@ -79,6 +79,7 @@ int GetFileFormatFromExtension(const std::string &ext)
   else if(ext == ".stp")  return FORMAT_STEP;
   else if(ext == ".iges") return FORMAT_IGES;
   else if(ext == ".igs")  return FORMAT_IGES;
+  else if(ext == ".neu")  return FORMAT_NEU;
   else                           return -1;
 }
 
@@ -133,6 +134,7 @@ std::string GetDefaultFileName(int format)
   case FORMAT_BREP: name += ".brep"; break;
   case FORMAT_IGES: name += ".iges"; break;
   case FORMAT_STEP: name += ".step"; break;
+  case FORMAT_NEU: name += ".neu"; break;
   default: break;
   }
   return name;
@@ -396,6 +398,11 @@ void CreateOutputFile(const std::string &fileName, int format,
     GModel::current()->writeOCCSTEP(name);
     break;
 
+  case FORMAT_NEU:
+    GModel::current()->writeNEU
+      (name, CTX::instance()->mesh.saveAll, CTX::instance()->mesh.scalingFactor);
+    break;
+
 #if defined(HAVE_FLTK)
   case FORMAT_PPM:
   case FORMAT_YUV:
diff --git a/Common/GmshDefines.h b/Common/GmshDefines.h
index 630462e3fd90e1cf2571754bc7be9ec00270d903..770752ca4961d2d75d57aafcad89db134f538d95 100644
--- a/Common/GmshDefines.h
+++ b/Common/GmshDefines.h
@@ -7,54 +7,55 @@
 #define _GMSH_DEFINES_H_
 
 // IO file formats (numbers should not be changed)
-#define FORMAT_MSH   1
-#define FORMAT_UNV   2
-#define FORMAT_GREF  3
-#define FORMAT_XPM   4
-#define FORMAT_PS    5
-#define FORMAT_BMP   6
-#define FORMAT_GIF   7
-#define FORMAT_GEO   8
-#define FORMAT_JPEG  9
-#define FORMAT_AUTO  10
-#define FORMAT_PPM   11
-#define FORMAT_YUV   12
-#define FORMAT_DMG   13
-#define FORMAT_SMS   14
-#define FORMAT_OPT   15
-#define FORMAT_VTK   16
-#define FORMAT_MPEG  17
-#define FORMAT_TEX   18
-#define FORMAT_VRML  19
-#define FORMAT_EPS   20
-#define FORMAT_MAIL  21
-#define FORMAT_PNG   22
-#define FORMAT_TXT   23
-#define FORMAT_PDF   24
-#define FORMAT_RMED  25
-#define FORMAT_POS   26
-#define FORMAT_STL   27
-#define FORMAT_P3D   28
-#define FORMAT_SVG   29
-#define FORMAT_MESH  30
-#define FORMAT_BDF   31
-#define FORMAT_CGNS  32
-#define FORMAT_MED   33
-#define FORMAT_DIFF  34
-#define FORMAT_BREP  35
-#define FORMAT_STEP  36
-#define FORMAT_IGES  37
-#define FORMAT_IR3   38
-#define FORMAT_INP   39
-#define FORMAT_PLY2  40
-#define FORMAT_CELUM 41
-#define FORMAT_SU2   42
+#define FORMAT_MSH          1
+#define FORMAT_UNV          2
+#define FORMAT_GREF         3
+#define FORMAT_XPM          4
+#define FORMAT_PS           5
+#define FORMAT_BMP          6
+#define FORMAT_GIF          7
+#define FORMAT_GEO          8
+#define FORMAT_JPEG         9
+#define FORMAT_AUTO         10
+#define FORMAT_PPM          11
+#define FORMAT_YUV          12
+#define FORMAT_DMG          13
+#define FORMAT_SMS          14
+#define FORMAT_OPT          15
+#define FORMAT_VTK          16
+#define FORMAT_MPEG         17
+#define FORMAT_TEX          18
+#define FORMAT_VRML         19
+#define FORMAT_EPS          20
+#define FORMAT_MAIL         21
+#define FORMAT_PNG          22
+#define FORMAT_TXT          23
+#define FORMAT_PDF          24
+#define FORMAT_RMED         25
+#define FORMAT_POS          26
+#define FORMAT_STL          27
+#define FORMAT_P3D          28
+#define FORMAT_SVG          29
+#define FORMAT_MESH         30
+#define FORMAT_BDF          31
+#define FORMAT_CGNS         32
+#define FORMAT_MED          33
+#define FORMAT_DIFF         34
+#define FORMAT_BREP         35
+#define FORMAT_STEP         36
+#define FORMAT_IGES         37
+#define FORMAT_IR3          38
+#define FORMAT_INP          39
+#define FORMAT_PLY2         40
+#define FORMAT_CELUM        41
+#define FORMAT_SU2          42
 #define FORMAT_MPEG_PREVIEW 43
-#define FORMAT_PGF   44
-#define FORMAT_PVTU  45
-#define FORMAT_X3D   46
-#define FORMAT_TOCHNOG 47
-#define FORMAT_TIKZ  48
+#define FORMAT_PGF          44
+#define FORMAT_PVTU         45
+#define FORMAT_X3D          46
+#define FORMAT_TOCHNOG      47
+#define FORMAT_TIKZ         48
+#define FORMAT_NEU          49
 
 // Element types
 #define TYPE_PNT     1
diff --git a/Fltk/graphicWindow.cpp b/Fltk/graphicWindow.cpp
index 951a0687d017e480efd870f5cc2349613d5986f6..257ffe7c0f6c1174e9045cc8308a846c63b59226 100644
--- a/Fltk/graphicWindow.cpp
+++ b/Fltk/graphicWindow.cpp
@@ -316,6 +316,8 @@ static int _save_vrml(const char *name){ return genericMeshFileDialog
     (name, "VRML Options", FORMAT_VRML, false, false); }
 static int _save_ply2(const char *name){ return genericMeshFileDialog
     (name, "PLY2 Options", FORMAT_PLY2, false, false); }
+static int _save_neu(const char *name){ return genericMeshFileDialog
+    (name, "NEU Options", FORMAT_NEU, false, false); }
 static int _save_eps(const char *name){ return gl2psFileDialog
     (name, "EPS Options", FORMAT_EPS); }
 static int _save_gif(const char *name){ return gifFileDialog(name); }
@@ -380,6 +382,7 @@ static int _save_auto(const char *name)
   case FORMAT_STL  : return _save_stl(name);
   case FORMAT_VRML : return _save_vrml(name);
   case FORMAT_PLY2 : return _save_ply2(name);
+  case FORMAT_NEU  : return _save_neu(name);
   case FORMAT_EPS  : return _save_eps(name);
   case FORMAT_GIF  : return _save_gif(name);
   case FORMAT_JPEG : return _save_jpeg(name);
@@ -436,6 +439,7 @@ static void file_export_cb(Fl_Widget *w, void *data)
     {"Mesh - Tochnog" TT "*.dat", _save_tochnog},
     {"Mesh - PLY2 Surface" TT "*.ply2", _save_ply2},
     {"Mesh - SU2" TT "*.su2", _save_su2},
+    {"Mesh - GAMBIT Neutral File" TT "*.neu", _save_neu},
     {"Post-processing - Gmsh POS" TT "*.pos", _save_view_pos},
     {"Post-processing - X3D (X3D)" TT "*.x3d", _save_view_x3d},
 #if defined(HAVE_MED)
diff --git a/Geo/CMakeLists.txt b/Geo/CMakeLists.txt
index 2ac9ea624b919b808d1946d9943ee9b20984b87d..e8d73035adf0db89d78b9648e7a62cee26ddba3c 100644
--- a/Geo/CMakeLists.txt
+++ b/Geo/CMakeLists.txt
@@ -15,7 +15,7 @@ set(SRC
    gmshSurface.cpp
    OCCVertex.cpp OCCEdge.cpp OCCFace.cpp OCCRegion.cpp
    GenericVertex.cpp GenericEdge.cpp GenericFace.cpp GenericRegion.cpp
-    discreteEdge.cpp discreteFace.cpp discreteDiskFace.cpp discreteRegion.cpp 
+    discreteEdge.cpp discreteFace.cpp discreteDiskFace.cpp discreteRegion.cpp
     fourierEdge.cpp fourierFace.cpp fourierProjectionFace.cpp
   ACISVertex.cpp
   ACISEdge.cpp
@@ -25,12 +25,12 @@ set(SRC
     GModelVertexArrays.cpp
     GModelFactory.cpp
     GModelIO_GEO.cpp GModelIO_ACIS.cpp GModelIO_OCC.cpp GModelIO_TOCHNOG.cpp
-      GModelIO_Fourier.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_Fourier.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 GModelIO_CELUM.cpp
+      GModelIO_MAIL.cpp GModelIO_P3D.cpp GModelIO_CELUM.cpp GModelIO_NEU.cpp
       GModelIO_ACTRAN.cpp GModelIO_SU2.cpp GModelIO_SAMCEF.cpp
   ExtrudeParams.cpp
   Geo.cpp
@@ -51,5 +51,5 @@ set(SRC
   MVertexBoundaryLayerData.cpp
 )
 
-file(GLOB HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h) 
+file(GLOB HDR RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h)
 append_gmsh_src(Geo "${SRC};${HDR}")
diff --git a/Geo/GModel.h b/Geo/GModel.h
index b37ed3715174d4a968d805eae56ba4c54b2bdd43..4f382250a7f521dbd3bcaf37079279dabe623f72 100644
--- a/Geo/GModel.h
+++ b/Geo/GModel.h
@@ -724,6 +724,9 @@ class GModel {
 
   // SU2 mesh file
   int writeSU2(const std::string &name, bool saveAll, double scalingFactor);
+
+  // GAMBIT neutral mesh file (.neu)
+  int writeNEU(const std::string &name, bool saveAll, double scalingFactor);
 };
 
 #endif
diff --git a/Geo/GModelIO_NEU.cpp b/Geo/GModelIO_NEU.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..31461a1098c66deae293f5f3b57dec3415c5d154
--- /dev/null
+++ b/Geo/GModelIO_NEU.cpp
@@ -0,0 +1,286 @@
+// Gmsh - Copyright (C) 1997-2017 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to the public mailing list <gmsh@onelab.info>.
+
+#include <algorithm>
+#include <limits>
+#if __cplusplus >= 201103L
+#include <unordered_map>
+#else
+#include <map>
+#endif
+#include "GModel.h"
+#include "OS.h"
+#include "MTriangle.h"
+#include "MTetrahedron.h"
+
+namespace
+{
+  static const unsigned GAMBIT_TYPE_EDGE = 1;
+  static const unsigned GAMBIT_TYPE_QUAD = 2;
+  static const unsigned GAMBIT_TYPE_TRI  = 3;
+  static const unsigned GAMBIT_TYPE_TET  = 6;
+
+  // This struct allows us to take advantage of C++11 unordered_map while
+  // maintaining backwards compatibility with C++03
+  template<typename Key, typename Value>
+  struct hashMap {
+    #if __cplusplus >= 201103L
+    typedef std::unordered_map<Key, Value> _;
+    #else
+    typedef std::map<Key, Value> _;
+    #endif
+  };
+
+  const hashMap<std::string, unsigned>::_::value_type rawData[] = {
+    hashMap<std::string, unsigned>::_::value_type("UNSPECIFIED", 0),
+    hashMap<std::string, unsigned>::_::value_type("AXIS", 1),
+    hashMap<std::string, unsigned>::_::value_type("CONJUGATE", 2),
+    hashMap<std::string, unsigned>::_::value_type("CONVECTION", 3),
+    hashMap<std::string, unsigned>::_::value_type("CYCLIC", 4),
+    hashMap<std::string, unsigned>::_::value_type("DEAD", 5),
+    hashMap<std::string, unsigned>::_::value_type("ELEMENT_SID", 6),
+    hashMap<std::string, unsigned>::_::value_type("ESPECIES", 7),
+    hashMap<std::string, unsigned>::_::value_type("EXHAUST_FAN", 8),
+    hashMap<std::string, unsigned>::_::value_type("FAN", 9),
+    hashMap<std::string, unsigned>::_::value_type("FREE_SURFACE", 10),
+    hashMap<std::string, unsigned>::_::value_type("GAP", 11),
+    hashMap<std::string, unsigned>::_::value_type("INFLOW", 12),
+    hashMap<std::string, unsigned>::_::value_type("INLET", 13),
+    hashMap<std::string, unsigned>::_::value_type("INLET_VENT", 14),
+    hashMap<std::string, unsigned>::_::value_type("INTAKE_FAN", 15),
+    hashMap<std::string, unsigned>::_::value_type("INTERFACE", 16),
+    hashMap<std::string, unsigned>::_::value_type("INTERIOR", 17),
+    hashMap<std::string, unsigned>::_::value_type("INTERNAL", 18),
+    hashMap<std::string, unsigned>::_::value_type("LIVE", 19),
+    hashMap<std::string, unsigned>::_::value_type("MASS_FLOW_INLET", 20),
+    hashMap<std::string, unsigned>::_::value_type("MELT", 21),
+    hashMap<std::string, unsigned>::_::value_type("MELT_INTERFACE", 22),
+    hashMap<std::string, unsigned>::_::value_type("MOVING_BOUNDARY", 23),
+    hashMap<std::string, unsigned>::_::value_type("NODE", 24),
+    hashMap<std::string, unsigned>::_::value_type("OUTFLOW", 25),
+    hashMap<std::string, unsigned>::_::value_type("OUTLET", 26),
+    hashMap<std::string, unsigned>::_::value_type("OUTLET_VENT", 27),
+    hashMap<std::string, unsigned>::_::value_type("PERIODIC", 28),
+    hashMap<std::string, unsigned>::_::value_type("PLOT", 29),
+    hashMap<std::string, unsigned>::_::value_type("POROUS", 30),
+    hashMap<std::string, unsigned>::_::value_type("POROUS_JUMP", 31),
+    hashMap<std::string, unsigned>::_::value_type("PRESSURE", 32),
+    hashMap<std::string, unsigned>::_::value_type("PRESSURE_FAR_FIELD", 33),
+    hashMap<std::string, unsigned>::_::value_type("PRESSURE_INFLOW", 34),
+    hashMap<std::string, unsigned>::_::value_type("PRESSURE_INLET", 35),
+    hashMap<std::string, unsigned>::_::value_type("PRESSURE_OUTFLOW", 36),
+    hashMap<std::string, unsigned>::_::value_type("PRESSURE_OUTLET", 37),
+    hashMap<std::string, unsigned>::_::value_type("RADIATION", 38),
+    hashMap<std::string, unsigned>::_::value_type("RADIATOR", 39),
+    hashMap<std::string, unsigned>::_::value_type("RECIRCULATION_INLET", 40),
+    hashMap<std::string, unsigned>::_::value_type("RECIRCULATION_OUTLET", 41),
+    hashMap<std::string, unsigned>::_::value_type("SLIP", 42),
+    hashMap<std::string, unsigned>::_::value_type("SREACTION", 43),
+    hashMap<std::string, unsigned>::_::value_type("SURFACE", 44),
+    hashMap<std::string, unsigned>::_::value_type("SYMMETRY", 45),
+    hashMap<std::string, unsigned>::_::value_type("TRACTION", 46),
+    hashMap<std::string, unsigned>::_::value_type("TRAJECTORY", 47),
+    hashMap<std::string, unsigned>::_::value_type("VELOCITY", 48),
+    hashMap<std::string, unsigned>::_::value_type("VELOCITY_INLET", 49),
+    hashMap<std::string, unsigned>::_::value_type("VENT", 50),
+    hashMap<std::string, unsigned>::_::value_type("WALL", 51),
+    hashMap<std::string, unsigned>::_::value_type("SPRING", 52),
+  };
+  static const hashMap<std::string, unsigned>::_ boundaryCodeMap(rawData, rawData + (sizeof rawData / sizeof rawData[0]));
+
+  // Gambit numbers its faces slightly differently
+  static const unsigned GAMBIT_FACE_MAP[4] = {1,2,4,3};
+
+  unsigned const gambitBoundaryCode(std::string name)
+  {
+    std::transform(name.begin(), name.end(),name.begin(), ::toupper);
+    hashMap<std::string, unsigned>::_::const_iterator code = boundaryCodeMap.find(name);
+    return code == boundaryCodeMap.end() ? 0 : code->second;
+  }
+
+  typedef std::pair<unsigned, unsigned> TetFacePair;
+  typedef hashMap<unsigned, std::vector<TetFacePair> >::_ IDTetFaceMap;
+
+  bool const sortBCs(TetFacePair const& lhs, TetFacePair const& rhs)
+  {
+    return lhs.first < rhs.first;
+  }
+
+  IDTetFaceMap const gatherBC(GModel* gm)
+  {
+    // create association map for vertices and faces
+    hashMap<unsigned, std::vector<unsigned> >::_ vertmap;
+    for (GModel::fiter it = gm->firstFace(); it != gm->lastFace(); ++it) {
+      for (unsigned i = 0; i < (*it)->triangles.size(); ++i) {
+        MTriangle* tri = (*it)->triangles[i];
+        for (int j = 0; j < tri->getNumVertices(); ++j) {
+          vertmap[tri->getVertex(j)->getNum()].push_back(tri->getNum());
+        }
+      }
+    }
+
+    // determine which faces belong to which tetrahedra by comparing vertices
+    IDTetFaceMap tetfacemap;
+    for (GModel::riter it = gm->firstRegion(); it != gm->lastRegion(); ++it) {
+      for (unsigned i = 0; i < (*it)->tetrahedra.size(); ++i) {
+        MTetrahedron* tet = (*it)->tetrahedra[i];
+        for (int faceNum = 0; faceNum < tet->getNumFaces(); ++faceNum) {
+          std::vector<MVertex*> verts;
+          tet->getFaceVertices(faceNum, verts);
+
+          std::vector<unsigned> current = vertmap[verts[0]->getNum()];
+          for (unsigned j = 1; j < verts.size() && current.size() != 0; ++j) {
+            std::vector<unsigned> common_data;
+            set_intersection(current.begin(), current.end(),
+                             vertmap[verts[j]->getNum()].begin(),
+                             vertmap[verts[j]->getNum()].end(),
+                             std::back_inserter(common_data));
+            current = common_data;
+          }
+          if (current.size() == 1) {
+            tetfacemap[current[0]].push_back(
+                TetFacePair(tet->getNum(), GAMBIT_FACE_MAP[faceNum]));
+          }
+        }
+      }
+    }
+
+    // populate boundary conditions for tetrahedra given triangle physicals
+    IDTetFaceMap boundaryConditions;
+    for (GModel::fiter it = gm->firstFace(); it != gm->lastFace(); ++it) {
+      if ((*it)->physicals.size()) {
+        for (unsigned i = 0; i < (*it)->physicals.size(); ++i) {
+          unsigned phys = (*it)->physicals[i];
+          for (unsigned j = 0; j < (*it)->triangles.size(); ++j) {
+            MTriangle* tri = (*it)->triangles[j];
+            IDTetFaceMap::iterator tets = tetfacemap.find(tri->getNum());
+            if (tets != tetfacemap.end()) {
+              for (unsigned tet = 0; tet < tets->second.size(); ++tet) {
+                boundaryConditions[phys].push_back(tets->second[tet]);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    return boundaryConditions;
+  }
+}
+
+// for a specification of the GAMBIT neutral format:
+//   http://web.stanford.edu/class/me469b/handouts/gambit_write.pdf
+int GModel::writeNEU(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;
+  }
+
+  // gather tetrahedra and id normalization information
+  unsigned numTetrahedra = 0;
+  int lowestId = std::numeric_limits<int>::max();
+  hashMap<unsigned, std::vector<unsigned> >::_ elementGroups;
+  for (riter it = firstRegion(); it != lastRegion(); ++it) {
+    if (saveAll || (*it)->physicals.size()) {
+      numTetrahedra += (*it)->tetrahedra.size();
+
+      for (unsigned phys = 0; phys < (*it)->physicals.size(); ++phys) {
+        for (unsigned i = 0; i < (*it)->tetrahedra.size(); ++i) {
+          MTetrahedron* tet = (*it)->tetrahedra[i];
+          elementGroups[(*it)->physicals[phys]].push_back(tet->getNum());
+
+          if (tet->getNum() < lowestId) lowestId = tet->getNum()-1;
+        }
+      }
+    }
+  }
+
+  IDTetFaceMap boundaryConditions = gatherBC(this);
+
+  // Metadata
+  fprintf(fp, "        CONTROL INFO 2.0.0\n");
+  fprintf(fp, "** GAMBIT NEUTRAL FILE\n");
+  fprintf(fp, "Gmsh mesh in GAMBIT neutral file format\n");
+  fprintf(fp, "PROGRAM:                Gambit     VERSION:  2.0.0\n");
+
+  char datestring[256];
+  time_t rawtime;
+  struct tm* timeinfo;
+  time(&rawtime);
+  timeinfo = localtime(&rawtime);
+  strftime(datestring, sizeof(datestring), "%c", timeinfo);
+  fprintf(fp, "%s\n", datestring);
+
+  fprintf(fp, "     NUMNP     NELEM     NGRPS    NBSETS     NDFCD     NDFVL\n");
+  fprintf(fp, " %9d %9d %9lu %9lu %9d %9d\n", indexMeshVertices(saveAll), numTetrahedra,
+          elementGroups.size(), boundaryConditions.size(), getDim(),
+          getDim());
+  fprintf(fp, "ENDOFSECTION\n");
+
+  // Nodes
+  fprintf(fp, "   NODAL COORDINATES 2.0.0\n");
+  std::vector<GEntity*> entities;
+  getEntities(entities);
+  for (unsigned i = 0; i < entities.size(); ++i) {
+    for (unsigned j = 0; j < entities[i]->mesh_vertices.size(); ++j) {
+      entities[i]->mesh_vertices[j]->writeNEU(fp, getDim(), scalingFactor);
+    }
+  }
+  fprintf(fp, "ENDOFSECTION\n");
+
+  // Elements
+  fprintf(fp, "      ELEMENTS/CELLS 2.0.0\n");
+  for (riter it = firstRegion(); it != lastRegion(); ++it) {
+    unsigned numPhys = (*it)->physicals.size();
+    if (saveAll || numPhys) {
+      for (unsigned i = 0; i < (*it)->tetrahedra.size(); ++i) {
+        (*it)->tetrahedra[i]->writeNEU(fp, GAMBIT_TYPE_TET, lowestId,
+                                        numPhys ? (*it)->physicals[0] : 0);
+      }
+    }
+  }
+  fprintf(fp, "ENDOFSECTION\n");
+
+  // Element Groups
+
+  for (hashMap<unsigned, std::vector<unsigned> >::_::const_iterator
+        it = elementGroups.begin(); it != elementGroups.end(); ++it) {
+    fprintf(fp, "       ELEMENT GROUP 2.0.0\n");
+    fprintf(fp, "GROUP: %10d ELEMENTS: %10lu MATERIAL: 0 NFLAGS: %10d\n", it->first, it->second.size(), 1);
+    fprintf(fp, "Material group %d\n", it->first);
+    fprintf(fp, "       0");
+
+    for (unsigned i = 0; i < it->second.size(); ++i) {
+      if (i % 10 == 0) {
+        fprintf(fp, "\n");
+      }
+      fprintf(fp, "%8d", it->second[i] - lowestId);
+    }
+    fprintf(fp, "\n");
+    fprintf(fp, "ENDOFSECTION\n");
+  }
+
+  // Boundary Conditions
+  for (IDTetFaceMap::iterator it = boundaryConditions.begin();
+        it != boundaryConditions.end(); ++it) {
+    fprintf(fp, "       BOUNDARY CONDITIONS 2.0.0\n");
+
+    std::string const regionName = getPhysicalName(2, it->first);
+    fprintf(fp, "%32s%8d%8lu%8d%8d\n", regionName.c_str(), 1, it->second.size(), 0, gambitBoundaryCode(regionName));
+    std::sort(it->second.begin(), it->second.end(), sortBCs);
+    std::vector<TetFacePair>::iterator tfp = it->second.begin();
+    for (std::vector<TetFacePair>::iterator tfp = it->second.begin(); tfp != it->second.end(); ++tfp) {
+      fprintf(fp, "%10d %5d %5d\n", tfp->first-lowestId, GAMBIT_TYPE_TET, tfp->second);
+    }
+
+    fprintf(fp, "ENDOFSECTION\n");
+  }
+
+  fclose(fp);
+  return 1;
+}
diff --git a/Geo/MElement.cpp b/Geo/MElement.cpp
index 05689e94f56b26f12960f6908fd4d999707f25b3..4914775dd8b4b36cbc797055e4c4ae0f1564349f 100644
--- a/Geo/MElement.cpp
+++ b/Geo/MElement.cpp
@@ -1338,6 +1338,19 @@ void MElement::writeMESH(FILE *fp, int elementTagType, int elementary,
   if(physical < 0) reverse();
 }
 
+void MElement::writeNEU(FILE *fp, unsigned gambitType, int idAdjust, int phys)
+{
+  if(phys < 0) reverse();
+
+  fprintf(fp, "%8d %2d %2d ", _num-idAdjust, gambitType, getNumVertices());
+  for(int i = 0; i < getNumVertices(); ++i) {
+    fprintf(fp, "%8d", getVertex(i)->getIndex());
+  }
+  fprintf(fp, "\n");
+
+  if(phys < 0) reverse();
+}
+
 void MElement::writeIR3(FILE *fp, int elementTagType, int num, int elementary,
                         int physical)
 {
diff --git a/Geo/MElement.h b/Geo/MElement.h
index 20b7f37ffee9b91ce44b6cb17b833d9015387537..cf86a181b4c360fc157a8623725ac20c5c056599 100644
--- a/Geo/MElement.h
+++ b/Geo/MElement.h
@@ -385,6 +385,7 @@ class MElement
   virtual void writeTOCHNOG(FILE *fp, int num);
   virtual void writeMESH(FILE *fp, int elementTagType=1, int elementary=1,
                          int physical=0);
+  virtual void writeNEU(FILE *fp, unsigned gambitType, int adjust, int phys=0);
   virtual void writeIR3(FILE *fp, int elementTagType, int num, int elementary,
                         int physical);
   virtual void writeBDF(FILE *fp, int format=0, int elementTagType=1,
diff --git a/Geo/MVertex.cpp b/Geo/MVertex.cpp
index c56f6cedf6d4da65713d9930c95706a67d9028f6..cd1535f1d86152f204593e1963c6472f9c11bcb9 100644
--- a/Geo/MVertex.cpp
+++ b/Geo/MVertex.cpp
@@ -270,6 +270,26 @@ void MVertex::writeMESH(FILE *fp, double scalingFactor)
           _ge ? _ge->tag() : 0);
 }
 
+void MVertex::writeNEU(FILE *fp, int dim, double scalingFactor)
+{
+  if(_index < 0) return; // negative index vertices are never saved
+
+  switch(dim) {
+  case 3:
+    fprintf(fp, "%10d%20.11e%20.11e%20.11e\n",
+            _index, x() * scalingFactor, y() * scalingFactor,
+            z() * scalingFactor);
+    break;
+  case 2:
+    fprintf(fp, "%10d%20.11e%20.11e\n",
+            _index, x() * scalingFactor, y() * scalingFactor);
+    break;
+  case 1:
+    fprintf(fp, "%10d%20.11e\n", _index, x() * scalingFactor);
+    break;
+  }
+}
+
 static void double_to_char8(double val, char *str)
 {
   if(val >= 1.e6)
diff --git a/Geo/MVertex.h b/Geo/MVertex.h
index 0467a86606a63e5efab81e3e8687b247d235ac67..df093d162aa6a33fc6606b177298adeb8f03724d 100644
--- a/Geo/MVertex.h
+++ b/Geo/MVertex.h
@@ -100,6 +100,7 @@ class MVertex{
                 bool bigEndian=false);
   void writeTOCHNOG(FILE *fp, int dim, double scalingFactor=1.0);
   void writeMESH(FILE *fp, double scalingFactor=1.0);
+  void writeNEU(FILE *fp, int dim, double scalingFactor=1.0);
   void writeBDF(FILE *fp, int format=0, double scalingFactor=1.0);
   void writeINP(FILE *fp, double scalingFactor=1.0);
   void writeDIFF(FILE *fp, bool binary, double scalingFactor=1.0);