]> SALOME platform Git repositories - modules/smesh.git/blob - src/DriverCGNS/DriverCGNS_Write.cxx
Salome HOME
0021370: EDF SMESH: Hexahedron + Composite Side Disretization generate a bad mesh
[modules/smesh.git] / src / DriverCGNS / DriverCGNS_Write.cxx
1 // Copyright (C) 2007-2011  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 // File      : DriverCGNS_Write.cxx
23 // Created   : Fri Aug  5 17:43:54 2011
24 // Author    : Edward AGAPOV (eap)
25
26 #include "DriverCGNS_Write.hxx"
27
28 #include "SMDS_MeshNode.hxx"
29 #include "SMDS_VolumeTool.hxx"
30 #include "SMESHDS_GroupBase.hxx"
31 #include "SMESHDS_Mesh.hxx"
32 #include "SMESH_Comment.hxx"
33
34 #include <limits>
35 #include <cgnslib.h>
36
37 #if CGNS_VERSION < 3100
38 # define cgsize_t int
39 #endif
40
41 using namespace std;
42
43 namespace
44 {
45   //================================================================================
46   /*!
47    * \brief Return interlace and type of CGNS element for the given SMDSAbs_EntityType
48    */
49   //================================================================================
50
51   const int* getInterlaceAndType( const SMDSAbs_EntityType      smType,
52                                   CGNS_ENUMT( ElementType_t ) & cgType )
53   {
54     static vector< const int* >                 interlaces;
55     static vector< CGNS_ENUMT( ElementType_t )> cgTypes; 
56     if ( interlaces.empty() )
57     {
58       interlaces.resize( SMDSEntity_Last, 0 );
59       cgTypes.resize( SMDSEntity_Last, CGNS_ENUMV( ElementTypeNull ));
60       {
61         static int ids[] = {0};
62         interlaces[SMDSEntity_0D] = ids;
63         cgTypes   [SMDSEntity_0D] = CGNS_ENUMV( NODE );
64       }
65       {
66         static int ids[] = { 0, 1 };
67         interlaces[SMDSEntity_Edge] = ids;
68         cgTypes   [SMDSEntity_Edge] = CGNS_ENUMV( BAR_2 );
69       }
70       {
71         static int ids[] = { 0, 1, 2 };
72         interlaces[SMDSEntity_Quad_Edge] = ids;
73         cgTypes   [SMDSEntity_Quad_Edge] = CGNS_ENUMV( BAR_3 );
74       }
75       {
76         static int ids[] = { 0, 2, 1 };
77         interlaces[SMDSEntity_Triangle] = ids;
78         cgTypes   [SMDSEntity_Triangle] = CGNS_ENUMV( TRI_3 );
79       }
80       {
81         static int ids[] = { 0, 2, 1, 5, 4, 3 };
82         interlaces[SMDSEntity_Quad_Triangle] = ids;
83         cgTypes   [SMDSEntity_Quad_Triangle] = CGNS_ENUMV( TRI_6 );
84       }
85       {
86         static int ids[] = { 0, 3, 2, 1 };
87         interlaces[SMDSEntity_Quadrangle] = ids;
88         cgTypes   [SMDSEntity_Quadrangle] = CGNS_ENUMV( QUAD_4 );
89       }
90       {
91         static int ids[] = { 0,3,2,1,7,6,5,4 };
92         interlaces[SMDSEntity_Quad_Quadrangle] = ids;
93         cgTypes   [SMDSEntity_Quad_Quadrangle] = CGNS_ENUMV( QUAD_8 );
94       }
95       {
96         static int ids[] = { 0, 2, 1, 3 };
97         interlaces[SMDSEntity_Tetra] = ids;
98         cgTypes   [SMDSEntity_Tetra] = CGNS_ENUMV( TETRA_4 );
99       }
100       {
101         static int ids[] = { 0,2,1,3,6,5,4,7,9,8 };
102         interlaces[SMDSEntity_Quad_Tetra] = ids;
103         cgTypes   [SMDSEntity_Quad_Tetra] = CGNS_ENUMV( TETRA_10 );
104       }
105       {
106         static int ids[] = { 0,3,2,1,4 };
107         interlaces[SMDSEntity_Pyramid] = ids;
108         cgTypes   [SMDSEntity_Pyramid] = CGNS_ENUMV( PYRA_5 );
109       }
110       {
111         static int ids[] = { 0,3,2,1,4,8,7,6,5,9,12,11,10 };
112         interlaces[SMDSEntity_Quad_Pyramid] = ids;
113         cgTypes   [SMDSEntity_Quad_Pyramid] = CGNS_ENUMV( PYRA_13 );
114       }
115       {
116         static int ids[] = { 0,2,1,3,5,4 };
117         interlaces[SMDSEntity_Penta] = ids;
118         cgTypes   [SMDSEntity_Penta] = CGNS_ENUMV( PENTA_6 );
119       }
120       {
121         static int ids[] = { 0,2,1,3,5,4,8,7,6,9,11,10,14,13,12 };
122         interlaces[SMDSEntity_Quad_Penta] = ids;
123         cgTypes   [SMDSEntity_Quad_Penta] = CGNS_ENUMV( PENTA_15 );
124       }
125       {
126         static int ids[] = { 0,3,2,1,4,7,6,5 };
127         interlaces[SMDSEntity_Hexa] = ids;
128         cgTypes   [SMDSEntity_Hexa] = CGNS_ENUMV( HEXA_8 );
129       }
130       {
131         static int ids[] = { 0,3,2,1,4,7,6,5,11,10,9,8,12,15,14,13,19,18,17,16 };
132         interlaces[SMDSEntity_Quad_Hexa] = ids;
133         cgTypes   [SMDSEntity_Quad_Hexa] = CGNS_ENUMV( HEXA_20 );
134       }
135       {
136         cgTypes[SMDSEntity_Polygon]   = CGNS_ENUMV( NGON_n );
137         cgTypes[SMDSEntity_Polyhedra] = CGNS_ENUMV( NFACE_n );
138       }
139     }
140     cgType  = cgTypes[ smType ];
141     return interlaces[ smType ];
142   }
143
144   //================================================================================
145   /*!
146    * \brief Cut off type of boundary condition from the group name
147    */
148   //================================================================================
149
150   CGNS_ENUMT( BCType_t ) getBCType( string& groupName )
151   {
152     CGNS_ENUMT( BCType_t ) bcType = CGNS_ENUMV( BCGeneral ); // default type
153
154     // boundary condition type starts from "BC"
155     size_t bcBeg = groupName.find("BC");
156     if ( bcBeg != string::npos )
157     {
158       for ( int t = 0; t < NofValidBCTypes; ++t )
159       {
160         CGNS_ENUMT( BCType_t ) type = CGNS_ENUMT( BCType_t)( t );
161         string typeName = cg_BCTypeName( type );
162         if ( typeName == &groupName[0] + bcBeg )
163         {
164           bcType = type;
165           while ( bcBeg > 0 && isspace( bcBeg-1 ))
166             --bcBeg;
167           if ( bcBeg == 0 )
168             groupName = "Group";
169           else
170             groupName = groupName.substr( 0, bcBeg-1 );
171         }
172       }
173     }
174     return bcType;
175   }
176
177   //================================================================================
178   /*!
179    * \brief Sortable face of a polyhedron
180    */
181   struct TPolyhedFace
182   {
183     int _id; // id of NGON_n
184     vector< int > _nodes; // lowest node IDs used for sorting
185
186     TPolyhedFace( const SMDS_MeshNode** nodes, const int nbNodes, int ID):_id(ID)
187     {
188       set< int > ids;
189       for ( int i = 0; i < nbNodes; ++i )
190         ids.insert( nodes[i]->GetID() );
191
192       _nodes.resize( 3 ); // std::min( nbNodes, 4 )); hope 3 nodes is enough
193       set< int >::iterator idIt = ids.begin();
194       for ( size_t j = 0; j < _nodes.size(); ++j, ++idIt )
195         _nodes[j] = *idIt;
196     }
197     bool operator< (const TPolyhedFace& o ) const
198     {
199       return _nodes < o._nodes;
200     }
201   };
202   //================================================================================
203   /*!
204    * \brief Return CGNS id of an element
205    */
206   //================================================================================
207
208   cgsize_t cgnsID( const SMDS_MeshElement*                         elem,
209                    const map< const SMDS_MeshElement*, cgsize_t >& elem2cgID )
210   {
211     map< const SMDS_MeshElement*, cgsize_t >::const_iterator e2id = elem2cgID.find( elem );
212     return ( e2id == elem2cgID.end() ? elem->GetID() : e2id->second );
213   }
214
215 } // namespace
216
217 //================================================================================
218 /*!
219  * \brief Write the mesh into the CGNS file
220  */
221 //================================================================================
222
223 Driver_Mesh::Status DriverCGNS_Write::Perform()
224 {
225   myErrorMessages.clear();
226
227   if ( !myMesh || myMesh->GetMeshInfo().NbElements() < 1 )
228     return addMessage( !myMesh ? "NULL mesh" : "Empty mesh (no elements)", /*fatal = */true );
229
230   // open the file
231   if ( cg_open(myFile.c_str(), CG_MODE_MODIFY, &_fn) != CG_OK &&
232        cg_open(myFile.c_str(), CG_MODE_WRITE,  &_fn) != CG_OK )
233     return addMessage( cg_get_error(), /*fatal = */true );
234
235   // create a Base
236   // --------------
237
238   const int spaceDim = 3;
239   int meshDim = 1;
240   if ( myMesh->NbFaces() > 0 ) meshDim = 2;
241   if ( myMesh->NbVolumes() > 0 ) meshDim = 3;
242
243   if ( myMeshName.empty() )
244   {
245     int nbases = 0;
246     if ( cg_nbases( _fn, &nbases) == CG_OK)
247       myMeshName = ( SMESH_Comment("Base_") << nbases+1 );
248     else
249       myMeshName = "Base_0";
250   }
251   int iBase;
252   if ( cg_base_write( _fn, myMeshName.c_str(), meshDim, spaceDim, &iBase))
253     return addMessage( cg_get_error(), /*fatal = */true );
254
255   // create a Zone
256   // --------------
257
258   int nbCells = myMesh->NbEdges();
259   if ( meshDim == 3 )
260     nbCells = myMesh->NbVolumes();
261   else if ( meshDim == 2 )
262     nbCells = myMesh->NbFaces();
263
264   cgsize_t size[9] = { myMesh->NbNodes(), nbCells, /*NBoundVertex=*/0, 0,0,0,0,0,0 };
265   int iZone;
266   if ( cg_zone_write( _fn, iBase, "SMESH_Mesh", size,
267                       CGNS_ENUMV( Unstructured ), &iZone) != CG_OK )
268     return addMessage( cg_get_error(), /*fatal = */true );
269
270   // Map to store only elements whose an SMDS ID differs from a CGNS one
271   typedef map< const SMDS_MeshElement*, cgsize_t > TElem2cgIDMap;
272   vector< TElem2cgIDMap > elem2cgIDByEntity( SMDSEntity_Last );
273   TElem2cgIDMap::iterator elem2cgIDIter;
274
275   TElem2cgIDMap & n2cgID = elem2cgIDByEntity[ SMDSEntity_Node ];
276
277   // Write nodes
278   // ------------
279   {
280     vector< double > coords( myMesh->NbNodes() );
281     int iC;
282     // X
283     SMDS_NodeIteratorPtr nIt = myMesh->nodesIterator( /*idInceasingOrder=*/true );
284     for ( int i = 0; nIt->more(); ++i ) coords[i] = nIt->next()->X();
285     if ( cg_coord_write( _fn, iBase, iZone, CGNS_ENUMV(RealDouble),
286                           "CoordinateX", &coords[0], &iC) != CG_OK )
287       return addMessage( cg_get_error(), /*fatal = */true );
288     // Y
289     nIt = myMesh->nodesIterator( /*idInceasingOrder=*/true );
290     for ( int i = 0; nIt->more(); ++i ) coords[i] = nIt->next()->Y();
291     if ( cg_coord_write( _fn, iBase, iZone, CGNS_ENUMV(RealDouble),
292                           "CoordinateY", &coords[0], &iC) != CG_OK )
293       return addMessage( cg_get_error(), /*fatal = */true );
294     // Z
295     nIt = myMesh->nodesIterator( /*idInceasingOrder=*/true );
296     for ( int i = 0; nIt->more(); ++i ) coords[i] = nIt->next()->Z();
297     if ( cg_coord_write( _fn, iBase, iZone, CGNS_ENUMV(RealDouble),
298                           "CoordinateZ", &coords[0], &iC) != CG_OK )
299       return addMessage( cg_get_error(), /*fatal = */true );
300
301     // store CGNS ids of nodes
302     nIt = myMesh->nodesIterator( /*idInceasingOrder=*/true );
303     for ( int i = 0; nIt->more(); ++i )
304     {
305       const SMDS_MeshElement* n = nIt->next();
306       if ( n->GetID() != i+1 )
307         n2cgID.insert( n2cgID.end(), make_pair( n, i+1 ));
308     }
309   }
310   // Write elements
311   // ---------------
312   
313   cgsize_t cgID = 1, startID;
314
315   // write into a section all successive elements of one geom type
316   int iSec;
317   vector< cgsize_t > elemData;
318   SMDS_ElemIteratorPtr elemIt = myMesh->elementsIterator();
319   const SMDS_MeshElement* elem = elemIt->next();
320   while ( elem )
321   {
322     const SMDSAbs_EntityType elemType = elem->GetEntityType();
323     CGNS_ENUMT( ElementType_t ) cgType;
324     const int* interlace = getInterlaceAndType( elemType, cgType );
325
326     TElem2cgIDMap & elem2cgID = elem2cgIDByEntity[ elemType ];
327
328     elemData.clear();
329     startID = cgID;
330
331     if ( interlace ) // STANDARD elements
332       do
333       {
334         for ( int i = 0, nb = elem->NbNodes(); i < nb; ++i )
335           elemData.push_back( cgnsID( elem->GetNode( interlace[i] ), n2cgID ));
336         if ( elem->GetID() != cgID )
337           elem2cgID.insert( elem2cgID.end(), make_pair( elem, cgID ));
338         ++cgID;
339         elem = elemIt->more() ? elemIt->next() : 0;
340       }
341       while ( elem && elem->GetEntityType() == elemType );
342
343     else if ( elemType == SMDSEntity_Polygon ) // POLYGONS
344       do
345       {
346         elemData.push_back( elem->NbNodes() );
347         for ( int i = 0, nb = elem->NbNodes(); i < nb; ++i )
348           elemData.push_back( cgnsID( elem->GetNode(i), n2cgID ));
349         if ( elem->GetID() != cgID )
350           elem2cgID.insert( elem2cgID.end(), make_pair( elem, cgID ));
351         ++cgID;
352         elem = elemIt->more() ? elemIt->next() : 0;
353       }
354       while ( elem && elem->GetEntityType() == elemType );
355
356     else if ( elemType == SMDSEntity_Polyhedra ) // POLYHEDRA
357     {
358       // to save polyhedrons after all
359       const SMDS_MeshInfo& meshInfo = myMesh->GetMeshInfo();
360       if ( meshInfo.NbPolyhedrons() == meshInfo.NbElements() - cgID + 1 )
361         break; // only polyhedrons remain
362       while ( elem && elem->GetEntityType() == elemType )
363         elem = elemIt->more() ? elemIt->next() : 0;
364       continue;
365     }
366
367     SMESH_Comment sectionName( cg_ElementTypeName( cgType ));
368     sectionName << " " << startID << " - " << cgID-1;
369
370     if ( cg_section_write(_fn, iBase, iZone, sectionName.c_str(), cgType, startID,
371                           cgID-1, /*nbndry=*/0, &elemData[0], &iSec) != CG_OK )
372       return addMessage( cg_get_error(), /*fatal = */true );
373   }
374   // Write polyhedral volumes
375   // -------------------------
376
377   if ( myMesh->GetMeshInfo().NbPolyhedrons() > 0 )
378   {
379     // the polyhedron (NFACE_n) is described as a set of signed face IDs,
380     // so first we are to write all polygones (NGON_n) bounding polyhedrons
381
382     vector< cgsize_t > faceData;
383     set< TPolyhedFace > faces;
384     set< TPolyhedFace >::iterator faceInSet;
385     vector<const SMDS_MeshNode *> faceNodesVec;
386     int nbPolygones = 0, faceID;
387
388     SMDS_VolumeTool vol;
389
390     elemData.clear();
391
392     int nbPolyhTreated = 0;
393
394     TElem2cgIDMap * elem2cgID = 0;
395     TElem2cgIDMap & n2cgID    = elem2cgIDByEntity[ SMDSEntity_Node ];
396
397     SMDS_ElemIteratorPtr elemIt = myMesh->elementsIterator();
398     while ( elemIt->more() )
399     {
400       elem = elemIt->next();
401       if ( elem->GetEntityType() == SMDSEntity_Polyhedra )
402       {
403         ++nbPolyhTreated;
404         vol.Set( elem );
405         vol.SetExternalNormal();
406         const int nbFaces = vol.NbFaces();
407         elemData.push_back( nbFaces );
408         for ( int iF = 0; iF < nbFaces; ++iF )
409         {
410           const int nbNodes = vol.NbFaceNodes( iF );
411           const SMDS_MeshNode** faceNodes = vol.GetFaceNodes( iF );
412           faceNodesVec.assign( faceNodes, faceNodes + nbNodes );
413           if (( elem = myMesh->FindElement( faceNodesVec, SMDSAbs_Face, /*noMedium=*/false)))
414           {
415             // a face of the polyhedron is present in the mesh
416             faceID = cgnsID( elem, elem2cgIDByEntity[ elem->GetEntityType() ]);
417           }
418           else if ( vol.IsFreeFace( iF ))
419           {
420             // the face is not shared by volumes
421             faceID = cgID++;
422             ++nbPolygones;
423             faceData.push_back( nbNodes );
424             for ( int i = 0; i < nbNodes; ++i )
425               faceData.push_back( cgnsID( faceNodes[i], n2cgID ));
426           }
427           else
428           {
429             TPolyhedFace face( faceNodes, nbNodes, cgID );
430             faceInSet = faces.insert( faces.end(), face );
431             if ( faceInSet->_id == cgID ) // the face encounters for the 1st time
432             {
433               faceID = cgID++;
434               ++nbPolygones;
435               faceData.push_back( nbNodes );
436               for ( int i = 0; i < nbNodes; ++i )
437                 faceData.push_back( cgnsID( faceNodes[i], n2cgID ));
438             }
439             else
440             {
441               // the face encounters for the 2nd time; we hope it won't encounter once more,
442               // for that we can erase it from the set of faces
443               faceID = -faceInSet->_id;
444               faces.erase( faceInSet );
445             }
446           }
447           elemData.push_back( faceID );
448         }
449       }
450     }
451
452     if ( nbPolygones > 0 )
453     {
454       if ( cg_section_write(_fn, iBase, iZone, "Faces of Polyhedrons",
455                              CGNS_ENUMV( NGON_n ), cgID - nbPolygones, cgID-1,
456                              /*nbndry=*/0, &faceData[0], &iSec) != CG_OK )
457         return addMessage( cg_get_error(), /*fatal = */true );
458     }
459     
460     if ( cg_section_write(_fn, iBase, iZone, "Polyhedrons", 
461                              CGNS_ENUMV( NFACE_n ), cgID, cgID+nbPolyhTreated-1,
462                            /*nbndry=*/0, &elemData[0], &iSec) != CG_OK )
463       return addMessage( cg_get_error(), /*fatal = */true );
464
465     if ( !myMesh->GetGroups().empty() )
466     {
467       // store CGNS ids of polyhedrons
468       elem2cgID = &elem2cgIDByEntity[ SMDSEntity_Polyhedra ];
469       elemIt = myMesh->elementsIterator();
470       while ( elemIt->more() )
471       {
472         elem = elemIt->next();
473         if ( elem->GetEntityType() == SMDSEntity_Polyhedra )
474         {
475           if ( elem->GetID() != cgID )
476             elem2cgID->insert( elem2cgID->end(), make_pair( elem, cgID ));
477           ++cgID;
478         }
479       }
480     }
481   } // write polyhedral volumes
482
483
484   // Write groups as boundary conditions
485   // ------------------------------------
486
487   const set<SMESHDS_GroupBase*>& groups = myMesh->GetGroups();
488   set<SMESHDS_GroupBase*>::const_iterator grpIt = groups.begin();
489   set< string > groupNames; groupNames.insert(""); // to avoid duplicated and empty names
490   for ( ; grpIt != groups.end(); ++grpIt )
491   {
492     const SMESHDS_GroupBase* group = *grpIt;
493
494     // write BC location (default is Vertex)
495     CGNS_ENUMT( GridLocation_t ) location = CGNS_ENUMV( Vertex );
496     if ( group->GetType() != SMDSAbs_Node )
497     {
498       switch ( meshDim ) {
499       case 3:
500         switch ( group->GetType() ) {
501         case SMDSAbs_Volume: location = CGNS_ENUMV( FaceCenter ); break; // !!!
502         case SMDSAbs_Face:   location = CGNS_ENUMV( FaceCenter ); break; // OK
503         case SMDSAbs_Edge:   location = CGNS_ENUMV( EdgeCenter ); break; // OK
504         default:;
505         }
506         break;
507       case 2:
508         switch ( group->GetType() ) {
509         case SMDSAbs_Face: location = CGNS_ENUMV( FaceCenter ); break; // ???
510         case SMDSAbs_Edge: location = CGNS_ENUMV( EdgeCenter ); break; // OK
511         default:;
512         }
513         break;
514       case 1:
515         location = CGNS_ENUMV( EdgeCenter ); break; // ???
516         break;
517       }
518     }
519
520     // try to extract type of boundary condition from the group name
521     string name = group->GetStoreName();
522     CGNS_ENUMT( BCType_t ) bcType = getBCType( name );
523     while ( !groupNames.insert( name ).second )
524       name = (SMESH_Comment( "Group_") << groupNames.size());
525
526     // write IDs of elements
527     vector< cgsize_t > pnts;
528     pnts.reserve( group->Extent() );
529     SMDS_ElemIteratorPtr elemIt = group->GetElements();
530     while ( elemIt->more() )
531     {
532       const SMDS_MeshElement* elem = elemIt->next();
533       pnts.push_back( cgnsID( elem, elem2cgIDByEntity[ elem->GetEntityType() ]));
534     }
535     int iBC;
536     if ( cg_boco_write( _fn, iBase, iZone, name.c_str(), bcType,
537                         CGNS_ENUMV( PointList ), pnts.size(), &pnts[0], &iBC) != CG_OK )
538       return addMessage( cg_get_error(), /*fatal = */true);
539
540     // write BC location
541     if ( location != CGNS_ENUMV( Vertex ))
542     {
543       if ( cg_boco_gridlocation_write( _fn, iBase, iZone, iBC, location) != CG_OK )
544         return addMessage( cg_get_error(), /*fatal = */false);
545     }
546   }
547   return DRS_OK;
548 }
549
550 //================================================================================
551 /*!
552  * \brief Constructor
553  */
554 //================================================================================
555
556 DriverCGNS_Write::DriverCGNS_Write(): _fn(0)
557 {
558 }
559
560 //================================================================================
561 /*!
562  * \brief Close the cgns file at destruction
563  */
564 //================================================================================
565
566 DriverCGNS_Write::~DriverCGNS_Write()
567 {
568   if ( _fn > 0 )
569     cg_close( _fn );
570 }