diff --git a/Geo/GModelIO_CGNS.cpp b/Geo/GModelIO_CGNS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f817d024fe98446c6f8f9ed6a8e6c344e8c4c41 --- /dev/null +++ b/Geo/GModelIO_CGNS.cpp @@ -0,0 +1,561 @@ +#if defined(HAVE_LIBCGNS) + +// Copyright (C) 2006 S. Guzik, C. Geuzaine, J.-F. Remacle +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// +// Please report all bugs and problems to <gmsh@geuz.org>. + +#include <iostream> // DBG +#include <cstring> +#include <list> +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "GModel.h" +#include "Message.h" +#include "MElement.h" +#include "MNeighbour.h" +#include "MVertex.h" + +#include "cgnslib.h" + +int cgnsErr(const int cgIndexFile = -1) +{ + Msg(GERROR, cg_get_error()); + if(cgIndexFile != -1) + if(cg_close(cgIndexFile)) Msg(GERROR, "Even unable to close CGNS file"); + return 0; +} + + +/*============================================================================== + * File scope structures used for CGNS I/O + *============================================================================*/ + +//--Structure describing the zones sharing a vertex or element + +struct ZoneConn_t { + unsigned int indexZone; // Index of the zone + unsigned int indexConn; // Index of the vertex or element used + // to describe the connectivity + ZoneConn_t(const unsigned int iz, const unsigned int ic) : + indexZone(iz), indexConn(ic) { } +}; + +//--Class to make C style CGNS name strings act like C++ types + +class CgnsNameStr { + private: + char name[33]; + public: + // Constructors + CgnsNameStr() { name[0] = '\0'; } + CgnsNameStr(const char *const cstr) { + std::strncpy(name, cstr, 32); + name[32] = '\0'; + } + CgnsNameStr(const CgnsNameStr& cgs) { std::strcpy(name, cgs.name); } + // Assignment + CgnsNameStr& operator=(const CgnsNameStr& cgs) { + if ( &cgs != this ) { + std::strcpy(name, cgs.name); + } + return *this; + } + CgnsNameStr& operator=(const char *const cstr) { + std::strncpy(name, cstr, 32); + name[32] = '\0'; + return *this; + } + // Return the C string + char *c_str() { return name; } + const char *c_str() const { return name; } +}; + + +/*============================================================================== + * + * Routine readCGNS + * + * Purpose + * ======= + * + * + * + * I/O + * === + * + * name - (I) file name + * + *============================================================================*/ + +int GModel::readCGNS(const std::string &name) +{ + return 1; +} + + +/*============================================================================== + * + * Routine writeCGNS + * + * Purpose + * ======= + * + * Writes out mesh in CGNS format + * + * I/O + * === + * + * name - (I) file name + * scalingFactor - (I) scaling for coordinates + * + *============================================================================*/ + +int GModel::writeCGNS(const std::string &name, double scalingFactor) +{ + +//----- Type definitions -----// + + typedef std::map<int, std::vector<GEntity*> >::iterator zone_iterator; + typedef std::vector<MTriangle*>::const_iterator const_triangle_iterator; + typedef std::vector<MQuadrangle*>::const_iterator const_quadrangle_iterator; + typedef std::list<MVertex*> BoundaryVertList; + typedef std::set<MVertex*> InteriorVertSet; + typedef std::list<MElement*> BoundaryElemList; + typedef std::set<MElement*> InteriorElemSet; + +//----- Parameters -----// + + enum { + vertex, + edge, + face, + region + }; + + int meshDim = 0; // Dimension of the mesh + std::map<int, std::vector<GEntity*> > groups[4]; + // vector of entities that belong to + // each physical group (in each + // dimension) + std::multimap<MVertex*, ZoneConn_t> sharedVertex; + // map of shared vertices describing the + // zones that share it and the index of + // the vertex in each of the zones. +// std::multimap<MEdge, ZoneConn_t> sharedEdge; +// std::multimap<MFace, ZoneConn_t> sharedFace; + MNeighbour meshNeighbours; // Database of neighbours (computed for + // each zone) + +//--Get a list of groups in each dimension and each of the entities in that +//--group. The groups will define the zones. If no 2D or 3D groups exist, just +//--store all mesh elements in one zone. + + getPhysicalGroups(groups); + if(groups[face].size() + groups[region].size() == 0) { + // If there are no 2D or 3D physical groups, save all elements in one zone. + // Pretend that there is one physical group ecompassing all the elements. + for(riter it = firstRegion(); it != lastRegion(); ++it) + if((*it)->tetrahedra.size() + (*it)->hexahedra.size() + + (*it)->prisms.size() + (*it)->pyramids.size() > 0) + groups[region][1].push_back(*it); + if(!groups[region].size()) { + // No 3D elements were found. Look for 2D elements. + for(fiter it = firstFace(); it != lastFace(); ++it) + if((*it)->triangles.size() + (*it)->quadrangles.size() > 0) + groups[face][1].push_back(*it); + if(!groups[face].size()) { + Msg(GERROR, "No mesh elements were found"); + return 0; + } + } + } + +//--Open the file and get ready to write the zones + + // Will this be a 2D or 3D mesh? + if(groups[region].size()) meshDim = 3; + else meshDim = 2; + + // open the file + int cgIndexFile; + if(cg_open(name.c_str(), MODE_WRITE, &cgIndexFile)) return cgnsErr(); + + // write the base node + int cgIndexBase; + if(cg_base_write(cgIndexFile, "Base_0000", 2, 2, &cgIndexBase)) + return cgnsErr(); + + // write information about who generated the mesh + if(cg_goto(cgIndexFile, cgIndexBase, "end")) return(cgnsErr()); + if(cg_descriptor_write("About", "Created by Gmsh")) return cgnsErr(); + + unsigned int indexZone = 0; // Index of a zone + const unsigned int nZone = groups[face].size() + groups[region].size(); + // Total number of zones + std::vector<CgnsNameStr> zoneName(nZone); + // zone names + std::vector<BoundaryVertList> zoneBoundaryVert(nZone); + +/*--------------------------------------------------------------------* + * Write the 3D zone information + *--------------------------------------------------------------------*/ + +// TODO: add + +/*--------------------------------------------------------------------* + * Write the 2D zone information + *--------------------------------------------------------------------*/ + + for(zone_iterator itZone = groups[face].begin(); itZone != groups[face].end(); + ++itZone) { + const int nFace = itZone->second.size(); + std::cout << "Working on zone " << indexZone+1 << " " << itZone->first + << std::endl; + std::sprintf(zoneName[indexZone].c_str(), "Zone_%d\0", indexZone+1); + +//--Compute the mesh neighbours for this group + + meshNeighbours.add_elements_in_entities<GFace>(itZone->second.begin(), + itZone->second.end()); + +//--Load interior lists with all vertices and all elements + + unsigned int numTri3 = 0; + unsigned int numTri6 = 0; + unsigned int numQuad4 = 0; + unsigned int numQuad8 = 0; + unsigned int numQuad9 = 0; + + InteriorVertSet interiorVert; + BoundaryElemList boundaryElem; + InteriorElemSet interiorElem; + + for(unsigned int iFace = 0; iFace != nFace; ++iFace) { + GFace* face = static_cast<GFace*>(itZone->second[iFace]); + const std::vector<MTriangle*>::const_iterator itEndTri = + face->triangles.end(); + for(std::vector<MTriangle*>::const_iterator itTri = + face->triangles.begin(); itTri != itEndTri ; ++itTri) { + interiorElem.insert(*itTri); + interiorVert.insert((*itTri)->getVertex(0)); + interiorVert.insert((*itTri)->getVertex(1)); + interiorVert.insert((*itTri)->getVertex(2)); + switch ( (*itTri)->getTypeForMSH() ) { + case MSH_TRI_3: + ++numTri3; + break; + case MSH_TRI_6: + ++numTri6; + interiorVert.insert((*itTri)->getVertex(3)); + interiorVert.insert((*itTri)->getVertex(4)); + interiorVert.insert((*itTri)->getVertex(5)); + break; + } + } + const std::vector<MQuadrangle*>::const_iterator itEndQuad = + face->quadrangles.end(); + for(std::vector<MQuadrangle*>::const_iterator itQuad = + face->quadrangles.begin(); itQuad != itEndQuad ; ++itQuad) { + interiorElem.insert(*itQuad); + interiorVert.insert((*itQuad)->getVertex(0)); + interiorVert.insert((*itQuad)->getVertex(1)); + interiorVert.insert((*itQuad)->getVertex(2)); + interiorVert.insert((*itQuad)->getVertex(3)); + switch ( (*itQuad)->getTypeForMSH() ) { + case MSH_QUA_4: + ++numQuad4; + break; + case MSH_QUA_8: + ++numQuad8; + interiorVert.insert((*itQuad)->getVertex(4)); + interiorVert.insert((*itQuad)->getVertex(5)); + interiorVert.insert((*itQuad)->getVertex(6)); + interiorVert.insert((*itQuad)->getVertex(7)); + break; + case MSH_QUA_9: + ++numQuad9; + interiorVert.insert((*itQuad)->getVertex(4)); + interiorVert.insert((*itQuad)->getVertex(5)); + interiorVert.insert((*itQuad)->getVertex(6)); + interiorVert.insert((*itQuad)->getVertex(7)); + interiorVert.insert((*itQuad)->getVertex(8)); + break; + } + } + } + +//--Check edges for zone boundaries and move vertices and elements from interior +//--sets to boundary lists + + const MNeighbour::Edge_const_iterator itEndEdge = + meshNeighbours.edge_end(); + for(MNeighbour::Edge_const_iterator itEdge = meshNeighbours.edge_begin(); + itEdge != itEndEdge; ++itEdge) { + if(itEdge.num_neighbours() == 1) { // Boundary edge + // Find the boundary element + MElement *element; + meshNeighbours.edge_neighbours(itEdge, &element); + // Move primary vertices from interior to boundary + InteriorVertSet::iterator itVert = + interiorVert.find(itEdge->getVertex(0)); + if(itVert != interiorVert.end()) { + zoneBoundaryVert[indexZone].push_back(*itVert); + interiorVert.erase(itVert); + } + itVert = interiorVert.find(itEdge->getVertex(1)); + if(itVert != interiorVert.end()) { + zoneBoundaryVert[indexZone].push_back(*itVert); + interiorVert.erase(itVert); + } + // If this is a higher-order element, find the edge on the boundary + // and then the mid-edge vertex + if(element->getNumVertices() != element->getNumPrimaryVertices()) { + // Second-order element + int iEPE = 0; + while(element->getEdge(iEPE) != *itEdge) ++iEPE; + // Add mid-vertex on edge iEPE + itVert = interiorVert.find + (element->getVertex(iEPE + element->getNumPrimaryVertices())); + if(itVert != interiorVert.end()) { + zoneBoundaryVert[indexZone].push_back(*itVert); + interiorVert.erase(itVert); + } + } + } + } + +//--Loop through all the boundary vertices + + int numVert = 1; + BoundaryVertList::const_iterator itEndBVert = + zoneBoundaryVert[indexZone].end(); + { + MElement **boElem = new MElement*[meshNeighbours.max_vertex_neighbours()]; + for(BoundaryVertList::iterator itVert = + zoneBoundaryVert[indexZone].begin(); itVert != itEndBVert; + ++itVert) { + // Any elements with a vertex in the boundary vertex list should be + // moved to the boundary element list. + const int nElem = meshNeighbours.vertex_neighbours(*itVert, boElem); + for(int iElem = 0; iElem != nElem; ++iElem) { + InteriorElemSet::iterator itElem = interiorElem.find(boElem[iElem]); + if(itElem != interiorElem.end()) { + boundaryElem.push_back(boElem[iElem]); + interiorElem.erase(itElem); + } + } + // Renumber the boundary vertices + (*itVert)->setNum(numVert); + // Add all boundary vertices to the map of shared vertices + sharedVertex.insert(std::make_pair(*itVert, ZoneConn_t(indexZone, + numVert))); + ++numVert; + } + delete[] boElem; + } + +//--Renumber the interior mesh vertices + + InteriorVertSet::const_iterator itEndIVert = interiorVert.end(); + for(InteriorVertSet::iterator itVert = interiorVert.begin(); + itVert != itEndIVert; ++itVert) (*itVert)->setNum(numVert++); + +//--Write the zone node + + int cgIndexZone; + int cgZoneSize[3]; + cgZoneSize[0] = zoneBoundaryVert[indexZone].size() + interiorVert.size(); + cgZoneSize[1] = boundaryElem.size() + interiorElem.size(); + cgZoneSize[2] = zoneBoundaryVert[indexZone].size(); + if(cg_zone_write(cgIndexFile, cgIndexBase, zoneName[indexZone].c_str(), + cgZoneSize, Unstructured, &cgIndexZone)) return cgnsErr(); + +//--Write the grid node + + int cgIndexGrid; + if(cg_grid_write(cgIndexFile, cgIndexBase, cgIndexZone, "GridCoordinates", + &cgIndexGrid)) return cgnsErr(); + +//--Write the grid coordinates + + { + std::vector<double> coordx(cgZoneSize[0]); + std::vector<double> coordy(cgZoneSize[1]); + int iVert = 0; + for(BoundaryVertList::const_iterator itVert = + zoneBoundaryVert[indexZone].begin(); itVert != itEndBVert; + ++itVert) { + coordx[iVert] = (*itVert)->x()*scalingFactor; + coordy[iVert] = (*itVert)->y()*scalingFactor; + ++iVert; + } + for(InteriorVertSet::const_iterator itVert = interiorVert.begin(); + itVert != itEndIVert; ++itVert) { + coordx[iVert] = (*itVert)->x()*scalingFactor; + coordy[iVert] = (*itVert)->y()*scalingFactor; + ++iVert; + } + int cgIndexCoord; + if(cg_coord_write(cgIndexFile, cgIndexBase, cgIndexZone, RealDouble, + "CoordinateX", &coordx[0], &cgIndexCoord)) + return cgnsErr(); + if(cg_coord_write(cgIndexFile, cgIndexBase, cgIndexZone, RealDouble, + "CoordinateY", &coordy[0], &cgIndexCoord)) + return cgnsErr(); + } + +//--Write out the element connectivity + + { + int mixedElem = -1; + int numVPE; + int numElemData = 0; + ElementType_t cgElemType; + if(numTri3) { + ++mixedElem; + numVPE = 3; + numElemData += numTri3*3; + cgElemType = TRI_3; + } + if(numTri6) { + ++mixedElem; + numVPE = 6; + numElemData += numTri6*6; + cgElemType = TRI_6; + } + if(numQuad4) { + ++mixedElem; + numVPE = 4; + numElemData += numQuad4*4; + cgElemType = QUAD_4; + } + if(numQuad8) { + ++mixedElem; + numVPE = 8; + numElemData += numQuad8*8; + cgElemType = QUAD_8; + } + if(numQuad9) { + ++mixedElem; + numVPE = 9; + numElemData += numQuad9*9; + cgElemType = QUAD_9; + } + if(mixedElem) { + numVPE = 0; + numElemData += cgZoneSize[1]; + cgElemType = MIXED; + } + + std::vector<int> elementConn(numElemData); + int iElemData = 0; + + // Load boundary elements + BoundaryElemList::const_iterator itEndBElem = boundaryElem.end(); + if(mixedElem) { + for(BoundaryElemList::const_iterator itElem = boundaryElem.begin(); + itElem != itEndBElem; ++itElem) { + switch((*itElem)->getTypeForMSH()) { + case MSH_TRI_3: + elementConn[iElemData++] = TRI_3; + break; + case MSH_TRI_6: + elementConn[iElemData++] = TRI_6; + break; + case MSH_QUA_4: + elementConn[iElemData++] = QUAD_4; + break; + case MSH_QUA_8: + elementConn[iElemData++] = QUAD_8; + break; + case MSH_QUA_9: + elementConn[iElemData++] = QUAD_9; + break; + } + const int nVPE = (*itElem)->getNumVertices(); + for(int iVPE = 0; iVPE != nVPE; ++iVPE) + elementConn[iElemData++] = (*itElem)->getVertex(iVPE)->getNum(); + } + } + else { + const int nVPE = numVPE; + for(BoundaryElemList::const_iterator itElem = boundaryElem.begin(); + itElem != itEndBElem; ++itElem) + for(int iVPE = 0; iVPE != nVPE; ++iVPE) + elementConn[iElemData++] = (*itElem)->getVertex(iVPE)->getNum(); + } + + // Load interior elements + InteriorElemSet::const_iterator itEndIElem = interiorElem.end(); + if(mixedElem) { + for(InteriorElemSet::const_iterator itElem = interiorElem.begin(); + itElem != itEndIElem; ++itElem) { + switch((*itElem)->getTypeForMSH()) { + case MSH_TRI_3: + elementConn[iElemData++] = TRI_3; + break; + case MSH_TRI_6: + elementConn[iElemData++] = TRI_6; + break; + case MSH_QUA_4: + elementConn[iElemData++] = QUAD_4; + break; + case MSH_QUA_8: + elementConn[iElemData++] = QUAD_8; + break; + case MSH_QUA_9: + elementConn[iElemData++] = QUAD_9; + break; + } + const int nVPE = (*itElem)->getNumVertices(); + for(int iVPE = 0; iVPE != nVPE; ++iVPE) + elementConn[iElemData++] = (*itElem)->getVertex(iVPE)->getNum(); + } + } + else { + const int nVPE = numVPE; + for(InteriorElemSet::const_iterator itElem = interiorElem.begin(); + itElem != itEndIElem; ++itElem) + for(int iVPE = 0; iVPE != nVPE; ++iVPE) + elementConn[iElemData++] = (*itElem)->getVertex(iVPE)->getNum(); + } + + // Write to CGNS file + int cgIndexSection; + if(cg_section_write(cgIndexFile, cgIndexBase, cgIndexZone, "Section_Main", + cgElemType, 1, cgZoneSize[1], boundaryElem.size(), + &elementConn[0], &cgIndexSection)) return cgnsErr(); + } + + meshNeighbours.clear(); + ++indexZone; + + } + +//--Close the file + + if(cg_close(cgIndexFile)) return cgnsErr(); + + std::cout << "Done\n"; + return 1; +} + +#endif