/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2004-2011 OpenCFD Ltd.
\\/ 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 .
\*----------------------------------------------------------------------------*/
#include "meshRefinement.H"
#include "combineFaces.H"
#include "polyTopoChange.H"
#include "removePoints.H"
#include "faceSet.H"
#include "Time.H"
#include "motionSmoother.H"
#include "syncTools.H"
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
//// Merge faces that are in-line.
//Foam::label Foam::meshRefinement::mergePatchFaces
//(
// const scalar minCos,
// const scalar concaveCos,
// const labelList& patchIDs
//)
//{
// // Patch face merging engine
// combineFaces faceCombiner(mesh_);
//
// const polyBoundaryMesh& patches = mesh_.boundaryMesh();
//
// // Pick up all candidate cells on boundary
// labelHashSet boundaryCells(mesh_.nFaces()-mesh_.nInternalFaces());
//
// forAll(patchIDs, i)
// {
// label patchI = patchIDs[i];
//
// const polyPatch& patch = patches[patchI];
//
// if (!patch.coupled())
// {
// forAll(patch, i)
// {
// boundaryCells.insert(mesh_.faceOwner()[patch.start()+i]);
// }
// }
// }
//
// // Get all sets of faces that can be merged
// labelListList mergeSets
// (
// faceCombiner.getMergeSets
// (
// minCos,
// concaveCos,
// boundaryCells
// )
// );
//
// label nFaceSets = returnReduce(mergeSets.size(), sumOp());
//
// Info<< "mergePatchFaces : Merging " << nFaceSets
// << " sets of faces." << endl;
//
// if (nFaceSets > 0)
// {
// // Topology changes container
// polyTopoChange meshMod(mesh_);
//
// // Merge all faces of a set into the first face of the set. Remove
// // unused points.
// faceCombiner.setRefinement(mergeSets, meshMod);
//
// // Change the mesh (no inflation)
// autoPtr map = meshMod.changeMesh(mesh_, false, true);
//
// // Update fields
// mesh_.updateMesh(map);
//
// // Move mesh (since morphing does not do this)
// if (map().hasMotionPoints())
// {
// mesh_.movePoints(map().preMotionPoints());
// }
// else
// {
// // Delete mesh volumes. No other way to do this?
// mesh_.clearOut();
// }
//
//
// // Reset the instance for if in overwrite mode
// mesh_.setInstance(timeName());
//
// faceCombiner.updateMesh(map);
//
// // Get the kept faces that need to be recalculated.
// // Merging two boundary faces might shift the cell centre
// // (unless the faces are absolutely planar)
// labelHashSet retestFaces(6*mergeSets.size());
//
// forAll(mergeSets, setI)
// {
// label oldMasterI = mergeSets[setI][0];
//
// label faceI = map().reverseFaceMap()[oldMasterI];
//
// // faceI is always uncoupled boundary face
// const cell& cFaces = mesh_.cells()[mesh_.faceOwner()[faceI]];
//
// forAll(cFaces, i)
// {
// retestFaces.insert(cFaces[i]);
// }
// }
// updateMesh(map, retestFaces.toc());
// }
//
//
// return nFaceSets;
//}
//
//
//// Remove points not used by any face or points used by only two faces where
//// the edges are in line
//Foam::autoPtr Foam::meshRefinement::mergeEdges
//(
// const scalar minCos
//)
//{
// // Point removal analysis engine
// removePoints pointRemover(mesh_);
//
// // Count usage of points
// boolList pointCanBeDeleted;
// label nRemove = pointRemover.countPointUsage(minCos, pointCanBeDeleted);
//
// Info<< "Removing " << nRemove
// << " straight edge points." << endl;
//
// autoPtr map;
//
// if (nRemove > 0)
// {
// // Save my local faces that will change. These changed faces might
// // cause a shift in the cell centre which needs to be retested.
// // Have to do this before changing mesh since point will be removed.
// labelHashSet retestOldFaces(nRemove / Pstream::nProcs());
//
// {
// const faceList& faces = mesh_.faces();
//
// forAll(faces, faceI)
// {
// const face& f = faces[faceI];
//
// forAll(f, fp)
// {
// if (pointCanBeDeleted[f[fp]])
// {
// retestOldFaces.insert(faceI);
// break;
// }
// }
// }
// }
//
// // Topology changes container
// polyTopoChange meshMod(mesh_);
//
// pointRemover.setRefinement(pointCanBeDeleted, meshMod);
//
// // Change the mesh (no inflation)
// map = meshMod.changeMesh(mesh_, false, true);
//
// // Update fields
// mesh_.updateMesh(map);
//
// // Move mesh (since morphing does not do this)
// if (map().hasMotionPoints())
// {
// mesh_.movePoints(map().preMotionPoints());
// }
// else
// {
// // Delete mesh volumes. No other way to do this?
// mesh_.clearOut();
// }
//
// // Reset the instance for if in overwrite mode
// mesh_.setInstance(timeName());
//
// pointRemover.updateMesh(map);
//
// // Get the kept faces that need to be recalculated.
// labelHashSet retestFaces(6*retestOldFaces.size());
//
// const cellList& cells = mesh_.cells();
//
// forAllConstIter(labelHashSet, retestOldFaces, iter)
// {
// label faceI = map().reverseFaceMap()[iter.key()];
//
// const cell& ownFaces = cells[mesh_.faceOwner()[faceI]];
//
// forAll(ownFaces, i)
// {
// retestFaces.insert(ownFaces[i]);
// }
//
// if (mesh_.isInternalFace(faceI))
// {
// const cell& neiFaces = cells[mesh_.faceNeighbour()[faceI]];
//
// forAll(neiFaces, i)
// {
// retestFaces.insert(neiFaces[i]);
// }
// }
// }
// updateMesh(map, retestFaces.toc());
// }
//
// return map;
//}
Foam::label Foam::meshRefinement::mergePatchFacesUndo
(
const scalar minCos,
const scalar concaveCos,
const labelList& patchIDs,
const dictionary& motionDict
)
{
// Patch face merging engine
combineFaces faceCombiner(mesh_, true);
// Pick up all candidate cells on boundary
labelHashSet boundaryCells(mesh_.nFaces()-mesh_.nInternalFaces());
{
const polyBoundaryMesh& patches = mesh_.boundaryMesh();
forAll(patchIDs, i)
{
label patchI = patchIDs[i];
const polyPatch& patch = patches[patchI];
if (!patch.coupled())
{
forAll(patch, i)
{
boundaryCells.insert(mesh_.faceOwner()[patch.start()+i]);
}
}
}
}
// Get all sets of faces that can be merged
labelListList allFaceSets
(
faceCombiner.getMergeSets
(
minCos,
concaveCos,
boundaryCells
)
);
label nFaceSets = returnReduce(allFaceSets.size(), sumOp());
Info<< "Merging " << nFaceSets << " sets of faces." << nl << endl;
if (nFaceSets > 0)
{
if (debug)
{
faceSet allSets(mesh_, "allFaceSets", allFaceSets.size());
forAll(allFaceSets, setI)
{
forAll(allFaceSets[setI], i)
{
allSets.insert(allFaceSets[setI][i]);
}
}
Pout<< "Writing all faces to be merged to set "
<< allSets.objectPath() << endl;
allSets.instance() = timeName();
allSets.write();
const_cast(mesh_.time())++;
}
// Topology changes container
polyTopoChange meshMod(mesh_);
// Merge all faces of a set into the first face of the set.
faceCombiner.setRefinement(allFaceSets, meshMod);
// Experimental: store data for all the points that have been deleted
storeData
(
faceCombiner.savedPointLabels(), // points to store
labelList(0), // faces to store
labelList(0) // cells to store
);
// Change the mesh (no inflation)
autoPtr map = meshMod.changeMesh(mesh_, false, true);
// Update fields
mesh_.updateMesh(map);
// Move mesh (since morphing does not do this)
if (map().hasMotionPoints())
{
mesh_.movePoints(map().preMotionPoints());
}
else
{
// Delete mesh volumes.
mesh_.clearOut();
}
// Reset the instance for if in overwrite mode
mesh_.setInstance(timeName());
faceCombiner.updateMesh(map);
// Get the kept faces that need to be recalculated.
// Merging two boundary faces might shift the cell centre
// (unless the faces are absolutely planar)
labelHashSet retestFaces(6*allFaceSets.size());
forAll(allFaceSets, setI)
{
label oldMasterI = allFaceSets[setI][0];
label faceI = map().reverseFaceMap()[oldMasterI];
// faceI is always uncoupled boundary face
const cell& cFaces = mesh_.cells()[mesh_.faceOwner()[faceI]];
forAll(cFaces, i)
{
retestFaces.insert(cFaces[i]);
}
}
updateMesh(map, retestFaces.toc());
if (debug)
{
// Check sync
checkData();
Pout<< "Writing initial merged-faces mesh to time "
<< timeName() << nl << endl;
write();
}
for (label iteration = 0; iteration < 100; iteration++)
{
Info<< nl
<< "Undo iteration " << iteration << nl
<< "----------------" << endl;
// Check mesh for errors
// ~~~~~~~~~~~~~~~~~~~~~
faceSet errorFaces
(
mesh_,
"errorFaces",
mesh_.nFaces()-mesh_.nInternalFaces()
);
bool hasErrors = motionSmoother::checkMesh
(
false, // report
mesh_,
motionDict,
errorFaces
);
//if (checkEdgeConnectivity)
//{
// Info<< "Checking edge-face connectivity (duplicate faces"
// << " or non-consecutive shared vertices)" << endl;
//
// label nOldSize = errorFaces.size();
//
// hasErrors =
// mesh_.checkFaceFaces
// (
// false,
// &errorFaces
// )
// || hasErrors;
//
// Info<< "Detected additional "
// << returnReduce
// (
// errorFaces.size() - nOldSize,
// sumOp()
// )
// << " faces with illegal face-face connectivity"
// << endl;
//}
if (!hasErrors)
{
break;
}
if (debug)
{
errorFaces.instance() = timeName();
Pout<< "Writing all faces in error to faceSet "
<< errorFaces.objectPath() << nl << endl;
errorFaces.write();
}
// Check any master cells for using any of the error faces
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DynamicList mastersToRestore(allFaceSets.size());
forAll(allFaceSets, setI)
{
label masterFaceI = faceCombiner.masterFace()[setI];
if (masterFaceI != -1)
{
label masterCellII = mesh_.faceOwner()[masterFaceI];
const cell& cFaces = mesh_.cells()[masterCellII];
forAll(cFaces, i)
{
if (errorFaces.found(cFaces[i]))
{
mastersToRestore.append(masterFaceI);
break;
}
}
}
}
mastersToRestore.shrink();
label nRestore = returnReduce
(
mastersToRestore.size(),
sumOp()
);
Info<< "Masters that need to be restored:"
<< nRestore << endl;
if (debug)
{
faceSet restoreSet(mesh_, "mastersToRestore", mastersToRestore);
restoreSet.instance() = timeName();
Pout<< "Writing all " << mastersToRestore.size()
<< " masterfaces to be restored to set "
<< restoreSet.objectPath() << endl;
restoreSet.write();
}
if (nRestore == 0)
{
break;
}
// Undo
// ~~~~
if (debug)
{
const_cast(mesh_.time())++;
}
// Topology changes container
polyTopoChange meshMod(mesh_);
// Merge all faces of a set into the first face of the set.
// Experimental:mark all points/faces/cells that have been restored.
Map restoredPoints(0);
Map restoredFaces(0);
Map restoredCells(0);
faceCombiner.setUnrefinement
(
mastersToRestore,
meshMod,
restoredPoints,
restoredFaces,
restoredCells
);
// Change the mesh (no inflation)
autoPtr map = meshMod.changeMesh(mesh_, false, true);
// Update fields
mesh_.updateMesh(map);
// Move mesh (since morphing does not do this)
if (map().hasMotionPoints())
{
mesh_.movePoints(map().preMotionPoints());
}
else
{
// Delete mesh volumes.
mesh_.clearOut();
}
// Reset the instance for if in overwrite mode
mesh_.setInstance(timeName());
faceCombiner.updateMesh(map);
// Renumber restore maps
inplaceMapKey(map().reversePointMap(), restoredPoints);
inplaceMapKey(map().reverseFaceMap(), restoredFaces);
inplaceMapKey(map().reverseCellMap(), restoredCells);
// Get the kept faces that need to be recalculated.
// Merging two boundary faces might shift the cell centre
// (unless the faces are absolutely planar)
labelHashSet retestFaces(6*restoredFaces.size());
forAll(restoredFaces, setI)
{
label faceI = restoredFaces[setI];
// faceI is always uncoupled boundary face
const cell& cFaces = mesh_.cells()[mesh_.faceOwner()[faceI]];
forAll(cFaces, i)
{
retestFaces.insert(cFaces[i]);
}
}
// Experimental:restore all points/face/cells in maps
updateMesh
(
map,
retestFaces.toc(),
restoredPoints,
restoredFaces,
restoredCells
);
if (debug)
{
// Check sync
checkData();
Pout<< "Writing merged-faces mesh to time "
<< timeName() << nl << endl;
write();
}
Info<< endl;
}
}
else
{
Info<< "No faces merged ..." << endl;
}
return nFaceSets;
}
// Remove points. pointCanBeDeleted is parallel synchronised.
Foam::autoPtr Foam::meshRefinement::doRemovePoints
(
removePoints& pointRemover,
const boolList& pointCanBeDeleted
)
{
// Topology changes container
polyTopoChange meshMod(mesh_);
pointRemover.setRefinement(pointCanBeDeleted, meshMod);
// Change the mesh (no inflation)
autoPtr map = meshMod.changeMesh(mesh_, false, true);
// Update fields
mesh_.updateMesh(map);
// Move mesh (since morphing does not do this)
if (map().hasMotionPoints())
{
mesh_.movePoints(map().preMotionPoints());
}
else
{
// Delete mesh volumes.
mesh_.clearOut();
}
// Reset the instance for if in overwrite mode
mesh_.setInstance(timeName());
pointRemover.updateMesh(map);
updateMesh(map, labelList(0));
return map;
}
// Restore faces (which contain removed points)
Foam::autoPtr Foam::meshRefinement::doRestorePoints
(
removePoints& pointRemover,
const labelList& facesToRestore
)
{
// Topology changes container
polyTopoChange meshMod(mesh_);
// Determine sets of points and faces to restore
labelList localFaces, localPoints;
pointRemover.getUnrefimentSet
(
facesToRestore,
localFaces,
localPoints
);
// Undo the changes on the faces that are in error.
pointRemover.setUnrefinement
(
localFaces,
localPoints,
meshMod
);
// Change the mesh (no inflation)
autoPtr map = meshMod.changeMesh(mesh_, false, true);
// Update fields
mesh_.updateMesh(map);
// Move mesh (since morphing does not do this)
if (map().hasMotionPoints())
{
mesh_.movePoints(map().preMotionPoints());
}
else
{
// Delete mesh volumes.
mesh_.clearOut();
}
// Reset the instance for if in overwrite mode
mesh_.setInstance(timeName());
pointRemover.updateMesh(map);
updateMesh(map, labelList(0));
return map;
}
// Collect all faces that are both in candidateFaces and in the set.
// If coupled face also collects the coupled face.
Foam::labelList Foam::meshRefinement::collectFaces
(
const labelList& candidateFaces,
const labelHashSet& set
) const
{
// Has face been selected?
boolList selected(mesh_.nFaces(), false);
forAll(candidateFaces, i)
{
label faceI = candidateFaces[i];
if (set.found(faceI))
{
selected[faceI] = true;
}
}
syncTools::syncFaceList
(
mesh_,
selected,
orEqOp() // combine operator
);
labelList selectedFaces(findIndices(selected, true));
return selectedFaces;
}
// Pick up faces of cells of faces in set.
Foam::labelList Foam::meshRefinement::growFaceCellFace
(
const labelHashSet& set
) const
{
boolList selected(mesh_.nFaces(), false);
forAllConstIter(faceSet, set, iter)
{
label faceI = iter.key();
label own = mesh_.faceOwner()[faceI];
const cell& ownFaces = mesh_.cells()[own];
forAll(ownFaces, ownFaceI)
{
selected[ownFaces[ownFaceI]] = true;
}
if (mesh_.isInternalFace(faceI))
{
label nbr = mesh_.faceNeighbour()[faceI];
const cell& nbrFaces = mesh_.cells()[nbr];
forAll(nbrFaces, nbrFaceI)
{
selected[nbrFaces[nbrFaceI]] = true;
}
}
}
syncTools::syncFaceList
(
mesh_,
selected,
orEqOp() // combine operator
);
return findIndices(selected, true);
}
// Remove points not used by any face or points used by only two faces where
// the edges are in line
Foam::label Foam::meshRefinement::mergeEdgesUndo
(
const scalar minCos,
const dictionary& motionDict
)
{
Info<< nl
<< "Merging all points on surface that" << nl
<< "- are used by only two boundary faces and" << nl
<< "- make an angle with a cosine of more than " << minCos
<< "." << nl << endl;
// Point removal analysis engine with undo
removePoints pointRemover(mesh_, true);
// Count usage of points
boolList pointCanBeDeleted;
label nRemove = pointRemover.countPointUsage(minCos, pointCanBeDeleted);
if (nRemove > 0)
{
Info<< "Removing " << nRemove
<< " straight edge points ..." << nl << endl;
// Remove points
// ~~~~~~~~~~~~~
doRemovePoints(pointRemover, pointCanBeDeleted);
for (label iteration = 0; iteration < 100; iteration++)
{
Info<< nl
<< "Undo iteration " << iteration << nl
<< "----------------" << endl;
// Check mesh for errors
// ~~~~~~~~~~~~~~~~~~~~~
faceSet errorFaces
(
mesh_,
"errorFaces",
mesh_.nFaces()-mesh_.nInternalFaces()
);
bool hasErrors = motionSmoother::checkMesh
(
false, // report
mesh_,
motionDict,
errorFaces
);
//if (checkEdgeConnectivity)
//{
// Info<< "Checking edge-face connectivity (duplicate faces"
// << " or non-consecutive shared vertices)" << endl;
//
// label nOldSize = errorFaces.size();
//
// hasErrors =
// mesh_.checkFaceFaces
// (
// false,
// &errorFaces
// )
// || hasErrors;
//
// Info<< "Detected additional "
// << returnReduce(errorFaces.size()-nOldSize,sumOp())
// << " faces with illegal face-face connectivity"
// << endl;
//}
if (!hasErrors)
{
break;
}
if (debug)
{
errorFaces.instance() = timeName();
Pout<< "**Writing all faces in error to faceSet "
<< errorFaces.objectPath() << nl << endl;
errorFaces.write();
}
labelList masterErrorFaces
(
collectFaces
(
pointRemover.savedFaceLabels(),
errorFaces
)
);
label n = returnReduce(masterErrorFaces.size(), sumOp());
Info<< "Detected " << n
<< " error faces on boundaries that have been merged."
<< " These will be restored to their original faces." << nl
<< endl;
if (n == 0)
{
if (hasErrors)
{
Info<< "Detected "
<< returnReduce(errorFaces.size(), sumOp())
<< " error faces in mesh."
<< " Restoring neighbours of faces in error." << nl
<< endl;
labelList expandedErrorFaces
(
growFaceCellFace
(
errorFaces
)
);
doRestorePoints(pointRemover, expandedErrorFaces);
}
break;
}
doRestorePoints(pointRemover, masterErrorFaces);
}
if (debug)
{
const_cast(mesh_.time())++;
Pout<< "Writing merged-edges mesh to time "
<< timeName() << nl << endl;
write();
}
}
else
{
Info<< "No straight edges simplified and no points removed ..." << endl;
}
return nRemove;
}
// ************************************************************************* //