diff --git a/Geo/GModel.cpp b/Geo/GModel.cpp index 8d2ece39a7766075d83c62334ae25e25e4d1bf02..e53dbd37a4e61955c08d7ae4adfd2dce1b640e26 100644 --- a/Geo/GModel.cpp +++ b/Geo/GModel.cpp @@ -1237,10 +1237,10 @@ static bool getVertices(int num, int *indices, std::vector<MVertex*> &vec, return true; } -MElement *GModel::createElementMSH(int num, int typeMSH, int physical, - int reg, int part, std::vector<MVertex*> &v, - std::map<int, std::vector<MElement*> > elements[10], - std::map<int, std::map<int, std::string> > physicals[4]) +MElement *GModel::_createElementMSH(int num, int typeMSH, int physical, + int reg, int part, std::vector<MVertex*> &v, + std::map<int, std::vector<MElement*> > elements[10], + std::map<int, std::map<int, std::string> > physicals[4]) { if(CTX::instance()->mesh.switchElementTags) { int tmp = reg; @@ -1355,8 +1355,8 @@ GModel *GModel::createGModel(std::map<int, MVertex*> &vertexMap, } } - gm->createElementMSH(num, elementType[i], physical[i], elementary[i], - partition[i], vertices, elements, physicals); + gm->_createElementMSH(num, elementType[i], physical[i], elementary[i], + partition[i], vertices, elements, physicals); } // store the elements in their associated elementary entity. If the @@ -2361,6 +2361,7 @@ GEdge* GModel::addCompoundEdge(std::vector<GEdge*> edges, int num){ return gec; } + GFace* GModel::addCompoundFace(std::vector<GFace*> faces, int param, int split, int num) { #if defined(HAVE_SOLVER) @@ -2500,7 +2501,6 @@ GFace* GModel::addFace (std::vector<GEdge *> edges, return 0; } - GFace* GModel::addPlanarFace (std::vector<std::vector<GEdge *> > edges){ if(_factory) return _factory->addPlanarFace(this, edges); @@ -2602,8 +2602,9 @@ static void computeDuplicates(GModel *model, std::map<GVertex*, GVertex*> &Duplicates2Unique, const double &eps) { - // FIXME: currently we use a greedy algorithm in n^2 (using a - // kd-tree: cf. MVertexPositionSet) + // FIXME: currently we use a greedy algorithm in n^2 (using a kd-tree: + // cf. MVertexPositionSet) + // FIXME: add option to remove orphaned entities after duplicate check std::list<GVertex*> v; v.insert(v.begin(), model->firstVertex(), model->lastVertex()); diff --git a/Geo/GModel.h b/Geo/GModel.h index aee48a484631483e11c2f7130a35528a9d31b29e..ea9b32a5e904fc0641b933b2cdf41b763d69b39c 100644 --- a/Geo/GModel.h +++ b/Geo/GModel.h @@ -123,6 +123,13 @@ class GModel void _storePhysicalTagsInEntities(int dim, std::map<int, std::map<int, std::string> > &map); + // utility function to create a mesh element in the I/O routines (don't use in + // new code; it will be removed eventually) + MElement *_createElementMSH(int num, int typeMSH, int physical, + int reg, int part, std::vector<MVertex*> &v, + std::map<int, std::vector<MElement*> > elements[10], + std::map<int, std::map<int, std::string> > physicals[4]); + // entity that is currently being meshed (used for error reporting) GEntity *_currentMeshEntity; @@ -488,15 +495,9 @@ class GModel // if cutElem is set to false, split the model without cutting the elements GModel *buildCutGModel(gLevelset *ls, bool cutElem=true, bool saveTri=false); - // utility function to create a mesh element in the I/O routines - MElement *createElementMSH(int num, int typeMSH, int physical, - int reg, int part, std::vector<MVertex*> &v, - std::map<int, std::vector<MElement*> > elements[10], - std::map<int, std::map<int, std::string> > physicals[4]); - - // create a GModel by importing a mesh (vertexMap has a dim equal to - // the number of vertices and all the other vectors have a dim equal - // to the number of elements) + // create a GModel by importing a mesh (vertexMap has a dim equal to the + // number of vertices and all the other vectors have a dim equal to the number + // of elements) static GModel *createGModel(std::map<int, MVertex*> &vertexMap, std::vector<int> &numElement, std::vector<std::vector<int> > &vertexIndices, @@ -505,9 +506,9 @@ class GModel std::vector<int> &elementary, std::vector<int> &partition); - // create a GModel from newly created mesh elements - // (with their own newly created mesh vertices), - // and let element entities have given physical group tags + // create a GModel from newly created mesh elements (with their own newly + // created mesh vertices), and let element entities have given physical group + // tags static GModel *createGModel (std::map<int, std::vector<MElement*> > &entityToElementsMap, std::map<int, std::vector<int> > &entityToPhysicalsMap); diff --git a/Geo/GModelIO_DIFF.cpp b/Geo/GModelIO_DIFF.cpp index 079fe25b159eb9361f50840fe70d80874ca45f3f..0e5310f061a506a835233e0b5e31642d38e42aad 100644 --- a/Geo/GModelIO_DIFF.cpp +++ b/Geo/GModelIO_DIFF.cpp @@ -294,8 +294,8 @@ int GModel::readDIFF(const std::string &name) if(!getVertices(numVerticesPerElement, indices, vertexMap, vertices)) return 0; } - createElementMSH(num, type, physical, elementary[i-1][1], partition, - vertices, elements, physicals); + _createElementMSH(num, type, physical, elementary[i-1][1], partition, + vertices, elements, physicals); // trouble if elementary[i-1][0]>1 nodal post-processing needed ? if(numElements > 100000) Msg::ProgressMeter(i + 1, numElements, true, "Reading elements"); diff --git a/Geo/GModelIO_MSH.cpp b/Geo/GModelIO_MSH.cpp index 0401e647b52f451c9eb6f24ae7b475b587620dfe..3e949fd2fb63b20c1bc0a7acac77a77777dccb85 100644 --- a/Geo/GModelIO_MSH.cpp +++ b/Geo/GModelIO_MSH.cpp @@ -14,14 +14,345 @@ #include "MHexahedron.h" #include "MPrism.h" #include "MPyramid.h" +#include "StringUtils.h" + +static bool getVertices(int num, const std::vector<int> &indices, + std::map<int, MVertex*> &map, + std::vector<MVertex*> &vertices) +{ + for(int i = 0; i < num; i++){ + if(!map.count(indices[i])){ + Msg::Error("Wrong vertex index %d", indices[i]); + return false; + } + else + vertices.push_back(map[indices[i]]); + } + return true; +} + +static bool getVertices(int num, const std::vector<int> &indices, + std::vector<MVertex*> &vec, + std::vector<MVertex*> &vertices, int minVertex = 0) +{ + for(int i = 0; i < num; i++){ + if(indices[i] < minVertex || indices[i] > (int)(vec.size() - 1 + minVertex)){ + Msg::Error("Wrong vertex index %d", indices[i]); + return false; + } + else + vertices.push_back(vec[indices[i]]); + } + return true; +} int GModel::readMSH(const std::string &name) { + FILE *fp = fopen(name.c_str(), "rb"); + if(!fp){ + Msg::Error("Unable to open file '%s'", name.c_str()); + return 0; + } + + char str[256] = ""; double version = 2.2; - if(version < 3) - return _readMSH2(name); + bool binary = false, swap = false, postpro = false; + int minVertex = 0; + std::map<int, std::vector<int> > entities[4]; + std::map<int, std::vector<MElement*> > elements[10]; + + while(1) { + + while(str[0] != '$'){ + if(!fgets(str, sizeof(str), fp) || feof(fp)) + break; + } + + if(feof(fp)) + break; + + // $MeshFormat section + if(!strncmp(&str[1], "MeshFormat", 10)) { + if(!fgets(str, sizeof(str), fp)) return 0; + int format, size; + if(sscanf(str, "%lf %d %d", &version, &format, &size) != 3) return 0; + if(version < 3.) return _readMSH2(name); + if(format){ + binary = true; + Msg::Info("Mesh is in binary format"); + int one; + if(fread(&one, sizeof(int), 1, fp) != 1) return 0; + if(one != 1){ + swap = true; + Msg::Info("Swapping bytes from binary file"); + } + } + } + + // $PhysicalNames section + else if(!strncmp(&str[1], "PhysicalNames", 13)) { + if(!fgets(str, sizeof(str), fp)) return 0; + int numNames; + if(sscanf(str, "%d", &numNames) != 1) return 0; + for(int i = 0; i < numNames; i++) { + int dim, num; + if(fscanf(fp, "%d", &dim) != 1) return 0; + if(fscanf(fp, "%d", &num) != 1) return 0; + if(!fgets(str, sizeof(str), fp)) return 0; + std::string name = ExtractDoubleQuotedString(str, 256); + if(name.size()) setPhysicalName(name, dim, num); + } + } + + // $Entities section + else if(!strncmp(&str[1], "Entities", 8)) { + if(!fgets(str, sizeof(str), fp)) return 0; + int numEntities; + if(sscanf(str, "%d", &numEntities) != 1) return 0; + for(int i = 0; i < numEntities; i++) { + int num, dim, numPhysicals; + if(fscanf(fp, "%d %d %d", &num, &dim, &numPhysicals) != 3) return 0; + if(numPhysicals > 0){ + std::vector<int> physicals(numPhysicals); + for(int j = 0; j < numPhysicals; j++){ + if(fscanf(fp, "%d", &physicals[j]) != 1) return 0; + } + entities[dim][num] = physicals; + } + } + } + + // $Nodes section + else if(!strncmp(&str[1], "Nodes", 5)) { + if(!fgets(str, sizeof(str), fp)) return 0; + int numVertices; + if(sscanf(str, "%d", &numVertices) != 1) return 0; + Msg::Info("%d vertices", numVertices); + Msg::ResetProgressMeter(); + std::map<int, MVertex*> vertexMap; + std::vector<MVertex*> vertexVector; + int maxVertex = -1; + minVertex = numVertices + 1; + for(int i = 0; i < numVertices; i++) { + int num, entity, dim; + double xyz[3]; + MVertex *vertex = 0; + if(!binary){ + if(fscanf(fp, "%d %lf %lf %lf %d %d", &num, &xyz[0], &xyz[1], &xyz[2], + &entity, &dim) != 6) + return 0; + } + else{ + if(fread(&num, sizeof(int), 1, fp) != 1) return 0; + if(swap) SwapBytes((char*)&num, sizeof(int), 1); + if(fread(xyz, sizeof(double), 3, fp) != 3) return 0; + if(swap) SwapBytes((char*)xyz, sizeof(double), 3); + if(fread(&entity, sizeof(int), 1, fp) != 1) return 0; + if(swap) SwapBytes((char*)&entity, sizeof(int), 1); + if(fread(&dim, sizeof(int), 1, fp) != 1) return 0; + if(swap) SwapBytes((char*)&dim, sizeof(int), 1); + } + if(entity){ + switch(dim){ + case 0: + { + GVertex *gv = getVertexByTag(entity); + if (gv) gv->deleteMesh(); + vertex = new MVertex(xyz[0], xyz[1], xyz[2], gv, num); + } + break; + case 1: + { + GEdge *ge = getEdgeByTag(entity); + double u; + if(!binary){ + if (fscanf(fp, "%lf", &u) != 1) return 0; + } + else{ + if(fread(&u, sizeof(double), 1, fp) != 1) return 0; + if(swap) SwapBytes((char*)&u, sizeof(double), 1); + } + vertex = new MEdgeVertex(xyz[0], xyz[1], xyz[2], ge, u, -1.0, num); + } + break; + case 2: + { + GFace *gf = getFaceByTag(entity); + double uv[2]; + if(!binary){ + if (fscanf(fp, "%lf %lf", &uv[0], &uv[1]) != 2) return 0; + } + else{ + if(fread(uv, sizeof(double), 2, fp) != 2) return 0; + if(swap) SwapBytes((char*)uv, sizeof(double), 2); + } + vertex = new MFaceVertex(xyz[0], xyz[1], xyz[2], gf, uv[0], uv[1], num); + } + break; + case 3: + { + GRegion *gr = getRegionByTag(entity); + double uvw[3]; + if(!binary){ + if(fscanf(fp, "%lf %lf %lf", &uvw[0], &uvw[1], &uvw[2]) != 2) return 0; + } + else{ + if(fread(uvw, sizeof(double), 3, fp) != 3) return 0; + if(swap) SwapBytes((char*)uvw, sizeof(double), 3); + } + vertex = new MVertex(xyz[0], xyz[1], xyz[2], gr, num); + } + break; + default: + Msg::Error("Wrong entity dimension for vertex %d", num); + return 0; + } + } + minVertex = std::min(minVertex, num); + maxVertex = std::max(maxVertex, num); + if(vertexMap.count(num)) + Msg::Warning("Skipping duplicate vertex %d", num); + vertexMap[num] = vertex; + if(numVertices > 100000) + Msg::ProgressMeter(i + 1, numVertices, true, "Reading nodes"); + } + // if the vertex numbering is dense, transfer the map into a vector to + // speed up element creation + if((int)vertexMap.size() == numVertices && + ((minVertex == 1 && maxVertex == numVertices) || + (minVertex == 0 && maxVertex == numVertices - 1))){ + Msg::Info("Vertex numbering is dense"); + vertexVector.resize(vertexMap.size() + 1); + if(minVertex == 1) + vertexVector[0] = 0; + else + vertexVector[numVertices] = 0; + std::map<int, MVertex*>::const_iterator it = vertexMap.begin(); + for(; it != vertexMap.end(); ++it) + vertexVector[it->first] = it->second; + vertexMap.clear(); + } + + // cache the vertex indexing data + _vertexVectorCache = vertexVector; + _vertexMapCache = vertexMap; + } + + // $Elements section + else if(!strncmp(&str[1], "Elements", 8)) { + if(!fgets(str, sizeof(str), fp)) return 0; + int numElements; + if(sscanf(str, "%d", &numElements) != 1) return 0; + Msg::Info("%d elements", numElements); + Msg::ResetProgressMeter(); + std::map<int, MElement*> elementMap; + for(int i = 0; i < numElements; i++) { + int num, type, numTags, numVertices; + if(!binary){ + if(fscanf(fp, "%d %d %d", &num, &type, &numTags) != 3) return 0; + } + else{ + if(fread(&num, sizeof(int), 1, fp) != 1) return 0; + if(swap) SwapBytes((char*)&num, sizeof(int), 1); + if(fread(&type, sizeof(int), 1, fp) != 1) return 0; + if(swap) SwapBytes((char*)&type, sizeof(int), 1); + if(fread(&numTags, sizeof(int), 1, fp) != 1) return 0; + if(swap) SwapBytes((char*)&numTags, sizeof(int), 1); + } + std::vector<int> tags; + if(numTags > 0){ + tags.resize(numTags); + if(!binary){ + for(int j = 0; j < numTags; j++){ + if(fscanf(fp, "%d", &tags[j]) != 1) return 0; + } + } + else{ + if(fread(&tags[0], sizeof(int), numTags, fp) != numTags) return 0; + if(swap) SwapBytes((char*)&tags[0], sizeof(int), numTags); + } + } + if(!(numVertices = MElement::getInfoMSH(type))) { + return 0; + } + std::vector<int> indices(numVertices); + if(!binary){ + for(int j = 0; j < numVertices; j++) + if(fscanf(fp, "%d", &indices[j]) != 1) return 0; + } + else{ + if(fread(&indices[0], sizeof(int), numVertices, fp) != numVertices) return 0; + if(swap) SwapBytes((char*)&indices[0], sizeof(int), numVertices); + } + std::vector<MVertex*> vertices; + if(_vertexVectorCache.size()){ + if(!getVertices(numVertices, indices, _vertexVectorCache, vertices, minVertex)) + return 0; + } + else{ + if(!getVertices(numVertices, indices, _vertexMapCache, vertices)) + return 0; + } + std::vector<short> ghosts; + MElementFactory factory; + MElement *element = factory.create(num, type, tags, vertices, elementMap, + ghosts); + elementMap[num] = element; + int part = element->getPartition(); + if(part) getMeshPartitions().insert(part); + int elementary = tags.size() ? tags[0] : 0; + switch(element->getType()){ + case TYPE_PNT: elements[0][elementary].push_back(element); break; + case TYPE_LIN: elements[1][elementary].push_back(element); break; + case TYPE_TRI: elements[2][elementary].push_back(element); break; + case TYPE_QUA: elements[3][elementary].push_back(element); break; + case TYPE_TET: elements[4][elementary].push_back(element); break; + case TYPE_HEX: elements[5][elementary].push_back(element); break; + case TYPE_PRI: elements[6][elementary].push_back(element); break; + case TYPE_PYR: elements[7][elementary].push_back(element); break; + } + for(unsigned int j = 0; j < ghosts.size(); j++) + _ghostCells.insert(std::pair<MElement*, short>(element, ghosts[j])); + if(numElements > 100000) + Msg::ProgressMeter(i + 1, numElements, true, "Reading elements"); + } + // cache the element map + _elementMapCache = elementMap; + } + + // Post-processing sections + else if(!strncmp(&str[1], "NodeData", 8) || + !strncmp(&str[1], "ElementData", 11) || + !strncmp(&str[1], "ElementNodeData", 15)) { + postpro = true; + break; + } + + do { + if(!fgets(str, sizeof(str), fp) || feof(fp)) + break; + } while(str[0] != '$'); + } + + // store the elements in their associated elementary entity. If the + // entity does not exist, create a new (discrete) one. + for(int i = 0; i < (int)(sizeof(elements) / sizeof(elements[0])); i++) + _storeElementsInEntities(elements[i]); + + // associate the correct geometrical entity with each mesh vertex + _associateEntityWithMeshVertices(); + + // store the vertices in their associated geometrical entity + if(_vertexVectorCache.size()) + _storeVerticesInEntities(_vertexVectorCache); + else + _storeVerticesInEntities(_vertexMapCache); + + // FIXME: store physicals in entities + + fclose(fp); - return 0; + return postpro ? 2 : 1; } static int getNumElementsMSH(GEntity *ge, bool saveAll, int saveSinglePartition) diff --git a/Geo/MElement.cpp b/Geo/MElement.cpp index 87bc12f812bca3e194642ac3e00f0d1495aa7c34..02a359fb22c7f0b2ae0cdeae6d7af0eb59f173bc 100644 --- a/Geo/MElement.cpp +++ b/Geo/MElement.cpp @@ -1398,6 +1398,32 @@ MElement *MElementFactory::create(int type, std::vector<MVertex*> &v, } } +MElement *MElementFactory::create(int num, int type, const std::vector<int> &tags, + std::vector<MVertex*> &v, + std::map<int, MElement*> &elementCache, + std::vector<short> &ghosts) +{ + MElement *parent = 0; + int part = 0; + if(tags.size() > 2 && (type == MSH_PNT_SUB || type == MSH_LIN_SUB || + type == MSH_TRI_SUB || type == MSH_TET_SUB)){ + parent = elementCache[tags[1]]; + if(tags[2]){ // num partitions + part = tags[3]; + for(int i = 0; i < tags[2] - 1; i++) + ghosts.push_back(tags[4 + i]); + } + } + else if(tags.size() > 1){ + if(tags[1]){ // num partitions + part = tags[2]; + for(int i = 0; i < tags[1] - 1; i++) + ghosts.push_back(tags[3 + i]); + } + } + create(type, v, num, part, false, parent); +} + void MElement::xyzTouvw(fullMatrix<double> *xu) { double _xyz[3] = {(*xu)(0,0),(*xu)(0,1),(*xu)(0,2)}, _uvw[3]; diff --git a/Geo/MElement.h b/Geo/MElement.h index 3ce5d69ce73b71c35a856fa1dbdf0c65d77c5b95..d1d33ad50cecffefe80033c02250379275b0c1e1 100644 --- a/Geo/MElement.h +++ b/Geo/MElement.h @@ -361,6 +361,10 @@ class MElementFactory{ public: MElement *create(int type, std::vector<MVertex*> &v, int num=0, int part=0, bool owner=false, MElement *parent=0, MElement *d1=0, MElement *d2=0); + MElement *create(int num, int type, const std::vector<int> &tags, + std::vector<MVertex*> &v, + std::map<int, MElement*> &elementCache, + std::vector<short> &ghosts); }; // Traits of various elements based on the dimension. These generally define