diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H index cde15d06621414b1d4eb3c1604e9ea198d15a42b..f9648dc1e7749c6cd78f444c5a872d8aefbc6fe2 100644 --- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H +++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMesh.H @@ -536,6 +536,11 @@ private: const Delaunay::Finite_edges_iterator& eit ) const; + boolList dualFaceBoundaryPoints + ( + const Delaunay::Finite_edges_iterator& eit + ) const; + //- Finds the maximum filterCount of the dual vertices // (Delaunay cells) that form the dual face produced by the // supplied edge @@ -842,6 +847,13 @@ private: const Delaunay::Finite_facets_iterator& fit ) const; + //- Merge adjacent edges that are not attached to other faces + label mergeNearlyParallelEdges + ( + const pointField& pts, + const scalar maxCosAngle + ); + //- Merge vertices that are very close together void mergeCloseDualVertices ( diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C index 7cc8957de09059bfcccf443f1ca48f886cdba376..85b4ba7b27af55cdb62988913931f71464cbc992 100644 --- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C +++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshCalcDualMesh.C @@ -212,6 +212,20 @@ void Foam::conformalVoronoiMesh::calcDualMesh Info<< nl << "Collapsing unnecessary faces" << endl; collapseFaces(points, boundaryPts, deferredCollapseFaces); + + const scalar maxCosAngle + = cos(degToRad(cvMeshControls().edgeMergeAngle())); + + Info<< nl << "Merging adjacent edges which have an angle " + << "of greater than " + << cvMeshControls().edgeMergeAngle() << ": " << endl; + + label nRemovedEdges = + mergeNearlyParallelEdges(points, maxCosAngle); + + reduce(nRemovedEdges, sumOp<label>()); + + Info<< " Merged " << nRemovedEdges << " edges" << endl; } labelHashSet wrongFaces = checkPolyMeshQuality(points); @@ -652,6 +666,113 @@ Foam::label Foam::conformalVoronoiMesh::mergeCloseDualVertices } +Foam::label Foam::conformalVoronoiMesh::mergeNearlyParallelEdges +( + const pointField& pts, + const scalar maxCosAngle +) +{ + List<HashSet<label> > pointFaceCount(number_of_cells()); + labelList pointNeighbour(number_of_cells(), -1); + + Map<label> dualPtIndexMap; + + for + ( + Delaunay::Finite_edges_iterator eit = finite_edges_begin(); + eit != finite_edges_end(); + ++eit + ) + { + Cell_handle c = eit->first; + Vertex_handle vA = c->vertex(eit->second); + Vertex_handle vB = c->vertex(eit->third); + + if (isBoundaryDualFace(eit)) + { + const face f = buildDualFace(eit); + + forAll(f, pI) + { + const label pIndex = f[pI]; + + const label prevPointI = f.prevLabel(pI); + const label nextPointI = f.nextLabel(pI); + + pointFaceCount[pIndex].insert(prevPointI); + pointFaceCount[pIndex].insert(nextPointI); + pointNeighbour[pIndex] = nextPointI; + } + } + else if + ( + vA->internalOrBoundaryPoint() + || vB->internalOrBoundaryPoint() + ) + { + const face f = buildDualFace(eit); + const boolList faceBoundaryPoints = dualFaceBoundaryPoints(eit); + + forAll(f, pI) + { + const label pIndex = f[pI]; + + const label prevPointI = f.prevLabel(pI); + const label nextPointI = f.nextLabel(pI); + + pointFaceCount[pIndex].insert(prevPointI); + pointFaceCount[pIndex].insert(nextPointI); + + if (faceBoundaryPoints[pI] == false) + { + pointNeighbour[pIndex] = nextPointI; + } + else + { + if (faceBoundaryPoints[prevPointI] == true) + { + pointNeighbour[pIndex] = prevPointI; + } + else if (faceBoundaryPoints[nextPointI] == true) + { + pointNeighbour[pIndex] = nextPointI; + } + else + { + pointNeighbour[pIndex] = pIndex; + } + } + } + } + } + + forAll(pointFaceCount, pI) + { + if (pointFaceCount[pI].size() == 2) + { + List<vector> edges(2, vector(0, 0, 0)); + + label count = 0; + forAllConstIter(HashSet<label>, pointFaceCount[pI], iter) + { + edges[count] = pts[pI] - pts[iter.key()]; + edges[count] /= mag(edges[count]) + VSMALL; + count++; + } + + if (mag(edges[0] & edges[1]) > maxCosAngle) + { + dualPtIndexMap.insert(pI, pointNeighbour[pI]); + } + } + } + + reindexDualVertices(dualPtIndexMap); + + return dualPtIndexMap.size(); +} + + void Foam::conformalVoronoiMesh::smoothSurface ( pointField& pts, @@ -696,7 +817,6 @@ void Foam::conformalVoronoiMesh::smoothSurface } while (nCollapsedFaces > 0); // Force all points of boundary faces to be on the surface - for ( Delaunay::Finite_cells_iterator cit = finite_cells_begin(); @@ -917,6 +1037,7 @@ void Foam::conformalVoronoiMesh::collapseFaces mergeCloseDualVertices(pts, boundaryPts); + if (nCollapsedFaces > 0) { Info<< " Collapsed " << nCollapsedFaces << " faces" << endl; @@ -932,6 +1053,7 @@ void Foam::conformalVoronoiMesh::collapseFaces } } while (nCollapsedFaces > 0); + } @@ -1064,6 +1186,25 @@ Foam::conformalVoronoiMesh::collapseFace bool allowEarlyCollapseToPoint = true; + +// // Quick exit +// label smallEdges = 0; +// const edgeList& fEdges = f.edges(); +// forAll(fEdges, eI) +// { +// const edge& e = fEdges[eI]; +// +// if (e.mag(pts) < 0.2*targetFaceSize) +// { +// smallEdges++; +// } +// } +// if (smallEdges == 0) +// { +// return fcmNone; +// } + + // if (maxFC > cvMeshControls().filterCountSkipThreshold() - 3) // { // limitToQuadsOrTris = true; @@ -1071,6 +1212,7 @@ Foam::conformalVoronoiMesh::collapseFace // allowEarlyCollapseToPoint = false; // } + collapseSizeLimitCoeff *= pow ( cvMeshControls().filterErrorReductionCoeff(), @@ -1158,6 +1300,91 @@ Foam::conformalVoronoiMesh::collapseFace } } + + scalar maxDist = 0; + scalar minDist = GREAT; +// +// if (f.size() <= 3) +// { +// const edgeList& fEdges = f.edges(); +// +// forAll(fEdges, eI) +// { +// const edge& e = fEdges[eI]; +// const scalar d = e.mag(pts); +// +// if (d > maxDist) +// { +// maxDist = d; +// collapseAxis = e.vec(pts); +// } +// else if (d < minDist && d != 0) +// { +// minDist = d; +// } +// } +// } +// else +// { +// forAll(f, pI) +// { +// for (label i = pI + 1; i < f.size(); ++i) +// { +// if +// ( +// f[i] != f.nextLabel(pI) +// && f[i] != f.prevLabel(pI) +// ) +// { +// scalar d = mag(pts[f[pI]] - pts[f[i]]); +// +// if (d > maxDist) +// { +// maxDist = d; +// collapseAxis = pts[f[pI]] - pts[f[i]]; +// } +// else if (d < minDist && d != 0) +// { +// minDist = d; +// } +// } +// } +// } +// } +// +// const edgeList& fEdges = f.edges(); +// +// scalar perimeter = 0; +// +// forAll(fEdges, eI) +// { +// const edge& e = fEdges[eI]; +// const scalar d = e.mag(pts); +// +// perimeter += d; +// +//// collapseAxis += e.vec(pts); +// +// if (d > maxDist) +// { +// collapseAxis = e.vec(pts); +// maxDist = d; +// } +// else if (d < minDist && d != 0) +// { +// minDist = d; +// } +// } +// +// collapseAxis /= mag(collapseAxis); +// +//// Info<< f.size() << " " << minDist << " " << maxDist << " | " +//// << collapseAxis << endl; +// +// aspectRatio = maxDist/minDist; +// +// aspectRatio = min(aspectRatio, sqr(perimeter)/(16.0*fA)); + if (magSqr(collapseAxis) < VSMALL) { WarningIn @@ -1170,9 +1397,9 @@ Foam::conformalVoronoiMesh::collapseFace // Output face and collapse axis for visualisation Pout<< "# Aspect ratio = " << aspectRatio << nl - << "# inertia = " << J << nl - << "# determinant = " << detJ << nl - << "# eigenvalues = " << eigenValues(J) << nl +// << "# inertia = " << J << nl +// << "# determinant = " << detJ << nl +// << "# eigenvalues = " << eigenValues(J) << nl << "# collapseAxis = " << collapseAxis << nl << "# facePts = " << facePts << nl << endl; @@ -1280,8 +1507,6 @@ Foam::conformalVoronoiMesh::collapseFace { scalar guardFraction = cvMeshControls().edgeCollapseGuardFraction(); - cvMeshControls().maxCollapseFaceToPointSideLengthCoeff(); - if ( allowEarlyCollapseToPoint @@ -1337,6 +1562,8 @@ Foam::conformalVoronoiMesh::collapseFace Foam::point collapseToPt = collapseAxis*(sum(dNeg)/dNeg.size() - dShift) + fC; +// DynamicList<label> faceBoundaryPts(f.size()); + forAll(facePtsNeg, fPtI) { if (boundaryPts[facePtsNeg[fPtI]] == true) @@ -1351,9 +1578,32 @@ Foam::conformalVoronoiMesh::collapseFace collapseToPt = pts[collapseToPtI]; break; + +// faceBoundaryPts.append(facePtsNeg[fPtI]); } } +// if (!faceBoundaryPts.empty()) +// { +// if (faceBoundaryPts.size() == 2) +// { +// collapseToPtI = faceBoundaryPts[0]; +// +// collapseToPt = +// 0.5*(pts[faceBoundaryPts[0]] + pts[faceBoundaryPts[1]]); +// } +// else if (faceBoundaryPts.size() < f.size()) +// { +// face bFace(faceBoundaryPts); +// +// collapseToPtI = faceBoundaryPts.first(); +// +// collapseToPt = bFace.centre(pts); +// } +// } +// +// faceBoundaryPts.clear(); + // ...otherwise arbitrarily choosing the most distant // point as the index to collapse to. @@ -1384,9 +1634,30 @@ Foam::conformalVoronoiMesh::collapseFace collapseToPt = pts[collapseToPtI]; break; + +// faceBoundaryPts.append(facePtsNeg[fPtI]); } } +// if (!faceBoundaryPts.empty()) +// { +// if (faceBoundaryPts.size() == 2) +// { +// collapseToPtI = faceBoundaryPts[0]; +// +// collapseToPt = +// 0.5*(pts[faceBoundaryPts[0]] + pts[faceBoundaryPts[1]]); +// } +// else if (faceBoundaryPts.size() < f.size()) +// { +// face bFace(faceBoundaryPts); +// +// collapseToPtI = faceBoundaryPts.first(); +// +// collapseToPt = bFace.centre(pts); +// } +// } + // ...otherwise arbitrarily choosing the most distant // point as the index to collapse to. @@ -1406,6 +1677,8 @@ Foam::conformalVoronoiMesh::collapseFace Foam::point collapseToPt = fC; + DynamicList<label> faceBoundaryPts(f.size()); + forAll(facePts, fPtI) { if (boundaryPts[facePts[fPtI]] == true) @@ -1415,11 +1688,32 @@ Foam::conformalVoronoiMesh::collapseFace // use the first boundary point encountered if // there are multiple boundary points. - collapseToPtI = facePts[fPtI]; +// collapseToPtI = facePts[fPtI]; +// +// collapseToPt = pts[collapseToPtI]; +// +// break; - collapseToPt = pts[collapseToPtI]; + faceBoundaryPts.append(facePts[fPtI]); + } + } - break; + if (!faceBoundaryPts.empty()) + { + if (faceBoundaryPts.size() == 2) + { + collapseToPtI = faceBoundaryPts[0]; + + collapseToPt = + 0.5*(pts[faceBoundaryPts[0]] + pts[faceBoundaryPts[1]]); + } + else if (faceBoundaryPts.size() < f.size()) + { + face bFace(faceBoundaryPts); + + collapseToPtI = faceBoundaryPts.first(); + + collapseToPt = bFace.centre(pts); } } @@ -1858,7 +2152,10 @@ Foam::labelHashSet Foam::conformalVoronoiMesh::findOffsetPatchFaces } } - offsetBoundaryCells.write(); + if (cvMeshControls().objOutput()) + { + offsetBoundaryCells.write(); + } return offsetBoundaryCells; } @@ -2092,13 +2389,7 @@ void Foam::conformalVoronoiMesh::indexDualVertices pts[dualVertI] = cit->dual(); - if - ( - !cit->vertex(0)->internalOrBoundaryPoint() - || !cit->vertex(1)->internalOrBoundaryPoint() - || !cit->vertex(2)->internalOrBoundaryPoint() - || !cit->vertex(3)->internalOrBoundaryPoint() - ) + if (cit->boundaryDualVertex()) { // This is a boundary dual vertex boundaryPts[dualVertI] = true; diff --git a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H index 1e24b7d8ef29cf030425647cfc70fc03f50bf26a..43d8c077fc785724ad83b37ac8e48a7a76f77ba9 100644 --- a/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H +++ b/applications/utilities/mesh/generation/cvMesh/conformalVoronoiMesh/conformalVoronoiMesh/conformalVoronoiMeshI.H @@ -366,6 +366,49 @@ inline bool Foam::conformalVoronoiMesh::isBoundaryDualFace } +inline Foam::List<bool> Foam::conformalVoronoiMesh::dualFaceBoundaryPoints +( + const Delaunay::Finite_edges_iterator& eit +) const +{ + Cell_circulator ccStart = incident_cells(*eit); + Cell_circulator cc1 = ccStart; + Cell_circulator cc2 = cc1; + + // Advance the second circulator so that it always stays on the next + // cell around the edge; + cc2++; + + DynamicList<bool> tmpFaceBoundaryPoints; + + do + { + label cc1I = cc1->cellIndex(); + + label cc2I = cc2->cellIndex(); + + if (cc1I != cc2I) + { + if (cc1->boundaryDualVertex()) + { + tmpFaceBoundaryPoints.append(true); + } + else + { + tmpFaceBoundaryPoints.append(false); + } + } + + cc1++; + + cc2++; + + } while (cc1 != ccStart); + + return tmpFaceBoundaryPoints; +} + + inline Foam::List<Foam::label> Foam::conformalVoronoiMesh::processorsAttached ( const Delaunay::Finite_facets_iterator& fit