int MZoneBoundary<DIM>::exteriorBoundaryVertices (const int normalSource, ZoneBoVec &zoneBoVec) { if(globalBoVertMap.size() == 0) return 1; zoneBoVec.clear(); zoneBoVec.reserve(3*globalBoVertMap.size()/2); BCPatchIndex patch; // Provides a BC patch index for each // entity bool warnNormFromElem = true; // A warning that normals were // determined from elements. This // warning is only give once (after // which the flag is set to false) const typename GlobalBoVertexMap::const_iterator vMapEnd = globalBoVertMap.end(); for(typename GlobalBoVertexMap::const_iterator vMapIt = globalBoVertMap.begin(); vMapIt != vMapEnd; ++vMapIt) { const int nZone = vMapIt->second.zoneData.size(); for(int iZone = 0; iZone != nZone; ++iZone) { const typename GlobalVertexData<FaceT>::ZoneData &zoneData = vMapIt->second.zoneData[iZone]; // Ref. to data stored for this zone //--Try to find an outwards normal for this vertex if(normalSource) { updateBoVec<DIM, FaceT>(normalSource, vMapIt->first, zoneData.zoneIndex, zoneData.vertexIndex, vMapIt->second.faces, zoneBoVec, patch, warnNormFromElem); } else { // Keys to 'globalBoVertMap' will not change so const_cast is okay. zoneBoVec.push_back(VertexBoundary(zoneData.zoneIndex, 0, SVector3(0.), const_cast<MVertex*>(vMapIt->first), zoneData.vertexIndex)); } } } // If normals were written from the geometry, zoneBoVec currently stores // entities in bcPatchIndex. Update with the actual patch index. Why? - // because two entities may have been merged if the interface between them is // continuous. if(normalSource == NormalSourceGeometry) { patch.generatePatchIndices(); const int nBoVert = zoneBoVec.size(); for(int iBoVert = 0; iBoVert != nBoVert; ++iBoVert) { zoneBoVec[iBoVert].bcPatchIndex = patch.getIndex(zoneBoVec[iBoVert].bcPatchIndex); } } return 0; }
void updateBoVec<3, MFace>( const int normalSource, const MVertex *const vertex, const int zoneIndex, const int vertIndex, const CCon::FaceVector<MZoneBoundary<3>::GlobalVertexData<MFace>::FaceDataB> &faces, ZoneBoVec &zoneBoVec, BCPatchIndex &patch, bool &warnNormFromElem) { GEntity *ent; if(normalSource == NormalSourceElement) goto getNormalFromElements; ent = vertex->onWhat(); if(ent == 0) { goto getNormalFromElements; // No entity: try to find a normal from the faces } else { switch(ent->dim()) { case 0: case 1: /*--------------------------------------------------------------------* * In this case, there are possibly multiple GFaces from this zone * connected to the vertex. One patch for each GFace will be written. *--------------------------------------------------------------------*/ { //--Get a list of face entities connected to 'ent' std::list<GFace *> gFaceList; switch(ent->dim()) { case 0: { std::vector<GEdge *> gEdgeList = ent->edges(); std::list<GFace *> gFaceList; for(std::vector<GEdge *>::const_iterator gEIt = gEdgeList.begin(); gEIt != gEdgeList.end(); ++gEIt) { std::vector<GFace *> alist = (*gEIt)->faces(); gFaceList.insert(gFaceList.end(), alist.begin(), alist.end()); } // Remove duplicates gFaceList.sort(); gFaceList.unique(); } break; case 1: { std::vector<GFace *> fac = ent->faces(); gFaceList.insert(gFaceList.end(), fac.begin(), fac.end()); } break; } //--Get a list of face entities connected to the 'vertex' that are also // in the //--zone std::list<const GFace *> useGFace; std::vector<GEdge *> gEdgeList; const int nFace = faces.size(); for(int iFace = 0; iFace != nFace; ++iFace) { if(zoneIndex == faces[iFace].zoneIndex) { bool matchedFace = false; MFace mFace = faces[iFace].parentElement->getFace(faces[iFace].parentFace); const int nVOnF = mFace.getNumVertices(); int vertexOnF = 0; // The index of 'vertex' in the face for(int iVOnF = 0; iVOnF != nVOnF; ++iVOnF) { const MVertex *const vertex2 = mFace.getVertex(iVOnF); if(vertex == vertex2) vertexOnF = iVOnF; else { const GEntity *const ent2 = vertex2->onWhat(); if(ent2->dim() == 2) { matchedFace = true; useGFace.push_back(static_cast<const GFace *>(ent2)); break; } } } // Triangle MElements are a total P.I.T.A.: // - If the original 'ent' is a vertex, one MVertex can be on the // GVertex, and the other two on GEdges, and then the MElement is // still on the GFace. // - If the original 'ent' is an edge, one MVertex can be on the // original GEdge, another on a GVertex, and the final on another // GEdge, and then the MElement is still on the GFace. There is // also the unlikely case where the two other MVertex are both on // edges ... and the MElement is still on the GFace. if(!matchedFace && (3 == nVOnF)) { const MVertex *vertex2 = 0; const MVertex *vertex3 = 0; switch(vertexOnF) { case 0: vertex2 = mFace.getVertex(1); vertex3 = mFace.getVertex(2); break; case 1: vertex2 = mFace.getVertex(0); vertex3 = mFace.getVertex(2); break; case 2: vertex2 = mFace.getVertex(0); vertex3 = mFace.getVertex(1); break; } if(vertex2 && vertex3) { const GEntity *const ent2 = vertex2->onWhat(); const GEntity *const ent3 = vertex3->onWhat(); if(ent2 && ent3) { if(ent2->dim() == 1 && ent3->dim() == 1) { // Which GFace is bounded by edges ent2 and ent3? for(std::list<GFace *>::const_iterator gFIt = gFaceList.begin(); gFIt != gFaceList.end(); ++gFIt) { gEdgeList = (*gFIt)->edges(); if((std::find(gEdgeList.begin(), gEdgeList.end(), ent2) != gEdgeList.end()) && (std::find(gEdgeList.begin(), gEdgeList.end(), ent3) != gEdgeList.end())) { // Edges ent2 and ent3 bound this face useGFace.push_back(*gFIt); break; } } } else if(ent->dim() == 1 && (ent2->dim() + ent3->dim() == 1)) { const GEntity *entCmp; if(ent2->dim() == 1) entCmp = ent2; else entCmp = ent3; // Which GFace is bounded by entCmp for(std::list<GFace *>::const_iterator gFIt = gFaceList.begin(); gFIt != gFaceList.end(); ++gFIt) { gEdgeList = (*gFIt)->edges(); if(std::find(gEdgeList.begin(), gEdgeList.end(), entCmp) != gEdgeList.end()) { // Edge entCmp and the original edge bound this face useGFace.push_back(*gFIt); break; } } } } } } // Stupid triangles } // End if face in zone } // End loop over faces // Duplicates are a possibility, remove useGFace.sort(); useGFace.unique(); //--'useGFace' now contains the face entities that connect to vertex. A // BC //--patch will be written for each of them. for(std::list<const GFace *>::const_iterator gFIt = useGFace.begin(); gFIt != useGFace.end(); ++gFIt) { SPoint2 par; if(!reparamMeshVertexOnFace(const_cast<MVertex *>(vertex), *gFIt, par)) goto getNormalFromElements; // :P After all that! SVector3 boNormal = (*gFIt)->normal(par); SPoint3 interior(0., 0., 0.); int cFace = 0; const int nFace = faces.size(); for(int iFace = 0; iFace != nFace; ++iFace) { if(faces[iFace].zoneIndex == zoneIndex) { ++cFace; interior += faces[iFace].parentElement->barycenter(); } } interior /= cFace; if(dot(boNormal, SVector3(vertex->point(), interior)) < 0.) boNormal.negate(); zoneBoVec.push_back( VertexBoundary(zoneIndex, (*gFIt)->tag(), boNormal, const_cast<MVertex *>(vertex), vertIndex)); patch.add((*gFIt)->tag()); } } break; case 2: /*--------------------------------------------------------------------* * The vertex exists on a face and belongs to only 1 BC patch. *--------------------------------------------------------------------*/ { const GFace *const gFace = static_cast<const GFace *>(ent); SPoint2 par; if(!reparamMeshVertexOnFace(const_cast<MVertex *>(vertex), gFace, par)) goto getNormalFromElements; SVector3 boNormal = static_cast<const GFace *>(ent)->normal(par); SPoint3 interior(0., 0., 0.); int cFace = 0; const int nFace = faces.size(); for(int iFace = 0; iFace != nFace; ++iFace) { if(faces[iFace].zoneIndex == zoneIndex) { ++cFace; interior += faces[iFace].parentElement->barycenter(); } } interior /= cFace; if(dot(boNormal, SVector3(vertex->point(), interior)) < 0.) boNormal.negate(); zoneBoVec.push_back(VertexBoundary(zoneIndex, gFace->tag(), boNormal, const_cast<MVertex *>(vertex), vertIndex)); patch.add(gFace->tag()); } break; default: goto getNormalFromElements; } } return; getNormalFromElements:; /*--------------------------------------------------------------------* * Geometry information cannot be used - generate normals from the * elements *--------------------------------------------------------------------*/ { if(warnNormFromElem && normalSource == 1) { Msg::Warning("Some or all boundary normals were determined from mesh " "elements instead of from the geometry"); warnNormFromElem = false; } // Sum the normals from each element connected to the vertex and in the // zone. Each normal has to be converted independently into an inwards- // pointing normal. //**Weight each normal by the area of the boundary element? SVector3 boNormal(0.); const int nFace = faces.size(); for(int iFace = 0; iFace != nFace; ++iFace) { if(faces[iFace].zoneIndex == zoneIndex) { // Normal to the boundary (unknown direction) SVector3 bnt = faces[iFace].parentElement->getFace(faces[iFace].parentFace).normal(); // Inwards normal const SVector3 inwards(vertex->point(), faces[iFace].parentElement->barycenter()); if(dot(bnt, inwards) < 0.) bnt.negate(); boNormal += bnt; } } boNormal.normalize(); zoneBoVec.push_back(VertexBoundary( zoneIndex, 0, boNormal, const_cast<MVertex *>(vertex), vertIndex)); patch.add(0); } }
void updateBoVec<2, MEdge>( const int normalSource, const MVertex *const vertex, const int zoneIndex, const int vertIndex, const CCon::FaceVector<MZoneBoundary<2>::GlobalVertexData<MEdge>::FaceDataB> &faces, ZoneBoVec &zoneBoVec, BCPatchIndex &patch, bool &warnNormFromElem) { GEntity *ent; if(normalSource == NormalSourceElement) goto getNormalFromElements; ent = vertex->onWhat(); if(ent == 0) { goto getNormalFromElements; // No entity: try to find a normal from the faces } else { switch(ent->dim()) { case 0: /*--------------------------------------------------------------------* * In this case, there are possibly two GEdges from this zone * connected to the vertex. If the angle between the two edges is * significant, the BC patch will be split and this vertex will be * written in both patches with different normals. If the angle is * small, or only one GEdge belongs to this zone, then the vertex will * only be written to one patch. *--------------------------------------------------------------------*/ { // Find edge entities that are connected to elements in the zone std::vector<std::pair<GEdge *, int> > useGEdge; const int nFace = faces.size(); for(int iFace = 0; iFace != nFace; ++iFace) { if(zoneIndex == faces[iFace].zoneIndex) { MEdge mEdge = faces[iFace].parentElement->getEdge(faces[iFace].parentFace); // Get the other vertex on the mesh edge. MVertex *vertex2 = mEdge.getMinVertex(); if(vertex2 == vertex) vertex2 = mEdge.getMaxVertex(); // Check if the entity associated with vertex2 is a line and // is also connected to vertex. If so, add it to the container of // edge entities that will be used to determine the normal GEntity *const ent2 = vertex2->onWhat(); if(ent2->dim() == 1) { useGEdge.push_back( std::pair<GEdge *, int>(static_cast<GEdge *>(ent2), iFace)); } } } //--'useGEdge' now contains the edge entities that will be used to // determine //--the normals switch(useGEdge.size()) { case 0: // goto getNormalFromElements; // We probably don't want BC data if none of the faces attatched to // this vertex and in this zone are on the boundary. break; case 1: { const GEdge *const gEdge = static_cast<const GEdge *>(useGEdge[0].first); SVector3 boNormal; if(edge_normal(vertex, zoneIndex, gEdge, faces, boNormal)) goto getNormalFromElements; zoneBoVec.push_back(VertexBoundary(zoneIndex, gEdge->tag(), boNormal, const_cast<MVertex *>(vertex), vertIndex)); patch.add(gEdge->tag()); } break; case 2: { // Get the first normal const GEdge *const gEdge1 = static_cast<const GEdge *>(useGEdge[0].first); SVector3 boNormal1; if(edge_normal(vertex, zoneIndex, gEdge1, faces, boNormal1, useGEdge[0].second)) goto getNormalFromElements; // Get the second normal const GEdge *const gEdge2 = static_cast<const GEdge *>(useGEdge[1].first); SVector3 boNormal2; if(edge_normal(vertex, zoneIndex, gEdge2, faces, boNormal2, useGEdge[1].second)) goto getNormalFromElements; if(dot(boNormal1, boNormal2) < 0.98) { //--Write 2 separate patches zoneBoVec.push_back( VertexBoundary(zoneIndex, gEdge1->tag(), boNormal1, const_cast<MVertex *>(vertex), vertIndex)); patch.add(gEdge1->tag()); zoneBoVec.push_back( VertexBoundary(zoneIndex, gEdge2->tag(), boNormal2, const_cast<MVertex *>(vertex), vertIndex)); patch.add(gEdge2->tag()); } else { //--Write one patch boNormal1 += boNormal2; boNormal1 *= 0.5; zoneBoVec.push_back( VertexBoundary(zoneIndex, gEdge1->tag(), boNormal1, const_cast<MVertex *>(vertex), vertIndex)); patch.addPair(gEdge1->tag(), gEdge2->tag()); } } break; default: Msg::Error("Internal error based on 2-D boundary assumptions (file " "\'MZoneBoundary.cpp'). Boundary vertices may be " "incorrect"); break; } } break; case 1: /*--------------------------------------------------------------------* * The vertex exists on an edge and belongs to only 1 BC patch. *--------------------------------------------------------------------*/ { SVector3 boNormal; if(edge_normal(vertex, zoneIndex, static_cast<const GEdge *>(ent), faces, boNormal)) goto getNormalFromElements; zoneBoVec.push_back(VertexBoundary(zoneIndex, ent->tag(), boNormal, const_cast<MVertex *>(vertex), vertIndex)); patch.add(ent->tag()); } break; default: goto getNormalFromElements; } } return; getNormalFromElements:; /*--------------------------------------------------------------------* * Geometry information cannot be used - generate normals from the * elements *--------------------------------------------------------------------*/ { if(warnNormFromElem && normalSource == 1) { Msg::Warning("Some or all boundary normals were determined from mesh " "elements instead of from the geometry"); warnNormFromElem = false; } // The mesh plane normal is computed from all elements attached to the // vertex SVector3 meshPlaneNormal(0.); // This normal is perpendicular to the // plane of the mesh const int nFace = faces.size(); for(int iFace = 0; iFace != nFace; ++iFace) { if(faces[iFace].zoneIndex == zoneIndex) { // Make sure all the planes go in the same direction //**Required? SVector3 mpnt = faces[iFace].parentElement->getFace(0).normal(); if(dot(mpnt, meshPlaneNormal) < 0.) mpnt.negate(); meshPlaneNormal += mpnt; } } // Sum the normals from each element. The tangent is computed from all // faces in the zone attached to the vertex and is weighted by the length of // the edge. Each tangent has to be converted independently into an // inwards-pointing normal. SVector3 boNormal(0.); for(int iFace = 0; iFace != nFace; ++iFace) { if(faces[iFace].zoneIndex == zoneIndex) { const SVector3 tangent = faces[iFace] .parentElement->getEdge(faces[iFace].parentFace) .tangent(); // Normal to the boundary (unknown direction) SVector3 bnt = crossprod(tangent, meshPlaneNormal); // Inwards normal const SVector3 inwards(vertex->point(), faces[iFace].parentElement->barycenter()); if(dot(bnt, inwards) < 0.) bnt.negate(); boNormal += bnt; } } boNormal.normalize(); zoneBoVec.push_back(VertexBoundary( zoneIndex, 0, boNormal, const_cast<MVertex *>(vertex), vertIndex)); patch.add(0); } }