diff --git a/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp b/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp index 0c3ab4cea37fd8edc436c811e41f6406f415a957..4d057c9d00173db394bf4e2ddc1080eadff73a41 100644 --- a/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp +++ b/contrib/HighOrderMeshOptimizer/OptHomFastCurving.cpp @@ -55,304 +55,54 @@ namespace { -// Compute vertex -> element connectivity -void calcVertex2Elements(int dim, GEntity *entity, - std::map<MVertex*, std::vector<MElement *> > &vertex2elements) -{ - for (size_t i = 0; i < entity->getNumMeshElements(); ++i) { - MElement *element = entity->getMeshElement(i); - if (element->getDim() == dim) - for (int j = 0; j < element->getNumPrimaryVertices(); ++j) - vertex2elements[element->getVertex(j)].push_back(element); - } -} - - - -// Given a starting vertex and a direction, find the next vertex in a column -MVertex *findVertFromDir(const std::map<MVertex*, std::vector<MElement*> > &vertex2elements, - MVertex *v0, MVertex *vExclude, const SVector3 &dir, double spLimit) -{ - - const std::vector<MElement*> &elts = (*vertex2elements.find(v0)).second; // All elements connected to vertex - MVertex *vFound = 0; - double spMax = 0.; - for (std::vector<MElement*>::const_iterator itEl = elts.begin(); itEl != elts.end(); ++itEl) // Loop over all elements - for (int i=0; i<(*itEl)->getNumEdges(); i++) { // Loop over all edges of each element - MEdge edge = (*itEl)->getEdge(i); - MVertex *vA = edge.getVertex(0), *vB = edge.getVertex(1), *vTmp; - if (v0 == vA) // Skip if edge unconnected to vertex... - if (vB == vExclude) continue; // ... or if connecting to preceding vertex - else vTmp = vB; - else if (v0 == vB) - if (vA == vExclude) continue; - else vTmp = vA; - else continue; - SVector3 tanVec = edge.tangent(); - double sp = fabs(dot(tanVec, dir)); - if ((sp > spMax) && (sp > spLimit)){ // Retain the edge giving max. dot product with normal - spMax = sp; - vFound = vTmp; - } - } - return vFound; -} - - - -// Find a column of vertices starting with a given vertex -template<class bndType> -bool findColumn(const std::map<MVertex*, std::vector<MElement*> > &vertex2elements, - MVertex *baseVert, const SVector3 &norm, - const bndType &baseEd, std::vector<MVertex*> &col, int maxNumLayers) -{ - static const double spMin = 0.866025404; // Threshold in cosine between 1st edge and normal - static const double spTol = 0.999; // Threshold in cosine between an edge and 1st edge - - MVertex *vert = findVertFromDir(vertex2elements, baseVert, 0, norm, spMin); - if (!vert) { // If 1st normal edge not found... - vert = findVertFromDir(vertex2elements, baseVert, 0, baseEd.normal(), spMin); // ... tries normal to base edge/face - if (!vert) { - Msg::Warning("Could not find normal edge for vertex %i ", baseVert->getNum()); - return false; - } - } - SVector3 firstEdgeDir(baseVert->point(), vert->point()); - firstEdgeDir.normalize(); - - SVector3 dir(baseVert->point(), vert->point()); - col.push_back(vert); - MVertex *oldVert = baseVert, *newVert; - for (int iLayer = 0; iLayer < maxNumLayers; iLayer++) { - newVert = findVertFromDir(vertex2elements, vert, oldVert, firstEdgeDir, spTol); - col.push_back(newVert); // Will add null pointer at the end, which is OK - if (!newVert) break; - oldVert = vert; - vert = newVert; - } - - return (col.size() > 1); -} - - - -// Add normal to edge/face to data structure for vertices -void addNormVertEl(MElement *el, const SVector3 &norm, - std::map<MVertex*, SVector3> &normVert) -{ - for(unsigned int iV = 0; iV<el->getNumVertices(); iV++) { - MVertex *vert = el->getVertex(iV); - std::pair<std::map<MVertex*, SVector3>::iterator, bool> insNorm = // If nothing for vert... - normVert.insert(std::pair<MVertex*, SVector3>(vert, SVector3(0.))); // ... create zero normal - insNorm.first->second += norm; // Cumulate normals - } -} +typedef std::map<MEdge, std::vector<MElement*>, Less_Edge> MEdgeVecMEltMap; +typedef std::map<MFace, std::vector<MElement*>, Less_Face> MFaceVecMEltMap; -// Compute normals to boundary edges/faces and store them per vertex -void buildNormals(const std::map<MVertex*, std::vector<MElement*> > &vertex2elements, - const std::multimap<GEntity*,GEntity*> &entities, const FastCurvingParameters &p, - std::map<GEntity*, std::map<MVertex*, SVector3> > &normVertEnt) +// Compute edge -> element connectivity (for 2D elements) +void calcEdge2Elements(GEntity *entity, MEdgeVecMEltMap &ed2el) { - normVertEnt.clear(); - for (std::multimap<GEntity*,GEntity*>::const_iterator itBE = entities.begin(); - itBE != entities.end(); itBE++) { // Loop over model entities - GEntity* const &bndEnt = itBE->second; - std::map<MVertex*, SVector3> &normVert = normVertEnt[bndEnt]; - if (bndEnt->dim() == 1) { - GEdge *ge = bndEnt->cast2Edge(); - for(unsigned int iEl = 0; iEl<ge->lines.size(); iEl++) { - MLine* const &line = ge->lines[iEl]; - SVector3 norm = line->getEdge(0).normal(); - addNormVertEl(line, norm, normVert); - } - } - else if (bndEnt->dim() == 2) { - GFace *gf = bndEnt->cast2Face(); - for(unsigned int iEl = 0; iEl<gf->triangles.size(); iEl++) { - MTriangle* const &tri = gf->triangles[iEl]; - SVector3 norm = tri->getFace(0).normal(); - addNormVertEl(tri, norm, normVert); - } - for(unsigned int iEl = 0; iEl<gf->quadrangles.size(); iEl++) { - MQuadrangle* const &quad = gf->quadrangles[iEl]; - SVector3 norm = quad->getFace(0).normal(); - addNormVertEl(quad, norm, normVert); + for (size_t iEl = 0; iEl < entity->getNumMeshElements(); iEl++) { + MElement *elt = entity->getMeshElement(iEl); + if (elt->getDim() == 2) + for (int iEdge = 0; iEdge < elt->getNumEdges(); iEdge++) { + ed2el[elt->getEdge(iEdge)].push_back(elt); } - } - for (std::map<MVertex*, SVector3> ::iterator itNV = // Re-normalize for each geom. entity - normVert.begin(); itNV != normVert.end(); itNV++) - itNV->second.normalize(); } } -// Get first domain element for a given boundary edge -MElement *getFirstEl(const std::map<MVertex*, std::vector<MElement*> > &vertex2elements, - const MEdge &baseEd) +// Compute face -> element connectivity (for 3D elements) +void calcFace2Elements(GEntity *entity, MFaceVecMEltMap &face2el) { - MVertex *vb0 = baseEd.getVertex(0), *vb1 = baseEd.getVertex(1); - - const std::vector<MElement*> &nb0 = vertex2elements.find(vb0)->second; - const std::vector<MElement*> &nb1 = vertex2elements.find(vb1)->second; - MElement *firstEl = 0; - for (int iEl=0; iEl<nb0.size(); iEl++) { - if (find(nb1.begin(), nb1.end(), nb0[iEl]) != nb1.end()) { - firstEl = nb0[iEl]; - break; - } + for (size_t iEl = 0; iEl < entity->getNumMeshElements(); iEl++) { + MElement *elt = entity->getMeshElement(iEl); + if (elt->getDim() == 3) + for (int iFace = 0; iFace < elt->getNumFaces(); iFace++) + face2el[elt->getFace(iFace)].push_back(elt); } - - return firstEl; } -// Get intersection of two vectors of MElement* -void intersect(const std::vector<MElement*> &vi1, const std::vector<MElement*> &vi2, - std::vector<MElement*> &vo) +// Get the face index of an element given a MFace +inline int getElementFace(const MFace &face, MElement *el) { - vo.clear(); - for (std::vector<MElement*>::const_iterator it = vi1.begin(); it != vi1.end(); it++) - if (std::find(vi2.begin(), vi2.end(), *it) != vi2.end()) - vo.push_back(*it); + for (int iFace = 0; iFace < el->getNumFaces(); iFace++) + if (el->getFace(iFace) == face) return iFace; + return -1; } -// Get first domain element for a given boundary face -MElement *getFirstEl(const std::map<MVertex*, std::vector<MElement*> > &vertex2elements, - const MFace &baseFace) +// Get the edge index of an element given a MEdge +inline int getElementEdge(const MEdge &edge, MElement *el) { - MVertex *vb0 = baseFace.getVertex(0), *vb1 = baseFace.getVertex(1); - - // Get elements common to vertices 0 and 1 - const std::vector<MElement*> &nb0 = vertex2elements.find(vb0)->second; - const std::vector<MElement*> &nb1 = vertex2elements.find(vb1)->second; - std::vector<MElement*> nb01; - intersect(nb0, nb1, nb01); - if (nb01.empty()) return 0; - if (nb01.size() == 1) return nb01.front(); - - // Get elements common to vertices 0, 1 and 2 - MVertex *vb2 = baseFace.getVertex(2); - const std::vector<MElement*> &nb2 = vertex2elements.find(vb2)->second; - std::vector<MElement*> nb012; - intersect(nb01, nb2, nb012); - if (nb012.empty()) return 0; - if (nb012.size() == 1) return nb012.front(); - - // If quad, get elements common to all 4 vertices - if (baseFace.getNumVertices() == 4) { - MVertex *vb3 = baseFace.getVertex(3); - const std::vector<MElement*> &nb3 = vertex2elements.find(vb3)->second; - std::vector<MElement*> nb0123; - intersect(nb012, nb3, nb0123); - if (nb0123.empty()) return 0; - if (nb0123.size() == 1) return nb0123.front(); - } - - // Too many elements, return error - return 0; -} - - - -// Get base vertices (in order of first element) for an edge -bool getBaseVertices(const MEdge &baseEd, MElement *firstEl, std::vector<MVertex*> &baseVert) -{ - baseVert.clear(); - for (int iEd = 0; iEd < firstEl->getNumEdges(); iEd++) - if (firstEl->getEdge(iEd) == baseEd) - firstEl->getEdgeVertices(iEd, baseVert); - - if (baseVert.empty()) { - Msg::Error("Base edge vertices not found in getBaseVertices"); - return false; - } - else - return true; -} - - - -// Get base vertices (in order of first element) for a face -bool getBaseVertices(const MFace &baseFace, MElement *firstEl, std::vector<MVertex*> &baseVert) -{ - baseVert.clear(); - for (int iFace = 0; iFace < firstEl->getNumFaces(); iFace++) - if (firstEl->getFace(iFace) == baseFace) - firstEl->getFaceVertices(iFace, baseVert); - - if (baseVert.empty()) { - Msg::Error("Base face vertices not found in getBaseVertices"); - return false; - } - else - return true; -} - - - -// Top primary vertices (in order corresponding to the primary base vertices) -template<class bndType> -bool getTopPrimVertices(std::map<MVertex*, std::vector<MElement *> > &vertex2elements, - const std::map<MVertex*, SVector3> &normVert, - const bndType &baseBnd, int maxNumLayers, - const std::vector<MVertex*> &baseVert, - std::vector<MVertex*> &topPrimVert) -{ - int nbVert = baseBnd.getNumVertices(); - topPrimVert = std::vector<MVertex*>(nbVert); - for(int i = 0; i < nbVert; i++) { - std::map<MVertex*, SVector3>::const_iterator itNorm = normVert.find(baseVert[i]); - if (itNorm == normVert.end()) { - Msg::Error("Normal to vertex not found in getTopPrimVertices"); - itNorm = normVert.begin(); - } - std::vector<MVertex*> col; - bool colFound = findColumn(vertex2elements, baseVert[i], itNorm->second, - baseBnd, col, maxNumLayers); - if (!colFound) return false; // Give up element if column not found - topPrimVert[i] = *(col.end()-2); - } - - return true; -} - - - -// Get blob of elements encompassed by a given meta-element -// FIXME: Implement 3D -void get2DBLBlob(const std::map<MVertex*, std::vector<MElement*> > &vertex2elements, - MElement *firstEl, const MetaEl *supEl, std::set<MElement*> &blob) -{ - static const int maxDepth = 100; - typedef std::list<MElement*> elLType; - typedef std::vector<MElement*> elVType; - - // Build blob by looping over layers of elements (assuming that if an el is too far, its neighbours are too far as well) - blob.clear(); - elLType currentLayer, lastLayer; - blob.insert(firstEl); - lastLayer.push_back(firstEl); - for (int d = 0; d < maxDepth; ++d) { // Loop over layers - currentLayer.clear(); - for (elLType::iterator it = lastLayer.begin(); it != lastLayer.end(); ++it) { // Loop over elements of last layer - for (int i = 0; i < (*it)->getNumPrimaryVertices(); ++i) { - const elVType &neighbours = vertex2elements.find((*it)->getVertex(i))->second; - for (elVType::const_iterator itN = neighbours.begin(); itN != neighbours.end(); ++itN) { // Loop over neighbours of last layer - SPoint3 p = (*itN)->barycenter(true); - if (supEl->isPointIn(p)) // Add neighbour to blob if inside test element - if (blob.insert(*itN).second) currentLayer.push_back(*itN); // Add to current layer if new in blob - } - } - } - if (currentLayer.empty()) break; // Finished if no new element in blob - lastLayer = currentLayer; - } + for (int iEdge = 0; iEdge < el->getNumEdges(); iEdge++) + if (el->getEdge(iEdge) == edge) return iEdge; + return -1; } @@ -383,7 +133,8 @@ inline void insertIfCurved(MElement *el, std::list<MElement*> &bndEl) const int dim = el->getDim(); // Compute unit normal to straight edge/face - SVector3 normal = (dim == 1) ? el->getEdge(0).normal() : el->getFace(0).normal(); + SVector3 normal = (dim == 1) ? el->getEdge(0).normal() : + el->getFace(0).normal(); // Get functional spaces const nodalBasis *lagBasis = el->getFunctionSpace(); @@ -412,6 +163,102 @@ inline void insertIfCurved(MElement *el, std::list<MElement*> &bndEl) } + +void getOppositeEdge(MElement *el, const MEdge &elBaseEd, MEdge &elTopEd, + double &edLenMin, double &edLenMax) +{ + static const double BIG = 1e300; + + const int iElBaseEd = getElementEdge(elBaseEd, el); + edLenMin = BIG; + edLenMax = -BIG; + MEdge elMaxEd; + + // Look for largest edge except base one + for (int iElEd = 0; iElEd < el->getNumEdges(); iElEd++) { + if (iElEd != iElBaseEd) { + MEdge edTest = el->getEdge(iElEd); + double edLenTest = edTest.length(); + if (edLenTest < edLenMin) edLenMin = edLenTest; + if (edLenTest > edLenMax) { + edLenMax = edLenTest; + elMaxEd = edTest; + } + } + } + + // Build top edge from max edge (with right orientation) + bool sameOrient = false; + if (el->getType() == TYPE_TRI) { // Triangle: look for common vertex + sameOrient = (elBaseEd.getVertex(0) == elMaxEd.getVertex(0)); + } + else { // Quad: look for edge between base and top vert. + MEdge sideEdTest(elBaseEd.getVertex(0), elMaxEd.getVertex(0)); + for (int iElEd = 0; iElEd < el->getNumEdges(); iElEd++) + if (el->getEdge(iElEd) == sideEdTest) { + sameOrient = true; + break; + } + } + if (sameOrient) elTopEd = elMaxEd; + else elTopEd = MEdge(elMaxEd.getVertex(1), elMaxEd.getVertex(0)); +} + + + +bool getColumn2D(MEdgeVecMEltMap &ed2el, + int maxNumLayers, const MEdge &baseEd, + std::vector<MVertex*> &baseVert, + std::vector<MVertex*> &topPrimVert, + std::vector<MElement*> &blob) +{ + static const double tol = 2.; + + // Get first element and base vertices + std::vector<MElement*> firstElts = ed2el[baseEd]; + if (firstElts.size() != 1) { + Msg::Error("%i edge(s) found for edge (%i, %i)", firstElts.size(), + baseEd.getVertex(0)->getNum(), baseEd.getVertex(1)->getNum()); + return false; + } + MElement *el = firstElts[0]; + const int iFirstElEd = getElementEdge(baseEd, el); + el->getEdgeVertices(iFirstElEd, baseVert); + MEdge elBaseEd(baseVert[0], baseVert[1]); + MEdge elTopEd; + + // Sweep column upwards by choosing largest edges in each element + for (int iLayer = 0; iLayer < maxNumLayers; iLayer++) { + double edLenMin, edLenMax; + getOppositeEdge(el, elBaseEd, elTopEd, edLenMin, edLenMax); + if (edLenMax < edLenMin*tol) break; + blob.push_back(el); + std::vector<MElement*> newElts = ed2el[elTopEd]; + el = (newElts[0] == el) ? newElts[1] : newElts[0]; + elBaseEd = elTopEd; + } + + // TODO: improve by taking all vertices? + topPrimVert.resize(2); + topPrimVert[0] = elBaseEd.getVertex(0); + topPrimVert[1] = elBaseEd.getVertex(1); + return true; +} + + + +// TODO: Implement 3D +bool getColumn3D(MFaceVecMEltMap &face2el, + int maxNumLayers, const MFace &baseFace, + std::vector<MVertex*> &baseVert, + std::vector<MVertex*> &topPrimVert, + std::vector<MElement*> &blob) +{ + return true; +} + + + class DbgOutput { public: void addMetaEl(MetaEl &mEl) { @@ -455,9 +302,7 @@ private: -// Curve elements adjacent to a boundary model entity -void curveMeshFromBnd(std::map<MVertex*, std::vector<MElement *> > &vertex2elements, - const std::map<MVertex*, SVector3> &normVert, +void curveMeshFromBnd(MEdgeVecMEltMap &ed2el, MFaceVecMEltMap &face2el, GEntity *bndEnt, FastCurvingParameters &p) { // Build list of bnd. elements to consider @@ -475,7 +320,8 @@ void curveMeshFromBnd(std::map<MVertex*, std::vector<MElement *> > &vertex2eleme insertIfCurved(gFace->quadrangles[i], bndEl); } else - Msg::Error("Cannot treat model entity %i of dim %i", bndEnt->tag(), bndEnt->dim()); + Msg::Error("Cannot treat model entity %i of dim %i", bndEnt->tag(), + bndEnt->dim()); DbgOutput dbgOut; @@ -484,19 +330,16 @@ void curveMeshFromBnd(std::map<MVertex*, std::vector<MElement *> > &vertex2eleme itBE != bndEl.end(); itBE++) { // Loop over bnd. elements const int bndType = (*itBE)->getType(); int metaElType; - bool foundVert; - MElement *firstEl = 0; + bool foundCol; std::vector<MVertex*> baseVert, topPrimVert; + std::vector<MElement*> blob; if (bndType == TYPE_LIN) { // 1D boundary? MVertex *vb0 = (*itBE)->getVertex(0); MVertex *vb1 = (*itBE)->getVertex(1); metaElType = TYPE_QUA; MEdge baseEd(vb0, vb1); - firstEl = getFirstEl(vertex2elements, baseEd); - foundVert = getBaseVertices(baseEd, firstEl, baseVert); - if (!foundVert) continue; // Skip bnd. el. if base vertices not found - foundVert = getTopPrimVertices(vertex2elements, normVert, baseEd, - p.maxNumLayers, baseVert, topPrimVert); + foundCol = getColumn2D(ed2el, p.maxNumLayers, baseEd, baseVert, + topPrimVert, blob); } else { // 2D boundary MVertex *vb0 = (*itBE)->getVertex(0); @@ -512,28 +355,19 @@ void curveMeshFromBnd(std::map<MVertex*, std::vector<MElement *> > &vertex2eleme metaElType = TYPE_PRI; } MFace baseFace(vb0, vb1, vb2, vb3); - firstEl = getFirstEl(vertex2elements, baseFace); - if (!firstEl) { - Msg::Error("Did not find first domain element for boundary element %i", - (*itBE)->getNum()); - continue; - } - foundVert = getBaseVertices(baseFace, firstEl, baseVert); - if (!foundVert) continue; // Skip bnd. el. if base vertices not found - foundVert = getTopPrimVertices(vertex2elements, normVert, - baseFace, p.maxNumLayers, - baseVert, topPrimVert); + foundCol = getColumn3D(face2el, p.maxNumLayers, baseFace, baseVert, + topPrimVert, blob); } - if (!foundVert) continue; // Skip bnd. el. if top vertices not found - int order = firstEl->getPolynomialOrder(); + if (!foundCol) continue; // Skip bnd. el. if top vertices not found + int order = blob[0]->getPolynomialOrder(); MetaEl metaEl(metaElType, order, baseVert, topPrimVert); dbgOut.addMetaEl(metaEl); - std::set<MElement*> blob; - get2DBLBlob(vertex2elements, firstEl, &metaEl, blob); // TODO: Implement for 3D - for (std::set<MElement*>::iterator itE = blob.begin(); itE != blob.end(); ++itE) { - makeStraight(*itE, movedVert); - for (int i = (*itE)->getNumPrimaryVertices(); i < (*itE)->getNumVertices(); ++i) { // Loop over HO vert. of each el. in blob - MVertex* vert = (*itE)->getVertex(i); + for (int iEl = 0; iEl < blob.size(); iEl++) { + MElement *&elt = blob[iEl]; + makeStraight(elt, movedVert); + for (int iV = elt->getNumPrimaryVertices(); + iV < elt->getNumVertices(); ++iV) { // Loop over HO vert. of each el. in blob + MVertex* vert = elt->getVertex(iV); if (movedVert.find(vert) == movedVert.end()) { // Try to move vert. not already moved double xyzS[3] = {vert->x(), vert->y(), vert->z()}, xyzC[3]; if (metaEl.straightToCurved(xyzS,xyzC)) { @@ -565,9 +399,11 @@ void HighOrderMeshFastCurving(GModel *gm, FastCurvingParameters &p) // Compute vert. -> elt. connectivity Msg::Info("Computing connectivity..."); - std::map<MVertex*, std::vector<MElement *> > vertex2elements; + MEdgeVecMEltMap ed2el; + MFaceVecMEltMap face2el; for (int iEnt = 0; iEnt < allEntities.size(); ++iEnt) - calcVertex2Elements(p.dim, allEntities[iEnt], vertex2elements); + if (p.dim == 2) calcEdge2Elements(allEntities[iEnt], ed2el); + else calcFace2Elements(allEntities[iEnt], face2el); // Build multimap of each geometric entity to its boundaries std::multimap<GEntity*,GEntity*> entities; @@ -578,17 +414,12 @@ void HighOrderMeshFastCurving(GModel *gm, FastCurvingParameters &p) entities.insert(std::pair<GEntity*,GEntity*>(dummy, entity)); } - // Build normals to base vertices - std::map<GEntity*, std::map<MVertex*, SVector3> > normVertEnt; // Normal to each vertex for each geom. entity - buildNormals(vertex2elements, entities, p, normVertEnt); - // Loop over geometric entities for (std::multimap<GEntity*,GEntity*>::iterator itBE = entities.begin(); itBE != entities.end(); itBE++) { GEntity *domEnt = itBE->first, *bndEnt = itBE->second; Msg::Info("Curving elements for boundary entity %d...", bndEnt->tag()); - std::map<MVertex*, SVector3> &normVert = normVertEnt[bndEnt]; - curveMeshFromBnd(vertex2elements, normVert, bndEnt, p); + curveMeshFromBnd(ed2el, face2el, bndEnt, p); } double t2 = Cpu();