]> SALOME platform Git repositories - tools/medcoupling.git/commitdiff
Salome HOME
0021314: [CEA 490] Driver GIBI with medloader / medcoupling
authoreap <eap@opencascade.com>
Thu, 1 Sep 2011 10:08:15 +0000 (10:08 +0000)
committereap <eap@opencascade.com>
Thu, 1 Sep 2011 10:08:15 +0000 (10:08 +0000)
14 files changed:
src/MEDLoader/Makefile.am
src/MEDLoader/SauvMedConvertor.cxx [new file with mode: 0644]
src/MEDLoader/SauvMedConvertor.hxx [new file with mode: 0644]
src/MEDLoader/SauvReader.cxx [new file with mode: 0644]
src/MEDLoader/SauvReader.hxx [new file with mode: 0644]
src/MEDLoader/SauvUtilities.hxx [new file with mode: 0644]
src/MEDLoader/SauvWriter.cxx [new file with mode: 0644]
src/MEDLoader/SauvWriter.hxx [new file with mode: 0644]
src/MEDLoader/Swig/MEDLoader.i
src/MEDLoader/Swig/SauvLoaderTest.py [new file with mode: 0644]
src/MEDLoader/Test/Makefile.am
src/MEDLoader/Test/SauvLoaderTest.cxx [new file with mode: 0644]
src/MEDLoader/Test/SauvLoaderTest.hxx [new file with mode: 0644]
src/MEDLoader/Test/TestSauvLoader.cxx [new file with mode: 0644]

index 3c1a8e52323b08546fe0f54dc57e4f767f6d4654..99be5b856fd14c20b8522f40d530f83088da78b2 100755 (executable)
@@ -37,12 +37,14 @@ lib_LTLIBRARIES = libmedloader.la
 salomeinclude_HEADERS= \
 MEDLoaderDefines.hxx \
 MEDLoader.hxx MEDLoaderBase.hxx MEDFileUtilities.hxx MEDFileMesh.hxx MEDFileMeshLL.hxx \
-MEDFileMeshElt.hxx MEDFileBasis.hxx MEDFileField.hxx MEDFileData.hxx
+MEDFileMeshElt.hxx MEDFileBasis.hxx MEDFileField.hxx MEDFileData.hxx \
+SauvMedConvertor.hxx SauvReader.hxx SauvWriter.hxx SauvUtilities.hxx
 
 dist_libmedloader_la_SOURCES= \
 MEDLoader.cxx MEDLoaderBase.cxx MEDFileUtilities.cxx      \
 MEDFileMesh.cxx MEDFileMeshElt.cxx MEDFileBasis.cxx       \
-MEDFileMeshLL.cxx MEDFileField.cxx MEDFileData.cxx
+MEDFileMeshLL.cxx MEDFileField.cxx MEDFileData.cxx      \
+SauvMedConvertor.cxx SauvReader.cxx SauvWriter.cxx
 
 libmedloader_la_CPPFLAGS= $(MED2_INCLUDES) $(HDF5_INCLUDES) @CXXTMPDPTHFLAGS@ \
        -I$(srcdir)/../INTERP_KERNEL \
