openfoam/applications/utilities/mesh/advanced/collapseEdges/collapseEdges.C

1441 lines
38 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2012 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Description
Collapse short edges and combines edges that are in line.
- collapse short edges. Length of edges to collapse provided as argument.
- merge two edges if they are in line. Maximum angle provided as argument.
- remove unused points.
Cannot remove cells. Can remove faces and points but does not check
for nonsense resulting topology.
When collapsing an edge with one point on the boundary it will leave
the boundary point intact. When both points inside it chooses random. When
both points on boundary random again. Note: it should in fact use features
where if one point is on a feature it collapses to that one. Alas we don't
have features on a polyMesh.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "polyTopoChange.H"
#include "polyTopoChanger.H"
#include "polyMesh.H"
#include "fvMesh.H"
#include "mapPolyMesh.H"
#include "mathematicalConstants.H"
#include "PackedBoolList.H"
#include "SortableList.H"
#include "unitConversion.H"
#include "globalMeshData.H"
#include "globalIndex.H"
#include "PointEdgeWave.H"
#include "pointEdgeCollapse.H"
#include "motionSmoother.H"
#include "OFstream.H"
#include "meshTools.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
label findIndex
(
const labelList& elems,
const label start,
const label size,
const label val
)
{
for (label i = start; i < size; i++)
{
if (elems[i] == val)
{
return i;
}
}
return -1;
}
void filterFace
(
const label faceI,
face& f,
const List<pointEdgeCollapse>& allPointInfo,
const Map<DynamicList<label> >& collapseStrings
)
{
label newFp = 0;
face oldFace = f;
forAll(f, fp)
{
label pointI = f[fp];
label collapsePoint = allPointInfo[pointI].collapseIndex();
if (collapseStrings.found(collapsePoint))
{
collapsePoint = collapseStrings[collapsePoint][0];
}
if (collapsePoint == -1)
{
WarningIn
(
"filterFace"
"(const label, face&, const List<pointEdgeCollapse>&)"
)
<< "Point " << pointI << " was not visited by PointEdgeWave"
<< endl;
}
else if (collapsePoint == -2)
{
f[newFp++] = pointI;
}
else
{
if (findIndex(f, 0, newFp, collapsePoint) == -1)
{
f[newFp++] = collapsePoint;
}
}
}
// Check for pinched face. Tries to correct
// - consecutive duplicate vertex. Removes duplicate vertex.
// - duplicate vertex with one other vertex in between (spike).
// Both of these should not really occur! and should be checked before
// collapsing edges.
const label size = newFp;
newFp = 2;
for (label fp = 2; fp < size; fp++)
{
label fp1 = fp-1;
label fp2 = fp-2;
label pointI = f[fp];
// Search for previous occurrence.
label index = findIndex(f, 0, fp, pointI);
if (index == fp1)
{
WarningIn
(
"Foam::edgeCollapser::filterFace(const label faceI, "
"face& f) const"
) << "Removing consecutive duplicate vertex in face "
<< f << endl;
// Don't store current pointI
}
else if (index == fp2)
{
WarningIn
(
"Foam::edgeCollapser::filterFace(const label faceI, "
"face& f) const"
) << "Removing non-consecutive duplicate vertex in face "
<< f << endl;
// Don't store current pointI and remove previous
newFp--;
}
else if (index != -1)
{
WarningIn
(
"Foam::edgeCollapser::filterFace(const label faceI, "
"face& f) const"
) << "Pinched face " << f << endl;
f[newFp++] = pointI;
}
else
{
f[newFp++] = pointI;
}
}
f.setSize(newFp);
}
bool setRefinement
(
const polyMesh& mesh,
polyTopoChange& meshMod,
const List<pointEdgeCollapse>& allPointInfo
)
{
const cellList& cells = mesh.cells();
const labelList& faceOwner = mesh.faceOwner();
const labelList& faceNeighbour = mesh.faceNeighbour();
const labelListList& pointFaces = mesh.pointFaces();
const pointZoneMesh& pointZones = mesh.pointZones();
globalIndex globalStrings(mesh.nPoints());
boolList removedPoints(mesh.nPoints(), false);
// Create strings of edges
Map<DynamicList<label> > collapseStrings;
forAll(allPointInfo, pointI)
{
const label collapseIndex = allPointInfo[pointI].collapseIndex();
if (collapseIndex != -1 && collapseIndex != -2)
{
collapseStrings(collapseIndex).append(pointI);
}
}
bool meshChanged = false;
// Current faces (is also collapseStatus: f.size() < 3)
faceList newFaces(mesh.faces());
// Current cellCollapse status
boolList cellRemoved(mesh.nCells(), false);
label nUnvisited = 0;
label nUncollapsed = 0;
label nCollapsed = 0;
forAll(allPointInfo, pI)
{
const pointEdgeCollapse& pec = allPointInfo[pI];
if (pec.collapseIndex() == -1)
{
nUnvisited++;
}
else if (pec.collapseIndex() == -2)
{
nUncollapsed++;
}
else if (pec.collapseIndex() >= 0)
{
nCollapsed++;
}
}
label nPoints = allPointInfo.size();
reduce(nPoints, sumOp<label>());
reduce(nUnvisited, sumOp<label>());
reduce(nUncollapsed, sumOp<label>());
reduce(nCollapsed, sumOp<label>());
Info<< incrIndent;
Info<< indent << "Number of points : " << nPoints << nl
<< indent << "Not visited : " << nUnvisited << nl
<< indent << "Not collapsed : " << nUncollapsed << nl
<< indent << "Collapsed : " << nCollapsed << nl
<< endl;
Info<< decrIndent;
do
{
// Update face collapse from edge collapses
forAll(newFaces, faceI)
{
filterFace(faceI, newFaces[faceI], allPointInfo, collapseStrings);
}
// Check if faces to be collapsed cause cells to become collapsed.
label nCellCollapsed = 0;
forAll(cells, cellI)
{
if (!cellRemoved[cellI])
{
const cell& cFaces = cells[cellI];
label nFaces = cFaces.size();
forAll(cFaces, i)
{
label faceI = cFaces[i];
if (newFaces[faceI].size() < 3)
{
--nFaces;
if (nFaces < 4)
{
Info<< "Cell:" << cellI
<< " uses faces:" << cFaces
<< " of which too many are marked for removal:"
<< endl
<< " ";
forAll(cFaces, j)
{
if (newFaces[cFaces[j]].size() < 3)
{
Info<< ' '<< cFaces[j];
}
}
Info<< endl;
cellRemoved[cellI] = true;
// Collapse all edges of cell to nothing
//collapseEdges(cellEdges[cellI]);
nCellCollapsed++;
break;
}
}
}
}
}
if (nCellCollapsed == 0)
{
break;
}
} while (true);
// Keep track of faces that have been done already.
boolList doneFace(mesh.nFaces(), false);
{
// Mark points used.
boolList usedPoint(mesh.nPoints(), false);
forAll(cellRemoved, cellI)
{
if (cellRemoved[cellI])
{
meshMod.removeCell(cellI, -1);
}
}
// Remove faces
forAll(newFaces, faceI)
{
const face& f = newFaces[faceI];
if (f.size() < 3)
{
meshMod.removeFace(faceI, -1);
meshChanged = true;
// Mark face as been done.
doneFace[faceI] = true;
}
else
{
// Kept face. Mark vertices
forAll(f, fp)
{
usedPoint[f[fp]] = true;
}
}
}
// Remove unused vertices that have not been marked for removal already
forAll(usedPoint, pointI)
{
if (!usedPoint[pointI])
{
removedPoints[pointI] = true;
meshMod.removePoint(pointI, -1);
meshChanged = true;
}
}
}
// Modify the point location of the remaining points
forAll(allPointInfo, pointI)
{
const label collapseIndex = allPointInfo[pointI].collapseIndex();
const point& collapsePoint = allPointInfo[pointI].collapsePoint();
if
(
removedPoints[pointI] == false
&& collapseIndex != -1
&& collapseIndex != -2
)
{
meshMod.modifyPoint
(
pointI,
collapsePoint,
pointZones.whichZone(pointI),
false
);
}
}
const polyBoundaryMesh& boundaryMesh = mesh.boundaryMesh();
const faceZoneMesh& faceZones = mesh.faceZones();
// Renumber faces that use points
forAll(allPointInfo, pointI)
{
if (removedPoints[pointI] == true)
{
const labelList& changedFaces = pointFaces[pointI];
forAll(changedFaces, changedFaceI)
{
label faceI = changedFaces[changedFaceI];
if (!doneFace[faceI])
{
doneFace[faceI] = true;
// Get current zone info
label zoneID = faceZones.whichZone(faceI);
bool zoneFlip = false;
if (zoneID >= 0)
{
const faceZone& fZone = faceZones[zoneID];
zoneFlip = fZone.flipMap()[fZone.whichFace(faceI)];
}
// Get current connectivity
label own = faceOwner[faceI];
label nei = -1;
label patchID = -1;
if (mesh.isInternalFace(faceI))
{
nei = faceNeighbour[faceI];
}
else
{
patchID = boundaryMesh.whichPatch(faceI);
}
meshMod.modifyFace
(
newFaces[faceI], // face
faceI, // faceI to change
own, // owner
nei, // neighbour
false, // flipFaceFlux
patchID, // patch
zoneID,
zoneFlip
);
meshChanged = true;
}
}
}
}
// Print regions:
// printRegions();
return meshChanged;
}
// Get faceEdges in order of face points, i.e. faceEdges[0] is between
// f[0] and f[1]
labelList getSortedEdges
(
const edgeList& edges,
const labelList& f,
const labelList& edgeLabels
)
{
labelList faceEdges(edgeLabels.size(), -1);
// Find starting pos in f for every edgeLabels
forAll(edgeLabels, i)
{
label edgeI = edgeLabels[i];
const edge& e = edges[edgeI];
label fp = findIndex(f, e[0]);
label fp1 = f.fcIndex(fp);
if (f[fp1] == e[1])
{
// EdgeI between fp -> fp1
faceEdges[fp] = edgeI;
}
else
{
// EdgeI between fp-1 -> fp
faceEdges[f.rcIndex(fp)] = edgeI;
}
}
return faceEdges;
}
// Merges edges which are in straight line. I.e. edge split by point.
//label mergeEdges
//(
// const polyMesh& mesh,
// const scalar maxCos,
// List<pointEdgeCollapse>& allPointInfo
//)
//{
// const pointField& points = mesh.points();
// const edgeList& edges = mesh.edges();
// const labelListList& pointEdges = mesh.pointEdges();
// const labelList& region = collapser.pointRegion();
// const labelList& master = collapser.pointRegionMaster();
//
// label nCollapsed = 0;
//
// forAll(pointEdges, pointI)
// {
// const labelList& pEdges = pointEdges[pointI];
//
// if (pEdges.size() == 2)
// {
// const edge& leftE = edges[pEdges[0]];
// const edge& rightE = edges[pEdges[1]];
//
// // Get the two vertices on both sides of the point
// label leftV = leftE.otherVertex(pointI);
// label rightV = rightE.otherVertex(pointI);
//
// // Collapse only if none of the points part of merge network
// // or all of networks with different masters.
// label midMaster = -1;
// if (region[pointI] != -1)
// {
// midMaster = master[region[pointI]];
// }
//
// label leftMaster = -2;
// if (region[leftV] != -1)
// {
// leftMaster = master[region[leftV]];
// }
//
// label rightMaster = -3;
// if (region[rightV] != -1)
// {
// rightMaster = master[region[rightV]];
// }
//
// if
// (
// midMaster != leftMaster
// && midMaster != rightMaster
// && leftMaster != rightMaster
// )
// {
// // Check if the two edge are in line
// vector leftVec = points[pointI] - points[leftV];
// leftVec /= mag(leftVec) + VSMALL;
//
// vector rightVec = points[rightV] - points[pointI];
// rightVec /= mag(rightVec) + VSMALL;
//
// if ((leftVec & rightVec) > maxCos)
// {
// // Collapse one (left) side of the edge. Make left vertex
// // the master.
// //if (collapser.unaffectedEdge(pEdges[0]))
// const edge& e = mesh.edges()[pEdges[0]];
//
// if
// (
// allPointInfo[e[0]].collapseIndex() < 0
// && allPointInfo[e[1]].collapseIndex() < 0
// )
// {
// //pointEdgeCollapse pec(mesh.points()[leftV], leftV);
//
// allPointInfo[e[0]] = pec;
// allPointInfo[e[1]] = pec;
//
// //collapser.collapseEdge(pEdges[0], leftV);
// nCollapsed++;
// }
// }
// }
// }
// }
//
// return nCollapsed;
//}
// Return master point edge needs to be collapsed to (or -1)
label edgeMaster
(
const labelList& boundaryPoint,
const bool flipEdge,
const edge& e
)
{
label masterPoint = -1;
label e0 = e[0];
label e1 = e[1];
if (flipEdge)
{
e0 = e[1];
e1 = e[0];
}
// Check if one of the points is on a processor
// if
// (
// boundaryPoint[e0] > 0
// && boundaryPoint[e1] > 0
// )
// {
// if (boundaryPoint[e0] != boundaryPoint[e1])
// {
// return -1;
// }
// }
//
// if (boundaryPoint[e0] > 0)
// {
// return e0;
// }
// else if (boundaryPoint[e1] > 0)
// {
// return e1;
// }
// Collapse edge to boundary point.
if (boundaryPoint[e0] == 0)
{
if (boundaryPoint[e1] == 0)
{
// Both points on boundary. Choose one to collapse to.
// Note: should look at feature edges/points!
masterPoint = e0;
}
else
{
masterPoint = e0;
}
}
else
{
if (boundaryPoint[e1] == 0)
{
masterPoint = e1;
}
else
{
// None on boundary. Choose arbitrary.
// Note: should look at geometry?
masterPoint = e0;
}
}
return masterPoint;
}
label collapseSmallEdges
(
const polyMesh& mesh,
const scalarList& freezeEdges,
const labelList& boundaryPoint,
const scalar minLen,
List<pointEdgeCollapse>& allPointInfo
)
{
const pointField& points = mesh.points();
const edgeList& edges = mesh.edges();
// Store collapse direction in collapseEdge
// -1 -> Do not collapse
// 0 -> Collapse to start point
// 1 -> Collapse to end point
labelList collapseEdge(edges.size(), -1);
forAll(edges, edgeI)
{
const edge& e = edges[edgeI];
if (e.mag(points) < minLen*freezeEdges[edgeI])
{
collapseEdge[edgeI] = 0;
}
}
// Check whether edge point order is reversed from mesh to coupledPatch
// const globalMeshData& globalData = mesh.globalData();
// const mapDistribute& map = globalData.globalEdgeSlavesMap();
// const labelList& coupledMeshEdges = globalData.coupledPatchMeshEdges();
// const indirectPrimitivePatch& coupledPatch = globalData.coupledPatch();
// const PackedBoolList& cppOrientation = globalData.globalEdgeOrientation();
// PackedBoolList meshToPatchSameOrientation(coupledMeshEdges.size(), true);
//
// forAll(coupledMeshEdges, eI)
// {
// const label meshEdgeIndex = coupledMeshEdges[eI];
//
// if (collapseEdge[meshEdgeIndex] != -1)
// {
// const edge& meshEdge = edges[meshEdgeIndex];
// const edge& coupledPatchEdge = coupledPatch.edges()[eI];
//
// if
// (
// meshEdge[0] == coupledPatch.meshPoints()[coupledPatchEdge[1]]
// && meshEdge[1] == coupledPatch.meshPoints()[coupledPatchEdge[0]]
// )
// {
// meshToPatchSameOrientation[eI] = false;
// }
// }
// }
//
//
// labelList cppEdgeData(coupledMeshEdges.size(), -1);
//
// forAll(coupledMeshEdges, eI)
// {
// const label meshEdgeIndex = coupledMeshEdges[eI];
//
// if (collapseEdge[meshEdgeIndex] != -1)
// {
// if (meshToPatchSameOrientation[eI] == cppOrientation[eI])
// {
// cppEdgeData[eI] = 0;
// }
// else
// {
// cppEdgeData[eI] = 1;
// }
// }
// }
//
//
// // Synchronise cppEdgeData
// // Use minEqOp reduction, so that edge will only be collapsed on processor
// // boundary if both processors agree to collapse it
// globalData.syncData
// (
// cppEdgeData,
// globalData.globalEdgeSlaves(),
// globalData.globalEdgeTransformedSlaves(),
// map,
// minEqOp<label>()
// );
//
//
// forAll(coupledMeshEdges, eI)
// {
// const label meshEdgeIndex = coupledMeshEdges[eI];
//
// if (collapseEdge[meshEdgeIndex] != -1)
// {
// if (meshToPatchSameOrientation[eI] == cppOrientation[eI])
// {
// collapseEdge[meshEdgeIndex] = 0;
// }
// else
// {
// collapseEdge[meshEdgeIndex] = 1;
// }
// }
// }
label nCollapsed = 0;
DynamicList<label> initPoints(mesh.nPoints());
DynamicList<pointEdgeCollapse> initPointInfo(mesh.nPoints());
allPointInfo.resize(mesh.nPoints());
globalIndex globalStrings(mesh.nPoints());
List<pointEdgeCollapse> allEdgeInfo(mesh.nEdges());
forAll(allEdgeInfo, edgeI)
{
allEdgeInfo[edgeI] = pointEdgeCollapse(vector::zero, -1);
}
forAll(edges, edgeI)
{
const edge& e = edges[edgeI];
if (collapseEdge[edgeI] != -1)
{
const label master =
edgeMaster
(
boundaryPoint,
collapseEdge[edgeI],
e
);
// if (master != -1)
{
pointEdgeCollapse pec
(
points[master],
globalStrings.toGlobal(master)
);
allEdgeInfo[edgeI] = pec;
initPointInfo.append(pec);
initPoints.append(e.start());
initPointInfo.append(pec);
initPoints.append(e.end());
nCollapsed++;
}
}
}
PointEdgeWave<pointEdgeCollapse> collapsePropagator
(
mesh,
initPoints,
initPointInfo,
allPointInfo,
allEdgeInfo,
mesh.globalData().nTotalPoints() // Maximum number of iterations
);
return nCollapsed;
}
// Faces which have edges just larger than collapse length but faces which
// are very small. This one tries to collapse them if it can be done with
// edge collapse. For faces where a face gets replace by two edges use
// collapseFaces
//label collapseHighAspectFaces
//(
// const polyMesh& mesh,
// const PackedBoolList& boundaryPoint,
// const Map<label>& processorPoints,
// const scalar areaFac,
// const scalar edgeRatio,
// edgeCollapser& collapser
//)
//{
// const pointField& points = mesh.points();
// const edgeList& edges = mesh.edges();
// const faceList& faces = mesh.faces();
// const labelListList& faceEdges = mesh.faceEdges();
//
// scalarField magArea(mag(mesh.faceAreas()));
//
// label maxIndex = findMax(magArea);
//
// scalar minArea = areaFac * magArea[maxIndex];
//
// Info<< "Max face area:" << magArea[maxIndex] << endl
// << "Collapse area factor:" << areaFac << endl
// << "Collapse area:" << minArea << endl;
//
// label nCollapsed = 0;
//
// forAll(faces, faceI)
// {
// if (magArea[faceI] < minArea)
// {
// const face& f = faces[faceI];
//
// // Get the edges in face point order
// labelList fEdges(getSortedEdges(edges, f, faceEdges[faceI]));
//
// SortableList<scalar> lengths(fEdges.size());
// forAll(fEdges, i)
// {
// lengths[i] = edges[fEdges[i]].mag(points);
// }
// lengths.sort();
//
//
// label edgeI = -1;
//
// if (f.size() == 4)
// {
// // Compare second largest to smallest
// if (lengths[2] > edgeRatio*lengths[0])
// {
// // Collapse smallest only. Triangle should be cleared
// // next time around.
// edgeI = fEdges[lengths.indices()[0]];
// }
// }
// else if (f.size() == 3)
// {
// // Compare second largest to smallest
// if (lengths[1] > edgeRatio*lengths[0])
// {
// edgeI = fEdges[lengths.indices()[0]];
// }
// }
//
//
// if (edgeI != -1)
// {
// label master = edgeMaster
// (
// boundaryPoint,
// processorPoints,
// false,
// edges[edgeI]
// );
//
// if (master != -1)// && collapser.unaffectedEdge(edgeI))
// {
// collapser.collapseEdge(edgeI, master);
// nCollapsed++;
// }
// }
// }
// }
//
// return nCollapsed;
//}
void set(const labelList& elems, const bool val, boolList& status)
{
forAll(elems, i)
{
status[elems[i]] = val;
}
}
// Tries to simplify polygons to face of minSize (4=quad, 3=triangle)
//label simplifyFaces
//(
// const polyMesh& mesh,
// const PackedBoolList& boundaryPoint,
// const Map<label>& processorPoints,
// const label minSize,
// const scalar lenGap,
// edgeCollapser& collapser
//)
//{
// const pointField& points = mesh.points();
// const edgeList& edges = mesh.edges();
// const faceList& faces = mesh.faces();
// const cellList& cells = mesh.cells();
// const labelListList& faceEdges = mesh.faceEdges();
// const labelList& faceOwner = mesh.faceOwner();
// const labelList& faceNeighbour = mesh.faceNeighbour();
// const labelListList& pointCells = mesh.pointCells();
// const labelListList& cellEdges = mesh.cellEdges();
//
// label nCollapsed = 0;
//
// boolList protectedEdge(mesh.nEdges(), false);
//
// forAll(faces, faceI)
// {
// const face& f = faces[faceI];
//
// if
// (
// f.size() > minSize
// && cells[faceOwner[faceI]].size() >= 6
// && (
// mesh.isInternalFace(faceI)
// && cells[faceNeighbour[faceI]].size() >= 6
// )
// )
// {
// // Get the edges in face point order
// labelList fEdges(getSortedEdges(edges, f, faceEdges[faceI]));
//
// SortableList<scalar> lengths(fEdges.size());
// forAll(fEdges, i)
// {
// lengths[i] = edges[fEdges[i]].mag(points);
// }
// lengths.sort();
//
//
// // Now find a gap in length between consecutive elements greater
// // than lenGap.
//
// label gapPos = -1;
//
// for (label i = f.size()-1-minSize; i >= 0; --i)
// {
// if (lengths[i+1] > lenGap*lengths[i])
// {
// gapPos = i;
//
// break;
// }
// }
//
// if (gapPos != -1)
// {
// //for (label i = gapPos; i >= 0; --i)
// label i = 0; // Hack: collapse smallest edge only.
// {
// label edgeI = fEdges[lengths.indices()[i]];
//
// if (!protectedEdge[edgeI])
// {
// const edge& e = edges[edgeI];
//
// label master
// = edgeMaster(boundaryPoint, processorPoints, false, e);
//
// if (master != -1)
// {
// collapser.collapseEdge(edgeI, master);
//
// // Protect all other edges on all cells using edge
// // points.
//
// const labelList& pCells0 = pointCells[e[0]];
//
// forAll(pCells0, i)
// {
// set(cellEdges[pCells0[i]], true, protectedEdge);
// }
// const labelList& pCells1 = pointCells[e[1]];
//
// forAll(pCells1, i)
// {
// set(cellEdges[pCells1[i]], true, protectedEdge);
// }
//
// nCollapsed++;
// }
// }
// }
// }
// }
// }
//
// return nCollapsed;
//}
labelHashSet checkMeshQuality(const polyMesh& mesh)
{
//mesh.checkMesh(true);
labelHashSet freezePoints;
IOdictionary meshQualityDict
(
IOobject
(
"meshQualityControls",
mesh.time().system(),
mesh.time(),
IOobject::MUST_READ,
IOobject::NO_WRITE
)
);
labelHashSet badFaces(mesh.nFaces()/100);
DynamicList<label> checkFaces(mesh.nFaces());
const vectorField& fAreas = mesh.faceAreas();
scalar faceAreaLimit = SMALL;
forAll(fAreas, fI)
{
if (mag(fAreas[fI]) > faceAreaLimit)
{
checkFaces.append(fI);
}
}
motionSmoother::checkMesh
(
false,
mesh,
meshQualityDict,
checkFaces,
badFaces
);
label nBadFaces = badFaces.size();
reduce(nBadFaces, sumOp<label>());
Info<< nl << "Number of bad faces : " << nBadFaces << endl;
forAllConstIter(labelHashSet, badFaces, iter)
{
const face& f = mesh.faces()[iter.key()];
forAll(f, pI)
{
freezePoints.insert(f[pI]);
}
}
// const edgeList& edges = mesh.edges();
//
// label nFrozenEdges = 0;
//
// OFstream str("frozenEdges.obj");
//
// label count = 0;
// forAll(edges, eI)
// {
// const edge& e = edges[eI];
//
// if (freezePoints.found(e[0]) && freezePoints.found(e[1]))
// {
// freezeEdges[eI] = true;
// nFrozenEdges++;
// }
// }
return freezePoints;
}
// Mark boundary points
// boundaryPoint:
// + -1 : point not on boundary
// + 0 : point on a real boundary
// + >0 : point on a processor patch with that ID
labelList findBoundaryPoints(const polyMesh& mesh)
{
const faceList& faces = mesh.faces();
const polyBoundaryMesh& bMesh = mesh.boundaryMesh();
labelList boundaryPoint(mesh.nPoints(), -1);
// Get all points on a boundary
label nIntFaces = mesh.nInternalFaces();
for (label faceI = nIntFaces; faceI < mesh.nFaces(); faceI++)
{
const face& f = faces[faceI];
forAll(f, fp)
{
boundaryPoint[f[fp]] = 0;
}
}
// Get all processor boundary points and the processor patch label
// that they are on.
forAll(bMesh, patchI)
{
const polyPatch& patch = bMesh[patchI];
if (isA<processorPolyPatch>(patch))
{
const processorPolyPatch& pPatch =
refCast<const processorPolyPatch>(patch);
forAll(pPatch, fI)
{
const face& f = pPatch[fI];
forAll(f, fp)
{
boundaryPoint[f[fp]] = patchI;
}
}
}
}
return boundaryPoint;
}
// Main program:
int main(int argc, char *argv[])
{
# include "addOverwriteOption.H"
argList::validArgs.append("edge length [m]");
argList::validArgs.append("merge angle (degrees)");
argList::addOption("minLenFactor", "scalar", "edge length factor");
# include "setRootCase.H"
# include "createTime.H"
runTime.functionObjects().off();
# include "createPolyMesh.H"
const word oldInstance = mesh.pointsInstance();
scalar minLen = args.argRead<scalar>(1);
const scalar angle = args.argRead<scalar>(2);
const scalar minLenFactor
= args.optionLookupOrDefault<scalar>("minLenFactor", 0.5);
const bool overwrite = args.optionFound("overwrite");
scalar maxCos = Foam::cos(degToRad(angle));
Info<< "Merging:" << nl
<< " edges with length less than " << minLen << " meters" << nl
<< " edges split by a point with edges in line to within " << angle
<< " degrees" << nl
<< endl;
Info<< "If an invalid mesh is generated then the edge length will be " << nl
<< "multiplied by a factor of " << minLenFactor << " and collapsing "
<< "will be reattempted" << nl << endl;
bool meshChanged = false;
checkMeshQuality(mesh);
autoPtr<fvMesh> fvMeshPtr;
scalarList freezeEdges(mesh.nEdges(), 1.0);
do
{
label nIterations = 0;
label nFrozenEdges = 0;
fvMeshPtr.reset(new fvMesh(mesh));
fvMesh& fvMeshRef = fvMeshPtr();
// Contains new point label for original points
labelList pointMap(identity(mesh.nPoints()));
scalarList tmpFreezeEdges = freezeEdges;
autoPtr<mapPolyMesh> morphMap;
while (true)
{
Info<< "Iteration " << nIterations << incrIndent << endl;
labelList boundaryPoint = findBoundaryPoints(fvMeshRef);
List<pointEdgeCollapse> allPointInfo;
// Collapse all edges that are too small.
label nSmallCollapsed =
collapseSmallEdges
(
fvMeshRef,
tmpFreezeEdges,
boundaryPoint,
minLen,
allPointInfo
);
reduce(nSmallCollapsed, sumOp<label>());
Info<< indent << "Collapsing " << nSmallCollapsed
<< " small edges" << endl;
label nMerged = 0;
// Remove midpoints on straight edges.
if (nSmallCollapsed == 0)
{
//nMerged = mergeEdges(fvMeshRef, maxCos, allPointInfo);
}
reduce(nMerged, sumOp<label>());
Info<< indent << "Collapsing " << nMerged << " in line edges"
<< endl;
label nSliversCollapsed = 0;
// Remove small sliver faces that can be collapsed to single edge
// if (nSmallCollapsed == 0 && nMerged == 0)
// {
// nSliversCollapsed =
// collapseHighAspectFaces
// (
// mesh,
// boundaryPoint,
// processorPoints,
// 1E-9,
// 5,
// collapser
// );
// }
reduce(nSliversCollapsed, sumOp<label>());
Info<< indent << "Collapsing " << nSliversCollapsed
<< " small high aspect ratio faces" << endl;
// Simplify faces to quads wherever possible
//if (nCollapsed == 0)
//{
// nCollapsed =
// simplifyFaces
// (
// mesh,
// boundaryPoint,
// 4, // minimum size of face
// 0.2, // gap in edge lengths on face
// collapser
// );
// Info<< "Collapsing " << nCollapsed << " polygonal faces"
// << endl;
//}
label totalCollapsed =
nSmallCollapsed
+ nMerged
+ nSliversCollapsed;
polyTopoChange meshMod(fvMeshRef);
// Insert mesh refinement into polyTopoChange.
setRefinement(fvMeshRef, meshMod, allPointInfo);
// Do all changes
Info<< indent << "Applying changes to the mesh" << nl
<< decrIndent << endl;
morphMap = meshMod.changeMesh(fvMeshRef, false);
// // Contains new point label for old points
// const labelList& reversePointMap = morphMap().reversePointMap();
//
// forAll(pointMap, pI)
// {
// const label originalPoint = pI;
// const label currentPoint = pointMap[pI];
//
// if (currentPoint < reversePointMap.size())
// {
// const label newPoint = reversePointMap[currentPoint];
//
// if (newPoint != -1)
// {
// pointMap[originalPoint] = newPoint;
// }
// }
// }
if (totalCollapsed == 0)
{
labelHashSet freezePoints = checkMeshQuality(fvMeshRef);
label nFreezePoints = freezePoints.size();
reduce(nFreezePoints, sumOp<label>());
nFrozenEdges = nFreezePoints;
Info<< "Number of frozen points : " << nFreezePoints
<< endl;
break;
}
if (morphMap().hasMotionPoints())
{
fvMeshRef.movePoints(morphMap().preMotionPoints());
}
meshChanged = true;
nIterations++;
}
if (nFrozenEdges > 0)
{
minLen *= minLenFactor;
}
reduce(nFrozenEdges, sumOp<label>());
Info<< "Number of frozen edges : " << nFrozenEdges << nl
<< endl;
if (nFrozenEdges == 0)
{
break;
}
} while (true);
if (meshChanged)
{
// Write resulting mesh
if (!overwrite)
{
runTime++;
}
else
{
fvMeshPtr().setInstance(oldInstance);
}
Info<< nl << "Writing collapsed mesh to time "
<< runTime.timeName() << nl << endl;
fvMeshPtr().write();
}
Info<< "Final minimum length : " << minLen << " m" << nl << endl;
Info<< nl << "ExecutionTime = " << runTime.elapsedCpuTime() << " s"
<< " ClockTime = " << runTime.elapsedClockTime() << " s"
<< nl << endl;
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //