Files
openfoam/src/mesh/snappyHexMesh/snappyHexMeshDriver/snappyRefineDriver.C
2019-06-26 11:36:49 +02:00

3099 lines
92 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2015-2019 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
| Copyright (C) 2011-2015 OpenFOAM Foundation
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
#include "snappyRefineDriver.H"
#include "meshRefinement.H"
#include "fvMesh.H"
#include "Time.H"
#include "cellSet.H"
#include "syncTools.H"
#include "refinementParameters.H"
#include "refinementSurfaces.H"
#include "refinementFeatures.H"
#include "shellSurfaces.H"
#include "mapDistributePolyMesh.H"
#include "unitConversion.H"
#include "snapParameters.H"
#include "localPointRegion.H"
#include "IOmanip.H"
#include "labelVector.H"
#include "profiling.H"
#include "searchableSurfaces.H"
#include "fvMeshSubset.H"
#include "interpolationTable.H"
#include "snappyVoxelMeshDriver.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(snappyRefineDriver, 0);
} // End namespace Foam
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::snappyRefineDriver::snappyRefineDriver
(
meshRefinement& meshRefiner,
decompositionMethod& decomposer,
fvMeshDistribute& distributor,
const labelUList& globalToMasterPatch,
const labelUList& globalToSlavePatch,
const writer<scalar>& setFormatter,
const bool dryRun
)
:
meshRefiner_(meshRefiner),
decomposer_(decomposer),
distributor_(distributor),
globalToMasterPatch_(globalToMasterPatch),
globalToSlavePatch_(globalToSlavePatch),
setFormatter_(setFormatter),
dryRun_(dryRun)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::label Foam::snappyRefineDriver::featureEdgeRefine
(
const refinementParameters& refineParams,
const label maxIter,
const label minRefine
)
{
if (dryRun_)
{
return 0;
}
if (refineParams.minRefineCells() == -1)
{
// Special setting to be able to restart shm on meshes with inconsistent
// cellLevel/pointLevel
return 0;
}
addProfiling(edge, "snappyHexMesh::refine::edge");
const fvMesh& mesh = meshRefiner_.mesh();
label iter = 0;
if (meshRefiner_.features().size() && maxIter > 0)
{
for (; iter < maxIter; iter++)
{
Info<< nl
<< "Feature refinement iteration " << iter << nl
<< "------------------------------" << nl
<< endl;
labelList candidateCells
(
meshRefiner_.refineCandidates
(
refineParams.locationsInMesh(),
refineParams.curvature(),
refineParams.planarAngle(),
true, // featureRefinement
false, // featureDistanceRefinement
false, // internalRefinement
false, // surfaceRefinement
false, // curvatureRefinement
false, // smallFeatureRefinement
false, // gapRefinement
false, // bigGapRefinement
false, // spreadGapSize
refineParams.maxGlobalCells(),
refineParams.maxLocalCells()
)
);
labelList cellsToRefine
(
meshRefiner_.meshCutter().consistentRefinement
(
candidateCells,
true
)
);
Info<< "Determined cells to refine in = "
<< mesh.time().cpuTimeIncrement() << " s" << endl;
label nCellsToRefine = cellsToRefine.size();
reduce(nCellsToRefine, sumOp<label>());
Info<< "Selected for feature refinement : " << nCellsToRefine
<< " cells (out of " << mesh.globalData().nTotalCells()
<< ')' << endl;
if (nCellsToRefine <= minRefine)
{
Info<< "Stopping refining since too few cells selected."
<< nl << endl;
break;
}
if (debug > 0)
{
const_cast<Time&>(mesh.time())++;
}
if
(
returnReduce
(
(mesh.nCells() >= refineParams.maxLocalCells()),
orOp<bool>()
)
)
{
meshRefiner_.balanceAndRefine
(
"feature refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
else
{
meshRefiner_.refineAndBalance
(
"feature refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
}
}
return iter;
}
Foam::label Foam::snappyRefineDriver::smallFeatureRefine
(
const refinementParameters& refineParams,
const label maxIter
)
{
if (dryRun_)
{
return 0;
}
if (refineParams.minRefineCells() == -1)
{
// Special setting to be able to restart shm on meshes with inconsistent
// cellLevel/pointLevel
return 0;
}
addProfiling(feature, "snappyHexMesh::refine::smallFeature");
const fvMesh& mesh = meshRefiner_.mesh();
label iter = 0;
// See if any surface has an extendedGapLevel
labelList surfaceMaxLevel(meshRefiner_.surfaces().maxGapLevel());
labelList shellMaxLevel(meshRefiner_.shells().maxGapLevel());
if (max(surfaceMaxLevel) == 0 && max(shellMaxLevel) == 0)
{
return iter;
}
for (; iter < maxIter; iter++)
{
Info<< nl
<< "Small surface feature refinement iteration " << iter << nl
<< "--------------------------------------------" << nl
<< endl;
// Determine cells to refine
// ~~~~~~~~~~~~~~~~~~~~~~~~~
labelList candidateCells
(
meshRefiner_.refineCandidates
(
refineParams.locationsInMesh(),
refineParams.curvature(),
refineParams.planarAngle(),
false, // featureRefinement
false, // featureDistanceRefinement
false, // internalRefinement
false, // surfaceRefinement
false, // curvatureRefinement
true, // smallFeatureRefinement
false, // gapRefinement
false, // bigGapRefinement
false, // spreadGapSize
refineParams.maxGlobalCells(),
refineParams.maxLocalCells()
)
);
labelList cellsToRefine
(
meshRefiner_.meshCutter().consistentRefinement
(
candidateCells,
true
)
);
Info<< "Determined cells to refine in = "
<< mesh.time().cpuTimeIncrement() << " s" << endl;
label nCellsToRefine = cellsToRefine.size();
reduce(nCellsToRefine, sumOp<label>());
Info<< "Selected for refinement : " << nCellsToRefine
<< " cells (out of " << mesh.globalData().nTotalCells()
<< ')' << endl;
// Stop when no cells to refine or have done minimum necessary
// iterations and not enough cells to refine.
if (nCellsToRefine == 0)
{
Info<< "Stopping refining since too few cells selected."
<< nl << endl;
break;
}
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
if
(
returnReduce
(
(mesh.nCells() >= refineParams.maxLocalCells()),
orOp<bool>()
)
)
{
meshRefiner_.balanceAndRefine
(
"small feature refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
else
{
meshRefiner_.refineAndBalance
(
"small feature refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
}
return iter;
}
Foam::label Foam::snappyRefineDriver::surfaceOnlyRefine
(
const refinementParameters& refineParams,
const label maxIter
)
{
if (dryRun_)
{
return 0;
}
if (refineParams.minRefineCells() == -1)
{
// Special setting to be able to restart shm on meshes with inconsistent
// cellLevel/pointLevel
return 0;
}
addProfiling(surface, "snappyHexMesh::refine::surface");
const fvMesh& mesh = meshRefiner_.mesh();
// Determine the maximum refinement level over all surfaces. This
// determines the minimum number of surface refinement iterations.
label overallMaxLevel = max(meshRefiner_.surfaces().maxLevel());
label iter;
for (iter = 0; iter < maxIter; iter++)
{
Info<< nl
<< "Surface refinement iteration " << iter << nl
<< "------------------------------" << nl
<< endl;
// Determine cells to refine
// ~~~~~~~~~~~~~~~~~~~~~~~~~
// Only look at surface intersections (minLevel and surface curvature),
// do not do internal refinement (refinementShells)
labelList candidateCells
(
meshRefiner_.refineCandidates
(
refineParams.locationsInMesh(),
refineParams.curvature(),
refineParams.planarAngle(),
false, // featureRefinement
false, // featureDistanceRefinement
false, // internalRefinement
true, // surfaceRefinement
true, // curvatureRefinement
false, // smallFeatureRefinement
false, // gapRefinement
false, // bigGapRefinement
false, // spreadGapSize
refineParams.maxGlobalCells(),
refineParams.maxLocalCells()
)
);
labelList cellsToRefine
(
meshRefiner_.meshCutter().consistentRefinement
(
candidateCells,
true
)
);
Info<< "Determined cells to refine in = "
<< mesh.time().cpuTimeIncrement() << " s" << endl;
label nCellsToRefine = cellsToRefine.size();
reduce(nCellsToRefine, sumOp<label>());
Info<< "Selected for refinement : " << nCellsToRefine
<< " cells (out of " << mesh.globalData().nTotalCells()
<< ')' << endl;
// Stop when no cells to refine or have done minimum necessary
// iterations and not enough cells to refine.
if
(
nCellsToRefine == 0
|| (
iter >= overallMaxLevel
&& nCellsToRefine <= refineParams.minRefineCells()
)
)
{
Info<< "Stopping refining since too few cells selected."
<< nl << endl;
break;
}
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
if
(
returnReduce
(
(mesh.nCells() >= refineParams.maxLocalCells()),
orOp<bool>()
)
)
{
meshRefiner_.balanceAndRefine
(
"surface refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
else
{
meshRefiner_.refineAndBalance
(
"surface refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
}
return iter;
}
Foam::label Foam::snappyRefineDriver::gapOnlyRefine
(
const refinementParameters& refineParams,
const label maxIter
)
{
if (dryRun_)
{
return 0;
}
if (refineParams.minRefineCells() == -1)
{
// Special setting to be able to restart shm on meshes with inconsistent
// cellLevel/pointLevel
return 0;
}
const fvMesh& mesh = meshRefiner_.mesh();
// Determine the maximum refinement level over all surfaces. This
// determines the minimum number of surface refinement iterations.
label maxIncrement = 0;
const labelList& maxLevel = meshRefiner_.surfaces().maxLevel();
const labelList& gapLevel = meshRefiner_.surfaces().gapLevel();
forAll(maxLevel, i)
{
maxIncrement = max(maxIncrement, gapLevel[i]-maxLevel[i]);
}
label iter = 0;
if (maxIncrement == 0)
{
return iter;
}
for (iter = 0; iter < maxIter; iter++)
{
Info<< nl
<< "Gap refinement iteration " << iter << nl
<< "--------------------------" << nl
<< endl;
// Determine cells to refine
// ~~~~~~~~~~~~~~~~~~~~~~~~~
// Only look at surface intersections (minLevel and surface curvature),
// do not do internal refinement (refinementShells)
labelList candidateCells
(
meshRefiner_.refineCandidates
(
refineParams.locationsInMesh(),
refineParams.curvature(),
refineParams.planarAngle(),
false, // featureRefinement
false, // featureDistanceRefinement
false, // internalRefinement
false, // surfaceRefinement
false, // curvatureRefinement
false, // smallFeatureRefinement
true, // gapRefinement
false, // bigGapRefinement
false, // spreadGapSize
refineParams.maxGlobalCells(),
refineParams.maxLocalCells()
)
);
if (debug&meshRefinement::MESH)
{
Pout<< "Writing current mesh to time "
<< meshRefiner_.timeName() << endl;
meshRefiner_.write
(
meshRefinement::debugType(debug),
meshRefinement::writeType
(
meshRefinement::writeLevel()
| meshRefinement::WRITEMESH
),
mesh.time().path()/meshRefiner_.timeName()
);
Pout<< "Dumped mesh in = "
<< mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
Pout<< "Dumping " << candidateCells.size()
<< " cells to cellSet candidateCellsFromGap." << endl;
cellSet c(mesh, "candidateCellsFromGap", candidateCells);
c.instance() = meshRefiner_.timeName();
c.write();
}
// Grow by one layer to make sure we're covering the gap
{
boolList isCandidateCell(mesh.nCells(), false);
forAll(candidateCells, i)
{
isCandidateCell[candidateCells[i]] = true;
}
for (label i=0; i<1; i++)
{
boolList newIsCandidateCell(isCandidateCell);
// Internal faces
for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
{
label own = mesh.faceOwner()[facei];
label nei = mesh.faceNeighbour()[facei];
if (isCandidateCell[own] != isCandidateCell[nei])
{
newIsCandidateCell[own] = true;
newIsCandidateCell[nei] = true;
}
}
// Get coupled boundary condition values
boolList neiIsCandidateCell;
syncTools::swapBoundaryCellList
(
mesh,
isCandidateCell,
neiIsCandidateCell
);
// Boundary faces
for
(
label facei = mesh.nInternalFaces();
facei < mesh.nFaces();
facei++
)
{
label own = mesh.faceOwner()[facei];
label bFacei = facei-mesh.nInternalFaces();
if (isCandidateCell[own] != neiIsCandidateCell[bFacei])
{
newIsCandidateCell[own] = true;
}
}
isCandidateCell.transfer(newIsCandidateCell);
}
label n = 0;
forAll(isCandidateCell, celli)
{
if (isCandidateCell[celli])
{
n++;
}
}
candidateCells.setSize(n);
n = 0;
forAll(isCandidateCell, celli)
{
if (isCandidateCell[celli])
{
candidateCells[n++] = celli;
}
}
}
if (debug&meshRefinement::MESH)
{
Pout<< "Dumping " << candidateCells.size()
<< " cells to cellSet candidateCellsFromGapPlusBuffer." << endl;
cellSet c(mesh, "candidateCellsFromGapPlusBuffer", candidateCells);
c.instance() = meshRefiner_.timeName();
c.write();
}
labelList cellsToRefine
(
meshRefiner_.meshCutter().consistentRefinement
(
candidateCells,
true
)
);
Info<< "Determined cells to refine in = "
<< mesh.time().cpuTimeIncrement() << " s" << endl;
label nCellsToRefine = cellsToRefine.size();
reduce(nCellsToRefine, sumOp<label>());
Info<< "Selected for refinement : " << nCellsToRefine
<< " cells (out of " << mesh.globalData().nTotalCells()
<< ')' << endl;
// Stop when no cells to refine or have done minimum necessary
// iterations and not enough cells to refine.
if
(
nCellsToRefine == 0
|| (
iter >= maxIncrement
&& nCellsToRefine <= refineParams.minRefineCells()
)
)
{
Info<< "Stopping refining since too few cells selected."
<< nl << endl;
break;
}
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
if
(
returnReduce
(
(mesh.nCells() >= refineParams.maxLocalCells()),
orOp<bool>()
)
)
{
meshRefiner_.balanceAndRefine
(
"gap refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
else
{
meshRefiner_.refineAndBalance
(
"gap refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
}
return iter;
}
Foam::label Foam::snappyRefineDriver::surfaceProximityBlock
(
const refinementParameters& refineParams,
const label maxIter
)
{
if (refineParams.minRefineCells() == -1)
{
// Special setting to be able to restart shm on meshes with inconsistent
// cellLevel/pointLevel
return 0;
}
fvMesh& mesh = meshRefiner_.mesh();
if (min(meshRefiner_.surfaces().blockLevel()) == labelMax)
{
return 0;
}
label iter = 0;
for (iter = 0; iter < maxIter; iter++)
{
Info<< nl
<< "Gap blocking iteration " << iter << nl
<< "------------------------" << nl
<< endl;
// Determine cells to block
// ~~~~~~~~~~~~~~~~~~~~~~~~
meshRefiner_.removeGapCells
(
refineParams.planarAngle(),
meshRefiner_.surfaces().blockLevel(),
globalToMasterPatch_,
refineParams.nFilterIter()
);
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
}
return iter;
}
Foam::label Foam::snappyRefineDriver::bigGapOnlyRefine
(
const refinementParameters& refineParams,
const bool spreadGapSize,
const label maxIter
)
{
if (refineParams.minRefineCells() == -1)
{
// Special setting to be able to restart shm on meshes with inconsistent
// cellLevel/pointLevel
return 0;
}
if (dryRun_)
{
return 0;
}
const fvMesh& mesh = meshRefiner_.mesh();
label iter = 0;
// See if any surface has an extendedGapLevel
labelList surfaceMaxLevel(meshRefiner_.surfaces().maxGapLevel());
labelList shellMaxLevel(meshRefiner_.shells().maxGapLevel());
label overallMaxLevel(max(max(surfaceMaxLevel), max(shellMaxLevel)));
if (overallMaxLevel == 0)
{
return iter;
}
for (; iter < maxIter; iter++)
{
Info<< nl
<< "Big gap refinement iteration " << iter << nl
<< "------------------------------" << nl
<< endl;
// Determine cells to refine
// ~~~~~~~~~~~~~~~~~~~~~~~~~
labelList candidateCells
(
meshRefiner_.refineCandidates
(
refineParams.locationsInMesh(),
refineParams.curvature(),
refineParams.planarAngle(),
false, // featureRefinement
false, // featureDistanceRefinement
false, // internalRefinement
false, // surfaceRefinement
false, // curvatureRefinement
false, // smallFeatureRefinement
false, // gapRefinement
true, // bigGapRefinement
spreadGapSize, // spreadGapSize
refineParams.maxGlobalCells(),
refineParams.maxLocalCells()
)
);
if (debug&meshRefinement::MESH)
{
Pout<< "Writing current mesh to time "
<< meshRefiner_.timeName() << endl;
meshRefiner_.write
(
meshRefinement::debugType(debug),
meshRefinement::writeType
(
meshRefinement::writeLevel()
| meshRefinement::WRITEMESH
),
mesh.time().path()/meshRefiner_.timeName()
);
Pout<< "Dumped mesh in = "
<< mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
Pout<< "Dumping " << candidateCells.size()
<< " cells to cellSet candidateCellsFromBigGap." << endl;
cellSet c(mesh, "candidateCellsFromBigGap", candidateCells);
c.instance() = meshRefiner_.timeName();
c.write();
}
labelList cellsToRefine
(
meshRefiner_.meshCutter().consistentRefinement
(
candidateCells,
true
)
);
Info<< "Determined cells to refine in = "
<< mesh.time().cpuTimeIncrement() << " s" << endl;
label nCellsToRefine = cellsToRefine.size();
reduce(nCellsToRefine, sumOp<label>());
Info<< "Selected for refinement : " << nCellsToRefine
<< " cells (out of " << mesh.globalData().nTotalCells()
<< ')' << endl;
// Stop when no cells to refine or have done minimum necessary
// iterations and not enough cells to refine.
if
(
nCellsToRefine == 0
|| (
iter >= overallMaxLevel
&& nCellsToRefine <= refineParams.minRefineCells()
)
)
{
Info<< "Stopping refining since too few cells selected."
<< nl << endl;
break;
}
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
if
(
returnReduce
(
(mesh.nCells() >= refineParams.maxLocalCells()),
orOp<bool>()
)
)
{
meshRefiner_.balanceAndRefine
(
"big gap refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
else
{
meshRefiner_.refineAndBalance
(
"big gap refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
}
return iter;
}
Foam::label Foam::snappyRefineDriver::danglingCellRefine
(
const refinementParameters& refineParams,
const label nFaces,
const label maxIter
)
{
if (refineParams.minRefineCells() == -1)
{
// Special setting to be able to restart shm on meshes with inconsistent
// cellLevel/pointLevel
return 0;
}
if (dryRun_)
{
return 0;
}
addProfiling(dangling, "snappyHexMesh::refine::danglingCell");
const fvMesh& mesh = meshRefiner_.mesh();
label iter;
for (iter = 0; iter < maxIter; iter++)
{
Info<< nl
<< "Dangling coarse cells refinement iteration " << iter << nl
<< "--------------------------------------------" << nl
<< endl;
// Determine cells to refine
// ~~~~~~~~~~~~~~~~~~~~~~~~~
const cellList& cells = mesh.cells();
const polyBoundaryMesh& pbm = mesh.boundaryMesh();
labelList candidateCells;
{
cellSet candidateCellSet(mesh, "candidateCells", cells.size()/1000);
forAll(cells, celli)
{
const cell& cFaces = cells[celli];
label nIntFaces = 0;
forAll(cFaces, i)
{
label bFacei = cFaces[i]-mesh.nInternalFaces();
if (bFacei < 0)
{
nIntFaces++;
}
else
{
label patchi = pbm.patchID()[bFacei];
if (pbm[patchi].coupled())
{
nIntFaces++;
}
}
}
if (nIntFaces == nFaces)
{
candidateCellSet.insert(celli);
}
}
if (debug&meshRefinement::MESH)
{
Pout<< "Dumping " << candidateCellSet.size()
<< " cells to cellSet candidateCellSet." << endl;
candidateCellSet.instance() = meshRefiner_.timeName();
candidateCellSet.write();
}
candidateCells = candidateCellSet.toc();
}
labelList cellsToRefine
(
meshRefiner_.meshCutter().consistentRefinement
(
candidateCells,
true
)
);
Info<< "Determined cells to refine in = "
<< mesh.time().cpuTimeIncrement() << " s" << endl;
label nCellsToRefine = cellsToRefine.size();
reduce(nCellsToRefine, sumOp<label>());
Info<< "Selected for refinement : " << nCellsToRefine
<< " cells (out of " << mesh.globalData().nTotalCells()
<< ')' << endl;
// Stop when no cells to refine. After a few iterations check if too
// few cells
if
(
nCellsToRefine == 0
|| (
iter >= 1
&& nCellsToRefine <= refineParams.minRefineCells()
)
)
{
Info<< "Stopping refining since too few cells selected."
<< nl << endl;
break;
}
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
if
(
returnReduce
(
(mesh.nCells() >= refineParams.maxLocalCells()),
orOp<bool>()
)
)
{
meshRefiner_.balanceAndRefine
(
"coarse cell refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
else
{
meshRefiner_.refineAndBalance
(
"coarse cell refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
}
return iter;
}
// Detect cells with opposing intersected faces of differing refinement
// level and refine them.
Foam::label Foam::snappyRefineDriver::refinementInterfaceRefine
(
const refinementParameters& refineParams,
const label maxIter
)
{
if (refineParams.minRefineCells() == -1)
{
// Special setting to be able to restart shm on meshes with inconsistent
// cellLevel/pointLevel
return 0;
}
if (dryRun_)
{
return 0;
}
addProfiling(interface, "snappyHexMesh::refine::transition");
const fvMesh& mesh = meshRefiner_.mesh();
label iter = 0;
if (refineParams.interfaceRefine())
{
for (;iter < maxIter; iter++)
{
Info<< nl
<< "Refinement transition refinement iteration " << iter << nl
<< "--------------------------------------------" << nl
<< endl;
const labelList& surfaceIndex = meshRefiner_.surfaceIndex();
const hexRef8& cutter = meshRefiner_.meshCutter();
const vectorField& fA = mesh.faceAreas();
const labelList& faceOwner = mesh.faceOwner();
// Determine cells to refine
// ~~~~~~~~~~~~~~~~~~~~~~~~~
const cellList& cells = mesh.cells();
labelList candidateCells;
{
// Pass1: pick up cells with differing face level
cellSet transitionCells
(
mesh,
"transitionCells",
cells.size()/100
);
forAll(cells, celli)
{
const cell& cFaces = cells[celli];
label cLevel = cutter.cellLevel()[celli];
forAll(cFaces, cFacei)
{
label facei = cFaces[cFacei];
if (surfaceIndex[facei] != -1)
{
label fLevel = cutter.faceLevel(facei);
if (fLevel != cLevel)
{
transitionCells.insert(celli);
}
}
}
}
cellSet candidateCellSet
(
mesh,
"candidateCells",
cells.size()/1000
);
// Pass2: check for oppositeness
//for (const label celli : transitionCells)
//{
// const cell& cFaces = cells[celli];
// const point& cc = cellCentres[celli];
// const scalar rCVol = pow(cellVolumes[celli], -5.0/3.0);
//
// // Determine principal axes of cell
// symmTensor R(Zero);
//
// forAll(cFaces, i)
// {
// label facei = cFaces[i];
//
// const point& fc = faceCentres[facei];
//
// // Calculate face-pyramid volume
// scalar pyrVol = 1.0/3.0 * fA[facei] & (fc-cc);
//
// if (faceOwner[facei] != celli)
// {
// pyrVol = -pyrVol;
// }
//
// // Calculate face-pyramid centre
// vector pc = (3.0/4.0)*fc + (1.0/4.0)*cc;
//
// R += pyrVol*sqr(pc-cc)*rCVol;
// }
//
// //- MEJ: Problem: truncation errors cause complex evs
// vector lambdas(eigenValues(R));
// const tensor axes(eigenVectors(R, lambdas));
//
//
// // Check if this cell has
// // - opposing sides intersected
// // - which are of different refinement level
// // - plus the inbetween face
//
// labelVector plusFaceLevel(labelVector(-1, -1, -1));
// labelVector minFaceLevel(labelVector(-1, -1, -1));
//
// forAll(cFaces, cFacei)
// {
// label facei = cFaces[cFacei];
//
// if (surfaceIndex[facei] != -1)
// {
// label fLevel = cutter.faceLevel(facei);
//
// // Get outwards pointing normal
// vector n = fA[facei]/mag(fA[facei]);
// if (faceOwner[facei] != celli)
// {
// n = -n;
// }
//
// // What is major direction and sign
// direction cmpt = vector::X;
// scalar maxComp = (n&axes.x());
//
// scalar yComp = (n&axes.y());
// scalar zComp = (n&axes.z());
//
// if (mag(yComp) > mag(maxComp))
// {
// maxComp = yComp;
// cmpt = vector::Y;
// }
//
// if (mag(zComp) > mag(maxComp))
// {
// maxComp = zComp;
// cmpt = vector::Z;
// }
//
// if (maxComp > 0)
// {
// plusFaceLevel[cmpt] = max
// (
// plusFaceLevel[cmpt],
// fLevel
// );
// }
// else
// {
// minFaceLevel[cmpt] = max
// (
// minFaceLevel[cmpt],
// fLevel
// );
// }
// }
// }
//
// // Check if we picked up any opposite differing level
// for (direction dir = 0; dir < vector::nComponents; dir++)
// {
// if
// (
// plusFaceLevel[dir] != -1
// && minFaceLevel[dir] != -1
// && plusFaceLevel[dir] != minFaceLevel[dir]
// )
// {
// candidateCellSet.insert(celli);
// }
// }
//}
const scalar oppositeCos = Foam::cos(degToRad(135.0));
for (const label celli : transitionCells)
{
const cell& cFaces = cells[celli];
label cLevel = cutter.cellLevel()[celli];
// Detect opposite intersection
bool foundOpposite = false;
forAll(cFaces, cFacei)
{
label facei = cFaces[cFacei];
if
(
surfaceIndex[facei] != -1
&& cutter.faceLevel(facei) > cLevel
)
{
// Get outwards pointing normal
vector n = fA[facei]/mag(fA[facei]);
if (faceOwner[facei] != celli)
{
n = -n;
}
// Check for any opposite intersection
forAll(cFaces, cFaceI2)
{
label face2i = cFaces[cFaceI2];
if
(
face2i != facei
&& surfaceIndex[face2i] != -1
)
{
// Get outwards pointing normal
vector n2 = fA[face2i]/mag(fA[face2i]);
if (faceOwner[face2i] != celli)
{
n2 = -n2;
}
if ((n&n2) < oppositeCos)
{
foundOpposite = true;
break;
}
}
}
if (foundOpposite)
{
break;
}
}
}
if (foundOpposite)
{
candidateCellSet.insert(celli);
}
}
if (debug&meshRefinement::MESH)
{
Pout<< "Dumping " << candidateCellSet.size()
<< " cells to cellSet candidateCellSet." << endl;
candidateCellSet.instance() = meshRefiner_.timeName();
candidateCellSet.write();
}
candidateCells = candidateCellSet.toc();
}
labelList cellsToRefine
(
meshRefiner_.meshCutter().consistentRefinement
(
candidateCells,
true
)
);
Info<< "Determined cells to refine in = "
<< mesh.time().cpuTimeIncrement() << " s" << endl;
label nCellsToRefine = cellsToRefine.size();
reduce(nCellsToRefine, sumOp<label>());
Info<< "Selected for refinement : " << nCellsToRefine
<< " cells (out of " << mesh.globalData().nTotalCells()
<< ')' << endl;
// Stop when no cells to refine. After a few iterations check if too
// few cells
if
(
nCellsToRefine == 0
|| (
iter >= 1
&& nCellsToRefine <= refineParams.minRefineCells()
)
)
{
Info<< "Stopping refining since too few cells selected."
<< nl << endl;
break;
}
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
if
(
returnReduce
(
(mesh.nCells() >= refineParams.maxLocalCells()),
orOp<bool>()
)
)
{
meshRefiner_.balanceAndRefine
(
"interface cell refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
else
{
meshRefiner_.refineAndBalance
(
"interface cell refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
}
}
return iter;
}
void Foam::snappyRefineDriver::removeInsideCells
(
const refinementParameters& refineParams,
const label nBufferLayers
)
{
// Skip if no limitRegion and zero bufferLayers
if (meshRefiner_.limitShells().shells().size() == 0 && nBufferLayers == 0)
{
return;
}
if (dryRun_)
{
return;
}
Info<< nl
<< "Removing mesh beyond surface intersections" << nl
<< "------------------------------------------" << nl
<< endl;
const fvMesh& mesh = meshRefiner_.mesh();
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
// Remove any cells inside limitShells with level -1
meshRefiner_.removeLimitShells
(
nBufferLayers,
1,
globalToMasterPatch_,
globalToSlavePatch_,
refineParams.locationsInMesh(),
refineParams.zonesInMesh()
);
// Fix any additional (e.g. locationsOutsideMesh). Note: probably not
// necessary.
meshRefiner_.splitMesh
(
nBufferLayers, // nBufferLayers
refineParams.nErodeCellZone(),
globalToMasterPatch_,
globalToSlavePatch_,
refineParams.locationsInMesh(),
refineParams.zonesInMesh(),
refineParams.locationsOutsideMesh(),
setFormatter_
);
if (debug&meshRefinement::MESH)
{
const_cast<Time&>(mesh.time())++;
Pout<< "Writing subsetted mesh to time "
<< meshRefiner_.timeName() << endl;
meshRefiner_.write
(
meshRefinement::debugType(debug),
meshRefinement::writeType
(
meshRefinement::writeLevel()
| meshRefinement::WRITEMESH
),
mesh.time().path()/meshRefiner_.timeName()
);
Pout<< "Dumped mesh in = "
<< mesh.time().cpuTimeIncrement() << " s\n" << nl << endl;
}
}
Foam::label Foam::snappyRefineDriver::shellRefine
(
const refinementParameters& refineParams,
const label maxIter
)
{
if (dryRun_)
{
return 0;
}
if (refineParams.minRefineCells() == -1)
{
// Special setting to be able to restart shm on meshes with inconsistent
// cellLevel/pointLevel
return 0;
}
addProfiling(shell, "snappyHexMesh::refine::shell");
const fvMesh& mesh = meshRefiner_.mesh();
// Mark current boundary faces with 0. Have meshRefiner maintain them.
meshRefiner_.userFaceData().setSize(1);
// mark list to remove any refined faces
meshRefiner_.userFaceData()[0].first() = meshRefinement::REMOVE;
meshRefiner_.userFaceData()[0].second() = ListOps::createWithValue<label>
(
mesh.nFaces(),
meshRefiner_.intersectedFaces(),
0, // set value
-1 // default value
);
// Determine the maximum refinement level over all volume refinement
// regions. This determines the minimum number of shell refinement
// iterations.
label overallMaxShellLevel = meshRefiner_.shells().maxLevel();
label iter;
for (iter = 0; iter < maxIter; iter++)
{
Info<< nl
<< "Shell refinement iteration " << iter << nl
<< "----------------------------" << nl
<< endl;
labelList candidateCells
(
meshRefiner_.refineCandidates
(
refineParams.locationsInMesh(),
refineParams.curvature(),
refineParams.planarAngle(),
false, // featureRefinement
true, // featureDistanceRefinement
true, // internalRefinement
false, // surfaceRefinement
false, // curvatureRefinement
false, // smallFeatureRefinement
false, // gapRefinement
false, // bigGapRefinement
false, // spreadGapSize
refineParams.maxGlobalCells(),
refineParams.maxLocalCells()
)
);
if (debug&meshRefinement::MESH)
{
Pout<< "Dumping " << candidateCells.size()
<< " cells to cellSet candidateCellsFromShells." << endl;
cellSet c(mesh, "candidateCellsFromShells", candidateCells);
c.instance() = meshRefiner_.timeName();
c.write();
}
// Problem choosing starting faces for bufferlayers (bFaces)
// - we can't use the current intersected boundary faces
// (intersectedFaces) since this grows indefinitely
// - if we use 0 faces we don't satisfy bufferLayers from the
// surface.
// - possibly we want to have bFaces only the initial set of faces
// and maintain the list while doing the refinement.
labelList bFaces
(
findIndices(meshRefiner_.userFaceData()[0].second(), 0)
);
//Info<< "Collected boundary faces : "
// << returnReduce(bFaces.size(), sumOp<label>()) << endl;
labelList cellsToRefine;
if (refineParams.nBufferLayers() <= 2)
{
cellsToRefine = meshRefiner_.meshCutter().consistentSlowRefinement
(
refineParams.nBufferLayers(),
candidateCells, // cells to refine
bFaces, // faces for nBufferLayers
1, // point difference
meshRefiner_.intersectedPoints() // points to check
);
}
else
{
cellsToRefine = meshRefiner_.meshCutter().consistentSlowRefinement2
(
refineParams.nBufferLayers(),
candidateCells, // cells to refine
bFaces // faces for nBufferLayers
);
}
Info<< "Determined cells to refine in = "
<< mesh.time().cpuTimeIncrement() << " s" << endl;
label nCellsToRefine = cellsToRefine.size();
reduce(nCellsToRefine, sumOp<label>());
Info<< "Selected for internal refinement : " << nCellsToRefine
<< " cells (out of " << mesh.globalData().nTotalCells()
<< ')' << endl;
// Stop when no cells to refine or have done minimum necessary
// iterations and not enough cells to refine.
if
(
nCellsToRefine == 0
|| (
iter >= overallMaxShellLevel
&& nCellsToRefine <= refineParams.minRefineCells()
)
)
{
Info<< "Stopping refining since too few cells selected."
<< nl << endl;
break;
}
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
if
(
returnReduce
(
(mesh.nCells() >= refineParams.maxLocalCells()),
orOp<bool>()
)
)
{
meshRefiner_.balanceAndRefine
(
"shell refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
else
{
meshRefiner_.refineAndBalance
(
"shell refinement iteration " + name(iter),
decomposer_,
distributor_,
cellsToRefine,
refineParams.maxLoadUnbalance()
);
}
}
meshRefiner_.userFaceData().clear();
return iter;
}
Foam::label Foam::snappyRefineDriver::directionalShellRefine
(
const refinementParameters& refineParams,
const label maxIter
)
{
if (dryRun_)
{
return 0;
}
addProfiling(shell, "snappyHexMesh::refine::directionalShell");
const fvMesh& mesh = meshRefiner_.mesh();
const shellSurfaces& shells = meshRefiner_.shells();
labelList& cellLevel =
const_cast<labelIOList&>(meshRefiner_.meshCutter().cellLevel());
labelList& pointLevel =
const_cast<labelIOList&>(meshRefiner_.meshCutter().pointLevel());
// Determine the minimum and maximum cell levels that are candidates for
// directional refinement
const labelPairList dirSelect(shells.directionalSelectLevel());
label overallMinLevel = labelMax;
label overallMaxLevel = labelMin;
forAll(dirSelect, shelli)
{
overallMinLevel = min(dirSelect[shelli].first(), overallMinLevel);
overallMaxLevel = max(dirSelect[shelli].second(), overallMaxLevel);
}
if (overallMinLevel > overallMaxLevel)
{
return 0;
}
// Maintain directional refinement levels
List<labelVector> dirCellLevel(cellLevel.size());
forAll(cellLevel, celli)
{
dirCellLevel[celli] = labelVector::uniform(cellLevel[celli]);
}
label iter;
for (iter = 0; iter < maxIter; iter++)
{
Info<< nl
<< "Directional shell refinement iteration " << iter << nl
<< "----------------------------------------" << nl
<< endl;
label nAllRefine = 0;
for (direction dir = 0; dir < vector::nComponents; dir++)
{
// Select the cells that need to be refined in certain direction:
// - cell inside/outside shell
// - original cellLevel (using mapping) mentioned in levelIncrement
// - dirCellLevel not yet up to cellLevel+levelIncrement
// Extract component of directional level
labelList currentLevel(dirCellLevel.size());
forAll(dirCellLevel, celli)
{
currentLevel[celli] = dirCellLevel[celli][dir];
}
labelList candidateCells
(
meshRefiner_.directionalRefineCandidates
(
refineParams.maxGlobalCells(),
refineParams.maxLocalCells(),
currentLevel,
dir
)
);
// Extend to keep 2:1 ratio
labelList cellsToRefine
(
meshRefiner_.meshCutter().consistentRefinement
(
currentLevel,
candidateCells,
true
)
);
Info<< "Determined cells to refine in = "
<< mesh.time().cpuTimeIncrement() << " s" << endl;
label nCellsToRefine = cellsToRefine.size();
reduce(nCellsToRefine, sumOp<label>());
Info<< "Selected for direction " << vector::componentNames[dir]
<< " refinement : " << nCellsToRefine
<< " cells (out of " << mesh.globalData().nTotalCells()
<< ')' << endl;
nAllRefine += nCellsToRefine;
// Stop when no cells to refine or have done minimum necessary
// iterations and not enough cells to refine.
if (nCellsToRefine > 0)
{
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
const bitSet isRefineCell(mesh.nCells(), cellsToRefine);
autoPtr<mapPolyMesh> map
(
meshRefiner_.directionalRefine
(
"directional refinement iteration " + name(iter),
dir,
cellsToRefine
)
);
Info<< "Refined mesh in = "
<< mesh.time().cpuTimeIncrement() << " s" << endl;
meshRefinement::updateList
(
map().cellMap(),
labelVector(0, 0, 0),
dirCellLevel
);
// Note: edges will have been split. The points might have
// inherited pointLevel from either side of the edge which
// might not be the same for coupled edges so sync
syncTools::syncPointList
(
mesh,
pointLevel,
maxEqOp<label>(),
labelMin
);
forAll(map().cellMap(), celli)
{
if (isRefineCell[map().cellMap()[celli]])
{
dirCellLevel[celli][dir]++;
}
}
// Do something with the pointLevel. See discussion about the
// cellLevel. Do we keep min/max ?
forAll(map().pointMap(), pointi)
{
label oldPointi = map().pointMap()[pointi];
if (map().reversePointMap()[oldPointi] != pointi)
{
// Is added point (splitting an edge)
pointLevel[pointi]++;
}
}
}
}
if (nAllRefine == 0)
{
Info<< "Stopping refining since no cells selected."
<< nl << endl;
break;
}
meshRefiner_.printMeshInfo
(
debug,
"After directional refinement iteration " + name(iter)
);
if (debug&meshRefinement::MESH)
{
Pout<< "Writing directional refinement iteration "
<< iter << " mesh to time " << meshRefiner_.timeName() << endl;
meshRefiner_.write
(
meshRefinement::debugType(debug),
meshRefinement::writeType
(
meshRefinement::writeLevel()
| meshRefinement::WRITEMESH
),
mesh.time().path()/meshRefiner_.timeName()
);
}
}
// Adjust cellLevel from dirLevel? As max? Or the min?
// For now: use max. The idea is that if there is a wall
// any directional refinement is likely to be aligned with
// the wall (wall layers) so any snapping/layering would probably
// want to use this highest refinement level.
forAll(cellLevel, celli)
{
cellLevel[celli] = cmptMax(dirCellLevel[celli]);
}
return iter;
}
void Foam::snappyRefineDriver::mergeAndSmoothRatio
(
const scalarList& allSeedPointDist,
const label nSmoothExpansion,
List<Tuple2<scalar, scalar>>& keyAndValue
)
{
// Merge duplicate distance from coupled locations to get unique
// distances to operate on, do on master
SortableList<scalar> unmergedDist(allSeedPointDist);
DynamicList<scalar> mergedDist;
scalar prevDist = GREAT;
forAll(unmergedDist, i)
{
scalar curDist = unmergedDist[i];
scalar difference = mag(curDist - prevDist);
if (difference > meshRefiner_.mergeDistance())
//if (difference > 0.01)
{
mergedDist.append(curDist);
prevDist = curDist;
}
}
// Sort the unique distances
SortableList<scalar> sortedDist(mergedDist);
labelList indexSet = sortedDist.indices();
// Get updated position starting from original (undistorted) mesh
scalarList seedPointsNewLocation = sortedDist;
scalar initResidual = 0.0;
scalar prevIterResidual = GREAT;
for (label iter = 0; iter < nSmoothExpansion; iter++)
{
// Position based edge averaging algorithm operated on
// all seed plane locations in normalized form.
//
// 0 1 2 3 4 5 6 (edge numbers)
// ---x---x---x---x---x---x---
// 0 1 2 3 4 5 (point numbers)
//
// Average of edge 1-3 in terms of position
// = (point3 - point0)/3
// Keeping points 0-1 frozen, new position of point 2
// = position2 + (average of edge 1-3 as above)
for(label i = 2; i<mergedDist.size()-1; i++)
{
scalar oldX00 = sortedDist[i-2];
scalar oldX1 = sortedDist[i+1];
scalar curX0 = seedPointsNewLocation[i-1];
seedPointsNewLocation[i] = curX0 + (oldX1 - oldX00)/3;
}
const scalarField residual(seedPointsNewLocation-sortedDist);
{
scalar res(sumMag(residual));
if (iter == 0)
{
initResidual = res;
}
res /= initResidual;
if (mag(prevIterResidual - res) < SMALL)
{
if (debug)
{
Pout<< "Converged with iteration " << iter
<< " initResidual: " << initResidual
<< " final residual : " << res << endl;
}
break;
}
else
{
prevIterResidual = res;
}
}
// Update the field for next iteration, avoid moving points
sortedDist = seedPointsNewLocation;
}
keyAndValue.setSize(mergedDist.size());
forAll(mergedDist, i)
{
keyAndValue[i].first() = mergedDist[i];
label index = indexSet[i];
keyAndValue[i].second() = seedPointsNewLocation[index];
}
}
Foam::label Foam::snappyRefineDriver::directionalSmooth
(
const refinementParameters& refineParams
)
{
addProfiling(split, "snappyHexMesh::refine::smooth");
Info<< nl
<< "Directional expansion ratio smoothing" << nl
<< "-------------------------------------" << nl
<< endl;
fvMesh& baseMesh = meshRefiner_.mesh();
const searchableSurfaces& geometry = meshRefiner_.surfaces().geometry();
const shellSurfaces& shells = meshRefiner_.shells();
label iter = 0;
forAll(shells.nSmoothExpansion(), shellI)
{
if
(
shells.nSmoothExpansion()[shellI] > 0
|| shells.nSmoothPosition()[shellI] > 0
)
{
label surfi = shells.shells()[shellI];
const vector& userDirection = shells.smoothDirection()[shellI];
// Extract inside points
labelList pointLabels;
{
// Get inside points
List<volumeType> volType;
geometry[surfi].getVolumeType(baseMesh.points(), volType);
label nInside = 0;
forAll(volType, pointi)
{
if (volType[pointi] == volumeType::INSIDE)
{
nInside++;
}
}
pointLabels.setSize(nInside);
nInside = 0;
forAll(volType, pointi)
{
if (volType[pointi] == volumeType::INSIDE)
{
pointLabels[nInside++] = pointi;
}
}
//bitSet isInsidePoint(baseMesh.nPoints());
//forAll(volType, pointi)
//{
// if (volType[pointi] == volumeType::INSIDE)
// {
// isInsidePoint.set(pointi);
// }
//}
//pointLabels = isInsidePoint.used();
}
// Mark all directed edges
bitSet isXEdge(baseMesh.edges().size());
{
const edgeList& edges = baseMesh.edges();
forAll(edges, edgei)
{
const edge& e = edges[edgei];
vector eVec(e.vec(baseMesh.points()));
eVec /= mag(eVec);
if (mag(eVec&userDirection) > 0.9)
{
isXEdge.set(edgei);
}
}
}
// Get the extreme of smoothing region and
// normalize all points within
const scalar totalLength =
geometry[surfi].bounds().span()
& userDirection;
const scalar startPosition =
geometry[surfi].bounds().min()
& userDirection;
scalarField normalizedPosition(pointLabels.size(), Zero);
forAll(pointLabels, i)
{
label pointi = pointLabels[i];
normalizedPosition[i] =
(
((baseMesh.points()[pointi]&userDirection) - startPosition)
/ totalLength
);
}
// Sort the normalized position
labelList order;
sortedOrder(normalizedPosition, order);
DynamicList<scalar> seedPointDist;
// Select points from finest refinement (one point-per plane)
scalar prevDist = GREAT;
forAll(order, i)
{
label pointi = order[i];
scalar curDist = normalizedPosition[pointi];
if (mag(curDist - prevDist) > meshRefiner_.mergeDistance())
{
seedPointDist.append(curDist);
prevDist = curDist;
}
}
// Collect data from all processors
scalarList allSeedPointDist;
{
List<scalarList> gatheredDist(Pstream::nProcs());
gatheredDist[Pstream::myProcNo()] = seedPointDist;
Pstream::gatherList(gatheredDist);
// Combine processor lists into one big list.
allSeedPointDist =
ListListOps::combine<scalarList>
(
gatheredDist, accessOp<scalarList>()
);
}
// Pre-set the points not to smooth (after expansion)
bitSet isFrozenPoint(baseMesh.nPoints(), true);
{
scalar minSeed = min(allSeedPointDist);
Pstream::scatter(minSeed);
scalar maxSeed = max(allSeedPointDist);
Pstream::scatter(maxSeed);
forAll(normalizedPosition, posI)
{
const scalar pos = normalizedPosition[posI];
if
(
(mag(pos-minSeed) < meshRefiner_.mergeDistance())
|| (mag(pos-maxSeed) < meshRefiner_.mergeDistance())
)
{
// Boundary point: freeze
isFrozenPoint.set(pointLabels[posI]);
}
else
{
// Internal to moving region
isFrozenPoint.unset(pointLabels[posI]);
}
}
}
Info<< "Smoothing " << geometry[surfi].name() << ':' << nl
<< " Direction : " << userDirection << nl
<< " Number of points : "
<< returnReduce(pointLabels.size(), sumOp<label>())
<< " (out of " << baseMesh.globalData().nTotalPoints()
<< ")" << nl
<< " Smooth expansion iterations : "
<< shells.nSmoothExpansion()[shellI] << nl
<< " Smooth position iterations : "
<< shells.nSmoothPosition()[shellI] << nl
<< " Number of planes : "
<< allSeedPointDist.size()
<< endl;
// Make lookup from original normalized distance to new value
List<Tuple2<scalar, scalar>> keyAndValue(allSeedPointDist.size());
// Filter unique seed distances and iterate for user given steps
// or until convergence. Then get back map from old to new distance
if (Pstream::master())
{
mergeAndSmoothRatio
(
allSeedPointDist,
shells.nSmoothExpansion()[shellI],
keyAndValue
);
}
Pstream::scatter(keyAndValue);
// Construct an iterpolation table for further queries
// - although normalized values are used for query,
// it might flow out of bounds due to precision, hence clamped
const interpolationTable<scalar> table
(
keyAndValue,
bounds::repeatableBounding::CLAMP,
"undefined"
);
// Now move the points directly on the baseMesh.
pointField baseNewPoints(baseMesh.points());
forAll(pointLabels, i)
{
label pointi = pointLabels[i];
const point& curPoint = baseMesh.points()[pointi];
scalar curDist = normalizedPosition[i];
scalar newDist = table(curDist);
scalar newPosition = startPosition + newDist*totalLength;
baseNewPoints[pointi] +=
userDirection * (newPosition - (curPoint &userDirection));
}
// Moving base mesh with expansion ratio smoothing
vectorField disp(baseNewPoints-baseMesh.points());
syncTools::syncPointList
(
baseMesh,
disp,
maxMagSqrEqOp<vector>(),
point::zero
);
baseMesh.movePoints(baseMesh.points()+disp);
if (debug&meshRefinement::MESH)
{
const_cast<Time&>(baseMesh.time())++;
Pout<< "Writing directional expansion ratio smoothed"
<< " mesh to time " << meshRefiner_.timeName() << endl;
meshRefiner_.write
(
meshRefinement::debugType(debug),
meshRefinement::writeType
(
meshRefinement::writeLevel()
| meshRefinement::WRITEMESH
),
baseMesh.time().path()/meshRefiner_.timeName()
);
}
// Now we have moved the points in user specified region. Smooth
// them with neighbour points to avoid skewed cells
// Instead of moving actual mesh, operate on copy
pointField baseMeshPoints(baseMesh.points());
scalar initResidual = 0.0;
scalar prevIterResidual = GREAT;
for (iter = 0; iter < shells.nSmoothPosition()[shellI]; iter++)
{
{
const edgeList& edges = baseMesh.edges();
const labelListList& pointEdges = baseMesh.pointEdges();
pointField unsmoothedPoints(baseMeshPoints);
scalarField sumOther(baseMesh.nPoints(), Zero);
labelList nSumOther(baseMesh.nPoints(), Zero);
labelList nSumXEdges(baseMesh.nPoints(), Zero);
forAll(edges, edgei)
{
const edge& e = edges[edgei];
sumOther[e[0]] +=
(unsmoothedPoints[e[1]]&userDirection);
nSumOther[e[0]]++;
sumOther[e[1]] +=
(unsmoothedPoints[e[0]]&userDirection);
nSumOther[e[1]]++;
if (isXEdge[edgei])
{
nSumXEdges[e[0]]++;
nSumXEdges[e[1]]++;
}
}
syncTools::syncPointList
(
baseMesh,
nSumXEdges,
plusEqOp<label>(),
label(0)
);
forAll(pointLabels, i)
{
label pointi = pointLabels[i];
if (nSumXEdges[pointi] < 2)
{
// Hanging node. Remove the (single!) X edge so it
// will follow points above or below instead
const labelList& pEdges = pointEdges[pointi];
forAll(pEdges, pE)
{
label edgei = pEdges[pE];
if (isXEdge[edgei])
{
const edge& e = edges[edgei];
label otherPt = e.otherVertex(pointi);
nSumOther[pointi]--;
sumOther[pointi] -=
(
unsmoothedPoints[otherPt]
& userDirection
);
}
}
}
}
syncTools::syncPointList
(
baseMesh,
sumOther,
plusEqOp<scalar>(),
scalar(0)
);
syncTools::syncPointList
(
baseMesh,
nSumOther,
plusEqOp<label>(),
label(0)
);
forAll(pointLabels, i)
{
label pointi = pointLabels[i];
if ((nSumOther[pointi] >= 2) && !isFrozenPoint[pointi])
{
scalar smoothPos =
0.5
*(
(unsmoothedPoints[pointi]&userDirection)
+sumOther[pointi]/nSumOther[pointi]
);
vector& v = baseNewPoints[pointi];
v += (smoothPos-(v&userDirection))*userDirection;
}
}
const vectorField residual(baseNewPoints - baseMeshPoints);
{
scalar res(gSum(mag(residual)));
if (iter == 0)
{
initResidual = res;
}
res /= initResidual;
if (mag(prevIterResidual - res) < SMALL)
{
Info<< "Converged smoothing in iteration " << iter
<< " initResidual: " << initResidual
<< " final residual : " << res << endl;
break;
}
else
{
prevIterResidual = res;
}
}
// Just copy new location instead of moving base mesh
baseMeshPoints = baseNewPoints;
}
}
// Move mesh to new location
vectorField dispSmooth(baseMeshPoints-baseMesh.points());
syncTools::syncPointList
(
baseMesh,
dispSmooth,
maxMagSqrEqOp<vector>(),
point::zero
);
baseMesh.movePoints(baseMesh.points()+dispSmooth);
if (debug&meshRefinement::MESH)
{
const_cast<Time&>(baseMesh.time())++;
Pout<< "Writing positional smoothing iteration "
<< iter << " mesh to time " << meshRefiner_.timeName()
<< endl;
meshRefiner_.write
(
meshRefinement::debugType(debug),
meshRefinement::writeType
(
meshRefinement::writeLevel()
| meshRefinement::WRITEMESH
),
baseMesh.time().path()/meshRefiner_.timeName()
);
}
}
}
return iter;
}
void Foam::snappyRefineDriver::baffleAndSplitMesh
(
const refinementParameters& refineParams,
const snapParameters& snapParams,
const bool handleSnapProblems,
const dictionary& motionDict
)
{
if (dryRun_)
{
return;
}
addProfiling(split, "snappyHexMesh::refine::splitting");
Info<< nl
<< "Splitting mesh at surface intersections" << nl
<< "---------------------------------------" << nl
<< endl;
const fvMesh& mesh = meshRefiner_.mesh();
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
// Introduce baffles at surface intersections. Note:
// meshRefinement::surfaceIndex() will
// be like boundary face from now on so not coupled anymore.
meshRefiner_.baffleAndSplitMesh
(
handleSnapProblems, // detect&remove potential snap problem
// Snap problem cell detection
snapParams,
refineParams.useTopologicalSnapDetection(),
false, // perpendicular edge connected cells
scalarField(0), // per region perpendicular angle
refineParams.nErodeCellZone(),
motionDict,
const_cast<Time&>(mesh.time()),
globalToMasterPatch_,
globalToSlavePatch_,
refineParams.locationsInMesh(),
refineParams.zonesInMesh(),
refineParams.locationsOutsideMesh(),
setFormatter_
);
if (!handleSnapProblems) // merge free standing baffles?
{
meshRefiner_.mergeFreeStandingBaffles
(
snapParams,
refineParams.useTopologicalSnapDetection(),
false, // perpendicular edge connected cells
scalarField(0), // per region perpendicular angle
refineParams.planarAngle(),
motionDict,
const_cast<Time&>(mesh.time()),
globalToMasterPatch_,
globalToSlavePatch_,
refineParams.locationsInMesh(),
refineParams.locationsOutsideMesh(),
setFormatter_
);
}
}
void Foam::snappyRefineDriver::zonify
(
const refinementParameters& refineParams,
wordPairHashTable& zonesToFaceZone
)
{
// Mesh is at its finest. Do zoning
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This puts all faces with intersection across a zoneable surface
// into that surface's faceZone. All cells inside faceZone get given the
// same cellZone.
const labelList namedSurfaces =
surfaceZonesInfo::getNamedSurfaces(meshRefiner_.surfaces().surfZones());
if
(
namedSurfaces.size()
|| refineParams.zonesInMesh().size()
)
{
Info<< nl
<< "Introducing zones for interfaces" << nl
<< "--------------------------------" << nl
<< endl;
const fvMesh& mesh = meshRefiner_.mesh();
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
meshRefiner_.zonify
(
refineParams.allowFreeStandingZoneFaces(),
refineParams.nErodeCellZone(),
refineParams.locationsInMesh(),
refineParams.zonesInMesh(),
zonesToFaceZone
);
if (debug&meshRefinement::MESH)
{
Pout<< "Writing zoned mesh to time "
<< meshRefiner_.timeName() << endl;
meshRefiner_.write
(
meshRefinement::debugType(debug),
meshRefinement::writeType
(
meshRefinement::writeLevel()
| meshRefinement::WRITEMESH
),
mesh.time().path()/meshRefiner_.timeName()
);
}
// Check that all faces are synced
meshRefinement::checkCoupledFaceZones(mesh);
}
}
void Foam::snappyRefineDriver::splitAndMergeBaffles
(
const refinementParameters& refineParams,
const snapParameters& snapParams,
const bool handleSnapProblems,
const dictionary& motionDict
)
{
if (dryRun_)
{
return;
}
Info<< nl
<< "Handling cells with snap problems" << nl
<< "---------------------------------" << nl
<< endl;
const fvMesh& mesh = meshRefiner_.mesh();
// Introduce baffles and split mesh
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
const scalarField& perpAngle = meshRefiner_.surfaces().perpendicularAngle();
meshRefiner_.baffleAndSplitMesh
(
handleSnapProblems,
// Snap problem cell detection
snapParams,
refineParams.useTopologicalSnapDetection(),
handleSnapProblems, // remove perp edge connected cells
perpAngle, // perp angle
refineParams.nErodeCellZone(),
motionDict,
const_cast<Time&>(mesh.time()),
globalToMasterPatch_,
globalToSlavePatch_,
refineParams.locationsInMesh(),
refineParams.zonesInMesh(),
refineParams.locationsOutsideMesh(),
setFormatter_
);
// Merge free-standing baffles always
meshRefiner_.mergeFreeStandingBaffles
(
snapParams,
refineParams.useTopologicalSnapDetection(),
handleSnapProblems,
perpAngle,
refineParams.planarAngle(),
motionDict,
const_cast<Time&>(mesh.time()),
globalToMasterPatch_,
globalToSlavePatch_,
refineParams.locationsInMesh(),
refineParams.locationsOutsideMesh(),
setFormatter_
);
if (debug)
{
const_cast<Time&>(mesh.time())++;
}
// Duplicate points on baffles that are on more than one cell
// region. This will help snapping pull them to separate surfaces.
meshRefiner_.dupNonManifoldPoints();
// Merge all baffles that are still remaining after duplicating points.
List<labelPair> couples(localPointRegion::findDuplicateFacePairs(mesh));
label nCouples = returnReduce(couples.size(), sumOp<label>());
Info<< "Detected unsplittable baffles : " << nCouples << endl;
if (nCouples > 0)
{
// Actually merge baffles. Note: not exactly parallellized. Should
// convert baffle faces into processor faces if they resulted
// from them.
meshRefiner_.mergeBaffles(couples, Map<label>(0));
if (debug)
{
// Debug:test all is still synced across proc patches
meshRefiner_.checkData();
}
// Remove any now dangling parts
meshRefiner_.splitMeshRegions
(
globalToMasterPatch_,
globalToSlavePatch_,
refineParams.locationsInMesh(),
refineParams.locationsOutsideMesh(),
setFormatter_
);
if (debug)
{
// Debug:test all is still synced across proc patches
meshRefiner_.checkData();
}
Info<< "Merged free-standing baffles in = "
<< mesh.time().cpuTimeIncrement() << " s." << endl;
}
if (debug&meshRefinement::MESH)
{
Pout<< "Writing handleProblemCells mesh to time "
<< meshRefiner_.timeName() << endl;
meshRefiner_.write
(
meshRefinement::debugType(debug),
meshRefinement::writeType
(
meshRefinement::writeLevel()
| meshRefinement::WRITEMESH
),
mesh.time().path()/meshRefiner_.timeName()
);
}
}
void Foam::snappyRefineDriver::addFaceZones
(
meshRefinement& meshRefiner,
const refinementParameters& refineParams,
const HashTable<Pair<word>>& faceZoneToPatches
)
{
if (faceZoneToPatches.size())
{
Info<< nl
<< "Adding patches for face zones" << nl
<< "-----------------------------" << nl
<< endl;
Info<< setf(ios_base::left)
<< setw(6) << "Patch"
<< setw(20) << "Type"
<< setw(30) << "Name"
<< setw(30) << "FaceZone"
<< setw(10) << "FaceType"
<< nl
<< setw(6) << "-----"
<< setw(20) << "----"
<< setw(30) << "----"
<< setw(30) << "--------"
<< setw(10) << "--------"
<< endl;
const polyMesh& mesh = meshRefiner.mesh();
// Add patches for added inter-region faceZones
forAllConstIters(faceZoneToPatches, iter)
{
const word& fzName = iter.key();
const Pair<word>& patchNames = iter.val();
// Get any user-defined faceZone data
surfaceZonesInfo::faceZoneType fzType;
dictionary patchInfo = refineParams.getZoneInfo(fzName, fzType);
const word& masterName = fzName;
//const word slaveName = fzName + "_slave";
//const word slaveName = czNames.second()+"_to_"+czNames.first();
const word& slaveName = patchNames.second();
label mpi = meshRefiner.addMeshedPatch(masterName, patchInfo);
Info<< setf(ios_base::left)
<< setw(6) << mpi
<< setw(20) << mesh.boundaryMesh()[mpi].type()
<< setw(30) << masterName
<< setw(30) << fzName
<< setw(10) << surfaceZonesInfo::faceZoneTypeNames[fzType]
<< nl;
label sli = meshRefiner.addMeshedPatch(slaveName, patchInfo);
Info<< setf(ios_base::left)
<< setw(6) << sli
<< setw(20) << mesh.boundaryMesh()[sli].type()
<< setw(30) << slaveName
<< setw(30) << fzName
<< setw(10) << surfaceZonesInfo::faceZoneTypeNames[fzType]
<< nl;
meshRefiner.addFaceZone(fzName, masterName, slaveName, fzType);
}
Info<< endl;
}
}
void Foam::snappyRefineDriver::mergePatchFaces
(
const meshRefinement::FaceMergeType mergeType,
const refinementParameters& refineParams,
const dictionary& motionDict
)
{
if (dryRun_)
{
return;
}
addProfiling(merge, "snappyHexMesh::refine::merge");
Info<< nl
<< "Merge refined boundary faces" << nl
<< "----------------------------" << nl
<< endl;
const fvMesh& mesh = meshRefiner_.mesh();
if
(
mergeType == meshRefinement::FaceMergeType::GEOMETRIC
|| mergeType == meshRefinement::FaceMergeType::IGNOREPATCH
)
{
meshRefiner_.mergePatchFacesUndo
(
Foam::cos(degToRad(45.0)),
Foam::cos(degToRad(45.0)),
meshRefiner_.meshedPatches(),
motionDict,
labelList(mesh.nFaces(), -1),
mergeType
);
}
else
{
// Still merge refined boundary faces if all four are on same patch
meshRefiner_.mergePatchFaces
(
Foam::cos(degToRad(45.0)),
Foam::cos(degToRad(45.0)),
4, // only merge faces split into 4
meshRefiner_.meshedPatches(),
meshRefinement::FaceMergeType::GEOMETRIC // no merge across patches
);
}
if (debug)
{
meshRefiner_.checkData();
}
meshRefiner_.mergeEdgesUndo(Foam::cos(degToRad(45.0)), motionDict);
if (debug)
{
meshRefiner_.checkData();
}
}
void Foam::snappyRefineDriver::doRefine
(
const dictionary& refineDict,
const refinementParameters& refineParams,
const snapParameters& snapParams,
const bool prepareForSnapping,
const meshRefinement::FaceMergeType mergeType,
const dictionary& motionDict
)
{
addProfiling(refine, "snappyHexMesh::refine");
Info<< nl
<< "Refinement phase" << nl
<< "----------------" << nl
<< endl;
const fvMesh& mesh = meshRefiner_.mesh();
// Check that all the keep points are inside the mesh.
refineParams.findCells(true, mesh, refineParams.locationsInMesh());
// Check that all the keep points are inside the mesh.
if (dryRun_)
{
refineParams.findCells(true, mesh, refineParams.locationsOutsideMesh());
}
// Estimate cell sizes
if (dryRun_)
{
snappyVoxelMeshDriver voxelDriver
(
meshRefiner_,
globalToMasterPatch_,
globalToSlavePatch_
);
voxelDriver.doRefine(refineParams);
}
// Refine around feature edges
featureEdgeRefine
(
refineParams,
100, // maxIter
0 // min cells to refine
);
// Initial automatic gap-level refinement: gaps smaller than the cell size
if
(
max(meshRefiner_.surfaces().maxGapLevel()) > 0
|| max(meshRefiner_.shells().maxGapLevel()) > 0
)
{
// In case we use automatic gap level refinement do some pre-refinement
// (fast) since it is so slow.
// Refine based on surface
surfaceOnlyRefine
(
refineParams,
20 // maxIter
);
// Refine cells that contain a gap
smallFeatureRefine
(
refineParams,
100 // maxIter
);
}
// Refine based on surface
surfaceOnlyRefine
(
refineParams,
100 // maxIter
);
// Pass1 of automatic gap-level refinement: surface-intersected cells
// in narrow gaps. Done early so we can remove the inside
gapOnlyRefine
(
refineParams,
100 // maxIter
);
// Remove cells inbetween two surfaces
surfaceProximityBlock
(
refineParams,
1 //100 // maxIter
);
// Remove cells (a certain distance) beyond surface intersections
removeInsideCells
(
refineParams,
1 // nBufferLayers
);
// Pass2 of automatic gap-level refinement: all cells in gaps
bigGapOnlyRefine
(
refineParams,
true, // spreadGapSize
100 // maxIter
);
// Internal mesh refinement
shellRefine
(
refineParams,
100 // maxIter
);
// Remove any extra cells from limitRegion with level -1, without
// adding any buffer layer this time
removeInsideCells
(
refineParams,
0 // nBufferLayers
);
// Refine any hexes with 5 or 6 faces refined to make smooth edges
danglingCellRefine
(
refineParams,
21, // 1 coarse face + 5 refined faces
100 // maxIter
);
danglingCellRefine
(
refineParams,
24, // 0 coarse faces + 6 refined faces
100 // maxIter
);
// Refine any cells with differing refinement level on either side
refinementInterfaceRefine
(
refineParams,
10 // maxIter
);
// Directional shell refinement
directionalShellRefine
(
refineParams,
100 // maxIter
);
if
(
max(meshRefiner_.shells().nSmoothExpansion()) > 0
|| max(meshRefiner_.shells().nSmoothPosition()) > 0
)
{
directionalSmooth(refineParams);
}
// Introduce baffles at surface intersections. Remove sections unreachable
// from keepPoint.
baffleAndSplitMesh
(
refineParams,
snapParams,
prepareForSnapping,
motionDict
);
// Mesh is at its finest. Do optional zoning (cellZones and faceZones)
wordPairHashTable zonesToFaceZone;
zonify(refineParams, zonesToFaceZone);
// Create pairs of patches for faceZones
{
HashTable<Pair<word>> faceZoneToPatches(zonesToFaceZone.size());
// Note: zonesToFaceZone contains the same data on different
// processors but in different order. We could sort the
// contents but instead just loop in sortedToc order.
List<Pair<word>> czs(zonesToFaceZone.sortedToc());
forAll(czs, i)
{
const Pair<word>& czNames = czs[i];
const word& fzName = zonesToFaceZone[czNames];
const word& masterName = fzName;
const word slaveName = czNames.second() + "_to_" + czNames.first();
Pair<word> patches(masterName, slaveName);
faceZoneToPatches.insert(fzName, patches);
}
addFaceZones(meshRefiner_, refineParams, faceZoneToPatches);
}
// Pull baffles apart
splitAndMergeBaffles
(
refineParams,
snapParams,
prepareForSnapping,
motionDict
);
// Do something about cells with refined faces on the boundary
if (prepareForSnapping)
{
mergePatchFaces(mergeType, refineParams, motionDict);
}
if (!dryRun_ && Pstream::parRun())
{
Info<< nl
<< "Doing final balancing" << nl
<< "---------------------" << nl
<< endl;
// Do final balancing. Keep zoned faces on one processor since the
// snap phase will convert them to baffles and this only works for
// internal faces.
meshRefiner_.balance
(
true, // keepZoneFaces
false, // keepBaffles
scalarField(mesh.nCells(), 1), // cellWeights
decomposer_,
distributor_
);
}
}
// ************************************************************************* //