1 // Gmsh - Copyright (C) 1997-2021 C. Geuzaine, J.-F. Remacle
2 //
3 // See the LICENSE.txt file in the Gmsh root directory for license information.
4 // Please report all issues on https://gitlab.onelab.info/gmsh/gmsh/issues.
5 //
6 // Contributor(s):
7 //   Thomas Toulorge
8 
9 #include "Context.h"
10 #include "GmshMessage.h"
11 #include "MVertex.h"
12 #include "affineTransformation.h"
13 #include "CGNSCommon.h"
14 #include "CGNSConventions.h"
15 #include "CGNSRead.h"
16 #include "CGNSZone.h"
17 
18 #if defined(HAVE_LIBCGNS)
19 
CGNSZone(int fileIndex,int baseIndex,int zoneIndex,CGNS_ENUMT (ZoneType_t)type,int meshDim,cgsize_t startNode,const Family2EltNodeTransfo & allEltNodeTransfo,int & err)20 CGNSZone::CGNSZone(int fileIndex, int baseIndex, int zoneIndex,
21                    CGNS_ENUMT(ZoneType_t) type, int meshDim, cgsize_t startNode,
22                    const Family2EltNodeTransfo &allEltNodeTransfo, int &err)
23   : fileIndex_(fileIndex), baseIndex_(baseIndex), meshDim_(meshDim),
24     zoneIndex_(zoneIndex), type_(type), startNode_(startNode),
25     eltNodeTransfo_(nullptr), nbPerConnect_(0)
26 {
27   int cgnsErr;
28 
29   // read zone name & size
30   char name[CGNS_MAX_STR_LEN];
31   cgnsErr = cg_zone_read(fileIndex, baseIndex, zoneIndex, name, size_);
32   if(cgnsErr != CG_OK) err = cgnsError(__FILE__, __LINE__, fileIndex);
33   name_ = std::string(name);
34 
35   // read family name and retrieve element node tranformations (CPEX0045)
36   cgnsErr = cg_goto(fileIndex, baseIndex, "Zone_t", zoneIndex, "end");
37   if(cgnsErr != CG_OK) err = cgnsError(__FILE__, __LINE__, fileIndex);
38   char famName[CGNS_MAX_STR_LEN];
39   cgnsErr = cg_famname_read(famName);
40   if(cgnsErr != CG_NODE_NOT_FOUND) {
41     if(cgnsErr == CG_OK) {
42       auto it = allEltNodeTransfo.find(std::string(famName));
43       if(it != allEltNodeTransfo.end()) eltNodeTransfo_ = &(it->second);
44     }
45     else
46       err = cgnsError(__FILE__, __LINE__, fileIndex);
47   }
48 
49   err = 1;
50 }
51 
52 // read a boundary condition in a zone
readBoundaryCondition(int iZoneBC,const std::vector<CGNSZone * > & allZones,std::vector<std::string> & allGeomName)53 int CGNSZone::readBoundaryCondition(int iZoneBC,
54                                     const std::vector<CGNSZone *> &allZones,
55                                     std::vector<std::string> &allGeomName)
56 {
57   int cgnsErr;
58 
59   // read general information on boundary condition
60   char rawBCName[CGNS_MAX_STR_LEN];
61   CGNS_ENUMT(BCType_t) bcType;
62   CGNS_ENUMT(PointSetType_t) ptSetType;
63   cgsize_t nbVal, normalSize;
64   CGNS_ENUMT(DataType_t) normalType;
65   int nbDataSet;
66   int normalIndex;
67   cgnsErr = cg_boco_info(fileIndex(), baseIndex(), index(), iZoneBC, rawBCName,
68                          &bcType, &ptSetType, &nbVal, &normalIndex, &normalSize,
69                          &normalType, &nbDataSet);
70   if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
71 
72   // get BC name from family linked to BC, use original BC name if not present,
73   // then retrieve BC index
74   std::string geomName;
75   cgnsErr = cg_goto(fileIndex(), baseIndex(), "Zone_t", index(), "ZoneBC_t", 1,
76                     "BC_t", iZoneBC, "end");
77   if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
78   char rawFamilyName[CGNS_MAX_STR_LEN];
79   cgnsErr = cg_famname_read(rawFamilyName);
80   if(cgnsErr != CG_NODE_NOT_FOUND) {
81     if(cgnsErr == CG_OK)
82       geomName = std::string(rawFamilyName);
83     else
84       return cgnsError(__FILE__, __LINE__, fileIndex());
85   }
86   else
87     geomName = std::string(rawBCName);
88   const int indGeom = nameIndex(geomName, allGeomName);
89 
90   // read location of bnd. condition (type of mesh entity on which it applies)
91   CGNS_ENUMT(GridLocation_t) location;
92   cgnsErr = cg_boco_gridlocation_read(fileIndex(), baseIndex(), index(),
93                                       iZoneBC, &location);
94   if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
95 
96   // check that boundary condition is imposed on face elements
97   if((type() == CGNS_ENUMV(Unstructured)) && (meshDim() == 2) &&
98      (location != CGNS_ENUMV(CellCenter)) &&
99      (location != CGNS_ENUMV(EdgeCenter))) {
100     Msg::Warning("Boundary condition %s is specified on %s instead of "
101                  "CellCenter/EdgeCenter in a 2D zone, skipping",
102                  geomName.c_str(), cg_GridLocationName(location));
103     return 1;
104   }
105   else if((type() == CGNS_ENUMV(Unstructured)) && (meshDim() == 3) &&
106           (location != CGNS_ENUMV(CellCenter)) &&
107           (location != CGNS_ENUMV(FaceCenter))) {
108     Msg::Warning("Boundary condition %s is specified on %s instead of "
109                  "CellCenter/FaceCenter in a 3D zone, skipping",
110                  geomName.c_str(), cg_GridLocationName(location));
111     return 1;
112   }
113 
114   // read and store elements on which the BC is imposed
115   std::vector<cgsize_t> bcElt;
116   switch(ptSetType) {
117   case CGNS_ENUMV(ElementRange):
118   case CGNS_ENUMV(PointRange):
119     readBoundaryConditionRange(iZoneBC, bcElt);
120     break;
121   case CGNS_ENUMV(ElementList):
122   case CGNS_ENUMV(PointList):
123     readBoundaryConditionList(iZoneBC, nbVal, bcElt);
124     break;
125   default:
126     Msg::Error("Wrong point set type %s is for boundary condition %s",
127                cg_PointSetTypeName(ptSetType), geomName.c_str());
128     return 0;
129     break;
130   }
131   for(std::size_t iElt = 0; iElt < bcElt.size(); iElt++) {
132     elt2Geom()[bcElt[iElt]] = indGeom;
133   }
134 
135   return 1;
136 }
137 
readVertices(int dim,double scale,std::vector<CGNSZone * > & allZones,std::vector<MVertex * > & zoneVert)138 int CGNSZone::readVertices(int dim, double scale,
139                            std::vector<CGNSZone *> &allZones,
140                            std::vector<MVertex *> &zoneVert)
141 {
142   int cgnsErr;
143 
144   // read dimension of coordinates and check consistency with base node
145   int dimZone;
146   cgnsErr = cg_ncoords(fileIndex(), baseIndex(), index(), &dimZone);
147   if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
148   if(dimZone > dim) {
149     Msg::Warning("%i coordinates in CGNS zone %i, while base has dimension %i,"
150                  " discarding upper dimensions",
151                  dimZone, index(), dim);
152   }
153   else if(dimZone < dim) {
154     Msg::Error("%i coordinates in CGNS zone %i, while base has dimension %i",
155                dimZone, index(), dim);
156     return 0;
157   }
158 
159   // read vertex coordinates
160   std::vector<double> xyz[3];
161   for(int iXYZ = 0; iXYZ < dim; iXYZ++) {
162     char xyzName[CGNS_MAX_STR_LEN];
163     CGNS_ENUMT(DataType_t) dataType;
164     cgnsErr = cg_coord_info(fileIndex(), baseIndex(), index(), iXYZ + 1,
165                             &dataType, xyzName);
166     if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
167     const cgsize_t startInd[3] = {1, 1, 1};
168     xyz[iXYZ].resize(nbNode());
169     cgnsErr =
170       cg_coord_read(fileIndex(), baseIndex(), index(), xyzName,
171                     CGNS_ENUMV(RealDouble), startInd, size(), xyz[iXYZ].data());
172     if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
173   }
174 
175   // create vertices
176   zoneVert.reserve(nbNode());
177   for(int i = 0; i < nbNode(); i++) {
178     const double x = xyz[0][i] * scale;
179     const double y = (dim > 1) ? xyz[1][i] * scale : 0.;
180     const double z = (dim > 2) ? xyz[2][i] * scale : 0.;
181     zoneVert.push_back(new MVertex(x, y, z));
182   }
183 
184   return 1;
185 }
186 
readConnectivities(const std::map<std::string,int> & name2Zone,std::vector<CGNSZone * > & allZones)187 int CGNSZone::readConnectivities(const std::map<std::string, int> &name2Zone,
188                                  std::vector<CGNSZone *> &allZones)
189 {
190   int cgnsErr;
191 
192   // read number of connectivities
193   int nbConnect;
194   cgnsErr = cg_nconns(fileIndex(), baseIndex(), index(), &nbConnect);
195   if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
196 
197   // reserve memory for data containers
198   perTransfo_.reserve(nbConnect);
199   slaveNode_.reserve(nbConnect);
200   slaveVert_.reserve(nbConnect);
201   masterZone_.reserve(nbConnect);
202   masterNode_.reserve(nbConnect);
203   masterVert_.reserve(nbConnect);
204 
205   for(int iConnect = 1; iConnect <= nbConnect; iConnect++) {
206     // read connection info
207     char connectName[CGNS_MAX_STR_LEN], donorName[CGNS_MAX_STR_LEN];
208     CGNS_ENUMT(GridLocation_t) location;
209     CGNS_ENUMT(GridConnectivityType_t) connectType;
210     CGNS_ENUMT(PointSetType_t) ptSetType, ptSetTypeDonor;
211     cgsize_t connectSize, connectSizeDonor;
212     CGNS_ENUMT(ZoneType_t) zoneTypeDonor;
213     CGNS_ENUMT(DataType_t) dataTypeDonor;
214     cgnsErr = cg_conn_info(fileIndex(), baseIndex(), index(), iConnect,
215                            connectName, &location, &connectType, &ptSetType,
216                            &connectSize, donorName, &zoneTypeDonor,
217                            &ptSetTypeDonor, &dataTypeDonor, &connectSizeDonor);
218     if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
219 
220     // read periodic info, skip if not periodic
221     float rotCenter[3], rotAngle[3], trans[3];
222     cgnsErr = cg_conn_periodic_read(fileIndex(), baseIndex(), index(), iConnect,
223                                     rotCenter, rotAngle, trans);
224     if(cgnsErr == CG_NODE_NOT_FOUND) continue;
225     if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
226 
227     // invert transformation because CGNS transformation is from current zone
228     // to donor zone, while Gmsh transformation is from master to slave
229     for(int d = 0; d < 3; d++) {
230       rotAngle[d] = -rotAngle[d];
231       trans[d] = -trans[d];
232     }
233 
234     // check if connection type is OK
235     if(connectType != CGNS_ENUMV(Abutting1to1)) {
236       Msg::Warning("Non-conformal connection not supported in CGNS reader");
237       continue;
238     }
239     if(location != CGNS_ENUMV(Vertex)) {
240       Msg::Warning("Only vertex connections are supported in CGNS reader");
241       continue;
242     }
243 
244     // get and check data on master zone
245     const std::string masterName(donorName);
246     const auto itMasterName = name2Zone.find(masterName);
247     if(itMasterName == name2Zone.end()) {
248       Msg::Error("Zone name '%s' in not found in connection %i of zone %i",
249                  masterName.c_str(), iConnect, index());
250       return 0;
251     }
252     const int masterZoneIndex = itMasterName->second;
253     CGNSZone *mZone = allZones[masterZoneIndex];
254     if(mZone->type() != zoneTypeDonor) {
255       Msg::Error("Inconsistent type for zone '%s' in connection %i of zone %i",
256                  masterName.c_str(), iConnect, index());
257       return 0;
258     }
259 
260     // read connectivity data
261     std::vector<cgsize_t> slaveData(indexDataSize(connectSize));
262     std::vector<cgsize_t> masterData(mZone->indexDataSize(connectSizeDonor));
263     cgnsErr = cg_conn_read(fileIndex(), baseIndex(), index(), iConnect,
264                            slaveData.data(), dataTypeDonor, masterData.data());
265     if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
266 
267     // get slave and master nodes
268     std::vector<cgsize_t> sNode, mNode;
269     if(ptSetType == CGNS_ENUMV(PointRange))
270       nodeFromRange(slaveData, sNode);
271     else if(ptSetType == CGNS_ENUMV(PointList))
272       nodeFromList(slaveData, sNode);
273     if(ptSetTypeDonor != CGNS_ENUMV(PointListDonor)) {
274       Msg::Error("Only PointListDonor sets are supported for donnor points for "
275                  "general connections in CGNS reader");
276       return 0;
277     }
278     mZone->nodeFromList(masterData, mNode);
279 
280     // add periodic connection
281     nbPerConnect_++;
282     perTransfo_.push_back(std::vector<double>());
283     computeAffineTransformation(rotCenter, rotAngle, trans, perTransfo_.back());
284     masterZone_.push_back(masterZoneIndex);
285     slaveNode_.push_back(sNode);
286     slaveVert_.push_back(std::vector<MVertex *>());
287     masterNode_.push_back(mNode);
288     masterVert_.push_back(std::vector<MVertex *>());
289   }
290 
291   return 1;
292 }
293 
readMesh(int dim,double scale,std::vector<CGNSZone * > & allZones,std::vector<MVertex * > & allVert,std::map<int,std::vector<MElement * >> * allElt,std::vector<MVertex * > & zoneVert,std::vector<MElement * > & zoneElt,std::vector<std::string> & allGeomName)294 int CGNSZone::readMesh(int dim, double scale, std::vector<CGNSZone *> &allZones,
295                        std::vector<MVertex *> &allVert,
296                        std::map<int, std::vector<MElement *> > *allElt,
297                        std::vector<MVertex *> &zoneVert,
298                        std::vector<MElement *> &zoneElt,
299                        std::vector<std::string> &allGeomName)
300 {
301   // read boundary conditions for classification of mesh on geometry
302   if(CTX::instance()->mesh.cgnsImportIgnoreBC == 0) {
303     int cgnsErr;
304     int nbZoneBC;
305     cgnsErr = cg_nbocos(fileIndex(), baseIndex(), index(), &nbZoneBC);
306     if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
307     for(int iZoneBC = 1; iZoneBC <= nbZoneBC; iZoneBC++) {
308       int errBC = readBoundaryCondition(iZoneBC, allZones, allGeomName);
309       if(errBC == 0) return 0;
310     }
311   }
312 
313   // read and create vertices
314   int errVert = readVertices(dim, scale, allZones, zoneVert);
315   if(errVert == 0) return 0;
316   allVert.insert(allVert.end(), zoneVert.begin(), zoneVert.end());
317 
318   // read and create elements
319   int err = readElements(allVert, allElt, zoneElt, allGeomName);
320   if(err == 0) return 0;
321 
322   // cleanup unncessary memory
323   elt2Geom().clear();
324 
325   return 1;
326 }
327 
setPeriodicVertices(const std::vector<CGNSZone * > & allZones,const std::vector<MVertex * > & allVert)328 void CGNSZone::setPeriodicVertices(const std::vector<CGNSZone *> &allZones,
329                                    const std::vector<MVertex *> &allVert)
330 {
331   for(int iPer = 0; iPer < nbPerConnect(); iPer++) {
332     const std::vector<cgsize_t> &sNode = slaveNode(iPer);
333     const std::vector<cgsize_t> &mNode = masterNode(iPer);
334     std::vector<MVertex *> &sVert = slaveVert(iPer);
335     std::vector<MVertex *> &mVert = masterVert(iPer);
336     CGNSZone *mZone = allZones[masterZone(iPer)];
337     for(std::size_t iN = 0; iN < sNode.size(); iN++) {
338       const cgsize_t sInd = startNode() + sNode[iN];
339       const cgsize_t mInd = mZone->startNode() + mNode[iN];
340       sVert.push_back(allVert[sInd]);
341       mVert.push_back(allVert[mInd]);
342     }
343   }
344 }
345 
readBoundaryConditionRange(int iZoneBC,std::vector<cgsize_t> & bcElt)346 int CGNSZone::readBoundaryConditionRange(int iZoneBC,
347                                          std::vector<cgsize_t> &bcElt)
348 {
349   int cgnsErr;
350 
351   std::vector<cgsize_t> bcData(indexDataSize(2));
352   cgnsErr = cg_boco_read(fileIndex(), baseIndex(), index(), iZoneBC,
353                          bcData.data(), nullptr);
354   if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
355 
356   // get list of elements from range data
357   eltFromRange(bcData, bcElt);
358 
359   return 1;
360 }
361 
readBoundaryConditionList(int iZoneBC,cgsize_t nbVal,std::vector<cgsize_t> & bcElt)362 int CGNSZone::readBoundaryConditionList(int iZoneBC, cgsize_t nbVal,
363                                         std::vector<cgsize_t> &bcElt)
364 {
365   int cgnsErr;
366 
367   // read data
368   std::vector<cgsize_t> bcData(indexDataSize(nbVal));
369   cgnsErr = cg_boco_read(fileIndex(), baseIndex(), index(), iZoneBC,
370                          bcData.data(), nullptr);
371   if(cgnsErr != CG_OK) return cgnsError(__FILE__, __LINE__, fileIndex());
372 
373   // get list of elements from list data
374   eltFromList(bcData, bcElt);
375 
376   return 1;
377 }
378 
379 #endif // HAVE_LIBCGNS
380