Forked from
gmsh / gmsh
16337 commits behind the upstream repository.
-
Christophe Geuzaine authoredChristophe Geuzaine authored
GModelIO_CGNS.cpp 18.98 KiB
// Gmsh - Copyright (C) 1997-2008 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to <gmsh@geuz.org>.
// 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 <string.h>
#include <iostream> // DBG
#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"
#if defined(HAVE_LIBCGNS)
#include <cgnslib.h>
int cgnsErr(const int cgIndexFile = -1)
{
Msg::Error(cg_get_error());
if(cgIndexFile != -1)
if(cg_close(cgIndexFile)) Msg::Error("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::Error("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;
}
#else
int GModel::readCGNS(const std::string &name)
{
Msg::Error("This version of Gmsh was compiled without CGNS support");
return 0;
}
int GModel::writeCGNS(const std::string &name, double scalingFactor)
{
Msg::Error("This version of Gmsh was compiled without CGNS support");
return 0;
}
#endif