diff --git a/src/MEDLoader/SauvMedConvertor.cxx b/src/MEDLoader/SauvMedConvertor.cxx
new file mode 100644 (file)
index 0000000..d19ec9d
--- /dev/null
@@ -0,0 +1,2437 @@
+// Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File      : SauvMedConvertor.cxx
+// Created   : Tue Aug 16 14:43:20 2011
+// Author    : Edward AGAPOV (eap)
+//
+
+#include "SauvMedConvertor.hxx"
+
+#include "CellModel.hxx"
+#include "MEDFileMesh.hxx"
+#include "MEDFileField.hxx"
+#include "MEDFileData.hxx"
+#include "MEDCouplingFieldDouble.hxx"
+
+#include <iostream>
+#include <cassert>
+#include <cmath>
+#include <queue>
+#include <limits>
+
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+
+#ifdef WNT
+#include <io.h>
+#endif
+
+#ifndef WNT
+#define HAS_XDR
+#endif
+
+#ifdef HAS_XDR
+#include <rpc/xdr.h>
+#endif
+
+using namespace SauvUtilities;
+using namespace ParaMEDMEM;
+using namespace std;
+
+namespace
+{
+  // for ASCII file reader
+  const int GIBI_MaxOutputLen = 150; // max length of a line in the sauve file
+  const int GIBI_BufferSize   = 16184; // for buffered reading
+
+  using namespace INTERP_KERNEL;
+
+  const size_t MaxMedCellType = NORM_ERROR;
+  const size_t NbGibiCellTypes = 47;
+  const TCellType GibiTypeToMed[NbGibiCellTypes] =
+    {
+      /*1 */ NORM_POINT1 ,/*2 */ NORM_SEG2   ,/*3 */ NORM_SEG3   ,/*4 */ NORM_TRI3   ,/*5 */ NORM_ERROR  ,
+      /*6 */ NORM_TRI6   ,/*7 */ NORM_ERROR  ,/*8 */ NORM_QUAD4  ,/*9 */ NORM_ERROR  ,/*10*/ NORM_QUAD8  ,
+      /*11*/ NORM_ERROR  ,/*12*/ NORM_ERROR  ,/*13*/ NORM_ERROR  ,/*14*/ NORM_HEXA8  ,/*15*/ NORM_HEXA20 ,
+      /*16*/ NORM_PENTA6 ,/*17*/ NORM_PENTA15,/*18*/ NORM_ERROR  ,/*19*/ NORM_ERROR  ,/*20*/ NORM_ERROR  ,
+      /*21*/ NORM_ERROR  ,/*22*/ NORM_ERROR  ,/*23*/ NORM_TETRA4 ,/*24*/ NORM_TETRA10,/*25*/ NORM_PYRA5  ,
+      /*26*/ NORM_PYRA13 ,/*27*/ NORM_ERROR  ,/*28*/ NORM_ERROR  ,/*29*/ NORM_ERROR  ,/*30*/ NORM_ERROR  ,
+      /*31*/ NORM_ERROR  ,/*32*/ NORM_ERROR  ,/*33*/ NORM_ERROR  ,/*34*/ NORM_ERROR  ,/*35*/ NORM_ERROR  ,
+      /*36*/ NORM_ERROR  ,/*37*/ NORM_ERROR  ,/*38*/ NORM_ERROR  ,/*39*/ NORM_ERROR  ,/*40*/ NORM_ERROR  ,
+      /*41*/ NORM_ERROR  ,/*42*/ NORM_ERROR  ,/*43*/ NORM_ERROR  ,/*44*/ NORM_ERROR  ,/*45*/ NORM_ERROR  ,
+      /*46*/ NORM_ERROR  ,/*47*/ NORM_ERROR
+    };
+
+  //================================================================================
+  /*!
+   * \brief Return dimension of a group
+   */
+  //================================================================================
+
+  unsigned getDim( const Group* grp )
+  {
+    return SauvUtilities::getDimension( grp->_groups.empty() ? grp->_cellType : grp->_groups[0]->_cellType );
+  }
+
+  //================================================================================
+  /*!
+   * \brief Converts connectivity of quadratic elements
+   */
+  //================================================================================
+
+  inline void ConvertQuadratic( const INTERP_KERNEL::NormalizedCellType type,
+                                const Cell &                     aCell )
+  {
+    if ( const int * conn = getGibi2MedQuadraticInterlace( type ))
+      {
+        Cell* ma = (Cell*) & aCell;
+        //cout << "###### BEFORE ConvertQuadratic() " << *ma << endl;
+        vector< Node* > new_nodes( ma->_nodes.size() );
+        for ( size_t i = 0; i < new_nodes.size(); ++i )
+          new_nodes[ i ] = ma->_nodes[ conn[ i ]];
+        ma->_nodes.swap( new_nodes );
+        //cout << "###### AFTER ConvertQuadratic() " << *ma << endl;
+      }
+  }
+
+  //================================================================================
+  /*!
+   * \brief Returns a vector of pairs of node indices to inverse a med volume element
+   */
+  //================================================================================
+
+  void getReverseVector (const INTERP_KERNEL::NormalizedCellType type,
+                         vector<pair<int,int> > &                swapVec )
+  {
+    swapVec.clear();
+
+    switch ( type )
+      {
+      case NORM_TETRA4:
+        swapVec.resize(1);
+        swapVec[0] = make_pair( 1, 2 );
+        break;
+      case NORM_PYRA5:
+        swapVec.resize(1);
+        swapVec[0] = make_pair( 1, 3 );
+        break;
+      case NORM_PENTA6:
+        swapVec.resize(2);
+        swapVec[0] = make_pair( 1, 2 );
+        swapVec[1] = make_pair( 4, 5 );
+        break;
+      case NORM_HEXA8:
+        swapVec.resize(2);
+        swapVec[0] = make_pair( 1, 3 );
+        swapVec[1] = make_pair( 5, 7 );
+        break;
+      case NORM_TETRA10:
+        swapVec.resize(3);
+        swapVec[0] = make_pair( 1, 2 );
+        swapVec[1] = make_pair( 4, 6 );
+        swapVec[2] = make_pair( 8, 9 );
+        break;
+      case NORM_PYRA13:
+        swapVec.resize(4);
+        swapVec[0] = make_pair( 1, 3 );
+        swapVec[1] = make_pair( 5, 8 );
+        swapVec[2] = make_pair( 6, 7 );
+        swapVec[3] = make_pair( 10, 12 );
+        break;
+      case NORM_PENTA15:
+        swapVec.resize(4);
+        swapVec[0] = make_pair( 1, 2 );
+        swapVec[1] = make_pair( 4, 5 );
+        swapVec[2] = make_pair( 6, 8 );
+        swapVec[3] = make_pair( 9, 11 );
+        break;
+      case NORM_HEXA20:
+        swapVec.resize(7);
+        swapVec[0] = make_pair( 1, 3 );
+        swapVec[1] = make_pair( 5, 7 );
+        swapVec[2] = make_pair( 8, 11 );
+        swapVec[3] = make_pair( 9, 10 );
+        swapVec[4] = make_pair( 12, 15 );
+        swapVec[5] = make_pair( 13, 14 );
+        swapVec[6] = make_pair( 17, 19 );
+        break;
+        //   case NORM_SEG3: no need to reverse edges
+        //     swapVec.resize(1);
+        //     swapVec[0] = make_pair( 1, 2 );
+        //     break;
+      case NORM_TRI6:
+        swapVec.resize(2);
+        swapVec[0] = make_pair( 1, 2 );
+        swapVec[1] = make_pair( 3, 5 );
+        break;
+      case NORM_QUAD8:
+        swapVec.resize(3);
+        swapVec[0] = make_pair( 1, 3 );
+        swapVec[1] = make_pair( 4, 7 );
+        swapVec[2] = make_pair( 5, 6 );
+        break;
+      default:;
+      }
+  }
+
+  //================================================================================
+  /*!
+   * \brief Inverses element orientation using vector of indices to swap
+   */
+  //================================================================================
+
+  inline void reverse(const Cell & aCell, const vector<pair<int,int> > & swapVec )
+  {
+    Cell* ma = (Cell*) & aCell;
+    for ( unsigned i = 0; i < swapVec.size(); ++i )
+      std::swap( ma->_nodes[ swapVec[i].first ],
+                 ma->_nodes[ swapVec[i].second ]);
+    if ( swapVec.empty() )
+      ma->_reverse = true;
+    else
+      ma->_reverse = false;
+  }
+  //================================================================================
+  /*!
+   * \brief Comparator of cells by number used for ordering cells thinin a med group
+   */
+  struct TCellByIDCompare
+  {
+    bool operator () (const Cell* i1, const Cell* i2)
+    {
+      return i1->_number < i2->_number;
+    }
+  };
+  typedef map< const Cell*, unsigned, TCellByIDCompare > TCellToOrderMap;
+
+  //================================================================================
+  /*!
+   * \brief Fill Group::_relocTable if necessary
+   */
+  //================================================================================
+
+  void setRelocationTable( Group* grp, TCellToOrderMap& cell2order )
+  {
+    if ( !grp->_isProfile ) return;
+
+    // check if relocation table is necessary
+    bool isRelocated = false;
+    unsigned newOrder = 0;
+    TCellToOrderMap::iterator c2oIt = cell2order.begin(), c2oEnd = cell2order.end();
+    for ( ; !isRelocated && c2oIt != c2oEnd; ++c2oIt, ++newOrder )
+      isRelocated = ( c2oIt->second != newOrder );
+
+    if ( isRelocated )
+    {
+      grp->_relocTable.resize( cell2order.size() );
+      for ( newOrder = 0, c2oIt = cell2order.begin(); c2oIt != c2oEnd; ++c2oIt, ++newOrder )
+        grp->_relocTable[ c2oIt->second ] = newOrder;
+    }
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Return dimension for the given cell type
+ */
+//================================================================================
+
+unsigned SauvUtilities::getDimension( INTERP_KERNEL::NormalizedCellType type )
+{
+  return type == NORM_ERROR ? -1 : INTERP_KERNEL::CellModel::GetCellModel( type ).getDimension();
+}
+
+//================================================================================
+/*!
+ * \brief Returns interlace array to transform a quadratic GIBI element to a MED one
+ */
+//================================================================================
+
+const int * SauvUtilities::getGibi2MedQuadraticInterlace( INTERP_KERNEL::NormalizedCellType type )
+{
+  static vector<const int*> conn;
+  static const int hexa20 [] = {0,6,4,2, 12,18,16,14, 7,5,3,1, 19,17,15,13, 8,11,10,9};
+  static const int penta15[] = {0,2,4, 9,11,13, 1,3,5, 10,12,14, 6,7,3};
+  static const int pyra13 [] = {0,2,4,6, 12, 1,3,5,7, 8,9,10,11};
+  static const int tetra10[] = {0,2,4, 9, 1,3,5, 6,7,8};
+  static const int quad8  [] = {0,2,4,6, 1,3,5,7};
+  static const int tria6  [] = {0,2,4, 1,3,5};
+  static const int seg3   [] = {0,2,1};
+  if ( conn.empty() )
+  {
+    conn.resize( MaxMedCellType + 1, 0 );
+    conn[ NORM_HEXA20 ] = hexa20;
+    conn[ NORM_PENTA15] = penta15;
+    conn[ NORM_PYRA13 ] = pyra13;
+    conn[ NORM_TETRA10] = tetra10;
+    conn[ NORM_SEG3   ] = seg3;
+    conn[ NORM_TRI6   ] = tria6;
+    conn[ NORM_QUAD8  ] = quad8;
+  }
+  return conn[ type ];
+}
+
+//================================================================================
+/*!
+ * \brief Avoid coping sortedNodeIDs
+ */
+//================================================================================
+
+Cell::Cell(const Cell& ma)
+  : _nodes(ma._nodes), _reverse(ma._reverse), _sortedNodeIDs(0), _number(ma._number)
+{
+  if ( ma._sortedNodeIDs )
+    {
+      _sortedNodeIDs = new int[ _nodes.size() ];
+      std::copy( ma._sortedNodeIDs, ma._sortedNodeIDs + _nodes.size(), _sortedNodeIDs );
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Rerturn the i-th link of face
+ */
+//================================================================================
+
+SauvUtilities::Link Cell::link(int i) const
+{
+  int i2 = ( i + 1 ) % _nodes.size();
+  if ( _reverse )
+    return make_pair( _nodes[i2]->_number, _nodes[i]->_number );
+  else
+    return make_pair( _nodes[i]->_number, _nodes[i2]->_number );
+}
+
+//================================================================================
+/*!
+ * \brief creates if needed and return _sortedNodeIDs
+ */
+//================================================================================
+
+const TID* Cell::getSortedNodes() const
+{
+  if ( !_sortedNodeIDs )
+  {
+    size_t l=_nodes.size();
+    _sortedNodeIDs = new int[ l ];
+
+    for (size_t i=0; i!=l; ++i)
+      _sortedNodeIDs[i]=_nodes[i]->_number;
+    std::sort( _sortedNodeIDs, _sortedNodeIDs + l );
+  }
+  return _sortedNodeIDs;
+}
+
+//================================================================================
+/*!
+ * \brief Compare sorted ids of cell nodes
+ */
+//================================================================================
+
+bool Cell::operator< (const Cell& ma) const
+{
+  if ( _nodes.size() == 1 )
+    return _nodes[0] < ma._nodes[0];
+
+  const int* v1 = getSortedNodes();
+  const int* v2 = ma.getSortedNodes();
+  for ( const int* vEnd = v1 + _nodes.size(); v1 < vEnd; ++v1, ++v2 )
+    if(*v1 != *v2)
+      return *v1 < *v2;
+  return false;
+}
+
+//================================================================================
+/*!
+ * \brief Dump a Cell
+ */
+//================================================================================
+
+std::ostream& SauvUtilities::operator<< (std::ostream& os, const SauvUtilities::Cell& ma)
+{
+  os << "cell " << ma._number << " (" << ma._nodes.size() << " nodes) : < " << ma._nodes[0];
+  for( size_t i=1; i!=ma._nodes.size(); ++i)
+    os << ", " << ma._nodes[0];
+#ifdef _DEBUG_
+  os << " > sortedNodes: ";
+  if ( ma._sortedNodeIDs ) {
+    os << "< ";
+    for( size_t i=0; i!=ma._nodes.size(); ++i)
+      os << ( i ? ", " : "" ) << ma._sortedNodeIDs[i];
+    os << " >";
+  }
+  else {
+    os << "NULL";
+  }
+#endif
+  return os;
+}
+
+//================================================================================
+/*!
+ * \brief Return nb of elements in the group
+ */
+//================================================================================
+
+int Group::size() const
+{
+  int size = 0;
+  if ( !_relocTable.empty() )
+    size =  _relocTable.size();
+  else if ( _medGroup )
+    size = _medGroup->getNumberOfTuples();
+  else if ( !_cells.empty() )
+    size = _cells.size();
+  else
+    for ( size_t i = 0; i < _groups.size(); ++i )
+      size += _groups[i]->size();
+  return size;
+}
+
+//================================================================================
+/*!
+ * \brief Conver gibi element type to med one
+ */
+//================================================================================
+
+INTERP_KERNEL::NormalizedCellType SauvUtilities::gibi2medGeom( size_t gibiType )
+{
+  if ( gibiType < 1 || gibiType > NbGibiCellTypes )
+    return NORM_ERROR;
+
+  return GibiTypeToMed[ gibiType - 1 ];
+}
+
+//================================================================================
+/*!
+ * \brief Conver med element type to gibi one
+ */
+//================================================================================
+
+int SauvUtilities::med2gibiGeom( INTERP_KERNEL::NormalizedCellType medGeomType )
+{
+  for ( int i = 0; i < NbGibiCellTypes; i++ )
+    if ( GibiTypeToMed[ i ] == medGeomType )
+      return i + 1;
+
+  return -1;
+}
+
+//================================================================================
+/*!
+ * \brief Remember the file name
+ */
+//================================================================================
+
+FileReader::FileReader(const char* fileName):_fileName(fileName),_iRead(0),_nbToRead(0)
+{
+}
+
+//================================================================================
+/*!
+ * \brief Constructor of ASCII sauve file reader
+ */
+//================================================================================
+
+ASCIIReader::ASCIIReader(const char* fileName)
+  :FileReader(fileName),
+   _file(-1)
+{
+}
+
+//================================================================================
+/*!
+ * \brief Return true
+ */
+//================================================================================
+
+bool ASCIIReader::isASCII() const
+{
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Try to open an ASCII file
+ */
+//================================================================================
+
+bool ASCIIReader::open()
+{
+#ifdef WNT
+  _file = ::_open (_fileName.c_str(), _O_RDONLY|_O_BINARY);
+#else
+  _file = ::open (_fileName.c_str(), O_RDONLY);
+#endif
+  if (_file >= 0)
+    {
+      _start  = new char [GIBI_BufferSize]; // working buffer beginning
+      //_tmpBuf = new char [GIBI_MaxOutputLen];
+      _ptr    = _start;
+      _eptr   = _start;
+      _lineNb = 0;
+    }
+  else
+    {
+      //THROW_IK_EXCEPTION("Can't open file "<<_fileName << " fd: " << _file);
+    }
+  return (_file >= 0);
+}
+
+//================================================================================
+/*!
+ * \brief Close the file
+ */
+//================================================================================
+
+ASCIIReader::~ASCIIReader()
+{
+  if (_file >= 0)
+    {
+      ::close (_file);
+      if (_start != 0L)
+        {
+          delete [] _start;
+          //delete [] _tmpBuf;
+          _start = 0;
+        }
+      _file = -1;
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Return a next line of the file
+ */
+//================================================================================
+
+bool ASCIIReader::getNextLine (char* & line, bool raiseOEF /*= true*/ )
+{
+  if ( getLine( line )) return true;
+  if ( raiseOEF )
+    THROW_IK_EXCEPTION("Unexpected EOF on ln "<<_lineNb);
+  return false;
+}
+
+//================================================================================
+/*!
+ * \brief Read a next line of the file if necessary
+ */
+//================================================================================
+
+bool ASCIIReader::getLine(char* & line)
+{
+  bool aResult = true;
+  // Check the state of the buffer;
+  // if there is too little left, read the next portion of data
+  int nBytesRest = _eptr - _ptr;
+  if (nBytesRest < GIBI_MaxOutputLen)
+    {
+      if (nBytesRest > 0)
+        {
+          // move the remaining portion to the buffer beginning
+          for ( int i = 0; i < nBytesRest; ++i )
+            _start[i] = _ptr[i];
+          //memcpy (_tmpBuf, _ptr, nBytesRest);
+          //memcpy (_start, _tmpBuf, nBytesRest);
+        }
+      else
+        {
+          nBytesRest = 0;
+        }
+      _ptr = _start;
+      const int nBytesRead = ::read (_file,
+                                     &_start [nBytesRest],
+                                     GIBI_BufferSize - nBytesRest);
+      nBytesRest += nBytesRead;
+      _eptr = &_start [nBytesRest];
+    }
+  // Check the buffer for the end-of-line
+  char * ptr = _ptr;
+  while (true)
+    {
+      // Check for end-of-the-buffer, the ultimate criterion for termination
+      if (ptr >= _eptr)
+        {
+          if (nBytesRest <= 0)
+            aResult = false;
+          else
+            _eptr[-1] = '\0';
+          break;
+        }
+      // seek the line-feed character
+      if (ptr[0] == '\n')
+        {
+          if (ptr[-1] == '\r')
+            ptr[-1] = '\0';
+          ptr[0] = '\0';
+          ++ptr;
+          break;
+        }
+      ++ptr;
+    }
+  // Output the result
+  line = _ptr;
+  _ptr = ptr;
+  _lineNb++;
+
+  return aResult;
+}
+
+//================================================================================
+/*!
+ * \brief Prepare for iterating over given nb of values
+ *  \param nbToRead - nb of fields to read
+ *  \param nbPosInLine - nb of fields in one line
+ *  \param width - field width
+ *  \param shift - shift from line beginning to the field start
+ */
+//================================================================================
+
+void ASCIIReader::init( int nbToRead, int nbPosInLine, int width, int shift /*= 0*/ )
+{
+  _nbToRead    = nbToRead;
+  _nbPosInLine = nbPosInLine;
+  _width       = width;
+  _shift       = shift;
+  _iPos = _iRead = 0;
+  if ( _nbToRead )
+    {
+      getNextLine( _curPos );
+      _curPos = _curPos + _shift;
+    }
+  else
+    {
+      _curPos = 0;
+    }
+  _curLocale.clear();
+}
+
+//================================================================================
+/*!
+ * \brief Prepare for iterating over given nb of string values
+ */
+//================================================================================
+
+void ASCIIReader::initNameReading(int nbValues, int width /*= 8*/)
+{
+  init( nbValues, 72 / ( width + 1 ), width, 1 );
+}
+
+//================================================================================
+/*!
+ * \brief Prepare for iterating over given nb of integer values
+ */
+//================================================================================
+
+void ASCIIReader::initIntReading(int nbValues)
+{
+  init( nbValues, 10, 8 );
+}
+
+//================================================================================
+/*!
+ * \brief Prepare for iterating over given nb of real values
+ */
+//================================================================================
+
+void ASCIIReader::initDoubleReading(int nbValues)
+{
+  init( nbValues, 3, 22 );
+
+  // Correction 2 of getDouble(): set "C" numeric locale to read numbers
+  // with dot decimal point separator, as it is in SAUVE files
+  _curLocale = setlocale(LC_NUMERIC, "C");
+}
+
+//================================================================================
+/*!
+ * \brief Return true if not all values have been read
+ */
+//================================================================================
+
+bool ASCIIReader::more() const
+{
+  bool result = false;
+  if ( _iRead < _nbToRead)
+    {
+      if ( _curPos ) result = true;
+    }
+  return result;
+}
+
+//================================================================================
+/*!
+ * \brief Go to the nex value
+ */
+//================================================================================
+
+void ASCIIReader::next()
+{
+  if ( !more() )
+    THROW_IK_EXCEPTION("SauvUtilities::ASCIIReader::next(): no more() values to read");
+  ++_iRead;
+  ++_iPos;
+  if ( _iRead < _nbToRead )
+    {
+      if ( _iPos >= _nbPosInLine )
+        {
+          getNextLine( _curPos );
+          _curPos = _curPos + _shift;
+          _iPos = 0;
+        }
+      else
+        {
+          _curPos = _curPos + _width + _shift;
+        }
+    }
+  else
+    {
+      _curPos = 0;
+      if ( !_curLocale.empty() )
+        {
+          setlocale(LC_NUMERIC, _curLocale.c_str());
+          _curLocale.clear();
+        }
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Return the current integer value
+ */
+//================================================================================
+
+int ASCIIReader::getInt() const
+{
+  // fix for two glued ints (issue 0021009):
+  // Line nb    |   File contents
+  // ------------------------------------------------------------------------------------
+  // 53619905   |       1       2       6       8
+  // 53619906   |                                                                SCALAIRE
+  // 53619907   |    -63312600499       1       0       0       0      -2       0       2
+  //   where -63312600499 is actualy -633 and 12600499
+  char hold=_curPos[_width];
+  _curPos[_width] = '\0';
+  int result = atoi( _curPos );
+  _curPos[_width] = hold;
+  return result;
+  //return atoi(str());
+}
+
+//================================================================================
+/*!
+ * \brief Return the current float value
+ */
+//================================================================================
+
+float ASCIIReader::getFloat() const
+{
+  return getDouble();
+}
+
+//================================================================================
+/*!
+ * \brief Return the current double value
+ */
+//================================================================================
+
+double ASCIIReader::getDouble() const
+{
+  //std::string aStr (_curPos);
+
+  // Correction: add missing 'E' specifier
+  // int aPosStart = aStr.find_first_not_of(" \t");
+  // if (aPosStart < (int)aStr.length()) {
+  //   int aPosSign = aStr.find_first_of("+-", aPosStart + 1); // pass the leading symbol, as it can be a sign
+  //   if (aPosSign < (int)aStr.length()) {
+  //     if (aStr[aPosSign - 1] != 'e' && aStr[aPosSign - 1] != 'E')
+  //       aStr.insert(aPosSign, "E", 1);
+  //   }
+  // }
+
+  // Different Correction (more optimal)
+  // Sample:
+  //  0.00000000000000E+00 -2.37822406690632E+01  6.03062748797469E+01
+  //  7.70000000000000-100  7.70000000000000+100  7.70000000000000+100
+  //0123456789012345678901234567890123456789012345678901234567890123456789
+  const size_t posE = 18;
+  if ( _curPos[posE] != 'E' && _curPos[posE] != 'e' )
+    {
+      std::string aStr (_curPos);
+      if ( aStr.size() < posE )
+        THROW_IK_EXCEPTION("No more doubles (line #" << lineNb() << ")");
+      aStr.insert( posE, "E", 1 );
+      return atof(aStr.c_str());
+    }
+  return atof( _curPos );
+}
+
+//================================================================================
+/*!
+ * \brief Return the current string value
+ */
+//================================================================================
+
+string ASCIIReader::getName() const
+{
+  int len = _width;
+  while (( _curPos[len-1] == ' ' || _curPos[len-1] == 0) && len > 0 )
+    len--;
+  return string( _curPos, len );
+}
+
+//================================================================================
+/*!
+ * \brief Constructor of a binary sauve file reader
+ */
+//================================================================================
+
+XDRReader::XDRReader(const char* fileName) :FileReader(fileName), _xdrs_file(NULL)
+{
+}
+
+//================================================================================
+/*!
+ * \brief Close the XDR sauve file
+ */
+//================================================================================
+
+XDRReader::~XDRReader()
+{
+#ifdef HAS_XDR  
+  if ( _xdrs_file )
+    {
+      xdr_destroy((XDR*)_xdrs);
+      free((XDR*)_xdrs);
+      ::fclose(_xdrs_file);
+      _xdrs_file = NULL;
+    }
+#endif
+}
+
+//================================================================================
+/*!
+ * \brief Return false
+ */
+//================================================================================
+
+bool XDRReader::isASCII() const
+{
+  return false;
+}
+
+//================================================================================
+/*!
+ * \brief Try to open an XRD file
+ */
+//================================================================================
+
+bool XDRReader::open()
+{
+  bool xdr_ok = false;
+#ifdef HAS_XDR
+  if ((_xdrs_file = ::fopen(_fileName.c_str(), "r")))
+    {
+      _xdrs = (XDR *)malloc(sizeof(XDR));
+      xdrstdio_create((XDR*)_xdrs, _xdrs_file, XDR_DECODE);
+
+      const int maxsize = 10;
+      char icha[maxsize+1];
+      char* icha2 = icha;
+      if (( xdr_ok = xdr_string((XDR*)_xdrs, &icha2, maxsize)))
+        {
+          icha[maxsize] = '\0';
+          xdr_ok = (strcmp(icha, "CASTEM XDR") == 0);
+        }
+      if ( !xdr_ok )
+        {
+          xdr_destroy((XDR*)_xdrs);
+          free((XDR*)_xdrs);
+          fclose(_xdrs_file);
+          _xdrs_file = NULL;
+        }
+    }
+#endif
+  return xdr_ok;
+}
+
+//================================================================================
+/*!
+ * \brief A stub
+ */
+//================================================================================
+
+bool XDRReader::getNextLine (char* &, bool )
+{
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Prepare for iterating over given nb of values
+ *  \param nbToRead - nb of fields to read
+ *  \param width - field width
+ */
+//================================================================================
+
+void XDRReader::init( int nbToRead, int width/*=0*/ )
+{
+  if(_iRead < _nbToRead)
+    {
+      cout << "_iRead, _nbToRead : " << _iRead << " " << _nbToRead << endl;
+      cout << "Unfinished iteration before new one !" << endl;
+      THROW_IK_EXCEPTION("SauvUtilities::XDRReader::init(): Unfinished iteration before new one !");
+    }
+  _iRead    = 0;
+  _nbToRead = nbToRead;
+  _width    = width;
+}
+
+//================================================================================
+/*!
+ * \brief Prepare for iterating over given nb of string values
+ */
+//================================================================================
+
+void XDRReader::initNameReading(int nbValues, int width)
+{
+  init( nbValues, width );
+  _xdr_kind = _xdr_kind_char;
+  if(nbValues*width)
+    {
+      unsigned int nels = nbValues*width;
+      _xdr_cvals = (char*)malloc((nels+1)*sizeof(char));
+#ifdef HAS_XDR
+      xdr_string((XDR*)_xdrs, &_xdr_cvals, nels);
+#endif
+      _xdr_cvals[nels] = '\0';
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Prepare for iterating over given nb of integer values
+ */
+//================================================================================
+
+void XDRReader::initIntReading(int nbValues)
+{
+  init( nbValues );
+  _xdr_kind = _xdr_kind_int;
+  if(nbValues)
+    {
+#ifdef HAS_XDR
+      unsigned int nels = nbValues;
+      unsigned int actual_nels;
+      _xdr_ivals = (int*)malloc(nels*sizeof(int));
+      xdr_array((XDR*)_xdrs, (char **)&_xdr_ivals, &actual_nels, nels, sizeof(int), (xdrproc_t)xdr_int);
+#endif
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Prepare for iterating over given nb of real values
+ */
+//================================================================================
+
+void XDRReader::initDoubleReading(int nbValues)
+{
+  init( nbValues );
+  _xdr_kind = _xdr_kind_double;
+  if(nbValues)
+    {
+#ifdef HAS_XDR
+      unsigned int nels = nbValues;
+      unsigned int actual_nels;
+      _xdr_dvals = (double*)malloc(nels*sizeof(double));
+      xdr_array((XDR*)_xdrs, (char **)&_xdr_dvals, &actual_nels, nels, sizeof(double), (xdrproc_t)xdr_double);
+#endif
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Return true if not all values have been read
+ */
+//================================================================================
+
+bool XDRReader::more() const
+{
+  return _iRead < _nbToRead;
+}
+
+//================================================================================
+/*!
+ * \brief Go to the nex value
+ */
+//================================================================================
+
+void XDRReader::next()
+{
+  if ( !more() )
+    THROW_IK_EXCEPTION("SauvUtilities::XDRReader::next(): no more() values to read");
+
+  ++_iRead;
+  if ( _iRead < _nbToRead )
+    {
+    }
+  else
+    {
+      if(_xdr_kind == _xdr_kind_char) free(_xdr_cvals);
+      if(_xdr_kind == _xdr_kind_int) free(_xdr_ivals);
+      if(_xdr_kind == _xdr_kind_double) free(_xdr_dvals);
+      _xdr_kind = _xdr_kind_null;
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Return the current integer value
+ */
+//================================================================================
+
+int XDRReader::getInt() const
+{
+  if(_iRead < _nbToRead)
+    {
+      return _xdr_ivals[_iRead];
+    }
+  else
+    {
+      int result = 0;
+#ifdef HAS_XDR
+      xdr_int((XDR*)_xdrs, &result);
+#endif
+      return result;
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Return the current float value
+ */
+//================================================================================
+
+float  XDRReader::getFloat() const
+{
+  float result = 0;
+#ifdef HAS_XDR
+  xdr_float((XDR*)_xdrs, &result);
+#endif
+  return result;
+}
+
+//================================================================================
+/*!
+ * \brief Return the current double value
+ */
+//================================================================================
+
+double XDRReader::getDouble() const
+{
+  if(_iRead < _nbToRead)
+    {
+      return _xdr_dvals[_iRead];
+    }
+  else
+    {
+      double result = 0;
+#ifdef HAS_XDR
+      xdr_double((XDR*)_xdrs, &result);
+#endif
+      return result;
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Return the current string value
+ */
+//================================================================================
+
+std::string XDRReader::getName() const
+{
+  int len = _width;
+  char* s = _xdr_cvals + _iRead*_width;
+  while (( s[len-1] == ' ' || s[len-1] == 0) && len > 0 )
+    len--;
+  return string( s, len );
+}
+
+//================================================================================
+/*!
+ * \brief Throw an exception if not all needed data is present
+ */
+//================================================================================
+
+void IntermediateMED::checkDataAvailability() const throw(INTERP_KERNEL::Exception)
+{
+  if ( _spaceDim == 0 )
+    THROW_IK_EXCEPTION("Wrong file format"); // it is the first record in the sauve file
+
+  if ( _groups.empty() )
+    THROW_IK_EXCEPTION("No elements have been read");
+
+  if ( _points.empty() || _nbNodes == 0 )
+    THROW_IK_EXCEPTION("Nodes of elements are not filled");
+
+  if ( _coords.empty() )
+    THROW_IK_EXCEPTION("Node coordinates are missing");
+
+  if ( _coords.size() < _nbNodes * _spaceDim )
+    THROW_IK_EXCEPTION("Nodes and coordinates mismatch");
+}
+
+//================================================================================
+/*!
+ * \brief Makes ParaMEDMEM::MEDFileData from self
+ */
+//================================================================================
+
+ParaMEDMEM::MEDFileData* IntermediateMED::convertInMEDFileDS()
+{
+  MEDCouplingAutoRefCountObjectPtr< MEDFileUMesh >  mesh   = makeMEDFileMesh();
+  MEDCouplingAutoRefCountObjectPtr< MEDFileFields > fields = makeMEDFileFields(mesh);
+
+  MEDCouplingAutoRefCountObjectPtr< MEDFileMeshes > meshes = MEDFileMeshes::New();
+  MEDCouplingAutoRefCountObjectPtr< MEDFileData >  medData = MEDFileData::New();
+  meshes->pushMesh( mesh );
+  medData->setMeshes( meshes );
+  if ( fields ) medData->setFields( fields );
+
+  medData->incrRef();
+  return medData;
+}
+
+//================================================================================
+/*!
+ * \brief Creates ParaMEDMEM::MEDFileUMesh from its data
+ */
+//================================================================================
+
+ParaMEDMEM::MEDFileUMesh* IntermediateMED::makeMEDFileMesh()
+{
+  // check if all needed piles are present
+  checkDataAvailability();
+
+  // set long names
+  setGroupLongNames();
+
+  // fix element orientation
+  if ( _spaceDim == 2 )
+    orientElements2D();
+  else if ( _spaceDim == 3 )
+    orientElements3D();
+
+  // process groups
+  decreaseHierarchicalDepthOfSubgroups();
+  eraseUselessGroups();
+  detectMixDimGroups();
+
+  // assign IDs
+  _points.numberNodes();
+  numberElements();
+
+  // make the med mesh
+
+  MEDFileUMesh* mesh = MEDFileUMesh::New();
+
+  DataArrayDouble *coords = getCoords();
+  setConnectivity( mesh, coords );
+  setGroups( mesh );
+
+  coords->decrRef();
+
+  if ( !mesh->getName() || strlen( mesh->getName() ) == 0 )
+    mesh->setName( "MESH" );
+
+  return mesh;
+}
+
+//================================================================================
+/*!
+ * \brief Set long names to groups
+ */
+//================================================================================
+
+void IntermediateMED::setGroupLongNames()
+{
+  // IMP 0020434: mapping GIBI names to MED names
+  // set med names to objects (mesh, fields, support, group or other)
+
+  set<int> treatedGroups;
+
+  list<nameGIBItoMED>::iterator itGIBItoMED = _listGIBItoMED_mail.begin();
+  for (; itGIBItoMED != _listGIBItoMED_mail.end(); itGIBItoMED++)
+    {
+      if ( (int)_groups.size() < itGIBItoMED->gibi_id ) continue;
+
+      SauvUtilities::Group & grp = _groups[itGIBItoMED->gibi_id - 1];
+
+      // if there are several names for grp then the 1st name is the name
+      // of grp and the rest ones are names of groups referring grp (issue 0021311)
+      const bool isRefName = !treatedGroups.insert( itGIBItoMED->gibi_id ).second;
+      if ( !isRefName )
+        grp._name = _mapStrings[ itGIBItoMED->med_id ];
+      else
+        for ( unsigned i = 0; i < grp._refNames.size(); ++i )
+          if ( grp._refNames[i].empty() )
+            grp._refNames[i] = _mapStrings[ (*itGIBItoMED).med_id ];
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Set long names to fields
+ */
+//================================================================================
+
+void IntermediateMED::setFieldLongNames(set< string >& usedNames)
+{
+  list<nameGIBItoMED>::iterator itGIBItoMED = _listGIBItoMED_cham.begin();
+  for (; itGIBItoMED != _listGIBItoMED_cham.end(); itGIBItoMED++)
+    {
+      if (itGIBItoMED->gibi_pile == PILE_FIELD)
+        {
+          _cellFields[itGIBItoMED->gibi_id - 1]->_name = _mapStrings[itGIBItoMED->med_id];
+        }
+      else if (itGIBItoMED->gibi_pile == PILE_NODES_FIELD)
+        {
+          _nodeFields[itGIBItoMED->gibi_id - 1]->_name = _mapStrings[itGIBItoMED->med_id];
+        }
+    } // iterate on _listGIBItoMED_cham
+
+  for (itGIBItoMED =_listGIBItoMED_comp.begin(); itGIBItoMED != _listGIBItoMED_comp.end(); itGIBItoMED++)
+    {
+      string medName  = _mapStrings[itGIBItoMED->med_id];
+      string gibiName = _mapStrings[itGIBItoMED->gibi_id];
+
+      bool name_found = false;
+      for ( int isNodal = 0; isNodal < 2 && !name_found; ++isNodal )
+        {
+          vector<DoubleField* > & fields = isNodal ? _nodeFields : _cellFields;
+          for ( size_t ifi = 0; ifi < fields.size() && !name_found; ifi++)
+            {
+              if (medName.find( fields[ifi]->_name + "." ) == 0 )
+                {
+                  vector<DoubleField::_Sub_data>& aSubDs = fields[ifi]->_sub;
+                  int nbSub = aSubDs.size();
+                  for (int isu = 0; isu < nbSub; isu++)
+                    for (int ico = 0; ico < aSubDs[isu].nbComponents(); ico++)
+                      {
+                        if (aSubDs[isu].compName(ico) == gibiName)
+                          {
+                            string medNameCompo = medName.substr( fields[ifi]->_name.size() + 1 );
+                            fields[ifi]->_sub[isu].compName(ico) = medNameCompo;
+                          }
+                      }
+                }
+            }
+        }
+    } // iterate on _listGIBItoMED_comp
+
+  for ( size_t i = 0; i < _nodeFields.size() ; i++)
+    usedNames.insert( _nodeFields[i]->_name );
+  for ( size_t i = 0; i < _cellFields.size() ; i++)
+    usedNames.insert( _cellFields[i]->_name );
+}
+
+//================================================================================
+/*!
+ * \brief Decrease hierarchical depth of subgroups
+ */
+//================================================================================
+
+void IntermediateMED::decreaseHierarchicalDepthOfSubgroups()
+{
+  for (size_t i=0; i!=_groups.size(); ++i)
+  {
+    Group& grp = _groups[i];
+    for (size_t j = 0; j < grp._groups.size(); ++j )
+    {
+      Group & sub_grp = *grp._groups[j];
+      if ( !sub_grp._groups.empty() )
+      {
+        // replace j with its 1st subgroup
+        grp._groups[j] = sub_grp._groups[0];
+        // push back the rest subs
+        grp._groups.insert( grp._groups.end(), ++sub_grp._groups.begin(), sub_grp._groups.end() );
+      }
+    }
+    // remove empty sub-_groups
+    vector< Group* > newSubGroups;
+    newSubGroups.reserve( grp._groups.size() );
+    for (size_t j = 0; j < grp._groups.size(); ++j )
+      if ( !grp._groups[j]->empty() )
+        newSubGroups.push_back( grp._groups[j] );
+    if ( newSubGroups.size() < grp._groups.size() )
+      grp._groups.swap( newSubGroups );
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Erase _groups that won't be converted
+ */
+//================================================================================
+
+void IntermediateMED::eraseUselessGroups()
+{
+  // propagate _isProfile=true to sub-groups of composite groups
+  // for (size_t int i=0; i!=_groups.size(); ++i)
+  // {
+  //   Group* grp = _groups[i];
+  //   if ( grp->_isProfile && !grp->_groups.empty() )
+  //     for (size_t j = 0; j < grp->_groups.size(); ++j )
+  //       grp->_groups[j]->_isProfile=true;
+  // }
+  std::set<Group*> groups2convert;
+  // keep not named sub-groups of field supports
+  for (size_t i=0; i!=_groups.size(); ++i)
+  {
+    Group& grp = _groups[i];
+    if ( grp._isProfile && !grp._groups.empty() )
+      groups2convert.insert( grp._groups.begin(), grp._groups.end() );
+  }
+
+  // keep named groups and their subgroups
+  for (size_t i=0; i!=_groups.size(); ++i)
+  {
+    Group& grp = _groups[i];
+    if ( !grp._name.empty() && !grp.empty() )
+    {
+      groups2convert.insert( &grp );
+      groups2convert.insert( grp._groups.begin(), grp._groups.end() );
+    }
+  }
+  // erase groups that are not in groups2convert and not _isProfile
+  for (size_t i=0; i!=_groups.size(); ++i)
+  {
+    Group* grp = &_groups[i];
+    if ( !grp->_isProfile && !groups2convert.count( grp ) )
+    {
+      grp->_cells.clear();
+      grp->_groups.clear();
+    }
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Detect _groups of mixed dimension
+ */
+//================================================================================
+
+void IntermediateMED::detectMixDimGroups()
+{
+  //hasMixedCells = false;
+  for ( size_t i=0; i < _groups.size(); ++i )
+  {
+    Group& grp = _groups[i];
+    if ( grp._groups.size() < 2 )
+      continue;
+
+    // check if sub-groups have different dimension
+    unsigned dim1 = getDim( &grp );
+    for ( size_t j = 1; j  < grp._groups.size(); ++j )
+    {
+      unsigned dim2 = getDim( &grp );
+      if ( dim1 != dim2 )
+      {
+        grp._cells.clear();
+        grp._groups.clear();
+        if ( !grp._name.empty() )
+          cout << "Erase a group with elements of different dim |" << grp._name << "|"<< endl;
+        break;
+      }
+    }
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Fix connectivity of elements in 2D space
+ */
+//================================================================================
+
+void IntermediateMED::orientElements2D()
+{
+  set<Cell>::const_iterator elemIt, elemEnd;
+  vector< pair<int,int> > swapVec;
+
+  // ------------------------------------
+  // fix connectivity of quadratic edges
+  // ------------------------------------
+  set<Cell>& quadEdges = _cellsByType[ INTERP_KERNEL::NORM_SEG3 ];
+  if ( !quadEdges.empty() )
+    {
+      elemIt = quadEdges.begin(), elemEnd = quadEdges.end();
+      for ( ; elemIt != elemEnd; ++elemIt )
+        ConvertQuadratic( INTERP_KERNEL::NORM_SEG3, *elemIt );
+    }
+
+  CellsByDimIterator faceIt( *this, 2 );
+  while ( const set<Cell > * faces = faceIt.nextType() )
+    {
+      TCellType cellType = faceIt.type();
+      bool isQuadratic = getGibi2MedQuadraticInterlace( cellType );
+
+      getReverseVector( cellType, swapVec );
+
+      // ------------------------------------
+      // fix connectivity of quadratic faces
+      // ------------------------------------
+      if ( isQuadratic )
+        for ( elemIt = faces->begin(), elemEnd = faces->end(); elemIt != elemEnd; elemIt++ )
+          ConvertQuadratic( cellType, *elemIt );
+
+      // --------------------------
+      // orient faces clockwise
+      // --------------------------
+      int iQuad = isQuadratic ? 2 : 1;
+      for ( elemIt = faces->begin(), elemEnd = faces->end(); elemIt != elemEnd; elemIt++ )
+        {
+          // look for index of the most left node
+          int iLeft = 0, iNode, nbNodes = elemIt->_nodes.size() / iQuad;
+          double x, minX = nodeCoords( elemIt->_nodes[0] )[0];
+          for ( iNode = 1; iNode < nbNodes; ++iNode )
+            if (( x = nodeCoords( elemIt->_nodes[ iNode ])[ 0 ]) < minX )
+              minX = x, iLeft = iNode;
+
+          // indeces of the nodes neighboring the most left one
+          int iPrev = ( iLeft - 1 < 0 ) ? nbNodes - 1 : iLeft - 1;
+          int iNext = ( iLeft + 1 == nbNodes ) ? 0 : iLeft + 1;
+          // find components of prev-left and left-next vectors
+          double xP = nodeCoords( elemIt->_nodes[ iPrev ])[ 0 ];
+          double yP = nodeCoords( elemIt->_nodes[ iPrev ])[ 1 ];
+          double xN = nodeCoords( elemIt->_nodes[ iNext ])[ 0 ];
+          double yN = nodeCoords( elemIt->_nodes[ iNext ])[ 1 ];
+          double xL = nodeCoords( elemIt->_nodes[ iLeft ])[ 0 ];
+          double yL = nodeCoords( elemIt->_nodes[ iLeft ])[ 1 ];
+          double xPL = xL - xP, yPL = yL - yP; // components of prev-left vector
+          double xLN = xN - xL, yLN = yN - yL; // components of left-next vector
+          // normalise y of the vectors
+          double modPL = sqrt ( xPL * xPL + yPL * yPL );
+          double modLN = sqrt ( xLN * xLN + yLN * yLN );
+          if ( modLN > std::numeric_limits<double>::min() &&
+               modPL > std::numeric_limits<double>::min() )
+            {
+              yPL /= modPL;
+              yLN /= modLN;
+              // summary direction of neighboring links must be positive
+              bool clockwise = ( yPL + yLN > 0 );
+              if ( !clockwise )
+                reverse( *elemIt, swapVec );
+            }
+        }
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Fix connectivity of elements in 3D space
+ */
+//================================================================================
+
+void IntermediateMED::orientElements3D()
+{
+  // set _reverse flags of faces
+  orientFaces3D();
+
+  // -----------------
+  // fix connectivity
+  // -----------------
+
+  set<Cell>::const_iterator elemIt, elemEnd;
+  vector< pair<int,int> > swapVec;
+
+  for ( int dim = 1; dim <= 3; ++dim )
+  {
+    CellsByDimIterator cellsIt( *this, dim );
+    while ( const set<Cell > * elems = cellsIt.nextType() )
+    {
+      TCellType cellType = cellsIt.type();
+      bool isQuadratic = getGibi2MedQuadraticInterlace( cellType );
+      getReverseVector( cellType, swapVec );
+
+      elemIt = elems->begin(), elemEnd = elems->end();
+      for ( ; elemIt != elemEnd; elemIt++ )
+      {
+        // GIBI connectivity -> MED one
+        if( isQuadratic )
+          ConvertQuadratic( cellType, *elemIt );
+
+        // reverse faces
+        if ( elemIt->_reverse )
+          reverse ( *elemIt, swapVec );
+      }
+    }
+  }
+
+  orientVolumes();
+}
+
+//================================================================================
+/*!
+ * \brief Orient equally (by setting _reverse flag) all connected faces in 3D space
+ */
+//================================================================================
+
+void IntermediateMED::orientFaces3D()
+{
+  // fill map of links and their faces
+  set<const Cell*> faces;
+  map<const Cell*, Group*> fgm;
+  map<Link, list<const Cell*> > linkFacesMap;
+  map<Link, list<const Cell*> >::iterator lfIt, lfIt2;
+
+  for (size_t i=0; i!=_groups.size(); ++i)
+    {
+      Group& grp = _groups[i];
+      if ( !grp._cells.empty() && getDimension( grp._cellType ) == 2 )
+        for ( size_t j = 0; j < grp._cells.size(); ++j )
+          if ( faces.insert( grp._cells[j] ).second )
+            {
+              for ( size_t k = 0; k < grp._cells[j]->_nodes.size(); ++k )
+                linkFacesMap[ grp._cells[j]->link( k ) ].push_back( grp._cells[j] );
+              fgm.insert( make_pair( grp._cells[j], &grp ));
+            }
+    }
+  // dump linkFacesMap
+  //     for ( lfIt = linkFacesMap.begin(); lfIt!=linkFacesMap.end(); lfIt++) {
+  //       cout<< "LINK: " << lfIt->first.first << "-" << lfIt->first.second << endl;
+  //       list<const Cell*> & fList = lfIt->second;
+  //       list<const Cell*>::iterator fIt = fList.begin();
+  //       for ( ; fIt != fList.end(); fIt++ )
+  //         cout << "\t" << **fIt << fgm[*fIt]->nom << endl;
+  //     }
+
+  // Each oriented link must appear in one face only, else a face is reversed.
+
+  queue<const Cell*> faceQueue; /* the queue contains well oriented faces
+                                     whose neighbors orientation is to be checked */
+  bool manifold = true;
+  while ( !linkFacesMap.empty() )
+    {
+      if ( faceQueue.empty() )
+        {
+          assert( !linkFacesMap.begin()->second.empty() );
+          faceQueue.push( linkFacesMap.begin()->second.front() );
+        }
+      while ( !faceQueue.empty() )
+        {
+          const Cell* face = faceQueue.front();
+          faceQueue.pop();
+
+          // loop on links of <face>
+          for ( int i = 0; i < (int)face->_nodes.size(); ++i )
+            {
+              Link link = face->link( i );
+              // find the neighbor faces
+              lfIt = linkFacesMap.find( link );
+              int nbFaceByLink = 0;
+              list< const Cell* > ml;
+              if ( lfIt != linkFacesMap.end() )
+                {
+                  list<const Cell*> & fList = lfIt->second;
+                  list<const Cell*>::iterator fIt = fList.begin();
+                  assert( fIt != fList.end() );
+                  for ( ; fIt != fList.end(); fIt++, nbFaceByLink++ )
+                    {
+                      ml.push_back( *fIt );
+                      if ( *fIt != face ) // wrongly oriented neighbor face
+                        {
+                          const Cell* badFace = *fIt;
+                          // reverse and remove badFace from linkFacesMap
+                          for ( int j = 0; j < (int)badFace->_nodes.size(); ++j )
+                            {
+                              Link badlink = badFace->link( j );
+                              if ( badlink == link ) continue;
+                              lfIt2 = linkFacesMap.find( badlink );
+                              if ( lfIt2 != linkFacesMap.end() )
+                                {
+                                  list<const Cell*> & ff = lfIt2->second;
+                                  ff.erase( find( ff.begin(), ff.end(), badFace ));
+                                  if ( ff.empty() )
+                                    linkFacesMap.erase( lfIt2 );
+                                }
+                            }
+                          badFace->_reverse = true; // reverse
+                          //INFOS_MED( "REVERSE " << *badFace );
+                          faceQueue.push( badFace );
+                        }
+                    }
+                  linkFacesMap.erase( lfIt );
+                }
+              // add good neighbors to the queue
+              Link revLink( link.second, link.first );
+              lfIt = linkFacesMap.find( revLink );
+              if ( lfIt != linkFacesMap.end() )
+                {
+                  list<const Cell*> & fList = lfIt->second;
+                  list<const Cell*>::iterator fIt = fList.begin();
+                  for ( ; fIt != fList.end(); fIt++, nbFaceByLink++ )
+                    {
+                      ml.push_back( *fIt );
+                      if ( *fIt != face )
+                        faceQueue.push( *fIt );
+                    }
+                  linkFacesMap.erase( lfIt );
+                }
+              if ( nbFaceByLink > 2 )
+                {
+                  if ( manifold )
+                    {
+                      list<const Cell*>::iterator i = ml.begin();
+                      cout << nbFaceByLink << " faces by 1 link:";
+                      for( ; i!= ml.end(); i++ )
+                        cout << "in sub-mesh " << fgm[ *i ]->_name << endl << **i;
+                    }
+                  manifold = false;
+                }
+            } // loop on links of the being checked face
+        } // loop on the face queue
+    } // while ( !linkFacesMap.empty() )
+
+  if ( !manifold )
+    cout << " -> Non manifold mesh, faces orientation may be incorrect" << endl;
+}
+
+//================================================================================
+/*!
+ * \brief Orient volumes according to MED conventions:
+ * normal of a bottom (first) face should be outside
+ */
+//================================================================================
+
+void IntermediateMED::orientVolumes()
+{
+  set<Cell>::const_iterator elemIt, elemEnd;
+  vector< pair<int,int> > swapVec;
+
+  CellsByDimIterator cellsIt( *this, 3 );
+  while ( const set<Cell > * elems = cellsIt.nextType() )
+    {
+      TCellType cellType = cellsIt.type();
+      elemIt = elems->begin(), elemEnd = elems->end();
+      int nbBottomNodes = 0;
+      switch ( cellType )
+        {
+        case NORM_TETRA4:
+        case NORM_TETRA10:
+        case NORM_PENTA6:
+        case NORM_PENTA15:
+          nbBottomNodes = 3; break;
+        case NORM_PYRA5:
+        case NORM_PYRA13:
+        case NORM_HEXA8:
+        case NORM_HEXA20:
+          nbBottomNodes = 4; break;
+        default: continue;
+        }
+      getReverseVector( cellType, swapVec );
+
+      for ( ; elemIt != elemEnd; elemIt++ )
+        {
+          // find a normal to the bottom face
+          const double* n[4];
+          n[0] = nodeCoords( elemIt->_nodes[0]); // 3 bottom nodes
+          n[1] = nodeCoords( elemIt->_nodes[1]);
+          n[2] = nodeCoords( elemIt->_nodes[2]);
+          n[3] = nodeCoords( elemIt->_nodes[nbBottomNodes]); // a top node
+          double vec01[3]; // vector n[0]-n[1]
+          vec01[0] = n[1][0] - n[0][0];
+          vec01[1] = n[1][1] - n[0][1];
+          vec01[2] = n[1][2] - n[0][2];
+          double vec02 [3]; // vector n[0]-n[2]
+          vec02[0] = n[2][0] - n[0][0];
+          vec02[1] = n[2][1] - n[0][1];
+          vec02[2] = n[2][2] - n[0][2];
+          double normal [3]; // vec01 ^ vec02
+          normal[0] = vec01[1] * vec02[2] - vec01[2] * vec02[1];
+          normal[1] = vec01[2] * vec02[0] - vec01[0] * vec02[2];
+          normal[2] = vec01[0] * vec02[1] - vec01[1] * vec02[0];
+          // check if the 102 angle is convex
+          if ( nbBottomNodes > 3 )
+            {
+              const double* n3 = nodeCoords( elemIt->_nodes[nbBottomNodes-1] );// last bottom node
+              double vec03 [3];  // vector n[0]-n3
+              vec03[0] = n3[0] - n[0][0];
+              vec03[1] = n3[1] - n[0][1];
+              vec03[2] = n3[2] - n[0][2];
+              if ( fabs( normal[0]+normal[1]+normal[2] ) <= numeric_limits<double>::max() ) // vec01 || vec02
+                {
+                  normal[0] = vec01[1] * vec03[2] - vec01[2] * vec03[1]; // vec01 ^ vec03
+                  normal[1] = vec01[2] * vec03[0] - vec01[0] * vec03[2];
+                  normal[2] = vec01[0] * vec03[1] - vec01[1] * vec03[0];
+                }
+              else
+                {
+                  double vec [3]; // normal ^ vec01
+                  vec[0] = normal[1] * vec01[2] - normal[2] * vec01[1];
+                  vec[1] = normal[2] * vec01[0] - normal[0] * vec01[2];
+                  vec[2] = normal[0] * vec01[1] - normal[1] * vec01[0];
+                  double dot2 = vec[0]*vec03[0] + vec[1]*vec03[1] + vec[2]*vec03[2]; // vec*vec03
+                  if ( dot2 < 0 ) // concave -> reverse normal
+                    {
+                      normal[0] *= -1;
+                      normal[1] *= -1;
+                      normal[2] *= -1;
+                    }
+                }
+            }
+          // direction from top to bottom
+          double tbDir[3];
+          tbDir[0] = n[0][0] - n[3][0];
+          tbDir[1] = n[0][1] - n[3][1];
+          tbDir[2] = n[0][2] - n[3][2];
+
+          // compare 2 directions: normal and top-bottom
+          double dot = normal[0]*tbDir[0] + normal[1]*tbDir[1] + normal[2]*tbDir[2];
+          if ( dot < 0. ) // need reverse
+            reverse( *elemIt, swapVec );
+
+        } // loop on volumes of one geometry
+    } // loop on 3D geometry types
+
+}
+
+//================================================================================
+/*!
+ * \brief Assign new IDs to nodes by skipping not used nodes and return their number
+ */
+//================================================================================
+
+int NodeContainer::numberNodes()
+{
+  int id = 1;
+  for ( size_t i = 0; i < _nodes.size(); ++i )
+    for ( size_t j = 0; j < _nodes[i].size(); ++j )
+      if ( _nodes[i][j].isUsed() )
+        _nodes[i][j]._number = id++;
+  return id-1;
+}
+
+
+//================================================================================
+/*!
+ * \brief Assign new IDs to elements
+ */
+//================================================================================
+
+void IntermediateMED::numberElements()
+{
+  set<Cell>::const_iterator elemIt, elemEnd;
+
+  // numbering _cells of type NORM_POINT1 by node number
+  {
+    const set<Cell>& points = _cellsByType[ INTERP_KERNEL::NORM_POINT1 ];
+    elemIt = points.begin(), elemEnd = points.end();
+    for ( ; elemIt != elemEnd; ++elemIt )
+      elemIt->_number = elemIt->_nodes[0]->_number;
+  }
+
+  // numbering 1D-3D _cells
+  for ( int dim = 1; dim <= 3; ++dim )
+    {
+      // check if re-numeration is needed (to try to keep elem oreder as in sauve file )
+      bool ok = true, renumEntity = false;
+      CellsByDimIterator cellsIt( *this, dim );
+      int prevNbElems = 0;
+      while ( const set<Cell> * typeCells = cellsIt.nextType() )
+        {
+          TID minNumber = std::numeric_limits<TID>::max(), maxNumber = 0;
+          for ( elemIt = typeCells->begin(), elemEnd = typeCells->end(); elemIt!=elemEnd; ++elemIt)
+            {
+              if ( elemIt->_number < minNumber ) minNumber = elemIt->_number;
+              if ( elemIt->_number > maxNumber ) maxNumber = elemIt->_number;
+            }
+          TID typeSize = typeCells->size();
+          if ( typeSize != maxNumber - minNumber + 1 )
+            ok = false;
+          if ( prevNbElems != 0 ) {
+            if ( minNumber == 1 )
+              renumEntity = true;
+            else if ( prevNbElems+1 != (int)minNumber )
+              ok = false;
+          }
+          prevNbElems += typeSize;
+        }
+
+      if ( ok && renumEntity ) // each geom type was numerated separately
+        {
+          cellsIt.init( dim );
+          prevNbElems = cellsIt.nextType()->size(); // no need to renumber the first type
+          while ( const set<Cell> * typeCells = cellsIt.nextType() )
+            {
+              for ( elemIt = typeCells->begin(), elemEnd = typeCells->end(); elemIt!=elemEnd; ++elemIt)
+                elemIt->_number += prevNbElems;
+              prevNbElems += typeCells->size();
+            }
+        }
+      if ( !ok )
+        {
+          int cellID=1;
+          cellsIt.init( dim );
+          while ( const set<Cell> * typeCells = cellsIt.nextType() )
+            for ( elemIt = typeCells->begin(), elemEnd = typeCells->end(); elemIt!=elemEnd; ++elemIt)
+              elemIt->_number = cellID++;
+        }
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Creates coord array
+ */
+//================================================================================
+
+ParaMEDMEM::DataArrayDouble * IntermediateMED::getCoords()
+{
+  DataArrayDouble* coordArray = DataArrayDouble::New();
+  coordArray->alloc( _nbNodes, _spaceDim );
+  double * coordPrt = coordArray->getPointer();
+  for ( int i = 0, nb = _points.size(); i < nb; ++i )
+    {
+      Node* n = getNode( i+1 );
+      if ( n->isUsed() )
+        {
+          const double* nCoords = nodeCoords( n );
+          std::copy( nCoords, nCoords+_spaceDim, coordPrt );
+          coordPrt += _spaceDim;
+        }
+    }
+  return coordArray;
+}
+
+//================================================================================
+/*!
+ * \brief Sets connectivity of elements to the mesh
+ *  \param mesh - mesh to fill in
+ *  \param coords - coordinates that must be shared by all meshes of different dim
+ */
+//================================================================================
+
+void IntermediateMED::setConnectivity( ParaMEDMEM::MEDFileUMesh*    mesh,
+                                       ParaMEDMEM::DataArrayDouble* coords )
+{
+  int meshDim = 0;
+
+  mesh->setCoords( coords );
+
+  set<Cell>::const_iterator elemIt, elemEnd;
+  for ( int dim = 3; dim > 0; --dim )
+  {
+    CellsByDimIterator dimCells( *this, dim );
+
+    int nbOfCells = 0;
+    while ( const std::set<Cell > * cells = dimCells.nextType() )
+      nbOfCells += cells->size();
+    if ( nbOfCells == 0 )
+      continue;
+
+    if ( !meshDim ) meshDim = dim;
+
+    MEDCouplingUMesh* dimMesh = MEDCouplingUMesh::New();
+    dimMesh->setCoords( coords );
+    dimMesh->setMeshDimension( dim );
+    dimMesh->allocateCells( nbOfCells );
+
+    int prevNbCells = 0;
+    dimCells.init( dim );
+    while ( const std::set<Cell > * cells = dimCells.nextType() )
+      {
+        // fill connectivity array to take into account order of elements in the sauv file
+        const int nbCellNodes = cells->begin()->_nodes.size();
+        vector< TID > connectivity( cells->size() * nbCellNodes );
+        int * nodalConnOfCell;
+        for ( elemIt = cells->begin(), elemEnd = cells->end(); elemIt != elemEnd; ++elemIt )
+          {
+            const Cell& cell = *elemIt;
+            const int index = cell._number - 1 - prevNbCells;
+            nodalConnOfCell = &connectivity[ index * nbCellNodes ];
+            if ( cell._reverse )
+              for ( int i = nbCellNodes-1; i >= 0; --i )
+                *nodalConnOfCell++ = cell._nodes[i]->_number - 1;
+            else
+              for ( int i = 0; i < nbCellNodes; ++i )
+                *nodalConnOfCell++ = cell._nodes[i]->_number - 1;
+          }
+        prevNbCells += cells->size();
+
+        // fill dimMesh
+        TCellType cellType = dimCells.type();
+        nodalConnOfCell = &connectivity[0];
+        for ( size_t i = 0; i < cells->size(); ++i, nodalConnOfCell += nbCellNodes )
+          dimMesh->insertNextCell( cellType, nbCellNodes, nodalConnOfCell );
+      }
+    dimMesh->finishInsertingCells();
+    mesh->setMeshAtLevel( dim - meshDim, dimMesh );
+    dimMesh->decrRef();
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Fill in the mesh with groups
+ *  \param mesh - mesh to fill in
+ */
+//================================================================================
+
+void IntermediateMED::setGroups( ParaMEDMEM::MEDFileUMesh* mesh )
+{
+  const int meshDim = mesh->getMeshDimension();
+  for ( int dim = 0; dim <= meshDim; ++dim )
+    {
+      const int meshDimRelToMaxExt = ( dim == 0 ? 1 : dim - meshDim );
+
+      vector<const DataArrayInt *> medGroups;
+      vector<MEDCouplingAutoRefCountObjectPtr<DataArrayInt> > refGroups;
+      for ( size_t i = 0; i < _groups.size(); ++i )
+        {
+          Group& grp = _groups[i];
+          if ( getDim( &grp ) != dim )
+            continue;
+          // convert only named groups or field supports
+          if ( grp.empty() || (grp._name.empty() && !grp._isProfile ))
+            continue;
+          //if ( grp._medGroup ) continue; // already converted
+
+          // sort cells by ID and remember their initial order in the group
+          TCellToOrderMap cell2order;
+          unsigned orderInGroup = 0;
+          vector< Group* > groupVec;
+          if ( grp._groups.empty() ) groupVec.push_back( & grp );
+          else                       groupVec = grp._groups;
+          for ( size_t iG = 0; iG < groupVec.size(); ++iG )
+            {
+              Group* aG = groupVec[ iG ];
+              for ( size_t iC = 0; iC < aG->_cells.size(); ++iC )
+                cell2order.insert( cell2order.end(), make_pair( aG->_cells[iC], orderInGroup++ ));
+            }
+          bool isSelfIntersect = ( orderInGroup != cell2order.size() );
+          if ( isSelfIntersect ) // self intersecting group
+            {
+              ostringstream msg;
+              msg << "Self intersecting sub-mesh: id = " << i+1
+                  << ", name = |" << grp._name << "|" << endl
+                  << " nb unique elements = " << cell2order.size() << endl
+                  << " total nb elements  = " << orderInGroup;
+              if ( grp._isProfile )
+                {
+                  THROW_IK_EXCEPTION( msg.str() );
+                }
+              else
+                {
+                  cout << msg << endl;
+                }
+            }
+          // create a med group
+          grp._medGroup = DataArrayInt::New();
+          grp._medGroup->setName( grp._name.c_str() );
+          grp._medGroup->alloc( orderInGroup, /*nbOfCompo=*/1 );
+          int * idsPrt = grp._medGroup->getPointer();
+          TCellToOrderMap::iterator cell2orderIt, cell2orderEnd = cell2order.end();
+          for ( cell2orderIt = cell2order.begin(); cell2orderIt != cell2orderEnd; ++cell2orderIt )
+            *idsPrt++ = (*cell2orderIt).first->_number - 1;
+
+          // try to set the mesh name
+          if ( dim == meshDim &&
+               !grp._name.empty() &&
+               grp.size() == mesh->getSizeAtLevel( meshDimRelToMaxExt ))
+            {
+              mesh->setName( grp._name.c_str() );
+            }
+          else if ( !grp._name.empty() )
+            {
+              medGroups.push_back( grp._medGroup );
+            }
+          // set relocation table
+          setRelocationTable( &grp, cell2order );
+
+          // Issue 0021311. Use case: a gibi group has references (recorded in pile 1)
+          // and several names (pile 27) refer (pile 10) to this group.
+          // We create a copy of this group per each named reference
+          for ( unsigned iRef = 0 ; iRef < grp._refNames.size(); ++iRef )
+            if ( !grp._refNames[ iRef ].empty() )
+              {
+                refGroups.push_back( grp._medGroup->deepCpy() );
+                refGroups.back()->setName( grp._refNames[ iRef ].c_str() );
+                medGroups.push_back( refGroups.back() );
+              }
+        }
+      mesh->setGroupsAtLevel( meshDimRelToMaxExt, medGroups );
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Return true if the group is on all elements and return its relative dimension
+ */
+//================================================================================
+
+bool IntermediateMED::isOnAll( const Group* grp, int & dimRel ) const
+{
+  int dim = getDim( grp );
+
+  int nbElems = 0;
+  CellsByDimIterator dimCells( *this, dim );
+  while ( const set<Cell > * cells = dimCells.nextType() )
+    nbElems += cells->size();
+
+  const bool onAll = ( nbElems == grp->size() );
+
+  if ( dim == 0 )
+    dimRel = 0;
+  else
+    {
+      int meshDim = 3;
+      for ( ; meshDim > 0; --meshDim )
+        {
+          dimCells.init( meshDim );
+          if ( dimCells.nextType() )
+            break;
+        }
+      dimRel = dim - meshDim;
+    }
+  return onAll;
+}
+
+//================================================================================
+/*!
+ * \brief Makes fields from own data
+ */
+//================================================================================
+
+ParaMEDMEM::MEDFileFields * IntermediateMED::makeMEDFileFields(ParaMEDMEM::MEDFileUMesh* mesh)
+{
+  if ( _nodeFields.empty() && _cellFields.empty() ) return 0;
+
+  // set long names
+  set< string > usedFieldNames;
+  setFieldLongNames(usedFieldNames);
+
+  MEDFileFields* fields = MEDFileFields::New();
+
+  for ( size_t i = 0; i < _nodeFields.size(); ++i )
+    setFields( _nodeFields[i], fields, mesh, i+1, usedFieldNames );
+
+  for ( size_t i = 0; i < _cellFields.size(); ++i )
+    setFields( _cellFields[i], fields, mesh, i+1, usedFieldNames );
+
+  return fields;
+}
+
+//================================================================================
+/*!
+ * \brief Make med fields from a SauvUtilities::DoubleField
+ */
+//================================================================================
+
+void IntermediateMED::setFields( SauvUtilities::DoubleField* fld,
+                                 ParaMEDMEM::MEDFileFields*  medFields,
+                                 ParaMEDMEM::MEDFileUMesh*   mesh,
+                                 const TID                   castemID,
+                                 set< string >&              usedFieldNames)
+{
+  if ( !fld || !fld->isMedCompatible() ) return;
+
+  // if ( !fld->hasCommonSupport() ):
+  //     each sub makes MEDFileFieldMultiTS
+  // else:
+  //     unite several subs into a MEDCouplingFieldDouble
+
+  const bool uniteSubs = fld->hasCommonSupport();
+  if ( !uniteSubs )
+    cout << "Castem field #" << castemID << " " << fld->_name
+         << " is incompatible with MED format, so we split it into several fields" << endl;
+
+  for ( size_t iSub = 0; iSub < fld->_sub.size(); )
+    {
+      // set field name
+      if ( !uniteSubs || fld->_name.empty() )
+        makeFieldNewName( usedFieldNames, fld );
+
+      // allocate values
+      DataArrayDouble * values = DataArrayDouble::New();
+      values->alloc( fld->getNbTuples(iSub), fld->_sub[iSub].nbComponents() );
+
+      // set values
+      double * valPtr = values->getPointer();
+      if ( uniteSubs )
+        {
+          int nbElems = fld->_group->size();
+          for ( int elemShift = 0; elemShift < nbElems; )
+            elemShift += fld->setValues( valPtr, iSub++, elemShift );
+          setTS( fld, values, medFields, mesh );
+        }
+      else
+        {
+          fld->setValues( valPtr, iSub++ );
+          setTS( fld, values, medFields, mesh, iSub );
+        }
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Store value array of a field into med fields
+ */
+//================================================================================
+
+void IntermediateMED::setTS( SauvUtilities::DoubleField*  fld,
+                             ParaMEDMEM::DataArrayDouble* values,
+                             ParaMEDMEM::MEDFileFields*   medFields,
+                             ParaMEDMEM::MEDFileUMesh*    mesh,
+                             const int                    iSub)
+{
+  // analyze a field support
+  const Group* support = fld->getSupport();
+  int dimRel;
+  const bool onAll = isOnAll( support, dimRel );
+  if ( !onAll && support->_name.empty() )
+    {
+      const_cast<Group*>(support)->_name += "PFL_" + fld->_name;
+      support->_medGroup->setName( support->_name.c_str() );
+    }
+
+  // make a time-stamp
+  MEDCouplingFieldDouble * timeStamp = MEDCouplingFieldDouble::New( fld->getMedType(),
+                                                                    fld->getMedTimeDisc() );
+  timeStamp->setName( fld->_name.c_str() );
+  timeStamp->setDescription( fld->_description.c_str() );
+  MEDCouplingAutoRefCountObjectPtr< MEDCouplingUMesh > dimMesh = mesh->getMeshAtLevel( dimRel );
+  timeStamp->setMesh( dimMesh );
+  for ( size_t i = 0; i < fld->_sub[iSub].nbComponents(); ++i )
+    values->setInfoOnComponent( i, fld->_sub[iSub]._comp_names[ i ].c_str() );
+  timeStamp->setArray( values );
+  values->decrRef();
+
+  // get a field to add the time-stamp
+  bool isNewMedField = false;
+  if ( !fld->_curMedField || fld->_name != fld->_curMedField->getName() )
+    {
+      fld->_curMedField = MEDFileFieldMultiTS::New();
+      isNewMedField = true;
+    }
+
+  // set an order
+  timeStamp->setOrder( fld->_curMedField->getNumberOfTS() );
+
+  // add the time-stamp
+  if ( onAll )
+    fld->_curMedField->appendFieldNoProfileSBT( timeStamp );
+  else
+    fld->_curMedField->appendFieldProfile( timeStamp, mesh, dimRel, support->_medGroup );
+  timeStamp->decrRef();
+
+  if ( isNewMedField ) // timeStamp must be added before this
+    {
+      medFields->pushField( fld->_curMedField );
+      fld->_curMedField->decrRef();
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Make a new unique name for a field
+ */
+//================================================================================
+
+void IntermediateMED::makeFieldNewName(std::set< std::string >&    usedNames,
+                                       SauvUtilities::DoubleField* fld )
+{
+  string base = fld->_name;
+  if ( base.empty() )
+    {
+      base = "F_";
+    }
+  else
+    {
+      string::size_type pos = base.rfind('_');
+      if ( pos != string::npos )
+        base = base.substr( 0, pos+1 );
+      else
+        base += '_';
+    }
+
+  int i = 1;
+  do
+    {
+      fld->_name = base + SauvUtilities::toString( i++ );
+    }
+  while( !usedNames.insert( fld->_name ).second );
+}
+
+//================================================================================
+/*!
+ * \brief Return a vector ready to fill in
+ */
+//================================================================================
+
+std::vector< double >& DoubleField::addComponent( int nb_values )
+{
+  _comp_values.push_back( std::vector< double >() );
+  std::vector< double >& res = _comp_values.back();
+  res.resize( nb_values );
+  return res;
+}
+//================================================================================
+/*!
+ * \brief Returns a supporting group
+ */
+//================================================================================
+
+const Group* DoubleField::getSupport( const int iSub ) const
+{
+  return _group ? _group : _sub[iSub]._support;
+}
+
+//================================================================================
+/*!
+ * \brief Return true if each sub-component is a time stamp
+ */
+//================================================================================
+
+bool DoubleField::isMultiTimeStamps() const
+{
+  if ( _sub.size() < 2 )
+    return false;
+  bool sameSupports = true;
+  Group* grp1 = _sub[0]._support;
+  for ( size_t i = 1; i < _sub.size() && sameSupports; ++i )
+    sameSupports = ( grp1 == _sub[i]._support );
+
+  return sameSupports;
+}
+
+//================================================================================
+/*!
+ * \brief True if the field can be converted into the med field
+ */
+//================================================================================
+
+bool DoubleField::isMedCompatible() const
+{
+  for ( size_t iSub = 0; iSub < _sub.size(); ++iSub )
+    {
+      if ( !getSupport(iSub) || !getSupport(iSub)->_medGroup )
+        THROW_IK_EXCEPTION("SauvReader INTERNAL ERROR: NULL field support");
+
+      if ( !_sub[iSub].isValidNbGauss() )
+        {
+          cout << "Skip field <" << _name << "> : different nb of gauss points in components" <<endl;
+          return false;
+        }
+    }
+  // check if there are no gauss or nbGauss() == nbCellNodes,
+  // else we lack info on gauss point localization
+  // TODO?
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief return true if all sub-components has same components and same nbGauss
+ */
+//================================================================================
+
+bool DoubleField::hasSameComponentsBySupport() const
+{
+  vector< _Sub_data >::const_iterator sub_data = _sub.begin();
+  const _Sub_data& first_sub_data = *sub_data;
+  for ( ++sub_data ; sub_data != _sub.end(); ++sub_data )
+  {
+    if ( first_sub_data._comp_names != sub_data->_comp_names )
+      return false; // diff names of components
+
+    if ( first_sub_data._nb_gauss != sub_data->_nb_gauss &&
+         first_sub_data._support->_cellType == sub_data->_support->_cellType)
+      return false; // diff nb of gauss points on same cell type
+  }
+  return true;
+}
+
+//================================================================================
+/*!
+ * \brief Return type of MEDCouplingFieldDouble
+ */
+//================================================================================
+
+ParaMEDMEM::TypeOfField DoubleField::getMedType( const int iSub ) const
+{
+  using namespace INTERP_KERNEL;
+
+  const Group* grp = hasCommonSupport() ? _group : _sub[iSub]._support;
+  if ( _sub[iSub].nbGauss() > 1 )
+    {
+      const CellModel& cm = CellModel::GetCellModel( _sub[iSub]._support->_cellType );
+      return cm.getNumberOfNodes() == _sub[iSub].nbGauss() ? ON_GAUSS_NE : ON_GAUSS_PT;
+    }
+  else
+    {
+      return getDim( grp ) == 0 ? ON_NODES : ON_CELLS;
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Return TypeOfTimeDiscretization
+ */
+//================================================================================
+
+ParaMEDMEM::TypeOfTimeDiscretization DoubleField::getMedTimeDisc() const
+{
+  return ONE_TIME;
+  // NO_TIME = 4,
+  // ONE_TIME = 5,
+  // LINEAR_TIME = 6,
+  // CONST_ON_TIME_INTERVAL = 7
+}
+
+//================================================================================
+/*!
+ * \brief Return nb tuples to be used to allocate DataArrayDouble
+ */
+//================================================================================
+
+int DoubleField::getNbTuples( const int iSub ) const
+{
+  int nb = 0;
+  if ( hasCommonSupport() && !_group->_groups.empty() )
+    for ( size_t i = 0; i < _group->_groups.size(); ++i )
+      nb += _sub[i].nbGauss() * _sub[i]._support->size();
+  else
+    nb = _sub[iSub].nbGauss() * getSupport(iSub)->size();
+  return nb;
+}
+
+//================================================================================
+/*!
+ * \brief Store values of a sub-component and return nb of elements in the iSub
+ */
+//================================================================================
+
+int DoubleField::setValues( double * valPtr, const int iSub, const int elemShift ) const
+{
+  // find values for iSub
+  int iComp = 0;
+  for ( int iS = 0; iS < iSub; ++iS )
+    iComp += _sub[iS].nbComponents();
+  const vector< double > * compValues = &_comp_values[ iComp ];
+
+  const vector< unsigned >& relocTable = getSupport( iSub )->_relocTable;
+
+  // Set values
+
+  const int nbElems      = _sub[iSub]._support->size();
+  const int nbGauss      = _sub[iSub].nbGauss();
+  const int nbComponents = _sub[iSub].nbComponents();
+  const int nbValsByElem = nbComponents * nbGauss;
+  // check nb values
+  int nbVals = 0;
+  for ( iComp = 0; iComp < nbComponents; ++iComp )
+    nbVals += compValues[iComp].size();
+  if ( nbVals != nbElems * nbValsByElem )
+    THROW_IK_EXCEPTION("SauvMedConvertor.cxx: support size mismatches field size");
+  // compute nb values in previous subs
+  int valsShift = 0;
+  for ( int iS = iSub-1, shift = elemShift; shift > 0; )
+  {
+    int nbE = _sub[iS]._support->size();
+    shift -= nbE;
+    valsShift += nbE * _sub[iS].nbComponents() * _sub[iS].nbGauss();
+  }
+  for ( int iE = 0; iE < nbElems; ++iE )
+    {
+      int iMed = valsShift + nbValsByElem * ( relocTable.empty() ? iE : relocTable[iE+elemShift]-elemShift );
+      for ( iComp = 0; iComp < nbComponents; ++iComp )
+        for ( int iG = 0; iG < nbGauss; ++iG )
+          valPtr[ iMed + iG * nbComponents + iComp ] = compValues[iComp][ iE * nbGauss + iG ];
+    }
+  return nbElems;
+}
+
+//================================================================================
+/*!
+ * \brief Destructor of IntermediateMED
+ */
+//================================================================================
+
+IntermediateMED::~IntermediateMED()
+{
+  for ( size_t i = 0; i < _nodeFields.size(); ++i )
+    if ( _nodeFields[i] )
+      delete _nodeFields[i];
+  _nodeFields.clear();
+
+  for ( size_t i = 0; i < _cellFields.size(); ++i )
+    if ( _cellFields[i] )
+      delete _cellFields[i];
+  _cellFields.clear();
+
+  for ( size_t i = 0; i < _groups.size(); ++i )
+    if ( _groups[i]._medGroup )
+      _groups[i]._medGroup->decrRef();
+}
+
+//================================================================================
+/*!
+ * \brief CellsByDimIterator constructor
+ */
+CellsByDimIterator::CellsByDimIterator( const IntermediateMED & medi, int dim)
+{
+  myImed = & medi;
+  init( dim );
+}
+/*!
+ * \brief Initialize iteration on cells of given dimention
+ */
+void CellsByDimIterator::init(const int  dim)
+{
+  myCurType = -1;
+  myTypeEnd = INTERP_KERNEL::NORM_HEXA20 + 1;
+  myDim = dim;
+}
+/*!
+ * \brief return next set of Cell's of required dimension
+ */
+const std::set< Cell > * CellsByDimIterator::nextType()
+{
+  while ( ++myCurType < myTypeEnd )
+    if ( !myImed->_cellsByType[myCurType].empty() && ( myDim < 0 || dim(false) == myDim ))
+      return & myImed->_cellsByType[myCurType];
+  return 0;
+}
+/*!
+ * \brief return dimension of cells returned by the last or further next()
+ */
+int CellsByDimIterator::dim(const bool last) const
+{
+  int type = myCurType;
+  if ( !last )
+    while ( type < myTypeEnd && myImed->_cellsByType[type].empty() )
+      ++type;
+  return type < myTypeEnd ? getDimension( TCellType( type )) : 4;
+}
+// END CellsByDimIterator ========================================================
+
diff --git a/src/MEDLoader/SauvMedConvertor.hxx b/src/MEDLoader/SauvMedConvertor.hxx
new file mode 100644 (file)
index 0000000..a614778
--- /dev/null
@@ -0,0 +1,383 @@
+// Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File      : SauvMedConvertor.hxx
+// Created   : Tue Aug 16 14:14:02 2011
+// Author    : Edward AGAPOV (eap)
+//
+
+#ifndef __SauvMedConvertor_HXX__
+#define __SauvMedConvertor_HXX__
+
+#include "InterpKernelException.hxx"
+#include "NormalizedUnstructuredMesh.hxx"
+#include "MEDCouplingRefCountObject.hxx"
+#include "SauvUtilities.hxx"
+
+#include <vector>
+#include <set>
+#include <map>
+#include <list>
+#include <algorithm>
+
+namespace ParaMEDMEM
+{
+  class DataArrayDouble;
+  class DataArrayInt;
+  class MEDFileData;
+  class MEDFileFields;
+  class MEDFileFieldMultiTS;
+  class MEDFileUMesh;
+}
+
+namespace SauvUtilities
+{
+  class IntermediateMED;
+
+  // ==============================================================================
+  typedef int                TID;  // an ID countered from 1
+  typedef std::pair<TID,TID> Link; // a pair of node numbers
+
+  typedef INTERP_KERNEL::NormalizedCellType TCellType;
+
+  // ==============================================================================
+  struct Node
+  {
+    TID    _number;
+    size_t _coordID;
+
+    Node():_number(0){}
+    bool isUsed() const { return _number != 0; }
+  };
+
+  // ==============================================================================
+  struct Cell
+  {
+    std::vector< Node* > _nodes;
+    mutable bool         _reverse; // to reverse orienation of a face only
+    mutable TID*         _sortedNodeIDs; // for comparison
+    mutable TID          _number;
+
+    Cell(size_t nnNodes=0) : _nodes(nnNodes),_reverse(false),_sortedNodeIDs(0),_number(0) {}
+    Cell(const Cell& ma);
+    void init() const { if ( _sortedNodeIDs ) delete [] _sortedNodeIDs; _sortedNodeIDs = 0; }
+    ~Cell() { init(); }
+
+    const TID* getSortedNodes() const; // creates if needed and return _sortedNodeIDs
+    bool operator < (const Cell& ma) const;
+    Link link(int i) const;
+
+  private:
+    Cell& operator=(const Cell& ma);
+  };
+  std::ostream& operator << (std::ostream& os, const Cell& ma);
+
+  // ==============================================================================
+  struct Group
+  {
+    TCellType                _cellType;
+    std::string              _name;
+    std::vector<const Cell*> _cells;
+    std::vector< Group* >    _groups;    // des sous-groupes composant le Group
+    //bool                     _isShared;  // true if any Cell was added to the mesh from other Group
+    bool                     _isProfile; // is a field support or not
+    std::vector<std::string> _refNames;  /* names of groups referring this one;
+                                            _refNames is resized according to nb of references
+                                            while reading a group (pile 1) and it is filled with
+                                            names while reading long names (pile 27); each named
+                                            reference is converted into a copy of the medGroup
+                                            (issue 0021311)
+                                         */
+    ParaMEDMEM::DataArrayInt* _medGroup;   // result of conversion
+    std::vector< unsigned >   _relocTable; // for _cells[i] gives its index in _medGroup
+
+    bool empty() const { return _cells.empty() && _groups.empty(); }
+    int  size()  const;
+    Group():_cellType(INTERP_KERNEL::NORM_ERROR), _isProfile(false), _medGroup(NULL) {}
+  };
+
+  // ==============================================================================
+  struct DoubleField
+  {
+    // a field contains several subcomponents each referring to its own support and
+    // having several named components
+    // ----------------------------------------------------------------------------
+    struct _Sub_data // a subcomponent
+    // --------------------------------
+    {
+      Group*                   _support;    // support
+      std::vector<std::string> _comp_names; // component names
+      std::vector<int>         _nb_gauss;   // nb values per element in a component
+
+      void setData( int nb_comp, Group* supp )
+      { _support = supp; _comp_names.resize(nb_comp); _nb_gauss.resize(nb_comp,1); }
+      int nbComponents() const { return _comp_names.size(); }
+      std::string & compName( int i_comp ) { return _comp_names[ i_comp ]; }
+      bool isValidNbGauss() const { return *std::max_element( _nb_gauss.begin(), _nb_gauss.end() ) ==
+          *std::min_element( _nb_gauss.begin(), _nb_gauss.end() ); }
+      int nbGauss() const { return _nb_gauss[0] ? _nb_gauss[0] : 1; }
+      bool hasGauss() const { return nbGauss() > 1; }
+    };
+    // ----------------------------------------------------------------------------
+    TID                      _idInFile;
+    std::string              _name;
+    std::string              _description; // field description
+    std::vector< _Sub_data > _sub;
+    Group*                   _group; /* if _group == NULL then each subcomponent makes a
+                                        separate med field, else all subcomponents
+                                        are converted into timestamps of one med field.
+                                        The latter is possible only if nb of components in all subs
+                                        is the same and supports of subcomponents do not overlap
+                                     */
+    std::vector< std::vector< double > > _comp_values;
+    ParaMEDMEM::MEDFileFieldMultiTS*     _curMedField;
+
+    DoubleField( int nb_sub, int total_nb_comp )
+      : _sub(nb_sub), _group(NULL), _curMedField(NULL) { _comp_values.reserve( total_nb_comp ); }
+    std::vector< double >& addComponent( int nb_values ); // return a vector ready to fill in
+    bool hasCommonSupport() const { return _group; } // true if there is one support for all subs
+    bool hasSameComponentsBySupport() const;
+
+    bool isMultiTimeStamps() const;
+    bool isMedCompatible() const;
+    ParaMEDMEM::TypeOfField getMedType( const int iSub=0 ) const;
+    ParaMEDMEM::TypeOfTimeDiscretization getMedTimeDisc() const;
+    int getNbTuples( const int iSub=0 ) const;
+    int getNbValuesPerElement( const int iSub=0 ) const;
+    int getNbGauss( const int iSub=0 ) const;
+    const Group* getSupport( const int iSub=0 ) const;
+    int setValues( double * valPtr, const int iSub, const int elemShift=0 ) const;
+
+    //virtual void dump(std::ostream&) const;
+    //virtual ~DoubleField() {}
+  };
+  // ==============================================================================
+  /*!
+   * \if developper
+   * Iterator on set of Cell's of given dimension
+   * \endif
+   */
+  class CellsByDimIterator
+  {
+  public:
+    CellsByDimIterator( const IntermediateMED & medi, int dim=-1); // dim=-1 - for all dimensions
+    void init(const int  dim=-1);
+
+    //!< return next set of Cell's of required dimension
+    const std::set<Cell > * nextType();
+    //!< return dimension of Cell's returned by the last or further next()
+    int dim(const bool last=true) const;
+    //!< return type of Cell's returned by the last next()
+    TCellType type() const { return TCellType( myCurType ); }
+
+  private:
+    const IntermediateMED* myImed;
+    int myCurType, myTypeEnd;
+    int myDim;
+  };
+
+  // ==============================================================================
+  /*!
+   * \if developper
+   * Container of Node's. Prevents re-allocation at addition of Node's
+   * \endif
+   */
+  class NodeContainer
+  {
+    std::vector< std::vector< Node > > _nodes;
+  public:
+    Node* getNode( const TID nID )
+    {
+      const size_t chunkSize = 1000;
+      const size_t chunkID = (nID-1) / chunkSize;
+      const size_t pos     = (nID-1) % chunkSize;
+      if ( _nodes.size() < chunkID+1 )
+      {
+        _nodes.resize( chunkID+1 );
+        for ( size_t i = 0; i < _nodes.size(); ++i )
+          _nodes[i].resize( chunkSize );
+      }
+      return & _nodes[chunkID][pos];
+    }
+    bool empty() const { return _nodes.empty(); }
+    size_t size() const { return empty() ? 0 : _nodes.size() * _nodes[0].size(); }
+    int numberNodes();
+  };
+
+  // ==============================================================================
+  /*!
+   * \if developper
+   * Intermediate structure used to store data read from the Sauve format file.
+   * The structure provides functions that transform the stored data to the MED format
+   *
+   * The elements inserted in maillage are ordered in order to avoid duplicated elements.
+   * \endif
+   */
+  struct IntermediateMED
+  {
+    unsigned                   _spaceDim;
+    unsigned                   _nbNodes;
+    NodeContainer              _points;
+    std::vector<double>        _coords;
+    std::vector<Group>         _groups;
+    std::vector<DoubleField* > _nodeFields;
+    std::vector<DoubleField* > _cellFields;
+
+    // IMP 0020434: mapping GIBI names to MED names
+    std::list<nameGIBItoMED>  _listGIBItoMED_mail; // to read from table "MED_MAIL" of PILE_TABLES
+    std::list<nameGIBItoMED>  _listGIBItoMED_cham; // to read from table "MED_CHAM" of PILE_TABLES
+    std::list<nameGIBItoMED>  _listGIBItoMED_comp; // to read from table "MED_COMP" of PILE_TABLES
+    std::map<int,std::string> _mapStrings;         // to read from PILE_STRINGS
+
+    IntermediateMED(): _spaceDim(0), _nbNodes(0) {}
+    ~IntermediateMED();
+
+    Node* getNode( TID nID ) { return _points.getNode( nID ); }
+    int getNbCellsOfType( TCellType type ) const { return _cellsByType[type].size(); }
+    const Cell* insert(TCellType type, const Cell& ma) { return &( *_cellsByType[type].insert( ma ).first ); }
+    ParaMEDMEM::MEDFileData* convertInMEDFileDS();
+
+  private:
+
+    ParaMEDMEM::MEDFileUMesh* makeMEDFileMesh();
+    ParaMEDMEM::DataArrayDouble * getCoords();
+    void setConnectivity( ParaMEDMEM::MEDFileUMesh* mesh, ParaMEDMEM::DataArrayDouble* coords );
+    void setGroups( ParaMEDMEM::MEDFileUMesh* mesh );
+    ParaMEDMEM::MEDFileFields * makeMEDFileFields(ParaMEDMEM::MEDFileUMesh* mesh);
+    void setFields( SauvUtilities::DoubleField*    fld,
+                    ParaMEDMEM::MEDFileFields*     medFields,
+                    ParaMEDMEM::MEDFileUMesh*      mesh,
+                    const TID                      castemID,
+                    std::set< std::string >&       usedNames);
+    void setTS( SauvUtilities::DoubleField*  fld,
+                ParaMEDMEM::DataArrayDouble* values,
+                ParaMEDMEM::MEDFileFields*   medFields,
+                ParaMEDMEM::MEDFileUMesh*    mesh,
+                const int                    iSub=0);
+    void checkDataAvailability() const throw(INTERP_KERNEL::Exception);
+    void setGroupLongNames();
+    void setFieldLongNames(std::set< std::string >& usedNames);
+    void makeFieldNewName(std::set< std::string >&    usedNames,
+                          SauvUtilities::DoubleField* fld );
+    void decreaseHierarchicalDepthOfSubgroups();
+    void eraseUselessGroups();
+    void detectMixDimGroups();
+    void orientElements2D();
+    void orientElements3D();
+    void orientFaces3D();
+    void orientVolumes();
+    void numberElements();
+    bool isOnAll( const Group* grp, int & dimRel ) const;
+    const double* nodeCoords( const Node* n ) { return &_coords[ (n->_coordID-1) * _spaceDim ]; }
+
+    // IntermediateMED()
+    // { myNodesNumerated = myMaillesNumerated = myGroupsTreated = false; currentTypeMailles = 0; }
+    // ~IntermediateMED();
+
+    //bool myNodesNumerated, myMaillesNumerated;
+
+    // mailles groupped by geom type; use insert() for filling in and
+    // _CellsByDimIterator for exploring it
+    //std::set<_Cell> maillage;
+    std::set< Cell >  _cellsByType[ INTERP_KERNEL::NORM_HEXA20 + 1 ];
+    friend class CellsByDimIterator;
+  };
+
+// ==============================================================================
+  /*!
+   * \brief ASCII sauve file reader
+   */
+  class ASCIIReader : public FileReader
+  {
+  public:
+    ASCIIReader(const char* fileName);
+    virtual ~ASCIIReader();
+    virtual bool isASCII() const;
+    virtual bool open();
+    virtual bool getNextLine (char* & line, bool raiseOEF = true );
+    virtual void initNameReading(int nbValues, int width = 8);
+    virtual void initIntReading(int nbValues);
+    virtual void initDoubleReading(int nbValues);
+    virtual bool more() const;
+    virtual void next();
+    virtual int    getInt() const;
+    virtual float  getFloat() const;
+    virtual double getDouble() const;
+    virtual std::string getName() const;
+    int lineNb() const { return _lineNb; }
+
+  private:
+
+    bool getLine(char* & line);
+    void init( int nbToRead, int nbPosInLine, int width, int shift = 0 );
+
+    // getting a line from the file
+    int   _file;
+    char* _start; // working buffer beginning
+    char* _ptr;
+    char* _eptr;
+    int   _lineNb;
+
+    // line parsing
+    int _iPos, _nbPosInLine, _width, _shift;
+    char* _curPos;
+  };
+// ==============================================================================
+  /*!
+   * \brief XDR (binary) sauve file reader
+   */
+  class XDRReader : public FileReader
+  {
+  public:
+    XDRReader(const char* fileName);
+    virtual ~XDRReader();
+    virtual bool isASCII() const;
+    virtual bool open();
+    virtual bool getNextLine (char* & line, bool raiseOEF = true );
+    virtual void initNameReading(int nbValues, int width = 8);
+    virtual void initIntReading(int nbValues);
+    virtual void initDoubleReading(int nbValues);
+    virtual bool more() const;
+    virtual void next();
+    virtual int    getInt() const;
+    virtual float  getFloat() const;
+    virtual double getDouble() const;
+    virtual std::string getName() const;
+
+  private:
+
+    void init( int nbToRead, int width = 0 );
+
+    FILE* _xdrs_file;
+    void* _xdrs;
+    int* _xdr_ivals;
+    double* _xdr_dvals;
+    char* _xdr_cvals;
+    int _width;
+    int _xdr_kind;
+    enum
+      {
+        _xdr_kind_null,
+        _xdr_kind_char,
+        _xdr_kind_int,
+        _xdr_kind_double
+      };
+  };
+}
+
+#endif
diff --git a/src/MEDLoader/SauvReader.cxx b/src/MEDLoader/SauvReader.cxx
new file mode 100644 (file)
index 0000000..907f6ba
--- /dev/null
@@ -0,0 +1,1149 @@
+// Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File      : SauvReader.cxx
+// Created   : Tue Aug 16 13:57:42 2011
+// Author    : Edward AGAPOV (eap)
+//
+
+#include "SauvReader.hxx"
+
+#include "SauvMedConvertor.hxx"
+#include "MEDCouplingAutoRefCountObjectPtr.hxx"
+#include "NormalizedUnstructuredMesh.hxx"
+#include "MEDCouplingRefCountObject.hxx"
+
+#include <cstring>
+#include <sstream>
+#include <iostream>
+
+using namespace ParaMEDMEM;
+using namespace SauvUtilities;
+using namespace std;
+
+#define GIBI_EQUAL(var_str, stat_str) (strncmp (var_str, stat_str, strlen(stat_str)) == 0)
+
+//================================================================================
+/*!
+ * \brief Creates a reader of a given sauve file
+ */
+//================================================================================
+
+SauvReader* SauvReader::New(const char *fileName) throw(INTERP_KERNEL::Exception)
+{
+  if ( !fileName || strlen(fileName) < 1 ) THROW_IK_EXCEPTION("Invalid file name");
+
+  ParaMEDMEM::MEDCouplingAutoRefCountObjectPtr< SauvUtilities::FileReader> parser;
+
+  // try to open as XRD
+  parser = new XDRReader( fileName );
+  if ( parser->open() )
+    {
+      SauvReader* reader = new SauvReader;
+      reader->_fileReader = parser;
+      parser->incrRef();
+      return reader;
+    }
+
+  // try to open as ASCII
+  parser = new ASCIIReader( fileName );
+  if ( parser->open() )
+    {
+      SauvReader* reader = new SauvReader;
+      reader->_fileReader = parser;
+      parser->incrRef();
+      return reader;
+    }
+
+  THROW_IK_EXCEPTION("Unable to open file |"<< fileName << "|");
+}
+//================================================================================
+/*!
+ * \brief Destructor
+ */
+//================================================================================
+
+SauvReader::~SauvReader()
+{
+  _fileReader->decrRef();
+}
+
+//================================================================================
+/*!
+ * \brief Return current line of ASCII file to report an error
+ */
+//================================================================================
+
+std::string SauvReader::lineNb() const
+{
+  if ( isASCII() )
+    return string(" (line #") + SauvUtilities::toString
+      ( static_cast<SauvUtilities::ASCIIReader*>( _fileReader )->lineNb() ) + ")";
+
+  return "";
+}
+
+//================================================================================
+/*!
+ * \brief Reads contents of the sauve file and convert it to MEDFileData
+ */
+//================================================================================
+
+ParaMEDMEM::MEDFileData * SauvReader::loadInMEDFileDS() throw(INTERP_KERNEL::Exception)
+{
+  SauvUtilities::IntermediateMED iMed; // intermadiate DS
+  _iMed = &iMed;
+
+  char* line; // a current line
+  const char* enregistrement_type=" ENREGISTREMENT DE TYPE";
+
+  while ( getNextLine(line, /*raiseOEF=*/false)) // external loop looking for "ENREGISTREMENT DE TYPE"
+    {
+      if ( isASCII() && !GIBI_EQUAL( line, enregistrement_type ))
+        continue; // "ENREGISTREMENT DE TYPE" not found -> read the next line
+
+      // read the number of a record
+      int recordNumber;
+      if ( isASCII() )
+        recordNumber = atoi( line + strlen(enregistrement_type) + 1 );
+      else
+        recordNumber = getInt();
+
+      // read the record
+      switch ( recordNumber )
+        {
+        case 2:
+          readRecord2();
+          break;
+        case 4:
+          readRecord4();
+          break;
+        case 7:
+          readRecord7();
+          break;
+        case 5:
+          break;
+        default:
+          if ( !isASCII() )
+            THROW_IK_EXCEPTION("XDR : ENREGISTREMENT DE TYPE " << recordNumber << " not implemented!!!");
+        }
+    }
+
+  ParaMEDMEM::MEDFileData* medFileData = iMed.convertInMEDFileDS();
+
+  return medFileData;
+}
+
+//================================================================================
+/*!
+ * \brief Reads "ENREGISTREMENT DE TYPE 4"
+ */
+//================================================================================
+
+void SauvReader::readRecord4()
+{
+  if ( !isASCII() )
+    {
+      getInt(); // skip NIVEAU
+      getInt(); // skip ERREUR
+      _iMed->_spaceDim = getInt();
+      getFloat(); // skip DENSITE
+    }
+  else
+    {
+      char* line;
+      getNextLine(line);
+      const char* s = " NIVEAU  15 NIVEAU ERREUR   0 DIMENSION";
+      _iMed->_spaceDim = atoi( line + strlen( s ) + 1 );
+      if ( !GIBI_EQUAL( line, " NIVEAU" ))
+        THROW_IK_EXCEPTION( "Could not read space dimension" << lineNb() );
+      }
+  if ( _iMed->_spaceDim < 1 )
+    THROW_IK_EXCEPTION( "Invalid space dimension:" << _iMed->_spaceDim );
+}
+
+//================================================================================
+/*!
+ * \brief Reads "ENREGISTREMENT DE TYPE 7"
+ */
+//================================================================================
+
+void SauvReader::readRecord7()
+{
+  if ( !isASCII() )
+    {
+      getInt(); // skip NOMBRE INFO CASTEM2000
+      getInt(); // skip IFOUR
+      getInt(); // skip NIFOUR
+      getInt(); // skip IFOMOD
+      getInt(); // skip IECHO
+      getInt(); // skip IIMPI
+      getInt(); // skip IOSPI
+      getInt(); // skip ISOTYP
+      getInt(); // skip NSDPGE
+    }
+  else
+    {
+      // skip 3 lines:
+      // NOMBRE INFO CASTEM2000   8
+      // IFOUR   2 NIFOUR   0 IFOMOD   2 IECHO   1 IIMPI   0 IOSPI   0 ISOTYP   1
+      // NSDPGE     0
+      char* line;
+      getNextLine(line);
+      getNextLine(line);
+      getNextLine(line);
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Reads the pile number, nb of objects and nb named of objects
+ */
+//================================================================================
+
+int SauvReader::readPileNumber(int& nbNamedObjects, int& nbObjects)
+{
+  // FORMAT(' PILE NUMERO',I4,'NBRE ObjectS NOMMES',I8,'NBRE ObjectS',I8)
+  int pileNumber;
+  if ( !isASCII() )
+    {
+      initIntReading(3);
+      pileNumber     = getInt(); next();
+      nbNamedObjects = getInt(); next();
+      nbObjects      = getInt(); next();
+    }
+  else
+    {
+      char* line;
+      getNextLine(line);
+      const char *s1 = " PILE NUMERO", *s2 = "NBRE ObjectS NOMMES", *s3 = "NBRE ObjectS";
+      if ( ! GIBI_EQUAL( line, s1 ) )
+        THROW_IK_EXCEPTION("Could not read the pile number " << lineNb() );
+      line           = line + strlen(s1);
+      pileNumber     = atoi( line );
+      line           = line + 4 + strlen(s2);
+      nbNamedObjects = atoi( line );
+      line           = line + 8 + strlen(s3);
+      nbObjects      = atoi( line );
+    }
+  if ( nbNamedObjects<0 )
+    THROW_IK_EXCEPTION("Invalid nb of named objects: " << nbNamedObjects  << lineNb() );
+  if ( nbObjects<0)
+    THROW_IK_EXCEPTION("Invalid nb of objects: " << nbObjects  << lineNb() );
+  if ( nbObjects<nbNamedObjects)
+    THROW_IK_EXCEPTION("In PILE " << pileNumber <<
+                       " nb of objects is less than nb of named objects"  << lineNb() );
+  return pileNumber;
+}
+
+//================================================================================
+/*!
+ * \brief Reads "ENREGISTREMENT DE TYPE 2"
+ */
+//================================================================================
+
+void SauvReader::readRecord2()
+{
+  if ( _iMed->_spaceDim == 0 )
+    THROW_IK_EXCEPTION("Missing ENREGISTREMENT DE TYPE   4");
+
+  // read a pile number
+  int pileNumber, nbNamedObjects, nbObjects;
+  pileNumber = readPileNumber(nbNamedObjects, nbObjects);
+
+  if ( !_encounteredPiles.insert( pileNumber ).second && // piles may repeat
+       isASCII())
+    return;
+
+  // read object names and their indices
+  vector<string> objectNames(nbNamedObjects);
+  for ( initNameReading( nbNamedObjects ); more(); next() )
+    objectNames[ index() ] = getName();
+
+  vector<int> nameIndices(nbNamedObjects);
+  for ( initIntReading( nbNamedObjects ); more(); next() )
+    nameIndices[ index() ] = getInt();
+
+  switch ( pileNumber )
+    {
+    case PILE_SOUS_MAILLAGE:
+      read_PILE_SOUS_MAILLAGE(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_NODES_FIELD:
+      read_PILE_NODES_FIELD(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_TABLES:
+      read_PILE_TABLES(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_LREEL:
+      read_PILE_LREEL(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_LOGIQUES:
+      read_PILE_LOGIQUES(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_FLOATS:
+      read_PILE_FLOATS(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_INTEGERS:
+      read_PILE_INTEGERS(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_STRINGS:
+      read_PILE_STRINGS(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_LMOTS:
+      read_PILE_LMOTS(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_NOEUDS:
+      read_PILE_NOEUDS(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_COORDONNEES:
+      read_PILE_COORDONNEES(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_MODL:
+      read_PILE_MODL(nbObjects, objectNames, nameIndices);
+      break;
+    case PILE_FIELD:
+      read_PILE_FIELD(nbObjects, objectNames, nameIndices);
+      break;
+    default:
+      if ( !isASCII() )
+        THROW_IK_EXCEPTION("XDR : reading PILE " << pileNumber << " not implemented !!!");
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Reads "PILE NUMERO   1": gibi sub-meshes that are converted into med groups
+ */
+//================================================================================
+
+void SauvReader::read_PILE_SOUS_MAILLAGE(const int                 nbObjects,
+                                         std::vector<std::string>& objectNames,
+                                         std::vector<int>&         nameIndices)
+{
+  _iMed->_groups.reserve(nbObjects*2); // fields may add some groups
+
+  char* line;
+  map<int,int> strangeGroupType;
+  int i;
+
+  for (int object=0; object!=nbObjects; ++object) // loop on sub-groups
+    {
+      initIntReading( 5 );
+      int castemCellType = getIntNext();
+      int nbSubGroups    = getIntNext();
+      int nbReferences   = getIntNext();
+      int nbNodesPerElem = getIntNext();
+      int nbElements     = getIntNext();
+
+      _iMed->_groups.push_back(Group());
+      SauvUtilities::Group & group = _iMed->_groups.back();
+
+      // Issue 0021311. Allocate places for names of referring groups
+      // that will be possibly filled after reading long names from
+      // PILE_TABLES and PILE_STRINGS
+      group._refNames.resize( nbReferences );
+
+      // castemCellType=0 corresponds to a sub-mesh composed of other sub-meshes
+      if (castemCellType==0 && nbSubGroups>0)
+        {
+          group._groups.resize( nbSubGroups );
+          for ( initIntReading( nbSubGroups ); more(); next() )
+            group._groups[ index() ] = & _iMed->_groups[ getInt() - 1 ];
+          //std::sort( group._groups.begin(), group._groups.end() ); // for _groups comparison in getFieldSupport()
+        }
+      // skip references
+      if ( isASCII() )
+        for ( i = 0; i < nbReferences; i += 10 ) // FORMAT(10I8)
+          getNextLine(line);
+      else
+        for (initIntReading(nbReferences); more(); next());
+
+      // skip colors
+      if ( isASCII() )
+        for ( i = 0; i < nbElements; i += 10 )
+          getNextLine(line);
+      else
+        for (initIntReading(nbElements); more(); next());
+
+      // not a composit group
+      if (castemCellType>0 && nbSubGroups==0)
+        {
+          group._cellType = SauvUtilities::gibi2medGeom(castemCellType);
+
+          initIntReading( nbElements * nbNodesPerElem );
+          if ( group._cellType == INTERP_KERNEL::NORM_ERROR ) // look for group end
+            {
+              for ( ; more();  next());
+              strangeGroupType.insert( make_pair( object, castemCellType ));
+            }
+          else
+            {
+              // if ( group._cellType == MED_POINT1 ) group._cellType = NORM_ERROR; // issue 21199
+
+              // read connectivity of elements of a group
+              SauvUtilities::Cell ma( nbNodesPerElem );
+              SauvUtilities::Node* pNode;
+              group._cells.resize( nbElements );
+              for ( i = 0; i < nbElements; ++i )
+                {
+                  ma.init();
+                  for ( int n = 0; n < nbNodesPerElem; ++n )
+                    {
+                      int nodeID = getIntNext();
+                      pNode = _iMed->getNode( nodeID );
+                      ma._nodes[n] = pNode;
+                      _iMed->_nbNodes += ( !pNode->isUsed() );
+                      pNode->_number = nodeID;
+                    }
+                  ma._number = _iMed->getNbCellsOfType( group._cellType ) + 1;
+                  group._cells[i] = _iMed->insert( group._cellType, ma );
+                }
+            }
+        }
+    } // loop on groups
+
+  // set group names
+  for (i=0; i!=(int)objectNames.size(); ++i)
+    {
+      int grpID = nameIndices[i];
+      SauvUtilities::Group & grp = _iMed->_groups[ grpID-1 ];
+      if ( !grp._name.empty() ) // a group has several names
+        { // create a group with subgroup grp and named grp.name
+          _iMed->_groups.push_back(Group());
+          _iMed->_groups.back()._groups.push_back( &_iMed->_groups[ grpID-1 ]);
+          _iMed->_groups.back()._name = grp._name;
+        }
+      grp._name=objectNames[i];
+#ifdef _DEBUG
+      map<int,int>::iterator it = strangeGroupType.find( grpID - 1 );
+      if ( it != strangeGroupType.end() )
+        cout << "Skip " << grp._name << " of not supported CASTEM type: " << it->second << endl;
+#endif
+    }
+} // read_PILE_SOUS_MAILLAGE()
+
+//================================================================================
+/*!
+ * \brief Skip "PILE NUMERO  18" of XDR file
+ */
+//================================================================================
+
+void SauvReader::read_PILE_LREEL (const int nbObjects, std::vector<std::string>&, std::vector<int>&)
+{
+  if ( isXRD() )
+    {
+      for (int object=0; object!=nbObjects; ++object) // pour chaque Group
+        {
+          initIntReading(1);
+          int nb_vals = getIntNext();
+          initDoubleReading(nb_vals);
+          for(int i=0; i<nb_vals; i++) next();
+        }
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Skip "PILE NUMERO  24" of XDR file
+ */
+//================================================================================
+
+void SauvReader::read_PILE_LOGIQUES (const int, std::vector<std::string>&, std::vector<int>&)
+{
+  if ( isXRD() )
+    {
+      initIntReading(1);
+      int nb_vals = getIntNext();
+      initIntReading(nb_vals);
+      for(int i=0; i<nb_vals; i++) next();
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Skip "PILE NUMERO  25" of XDR file
+ */
+//================================================================================
+
+void SauvReader::read_PILE_FLOATS (const int, std::vector<std::string>&, std::vector<int>&)
+{
+  if ( isXRD() )
+    {
+      initIntReading(1);
+      int nb_vals = getIntNext();
+      initDoubleReading(nb_vals);
+      for(int i=0; i<nb_vals; i++) next();
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Skip "PILE NUMERO  26" of XDR file
+ */
+//================================================================================
+
+void SauvReader::read_PILE_INTEGERS (const int, std::vector<std::string>&, std::vector<int>&)
+{
+  if ( isXRD() )
+    {
+      initIntReading(1);
+      int nb_vals = getIntNext();
+      initIntReading(nb_vals);
+      for(int i=0; i<nb_vals; i++) next();
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Skip "PILE NUMERO  29" of XDR file
+ */
+//================================================================================
+
+void SauvReader::read_PILE_LMOTS (const int nbObjects, std::vector<std::string>&, std::vector<int>&)
+{
+  if ( isXRD() )
+    {
+      for (int object=0; object!=nbObjects; ++object) // pour chaque Group
+        {
+          initIntReading(2);
+          int len = getIntNext();
+          int nb_vals = getIntNext();
+          int nb_char = len*nb_vals;
+          int nb_char_tmp = 0;
+          int fixed_length = 71;
+          while (nb_char_tmp < nb_char)
+            {
+              int remain_len = nb_char - nb_char_tmp;
+              int width;
+              if ( remain_len > fixed_length )
+                {
+                  width = fixed_length;
+                }
+              else
+                {
+                  width = remain_len;
+                }
+              initNameReading(1, width);
+              next();
+              nb_char_tmp += width;
+            }
+        }
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Skip "PILE NUMERO  38" of XDR file
+ */
+//================================================================================
+
+void SauvReader::read_PILE_MODL (const int nbObjects, std::vector<std::string>&, std::vector<int>&)
+{
+  if ( isXRD() )
+    {
+      for (int object=0; object!=nbObjects; ++object) // pour chaque Group
+        {
+          // see wrmodl.eso
+          initIntReading(10);
+          int n1  = getIntNext();
+          int nm2 = getIntNext();
+          int nm3 = getIntNext();
+          int nm4 = getIntNext();
+          int nm5 = getIntNext();
+          int n45 = getIntNext();
+          /*int nm6 =*/ getIntNext();
+          /*int nm7 =*/ getIntNext();
+          next();
+          next();
+          int nm1 = n1 * n45;
+          int nm9 = n1 * 16;
+          for (initIntReading(nm1); more(); next());
+          for (initIntReading(nm9); more(); next());
+          for (initNameReading(nm5, 8); more(); next());
+          for (initNameReading(nm2, 8); more(); next());
+          for (initNameReading(nm3, 8); more(); next());
+          for (initIntReading(nm4); more(); next());
+        }
+    }
+} // Fin case pile 38
+
+//================================================================================
+/*!
+ * \brief Read "PILE NUMERO  32": links to node coordinates
+ */
+//================================================================================
+
+void SauvReader::read_PILE_NOEUDS (const int nbObjects, std::vector<std::string>&, std::vector<int>&)
+{
+  initIntReading(1);
+  int nb_indices = getIntNext();
+
+  if (nb_indices != nbObjects)
+    THROW_IK_EXCEPTION("Error of reading PILE NUMERO  " << PILE_NOEUDS << lineNb() );
+
+  for ( initIntReading( nbObjects ); more(); next() )
+    {
+      int coordID = getInt();
+      _iMed->getNode( index()+1 )->_coordID = coordID;
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Read "PILE NUMERO  33": node coordinates
+ */
+//================================================================================
+
+void SauvReader::read_PILE_COORDONNEES (const int nbObjects, std::vector<std::string>&, std::vector<int>&)
+{
+  initIntReading(1);
+  int nbReals = getIntNext();
+
+  if ( nbReals < _iMed->_nbNodes*(_iMed->_spaceDim+1) )
+    THROW_IK_EXCEPTION("Erroor of reading PILE NUMERO  " << PILE_COORDONNEES << lineNb() );
+
+  // there are coordinates + density for each node
+  _iMed->_coords.resize( nbReals - nbReals/(_iMed->_spaceDim+1));
+  double* coordPtr = &_iMed->_coords[0];
+
+  initDoubleReading( nbReals );
+  while ( more() )
+    {
+      for (unsigned j = 0; j < _iMed->_spaceDim; ++j, next())
+        *coordPtr++ = getDouble();
+      // skip density
+      getDouble();
+      next();
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Finds or create a Group equal to a given field support 
+ */
+//================================================================================
+
+SauvUtilities::Group* SauvReader::getFieldSupport(const vector<SauvUtilities::Group*>& supports)
+{
+  SauvUtilities::Group* group = NULL;
+  set<SauvUtilities::Group*> sup_set( supports.begin(), supports.end() );
+  if (sup_set.size() == 1 ) // one or equal supports
+    {
+      group = supports[0];
+    }
+  else
+    {
+      // try to find an existing composite group with the same sub-groups
+      for ( size_t i = 0; i < _iMed->_groups.size() && !group; ++i )
+        {
+          Group & grp = _iMed->_groups[i];
+          if (sup_set.size() == grp._groups.size())
+            {
+              bool sameOrder = true;
+              for ( size_t j = 0; j < supports.size() && sameOrder; ++j )
+                sameOrder = ( supports[j] == grp._groups[ j % grp._groups.size() ]);
+              if ( sameOrder )
+                group = & _iMed->_groups[i];
+            }
+        }
+      if ( !group ) // no such a group, add a new one
+        {
+          vector<SauvUtilities::Group*> newGroups( supports.begin(),
+                                                   supports.begin() + sup_set.size() );
+          // check if supports includes newGroups in the same order
+          bool sameOrder = true;
+          for ( size_t j = newGroups.size(); j < supports.size() && sameOrder; ++j )
+            sameOrder = ( supports[j] == newGroups[ j % newGroups.size() ]);
+          if ( sameOrder )
+            {
+              _iMed->_groups.push_back( SauvUtilities::Group() );
+              group = & _iMed->_groups.back();
+              group->_groups.swap( newGroups );
+            }
+        }
+    }
+  if ( group )
+    group->_isProfile = true;
+  return group;
+}
+
+//================================================================================
+/*!
+ * \brief set field names
+ */
+//================================================================================
+
+void SauvReader::setFieldNames(const vector<SauvUtilities::DoubleField* >& fields,
+                               const vector<string>&                       objets_nommes,
+                               const vector<int>&                          indices_objets_nommes)
+{
+  unsigned i;
+  for ( i = 0; i < indices_objets_nommes.size(); ++i )
+    {
+      int fieldIndex = indices_objets_nommes[ i ];
+      if ( fields[ fieldIndex - 1 ] )
+        fields[ fieldIndex - 1 ]->_name = objets_nommes[ i ];
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Read "PILE NUMERO   2": NODE FIELDS
+ */
+//================================================================================
+
+void SauvReader::read_PILE_NODES_FIELD (const int                 nbObjects,
+                                        std::vector<std::string>& objectNames,
+                                        std::vector<int>&         nameIndices)
+{
+  _iMed->_nodeFields.resize( nbObjects, (SauvUtilities::DoubleField*) 0 );
+  for (int object=0; object!=nbObjects; ++object) // loop on fields
+    {
+      // EXAMPLE ( with no values )
+
+      // (1)       4       7       2       1
+      // (2)     -88       0       3     -89       0       1     -90       0       2     -91
+      // (2)       0       1
+      // (3) FX   FY   FZ   FZ   FX   FY   FLX
+      // (4)       0       0       0       0       0       0       0
+      // (5)           cree  par  muc pri
+      // (6)
+      // (7)       2
+
+      // (1): nb subcomponents, nb components(total), IFOUR, nb attributes
+      int nb_sub, total_nb_comp, nb_attr;
+      int i_sub, i_comp;
+      initIntReading( 4 );
+      nb_sub        = getIntNext();
+      total_nb_comp = getIntNext();
+      next(); // ignore IFOUR
+      nb_attr       = getIntNext();
+      if ( nb_sub < 0 || total_nb_comp < 0 || nb_attr < 0 )
+        THROW_IK_EXCEPTION("Error of field reading " << lineNb());
+
+      // (2) loop on subcomponents of a field, for each read
+      // (a) support, (b) number of values and (c) number of components
+      vector<Group*> supports( nb_sub );
+      vector<int> nb_values  ( nb_sub );
+      vector<int> nb_comps   ( nb_sub );
+      int total_nb_values = 0;
+      initIntReading( nb_sub * 3 );
+      for ( i_sub = 0; i_sub < nb_sub; ++i_sub )
+        {
+          int supId = -getIntNext(); // (a) reference to support
+          if ( supId < 1 || supId > (int)_iMed->_groups.size() )
+            THROW_IK_EXCEPTION("Wrong mesh reference: "<< supId << lineNb() );
+          supports[ i_sub ] = &_iMed->_groups[ supId-1 ]; // (a) reference to support
+
+          nb_values[ i_sub ] = getIntNext();    // (b) nb points
+          total_nb_values += nb_values[ i_sub ];
+          if ( nb_values[ i_sub ] < 0 )
+            THROW_IK_EXCEPTION(" Wrong nb of points: " << nb_values[ i_sub ]  << lineNb() );
+          nb_comps[ i_sub ] = getInt(); next();     // (c) nb of components in i_sub
+        }
+
+      // create a field if there are values
+      SauvUtilities::DoubleField* fdouble = 0;
+      if ( total_nb_values > 0 )
+        fdouble = new DoubleField( nb_sub, total_nb_comp );
+      _iMed->_nodeFields[ object ] = fdouble;
+
+      // (3) component names
+      initNameReading( total_nb_comp, 4 );
+      for ( i_sub = 0; i_sub < nb_sub; ++i_sub )
+        {
+          // store support id and nb components of a sub
+          if ( fdouble )
+            fdouble->_sub[ i_sub ].setData( nb_comps[ i_sub ], supports[ i_sub ] );
+          for ( i_comp = 0; i_comp < nb_comps[ i_sub ]; ++i_comp, next() )
+            {
+              // store component name
+              string compName = getName();
+              if ( fdouble )
+                fdouble->_sub[ i_sub ].compName( i_comp ) = compName;
+            }
+        }
+      // (4) nb harmonics ( ignored )
+      for ( initIntReading( total_nb_comp ); more(); next() );
+      // (5) TYPE ( ignored )
+      for (initNameReading(1, /*length=*/71); more(); next());
+      // (6) TITRE ( ignored )
+      for (initNameReading(1, /*length=*/71); more(); next());
+      // (7) attributes ( ignored )
+      for ( initIntReading( nb_attr ); more(); next() );
+
+      for ( i_sub = 0; i_sub < nb_sub; ++i_sub )
+        {
+          // loop on components: read values
+          initDoubleReading( nb_values[ i_sub ] * nb_comps[ i_sub ] );
+          for ( i_comp = 0; i_comp < nb_comps[ i_sub ]; ++i_comp )
+            {
+              if ( fdouble )
+                {
+                  vector<double>& vals = fdouble->addComponent( nb_values[ i_sub ] );
+                  for ( int i = 0; more() && i < nb_values[ i_sub ]; next(), ++i )
+                    vals[ i ] = getDouble();
+                }
+              else
+                {
+                  for ( int i = 0; i < nb_values[ i_sub ]; next(), ++i );
+                }
+            }
+        } // loop on subcomponents of a field
+
+      // set a supporting group including all subs supports but only
+      // if all subs have the same components
+      if ( fdouble && fdouble->hasSameComponentsBySupport() )
+        fdouble->_group = getFieldSupport( supports );
+      else
+        for ( i_sub = 0; i_sub < nb_sub; ++i_sub )
+          fdouble->_sub[ i_sub ]._support->_isProfile;
+
+    } // end loop on field objects
+
+  // set field names
+  setFieldNames( _iMed->_nodeFields, objectNames, nameIndices );
+
+}  // read_PILE_NODES_FIELD()
+
+//================================================================================
+/*!
+ * \brief Read "PILE NUMERO  39": FIELDS
+ */
+//================================================================================
+
+void SauvReader::read_PILE_FIELD (const int                 nbObjects,
+                                  std::vector<std::string>& objectNames,
+                                  std::vector<int>&         nameIndices)
+{
+  // REAL EXAMPLE
+
+  // (1)        1       2       6      16
+  // (2)                                                         CARACTERISTIQUES
+  // (3)      -15  317773       4       0       0       0      -2       0       3
+  // (4)             317581
+  // (5)  0
+  // (6)   317767  317761  317755  317815
+  // (7)  YOUN     NU       H        SIGY
+  // (8)  REAL*8            REAL*8            REAL*8            REAL*8
+  // (9)        1       1       0       0
+  // (10)  2.00000000000000E+05
+  // (11)       1       1       0       0
+  // (12)  3.30000000000000E-01
+  // (13)       1       1       0       0
+  // (14)  1.00000000000000E+04
+  // (15)       6     706       0       0
+  // (16)  1.00000000000000E+02  1.00000000000000E+02  1.00000000000000E+02
+  // (17)  1.00000000000000E+02  1.00000000000000E+02  1.00000000000000E+02
+  // (18)  ...
+
+  _iMed->_cellFields.resize( nbObjects, (SauvUtilities::DoubleField*) 0 );
+  for (int object=0; object!=nbObjects; ++object) // pour chaque field
+    {
+      initIntReading( 4 );
+      int i_sub, nb_sub = getIntNext(); // (1) <nb_sub> 2 6 <title length>
+      next(); // skip "2"
+      next(); // skip "6"
+      int title_length = getIntNext(); // <title length>
+      if ( nb_sub < 1 )
+        THROW_IK_EXCEPTION("Error of field reading: wrong nb of subcomponents " << nb_sub << lineNb() );
+
+      string description;
+      if ( title_length )
+        {
+          if ( isXRD() )
+            {
+              initNameReading(1, title_length);
+              description = getName();
+              next();
+            }
+          else
+            {
+              char* line;
+              getNextLine( line ); // (2) title
+              const int len = 72; // line length
+              description = string(line + len - title_length, title_length); // title is right justified
+            }
+        }
+      // look for a line starting with '-' : <reference to support>
+      if ( isXRD() )
+        {
+          initIntReading( nb_sub * 9 );
+        }
+      else
+        {
+          do {
+            initIntReading( nb_sub * 9 );
+          } while ( getInt() >= 0 );
+        }
+      int total_nb_comp = 0;
+      vector<Group*> supports( nb_sub );
+      vector<int>     nb_comp( nb_sub );
+      for ( i_sub = 0; i_sub < nb_sub; ++i_sub )
+        {                                    // (3)
+          int supportId     = -getIntNext(); // <reference to support>
+          next();                            // ignore <address>
+          nb_comp [ i_sub ] =  getIntNext(); // <nb of components in the sub>
+          for ( int i = 0; i < 6; ++i ) next();  // ignore 6 ints, in example "0 0 0 -2 0 3"
+
+          if ( supportId < 1 || supportId > (int)_iMed->_groups.size() )
+            THROW_IK_EXCEPTION("Error of field reading: wrong mesh reference "<< supportId << lineNb() );
+          if ( nb_comp[ i_sub ] < 0 )
+            THROW_IK_EXCEPTION("Error of field reading: wrong nb of components " <<nb_comp[ i_sub ] << lineNb() );
+
+          supports[ i_sub ] = &_iMed->_groups[ supportId-1 ];
+          total_nb_comp += nb_comp[ i_sub ];
+        }
+      // (4) dummy strings
+      for ( initNameReading( nb_sub, 17 ); more(); next() );
+      // (5) dummy strings
+      for ( initNameReading( nb_sub ); more(); next() );
+
+      // loop on subcomponents of a field, each of which refers to
+      // a certain support and has its own number of components;
+      // read component values
+      SauvUtilities::DoubleField* fdouble = 0;
+      for ( i_sub = 0; i_sub < nb_sub; ++ i_sub )
+        {
+          vector<string> comp_names( nb_comp[ i_sub ]), comp_type( nb_comp[ i_sub ]);
+          // (6) nb_comp addresses of MELVAL structure
+          for ( initIntReading( nb_comp[ i_sub ] ); more(); next() );
+          // (7) component names
+          for ( initNameReading( nb_comp[ i_sub ] ); more(); next() )
+            comp_names[ index() ] = getName();
+          // (8) component type
+          for ( initNameReading( nb_comp[ i_sub ], 17 ); more(); next() ) // 17 is name width
+            {
+              comp_type[ index() ] = getName();
+              // component types must be the same
+              if ( index() > 0 && comp_type[ index() ] != comp_type[ index() - 1] )
+                THROW_IK_EXCEPTION( "Error of field reading: diff component types <"
+                                    << comp_type[ index() ] << "> != <" << comp_type[ index() - 1 ]
+                                    << ">" << lineNb() );
+            }
+          // now type is known, create a field, one for all subs
+          bool isReal = (nb_comp[i_sub] > 0) ? (comp_type[0] == "REAL*8") : true;
+          if ( !fdouble && total_nb_comp )
+            {
+              if ( !isReal )
+                cout << "Warning: read NOT REAL field, type <" << comp_type[0] << ">" << lineNb() << endl;
+              _iMed->_cellFields[ object ] = fdouble = new SauvUtilities::DoubleField( nb_sub, total_nb_comp );
+              fdouble->_description = description;
+            }
+          // store support id and nb components of a sub
+          if ( fdouble )
+            fdouble->_sub[ i_sub ].setData( nb_comp[ i_sub ], supports[ i_sub ]);
+          // loop on components: read values
+          for ( int i_comp = 0; i_comp < nb_comp[ i_sub ]; ++i_comp )
+            {
+              // (9) nb of values
+              initIntReading( 4 );
+              int nb_val_by_elem = getIntNext();
+              int nb_values      = getIntNext();
+              next();
+              next();
+              fdouble->_sub[ i_sub ]._nb_gauss[ i_comp ] = nb_val_by_elem;
+
+              // (10) values
+              nb_values *= nb_val_by_elem;
+              if ( fdouble )
+                {
+                  vector<double> & vals = fdouble->addComponent( nb_values );
+                  for ( isReal ? initDoubleReading( nb_values ) : initIntReading( nb_values ); more(); next())
+                    vals[ index() ] = getDouble();
+                  // store component name
+                  fdouble->_sub[ i_sub ].compName( i_comp ) = comp_names[ i_comp ];
+                }
+              else
+                {
+                  for ( isReal ? initDoubleReading( nb_values ) : initIntReading( nb_values ); more(); next() ) ;
+                }
+            }
+        } // loop on subcomponents of a field
+
+      // set id of a group including all sub supports but only
+      // if all subs have the same nb of components
+      if ( fdouble && fdouble->hasSameComponentsBySupport() )
+        fdouble->_group = getFieldSupport( supports );
+      else
+        for ( i_sub = 0; i_sub < nb_sub; ++i_sub )
+          fdouble->_sub[ i_sub ]._support->_isProfile = true;
+
+    } // end loop on field objects
+
+  // set field names
+  setFieldNames( _iMed->_cellFields, objectNames, nameIndices );
+
+} // read_PILE_FIELD()
+
+//================================================================================
+/*!
+ * \brief Read "PILE NUMERO  10": TABLES
+ */
+//================================================================================
+
+void SauvReader::read_PILE_TABLES (const int                 nbObjects,
+                                   std::vector<std::string>& objectNames,
+                                   std::vector<int>&         nameIndices)
+{
+  // IMP 0020434: mapping GIBI names to MED names
+
+  string table_med_mail = "MED_MAIL";
+  string table_med_cham = "MED_CHAM";
+  string table_med_comp = "MED_COMP";
+  int table_med_mail_id = -1;
+  int table_med_cham_id = -1;
+  int table_med_comp_id = -1;
+  for (size_t iname = 0; iname < objectNames.size(); iname++)
+    if      (objectNames[iname] == table_med_mail) table_med_mail_id = nameIndices[iname];
+    else if (objectNames[iname] == table_med_cham) table_med_cham_id = nameIndices[iname];
+    else if (objectNames[iname] == table_med_comp) table_med_comp_id = nameIndices[iname];
+
+  if ( isASCII() )
+    if (table_med_mail_id < 0 && table_med_cham_id < 0 && table_med_comp_id < 0)
+      return;
+
+  for (int itable = 1; itable <= nbObjects; itable++)
+    {
+      // read tables "MED_MAIL", "MED_CHAM" and "MED_COMP", that keeps correspondence
+      // between GIBI names (8 symbols if any) and MED names (possibly longer)
+      initIntReading(1);
+      int nb_table_vals = getIntNext();
+      if (nb_table_vals < 0)
+        THROW_IK_EXCEPTION("Error of reading PILE NUMERO  10" << lineNb() );
+
+      int name_i_med_pile;
+      initIntReading(nb_table_vals);
+      for (int i = 0; i < nb_table_vals/4; i++)
+        {
+          if (itable == table_med_mail_id ||
+              itable == table_med_cham_id ||
+              itable == table_med_comp_id)
+            {
+              nameGIBItoMED name_i;
+              name_i_med_pile  = getIntNext();
+              name_i.med_id    = getIntNext();
+              name_i.gibi_pile = getIntNext();
+              name_i.gibi_id   = getIntNext();
+
+              if (name_i_med_pile != PILE_STRINGS)
+                {
+                  // error: med(long) names are always kept in PILE_STRINGS
+                }
+              if (itable == table_med_mail_id)
+                {
+                  if (name_i.gibi_pile != PILE_SOUS_MAILLAGE) {
+                    // error: must be PILE_SOUS_MAILLAGE
+                  }
+                  _iMed->_listGIBItoMED_mail.push_back(name_i);
+                }
+              else if (itable == table_med_cham_id)
+                {
+                  if (name_i.gibi_pile != PILE_FIELD &&
+                      name_i.gibi_pile != PILE_NODES_FIELD)
+                    {
+                      // error: must be PILE_FIELD or PILE_NODES_FIELD
+                    }
+                  _iMed->_listGIBItoMED_cham.push_back(name_i);
+                }
+              else if (itable == table_med_comp_id)
+                {
+                  if (name_i.gibi_pile != PILE_STRINGS)
+                    {
+                      // error: gibi(short) names of components are kept in PILE_STRINGS
+                    }
+                  _iMed->_listGIBItoMED_comp.push_back(name_i);
+                }
+            }
+          else
+            {
+              // pass table
+              for ( int i = 0; i < 4; ++i ) next();
+            }
+        }
+    } // for (int itable = 0; itable < nbObjects; itable++)
+}
+
+//================================================================================
+/*!
+ * \brief Read "PILE NUMERO  27"
+ */
+//================================================================================
+
+void SauvReader::read_PILE_STRINGS (const int                 nbObjects,
+                                    std::vector<std::string>& objectNames,
+                                    std::vector<int>&         nameIndices)
+{
+  // IMP 0020434: mapping GIBI names to MED names
+  initIntReading(2);
+  int stringLen    = getIntNext();
+  int nbSubStrings = getIntNext();
+  if (nbSubStrings != nbObjects)
+    THROW_IK_EXCEPTION("Error of reading PILE NUMERO  27" << lineNb() );
+
+  string aWholeString;
+  if ( isXRD() )
+    {
+      const int fixedLength = 71;
+      while ((int)aWholeString.length() < stringLen)
+        {
+          int remainLen = stringLen - aWholeString.length();
+          int len;
+          if ( remainLen > fixedLength )
+            {
+              len = fixedLength;
+            }
+          else
+            {
+              len = remainLen;
+            }
+          initNameReading(1, len);
+          aWholeString += getName();
+          next();
+        }
+    }
+  else
+    {
+      char* line;
+      const int fixedLength = 71;
+      while ((int)aWholeString.length() < stringLen)
+        {
+          getNextLine( line );
+          int remainLen = stringLen - aWholeString.length();
+          if ( remainLen > fixedLength )
+            {
+              aWholeString += line + 1;
+            }
+          else
+            {
+              aWholeString += line + ( 72 - remainLen );
+            }
+        }
+    }
+  int prevOffset = 0;
+  int currOffset = 0;
+  initIntReading(nbSubStrings);
+  for (int istr = 1; istr <= nbSubStrings; istr++, next())
+    {
+      currOffset = getInt();
+      // fill mapStrings
+      _iMed->_mapStrings[istr] = aWholeString.substr(prevOffset, currOffset - prevOffset);
+      prevOffset = currOffset;
+    }
+}
diff --git a/src/MEDLoader/SauvReader.hxx b/src/MEDLoader/SauvReader.hxx
new file mode 100644 (file)
index 0000000..359c135
--- /dev/null
@@ -0,0 +1,102 @@
+// Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File      : SauvReader.hxx
+// Created   : Tue Aug 16 13:04:25 2011
+// Author    : Edward AGAPOV (eap)
+//
+#ifndef __SauvReader_HXX__
+#define __SauvReader_HXX__
+
+#include "MEDLoaderDefines.hxx"
+#include "InterpKernelException.hxx"
+#include "SauvUtilities.hxx"
+#include "MEDCouplingRefCountObject.hxx"
+
+#include <vector>
+#include <string>
+#include <set>
+
+namespace SauvUtilities
+{
+  class FileReader;
+  class IntermediateMED;
+  class Group;
+  class DoubleField;
+}
+namespace ParaMEDMEM
+{
+  class MEDFileData;
+
+class MEDLOADER_EXPORT SauvReader : public ParaMEDMEM::RefCountObject
+{
+ public:
+  static SauvReader* New(const char *fileName) throw(INTERP_KERNEL::Exception);
+  ParaMEDMEM::MEDFileData * loadInMEDFileDS() throw(INTERP_KERNEL::Exception);
+  ~SauvReader();
+
+ private:
+
+  void readRecord2();
+  void readRecord4();
+  void readRecord7();
+
+  int readPileNumber(int& nbNamedObjects, int& nbObjects);
+  void read_PILE_SOUS_MAILLAGE(const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_NODES_FIELD  (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_TABLES       (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_LREEL        (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_LOGIQUES     (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_FLOATS       (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_INTEGERS     (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_STRINGS      (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_LMOTS        (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_NOEUDS       (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_COORDONNEES  (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_MODL         (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+  void read_PILE_FIELD        (const int nbObjects, std::vector<std::string>& objectNames, std::vector<int>& nameIndices);
+
+  SauvUtilities::Group* getFieldSupport(const std::vector<SauvUtilities::Group*>& fieldSupports);
+  void setFieldNames(const std::vector<SauvUtilities::DoubleField*>& fields,
+                     const std::vector<std::string>& objectNames,
+                     const std::vector<int>& nameIndices);
+
+  bool isASCII() const                                   { return _fileReader->isASCII(); }
+  bool isXRD() const                                     { return !isASCII(); }
+  bool getNextLine (char* & line, bool raiseOEF = true ) { return _fileReader->getNextLine( line, raiseOEF ); }
+  void initNameReading(int nbValues, int width = 8)      { _fileReader->initNameReading( nbValues, width ); }
+  void initIntReading(int nbValues)                      { _fileReader->initIntReading( nbValues ); }
+  void initDoubleReading(int nbValues)                   { _fileReader->initDoubleReading( nbValues ); }
+  bool more() const                                      { return _fileReader->more(); }
+  void next()                                            { _fileReader->next(); }
+  int  index() const                                     { return _fileReader->index(); }
+  int    getInt() const                                  { return _fileReader->getInt(); }
+  int    getIntNext()                                    { int i = getInt(); next(); return i; }
+  float  getFloat() const                                { return _fileReader->getFloat(); }
+  double getDouble() const                               { return _fileReader->getDouble(); }
+  std::string getName() const                            { return _fileReader->getName(); }
+  std::string lineNb() const;
+  
+
+  std::set<int> _encounteredPiles;
+
+  SauvUtilities::FileReader*      _fileReader;
+  SauvUtilities::IntermediateMED* _iMed;
+};
+}
+#endif
diff --git a/src/MEDLoader/SauvUtilities.hxx b/src/MEDLoader/SauvUtilities.hxx
new file mode 100644 (file)
index 0000000..a9f476c
--- /dev/null
@@ -0,0 +1,117 @@
+// Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File      : SauvUtilities.hxx
+// Created   : Mon Aug 22 18:27:34 2011
+// Author    : Edward AGAPOV (eap)
+
+#ifndef __SauvUtilities_HXX__
+#define __SauvUtilities_HXX__
+
+#include "MEDCouplingRefCountObject.hxx"
+#include "NormalizedUnstructuredMesh.hxx"
+
+#include <string>
+#include <sstream>
+
+#define THROW_IK_EXCEPTION(text)                        \
+  {                                                     \
+    std::ostringstream oss; oss << text;                \
+    throw INTERP_KERNEL::Exception(oss.str().c_str());  \
+  }
+
+namespace SauvUtilities
+{
+  INTERP_KERNEL::NormalizedCellType gibi2medGeom( size_t gibiType );
+  int med2gibiGeom( INTERP_KERNEL::NormalizedCellType medGeomType );
+  const int * getGibi2MedQuadraticInterlace( INTERP_KERNEL::NormalizedCellType type );
+  unsigned getDimension( INTERP_KERNEL::NormalizedCellType type );
+
+  enum Readable_Piles
+    {
+      PILE_SOUS_MAILLAGE=1 ,
+      PILE_NODES_FIELD  =2 ,
+      PILE_TABLES       =10,
+      PILE_LREEL        =18,
+      PILE_LOGIQUES     =24,
+      PILE_FLOATS       =25,
+      PILE_INTEGERS     =26,
+      PILE_STRINGS      =27,
+      PILE_LMOTS        =29,
+      PILE_NOEUDS       =32,
+      PILE_COORDONNEES  =33,
+      PILE_MODL         =38,
+      PILE_FIELD        =39,
+      PILE_LAST_READABLE=39
+    };
+
+  //================================================================================
+  /*!
+   * \brief Converts anything to string
+   */
+  //================================================================================
+
+  template<class T> std::string toString(const T& anything)
+  {
+    std::ostringstream s; s << anything; return s.str();
+  }
+
+  // ==============================================================================
+  // IMP 0020434: mapping GIBI names to MED names
+  struct nameGIBItoMED
+  {
+    // GIBI value
+    int gibi_pile;    // PILE_SOUS_MAILLAGE or PILE_FIELD/PILE_NODES_FIELD, or PILE_STRINGS(for components)
+    int gibi_id;
+    std::string gibi_name; // used only for components
+    // MED value
+    // med_pile = 27; // PILE_STRINGS
+    int         med_id;    // used only on reading
+    std::string med_name;  // used only on writing
+  };
+
+  // ==============================================================================
+  /*!
+   * \brief Base class for ASCII and XDR file readers
+   */
+  class FileReader : public ParaMEDMEM::RefCountObject
+  {
+  public:
+    FileReader(const char* fileName);
+    virtual ~FileReader() {}
+    virtual bool isASCII() const = 0;
+
+    virtual bool open() = 0;
+    virtual bool getNextLine (char* & line, bool raiseOEF = true ) = 0;
+    virtual void initNameReading(int nbValues, int width = 8) = 0;
+    virtual void initIntReading(int nbValues) = 0;
+    virtual void initDoubleReading(int nbValues) = 0;
+    virtual bool more() const = 0;
+    virtual void next() = 0;
+    virtual int  index() const { return _iRead; }
+    virtual int    getInt() const = 0;
+    virtual float  getFloat() const = 0;
+    virtual double getDouble() const = 0;
+    virtual std::string getName() const = 0;
+
+  protected:
+    std::string _fileName, _curLocale;
+    int _iRead, _nbToRead;
+  };
+}
+#endif
diff --git a/src/MEDLoader/SauvWriter.cxx b/src/MEDLoader/SauvWriter.cxx
new file mode 100644 (file)
index 0000000..c842270
--- /dev/null
@@ -0,0 +1,1323 @@
+// Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File      : SauvWriter.cxx
+// Created   : Wed Aug 24 12:55:55 2011
+// Author    : Edward AGAPOV (eap)
+
+#include "SauvWriter.hxx"
+
+#include "InterpKernelException.hxx"
+#include "MEDFileMesh.hxx"
+#include "MEDFileField.hxx"
+#include "MEDFileData.hxx"
+#include "CellModel.hxx"
+
+#include <fstream>
+#include <sstream>
+#include <iostream>
+#include <cstdlib>
+#include <iomanip>
+
+using namespace ParaMEDMEM;
+using namespace SauvUtilities;
+using namespace std;
+
+#define INFOS_MED(txt) cout << txt << endl;
+
+namespace
+{
+  const char* zeroI8 = "       0"; // FORMAT(I8)
+
+  // ============================================================
+  // the class writes endl to the file as soon as <limit> fields
+  // have been written after the last endl
+  // ============================================================
+
+  class TFieldCounter
+  {
+    fstream& _file;
+    int _count, _limit;
+  public:
+    TFieldCounter(fstream& f, int limit=0): _file(f), _limit(limit) { init(); }
+    void init(int limit=0) // init, is done by stop() as well
+    { if (limit) _limit = limit; _count = 0; }
+    void operator++(int) // next
+    { if ( ++_count == _limit ) { _file << endl; init(); }}
+    void stop() // init() and write endl if there was no endl after the last written field
+    { if ( _count ) _file << endl; init(); }
+    ~TFieldCounter() { stop(); }
+  };
+
+  //================================================================================
+  /*!
+   * \brief Return a name of a field support on all elements
+   */
+  //================================================================================
+
+  string noProfileName( INTERP_KERNEL::NormalizedCellType type )
+  {
+    return "INTERP_KERNEL::NormalizedCellType_" + SauvUtilities::toString( type );
+  }
+
+  //================================================================================
+  /*!
+   * \brief Remove white spaces from the head and tail
+   */
+  //================================================================================
+
+  string cleanName( const string& theName )
+  {
+    string name = theName;
+    if ( !name.empty() )
+      {
+        // cut off leading white spaces
+        string::size_type firstChar = name.find_first_not_of(" \t");
+        if (firstChar < name.length())
+          {
+            name = name.substr(firstChar);
+          }
+        else
+          {
+            name = ""; // only whitespaces there - remove them
+          }
+        // cut off trailing white spaces
+        string::size_type lastChar = name.find_last_not_of(" \t");
+        if (lastChar < name.length())
+          name = name.substr(0, lastChar + 1);
+      }
+    return name;
+  }
+
+  //================================================================================
+  /*!
+   * \brief Converts MED long names into SAUVE short ones, returnes a healed long name
+   */
+  //================================================================================
+
+  string addName (map<string,int>& nameMap,
+                  map<string,int>& namePrefixesMap,
+                  const string&    theName,
+                  const int        index)
+  {
+    // Converts names like:
+    // MED:                       GIBI:     
+    //   TEMPERATURE_FLUIDE   ->    TEMPE001
+    //   TEMPERATURE_SOLIDE   ->    TEMPE002
+    //   PRESSION             ->    PRESSION
+    //   NU                   ->    NU      
+    //   VOLUM001             ->    VOLUM001
+    //   VOLUMOFOBJECT        ->    VOLUM003
+    //   VOLUM002             ->    VOLUM002
+    string healedName = cleanName(theName);
+    int ind = index;
+
+    if (!healedName.empty())
+      {
+        string name = healedName;
+        int len = name.length();
+        for (int i = 0; i < len; ++i)
+          name[i] = toupper(name[i]);
+
+        bool doResave = false; // only for tracing
+
+        // I. Save a short name as it is
+        if (len <= 8)
+          {
+            INFOS_MED("Save <" << theName << "> as <" << name << ">");
+
+            map<string,int>::iterator it = nameMap.find(name);
+            if (it != nameMap.end())
+              {
+                // There is already such name in the map.
+
+                // a. Replace in the map the old pair by the current one
+                int old_ind = nameMap[name];
+                nameMap[name] = ind;
+                // b. Rebuild the old pair (which was in the map,
+                //    it seems to be built automatically by step II)
+                ind = old_ind;
+                // continue with step II
+                doResave = true; // only for tracing
+              }
+            else
+              {
+                // Save in the map
+                nameMap.insert(make_pair(name, ind));
+
+                // Update loc_index for this name (if last free characters represents a number)
+                // to avoid conflicts with long names, same in first 5 characters
+                if (len == 8)
+                  {
+                    int new_loc_index = atoi(name.c_str() + 5);
+                    if (new_loc_index > 0)
+                      {
+                        // prefix
+                        string str = name.substr(0,5);
+                        if (namePrefixesMap.find(str) != namePrefixesMap.end())
+                          {
+                            int old_loc_index = namePrefixesMap[str];
+                            if (new_loc_index < old_loc_index) new_loc_index = old_loc_index;
+                          }
+                        namePrefixesMap[str] = new_loc_index;
+                      }
+                  }
+                return healedName;
+              }
+          } // if (len <= 8)
+
+        // II. Cut long name and add a numeric suffix
+
+        // first 5 or less characters of the name
+        if (len > 5) name = name.substr(0,5);
+
+        // numeric suffix
+        map<string,int>::iterator name2ind = namePrefixesMap.insert( make_pair( name, 0 )).first;
+        string numSuffix = SauvUtilities::toString( ++(name2ind->second) );
+
+        if ( numSuffix.size() + name.size() > 8 )
+          THROW_IK_EXCEPTION("Can't write not unique name: " << healedName);
+
+        if ( numSuffix.size() < 3 )
+          numSuffix.insert( 0, 3 - numSuffix.size(), '0' );
+
+        name += numSuffix;
+        nameMap.insert(make_pair(name, ind));
+
+        if (doResave)
+          {
+            INFOS_MED("Resave previous <" << healedName << "> as <" << name << ">");
+          }
+        else
+          {
+            INFOS_MED("Save <" << theName << "> as <" << name << ">");
+          }
+      }
+    return healedName;
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Creates SauvWriter
+ */
+//================================================================================
+
+SauvWriter* SauvWriter::New()
+{
+  return new SauvWriter;
+}
+
+//================================================================================
+/*!
+ * \brief Fills own DS by MEDFileData
+ */
+//================================================================================
+
+void SauvWriter::setMEDFileDS(const MEDFileData* medData,
+                              unsigned           meshIndex)
+{
+  if ( !medData) THROW_IK_EXCEPTION("NULL MEDFileData");
+
+  MEDFileMeshes * meshes = medData->getMeshes();
+  MEDFileFields * fields = medData->getFields();
+  if ( !meshes) THROW_IK_EXCEPTION("No meshes in MEDFileData");
+
+  _fileMesh = meshes->getMeshAtPos( meshIndex );
+  _fileMesh->incrRef();
+
+  if ( fields )
+    for ( int i = 0; i < fields->getNumberOfFields(); ++i )
+      {
+        MEDFileFieldMultiTS * f = fields->getFieldAtPos(i);
+        if ( f->getMeshName() == _fileMesh->getName() )
+          {
+            vector< vector<TypeOfField> > fTypes = f->getTypesOfFieldAvailable();
+            if ( fTypes[0].size() == 1 && fTypes[0][0] == ON_NODES )
+              _nodeFields.push_back( f );
+            else
+              _cellFields.push_back( f );
+          }
+      }
+}
+
+//================================================================================
+/*!
+ * \brief Adds a submesh
+ */
+//================================================================================
+
+SauvWriter::SubMesh* SauvWriter::addSubMesh(const std::string& name, int dimRelExt)
+{
+  if ( _subs.capacity() < _subs.size() + 1 )
+    THROW_IK_EXCEPTION("SauvWriter: INTERNAL error, wrong evaluation of nb of sub-meshes");
+  _subs.resize( _subs.size() + 1 );
+  SubMesh& sm = _subs.back();
+  sm._name = name;
+  sm._dimRelExt = dimRelExt;
+  return &sm;
+}
+//================================================================================
+/*!
+ * \brief Returns nb of cell types
+ */
+//================================================================================
+
+int SauvWriter::SubMesh::nbTypes() const
+{
+  int nb = 0;
+  for (int i = 0; i < cellIDsByTypeSize(); ++i )
+    nb += int( !_cellIDsByType[i].empty() );
+  return nb;
+}
+
+//================================================================================
+/*!
+ * \brief Fill _subs
+ */
+//================================================================================
+
+void SauvWriter::fillSubMeshes( int& nbSauvObjects, map<string,int>& nameNbMap )
+{
+  // evaluate nb of _subs in order to avoid re-allocation of _subs
+  int nbSubs = 1; // for the very mesh
+  nbSubs += _fileMesh->getFamilyInfo().size() + 4;  // + 4 zero families (for each dimRelExt)
+  nbSubs += _fileMesh->getGroupInfo().size();
+  nbSubs += evaluateNbProfileSubMeshes();
+  _subs.clear();
+  _subs.reserve( nbSubs );
+
+  fillFamilySubMeshes();
+  fillGroupSubMeshes();
+  fillProfileSubMeshes();
+
+  // fill names of SubMesh'es and count nb of sauv sub-meshes they will be stored into
+  nbSauvObjects = 0;
+  map<string,int> namePrefixMap;
+  for ( size_t i = 0; i < _subs.size(); ++i )
+    {
+      SubMesh& sm = _subs[i];
+
+      sm._nbSauvObjects = 0;
+      if ( sm._subs.empty() )
+        {
+          sm._nbSauvObjects = sm.nbTypes();
+        }
+      else
+        {
+          sm._nbSauvObjects = 1;
+        }
+
+      sm._id = nbSauvObjects+1;
+      nbSauvObjects += sm._nbSauvObjects;
+
+      if ( sm._nbSauvObjects )
+        sm._name = addName( nameNbMap, namePrefixMap, sm._name, sm._id );
+
+      if ( sm._nbSauvObjects && !sm._name.empty() )
+        {
+          nameGIBItoMED aMEDName;
+          aMEDName.gibi_pile = PILE_SOUS_MAILLAGE;
+          aMEDName.gibi_id   = sm._id;
+          aMEDName.med_name  = sm._name;
+          _longNames[ LN_MAIL ].push_back(aMEDName);
+        }
+    }
+}
+
+//================================================================================
+/*!
+ * \brief fill sub-meshes of families
+ */
+//================================================================================
+
+void SauvWriter::fillFamilySubMeshes()
+{
+  SubMesh* nilSm = (SubMesh*) 0;
+  std::vector<int> dims = _fileMesh->getNonEmptyLevelsExt();
+  for ( size_t iDim = 0; iDim < dims.size(); ++iDim )
+    {
+      int dimRelExt = dims[ iDim ];
+      MEDCouplingAutoRefCountObjectPtr< MEDCouplingMesh > mesh = _fileMesh->getGenMeshAtLevel(dimRelExt);
+      const DataArrayInt * famIds = _fileMesh->getFamilyFieldAtLevel(dimRelExt);
+
+      int curFamID = 0;
+      SubMesh* curSubMesh = addSubMesh( "", dimRelExt ); // submesh of zero family
+      _famIDs2Sub[0] = curSubMesh;
+      int sub0Index = _subs.size()-1;
+
+      const int * famID = famIds->begin(), * famIDEnd = famIds->end();
+      for ( int cellID = 0; famID < famIDEnd; ++famID, cellID++ )
+        {
+          if ( *famID != curFamID )
+            {
+              curFamID = *famID;
+              map< int, SubMesh* >::iterator f2s = _famIDs2Sub.insert( make_pair( curFamID, nilSm )).first;
+              if ( !f2s->second )
+                f2s->second = addSubMesh( "", dimRelExt ); // no names for families
+              curSubMesh = f2s->second;
+            }
+          INTERP_KERNEL::NormalizedCellType cellType =
+            dimRelExt == 1 ? INTERP_KERNEL::NORM_POINT1 : mesh->getTypeOfCell( cellID );
+          curSubMesh->_cellIDsByType[ cellType ].push_back( cellID );
+        }
+
+      if ( dimRelExt == 1 )
+        {
+          // clear submesh of nodal zero family
+          _famIDs2Sub[0]->_cellIDsByType[ INTERP_KERNEL::NORM_POINT1 ].clear();
+        }
+      else if ( dimRelExt == 0 )
+        {
+          // make a submesh including all cells
+          if ( sub0Index == _subs.size()-1 )
+            {
+              _famIDs2Sub[0]->_name = _fileMesh->getName(); // there is the zero family only
+            }
+          else
+            {
+              curSubMesh = addSubMesh( _fileMesh->getName(), dimRelExt );
+              if ( _famIDs2Sub[0]->nbTypes() == 0 )
+                sub0Index++; // skip an empty zero family
+              for ( size_t i = sub0Index; i < _subs.size()-1; ++i )
+                curSubMesh->_subs.push_back( & _subs[i] );
+            }
+        }
+    }
+}
+
+//================================================================================
+/*!
+ * \brief fill sub-mehses of groups
+ */
+//================================================================================
+
+void SauvWriter::fillGroupSubMeshes()
+{
+  const map<string, vector<string> >& grpFams = _fileMesh->getGroupInfo();
+  map<string, vector<string> >::const_iterator g2ff = grpFams.begin();
+  for ( ; g2ff != grpFams.end(); ++g2ff )
+    {
+      const string&        groupName = g2ff->first;
+      const vector<string>& famNames = g2ff->second;
+      if ( famNames.empty() ) continue;
+      std::vector<SubMesh*> famSubMeshes( famNames.size() );
+      for ( size_t i = 0; i < famNames.size(); ++i )
+        {
+          int famID = _fileMesh->getFamilyId( famNames[i].c_str() );
+          map< int, SubMesh* >::iterator i2f = _famIDs2Sub.find( famID );
+          if ( i2f == _famIDs2Sub.end() )
+            THROW_IK_EXCEPTION("SauvWriter::fillGroupSubMeshes(): unknown family ID: " << famID);
+          famSubMeshes[ i ] = i2f->second;
+        }
+      SubMesh* grpSubMesh = addSubMesh( groupName, famSubMeshes[0]->_dimRelExt );
+      grpSubMesh->_subs.swap( famSubMeshes );
+    }
+}
+
+//================================================================================
+/*!
+ * \brief fill sub-mehses of profiles
+ */
+//================================================================================
+
+void SauvWriter::fillProfileSubMeshes()
+{
+  _profile2Sub.clear();
+  SubMesh* nilSm = (SubMesh*) 0;
+  for ( int isOnNodes = 0; isOnNodes < 2; ++isOnNodes )
+    {
+      vector< MEDCouplingAutoRefCountObjectPtr< MEDFileFieldMultiTS > >
+        fields = isOnNodes ? _nodeFields : _cellFields;
+      for ( size_t i = 0; i < fields.size(); ++i )
+        {
+          vector< pair<int,int> > iters = fields[i]->getIterations();
+
+          vector<INTERP_KERNEL::NormalizedCellType> types;
+          vector< vector<TypeOfField> > typesF;
+          vector< vector<string> > pfls, locs;
+          fields[i]->getFieldSplitedByType( iters[0].first, iters[0].second,
+                                            _fileMesh->getName(), types, typesF, pfls, locs);
+          int dimRelExt;
+          for ( size_t iType = 0; iType < types.size(); ++iType )
+            {
+              if ( types[iType] == INTERP_KERNEL::NORM_ERROR )
+                dimRelExt = 1; // on nodes
+              else
+                dimRelExt = getDimension( types[iType] ) - _fileMesh->getMeshDimension();
+              for ( size_t iPfl = 0; iPfl < pfls[iType].size(); ++iPfl )
+                {
+                  bool isOnAll = pfls[iType][iPfl].empty();
+                  if ( isOnAll ) pfls[iType][iPfl] = noProfileName( types[iType] );
+                  map< string, SubMesh* >::iterator pfl2sm =
+                    _profile2Sub.insert( make_pair( pfls[iType][iPfl], nilSm )).first;
+                  if ( !pfl2sm->second )
+                    {
+                      SubMesh* sm = pfl2sm->second = addSubMesh( "", dimRelExt ); // no names for profiles
+                      const DataArrayInt * pfl = isOnAll ? 0 : fields[i]->getProfile( pfls[iType][iPfl] );
+                      makeProfileIDs( sm, types[iType], pfl );
+                    }
+                }
+            }
+        }
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Return max possible nb of sub-meshes to decsribe field supports
+ */
+//================================================================================
+
+int SauvWriter::evaluateNbProfileSubMeshes() const
+{
+  int nb = 0;
+  for ( size_t i = 0; i < _nodeFields.size(); ++i )
+    nb += 1 + _nodeFields[i]->getPflsReallyUsed2().size();
+
+  for ( size_t i = 0; i < _cellFields.size(); ++i )
+    {
+      nb += _cellFields[i]->getPflsReallyUsed2().size();
+
+      vector< pair<int,int> > iters = _cellFields[i]->getIterations();
+
+      vector<INTERP_KERNEL::NormalizedCellType> types;
+      vector< vector<TypeOfField> > typesF;
+      vector< vector<string> > pfls, locs;
+      _cellFields[i]->getFieldSplitedByType( iters[0].first, iters[0].second,
+                                             _fileMesh->getName(), types, typesF, pfls, locs);
+      nb += 2 * types.size(); // x 2 - a type can be on nodes and on cells at the same time
+    }
+
+  return nb;
+}
+
+//================================================================================
+/*!
+ * \brief Transorm a profile into ids of mesh elements
+ */
+//================================================================================
+
+void SauvWriter::makeProfileIDs( SubMesh*                          sm,
+                                 INTERP_KERNEL::NormalizedCellType type,
+                                 const DataArrayInt*               profile )
+{
+  MEDCouplingAutoRefCountObjectPtr< MEDCouplingMesh >
+    mesh = _fileMesh->getGenMeshAtLevel(sm->_dimRelExt);
+  const MEDCouplingUMesh* uMesh = dynamic_cast< const MEDCouplingUMesh* > ((const MEDCouplingMesh*) mesh );
+
+  if ( sm->_dimRelExt == 1 ) type = INTERP_KERNEL::NORM_POINT1;
+  vector< int >& ids = sm->_cellIDsByType[ type ];
+
+  if ( sm->_dimRelExt == 1 || !uMesh )
+    {
+      // profile on nodes or mesh is CARTESIAN
+      if ( profile )
+        {
+          ids.assign( profile->begin(), profile->end() );
+        }
+      else // on all
+        {
+          ids.resize( sm->_dimRelExt == 1 ? mesh->getNumberOfNodes() : mesh->getNumberOfCells() );
+          for ( size_t i = 0; i < ids.size(); ++i )
+            ids[i]=i;
+        }
+    }
+  else
+    {
+      // profile on cells
+      vector<int> code(3);
+      code[0] = type;
+      if ( profile ) // on all cells
+        {
+          code[1] = profile->getNumberOfTuples();
+          code[2] = 0;
+        }
+      else
+        {
+          code[1] = mesh->getNumberOfCellsWithType( type );
+          code[2] = -1;
+        }
+      vector<const DataArrayInt *> idsPerType( 1, profile );
+      MEDCouplingAutoRefCountObjectPtr<DataArrayInt>
+        resIDs = uMesh->checkTypeConsistencyAndContig( code, idsPerType );
+      ids.assign( resIDs->begin(), resIDs->end() );
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Write its data into the SAUVE file
+ */
+//================================================================================
+
+void SauvWriter::write(const char* fileName)
+{
+  std::fstream fileStream;
+  fileStream.open( fileName, ios::out);
+  if
+#ifdef WNT
+    ( !fileStream || !fileStream.is_open() )
+#else
+    ( !fileStream || !fileStream.rdbuf()->is_open() )
+#endif
+      THROW_IK_EXCEPTION("Can't open the file |"<<fileName<<"|");
+  _sauvFile = &fileStream;
+
+  _subs.clear();
+  _famIDs2Sub.clear();
+  _profile2Sub.clear();
+  _longNames[ LN_MAIL ].clear();
+  _longNames[ LN_CHAM ].clear();
+  _longNames[ LN_COMP ].clear();
+
+  map<string,int> fldNamePrefixMap;
+
+  writeFileHead();
+  writeSubMeshes();
+  writeNodes();
+  writeNodalFields(fldNamePrefixMap);
+  writeElemFields(fldNamePrefixMap);
+  writeLongNames();
+  writeLastRecord();
+
+  _sauvFile->close();
+}
+//================================================================================
+/*!
+ * \brief Writes "ENREGISTREMENT DE TYPE" 4 and 7
+ */
+//================================================================================
+
+void SauvWriter::writeFileHead()
+{
+  MEDCouplingAutoRefCountObjectPtr< MEDCouplingMesh > mesh = _fileMesh->getGenMeshAtLevel(0);
+
+  *_sauvFile
+    << " ENREGISTREMENT DE TYPE   4" << endl
+    << " NIVEAU  16 NIVEAU ERREUR   0 DIMENSION   " << mesh->getSpaceDimension() <<endl
+    << " DENSITE 0.00000E+00" << endl
+    << " ENREGISTREMENT DE TYPE   7" << endl
+    << " NOMBRE INFO CASTEM2000   8" <<endl
+    << " IFOUR  -1 NIFOUR   0 IFOMOD  -1 IECHO   1 IIMPI   0 IOSPI   0 ISOTYP   1" << endl
+    << " NSDPGE     0" << endl;
+}
+
+//================================================================================
+/*!
+ * \brief Writes names of objects
+ */
+//================================================================================
+
+void SauvWriter::writeNames( const map<string,int>& nameNbMap )
+{
+  if ( !nameNbMap.empty() )
+  {
+    // write names of objects
+    // * 8001       FORMAT(8(1X,A8))
+    TFieldCounter fcount( *_sauvFile, 8 );
+    *_sauvFile << left;
+    map<string,int>::const_iterator nameNbIt = nameNbMap.begin();
+    for ( ; nameNbIt != nameNbMap.end(); nameNbIt++, fcount++ )
+      *_sauvFile << " " << setw(8) << nameNbIt->first;
+    fcount.stop();
+    *_sauvFile << right;
+
+    // write IDs of named objects in the pile
+    // *  8000 FORMAT(10I8)
+    nameNbIt = nameNbMap.begin();
+    for ( fcount.init(10); nameNbIt != nameNbMap.end(); nameNbIt++, fcount++ )
+      *_sauvFile << setw(8) << nameNbIt->second;
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Writes "PILE NUMERO   1"
+ */
+//================================================================================
+
+void SauvWriter::writeSubMeshes()
+{
+  int nbSauvObjects;
+  map<string,int> nameNbMap;
+  fillSubMeshes( nbSauvObjects, nameNbMap );
+
+  // * 800   FORMAT (' ENREGISTREMENT DE TYPE', I4)
+  *_sauvFile << " ENREGISTREMENT DE TYPE   2" << endl;
+  // * 801     FORMAT(' PILE NUMERO',I4,'NBRE OBJETS NOMMES',I8,'NBRE OBJETS',I8)
+  *_sauvFile << " PILE NUMERO   1NBRE OBJETS NOMMES" << setw(8) << nameNbMap.size() <<
+    "NBRE OBJETS" << setw(8) << nbSauvObjects <<endl;
+
+  writeNames( nameNbMap );
+
+  TFieldCounter fcount( *_sauvFile, 10 ); // 10 intergers per line
+
+  for ( size_t iSub = 0; iSub < _subs.size(); ++iSub )
+    {
+      SubMesh& sm = _subs[iSub];
+      if ( sm._nbSauvObjects < 1 ) continue;
+
+      // The first record of each sub-mesh writes
+      // - type of cells; zero means a compound object whose the 2nd record enumerates its components
+      // - number of components of a compound object
+      // - number of references; each reference means a "pointer" to this sub-mesh
+      // - number of nodes per cell
+      // - number of cells
+
+      if ( !sm._subs.empty() )
+        {
+          writeCompoundSubMesh(iSub);
+        }
+      else
+        {
+          // write each sub-type as a SAUV sub-mesh
+          MEDCouplingAutoRefCountObjectPtr< MEDCouplingMesh >
+            mesh = _fileMesh->getGenMeshAtLevel( sm._dimRelExt );
+          MEDCouplingAutoRefCountObjectPtr< MEDCouplingUMesh>
+            umesh = mesh->buildUnstructured();
+
+          for ( int iType=0; iType < sm.cellIDsByTypeSize(); ++iType )
+            {
+              const vector<int>& cellIDs = sm._cellIDsByType[iType];
+              if ( cellIDs.empty() ) continue;
+
+              INTERP_KERNEL::NormalizedCellType
+                cellType = INTERP_KERNEL::NormalizedCellType( iType );
+              const INTERP_KERNEL::CellModel &
+                cell = INTERP_KERNEL::CellModel::GetCellModel( cellType );
+              int castemType       = SauvUtilities::med2gibiGeom( cellType );
+              unsigned nbElemNodes = cell.getNumberOfNodes();
+              unsigned nbElems     = cellIDs.size();
+
+              *_sauvFile << setw(8) << castemType
+                        << zeroI8
+                        << zeroI8
+                        << setw(8) << nbElemNodes
+                        << setw(8) << nbElems << endl;
+
+              // write color of each element
+              // * 8000 FORMAT(10I8)
+              for ( size_t i = 0; i < nbElems; ++i, fcount++ ) *_sauvFile << zeroI8;
+              fcount.stop();
+
+              // write connectivity
+              // gibi IDs are in FORTRAN mode while MEDCoupling IDs are in C mode
+              if ( sm._dimRelExt == 1 ) // nodes
+                {
+                  for ( size_t i = 0; i < nbElems; ++i, fcount++ )
+                    *_sauvFile << setw(8) << ( cellIDs[i] + 1 );
+                }
+              else
+                {
+                  // indices to transform MED connectivity to GIBI one
+                  const int * toMedConn = getGibi2MedQuadraticInterlace( cellType );
+
+                  vector< int > cellConn( nbElemNodes ), transformedConn( nbElemNodes );
+                  for ( size_t i = 0; i < nbElems; ++i )
+                    {
+                      cellConn.clear();
+                      umesh->getNodeIdsOfCell( cellIDs[i], cellConn );
+                      if ( toMedConn )
+                        {
+                          for ( unsigned j = 0; j < nbElemNodes; ++j )
+                            transformedConn[ j ] = cellConn[ toMedConn[ j ]];
+                          cellConn.swap( transformedConn );
+                        }
+                      for ( unsigned j = 0; j < nbElemNodes; ++j, fcount++ )
+                        *_sauvFile << setw(8) << ( cellConn[j] + 1 );
+                    }
+                }
+              fcount.stop();
+
+            } // loop on cell types
+        } // not a compound object
+    } // loop on sub-meshes
+}
+
+//================================================================================
+/*!
+ * \brief Writes a sum-mesh composed of other sum-meshes
+ * This submesh corresponds to a med mesh or group composed of families
+ */
+//================================================================================
+
+void SauvWriter::writeCompoundSubMesh(int iSub)
+{
+  SubMesh& sm = _subs[iSub];
+  if ( sm._nbSauvObjects < 1 || sm._subs.empty()) return;
+
+  vector< int > subIDs;
+  for ( size_t i = 0; i < sm._subs.size(); ++i ) // loop on sub-meshes of families
+    for ( int j = 0; j < sm._subs[i]->_nbSauvObjects; ++j )
+      subIDs.push_back( sm._subs[i]->_id + j );
+      
+  *_sauvFile << zeroI8
+             << setw(8) << subIDs.size()
+             << zeroI8
+             << zeroI8
+             << zeroI8 << endl;
+
+  TFieldCounter fcount( *_sauvFile, 10 ); // 10 intergers per line
+  for ( size_t i = 0; i < subIDs.size(); ++i, fcount++ )
+    *_sauvFile << setw(8) << subIDs[i];
+}
+
+//================================================================================
+/*!
+ * \brief Write piles relating to nodes
+ */
+//================================================================================
+
+void SauvWriter::writeNodes()
+{
+  MEDCouplingAutoRefCountObjectPtr< MEDCouplingMesh > mesh = _fileMesh->getGenMeshAtLevel( 1 );
+  MEDCouplingAutoRefCountObjectPtr< MEDCouplingUMesh > umesh = mesh->buildUnstructured();
+
+  // write the index connecting nodes with their coodrinates
+
+  const int nbNodes = umesh->getNumberOfNodes();
+  *_sauvFile << " ENREGISTREMENT DE TYPE   2" << endl
+             << " PILE NUMERO  32NBRE OBJETS NOMMES       0NBRE OBJETS" << setw(8) << nbNodes << endl;
+  *_sauvFile << setw(8) << nbNodes << endl;
+  //
+  TFieldCounter fcount( *_sauvFile, 10 );// * 8000 FORMAT(10I8)
+  for ( int i = 0; i < nbNodes; ++i, fcount++ )
+    *_sauvFile << setw(8) << i + 1; 
+  fcount.stop();
+
+  // write coordinates and density of nodes
+
+  *_sauvFile << " ENREGISTREMENT DE TYPE   2" << endl;
+  *_sauvFile << " PILE NUMERO  33NBRE OBJETS NOMMES       0NBRE OBJETS       1" << endl;
+  // 
+  const int dim = umesh->getSpaceDimension();
+  const int nbValues = nbNodes * ( dim + 1 );
+  *_sauvFile << setw(8) << nbValues << endl;
+
+  // * 8003   FORMAT(1P,3E22.14)
+  const char* density = "  0.00000000000000E+00";
+  fcount.init(3);
+  _sauvFile->precision(14);
+  _sauvFile->setf( ios_base::scientific, ios_base::floatfield );
+  _sauvFile->setf( ios_base::uppercase );
+  MEDCouplingAutoRefCountObjectPtr< DataArrayDouble> coordArray = umesh->getCoordinatesAndOwner();
+  const double precision = 1.e-99; // PAL12077
+  for ( int i = 0; i < nbNodes; ++i)
+  {
+    for ( int j = 0; j < dim; ++j, fcount++ )
+      {
+        double coo = coordArray->getIJ( i, j );
+        bool  zero = ( -precision < coo && coo < precision );
+        *_sauvFile << setw(22) << ( zero ? 0.0 : coo );
+      }
+    *_sauvFile << density;
+    fcount++;
+  }
+}
+
+//================================================================================
+/*!
+ * \brief Store correspondence between GIBI (short) and MED (long) names
+ *
+ * IMP 0020434: mapping GIBI names to MED names
+ * Store correspondence between GIBI and MED names as one PILE_STRINGS and one
+ * PILE_TABLES (in three tables: MED_MAIL, MED_CHAM and MED_COMP)
+ */
+//================================================================================
+
+void SauvWriter::writeLongNames()
+{
+  int nbTables =
+    3 - _longNames[ LN_MAIL ].empty() - _longNames[ LN_CHAM ].empty() - _longNames[ LN_COMP ].empty();
+  if (nbTables == 0) return;
+
+  // ---------------------
+  // Write the TABLE pile
+  // ---------------------
+
+  *_sauvFile << " ENREGISTREMENT DE TYPE   2" << endl
+        << " PILE NUMERO  10NBRE OBJETS NOMMES" << setw(8) << nbTables
+        << "NBRE OBJETS" << setw(8) << nbTables << endl;
+  // table names
+  if (!_longNames[ LN_MAIL ].empty()) *_sauvFile << " MED_MAIL";
+  if (!_longNames[ LN_CHAM ].empty()) *_sauvFile << " MED_CHAM";
+  if (!_longNames[ LN_COMP ].empty()) *_sauvFile << " MED_COMP";
+  *_sauvFile << endl;
+  // table indices
+  for ( int i = 0; i < nbTables; ++i ) *_sauvFile << setw(8) << i+1;
+  *_sauvFile << endl;
+
+  string theWholeString; // concatenated long names
+  vector<int> theOffsets;
+  int iStr = 1;
+  TFieldCounter fcount (*_sauvFile, 10);
+
+  for ( int iTbl = 0; iTbl < LN_NB; ++iTbl )
+    {
+      vector<nameGIBItoMED>& longNames = _longNames[ iTbl ];
+      if ( longNames.empty() ) continue;
+      const bool isComp = ( iTbl == LN_COMP);
+
+      // to assure unique MED names
+      set<string> medUniqueNames;
+
+      *_sauvFile << setw(8) << longNames.size()*4 << endl; // Nb of table values
+
+      vector<nameGIBItoMED>::iterator itGIBItoMED = longNames.begin();
+      for (; itGIBItoMED != longNames.end(); itGIBItoMED++, iStr++)
+        {
+          // PILE of i-th key (med name)
+          *_sauvFile << setw(8) << PILE_STRINGS;
+          fcount++;
+          // ID of i-th key (med name)
+          *_sauvFile << setw(8) << iStr;
+          fcount++;
+          // PILE of i-th value (gibi name)
+          *_sauvFile << setw(8) << itGIBItoMED->gibi_pile;
+          fcount++;
+          // ID of i-th value (gibi name)
+          *_sauvFile << setw(8) << ( isComp ? ++iStr : itGIBItoMED->gibi_id );
+          fcount++;
+
+          // add a MED name to the string (while making it be unique for sub-meshes and fields)
+          string aMedName = itGIBItoMED->med_name;
+          if ( !isComp )
+            for (int ind = 1; !medUniqueNames.insert(aMedName).second; ++ind )
+              aMedName = itGIBItoMED->med_name + "_" + SauvUtilities::toString( ind );
+          theWholeString += aMedName;
+
+          // add an offset
+          theOffsets.push_back( theWholeString.size() );
+          if ( isComp )
+            {
+              theWholeString += itGIBItoMED->gibi_name;
+              theOffsets.push_back( theWholeString.size() );
+            }
+        }
+      fcount.stop();
+    }
+
+  // ----------------------
+  // Write the STRING pile
+  // ----------------------
+
+  const int nbNames = theOffsets.size();
+  *_sauvFile << " ENREGISTREMENT DE TYPE   2" << endl
+        << " PILE NUMERO  27NBRE OBJETS NOMMES" << zeroI8 << "NBRE OBJETS" << setw(8) << nbNames << endl
+        << setw(8) << theWholeString.length() << setw(8) << nbNames << endl;
+
+  // write the whole string
+  const int fixedLength = 71;
+  for ( string::size_type aPos = 0; aPos < theWholeString.length(); aPos += fixedLength)
+    *_sauvFile << setw(72) << theWholeString.substr(aPos, fixedLength) << endl;
+
+  // write the offsets
+  for ( size_t i = 0; i < theOffsets.size(); ++i, fcount++ )
+    *_sauvFile << setw(8) << theOffsets[i];
+}
+
+//================================================================================
+/*!
+ * \brief Write beginning of field record
+ */
+//================================================================================
+
+void SauvWriter::writeFieldNames( const bool                 isNodal,
+                                  std::map<std::string,int>& fldNamePrefixMap)
+{
+  vector< MEDCouplingAutoRefCountObjectPtr< MEDFileFieldMultiTS > >&
+    flds = isNodal ? _nodeFields : _cellFields;
+  map<string,int> nameNbMap;
+
+  for ( size_t iF = 0; iF < flds.size(); ++iF )
+    {
+      string name = addName( nameNbMap, fldNamePrefixMap, flds[iF]->getName(), iF+1 );
+      nameGIBItoMED aMEDName;
+      aMEDName.gibi_pile = isNodal ? PILE_NODES_FIELD : PILE_FIELD;
+      aMEDName.gibi_id   = iF+1;
+      aMEDName.med_name  = name;
+      _longNames[ LN_CHAM ].push_back(aMEDName);
+    }
+
+  *_sauvFile << " ENREGISTREMENT DE TYPE   2" << endl
+             << ( isNodal ? " PILE NUMERO   2" : " PILE NUMERO  39")
+             << "NBRE OBJETS NOMMES" << setw(8) << nameNbMap.size()
+             << "NBRE OBJETS"        << setw(8) << flds.size() << endl;
+  writeNames( nameNbMap );
+}
+
+//================================================================================
+/*!
+ * \brief Make short names of field components
+ *
+ * IMP 0020434: mapping GIBI names to MED names
+ */
+//================================================================================
+
+void SauvWriter::makeCompNames(const string&         fieldName,
+                               const vector<string>& compInfo,
+                               map<string, string>&  mapMedToGibi)
+{
+  for ( size_t i = 0; i < compInfo.size(); ++i )
+    mapMedToGibi[compInfo[i]] = cleanName( compInfo[i] );
+
+  int compIndex = 1;
+  map<string, string>::iterator namesIt = mapMedToGibi.begin();
+  for (; namesIt != mapMedToGibi.end(); namesIt++)
+    {
+      string & compGibiName = (*namesIt).second;
+      if (compGibiName.size() > 4) {
+        // use new name in form "CXXX", where "XXX" is a number
+        do
+          {
+            compGibiName = SauvUtilities::toString( compIndex++ );
+            if ( compGibiName.size() < 3 )
+              compGibiName.insert( 0, 3 - compGibiName.size(), '0' );
+            compGibiName = "C" + compGibiName;
+          }
+        while (mapMedToGibi.count(compGibiName) > 0); // real component name could be CXXX
+      }
+
+      string compMedName = fieldName + "." + namesIt->first;
+      nameGIBItoMED aMEDName;
+      aMEDName.med_name  = compMedName;
+      aMEDName.gibi_pile = PILE_STRINGS;
+      aMEDName.gibi_name = compGibiName;
+      _longNames[ LN_COMP ].push_back(aMEDName);
+    }
+}
+
+//================================================================================
+/*!
+ * \brief Writes "PILE NUMERO   2": fields on nodes
+ */
+//================================================================================
+
+void SauvWriter::writeNodalFields(map<string,int>& fldNamePrefixMap)
+{
+  writeFieldNames( /*isNodal=*/true, fldNamePrefixMap );
+
+  TFieldCounter fcount (*_sauvFile, 10);
+
+  // EXAMPLE ( with no values )
+
+  // (1)       4       7       2       1
+  // (2)     -88       0       3     -89       0       1     -90       0       2     -91
+  // (2)       0       1
+  // (3) FX   FY   FZ   FZ   FX   FY   FLX
+  // (4)       0       0       0       0       0       0       0
+  // (5)           cree  par  muc pri
+  // (6)
+  // (7)       2
+  for ( size_t iF = 0; iF < _nodeFields.size(); ++iF )
+    {
+      // (1) write nb subcomponents, nb components(total)
+      vector< pair<int,int> >  iters = _nodeFields[iF]->getIterations();
+      const vector<string>& compInfo = _nodeFields[iF]->getInfo();
+      const int nbSub = iters.size();
+      const int nbComp = compInfo.size();
+      const int totalNbComp = nbSub * nbComp;
+      *_sauvFile << setw(8) << nbSub
+                 << setw(8) << totalNbComp
+                 << setw(8) << -1         // IFOUR
+                 << setw(8) << 0 << endl; // nb attributes
+
+      // (2) for each sub-component (iteration)
+      // write support, number of values and number of components
+      fcount.init(10);
+      vector< int > vals(3);
+      for ( int iIt = 0; iIt < iters.size(); ++iIt )
+        {
+          pair<int,int> it = iters[iIt];
+
+          vector<INTERP_KERNEL::NormalizedCellType> types;
+          vector< vector<TypeOfField> > typesF;
+          vector< vector<string> > pfls, locs;
+          vector< vector<const DataArrayDouble *> > valsVec;
+          valsVec = _nodeFields[iF]->getFieldSplitedByType( it.first, it.second, _fileMesh->getName(),
+                                                            types, typesF, pfls, locs);
+          // believe that there can be only one type in a nodal field,
+          // so do not use a loop on types
+          if ( pfls[0][0].empty() ) pfls[0][0] = noProfileName( types[0] );
+          map< string, SubMesh* >::iterator pfl2Sub = _profile2Sub.find( pfls[0][0] );
+          if ( pfl2Sub == _profile2Sub.end() )
+            THROW_IK_EXCEPTION( "SauvWriter::writeNodalFields(): no sub-mesh for profile |"
+                                << pfls[0][0] << "|");
+          vals[0] = -pfl2Sub->second->_id;
+          vals[1] = valsVec[0][0]->getNumberOfTuples() / nbComp;
+          vals[2] = compInfo.size();
+          for ( size_t i = 0; i < vals.size(); ++i, fcount++ )
+            *_sauvFile << setw(8) << vals[i];
+        }
+      fcount.stop();
+
+      // (3) Write names of components
+      map<string, string> mapMedToGibi;
+      makeCompNames( _nodeFields[iF]->getName(), compInfo, mapMedToGibi );
+      fcount.init(8);
+      *_sauvFile << left;
+      for ( int iIt = 0; iIt < iters.size(); ++iIt )
+        for ( size_t i = 0; i < compInfo.size(); ++i, fcount++ )
+          *_sauvFile << " "  << setw(4) << mapMedToGibi[compInfo[i]];
+      *_sauvFile << right;
+      fcount.stop();
+
+      // (4) nb harmonics
+      fcount.init(10);
+      for ( size_t i = 0; i < totalNbComp; ++i, fcount++ )
+        *_sauvFile << " "  << setw(8) << 0;
+      fcount.stop();
+
+      string description = _nodeFields[iF]->getName();
+      *_sauvFile << endl;                                         // (5) TYPE
+      *_sauvFile << setw(72) << description.substr(0,71) << endl; // (6) TITRE
+      //*_sauvFile << endl;                                         // (7) 0 attributes
+
+      // write values of each component
+      fcount.init( 3 ); // 3 values per a line
+      for ( int iIt = 0; iIt < iters.size(); ++iIt )
+        {
+          pair<int,int> it = iters[iIt];
+
+          vector<INTERP_KERNEL::NormalizedCellType> types;
+          vector< vector<TypeOfField> > typesF;
+          vector< vector<string> > pfls, locs;
+          vector< vector<const DataArrayDouble *> > valsVec;
+          valsVec = _nodeFields[iF]->getFieldSplitedByType( it.first, it.second, _fileMesh->getName(),
+                                                            types, typesF, pfls, locs);
+          // believe that there can be only one type in a nodal field,
+          // so do not perform a loop on types
+          const DataArrayDouble* valsArray = valsVec[0][0];
+          for ( size_t j = 0; j < compInfo.size(); ++j )
+            {
+              for ( size_t i = 0; i < valsArray->getNumberOfTuples(); ++i, fcount++ )
+                *_sauvFile << setw(22) << valsArray->getIJ( i, j );
+              fcount.stop();
+            }
+        }
+    } // loop on fiels
+}
+
+//================================================================================
+/*!
+ * \brief Writes "PILE NUMERO  39": fields on cells
+ */
+//================================================================================
+
+void SauvWriter::writeElemFields(map<string,int>& fldNamePrefixMap)
+{
+  writeFieldNames( /*isNodal=*/false, fldNamePrefixMap );
+
+  TFieldCounter fcount (*_sauvFile, 10);
+
+  // REAL EXAMPLE
+
+  // (1)        1       2       6      16
+  // (2)                                                         CARACTERISTIQUES
+  // (3)      -15  317773       4       0       0       0      -2       0       3
+  // (4)             317581
+  // (5)  0
+  // (6)   317767  317761  317755  317815
+  // (7)  YOUN     NU       H        SIGY
+  // (8)  REAL*8            REAL*8            REAL*8            REAL*8
+  // (9)        1       1       0       0
+  // (10)  2.00000000000000E+05
+  // (9)       1       1       0       0
+  // (10)  3.30000000000000E-01
+  // (9)       1       1       0       0
+  // (10)  1.00000000000000E+04
+  // (9)       6     706       0       0
+  // (10)  1.00000000000000E+02  1.00000000000000E+02  1.00000000000000E+02
+  // (10)  1.00000000000000E+02  1.00000000000000E+02  1.00000000000000E+02
+  // (10)  ...
+
+  for ( size_t iF = 0; iF < _cellFields.size(); ++iF )
+    {
+      // count nb of sub-components
+      int iSub, nbSub = 0;
+      vector< pair<int,int> >  iters = _cellFields[iF]->getIterations();
+      for ( int iIt = 0; iIt < iters.size(); ++iIt )
+        {
+          pair<int,int> it = iters[iIt];
+
+          vector<INTERP_KERNEL::NormalizedCellType> types;
+          vector< vector<TypeOfField> > typesF;
+          vector< vector<string> > pfls, locs;
+          vector< vector<const DataArrayDouble *> > valsVec;
+          valsVec = _cellFields[iF]->getFieldSplitedByType( it.first, it.second, _fileMesh->getName(),
+                                                            types, typesF, pfls, locs);
+          for ( size_t i = 0; i < valsVec.size(); ++i )
+            nbSub += valsVec[i].size();
+        }
+      // (1) write nb sub-components, title length
+      *_sauvFile << setw(8) << nbSub
+                 << setw(8) << -1 // whatever
+                 << setw(8) << 6  // whatever
+                 << setw(8) << 72 << endl; // title length
+      // (2) title
+      string title = _cellFields[iF]->getName();
+      *_sauvFile << setw(72) << title.substr(0,71) << endl;
+      *_sauvFile << setw(72) << " " << endl;
+
+      // (3) support, nb components
+      vector<int> vals(9, 0);
+      const vector<string>& compInfo = _cellFields[iF]->getInfo();
+      vals[2] = compInfo.size();
+      fcount.init(10);
+      for ( int iIt = 0; iIt < iters.size(); ++iIt )
+        {
+          pair<int,int> it = iters[iIt];
+
+          vector<INTERP_KERNEL::NormalizedCellType> types;
+          vector< vector<TypeOfField> > typesF;
+          vector< vector<string> > pfls, locs;
+          vector< vector<const DataArrayDouble *> > valsVec;
+          valsVec = _cellFields[iF]->getFieldSplitedByType( it.first, it.second, _fileMesh->getName(),
+                                                            types, typesF, pfls, locs);
+          for ( size_t iType = 0; iType < pfls.size(); ++iType )
+            for ( size_t iP = 0; iP < pfls[iType].size(); ++iP )
+              {
+                if ( pfls[iType][iP].empty() ) pfls[iType][iP] = noProfileName( types[iType] );
+                map< string, SubMesh* >::iterator pfl2Sub = _profile2Sub.find( pfls[iType][iP] );
+                if ( pfl2Sub == _profile2Sub.end() )
+                  THROW_IK_EXCEPTION( "SauvWriter::writeElemFields(): no sub-mesh for profile |"
+                                      << pfls[iType][iP] << "|");
+                const int supportID = pfl2Sub->second->_id;
+                vals[0] = -supportID;
+
+                for ( size_t i = 0; i < vals.size(); ++i, fcount++ )
+                  *_sauvFile << setw(8) << vals[ i ];
+              }
+        }
+      fcount.stop();
+
+      // (4) dummy strings
+      for ( fcount.init(4), iSub = 0; iSub < nbSub; ++iSub, fcount++ )
+        *_sauvFile << "                  ";
+      fcount.stop();
+
+      // (5) dummy strings
+      for ( fcount.init(8), iSub = 0; iSub < nbSub; ++iSub, fcount++ )
+        *_sauvFile << "         ";
+      fcount.stop();
+
+      // loop on sub-components of a field, each of which refers to
+      // a certain support and has its own number of components
+      for ( int iIt = 0; iIt < iters.size(); ++iIt )
+        {
+          pair<int,int> it = iters[iIt];
+          writeElemTimeStamp( iF, it.first, it.second );
+        }
+    } // loop on cell fields
+}
+
+//================================================================================
+/*!
+ * \brief Write one elemental time stamp
+ */
+//================================================================================
+
+void SauvWriter::writeElemTimeStamp(int iF, int iter, int order)
+{
+  // (6)   317767  317761  317755  317815
+  // (7)  YOUN     NU       H        SIGY
+  // (8)  REAL*8            REAL*8            REAL*8            REAL*8
+  // (9)        1       1       0       0
+  // (10)  2.00000000000000E+05
+  // (9)       1       1       0       0
+  // (10)  3.30000000000000E-01
+  // (9)       1       1       0       0
+  // (10)  1.00000000000000E+04
+  // (9)       6     706       0       0
+  // (10)  1.00000000000000E+02  1.00000000000000E+02  1.00000000000000E+02
+  // (10)  1.00000000000000E+02  1.00000000000000E+02  1.00000000000000E+02
+
+  TFieldCounter fcount (*_sauvFile, 10);
+
+  vector<INTERP_KERNEL::NormalizedCellType> types;
+  vector< vector<TypeOfField> > typesF;
+  vector< vector<string> > pfls, locs;
+  vector< vector<const DataArrayDouble *> > valsVec;
+  valsVec = _cellFields[iF]->getFieldSplitedByType( iter, order, _fileMesh->getName(),
+                                                    types, typesF, pfls, locs);
+  for ( size_t iType = 0; iType < pfls.size(); ++iType )
+    for ( size_t iP = 0; iP < pfls[iType].size(); ++iP )
+      {
+        const vector<string>& compInfo = _cellFields[iF]->getInfo();
+
+        // (6) component addresses
+        int iComp = 0, nbComp = compInfo.size();
+        for ( fcount.init(10); iComp < nbComp; ++iComp, fcount++ )
+          *_sauvFile << setw(8) << 777; // a good number
+        fcount.stop();
+
+        // (7) component names
+        map<string, string> mapMedToGibi;
+        makeCompNames( _cellFields[iF]->getName(), compInfo, mapMedToGibi );
+        *_sauvFile << left;
+        for ( fcount.init(8), iComp = 0; iComp < nbComp; ++iComp, fcount++ )
+          *_sauvFile << " "  << setw(8) << mapMedToGibi[compInfo[iComp]];
+        fcount.stop();
+
+        // (8) component types
+        for ( fcount.init(4), iComp = 0; iComp < nbComp; ++iComp, fcount++ )
+          *_sauvFile << " "  << setw(17) << "REAL*8";
+        fcount.stop();
+        *_sauvFile << right;
+
+        // (9) nb values per element, nb of elements
+        int nbPntPerCell = 1;
+        if ( !locs[iType][iP].empty() )
+          {
+            int locID = _cellFields[iF]->getLocalizationId( locs[iType][iP].c_str() );
+            nbPntPerCell = _cellFields[iF]->getNbOfGaussPtPerCell( locID );
+          }
+        else if ( typesF[iType][iP] == ON_GAUSS_NE )
+          {
+            nbPntPerCell = INTERP_KERNEL::CellModel::GetCellModel(types[iType]).getNumberOfNodes();
+          }
+
+        // (10) values
+        const DataArrayDouble* valArray = valsVec[iType][iP];
+        for ( iComp = 0; iComp < nbComp; ++iComp )
+          {
+            *_sauvFile << setw(8) << nbPntPerCell
+                       << setw(8) << valArray->getNbOfElems() / nbPntPerCell / nbComp
+                       << setw(8) << 0
+                       << setw(8) << 0
+                       << endl;
+            fcount.init(3);
+            for ( size_t i = 0; i < valArray->getNumberOfTuples(); ++i, fcount++ )
+              *_sauvFile << setw(22) << valArray->getIJ( i, iComp );
+            fcount.stop();
+          }
+      }
+}
+
+//================================================================================
+/*!
+ * \brief Write the last record of the SAUV file
+ */
+//================================================================================
+
+void SauvWriter::writeLastRecord()
+{
+  *_sauvFile << " ENREGISTREMENT DE TYPE   5" << endl;
+  *_sauvFile << "LABEL AUTOMATIQUE :   1" << endl;
+}
diff --git a/src/MEDLoader/SauvWriter.hxx b/src/MEDLoader/SauvWriter.hxx
new file mode 100644 (file)
index 0000000..0bea6ac
--- /dev/null
@@ -0,0 +1,114 @@
+// Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+// File      : SauvWriter.hxx
+// Created   : Wed Aug 24 11:18:49 2011
+// Author    : Edward AGAPOV (eap)
+
+#ifndef __SauvWriter_HXX__
+#define __SauvWriter_HXX__
+
+#include "MEDLoaderDefines.hxx"
+#include "MEDCouplingRefCountObject.hxx"
+#include "NormalizedUnstructuredMesh.hxx"
+#include "SauvUtilities.hxx"
+#include "MEDCouplingAutoRefCountObjectPtr.hxx"
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace ParaMEDMEM
+{
+  class MEDFileData;
+  class MEDFileMesh;
+  class MEDFileFieldMultiTS;
+  class DataArrayInt;
+
+  /*!
+   * \brief Class to write a MEDFileData into a SAUVE format file
+   */
+  class MEDLOADER_EXPORT SauvWriter : public ParaMEDMEM::RefCountObject
+  {
+  public:
+    static SauvWriter * New();
+    void setMEDFileDS(const MEDFileData* medData, unsigned meshIndex = 0);
+    void write(const char* fileName);
+
+  private:
+
+    /*!
+     * \brief Class representing a GIBI sub-mesh (described in the pile 1 of the SAUVE file).
+     * It stands for a named med sub-mesh (family, etc) and contains either cell IDs or other sub-meshes. 
+     */
+    struct SubMesh
+    {
+      std::vector<int>      _cellIDsByType[ INTERP_KERNEL::NORM_MAXTYPE+1 ];
+      std::vector<SubMesh*> _subs;
+      std::string           _name;
+      int                   _id;
+      int                   _nbSauvObjects;
+      int                   _dimRelExt;
+      int nbTypes() const;
+      static int cellIDsByTypeSize() { return INTERP_KERNEL::NORM_MAXTYPE+1; }
+    };
+    SubMesh* addSubMesh(const std::string& name, int dimRelExt);
+
+    void fillSubMeshes(int& nbSauvObjects, std::map<std::string,int>& nameNbMap);
+    void fillFamilySubMeshes();
+    void fillGroupSubMeshes();
+    void fillProfileSubMeshes();
+    int evaluateNbProfileSubMeshes() const;
+    void makeCompNames(const std::string&                  fieldName,
+                       const std::vector<std::string>&     compInfo,
+                       std::map<std::string, std::string>& compMedToGibi );
+    void makeProfileIDs( SubMesh*                          sm,
+                         INTERP_KERNEL::NormalizedCellType type,
+                         const DataArrayInt*               profile );
+    void writeFileHead();
+    void writeSubMeshes();
+    void writeCompoundSubMesh(int iSub);
+    void writeNodes();
+    void writeLongNames();
+    void writeNodalFields(std::map<std::string,int>& fldNamePrefixMap);
+    void writeElemFields(std::map<std::string,int>& fldNamePrefixMap);
+    void writeFieldNames(const bool isNodal, std::map<std::string,int>& fldNamePrefixMap);
+    void writeElemTimeStamp(int iF, int iter, int order);
+    void writeLastRecord();
+    void writeNames( const std::map<std::string,int>& nameNbMap );
+
+  private:
+
+    MEDCouplingAutoRefCountObjectPtr< MEDFileMesh >                        _fileMesh;
+    std::vector< MEDCouplingAutoRefCountObjectPtr< MEDFileFieldMultiTS > > _nodeFields;
+    std::vector< MEDCouplingAutoRefCountObjectPtr< MEDFileFieldMultiTS > > _cellFields;
+
+    std::vector<SubMesh>                      _subs;
+    std::map< int, SubMesh* >                 _famIDs2Sub;
+    std::map< std::string, SubMesh* >         _profile2Sub;
+    enum
+      {
+        LN_MAIL=0, LN_CHAM, LN_COMP, LN_NB
+      };
+    std::vector<SauvUtilities::nameGIBItoMED> _longNames[ LN_NB ];
+
+    std::fstream*                             _sauvFile;
+  };
+}
+
+#endif
index aa16d7c959073667dfa817e38c95af32812f7d7c..3bdfbd25fa91e8061d1ec716565bd4244728fe4f 100644 (file)
@@ -30,6 +30,8 @@
 #include "MEDFileField.hxx"
 #include "MEDFileData.hxx"
 #include "MEDLoaderTypemaps.i"
+#include "SauvReader.hxx"
+#include "SauvWriter.hxx"
 
 using namespace ParaMEDMEM;
 %}
@@ -97,6 +99,10 @@ using namespace ParaMEDMEM;
 %newobject ParaMEDMEM::MEDFileData::getMeshes;
 %newobject ParaMEDMEM::MEDFileData::getFields;
 
+%newobject ParaMEDMEM::SauvWriter::New;
+%newobject ParaMEDMEM::SauvReader::New;
+%newobject ParaMEDMEM::SauvReader::loadInMEDFileDS;
+
 %feature("unref") MEDFileMesh "$this->decrRef();"
 %feature("unref") MEDFileUMesh "$this->decrRef();"
 %feature("unref") MEDFileCMesh "$this->decrRef();"
@@ -107,6 +113,8 @@ using namespace ParaMEDMEM;
 %feature("unref") MEDFileFieldMultiTS "$this->decrRef();"
 %feature("unref") MEDFileFields "$this->decrRef();"
 %feature("unref") MEDFileData "$this->decrRef();"
+%feature("unref") SauvReader "$this->decrRef();"
+%feature("unref") SauvWriter "$this->decrRef();"
 
 class MEDLoader
 {
@@ -694,6 +702,7 @@ namespace ParaMEDMEM
     int getNumberOfTS() const;
     std::string getName() const;
     std::string getMeshName() const throw(INTERP_KERNEL::Exception);
+    const std::vector<std::string>& getInfo() const;
     %extend
        {
          PyObject *getIterations() const
@@ -856,4 +865,20 @@ namespace ParaMEDMEM
          }
        }
   };
+
+  class SauvReader : public RefCountObject
+  {
+  public:
+    static SauvReader* New(const char *fileName) throw(INTERP_KERNEL::Exception);
+    MEDFileData * loadInMEDFileDS() throw(INTERP_KERNEL::Exception);
+  };
+
+  class SauvWriter : public RefCountObject
+  {
+  public:
+    static SauvWriter * New();
+    void setMEDFileDS(const MEDFileData* medData, unsigned meshIndex = 0);
+    void write(const char* fileName);
+  };
+
 }
diff --git a/src/MEDLoader/Swig/SauvLoaderTest.py b/src/MEDLoader/Swig/SauvLoaderTest.py
new file mode 100644 (file)
index 0000000..1b0a07a
--- /dev/null
@@ -0,0 +1,162 @@
+#  -*- coding: iso-8859-1 -*-
+# Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from MEDLoader import *
+import unittest, os
+from MEDLoaderDataForTest import MEDLoaderDataForTest
+
+class SauvLoaderTest(unittest.TestCase):
+
+    def testSauv2Med(self):
+        # get a file containing all types of readable piles
+        self.assertTrue( os.getenv("MED_ROOT_DIR") )
+        sauvFile = os.path.join( os.getenv("MED_ROOT_DIR"), "share","salome",
+                                 "resources","med","allPillesTest.sauv")
+        self.assertTrue( os.access( sauvFile, os.F_OK))
+
+        # read SAUV and write MED
+        medFile = "SauvLoaderTest.med"
+        sr=SauvReader.New(sauvFile);
+        d2=sr.loadInMEDFileDS();
+        d2.write(medFile,0);
+
+        # check 
+        self.assertEqual(1,d2.getNumberOfMeshes())
+        self.assertEqual(8+97,d2.getNumberOfFields())
+        mm = d2.getMeshes()
+        m = mm.getMeshAtPos(0)
+        self.assertEqual(17,len(m.getGroupsNames()))
+
+        os.remove( medFile )
+        pass
+
+    def testMed2Sauv(self):
+        # read pointe.med
+        self.assertTrue( os.getenv("MED_ROOT_DIR") )
+        medFile = os.path.join( os.getenv("MED_ROOT_DIR"), "share","salome",
+                                "resources","med","pointe.med")
+        self.assertTrue( os.access( medFile, os.F_OK))
+        pointeMed = MEDFileData.New( medFile )
+
+        # add 3 faces to pointeMed
+        pointeMedMesh = pointeMed.getMeshes().getMeshAtPos(0)
+        pointeM1D = MEDCouplingUMesh.New()
+        pointeM1D.setCoords( pointeMedMesh.getCoords() )
+        pointeM1D.setMeshDimension( 2 )
+        pointeM1D.allocateCells( 3 )
+        pointeM1D.insertNextCell( NORM_TRI3, 3, [0,1,2])
+        pointeM1D.insertNextCell( NORM_TRI3, 3, [0,1,3])
+        pointeM1D.insertNextCell( NORM_QUAD4, 4, [10,11,12,13])
+        pointeM1D.finishInsertingCells()
+        pointeMedMesh.setMeshAtLevel( -1, pointeM1D )
+        pointeMed.getMeshes().setMeshAtPos( 0, pointeMedMesh )
+
+        # add a field on 2 faces to pointeMed
+        ff1=MEDFileFieldMultiTS.New()
+        f1=MEDCouplingFieldDouble.New(ON_GAUSS_NE,ONE_TIME)
+        #f1.setMesh( pointeM1D )
+        f1.setName("Field on 2 faces")
+        d=DataArrayDouble.New()
+        d.alloc(3+4,2)
+        d.setInfoOnComponent(0,"sigX [MPa]")
+        d.setInfoOnComponent(1,"sigY [GPa]")
+        d.setValues([311,312,321,322,331,332,411,412,421,422,431,432,441,442],3+4,2)
+        f1.setArray(d)
+        da=DataArrayInt.New()
+        da.setValues([0,2],2,1)
+        da.setName("sup2")
+        ff1.appendFieldProfile(f1,pointeMedMesh,-1,da)
+        pointeMed.getFields().pushField( ff1 )
+
+        #remove fieldnodeint
+        pointeFields = pointeMed.getFields()
+        for i in range( pointeFields.getNumberOfFields() ):
+            if pointeFields.getFieldAtPos(i).getName() == "fieldnodeint":
+                pointeFields.destroyFieldAtPos( i )
+                break
+
+        # write pointeMed to SAUV
+        sauvFile = "SauvLoaderTest.sauv"
+        sw=SauvWriter.New();
+        sw.setMEDFileDS(pointeMed);
+        sw.write(sauvFile);
+
+        # read SAUV and check
+        sr=SauvReader.New(sauvFile);
+        d2=sr.loadInMEDFileDS();
+        self.assertEqual(1,d2.getNumberOfMeshes())
+        self.assertEqual(4,d2.getNumberOfFields())
+        m = d2.getMeshes().getMeshAtPos(0)
+        self.assertEqual("maa1",m.getName())
+        self.assertEqual(5,len(m.getGroupsNames()))
+        self.assertEqual(3,m.getMeshDimension())
+        groups = m.getGroupsNames()
+        self.assertTrue( "groupe1" in groups )
+        self.assertTrue( "groupe2" in groups )
+        self.assertTrue( "groupe3" in groups )
+        self.assertTrue( "groupe4" in groups )
+        self.assertTrue( "groupe5" in groups )
+        self.assertEqual(16,m.getSizeAtLevel(0))
+        um0 = m.getGenMeshAtLevel(0)
+        self.assertEqual(12, um0.getNumberOfCellsWithType( NORM_TETRA4 ))
+        self.assertEqual(2, um0.getNumberOfCellsWithType( NORM_PYRA5 ))
+        self.assertEqual(2, um0.getNumberOfCellsWithType( NORM_HEXA8 ))
+        um1 = m.getGenMeshAtLevel(-1)
+        self.assertEqual(2, um1.getNumberOfCellsWithType( NORM_TRI3 ))
+        pointeUM0 = pointeMedMesh.getGenMeshAtLevel(0)
+        self.assertTrue(m.getCoords().isEqualWithoutConsideringStr(pointeMedMesh.getCoords(),1e-12))
+        self.assertEqual( um0.getMeasureField(0).accumulate(0),
+                          pointeUM0.getMeasureField(0).accumulate(0),1e-12)
+        # check fields
+        # fieldnodedouble
+        fieldnodedoubleTS1 = pointeMed.getFields().getFieldWithName("fieldnodedouble")
+        fieldnodedoubleTS2 = d2.getFields().getFieldWithName("fieldnodedouble")
+        self.assertEqual( fieldnodedoubleTS1.getInfo(), fieldnodedoubleTS2.getInfo())
+        self.assertEqual( fieldnodedoubleTS1.getNumberOfTS(), fieldnodedoubleTS2.getNumberOfTS())
+        io1 = fieldnodedoubleTS1.getIterations()
+        io2 = fieldnodedoubleTS2.getIterations()
+        for i in range(fieldnodedoubleTS1.getNumberOfTS() ):
+            fnd1 = fieldnodedoubleTS1.getFieldOnMeshAtLevel(ON_NODES, io1[i][0],io1[i][1],pointeUM0)
+            fnd2 = fieldnodedoubleTS2.getFieldOnMeshAtLevel(ON_NODES, io2[i][0],io2[i][1],um0)
+            self.assertTrue( fnd1.getArray().isEqual( fnd2.getArray(), 1e-12 ))
+        # fieldcelldoublevector
+        fieldnodedoubleTS1 = pointeMed.getFields().getFieldWithName("fieldcelldoublevector")
+        fieldnodedoubleTS2 = d2.getFields().getFieldWithName("fieldcelldoublevector")
+        self.assertEqual( fieldnodedoubleTS1.getInfo(), fieldnodedoubleTS2.getInfo())
+        self.assertEqual( fieldnodedoubleTS1.getNumberOfTS(), fieldnodedoubleTS2.getNumberOfTS())
+        io1 = fieldnodedoubleTS1.getIterations()
+        io2 = fieldnodedoubleTS2.getIterations()
+        for i in range(fieldnodedoubleTS1.getNumberOfTS() ):
+            fnd1 = fieldnodedoubleTS1.getFieldOnMeshAtLevel(ON_CELLS, io1[i][0],io1[i][1],pointeUM0)
+            fnd2 = fieldnodedoubleTS2.getFieldOnMeshAtLevel(ON_CELLS, io2[i][0],io2[i][1],um0)
+            self.assertAlmostEqual( fnd1.accumulate(0), fnd2.accumulate(0) )
+            self.assertAlmostEqual( fnd1.accumulate(1), fnd2.accumulate(1) )
+            self.assertAlmostEqual( fnd1.accumulate(2), fnd2.accumulate(2) )
+        # Field on 2 faces
+        fieldOnFaces = d2.getFields().getFieldWithName(f1.getName())
+        io1 = fieldOnFaces.getIterations()
+        fof = fieldOnFaces.getFieldOnMeshAtLevel(f1.getTypeOfField(),io1[i][0],io1[i][1],um1)
+        self.assertTrue( d.isEqual( fof.getArray(), 1e-12 ))
+
+        os.remove( sauvFile )
+        pass
+    pass
+
+unittest.main()
index 3e57ba030182c3c5c4aa5be6d5cf1fa8dd800570..0d008b8f593d81cb062035039e01d864ddcb4a9e 100755 (executable)
 
 include $(top_srcdir)/adm_local/unix/make_common_starter.am
 
-bin_PROGRAMS= TestMEDLoader
+bin_PROGRAMS= TestMEDLoader TestSauvLoader
 
 TestMEDLoader_CPPFLAGS=@CPPUNIT_INCLUDES@ @PTHREAD_CFLAGS@ -I$(srcdir)/.. -I$(srcdir)/../../INTERP_KERNEL/Bases -I$(srcdir)/../../INTERP_KERNELTest -I$(srcdir)/../../INTERP_KERNEL -I$(srcdir)/../../INTERP_KERNEL/Geometric2D -I$(srcdir)/../../MEDCoupling
+TestSauvLoader_CPPFLAGS=$(TestMEDLoader_CPPFLAGS) $(MED2_INCLUDES) $(HDF5_INCLUDES)
 
 TestMEDLoader_LDFLAGS = @CPPUNIT_LIBS@ ../libmedloader.la ../../MEDCoupling/libmedcoupling.la ../../INTERP_KERNEL/libinterpkernel.la
+TestSauvLoader_LDFLAGS=$(TestMEDLoader_LDFLAGS)
 
 dist_TestMEDLoader_SOURCES = TestMEDLoader.cxx MEDLoaderTest.cxx MEDLoaderTest.hxx
+dist_TestSauvLoader_SOURCES = TestSauvLoader.cxx SauvLoaderTest.cxx SauvLoaderTest.hxx
 
-UNIT_TEST_PROG = TestMEDLoader
+UNIT_TEST_PROG = TestMEDLoader TestSauvLoader
 
 check : tests
 
 CLEANFILES = \
         UnitTestsResult
-       
+
 clean-local:
        rm -rf *.med
-       
\ No newline at end of file
diff --git a/src/MEDLoader/Test/SauvLoaderTest.cxx b/src/MEDLoader/Test/SauvLoaderTest.cxx
new file mode 100644 (file)
index 0000000..5b1d848
--- /dev/null
@@ -0,0 +1,237 @@
+// Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include "SauvLoaderTest.hxx"
+
+#include "SauvReader.hxx"
+#include "SauvWriter.hxx"
+#include "MEDFileData.hxx"
+#include "MEDCouplingFieldDouble.hxx"
+#include "MEDCouplingMemArray.hxx"
+
+#ifdef WNT
+#include <windows.h>
+#endif
+
+#include <vector>
+#include <string>
+
+using namespace ParaMEDMEM;
+
+void SauvLoaderTest::testSauv2Med()
+{
+  // read a file containing all types of readable piles
+  std::string file = getResourceFile("allPillesTest.sauv");
+  MEDCouplingAutoRefCountObjectPtr<SauvReader> sr=SauvReader::New(file.c_str());
+  MEDCouplingAutoRefCountObjectPtr<MEDFileData> d2=sr->loadInMEDFileDS();
+  // write MED
+  d2->write("allPillesTest.med",0);
+  // check 
+  CPPUNIT_ASSERT_EQUAL(1,d2->getNumberOfMeshes());
+  CPPUNIT_ASSERT_EQUAL(8+97,d2->getNumberOfFields());
+  MEDFileMesh * m = d2->getMeshes()->getMeshAtPos(0);
+  CPPUNIT_ASSERT_EQUAL(17,int(m->getGroupsNames().size()));
+}
+
+void SauvLoaderTest::testMed2Sauv()
+{
+  // read pointe.med
+  std::string file = getResourceFile("pointe.med");
+  MEDCouplingAutoRefCountObjectPtr<MEDFileData> pointeMed=MEDFileData::New(file.c_str());
+
+  // add 3 faces to pointeMed
+  MEDFileUMesh* pointeMedMesh = static_cast<MEDFileUMesh*>(pointeMed->getMeshes()->getMeshAtPos(0));
+  MEDCouplingAutoRefCountObjectPtr<MEDCouplingUMesh> pointeM1D = MEDCouplingUMesh::New();
+  MEDCouplingAutoRefCountObjectPtr<DataArrayDouble>     coords = pointeMedMesh->getCoords();
+  pointeM1D->setCoords( coords );
+  pointeM1D->setMeshDimension( 2 );
+  pointeM1D->allocateCells( 3 );
+  int conn[]=
+    {
+      0,1,2, 0,1,3, 10,11,12,13
+    };
+  pointeM1D->insertNextCell( INTERP_KERNEL::NORM_TRI3, 3, conn);
+  pointeM1D->insertNextCell( INTERP_KERNEL::NORM_TRI3, 3, conn+3);
+  pointeM1D->insertNextCell( INTERP_KERNEL::NORM_QUAD4, 4, conn+6);
+  pointeM1D->finishInsertingCells();
+  pointeMedMesh->setMeshAtLevel( -1, pointeM1D );
+  pointeMed->getMeshes()->setMeshAtPos( 0, pointeMedMesh );
+
+  // add a field on 2 faces to pointeMed
+  MEDCouplingAutoRefCountObjectPtr<MEDFileFieldMultiTS> ff1=MEDFileFieldMultiTS::New();
+  MEDCouplingAutoRefCountObjectPtr<MEDCouplingFieldDouble> f1=MEDCouplingFieldDouble::New(ON_GAUSS_NE,ONE_TIME);
+  f1->setMesh( pointeM1D );
+  f1->setName("Field on 2 faces");
+  MEDCouplingAutoRefCountObjectPtr<DataArrayDouble> d=DataArrayDouble::New();
+  d->alloc(3+4,2);
+  d->setInfoOnComponent(0,"sigX [MPa]");
+  d->setInfoOnComponent(1,"sigY [GPa]");
+  double vals[2*(3+4)] =
+    {
+      311,312,321,322,331,332,411,412,421,422,431,432,441,442
+    };
+  std::copy(vals,vals+d->getNbOfElems(),d->getPointer());
+  f1->setArray(d);
+  MEDCouplingAutoRefCountObjectPtr<DataArrayInt> da=DataArrayInt::New();
+  int ids[] =
+    {
+      0,2
+    };
+  da->alloc(2,1);
+  std::copy(ids,ids+da->getNbOfElems(),da->getPointer());
+  da->setName("sup2");
+  ff1->appendFieldProfile(f1,pointeMedMesh,-1,da);
+  pointeMed->getFields()->pushField( ff1 );
+
+  // remove "fieldnodeint"
+  MEDFileFields* pointeFields = pointeMed->getFields();
+  for ( int i = 0; i < pointeFields->getNumberOfFields(); ++i )
+    {
+      MEDCouplingAutoRefCountObjectPtr<MEDFileFieldMultiTS> ts = pointeFields->getFieldAtPos(i);
+      if ( std::string("fieldnodeint") == ts->getName())
+        {
+          pointeFields->destroyFieldAtPos( i );
+          break;
+        }
+    }
+  // write pointeMed to SAUV
+  const char* sauvFile = "pointe.sauv";
+  MEDCouplingAutoRefCountObjectPtr<SauvWriter> sw=SauvWriter::New();
+  sw->setMEDFileDS(pointeMed);
+  sw->write(sauvFile);
+
+  // read SAUV and check
+  MEDCouplingAutoRefCountObjectPtr<SauvReader> sr=SauvReader::New(sauvFile);
+  MEDCouplingAutoRefCountObjectPtr<MEDFileData> d2=sr->loadInMEDFileDS();
+  CPPUNIT_ASSERT_EQUAL(1,d2->getNumberOfMeshes());
+  CPPUNIT_ASSERT_EQUAL(4,d2->getNumberOfFields());
+  MEDFileUMesh * m = static_cast<MEDFileUMesh*>( d2->getMeshes()->getMeshAtPos(0) );
+  CPPUNIT_ASSERT_EQUAL(std::string("maa1"),std::string(m->getName() ));
+  CPPUNIT_ASSERT_EQUAL(3,m->getMeshDimension());
+  std::vector<std::string > groups = m->getGroupsNames();
+  CPPUNIT_ASSERT_EQUAL(5,(int)groups.size());
+  CPPUNIT_ASSERT( std::find(groups.begin(),groups.end(),"groupe1") != groups.end() );
+  CPPUNIT_ASSERT( std::find(groups.begin(),groups.end(),"groupe2") != groups.end() );
+  CPPUNIT_ASSERT( std::find(groups.begin(),groups.end(),"groupe3") != groups.end() );
+  CPPUNIT_ASSERT( std::find(groups.begin(),groups.end(),"groupe4") != groups.end() );
+  CPPUNIT_ASSERT( std::find(groups.begin(),groups.end(),"groupe5") != groups.end() );
+  CPPUNIT_ASSERT_EQUAL(16,m->getSizeAtLevel(0));
+  MEDCouplingAutoRefCountObjectPtr<MEDCouplingMesh> um0 = m->getGenMeshAtLevel(0);
+  CPPUNIT_ASSERT_EQUAL(12, um0->getNumberOfCellsWithType( INTERP_KERNEL::NORM_TETRA4 ));
+  CPPUNIT_ASSERT_EQUAL(2,  um0->getNumberOfCellsWithType( INTERP_KERNEL::NORM_PYRA5 ));
+  CPPUNIT_ASSERT_EQUAL(2,  um0->getNumberOfCellsWithType( INTERP_KERNEL::NORM_HEXA8 ));
+  MEDCouplingAutoRefCountObjectPtr<MEDCouplingMesh> um1 = m->getGenMeshAtLevel(-1);
+  CPPUNIT_ASSERT_EQUAL(2, um1->getNumberOfCellsWithType( INTERP_KERNEL::NORM_TRI3 ));
+  MEDCouplingAutoRefCountObjectPtr<MEDCouplingUMesh> pointeUM0 =
+    static_cast<MEDCouplingUMesh*>( pointeMedMesh->getGenMeshAtLevel(0));
+  MEDCouplingAutoRefCountObjectPtr< DataArrayDouble > coo = m->getCoords();
+  MEDCouplingAutoRefCountObjectPtr< DataArrayDouble > pointeCoo = pointeMedMesh->getCoords();
+  CPPUNIT_ASSERT(coo->isEqualWithoutConsideringStr(*pointeCoo,1e-12));
+  MEDCouplingAutoRefCountObjectPtr<MEDCouplingFieldDouble> vol = um0->getMeasureField(0);
+  MEDCouplingAutoRefCountObjectPtr<MEDCouplingFieldDouble> pointeVol = pointeUM0->getMeasureField(0);
+  CPPUNIT_ASSERT_DOUBLES_EQUAL( vol->accumulate(0), pointeVol->accumulate(0),1e-12);
+  // check fields
+  // fieldnodedouble
+  MEDCouplingAutoRefCountObjectPtr<MEDFileFieldMultiTS> fieldnodedoubleTS1 =
+    pointeMed->getFields()->getFieldWithName("fieldnodedouble");
+  MEDCouplingAutoRefCountObjectPtr<MEDFileFieldMultiTS> fieldnodedoubleTS2 =
+    d2->getFields()->getFieldWithName("fieldnodedouble");
+  CPPUNIT_ASSERT_EQUAL( fieldnodedoubleTS1->getInfo().size(), fieldnodedoubleTS2->getInfo().size());
+  for ( size_t i = 0; i < fieldnodedoubleTS1->getInfo().size(); ++i )
+    CPPUNIT_ASSERT_EQUAL( fieldnodedoubleTS1->getInfo()[i], fieldnodedoubleTS2->getInfo()[i]);
+  CPPUNIT_ASSERT_EQUAL( fieldnodedoubleTS1->getNumberOfTS(), fieldnodedoubleTS2->getNumberOfTS());
+  std::vector< std::pair<int,int> > io1 = fieldnodedoubleTS1->getIterations();
+  std::vector< std::pair<int,int> > io2 = fieldnodedoubleTS2->getIterations();
+  for ( int i =0; i < fieldnodedoubleTS1->getNumberOfTS(); ++i )
+    {
+      MEDCouplingAutoRefCountObjectPtr<MEDCouplingFieldDouble> fnd1 =
+        fieldnodedoubleTS1->getFieldOnMeshAtLevel(ON_NODES, io1[i].first,io1[i].second,pointeUM0);
+      MEDCouplingAutoRefCountObjectPtr<MEDCouplingFieldDouble> fnd2 =
+        fieldnodedoubleTS2->getFieldOnMeshAtLevel(ON_NODES, io2[i].first,io2[i].second,um0);
+      CPPUNIT_ASSERT( fnd1->getArray()->isEqual( *fnd2->getArray(), 1e-12 ));
+    }
+  // fieldcelldoublevector
+  MEDCouplingAutoRefCountObjectPtr<MEDFileFieldMultiTS> fieldcelldoublevectorTS1 =
+    pointeMed->getFields()->getFieldWithName("fieldcelldoublevector");
+  MEDCouplingAutoRefCountObjectPtr<MEDFileFieldMultiTS> fieldcelldoublevectorTS2 =
+    d2->getFields()->getFieldWithName("fieldcelldoublevector");
+  CPPUNIT_ASSERT_EQUAL( fieldcelldoublevectorTS1->getInfo().size(), fieldcelldoublevectorTS2->getInfo().size());
+  for ( size_t i = 0; i < fieldcelldoublevectorTS1->getInfo().size(); ++i )
+    CPPUNIT_ASSERT_EQUAL( fieldcelldoublevectorTS1->getInfo()[i], fieldcelldoublevectorTS2->getInfo()[i]);
+  CPPUNIT_ASSERT_EQUAL( fieldcelldoublevectorTS1->getNumberOfTS(), fieldcelldoublevectorTS2->getNumberOfTS());
+  io1 = fieldcelldoublevectorTS1->getIterations();
+  io2 = fieldcelldoublevectorTS2->getIterations();
+  for ( int i =0; i < fieldcelldoublevectorTS1->getNumberOfTS(); ++i )
+    {
+      MEDCouplingAutoRefCountObjectPtr<MEDCouplingFieldDouble> fnd1 =
+        fieldcelldoublevectorTS1->getFieldOnMeshAtLevel(ON_CELLS, io1[i].first,io1[i].second,pointeUM0);
+      MEDCouplingAutoRefCountObjectPtr<MEDCouplingFieldDouble> fnd2 =
+        fieldcelldoublevectorTS2->getFieldOnMeshAtLevel(ON_CELLS, io2[i].first,io2[i].second,um0);
+      CPPUNIT_ASSERT_DOUBLES_EQUAL( fnd1->accumulate(0), fnd2->accumulate(0), 1e-12 );
+      CPPUNIT_ASSERT_DOUBLES_EQUAL( fnd1->accumulate(1), fnd2->accumulate(1), 1e-12 );
+      CPPUNIT_ASSERT_DOUBLES_EQUAL( fnd1->accumulate(2), fnd2->accumulate(2), 1e-12 );
+    }
+  // "Field on 2 faces"
+  MEDCouplingAutoRefCountObjectPtr<MEDFileFieldMultiTS> fieldOnFaces =
+    d2->getFields()->getFieldWithName(f1->getName());
+  io1 = fieldOnFaces->getIterations();
+  MEDCouplingAutoRefCountObjectPtr<MEDCouplingFieldDouble> fof =
+    fieldOnFaces->getFieldOnMeshAtLevel(f1->getTypeOfField(),io1[0].first,io1[0].second,um1);
+  CPPUNIT_ASSERT( d->isEqual( *fof->getArray(), 1e-12 ));
+}
+
+void SauvLoaderTest::tearDown()
+{
+  const int nbFilesToRemove = 2;
+  const char* fileToRemove[nbFilesToRemove] = { "allPillesTest.med", "pointe.sauv" };
+  for ( int i = 0; i < nbFilesToRemove; ++i )
+    {
+#ifdef WNT
+      if (GetFileAttributes(fileToRemove[i]) != INVALID_FILE_ATTRIBUTES)
+#else
+        if (access(fileToRemove[i], F_OK) == 0)
+#endif
+      remove(fileToRemove[i]);
+  }
+}
+
+std::string SauvLoaderTest::getResourceFile( const std::string& filename )
+{
+  std::string resourceFile = "";
+
+  if ( getenv("top_srcdir") ) {
+    // we are in 'make check' step
+    resourceFile = getenv("top_srcdir");
+    resourceFile += "/resources/";
+  }
+  else if ( getenv("MED_ROOT_DIR") ) {
+    // use MED_ROOT_DIR env.var
+    resourceFile = getenv("MED_ROOT_DIR");
+    resourceFile += "/share/salome/resources/med/";
+  }
+  resourceFile += filename;
+#ifdef WNT
+  std::string fixedpath = resourceFile;
+  for ( int i=0; i < path.size(); ++i )
+    if (path[i] == '/')
+      fixedpath[i] = '\\';
+  return fixedpath;
+#endif
+  return resourceFile;
+}
diff --git a/src/MEDLoader/Test/SauvLoaderTest.hxx b/src/MEDLoader/Test/SauvLoaderTest.hxx
new file mode 100644 (file)
index 0000000..eea9b46
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef __SauvLoaderTest_HXX__
+#define __SauvLoaderTest_HXX__
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "MEDLoaderTest.hxx"
+
+namespace ParaMEDMEM
+{
+  class SauvLoaderTest : public CppUnit::TestFixture
+  {
+    CPPUNIT_TEST_SUITE(SauvLoaderTest);
+    CPPUNIT_TEST( testSauv2Med );
+    CPPUNIT_TEST( testMed2Sauv );
+    CPPUNIT_TEST_SUITE_END();
+  public:
+    void testSauv2Med();
+    void testMed2Sauv();
+
+  public:
+    void tearDown();
+    static std::string getResourceFile( const std::string& filename );
+  };
+}
+
+#endif
diff --git a/src/MEDLoader/Test/TestSauvLoader.cxx b/src/MEDLoader/Test/TestSauvLoader.cxx
new file mode 100644 (file)
index 0000000..b9fe7f3
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright (C) 2007-2011  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include "CppUnitTest.hxx"
+#include "SauvLoaderTest.hxx"
+
+CPPUNIT_TEST_SUITE_REGISTRATION( ParaMEDMEM::SauvLoaderTest );
+
+#include "BasicMainTest.hxx"