lagrangian: Support meshToMesh mapping

Lagrangian is now compatible with the meshToMesh topology changer. If a
cloud is being simulated and this topology changer is active, then the
cloud data will be automatically mapped between the specified sequence
of meshes in the same way as the finite volume data. This works both for
serial and parallel simulations.

In addition, mapFieldsPar now also supports mapping of Lagrangian data
when run in parallel.
This commit is contained in:
Will Bainbridge
2022-10-11 08:09:25 +01:00
parent 9e9ab2204c
commit 03b0619ee1
70 changed files with 4079 additions and 4013 deletions

View File

@ -1,5 +1,6 @@
mapLagrangian.C mapLagrangian.C
mapMeshes.C mapMeshes.C
mapFields.C mapFields.C
meshToMesh0.C
EXE = $(FOAM_APPBIN)/mapFields EXE = $(FOAM_APPBIN)/mapFields

View File

@ -2,12 +2,10 @@ EXE_INC = \
-I$(LIB_SRC)/parallel/decompose/decompositionMethods/lnInclude \ -I$(LIB_SRC)/parallel/decompose/decompositionMethods/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude \ -I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/lagrangian/basic/lnInclude \ -I$(LIB_SRC)/lagrangian/basic/lnInclude \
-I$(LIB_SRC)/finiteVolume/lnInclude \ -I$(LIB_SRC)/finiteVolume/lnInclude
-I$(LIB_SRC)/sampling/lnInclude
EXE_LIBS = \ EXE_LIBS = \
-ldecompositionMethods \ -ldecompositionMethods \
-lsampling \
-lmeshTools \ -lmeshTools \
-llagrangian \ -llagrangian \
-lfiniteVolume \ -lfiniteVolume \

View File

@ -0,0 +1,762 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2022 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/>.
\*---------------------------------------------------------------------------*/
#include "meshToMesh0.H"
#include "processorFvPatch.H"
#include "demandDrivenData.H"
#include "treeDataCell.H"
#include "treeDataFace.H"
#include "tetOverlapVolume.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(meshToMesh0, 0);
}
const Foam::scalar Foam::meshToMesh0::directHitTol = 1e-5;
void Foam::meshToMesh0::calcAddressing()
{
if (debug)
{
InfoInFunction
<< "Calculating mesh-to-mesh cell addressing" << endl;
}
// Set reference to cells
const cellList& fromCells = fromMesh_.cells();
const pointField& fromPoints = fromMesh_.points();
// In an attempt to preserve the efficiency of linear search and prevent
// failure, a RESCUE mechanism will be set up. Here, we shall mark all
// cells next to the solid boundaries. If such a cell is found as the
// closest, the relationship between the origin and cell will be examined.
// If the origin is outside the cell, a global n-squared search is
// triggered.
// SETTING UP RESCUE
// Visit all boundaries and mark the cell next to the boundary.
if (debug)
{
InfoInFunction << "Setting up rescue" << endl;
}
List<bool> boundaryCell(fromCells.size(), false);
// Set reference to boundary
const polyPatchList& patchesFrom = fromMesh_.boundaryMesh();
forAll(patchesFrom, patchi)
{
// Get reference to cells next to the boundary
const labelUList& bCells = patchesFrom[patchi].faceCells();
forAll(bCells, facei)
{
boundaryCell[bCells[facei]] = true;
}
}
treeBoundBox meshBb(fromPoints);
scalar typDim = meshBb.avgDim()/(2.0*cbrt(scalar(fromCells.size())));
treeBoundBox shiftedBb
(
meshBb.min(),
meshBb.max() + vector(typDim, typDim, typDim)
);
if (debug)
{
Info<< "\nMesh\n"
<< " bounding box : " << meshBb << nl
<< " bounding box (shifted) : " << shiftedBb << nl
<< " typical dimension :" << shiftedBb.typDim() << endl;
}
indexedOctree<treeDataCell> oc
(
treeDataCell(false, fromMesh_, polyMesh::CELL_TETS),
shiftedBb, // overall bounding box
8, // maxLevel
10, // leafsize
6.0 // duplicity
);
if (debug)
{
oc.print(Pout, false, 0);
}
cellAddresses
(
cellAddressing_,
toMesh_.cellCentres(),
fromMesh_,
boundaryCell,
oc
);
forAll(toMesh_.boundaryMesh(), patchi)
{
const polyPatch& toPatch = toMesh_.boundaryMesh()[patchi];
if (cuttingPatches_.found(toPatch.name()))
{
boundaryAddressing_[patchi].setSize(toPatch.size());
cellAddresses
(
boundaryAddressing_[patchi],
toPatch.faceCentres(),
fromMesh_,
boundaryCell,
oc
);
}
else if
(
patchMap_.found(toPatch.name())
&& fromMeshPatches_.found(patchMap_.find(toPatch.name())())
)
{
const polyPatch& fromPatch = fromMesh_.boundaryMesh()
[
fromMeshPatches_.find(patchMap_.find(toPatch.name())())()
];
if (fromPatch.empty())
{
WarningInFunction
<< "Source patch " << fromPatch.name()
<< " has no faces. Not performing mapping for it."
<< endl;
boundaryAddressing_[patchi].setSize(toPatch.size());
boundaryAddressing_[patchi] = -1;
}
else
{
treeBoundBox wallBb(fromPatch.localPoints());
scalar typDim =
wallBb.avgDim()/(2.0*sqrt(scalar(fromPatch.size())));
treeBoundBox shiftedBb
(
wallBb.min(),
wallBb.max() + vector(typDim, typDim, typDim)
);
// Note: allow more levels than in meshSearch. Assume patch
// is not as big as all boundary faces
indexedOctree<treeDataFace> oc
(
treeDataFace(false, fromPatch),
shiftedBb, // overall search domain
12, // maxLevel
10, // leafsize
6.0 // duplicity
);
const vectorField::subField centresToBoundary =
toPatch.faceCentres();
boundaryAddressing_[patchi].setSize(toPatch.size());
const scalar distSqr = sqr(wallBb.mag());
forAll(toPatch, toi)
{
boundaryAddressing_[patchi][toi] = oc.findNearest
(
centresToBoundary[toi],
distSqr
).index();
}
}
}
}
if (debug)
{
InfoInFunction
<< "Finished calculating mesh-to-mesh cell addressing" << endl;
}
}
void Foam::meshToMesh0::cellAddresses
(
labelList& cellAddressing_,
const pointField& points,
const fvMesh& fromMesh,
const List<bool>& boundaryCell,
const indexedOctree<treeDataCell>& oc
) const
{
// The implemented search method is a simple neighbour array search.
// It starts from a cell zero, searches its neighbours and finds one
// which is nearer to the target point than the current position.
// The location of the "current position" is reset to that cell and
// search through the neighbours continues. The search is finished
// when all the neighbours of the cell are farther from the target
// point than the current cell
// Set curCell label to zero (start)
label curCell = 0;
// Set reference to cell to cell addressing
const vectorField& centresFrom = fromMesh.cellCentres();
const labelListList& cc = fromMesh.cellCells();
forAll(points, toi)
{
// Pick up target position
const vector& p = points[toi];
// Set the sqr-distance
scalar distSqr = magSqr(p - centresFrom[curCell]);
bool closer;
do
{
closer = false;
// Set the current list of neighbouring cells
const labelList& neighbours = cc[curCell];
forAll(neighbours, ni)
{
const scalar curDistSqr =
magSqr(p - centresFrom[neighbours[ni]]);
// Search through all the neighbours.
// If the cell is closer, reset current cell and distance
if (curDistSqr < (1 - small)*distSqr)
{
curCell = neighbours[ni];
distSqr = curDistSqr;
closer = true; // A closer neighbour has been found
}
}
} while (closer);
cellAddressing_[toi] = -1;
// Check point is actually in the nearest cell
if (fromMesh.pointInCell(p, curCell))
{
cellAddressing_[toi] = curCell;
}
else
{
// If curCell is a boundary cell then the point maybe either outside
// the domain or in an other region of the doamin, either way use
// the octree search to find it.
if (boundaryCell[curCell])
{
cellAddressing_[toi] = oc.findInside(p);
if (cellAddressing_[toi] != -1)
{
curCell = cellAddressing_[toi];
}
}
else
{
// If not on the boundary search the neighbours
bool found = false;
// Set the current list of neighbouring cells
const labelList& neighbours = cc[curCell];
forAll(neighbours, ni)
{
// Search through all the neighbours.
// If point is in neighbour reset current cell
if (fromMesh.pointInCell(p, neighbours[ni]))
{
cellAddressing_[toi] = neighbours[ni];
found = true;
break;
}
}
if (!found)
{
// If still not found search the neighbour-neighbours
// Set the current list of neighbouring cells
const labelList& neighbours = cc[curCell];
forAll(neighbours, ni)
{
// Set the current list of neighbour-neighbouring cells
const labelList& nn = cc[neighbours[ni]];
forAll(nn, ni)
{
// Search through all the neighbours.
// If point is in neighbour reset current cell
if (fromMesh.pointInCell(p, nn[ni]))
{
cellAddressing_[toi] = nn[ni];
found = true;
break;
}
}
if (found) break;
}
}
if (!found)
{
// Still not found so use the octree
cellAddressing_[toi] = oc.findInside(p);
if (cellAddressing_[toi] != -1)
{
curCell = cellAddressing_[toi];
}
}
}
}
}
}
void Foam::meshToMesh0::calculateInverseDistanceWeights() const
{
if (debug)
{
InfoInFunction
<< "Calculating inverse distance weighting factors" << endl;
}
if (inverseDistanceWeightsPtr_)
{
FatalErrorInFunction
<< "weighting factors already calculated"
<< exit(FatalError);
}
//- Initialise overlap volume to zero
V_ = 0.0;
inverseDistanceWeightsPtr_ = new scalarListList(toMesh_.nCells());
scalarListList& invDistCoeffs = *inverseDistanceWeightsPtr_;
// get reference to source mesh data
const labelListList& cc = fromMesh_.cellCells();
const vectorField& centreFrom = fromMesh_.C();
const vectorField& centreTo = toMesh_.C();
forAll(cellAddressing_, celli)
{
if (cellAddressing_[celli] != -1)
{
const vector& target = centreTo[celli];
scalar m = mag(target - centreFrom[cellAddressing_[celli]]);
const labelList& neighbours = cc[cellAddressing_[celli]];
// if the nearest cell is a boundary cell or there is a direct hit,
// pick up the value
label directCelli = -1;
if (m < directHitTol || neighbours.empty())
{
directCelli = celli;
}
else
{
forAll(neighbours, ni)
{
scalar nm = mag(target - centreFrom[neighbours[ni]]);
if (nm < directHitTol)
{
directCelli = neighbours[ni];
break;
}
}
}
if (directCelli != -1)
{
// Direct hit
invDistCoeffs[directCelli].setSize(1);
invDistCoeffs[directCelli][0] = 1.0;
V_ += fromMesh_.V()[cellAddressing_[directCelli]];
}
else
{
invDistCoeffs[celli].setSize(neighbours.size() + 1);
// The first coefficient corresponds to the centre cell.
// The rest is ordered in the same way as the cellCells list.
scalar invDist = 1.0/m;
invDistCoeffs[celli][0] = invDist;
scalar sumInvDist = invDist;
// now add the neighbours
forAll(neighbours, ni)
{
invDist = 1.0/mag(target - centreFrom[neighbours[ni]]);
invDistCoeffs[celli][ni + 1] = invDist;
sumInvDist += invDist;
}
// divide by the total inverse-distance
forAll(invDistCoeffs[celli], i)
{
invDistCoeffs[celli][i] /= sumInvDist;
}
V_ +=
invDistCoeffs[celli][0]
*fromMesh_.V()[cellAddressing_[celli]];
for (label i = 1; i < invDistCoeffs[celli].size(); i++)
{
V_ +=
invDistCoeffs[celli][i]*fromMesh_.V()[neighbours[i-1]];
}
}
}
}
}
void Foam::meshToMesh0::calculateInverseVolumeWeights() const
{
if (debug)
{
InfoInFunction
<< "Calculating inverse volume weighting factors" << endl;
}
if (inverseVolumeWeightsPtr_)
{
FatalErrorInFunction
<< "weighting factors already calculated"
<< exit(FatalError);
}
//- Initialise overlap volume to zero
V_ = 0.0;
inverseVolumeWeightsPtr_ = new scalarListList(toMesh_.nCells());
scalarListList& invVolCoeffs = *inverseVolumeWeightsPtr_;
const labelListList& cellToCell = cellToCellAddressing();
tetOverlapVolume overlapEngine;
forAll(cellToCell, celli)
{
const labelList& overlapCells = cellToCell[celli];
if (overlapCells.size() > 0)
{
invVolCoeffs[celli].setSize(overlapCells.size());
forAll(overlapCells, j)
{
label cellFrom = overlapCells[j];
treeBoundBox bbFromMesh
(
pointField
(
fromMesh_.points(),
fromMesh_.cellPoints()[cellFrom]
)
);
scalar v = overlapEngine.cellCellOverlapVolumeMinDecomp
(
toMesh_,
celli,
fromMesh_,
cellFrom,
bbFromMesh
);
invVolCoeffs[celli][j] = v/toMesh_.V()[celli];
V_ += v;
}
}
}
}
void Foam::meshToMesh0::calculateCellToCellAddressing() const
{
if (debug)
{
InfoInFunction
<< "Calculating cell to cell addressing" << endl;
}
if (cellToCellAddressingPtr_)
{
FatalErrorInFunction
<< "addressing already calculated"
<< exit(FatalError);
}
//- Initialise overlap volume to zero
V_ = 0.0;
tetOverlapVolume overlapEngine;
cellToCellAddressingPtr_ = new labelListList(toMesh_.nCells());
labelListList& cellToCell = *cellToCellAddressingPtr_;
forAll(cellToCell, iTo)
{
const labelList overLapCells =
overlapEngine.overlappingCells(fromMesh_, toMesh_, iTo);
if (overLapCells.size() > 0)
{
cellToCell[iTo].setSize(overLapCells.size());
forAll(overLapCells, j)
{
cellToCell[iTo][j] = overLapCells[j];
V_ += fromMesh_.V()[overLapCells[j]];
}
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::meshToMesh0::meshToMesh0
(
const fvMesh& meshFrom,
const fvMesh& meshTo,
const HashTable<word>& patchMap,
const wordList& cuttingPatchNames
)
:
fromMesh_(meshFrom),
toMesh_(meshTo),
patchMap_(patchMap),
cellAddressing_(toMesh_.nCells()),
boundaryAddressing_(toMesh_.boundaryMesh().size()),
inverseDistanceWeightsPtr_(nullptr),
inverseVolumeWeightsPtr_(nullptr),
cellToCellAddressingPtr_(nullptr),
V_(0.0)
{
forAll(fromMesh_.boundaryMesh(), patchi)
{
fromMeshPatches_.insert
(
fromMesh_.boundaryMesh()[patchi].name(),
patchi
);
}
forAll(toMesh_.boundaryMesh(), patchi)
{
toMeshPatches_.insert
(
toMesh_.boundaryMesh()[patchi].name(),
patchi
);
}
forAll(cuttingPatchNames, i)
{
if (toMeshPatches_.found(cuttingPatchNames[i]))
{
cuttingPatches_.insert
(
cuttingPatchNames[i],
toMeshPatches_.find(cuttingPatchNames[i])()
);
}
else
{
WarningInFunction
<< "Cannot find cutting-patch " << cuttingPatchNames[i]
<< " in destination mesh" << endl;
}
}
forAll(toMesh_.boundaryMesh(), patchi)
{
// Add the processor patches in the toMesh to the cuttingPatches list
if (isA<processorPolyPatch>(toMesh_.boundaryMesh()[patchi]))
{
cuttingPatches_.insert
(
toMesh_.boundaryMesh()[patchi].name(),
patchi
);
}
}
calcAddressing();
}
Foam::meshToMesh0::meshToMesh0
(
const fvMesh& meshFrom,
const fvMesh& meshTo
)
:
fromMesh_(meshFrom),
toMesh_(meshTo),
cellAddressing_(toMesh_.nCells()),
boundaryAddressing_(toMesh_.boundaryMesh().size()),
inverseDistanceWeightsPtr_(nullptr),
inverseVolumeWeightsPtr_(nullptr),
cellToCellAddressingPtr_(nullptr),
V_(0.0)
{
// check whether both meshes have got the same number
// of boundary patches
if (fromMesh_.boundary().size() != toMesh_.boundary().size())
{
FatalErrorInFunction
<< "Incompatible meshes: different number of patches, "
<< "fromMesh = " << fromMesh_.boundary().size()
<< ", toMesh = " << toMesh_.boundary().size()
<< exit(FatalError);
}
forAll(fromMesh_.boundaryMesh(), patchi)
{
if
(
fromMesh_.boundaryMesh()[patchi].name()
!= toMesh_.boundaryMesh()[patchi].name()
)
{
FatalErrorInFunction
<< "Incompatible meshes: different patch names for patch "
<< patchi
<< ", fromMesh = " << fromMesh_.boundary()[patchi].name()
<< ", toMesh = " << toMesh_.boundary()[patchi].name()
<< exit(FatalError);
}
if
(
fromMesh_.boundaryMesh()[patchi].type()
!= toMesh_.boundaryMesh()[patchi].type()
)
{
FatalErrorInFunction
<< "Incompatible meshes: different patch types for patch "
<< patchi
<< ", fromMesh = " << fromMesh_.boundary()[patchi].type()
<< ", toMesh = " << toMesh_.boundary()[patchi].type()
<< exit(FatalError);
}
fromMeshPatches_.insert
(
fromMesh_.boundaryMesh()[patchi].name(),
patchi
);
toMeshPatches_.insert
(
toMesh_.boundaryMesh()[patchi].name(),
patchi
);
patchMap_.insert
(
toMesh_.boundaryMesh()[patchi].name(),
fromMesh_.boundaryMesh()[patchi].name()
);
}
calcAddressing();
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::meshToMesh0::~meshToMesh0()
{
deleteDemandDrivenData(inverseDistanceWeightsPtr_);
deleteDemandDrivenData(inverseVolumeWeightsPtr_);
deleteDemandDrivenData(cellToCellAddressingPtr_);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
const Foam::scalarListList& Foam::meshToMesh0::inverseDistanceWeights() const
{
if (!inverseDistanceWeightsPtr_)
{
calculateInverseDistanceWeights();
}
return *inverseDistanceWeightsPtr_;
}
const Foam::scalarListList& Foam::meshToMesh0::inverseVolumeWeights() const
{
if (!inverseVolumeWeightsPtr_)
{
calculateInverseVolumeWeights();
}
return *inverseVolumeWeightsPtr_;
}
const Foam::labelListList& Foam::meshToMesh0::cellToCellAddressing() const
{
if (!cellToCellAddressingPtr_)
{
calculateCellToCellAddressing();
}
return *cellToCellAddressingPtr_;
}
// ************************************************************************* //

View File

@ -1,5 +1,5 @@
mapMeshes.C mapGeometricFields.C
mapLagrangian.C mapClouds.C
mapFieldsPar.C mapFieldsPar.C
EXE = $(FOAM_APPBIN)/mapFieldsPar EXE = $(FOAM_APPBIN)/mapFieldsPar

View File

@ -1,11 +1,9 @@
EXE_INC = \ EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \ -I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/lagrangian/basic/lnInclude \ -I$(LIB_SRC)/lagrangian/basic/lnInclude \
-I$(LIB_SRC)/finiteVolume/lnInclude \ -I$(LIB_SRC)/finiteVolume/lnInclude
-I$(LIB_SRC)/sampling/lnInclude
EXE_LIBS = \ EXE_LIBS = \
-lsampling \
-lmeshTools \ -lmeshTools \
-llagrangian \ -llagrangian \
-lfiniteVolume \ -lfiniteVolume \

View File

@ -1,184 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2022 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/>.
InNamespace
Foam
Description
Gets the indices of (source)particles that have been appended to the
target cloud and maps the lagrangian fields accordingly.
\*---------------------------------------------------------------------------*/
#ifndef MapLagrangianFields_H
#define MapLagrangianFields_H
#include "cloud.H"
#include "GeometricField.H"
#include "meshToMesh.H"
#include "IOobjectList.H"
#include "CompactIOField.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
//- Gets the indices of (source)particles that have been appended to the
// target cloud and maps the lagrangian fields accordingly.
template<class Type>
void MapLagrangianFields
(
const string& cloudName,
const IOobjectList& objects,
const polyMesh& meshTarget,
const labelList& addParticles
)
{
{
IOobjectList fields = objects.lookupClass(IOField<Type>::typeName);
forAllIter(IOobjectList, fields, fieldIter)
{
const word& fieldName = fieldIter()->name();
Info<< " mapping lagrangian field " << fieldName << endl;
// Read field (does not need mesh)
IOField<Type> fieldSource(*fieldIter());
// Map
IOField<Type> fieldTarget
(
IOobject
(
fieldName,
meshTarget.time().timeName(),
cloud::prefix/cloudName,
meshTarget,
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
addParticles.size()
);
forAll(addParticles, i)
{
fieldTarget[i] = fieldSource[addParticles[i]];
}
// Write field
fieldTarget.write();
}
}
{
IOobjectList fieldFields =
objects.lookupClass(IOField<Field<Type>>::typeName);
forAllIter(IOobjectList, fieldFields, fieldIter)
{
const word& fieldName = fieldIter()->name();
Info<< " mapping lagrangian fieldField " << fieldName << endl;
// Read field (does not need mesh)
IOField<Field<Type>> fieldSource(*fieldIter());
// Map - use CompactIOField to automatically write in
// compact form for binary format.
CompactIOField<Field<Type>> fieldTarget
(
IOobject
(
fieldName,
meshTarget.time().timeName(),
cloud::prefix/cloudName,
meshTarget,
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
addParticles.size()
);
forAll(addParticles, i)
{
fieldTarget[i] = fieldSource[addParticles[i]];
}
// Write field
fieldTarget.write();
}
}
{
IOobjectList fieldFields =
objects.lookupClass(CompactIOField<Field<Type>>::typeName);
forAllIter(IOobjectList, fieldFields, fieldIter)
{
Info<< " mapping lagrangian fieldField "
<< fieldIter()->name() << endl;
// Read field (does not need mesh)
CompactIOField<Field<Type>> fieldSource(*fieldIter());
// Map
CompactIOField<Field<Type>> fieldTarget
(
IOobject
(
fieldIter()->name(),
meshTarget.time().timeName(),
cloud::prefix/cloudName,
meshTarget,
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
addParticles.size()
);
forAll(addParticles, i)
{
fieldTarget[i] = fieldSource[addParticles[i]];
}
// Write field
fieldTarget.write();
}
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,287 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2022 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/>.
\*---------------------------------------------------------------------------*/
#include "mapClouds.H"
#include "fvMeshToFvMesh.H"
#include "IOobjectList.H"
#include "OSspecific.H"
#include "passiveParticleCloud.H"
#include "patchToPatchTools.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
template<class Type>
List<Type> gatherAndFlatten(const List<Type>& l)
{
List<List<Type>> procLs(Pstream::nProcs());
procLs[Pstream::myProcNo()] = l;
Pstream::gatherList(procLs);
Pstream::scatterList(procLs);
return
HashSet<Type>
(
ListListOps::combine<List<Type>>
(
procLs,
accessOp<List<Type>>()
)
).sortedToc();
}
template<class ReadIOField, class WriteIOField>
void mapCloudTypeFields
(
const fileName& cloudDir,
const IOobjectList& objects,
const polyMesh& srcMesh,
const polyMesh& tgtMesh,
const distributionMap& map
)
{
const wordList fieldNames =
gatherAndFlatten(objects.lookupClass(ReadIOField::typeName).names());
forAll(fieldNames, fieldi)
{
const word& fieldName = fieldNames[fieldi];
Info<< " mapping lagrangian field " << fieldName << endl;
const IOobject* fieldIOPtr = objects.lookup(fieldName);
// Read the field
ReadIOField field
(
fieldIOPtr != nullptr
? *fieldIOPtr
: IOobject
(
fieldName,
srcMesh.time().timeName(),
cloud::prefix/cloudDir,
srcMesh,
IOobject::NO_READ,
IOobject::NO_WRITE,
false
)
);
// Distribute the field
map.distribute(field);
// Write it out into the target case
if (field.size())
{
WriteIOField
(
IOobject
(
fieldName,
tgtMesh.time().timeName(),
cloud::prefix/cloudDir,
tgtMesh,
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
move(field)
).write();
}
}
}
template<class Type>
void mapCloudTypeFieldsAndFieldFields
(
const fileName& cloudDir,
const IOobjectList& objects,
const polyMesh& srcMesh,
const polyMesh& tgtMesh,
const distributionMap& map
)
{
mapCloudTypeFields
<
IOField<Type>,
IOField<Type>
>
(
cloudDir,
objects,
srcMesh,
tgtMesh,
map
);
mapCloudTypeFields
<
IOField<Field<Type>>,
CompactIOField<Field<Type>>
>
(
cloudDir,
objects,
srcMesh,
tgtMesh,
map
);
mapCloudTypeFields
<
CompactIOField<Field<Type>>,
CompactIOField<Field<Type>>
>
(
cloudDir,
objects,
srcMesh,
tgtMesh,
map
);
}
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
void Foam::mapClouds(const fvMeshToFvMesh& interp)
{
const polyMesh& srcMesh = interp.srcMesh();
const polyMesh& tgtMesh = interp.tgtMesh();
// Determine the clouds present in this mesh
const fileNameList cloudDirs =
gatherAndFlatten
(
readDir
(
srcMesh.time().timePath()/cloud::prefix,
fileType::directory
)
);
forAll(cloudDirs, cloudi)
{
Info<< nl << "Mapping cloud " << cloudDirs[cloudi] << endl;
// Read the source cloud positions
passiveParticleCloud srcCloud(srcMesh, cloudDirs[cloudi], false);
Info<< " read "
<< returnReduce(srcCloud.size(), sumOp<label>())
<< " parcels from source mesh." << endl;
// Unpack into position and cell lists and construct a send map
pointField positions(srcCloud.size());
labelList tgtCells(srcCloud.size(), -1);
List<DynamicList<label>> sendsDyn(Pstream::nProcs());
{
label srcParticlei = 0;
forAllConstIter(Cloud<passiveParticle>, srcCloud, iter)
{
const point pos = iter().position(srcMesh);
const remote tgtProcCell =
interp.srcToTgtPoint(iter().cell(), pos);
positions[srcParticlei] = pos;
tgtCells[srcParticlei] = tgtProcCell.elementi;
sendsDyn[tgtProcCell.proci].append(srcParticlei ++);
}
}
labelListList sends;
patchToPatchTools::transferListList(sends, sendsDyn);
// Build a distribution map from the send map
autoPtr<distributionMap> mapPtr =
patchToPatchTools::constructDistributionMap(sends);
const distributionMap& map = mapPtr();
// Distribute the positions and target cell indices
map.distribute(positions);
map.distribute(tgtCells);
// Construct the target cloud positions
passiveParticleCloud tgtCloud
(
tgtMesh,
cloudDirs[cloudi],
IDLList<passiveParticle>()
);
forAll(positions, tgtParticlei)
{
tgtCloud.addParticle
(
new passiveParticle
(
tgtMesh,
positions[tgtParticlei],
tgtCells[tgtParticlei]
)
);
}
Info<< " mapped "
<< returnReduce(tgtCloud.size(), sumOp<label>())
<< " parcels to the target mesh." << endl;
// Write the positions
IOPosition<passiveParticleCloud>(tgtCloud).write();
// Search for Lagrangian objects for this time
IOobjectList objects
(
srcMesh,
srcMesh.time().timeName(),
cloud::prefix/cloudDirs[cloudi]
);
// Map and write the fields
#define MapCloudTypeFields(Type, nullArg) \
mapCloudTypeFieldsAndFieldFields<Type> \
( \
cloudDirs[cloudi], \
objects, \
srcMesh, \
tgtMesh, \
map \
);
MapCloudTypeFields(label, );
FOR_ALL_FIELD_TYPES(MapCloudTypeFields);
#undef MapCloudTypeFields
}
}
// ************************************************************************* //

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org \\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2021 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2022 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -21,35 +21,31 @@ License
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
InNamespace
Foam
Description
Maps Lagrangian clouds
SourceFiles
mapClouds.C
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef UnMapped_H #ifndef mapClouds_H
#define UnMapped_H #define mapClouds_H
#include "IOobjectList.H"
#include "OSspecific.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam namespace Foam
{ {
template<class Type> class fvMeshToFvMesh;
void UnMapped(const IOobjectList& objects)
{ void mapClouds(const fvMeshToFvMesh& interp);
IOobjectList fields = objects.lookupClass(Type::typeName);
forAllConstIter(IOobjectList, fields, fieldIter)
{
mvBak(fieldIter()->objectPath(false), "unmapped");
}
} }
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif #endif

View File

@ -31,11 +31,84 @@ Description
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "argList.H" #include "argList.H"
#include "mapMeshes.H" #include "fvMeshToFvMesh.H"
#include "mapGeometricFields.H"
#include "mapClouds.H"
#include "cellVolumeWeightMethod.H" #include "cellVolumeWeightMethod.H"
using namespace Foam; using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
void mapConsistentMesh
(
const fvMesh& srcMesh,
const fvMesh& tgtMesh,
const word& mapMethod,
const HashSet<word>& selectedFields,
const bool noLagrangian
)
{
Info<< nl << "Consistently creating and mapping fields for time "
<< srcMesh.time().timeName() << nl << endl;
fvMeshToFvMesh interp(srcMesh, tgtMesh, mapMethod);
Info<< nl << "Mapping geometric fields" << endl;
mapGeometricFields
(
interp,
selectedFields,
noLagrangian
);
if (!noLagrangian)
{
mapClouds(interp);
}
}
void mapSubMesh
(
const fvMesh& srcMesh,
const fvMesh& tgtMesh,
const HashTable<word>& patchMap,
const wordList& cuttingPatches,
const word& mapMethod,
const HashSet<word>& selectedFields,
const bool noLagrangian
)
{
Info<< nl << "Creating and mapping fields for time "
<< srcMesh.time().timeName() << nl << endl;
fvMeshToFvMesh interp
(
srcMesh,
tgtMesh,
mapMethod,
patchMap,
cuttingPatches
);
Info<< nl << "Mapping geometric fields" << endl;
mapGeometricFields
(
interp,
selectedFields,
noLagrangian
);
if (!noLagrangian)
{
mapClouds(interp);
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -162,7 +235,7 @@ int main(int argc, char *argv[])
Info<< "\nCreate meshes\n" << endl; Info<< "\nCreate meshes\n" << endl;
fvMesh meshSource fvMesh srcMesh
( (
IOobject IOobject
( (
@ -173,7 +246,7 @@ int main(int argc, char *argv[])
false false
); );
fvMesh meshTarget fvMesh tgtMesh
( (
IOobject IOobject
( (
@ -184,15 +257,18 @@ int main(int argc, char *argv[])
false false
); );
Info<< "Source mesh size: " << meshSource.nCells() << tab Info<< "Source mesh size: "
<< "Target mesh size: " << meshTarget.nCells() << nl << endl; << returnReduce(srcMesh.nCells(), sumOp<label>())
<< ", Target mesh size: "
<< returnReduce(tgtMesh.nCells(), sumOp<label>())
<< endl;
if (consistent) if (consistent)
{ {
mapConsistentMesh mapConsistentMesh
( (
meshSource, srcMesh,
meshTarget, tgtMesh,
mapMethod, mapMethod,
selectedFields, selectedFields,
noLagrangian noLagrangian
@ -202,8 +278,8 @@ int main(int argc, char *argv[])
{ {
mapSubMesh mapSubMesh
( (
meshSource, srcMesh,
meshTarget, tgtMesh,
patchMap, patchMap,
cuttingPatches, cuttingPatches,
mapMethod, mapMethod,

View File

@ -23,12 +23,12 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef MapVolFields_H #include "mapGeometricFields.H"
#define MapVolFields_H #include "fvMeshToFvMesh.H"
#include "surfaceMesh.H"
#include "GeometricField.H" #include "pointMesh.H"
#include "meshToMesh.H"
#include "IOobjectList.H" #include "IOobjectList.H"
#include "OSspecific.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -118,19 +118,17 @@ void evaluateConstraintTypes(GeometricField<Type, fvPatchField, volMesh>& fld)
template<class Type> template<class Type>
void MapVolFields void mapVolTypeFields
( (
const IOobjectList& objects, const IOobjectList& objects,
const HashSet<word>& selectedFields, const HashSet<word>& selectedFields,
const meshToMesh& interp const fvMeshToFvMesh& interp
) )
{ {
typedef GeometricField<Type, fvPatchField, volMesh> fieldType; const fvMesh& srcMesh = static_cast<const fvMesh&>(interp.srcMesh());
const fvMesh& tgtMesh = static_cast<const fvMesh&>(interp.tgtMesh());
const fvMesh& meshSource = static_cast<const fvMesh&>(interp.srcRegion()); IOobjectList fields = objects.lookupClass(VolField<Type>::typeName);
const fvMesh& meshTarget = static_cast<const fvMesh&>(interp.tgtRegion());
IOobjectList fields = objects.lookupClass(fieldType::typeName);
forAllIter(IOobjectList, fields, fieldIter) forAllIter(IOobjectList, fields, fieldIter)
{ {
@ -138,13 +136,13 @@ void MapVolFields
if (selectedFields.empty() || selectedFields.found(fieldName)) if (selectedFields.empty() || selectedFields.found(fieldName))
{ {
const fieldType fieldSource(*fieldIter(), meshSource); const VolField<Type> fieldSource(*fieldIter(), srcMesh);
typeIOobject<fieldType> targetIO typeIOobject<VolField<Type>> targetIO
( (
fieldName, fieldName,
meshTarget.time().timeName(), tgtMesh.time().timeName(),
meshTarget, tgtMesh,
IOobject::MUST_READ IOobject::MUST_READ
); );
@ -152,7 +150,7 @@ void MapVolFields
{ {
Info<< " interpolating onto existing field " Info<< " interpolating onto existing field "
<< fieldName << endl; << fieldName << endl;
fieldType fieldTarget(targetIO, meshTarget); VolField<Type> fieldTarget(targetIO, tgtMesh);
interp.mapSrcToTgt(fieldSource, fieldTarget); interp.mapSrcToTgt(fieldSource, fieldTarget);
@ -167,9 +165,12 @@ void MapVolFields
targetIO.readOpt() = IOobject::NO_READ; targetIO.readOpt() = IOobject::NO_READ;
tmp<fieldType> tfieldTarget(interp.mapSrcToTgt(fieldSource)); tmp<VolField<Type>> tfieldTarget
(
interp.mapSrcToTgt(fieldSource)
);
fieldType fieldTarget(targetIO, tfieldTarget); VolField<Type> fieldTarget(targetIO, tfieldTarget);
evaluateConstraintTypes(fieldTarget); evaluateConstraintTypes(fieldTarget);
@ -180,12 +181,60 @@ void MapVolFields
} }
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // template<class Type, template<class> class GeoField>
void unMappedTypeFields(const IOobjectList& objects)
{
IOobjectList fields = objects.lookupClass(GeoField<Type>::typeName);
forAllConstIter(IOobjectList, fields, fieldIter)
{
mvBak(fieldIter()->objectPath(false), "unmapped");
}
}
} // End namespace Foam } // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif void Foam::mapGeometricFields
(
const fvMeshToFvMesh& interp,
const HashSet<word>& selectedFields,
const bool noLagrangian
)
{
const polyMesh& srcMesh = interp.srcMesh();
const polyMesh& tgtMesh = interp.tgtMesh();
{
// Search for list of source objects for this time
IOobjectList objects(srcMesh, srcMesh.time().timeName());
// Map the fields
#define MapVolTypeFields(Type, nullArg) \
mapVolTypeFields<Type> \
( \
objects, \
selectedFields, \
interp \
);
FOR_ALL_FIELD_TYPES(MapVolTypeFields);
#undef MapVolTypeFields
}
{
// Search for list of target objects for this time
IOobjectList objects(tgtMesh, tgtMesh.time().timeName());
// Mark surface and point fields as unmapped
#define UnMappedTypeFields(Type, GeoField) \
unMappedTypeFields<Type, GeoField>(objects);
FOR_ALL_FIELD_TYPES(UnMappedTypeFields, SurfaceField);
FOR_ALL_FIELD_TYPES(UnMappedTypeFields, PointField);
#undef UnMappedTypeFields
}
}
// ************************************************************************* // // ************************************************************************* //

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org \\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2018 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2022 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -25,27 +25,31 @@ InNamespace
Foam Foam
Description Description
Maps lagrangian positions and fields Maps geometric fields
SourceFiles SourceFiles
mapLagrangian.C mapGeometricFields.C
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef mapLagrangian_H #ifndef mapGeometricFields_H
#define mapLagrangian_H #define mapGeometricFields_H
#include "meshToMesh.H" #include "fvMesh.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam namespace Foam
{ {
//- Maps lagrangian positions and fields class fvMeshToFvMesh;
void mapLagrangian(const meshToMesh& interp);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // void mapGeometricFields
(
const fvMeshToFvMesh& interp,
const HashSet<word>& selectedFields,
const bool noLagrangian
);
} // End namespace Foam } // End namespace Foam

View File

@ -1,319 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2022 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/>.
\*---------------------------------------------------------------------------*/
#include "MapLagrangianFields.H"
#include "passiveParticleCloud.H"
#include "meshSearch.H"
#include "OSspecific.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
static const scalar perturbFactor = 1e-6;
// Special version of findCell that generates a cell guaranteed to be
// compatible with tracking.
static label findCell(const Cloud<passiveParticle>& cloud, const point& pt)
{
label celli = -1;
label tetFacei = -1;
label tetPtI = -1;
const polyMesh& mesh = cloud.pMesh();
mesh.findCellFacePt(pt, celli, tetFacei, tetPtI);
if (celli >= 0)
{
return celli;
}
else
{
// See if particle on face by finding nearest face and shifting
// particle.
meshSearch meshSearcher
(
mesh,
polyMesh::FACE_PLANES // no decomposition needed
);
label facei = meshSearcher.findNearestBoundaryFace(pt);
if (facei >= 0)
{
const point& cc = mesh.cellCentres()[mesh.faceOwner()[facei]];
const point perturbPt = (1-perturbFactor)*pt+perturbFactor*cc;
mesh.findCellFacePt(perturbPt, celli, tetFacei, tetPtI);
return celli;
}
}
return -1;
}
void mapLagrangian(const meshToMesh& interp)
{
// Determine which particles are in meshTarget
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const polyMesh& meshSource = interp.srcRegion();
const polyMesh& meshTarget = interp.tgtRegion();
const labelListList& sourceToTarget = interp.srcToTgtCellAddr();
fileNameList cloudDirs
(
readDir
(
meshSource.time().timePath()/cloud::prefix,
fileType::directory
)
);
forAll(cloudDirs, cloudI)
{
// Search for list of lagrangian objects for this time
IOobjectList objects
(
meshSource,
meshSource.time().timeName(),
cloud::prefix/cloudDirs[cloudI]
);
IOobject* positionsPtr = objects.lookup(word("positions"));
if (positionsPtr)
{
Info<< nl << " processing cloud " << cloudDirs[cloudI] << endl;
// Read positions & cell
passiveParticleCloud sourceParcels
(
meshSource,
cloudDirs[cloudI],
false
);
Info<< " read " << sourceParcels.size()
<< " parcels from source mesh." << endl;
// Construct empty target cloud
passiveParticleCloud targetParcels
(
meshTarget,
cloudDirs[cloudI],
IDLList<passiveParticle>()
);
passiveParticle::trackingData td(targetParcels);
label sourceParticleI = 0;
// Indices of source particles that get added to targetParcels
DynamicList<label> addParticles(sourceParcels.size());
// Unmapped particles
labelHashSet unmappedSource(sourceParcels.size());
// Initial: track from fine-mesh cell centre to particle position
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This requires there to be no boundary in the way.
forAllConstIter(Cloud<passiveParticle>, sourceParcels, iter)
{
bool foundCell = false;
// Assume that cell from read parcel is the correct one...
if (iter().cell() >= 0)
{
const labelList& targetCells =
sourceToTarget[iter().cell()];
// Particle probably in one of the targetcells. Try
// all by tracking from their cell centre to the parcel
// position.
forAll(targetCells, i)
{
// Track from its cellcentre to position to make sure.
autoPtr<passiveParticle> newPtr
(
new passiveParticle
(
meshTarget,
barycentric(1, 0, 0, 0),
targetCells[i],
meshTarget.cells()[targetCells[i]][0],
1
)
);
passiveParticle& newP = newPtr();
newP.track
(
meshTarget,
iter().position(meshSource)
- newP.position(meshTarget),
0
);
if (!newP.onFace())
{
// Hit position.
foundCell = true;
addParticles.append(sourceParticleI);
targetParcels.addParticle(newPtr.ptr());
break;
}
}
}
if (!foundCell)
{
// Store for closer analysis
unmappedSource.insert(sourceParticleI);
}
sourceParticleI++;
}
Info<< " after meshToMesh addressing found "
<< targetParcels.size()
<< " parcels in target mesh." << endl;
// Do closer inspection for unmapped particles
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if (unmappedSource.size())
{
sourceParticleI = 0;
forAllIter(Cloud<passiveParticle>, sourceParcels, iter)
{
if (unmappedSource.found(sourceParticleI))
{
label targetCell =
findCell
(
targetParcels,
iter().position(meshSource)
);
if (targetCell >= 0)
{
unmappedSource.erase(sourceParticleI);
addParticles.append(sourceParticleI);
targetParcels.addParticle
(
new passiveParticle
(
meshTarget,
iter().position(meshSource),
targetCell
)
);
sourceParcels.remove(&iter());
}
}
sourceParticleI++;
}
}
addParticles.shrink();
Info<< " after additional mesh searching found "
<< targetParcels.size() << " parcels in target mesh." << endl;
if (addParticles.size())
{
IOPosition<passiveParticleCloud>(targetParcels).write();
// addParticles now contains the indices of the sourceMesh
// particles that were appended to the target mesh.
// Map lagrangian fields
// ~~~~~~~~~~~~~~~~~~~~~
MapLagrangianFields<label>
(
cloudDirs[cloudI],
objects,
meshTarget,
addParticles
);
MapLagrangianFields<scalar>
(
cloudDirs[cloudI],
objects,
meshTarget,
addParticles
);
MapLagrangianFields<vector>
(
cloudDirs[cloudI],
objects,
meshTarget,
addParticles
);
MapLagrangianFields<sphericalTensor>
(
cloudDirs[cloudI],
objects,
meshTarget,
addParticles
);
MapLagrangianFields<symmTensor>
(
cloudDirs[cloudI],
objects,
meshTarget,
addParticles
);
MapLagrangianFields<tensor>
(
cloudDirs[cloudI],
objects,
meshTarget,
addParticles
);
}
}
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// ************************************************************************* //

View File

@ -1,169 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2022 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/>.
\*---------------------------------------------------------------------------*/
#include "mapMeshes.H"
#include "surfaceMesh.H"
#include "processorFvPatch.H"
#include "pointMesh.H"
#include "mapLagrangian.H"
#include "MapVolFields.H"
#include "UnMapped.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
void Foam::mapMesh
(
const meshToMesh& interp,
const HashSet<word>& selectedFields,
const bool noLagrangian
)
{
{
const polyMesh& meshSource = interp.srcRegion();
// Search for list of objects for this time
IOobjectList objects(meshSource, meshSource.time().timeName());
// Map volFields
// ~~~~~~~~~~~~~
MapVolFields<scalar>
(
objects,
selectedFields,
interp
);
MapVolFields<vector>
(
objects,
selectedFields,
interp
);
MapVolFields<sphericalTensor>
(
objects,
selectedFields,
interp
);
MapVolFields<symmTensor>
(
objects,
selectedFields,
interp
);
MapVolFields<tensor>
(
objects,
selectedFields,
interp
);
}
{
const polyMesh& meshTarget = interp.tgtRegion();
// Search for list of target objects for this time
IOobjectList objects(meshTarget, meshTarget.time().timeName());
// Mark surfaceFields as unmapped
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UnMapped<surfaceScalarField>(objects);
UnMapped<surfaceVectorField>(objects);
UnMapped<surfaceSphericalTensorField>(objects);
UnMapped<surfaceSymmTensorField>(objects);
UnMapped<surfaceTensorField>(objects);
// Mark pointFields as unmapped
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UnMapped<pointScalarField>(objects);
UnMapped<pointVectorField>(objects);
UnMapped<pointSphericalTensorField>(objects);
UnMapped<pointSymmTensorField>(objects);
UnMapped<pointTensorField>(objects);
}
if (!noLagrangian)
{
mapLagrangian(interp);
}
}
void Foam::mapConsistentMesh
(
const fvMesh& meshSource,
const fvMesh& meshTarget,
const word& mapMethod,
const HashSet<word>& selectedFields,
const bool noLagrangian
)
{
Info<< nl << "Consistently creating and mapping fields for time "
<< meshSource.time().timeName() << nl << endl;
meshToMesh interp(meshSource, meshTarget, mapMethod);
mapMesh
(
interp,
selectedFields,
noLagrangian
);
}
void Foam::mapSubMesh
(
const fvMesh& meshSource,
const fvMesh& meshTarget,
const HashTable<word>& patchMap,
const wordList& cuttingPatches,
const word& mapMethod,
const HashSet<word>& selectedFields,
const bool noLagrangian
)
{
Info<< nl << "Creating and mapping fields for time "
<< meshSource.time().timeName() << nl << endl;
meshToMesh interp
(
meshSource,
meshTarget,
mapMethod,
patchMap,
cuttingPatches
);
mapMesh
(
interp,
selectedFields,
noLagrangian
);
}
// ************************************************************************* //

View File

@ -27,9 +27,14 @@ License
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::polyMeshMap::polyMeshMap(const polyMesh& mesh) Foam::polyMeshMap::polyMeshMap
(
const polyMesh& mesh,
const meshToMesh& mapper
)
: :
mesh_(mesh) mesh_(mesh),
mapper_(mapper)
{} {}

View File

@ -42,7 +42,7 @@ SourceFiles
namespace Foam namespace Foam
{ {
class polyMesh; class meshToMesh;
/*---------------------------------------------------------------------------*\ /*---------------------------------------------------------------------------*\
Class polyMeshMap Declaration Class polyMeshMap Declaration
@ -55,13 +55,16 @@ class polyMeshMap
//- Reference to polyMesh //- Reference to polyMesh
const polyMesh& mesh_; const polyMesh& mesh_;
//- Reference to the meshToMesh mapper
const meshToMesh& mapper_;
public: public:
// Constructors // Constructors
//- Construct for mesh //- Construct for mesh
polyMeshMap(const polyMesh& mesh); polyMeshMap(const polyMesh& mesh, const meshToMesh& mapper);
//- Disallow default bitwise copy construction //- Disallow default bitwise copy construction
polyMeshMap(const polyMeshMap&) = delete; polyMeshMap(const polyMeshMap&) = delete;
@ -74,6 +77,12 @@ public:
{ {
return mesh_; return mesh_;
} }
//- Return meshToMesh mapper
const meshToMesh& mapper() const
{
return mapper_;
}
}; };

View File

@ -512,4 +512,7 @@ $(faceSources)/patchFluxToFace/patchFluxToFace.C
solver/solver.C solver/solver.C
solver/solverNew.C solver/solverNew.C
fvMeshToFvMesh/fvMeshToFvMesh.C
fvMeshToFvMesh/patchToPatchFvPatchFieldMapper.C
LIB = $(FOAM_LIBBIN)/libfiniteVolume LIB = $(FOAM_LIBBIN)/libfiniteVolume

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org \\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2022 OpenFOAM Foundation \\ / A nd | Copyright (C) 2022 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -23,54 +23,20 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef mapMeshes_H #include "fvMeshToFvMesh.H"
#define mapMeshes_H
#include "meshToMesh.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam namespace Foam
{ {
defineTypeNameAndDebug(fvMeshToFvMesh, 0);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // }
void mapMesh
(
const meshToMesh& interp,
const HashSet<word>& selectedFields,
const bool noLagrangian
);
void mapConsistentMesh // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
(
const fvMesh& meshSource,
const fvMesh& meshTarget,
const word& mapMethod,
const HashSet<word>& selectedFields,
const bool noLagrangian
);
Foam::fvMeshToFvMesh::~fvMeshToFvMesh()
{}
void mapSubMesh
(
const fvMesh& meshSource,
const fvMesh& meshTarget,
const HashTable<word>& patchMap,
const wordList& cuttingPatches,
const word& mapMethod,
const HashSet<word>& selectedFields,
const bool noLagrangian
);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* // // ************************************************************************* //

View File

@ -0,0 +1,109 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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/>.
Class
Foam::fvMeshToFvMesh
Description
SourceFiles
fvMeshToFvMeshTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef fvMeshToFvMesh_H
#define fvMeshToFvMesh_H
#include "meshToMesh.H"
#include "volFields.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class fvMeshToFvMesh Declaration
\*---------------------------------------------------------------------------*/
class fvMeshToFvMesh
:
public meshToMesh
{
public:
//- Run-time type information
TypeName("fvMeshToFvMesh");
// Constructors
//- Inherit base class' constructors
using meshToMesh::meshToMesh;
//- Destructor
virtual ~fvMeshToFvMesh();
// Member Functions
// Evaluation
// Source-to-target volume field mapping
//- Interpolate a field with a defined operation. Values
// passed in via 'result' are used to initialise the return
// value.
template<class Type>
void mapSrcToTgt
(
const GeometricField<Type, fvPatchField, volMesh>& field,
GeometricField<Type, fvPatchField, volMesh>& result
) const;
//- Interpolate a field with a defined operation. The initial
// values of the result are set to zero.
template<class Type>
tmp<GeometricField<Type, fvPatchField, volMesh>> mapSrcToTgt
(
const GeometricField<Type, fvPatchField, volMesh>& field
) const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "fvMeshToFvMeshTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,171 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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/>.
\*---------------------------------------------------------------------------*/
#include "fvMeshToFvMesh.H"
#include "directFvPatchFieldMapper.H"
#include "patchToPatchFvPatchFieldMapper.H"
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
template<class Type>
void Foam::fvMeshToFvMesh::mapSrcToTgt
(
const GeometricField<Type, fvPatchField, volMesh>& field,
GeometricField<Type, fvPatchField, volMesh>& result
) const
{
meshToMesh::mapSrcToTgt(field, result.primitiveFieldRef());
typename GeometricField<Type, fvPatchField, volMesh>::
Boundary& resultBf = result.boundaryFieldRef();
forAll(srcToTgtPatchIDs(), i)
{
const label srcPatchi = srcToTgtPatchIDs()[i].first();
const label tgtPatchi = srcToTgtPatchIDs()[i].second();
const fvPatchField<Type>& srcField = field.boundaryField()[srcPatchi];
fvPatchField<Type>& tgtField = resultBf[tgtPatchi];
// Clone and map (since rmap does not do general mapping)
tmp<fvPatchField<Type>> tnewTgt
(
fvPatchField<Type>::New
(
srcField,
tgtField.patch(),
result(),
patchToPatchFvPatchFieldMapper
(
srcToTgtPatchToPatches()[i],
true
)
)
);
// Transfer all mapped quantities (value and e.g. gradient) onto
// tgtField. Value will get overwritten below.
tgtField.rmap(tnewTgt(), identity(tgtField.size()));
}
forAll(tgtCuttingPatchIDs(), i)
{
const label patchi = tgtCuttingPatchIDs()[i];
fvPatchField<Type>& pf = resultBf[patchi];
pf == pf.patchInternalField();
}
}
template<class Type>
Foam::tmp<Foam::GeometricField<Type, Foam::fvPatchField, Foam::volMesh>>
Foam::fvMeshToFvMesh::mapSrcToTgt
(
const GeometricField<Type, fvPatchField, volMesh>& field
) const
{
typedef GeometricField<Type, fvPatchField, volMesh> fieldType;
const fvMesh& tgtMesh = static_cast<const fvMesh&>(meshToMesh::tgtMesh());
const fvBoundaryMesh& tgtBm = tgtMesh.boundary();
const typename fieldType::Boundary& srcBfld =
field.boundaryField();
PtrList<fvPatchField<Type>> tgtPatchFields(tgtBm.size());
// construct tgt boundary patch types as copy of 'field' boundary types
// note: this will provide place holders for fields with additional
// entries, but these values will need to be reset
forAll(srcToTgtPatchIDs(), i)
{
const label srcPatchi = srcToTgtPatchIDs()[i].first();
const label tgtPatchi = srcToTgtPatchIDs()[i].second();
if (!tgtPatchFields.set(tgtPatchi))
{
tgtPatchFields.set
(
tgtPatchi,
fvPatchField<Type>::New
(
srcBfld[srcPatchi],
tgtMesh.boundary()[tgtPatchi],
DimensionedField<Type, volMesh>::null(),
directFvPatchFieldMapper
(
labelList(tgtMesh.boundary()[tgtPatchi].size(), -1)
)
)
);
}
}
// Any unset tgtPatchFields become calculated
forAll(tgtPatchFields, tgtPatchi)
{
if (!tgtPatchFields.set(tgtPatchi))
{
// Note: use factory New method instead of direct generation of
// calculated so we keep constraints
tgtPatchFields.set
(
tgtPatchi,
fvPatchField<Type>::New
(
calculatedFvPatchField<Type>::typeName,
tgtMesh.boundary()[tgtPatchi],
DimensionedField<Type, volMesh>::null()
)
);
}
}
tmp<fieldType> tresult
(
new fieldType
(
IOobject
(
typedName("interpolate(" + field.name() + ")"),
tgtMesh.time().timeName(),
tgtMesh,
IOobject::NO_READ,
IOobject::NO_WRITE
),
tgtMesh,
field.dimensions(),
Field<Type>(tgtMesh.nCells(), Zero),
tgtPatchFields
)
);
mapSrcToTgt(field, tresult.ref());
return tresult;
}
// ************************************************************************* //

View File

@ -1,13 +1,11 @@
EXE_INC = \ EXE_INC = \
-I$(LIB_SRC)/triSurface/lnInclude \ -I$(LIB_SRC)/triSurface/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude \ -I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/sampling/lnInclude \
-I$(LIB_SRC)/dynamicMesh/lnInclude \ -I$(LIB_SRC)/dynamicMesh/lnInclude \
-I$(LIB_SRC)/finiteVolume/lnInclude -I$(LIB_SRC)/finiteVolume/lnInclude
LIB_LIBS = \ LIB_LIBS = \
-ltriSurface \ -ltriSurface \
-lmeshTools \ -lmeshTools \
-lsampling \
-ldynamicMesh \ -ldynamicMesh \
-lfiniteVolume -lfiniteVolume

View File

@ -35,7 +35,7 @@ Description
#define MeshToMeshMapGeometricFields_H #define MeshToMeshMapGeometricFields_H
#include "polyMesh.H" #include "polyMesh.H"
#include "meshToMesh.H" #include "fvMeshToFvMesh.H"
#include "fvPatchFieldMapper.H" #include "fvPatchFieldMapper.H"
#include "pointPatchFieldMapper.H" #include "pointPatchFieldMapper.H"
#include "setSizeFieldMapper.H" #include "setSizeFieldMapper.H"
@ -131,12 +131,13 @@ void evaluateConstraintTypes(GeometricField<Type, fvPatchField, volMesh>& fld)
template<class Type> template<class Type>
void MeshToMeshMapVolFields void MeshToMeshMapVolFields
( (
const meshToMesh& mapper const fvMesh& mesh,
const fvMeshToFvMesh& mapper
) )
{ {
HashTable<const GeometricField<Type, fvPatchField, volMesh>*> fields HashTable<const GeometricField<Type, fvPatchField, volMesh>*> fields
( (
mapper.srcRegion().objectRegistry::template mesh.objectRegistry::template
lookupClass<GeometricField<Type, fvPatchField, volMesh>>() lookupClass<GeometricField<Type, fvPatchField, volMesh>>()
); );
@ -158,7 +159,7 @@ void MeshToMeshMapVolFields
fields = fields =
( (
mapper.srcRegion().objectRegistry::template mesh.objectRegistry::template
lookupClass<GeometricField<Type, fvPatchField, volMesh>>() lookupClass<GeometricField<Type, fvPatchField, volMesh>>()
); );
@ -174,7 +175,7 @@ void MeshToMeshMapVolFields
const_cast<GeometricField<Type, fvPatchField, volMesh>&> const_cast<GeometricField<Type, fvPatchField, volMesh>&>
(*fieldIter()); (*fieldIter());
if (meshToMesh::debug) if (fvMeshToFvMesh::debug)
{ {
Info<< "Mapping " << field.typeName << ' ' << field.name() Info<< "Mapping " << field.typeName << ' ' << field.name()
<< endl; << endl;
@ -196,7 +197,8 @@ template
> >
void NaNGeometricFields void NaNGeometricFields
( (
const meshToMesh& mapper const fvMesh& mesh,
const fvMeshToFvMesh& mapper
) )
{ {
typedef GeometricField<Type, PatchField, GeoMesh> Gfield; typedef GeometricField<Type, PatchField, GeoMesh> Gfield;
@ -220,7 +222,7 @@ void NaNGeometricFields
HashTable<const Gfield*> fields HashTable<const Gfield*> fields
( (
mapper.srcRegion().objectRegistry::template lookupClass<Gfield>() mesh.objectRegistry::template lookupClass<Gfield>()
); );
// Deleted old time fields // Deleted old time fields
@ -236,8 +238,7 @@ void NaNGeometricFields
field.clearOldTimes(); field.clearOldTimes();
} }
fields = fields = mesh.objectRegistry::template lookupClass<Gfield>();
mapper.srcRegion().objectRegistry::template lookupClass<Gfield>();
Type NaN; Type NaN;
@ -255,7 +256,7 @@ void NaNGeometricFields
{ {
Gfield& field = const_cast<Gfield&>(*fieldIter()); Gfield& field = const_cast<Gfield&>(*fieldIter());
if (meshToMesh::debug) if (fvMeshToFvMesh::debug)
{ {
Info<< "Setting to NaN " << field.typeName << ' ' << field.name() Info<< "Setting to NaN " << field.typeName << ' ' << field.name()
<< endl; << endl;

View File

@ -29,7 +29,7 @@ License
#include "surfaceInterpolate.H" #include "surfaceInterpolate.H"
#include "pointFields.H" #include "pointFields.H"
#include "meshToMeshAdjustTimeStepFunctionObject.H" #include "meshToMeshAdjustTimeStepFunctionObject.H"
#include "meshToMesh.H" #include "fvMeshToFvMesh.H"
#include "cellVolumeWeightMethod.H" #include "cellVolumeWeightMethod.H"
#include "surfaceToVolVelocity.H" #include "surfaceToVolVelocity.H"
#include "MeshToMeshMapGeometricFields.H" #include "MeshToMeshMapGeometricFields.H"
@ -154,7 +154,7 @@ bool Foam::fvMeshTopoChangers::meshToMesh::update()
fvMesh::stitchType::none fvMesh::stitchType::none
); );
autoPtr<Foam::meshToMesh> mapper; autoPtr<Foam::fvMeshToFvMesh> mapper;
// Create mesh-to-mesh mapper with support for cuttingPatches // Create mesh-to-mesh mapper with support for cuttingPatches
// if specified // if specified
@ -182,7 +182,7 @@ bool Foam::fvMeshTopoChangers::meshToMesh::update()
} }
} }
mapper = new Foam::meshToMesh mapper = new Foam::fvMeshToFvMesh
( (
mesh(), mesh(),
newMesh, newMesh,
@ -193,7 +193,7 @@ bool Foam::fvMeshTopoChangers::meshToMesh::update()
} }
else else
{ {
mapper = new Foam::meshToMesh mapper = new Foam::fvMeshToFvMesh
( (
mesh(), mesh(),
newMesh, newMesh,
@ -207,25 +207,27 @@ bool Foam::fvMeshTopoChangers::meshToMesh::update()
// Map all the volFields in the objectRegistry // Map all the volFields in the objectRegistry
#define mapVolFieldType(Type, nullArg) \ #define mapVolFieldType(Type, nullArg) \
MeshToMeshMapVolFields<Type>(mapper); MeshToMeshMapVolFields<Type>(mesh(), mapper);
FOR_ALL_FIELD_TYPES(mapVolFieldType); FOR_ALL_FIELD_TYPES(mapVolFieldType);
// Set all the surfaceFields in the objectRegistry to NaN // Set all the surfaceFields in the objectRegistry to NaN
#define NaNSurfaceFieldType(Type, nullArg) \ #define NaNSurfaceFieldType(Type, nullArg) \
NaNGeometricFields \ NaNGeometricFields \
<Type, fvsPatchField, surfaceMesh, fvPatchFieldMapper>(mapper); <Type, fvsPatchField, surfaceMesh, fvPatchFieldMapper> \
(mesh(), mapper);
FOR_ALL_FIELD_TYPES(NaNSurfaceFieldType); FOR_ALL_FIELD_TYPES(NaNSurfaceFieldType);
// Set all the pointFields in the objectRegistry to NaN // Set all the pointFields in the objectRegistry to NaN
#define NaNPointFieldType(Type, nullArg) \ #define NaNPointFieldType(Type, nullArg) \
NaNGeometricFields \ NaNGeometricFields \
<Type, pointPatchField, pointMesh, pointPatchFieldMapper>(mapper); <Type, pointPatchField, pointMesh, pointPatchFieldMapper> \
(mesh(), mapper);
FOR_ALL_FIELD_TYPES(NaNPointFieldType); FOR_ALL_FIELD_TYPES(NaNPointFieldType);
// Interpolate U's to Uf's // Interpolate U's to Uf's
interpolateUfs(); interpolateUfs();
polyMeshMap map(mesh()); polyMeshMap map(mesh(), mapper());
mesh().mapMesh(map); mesh().mapMesh(map);
} }

View File

@ -26,6 +26,7 @@ License
#include "Cloud.H" #include "Cloud.H"
#include "processorPolyPatch.H" #include "processorPolyPatch.H"
#include "globalMeshData.H" #include "globalMeshData.H"
#include "meshToMesh.H"
#include "PstreamCombineReduceOps.H" #include "PstreamCombineReduceOps.H"
#include "polyTopoChangeMap.H" #include "polyTopoChangeMap.H"
#include "polyMeshMap.H" #include "polyMeshMap.H"
@ -433,7 +434,109 @@ void Foam::Cloud<ParticleType>::topoChange(const polyTopoChangeMap& map)
template<class ParticleType> template<class ParticleType>
void Foam::Cloud<ParticleType>::mapMesh(const polyMeshMap& map) void Foam::Cloud<ParticleType>::mapMesh(const polyMeshMap& map)
{ {
NotImplemented; // Ask for the tetBasePtIs to trigger all processors to build
// them, otherwise, if some processors have no particles then
// there is a comms mismatch.
pMesh_.tetBasePtIs();
pMesh_.oldCellCentres();
// Update cached mesh indexing
patchNbrProc_ = patchNbrProc(pMesh_);
patchNbrProcPatch_ = patchNbrProcPatch(pMesh_);
patchNonConformalCyclicPatches_ = patchNonConformalCyclicPatches(pMesh_);
if (!globalPositionsPtr_.valid())
{
FatalErrorInFunction
<< "Global positions are not available. "
<< "Cloud::storeGlobalPositions has not been called."
<< exit(FatalError);
}
const vectorField& positions = globalPositionsPtr_();
// Loop the particles. Map those that remain on this processor, and
// transfer others into send arrays.
List<DynamicList<label>> sendCellIndices(Pstream::nProcs());
List<DynamicList<point>> sendPositions(Pstream::nProcs());
List<IDLList<ParticleType>> sendParticles(Pstream::nProcs());
{
label particlei = 0;
forAllIter(typename Cloud<ParticleType>, *this, iter)
{
const point& pos = positions[particlei ++];
const remote tgtProcCell =
map.mapper().srcToTgtPoint(iter().cell(), pos);
if (tgtProcCell == remote())
{
WarningInFunction
<< "Particle at " << pos << " mapped to a location outside "
<< "of the new mesh. This particle will be removed." << nl;
}
else if (tgtProcCell.proci == Pstream::myProcNo())
{
iter().map(pMesh_, pos, tgtProcCell.elementi);
}
else
{
sendCellIndices[tgtProcCell.proci].append(tgtProcCell.elementi);
sendPositions[tgtProcCell.proci].append(pos);
sendParticles[tgtProcCell.proci].append(this->remove(iter));
}
}
}
// If serial then there is nothing more to do
if (!Pstream::parRun())
{
return;
}
// Create transfer buffers
PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
// Stream into send buffers
forAll(sendParticles, proci)
{
if (sendParticles[proci].size())
{
UOPstream particleStream(proci, pBufs);
particleStream
<< sendCellIndices[proci]
<< sendPositions[proci]
<< sendParticles[proci];
}
}
// Finish sending
labelList receiveSizes(Pstream::nProcs());
pBufs.finishedSends(receiveSizes);
// Retrieve from receive buffers and map into the new mesh
forAll(sendParticles, proci)
{
if (receiveSizes[proci])
{
UIPstream particleStream(proci, pBufs);
const labelList receiveCellIndices(particleStream);
const List<point> receivePositions(particleStream);
IDLList<ParticleType> receiveParticles(particleStream);
label particlei = 0;
forAllIter(typename Cloud<ParticleType>, receiveParticles, iter)
{
const label celli = receiveCellIndices[particlei];
const vector& pos = receivePositions[particlei ++];
iter().map(pMesh_, pos, celli);
this->append(receiveParticles.remove(iter));
}
}
}
} }

View File

@ -215,6 +215,8 @@ triSurface/triSurfaceTools/pointToPointPlanarInterpolation.C
twoDPointCorrector/twoDPointCorrector.C twoDPointCorrector/twoDPointCorrector.C
patchToPatchTools/patchToPatchTools.C
patchToPatch/patchToPatch/patchToPatch.C patchToPatch/patchToPatch/patchToPatch.C
patchToPatch/patchToPatch/patchToPatchParallelOps.C patchToPatch/patchToPatch/patchToPatchParallelOps.C
patchToPatch/matching/matchingPatchToPatch.C patchToPatch/matching/matchingPatchToPatch.C
@ -223,6 +225,14 @@ patchToPatch/inverseDistance/inverseDistancePatchToPatch.C
patchToPatch/intersection/intersectionPatchToPatch.C patchToPatch/intersection/intersectionPatchToPatch.C
patchToPatch/rays/raysPatchToPatch.C patchToPatch/rays/raysPatchToPatch.C
meshToMesh/meshToMesh.C
meshToMesh/meshToMeshParallelOps.C
meshToMesh/calcMethod/meshToMeshMethod/meshToMeshMethod.C
meshToMesh/calcMethod/meshToMeshMethod/meshToMeshMethodNew.C
meshToMesh/calcMethod/cellVolumeWeight/cellVolumeWeightMethod.C
meshToMesh/calcMethod/direct/directMethod.C
meshToMesh/calcMethod/mapNearest/mapNearestMethod.C
mappedPatches/mappedPatchBase/mappedPatchBase.C mappedPatches/mappedPatchBase/mappedPatchBase.C
mappedPatches/mappedPolyPatch/mappedPolyPatch.C mappedPatches/mappedPolyPatch/mappedPolyPatch.C
mappedPatches/mappedPolyPatch/mappedWallPolyPatch.C mappedPatches/mappedPolyPatch/mappedWallPolyPatch.C

View File

@ -0,0 +1,414 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2012-2022 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/>.
\*---------------------------------------------------------------------------*/
#include "meshToMesh.H"
#include "globalIndex.H"
#include "meshToMeshMethod.H"
#include "PatchTools.H"
#include "patchToPatchTools.H"
#include "processorPolyPatch.H"
#include "Time.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(meshToMesh, 0);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::scalar Foam::meshToMesh::calculateCellToCells(const word& methodName)
{
Info<< "Creating mesh-to-mesh addressing for " << srcMesh_.name()
<< " and " << tgtMesh_.name() << " regions using "
<< methodName << endl;
singleProcess_ =
patchToPatchTools::singleProcess
(
srcMesh_.nCells(),
tgtMesh_.nCells()
);
autoPtr<meshToMeshMethod> methodPtr;
scalar V = 0;
if (isSingleProcess())
{
// Do the intersection
methodPtr = meshToMeshMethod::New(methodName, srcMesh_, tgtMesh_);
V = methodPtr->calculate
(
srcLocalTgtCells_,
srcWeights_,
tgtLocalSrcCells_,
tgtWeights_
);
// Normalise the weights
methodPtr->normalise(srcMesh_, srcLocalTgtCells_, srcWeights_);
methodPtr->normalise(tgtMesh_, tgtLocalSrcCells_, tgtWeights_);
}
else
{
// Create the target map of overlapping cells. This map gets remote
// parts of the target mesh so that everything needed to compute an
// intersection is available locally to the source. Use it to create a
// source-local target mesh.
tgtMapPtr_ =
patchToPatchTools::constructDistributionMap
(
tgtMeshSendCells(srcMesh_, tgtMesh_)
);
localTgtProcCellsPtr_.reset
(
new List<remote>
(
distributeMesh
(
tgtMapPtr_(),
tgtMesh_,
localTgtMeshPtr_
)
)
);
const polyMesh& localTgtMesh = localTgtMeshPtr_();
if (debug > 1)
{
Pout<< "Writing local target mesh: "
<< localTgtMesh.name() << endl;
localTgtMesh.write();
}
// Do the intersection
methodPtr = meshToMeshMethod::New(methodName, srcMesh_, localTgtMesh);
V = methodPtr->calculate
(
srcLocalTgtCells_,
srcWeights_,
tgtLocalSrcCells_,
tgtWeights_
);
// Trim the local target mesh
trimLocalTgt();
if (debug > 1)
{
Pout<< "Writing trimmed local target mesh: "
<< localTgtMesh.name() << endl;
localTgtMesh.write();
}
// Construct the source map
srcMapPtr_ =
patchToPatchTools::constructDistributionMap
(
patchToPatchTools::procSendIndices
(
tgtLocalSrcCells_,
localTgtProcCellsPtr_()
)
);
localSrcProcCellsPtr_.reset
(
new List<remote>
(
patchToPatchTools::distributeAddressing(srcMapPtr_())
)
);
// Collect the addressing on the target
patchToPatchTools::rDistributeTgtAddressing
(
tgtMesh_.nCells(),
tgtMapPtr_(),
localSrcProcCellsPtr_(),
tgtLocalSrcCells_
);
// Collect the weights on the target
patchToPatchTools::rDistributeListList
(
tgtMesh_.nCells(),
tgtMapPtr_(),
tgtWeights_
);
// Normalise the weights
methodPtr->normalise(srcMesh_, srcLocalTgtCells_, srcWeights_);
methodPtr->normalise(tgtMesh_, tgtLocalSrcCells_, tgtWeights_);
// collect volume intersection contributions
reduce(V, sumOp<scalar>());
}
Info<< " Overlap volume: " << V << endl;
return V;
}
void Foam::meshToMesh::calculatePatchToPatches(const word& methodName)
{
if (!srcToTgtPatchToPatches_.empty())
{
FatalErrorInFunction
<< "srcToTgtPatchToPatches already calculated"
<< exit(FatalError);
}
const word& patchToPatchType =
meshToMeshMethod::New
(
methodName,
NullObjectRef<polyMesh>(),
NullObjectRef<polyMesh>()
)->patchToPatchMethod();
srcToTgtPatchToPatches_.setSize(srcToTgtPatchIDs_.size());
forAll(srcToTgtPatchIDs_, i)
{
const label srcPatchi = srcToTgtPatchIDs_[i].first();
const label tgtPatchi = srcToTgtPatchIDs_[i].second();
const polyPatch& srcPP = srcMesh_.boundaryMesh()[srcPatchi];
const polyPatch& tgtPP = tgtMesh_.boundaryMesh()[tgtPatchi];
Info<< "Creating patchToPatch between source patch "
<< srcPP.name() << " and target patch " << tgtPP.name()
<< " using " << patchToPatchType << endl;
Info<< incrIndent;
srcToTgtPatchToPatches_.set
(
i,
patchToPatch::New(patchToPatchType, true)
);
srcToTgtPatchToPatches_[i].update
(
srcPP,
PatchTools::pointNormals(srcMesh_, srcPP),
tgtPP
);
Info<< decrIndent;
}
}
void Foam::meshToMesh::constructNoCuttingPatches
(
const word& methodName,
const bool interpAllPatches
)
{
if (interpAllPatches)
{
DynamicList<labelPair> srcToTgtPatchIDs;
forAll(srcMesh_.boundaryMesh(), srcPatchi)
{
const polyPatch& srcPp = srcMesh_.boundaryMesh()[srcPatchi];
// We want to map all the global patches, including constraint
// patches (since they might have mappable properties, e.g.
// jumpCyclic). We'll fix the value afterwards.
if (!isA<processorPolyPatch>(srcPp))
{
const label tgtPatchi =
tgtMesh_.boundaryMesh().findPatchID(srcPp.name());
if (tgtPatchi == -1)
{
FatalErrorInFunction
<< "Source patch " << srcPp.name()
<< " not found in target mesh. "
<< "Available target patches are "
<< tgtMesh_.boundaryMesh().names()
<< exit(FatalError);
}
srcToTgtPatchIDs.append(labelPair(srcPatchi, tgtPatchi));
}
}
srcToTgtPatchIDs_.transfer(srcToTgtPatchIDs);
}
// calculate cell addressing and weights
calculateCellToCells(methodName);
// calculate patch addressing and weights
calculatePatchToPatches(methodName);
}
void Foam::meshToMesh::constructFromCuttingPatches
(
const word& methodName,
const HashTable<word>& patchMap,
const wordList& tgtCuttingPatches
)
{
srcToTgtPatchIDs_.setSize(patchMap.size());
label i = 0;
forAllConstIter(HashTable<word>, patchMap, iter)
{
const word& tgtPatchName = iter.key();
const word& srcPatchName = iter();
srcToTgtPatchIDs_[i++] =
labelPair
(
srcMesh_.boundaryMesh().findPatchID(srcPatchName),
tgtMesh_.boundaryMesh().findPatchID(tgtPatchName)
);
}
// calculate cell addressing and weights
calculateCellToCells(methodName);
// calculate patch addressing and weights
calculatePatchToPatches(methodName);
// set IDs of cutting patches on target mesh
tgtCuttingPatchIDs_.setSize(tgtCuttingPatches.size());
forAll(tgtCuttingPatchIDs_, i)
{
const word& patchName = tgtCuttingPatches[i];
tgtCuttingPatchIDs_[i] = tgtMesh_.boundaryMesh().findPatchID(patchName);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::meshToMesh::meshToMesh
(
const polyMesh& src,
const polyMesh& tgt,
const word& methodName,
bool interpAllPatches
)
:
srcMesh_(src),
tgtMesh_(tgt),
srcToTgtPatchIDs_(),
srcToTgtPatchToPatches_(),
tgtCuttingPatchIDs_(),
srcLocalTgtCells_(),
tgtLocalSrcCells_(),
srcWeights_(),
tgtWeights_(),
singleProcess_(-1),
srcMapPtr_(nullptr),
tgtMapPtr_(nullptr),
localSrcProcCellsPtr_(nullptr),
localTgtProcCellsPtr_(nullptr),
localTgtMeshPtr_(nullptr)
{
constructNoCuttingPatches
(
methodName,
interpAllPatches
);
}
Foam::meshToMesh::meshToMesh
(
const polyMesh& src,
const polyMesh& tgt,
const word& methodName,
const HashTable<word>& patchMap,
const wordList& tgtCuttingPatches
)
:
srcMesh_(src),
tgtMesh_(tgt),
srcToTgtPatchIDs_(),
srcToTgtPatchToPatches_(),
tgtCuttingPatchIDs_(),
srcLocalTgtCells_(),
tgtLocalSrcCells_(),
srcWeights_(),
tgtWeights_(),
singleProcess_(-1),
srcMapPtr_(nullptr),
tgtMapPtr_(nullptr),
localTgtMeshPtr_(nullptr)
{
constructFromCuttingPatches
(
methodName,
patchMap,
tgtCuttingPatches
);
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::meshToMesh::~meshToMesh()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::remote Foam::meshToMesh::srcToTgtPoint
(
const label srcCelli,
const point& p
) const
{
forAll(srcLocalTgtCells_[srcCelli], i)
{
const label tgtCelli = srcLocalTgtCells_[srcCelli][i];
const polyMesh& tgtMesh =
singleProcess_ == -1 ? localTgtMeshPtr_() : tgtMesh_;
if (tgtMesh.pointInCell(p, tgtCelli))
{
return
singleProcess_ == -1
? localTgtProcCellsPtr_()[tgtCelli]
: remote(Pstream::myProcNo(), tgtCelli);
}
}
return remote();
}
// ************************************************************************* //

View File

@ -45,7 +45,6 @@ SourceFiles
#include "polyMesh.H" #include "polyMesh.H"
#include "boundBox.H" #include "boundBox.H"
#include "distributionMap.H" #include "distributionMap.H"
#include "volFieldsFwd.H"
#include "NamedEnum.H" #include "NamedEnum.H"
#include "patchToPatch.H" #include "patchToPatch.H"
@ -63,57 +62,65 @@ class meshToMesh
// Private Data // Private Data
//- Reference to the source mesh //- Reference to the source mesh
const polyMesh& srcRegion_; const polyMesh& srcMesh_;
//- Reference to the target mesh //- Reference to the target mesh
const polyMesh& tgtRegion_; const polyMesh& tgtMesh_;
//- List of target patch IDs per source patch (local index) //- List of corresponding source and target patches that are to be
List<label> srcPatchID_; // mapped to each other
List<labelPair> srcToTgtPatchIDs_;
//- List of source patch IDs per target patch (local index)
List<label> tgtPatchID_;
//- List of patchToPatch interpolation engines between source and //- List of patchToPatch interpolation engines between source and
// target patches // target patches
PtrList<patchToPatch> patchToPatches_; PtrList<patchToPatch> srcToTgtPatchToPatches_;
//- Cutting patches whose values are set using a zero-gradient condition //- Cutting patches whose values are set using a zero-gradient condition
List<label> cuttingPatches_; labelList tgtCuttingPatchIDs_;
//- Source to target cell addressing //- For each source cell, the coupled local target cells
labelListList srcToTgtCellAddr_; labelListList srcLocalTgtCells_;
//- Target to source cell addressing //- For each target cell, the coupled local source cells
labelListList tgtToSrcCellAddr_; labelListList tgtLocalSrcCells_;
//- Source to target cell interpolation weights //- For each source cell, the coupled target weights
scalarListList srcToTgtCellWght_; scalarListList srcWeights_;
//- Target to source cell interpolation weights //- For each target cell, the coupled source weights
scalarListList tgtToSrcCellWght_; scalarListList tgtWeights_;
//- Index of processor that holds all of both sides. -1 in all other //- Index of processor that holds all of both sides, or -1
// cases label singleProcess_;
label singleMeshProc_;
//- Source map pointer - parallel running only //- Map from source patch cells to target-local source patch cells
autoPtr<distributionMap> srcMapPtr_; autoPtr<distributionMap> srcMapPtr_;
//- Target map pointer - parallel running only //- Map from target patch cells to source-local target patch cells
autoPtr<distributionMap> tgtMapPtr_; autoPtr<distributionMap> tgtMapPtr_;
//- When running in parallel, a map from local source cell index to
// source processor and cell index
autoPtr<List<remote>> localSrcProcCellsPtr_;
//- When running in parallel, a map from local target cell index to
// target processor and cell index
autoPtr<List<remote>> localTgtProcCellsPtr_;
//- The target mesh, distributed locally to the source
autoPtr<polyMesh> localTgtMeshPtr_;
// Private Member Functions // Private Member Functions
//- Helper function to add a constant offset to a list //- Helper function to add a constant offset to a list
template<class Type> template<class Type>
void add(UList<Type>& fld, const label offset) const; static void add(UList<Type>& fld, const label offset);
//- Calculate the internal mesh overlap. Returns the overlap volume. //- Calculate the cell intersections. Returns the overlap volume.
scalar calculate(const word& methodName); scalar calculateCellToCells(const word& methodName);
//- Calculate patch overlap //- Calculate patch intersections
void calculatePatchToPatches(const word& patchToPatchType); void calculatePatchToPatches(const word& patchToPatchType);
//- Constructor helper //- Constructor helper
@ -128,66 +135,49 @@ class meshToMesh
( (
const word& methodName, const word& methodName,
const HashTable<word>& patchMap, const HashTable<word>& patchMap,
const wordList& cuttingPatches const wordList& tgtCuttingPatches
); );
//- Return the list of patchToPatch interpolation engines between
// source and target patches
inline const PtrList<patchToPatch>& patchToPatches() const;
// Parallel operations // Parallel operations
//- Determine whether the meshes are split across multiple pocessors //- Determine which target cells need to be sent to the source.
label calcDistribution // This is done before intersection. Bound boxes are used to
// estimate what cells will intersect.
labelListList tgtMeshSendCells
( (
const polyMesh& src, const polyMesh& srcMesh,
const polyMesh& tgt const polyMesh& tgtMesh
) const; ) const;
//- Determine which processor bounding-boxes overlap //- Distribute a mesh given its distribution map
label calcOverlappingProcs static List<remote> distributeMesh
(
const List<boundBox>& procBb,
const boundBox& bb,
boolList& overlaps
) const;
//- Calculate the mapping between processors
autoPtr<distributionMap> calcProcMap
(
const polyMesh& src,
const polyMesh& tgt
) const;
//- Distribute mesh info from 'my' processor to others
void distributeCells
( (
const distributionMap& map, const distributionMap& map,
const polyMesh& tgtMesh, const polyMesh& mesh,
const globalIndex& globalTgtCells, autoPtr<polyMesh>& localMeshPtr
List<pointField>& points, );
List<label>& nInternalFaces,
List<faceList>& faces,
List<labelList>& faceOwner,
List<labelList>& faceNeighbour,
List<labelList>& globalCellIDs,
List<labelList>& nbrProcIDs,
List<labelList>& procLocalFaceIDs
) const;
//- Collect pieces of tgt mesh from other processors and restructure //- Trim the local target addressing and mesh so that communication
void distributeAndMergeCells // from the target to the source is optimised
( void trimLocalTgt();
const distributionMap& map,
const polyMesh& tgtMesh,
const globalIndex& globalTgtCells, protected:
pointField& tgtPoints,
faceList& tgtFaces, // Protected Member Functions
labelList& tgtFaceOwners,
labelList& tgtFaceNeighbours, // Access
labelList& tgtCellIDs
) const; //- Return the source patch indices
inline const List<labelPair>& srcToTgtPatchIDs() const;
//- Return the list of patchToPatch interpolation engines between
// source and target patches
inline const PtrList<patchToPatch>& srcToTgtPatchToPatches() const;
//- Return the cutting patch indices
inline const labelList& tgtCuttingPatchIDs() const;
public: public:
@ -195,6 +185,7 @@ public:
//- Run-time type information //- Run-time type information
TypeName("meshToMesh"); TypeName("meshToMesh");
// Constructors // Constructors
//- Construct from source and target meshes, generic mapping methods //- Construct from source and target meshes, generic mapping methods
@ -229,22 +220,17 @@ public:
// Access // Access
//- Return const access to the source mesh //- Return const access to the source mesh
inline const polyMesh& srcRegion() const; inline const polyMesh& srcMesh() const;
//- Return const access to the target mesh //- Return const access to the target mesh
inline const polyMesh& tgtRegion() const; inline const polyMesh& tgtMesh() const;
//- Return const access to the source to target cell addressing //- Index of the processor holding all cells of the meshToMesh,
inline const labelListList& srcToTgtCellAddr() const; // or -1 if spread across multiple processors
inline label singleProcess() const;
//- Return const access to the target to source cell addressing //- Is this intersection on a single process?
inline const labelListList& tgtToSrcCellAddr() const; inline bool isSingleProcess() const;
//- Return const access to the source to target cell weights
inline const scalarListList& srcToTgtCellWght() const;
//- Return const access to the target to source cell weights
inline const scalarListList& tgtToSrcCellWght() const;
// Evaluation // Evaluation
@ -283,24 +269,18 @@ public:
tmp<Field<Type>> mapTgtToSrc(const Field<Type>& tgtFld) const; tmp<Field<Type>> mapTgtToSrc(const Field<Type>& tgtFld) const;
// Source-to-target volume field mapping // Source-to-target point finding
//- Interpolate a field with a defined operation. Values //- Find the target processor and cell associated with a point
// passed in via 'result' are used to initialise the return // in a source cell. Note that this will only work with
// value // methods that fill a cell's stencil with everything that
template<class Type> // overlaps that cell. At present this is just
void mapSrcToTgt // cellVolumeWeightMethod, but we might add a cheaper
// bound-box based method like patchToPatches::rays in future.
remote srcToTgtPoint
( (
const GeometricField<Type, fvPatchField, volMesh>& field, const label srcCelli,
GeometricField<Type, fvPatchField, volMesh>& result const point& p
) const;
//- Interpolate a field with a defined operation. The initial
// values of the result are set to zero
template<class Type>
tmp<GeometricField<Type, fvPatchField, volMesh>> mapSrcToTgt
(
const GeometricField<Type, fvPatchField, volMesh>& field
) const; ) const;

View File

@ -25,52 +25,51 @@ License
#include "meshToMesh.H" #include "meshToMesh.H"
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
inline const Foam::polyMesh& Foam::meshToMesh::srcRegion() const inline const Foam::List<Foam::labelPair>&
Foam::meshToMesh::srcToTgtPatchIDs() const
{ {
return srcRegion_; return srcToTgtPatchIDs_;
}
inline const Foam::polyMesh& Foam::meshToMesh::tgtRegion() const
{
return tgtRegion_;
}
inline const Foam::labelListList&
Foam::meshToMesh::srcToTgtCellAddr() const
{
return srcToTgtCellAddr_;
}
inline const Foam::labelListList&
Foam::meshToMesh::tgtToSrcCellAddr() const
{
return tgtToSrcCellAddr_;
}
inline const Foam::scalarListList&
Foam::meshToMesh::srcToTgtCellWght() const
{
return srcToTgtCellWght_;
}
inline const Foam::scalarListList&
Foam::meshToMesh::tgtToSrcCellWght() const
{
return tgtToSrcCellWght_;
} }
inline const Foam::PtrList<Foam::patchToPatch>& inline const Foam::PtrList<Foam::patchToPatch>&
Foam::meshToMesh::patchToPatches() const Foam::meshToMesh::srcToTgtPatchToPatches() const
{ {
return patchToPatches_; return srcToTgtPatchToPatches_;
}
inline const Foam::labelList& Foam::meshToMesh::tgtCuttingPatchIDs() const
{
return tgtCuttingPatchIDs_;
}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
inline const Foam::polyMesh& Foam::meshToMesh::srcMesh() const
{
return srcMesh_;
}
inline const Foam::polyMesh& Foam::meshToMesh::tgtMesh() const
{
return tgtMesh_;
}
inline Foam::label Foam::meshToMesh::singleProcess() const
{
return singleProcess_;
}
inline bool Foam::meshToMesh::isSingleProcess() const
{
return singleProcess_ != -1;
} }

View File

@ -0,0 +1,875 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2012-2022 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/>.
\*---------------------------------------------------------------------------*/
#include "meshToMesh.H"
#include "OFstream.H"
#include "Time.H"
#include "globalIndex.H"
#include "processorPolyPatch.H"
#include "SubField.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::labelListList Foam::meshToMesh::tgtMeshSendCells
(
const polyMesh& srcMesh,
const polyMesh& tgtMesh
) const
{
// Calculate and communicate the bound boxes for the source meshes
List<boundBox> procBb(Pstream::nProcs(), boundBox());
if (srcMesh.nCells() > 0)
{
// Bounding box for this mesh. Do not reduce.
procBb[Pstream::myProcNo()] = boundBox(srcMesh.points(), false);
// Slightly increase size to allow for cases where boxes are aligned
procBb[Pstream::myProcNo()].inflate(0.01);
}
Pstream::gatherList(procBb);
Pstream::scatterList(procBb);
// per processor indices into all segments to send
List<DynamicList<label>> resultDyn
(
Pstream::nProcs(),
DynamicList<label>(tgtMesh.nCells()/Pstream::nProcs())
);
// Send a target cell to a process if it overlaps the source bound box
// for that process
forAll(tgtMesh.cells(), tgtCelli)
{
const boundBox cellBb =
tgtMesh.cells()[tgtCelli].bb(tgtMesh.points(), tgtMesh.faces());
forAll(procBb, proci)
{
if (procBb[proci].overlaps(cellBb))
{
resultDyn[proci].append(tgtCelli);
}
}
}
// Transfer to permanent storage and return
labelListList result(Pstream::nProcs());
forAll(result, proci)
{
result[proci].transfer(resultDyn[proci]);
}
return result;
}
Foam::List<Foam::remote> Foam::meshToMesh::distributeMesh
(
const distributionMap& map,
const polyMesh& mesh,
autoPtr<polyMesh>& localMeshPtr
)
{
PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
// Exchange raw mesh topology/geometry
List<pointField> allPoints(Pstream::nProcs());
labelList allNInternalFaces(Pstream::nProcs(), 0);
List<faceList> allFaces(Pstream::nProcs());
List<labelList> allFaceOwners(Pstream::nProcs());
List<labelList> allFaceNeighbours(Pstream::nProcs());
List<List<remote>> allProcCells(Pstream::nProcs());
List<List<remote>> allProcProcessorPatchFaces(Pstream::nProcs());
for (label domain = 0; domain < Pstream::nProcs(); domain++)
{
const labelList& sendElems = map.subMap()[domain];
if (sendElems.size())
{
// reverse cell map
labelList reverseCellMap(mesh.nCells(), -1);
forAll(sendElems, subCelli)
{
reverseCellMap[sendElems[subCelli]] = subCelli;
}
DynamicList<face> subFaces(mesh.nFaces());
DynamicList<label> subFaceOwner(mesh.nFaces());
DynamicList<label> subFaceNeighbour(mesh.nFaces());
DynamicList<remote> subProcProcessorPatchFaces(mesh.nFaces());
label nInternal = 0;
// internal faces
forAll(mesh.faceNeighbour(), facei)
{
const label own = mesh.faceOwner()[facei];
const label nbr = mesh.faceNeighbour()[facei];
const label subOwn = reverseCellMap[own];
const label subNbr = reverseCellMap[nbr];
if (subOwn != -1 && subNbr != -1)
{
nInternal++;
if (subOwn < subNbr)
{
subFaces.append(mesh.faces()[facei]);
subFaceOwner.append(subOwn);
subFaceNeighbour.append(subNbr);
subProcProcessorPatchFaces.append(remote());
}
else
{
subFaces.append(mesh.faces()[facei].reverseFace());
subFaceOwner.append(subNbr);
subFaceNeighbour.append(subOwn);
subProcProcessorPatchFaces.append(remote());
}
}
}
// boundary faces for new region
forAll(mesh.faceNeighbour(), facei)
{
const label own = mesh.faceOwner()[facei];
const label nbr = mesh.faceNeighbour()[facei];
const label subOwn = reverseCellMap[own];
const label subNbr = reverseCellMap[nbr];
if (subOwn != -1 && subNbr == -1)
{
subFaces.append(mesh.faces()[facei]);
subFaceOwner.append(subOwn);
subFaceNeighbour.append(subNbr);
subProcProcessorPatchFaces.append(remote());
}
else if (subOwn == -1 && subNbr != -1)
{
subFaces.append(mesh.faces()[facei].reverseFace());
subFaceOwner.append(subNbr);
subFaceNeighbour.append(subOwn);
subProcProcessorPatchFaces.append(remote());
}
}
// boundary faces of existing region
forAll(mesh.boundaryMesh(), patchi)
{
const polyPatch& pp = mesh.boundaryMesh()[patchi];
const label nbrProci =
isA<processorPolyPatch>(pp)
? refCast<const processorPolyPatch>(pp).neighbProcNo()
: -1;
forAll(pp, i)
{
const label facei = pp.start() + i;
const label own = mesh.faceOwner()[facei];
if (reverseCellMap[own] != -1)
{
subFaces.append(mesh.faces()[facei]);
subFaceOwner.append(reverseCellMap[own]);
subFaceNeighbour.append(-1);
subProcProcessorPatchFaces.append
(
nbrProci != -1 ? remote(nbrProci, i) : remote()
);
}
}
}
// reverse point map
labelList reversePointMap(mesh.nPoints(), -1);
DynamicList<point> subPoints(mesh.nPoints());
forAll(subFaces, subFacei)
{
face& f = subFaces[subFacei];
forAll(f, fp)
{
label pointi = f[fp];
if (reversePointMap[pointi] == -1)
{
reversePointMap[pointi] = subPoints.size();
subPoints.append(mesh.points()[pointi]);
}
f[fp] = reversePointMap[pointi];
}
}
// cell indices
List<remote> subProcCells(sendElems.size());
forAll(sendElems, i)
{
subProcCells[i] = remote(Pstream::myProcNo(), sendElems[i]);
}
// pass data
if (domain == Pstream::myProcNo())
{
// allocate my own data
allPoints[Pstream::myProcNo()] = subPoints;
allNInternalFaces[Pstream::myProcNo()] = nInternal;
allFaces[Pstream::myProcNo()] = subFaces;
allFaceOwners[Pstream::myProcNo()] = subFaceOwner;
allFaceNeighbours[Pstream::myProcNo()] = subFaceNeighbour;
allProcCells[Pstream::myProcNo()] = subProcCells;
allProcProcessorPatchFaces[Pstream::myProcNo()] =
subProcProcessorPatchFaces;
}
else
{
// send data to other processor domains
UOPstream toDomain(domain, pBufs);
toDomain
<< subPoints
<< nInternal
<< subFaces
<< subFaceOwner
<< subFaceNeighbour
<< subProcCells
<< subProcProcessorPatchFaces;
}
}
}
// Start receiving
pBufs.finishedSends();
// Consume
for (label domain = 0; domain < Pstream::nProcs(); domain++)
{
const labelList& recvElems = map.constructMap()[domain];
if (domain != Pstream::myProcNo() && recvElems.size())
{
UIPstream str(domain, pBufs);
str >> allPoints[domain]
>> allNInternalFaces[domain]
>> allFaces[domain]
>> allFaceOwners[domain]
>> allFaceNeighbours[domain]
>> allProcCells[domain]
>> allProcProcessorPatchFaces[domain];
}
}
// Convert lists into format that can be used to generate a valid polyMesh
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Points and cells are collected into single flat lists:
// - i.e. proc0, proc1 ... procN
//
// Faces need to be sorted after collection to that internal faces are
// contiguous, followed by all boundary faces
//
// Processor patch faces between included cells on neighbouring processors
// are converted into internal faces
//
// Face list structure:
// - Per processor:
// - internal faces
// - processor faces that have been converted into internal faces
// - Followed by all boundary faces
// - from 'normal' boundary faces
// - from singularly-sided processor patch faces
// Number of internal+coupled faces
labelList allNIntCoupledFaces(allNInternalFaces);
// Starting offset for points
label nPoints = 0;
labelList pointOffset(Pstream::nProcs(), 0);
forAll(allPoints, proci)
{
pointOffset[proci] = nPoints;
nPoints += allPoints[proci].size();
}
// Count any coupled faces
typedef FixedList<label, 3> label3;
typedef HashTable<label, label3, label3::Hash<>> procCoupleInfo;
procCoupleInfo procFaceToGlobalCell;
forAll(allProcProcessorPatchFaces, proci)
{
const List<remote>& procProcessorPatchFaces =
allProcProcessorPatchFaces[proci];
forAll(procProcessorPatchFaces, i)
{
if (procProcessorPatchFaces[i] != remote())
{
const label3 key
({
min(proci, procProcessorPatchFaces[i].proci),
max(proci, procProcessorPatchFaces[i].proci),
procProcessorPatchFaces[i].elementi
});
procCoupleInfo::const_iterator fnd =
procFaceToGlobalCell.find(key);
if (fnd == procFaceToGlobalCell.end())
{
procFaceToGlobalCell.insert(key, -1);
}
else
{
if (debug)
{
Pout<< "Additional internal face between procs:"
<< key[0] << " and " << key[1]
<< " across local face " << key[2] << endl;
}
allNIntCoupledFaces[proci]++;
}
}
}
}
// Starting offset for internal faces
label nIntFaces = 0;
label nFacesTotal = 0;
labelList internalFaceOffset(Pstream::nProcs(), 0);
forAll(allNIntCoupledFaces, proci)
{
label nCoupledFaces =
allNIntCoupledFaces[proci] - allNInternalFaces[proci];
internalFaceOffset[proci] = nIntFaces;
nIntFaces += allNIntCoupledFaces[proci];
nFacesTotal += allFaceOwners[proci].size() - nCoupledFaces;
}
// Starting offset for cells
label nCells = 0;
labelList cellOffset(Pstream::nProcs(), 0);
forAll(allProcCells, proci)
{
cellOffset[proci] = nCells;
nCells += allProcCells[proci].size();
}
// Allocate
List<remote> localProcCells(nCells);
pointField localPoints(nPoints);
faceList localFaces(nFacesTotal);
labelList localFaceOwners(nFacesTotal);
labelList localFaceNeighbours(nFacesTotal);
// Insert proc-cells
forAll(allProcCells, proci)
{
const List<remote>& procCells = allProcCells[proci];
SubList<remote>(localProcCells, procCells.size(), cellOffset[proci]) =
procCells;
}
// Insert points
forAll(allPoints, proci)
{
const pointField& pts = allPoints[proci];
SubList<point>(localPoints, pts.size(), pointOffset[proci]) = pts;
}
// Insert internal faces (from internal faces)
forAll(allFaces, proci)
{
const faceList& fcs = allFaces[proci];
const labelList& faceOs = allFaceOwners[proci];
const labelList& faceNs = allFaceNeighbours[proci];
SubList<face> slice
(
localFaces,
allNInternalFaces[proci],
internalFaceOffset[proci]
);
slice = SubList<face>(fcs, allNInternalFaces[proci]);
forAll(slice, i)
{
add(slice[i], pointOffset[proci]);
}
SubField<label> ownSlice
(
localFaceOwners,
allNInternalFaces[proci],
internalFaceOffset[proci]
);
ownSlice = SubField<label>(faceOs, allNInternalFaces[proci]);
add(ownSlice, cellOffset[proci]);
SubField<label> nbrSlice
(
localFaceNeighbours,
allNInternalFaces[proci],
internalFaceOffset[proci]
);
nbrSlice = SubField<label>(faceNs, allNInternalFaces[proci]);
add(nbrSlice, cellOffset[proci]);
internalFaceOffset[proci] += allNInternalFaces[proci];
}
// Insert internal faces (from coupled face-pairs)
forAll(allProcProcessorPatchFaces, proci)
{
const List<remote>& procProcessorPatchFaces =
allProcProcessorPatchFaces[proci];
const labelList& faceOs = allFaceOwners[proci];
const faceList& fcs = allFaces[proci];
forAll(procProcessorPatchFaces, i)
{
if (procProcessorPatchFaces[i] != remote())
{
const label3 key
({
min(proci, procProcessorPatchFaces[i].proci),
max(proci, procProcessorPatchFaces[i].proci),
procProcessorPatchFaces[i].elementi
});
procCoupleInfo::iterator fnd = procFaceToGlobalCell.find(key);
if (fnd != procFaceToGlobalCell.end())
{
if (fnd() == -1)
{
// on first visit store the new cell on this side
fnd() = cellOffset[proci] + faceOs[i];
}
else
{
// get owner and neighbour in new cell numbering
const label newOwn = cellOffset[proci] + faceOs[i];
const label newNbr = fnd();
const label localFacei = internalFaceOffset[proci]++;
if (debug)
{
Pout<< " proc " << proci
<< "\tinserting face:" << localFacei
<< " connection between owner " << newOwn
<< " and neighbour " << newNbr
<< endl;
}
if (newOwn < newNbr)
{
// we have correct orientation
localFaces[localFacei] = fcs[i];
localFaceOwners[localFacei] = newOwn;
localFaceNeighbours[localFacei] = newNbr;
}
else
{
// reverse orientation
localFaces[localFacei] = fcs[i].reverseFace();
localFaceOwners[localFacei] = newNbr;
localFaceNeighbours[localFacei] = newOwn;
}
add(localFaces[localFacei], pointOffset[proci]);
// mark with unique value
fnd() = -2;
}
}
}
}
}
forAll(allProcProcessorPatchFaces, proci)
{
const List<remote>& procProcessorPatchFaces =
allProcProcessorPatchFaces[proci];
const labelList& faceOs = allFaceOwners[proci];
const labelList& faceNs = allFaceNeighbours[proci];
const faceList& fcs = allFaces[proci];
forAll(procProcessorPatchFaces, i)
{
// coupled boundary face
if (procProcessorPatchFaces[i] != remote())
{
const label3 key
({
min(proci, procProcessorPatchFaces[i].proci),
max(proci, procProcessorPatchFaces[i].proci),
procProcessorPatchFaces[i].elementi
});
if (procFaceToGlobalCell[key] == -1)
{
FatalErrorInFunction
<< "Unvisited " << key
<< abort(FatalError);
}
else if (procFaceToGlobalCell[key] != -2)
{
const label newOwn = cellOffset[proci] + faceOs[i];
const label localFacei = nIntFaces++;
if (debug)
{
Pout<< " proc " << proci
<< "\tinserting boundary face:" << localFacei
<< " from coupled face " << key
<< endl;
}
localFaces[localFacei] = fcs[i];
add(localFaces[localFacei], pointOffset[proci]);
localFaceOwners[localFacei] = newOwn;
localFaceNeighbours[localFacei] = -1;
}
}
// normal boundary face
else
{
const label own = faceOs[i];
const label nbr = faceNs[i];
if ((own != -1) && (nbr == -1))
{
const label newOwn = cellOffset[proci] + faceOs[i];
const label localFacei = nIntFaces++;
localFaces[localFacei] = fcs[i];
add(localFaces[localFacei], pointOffset[proci]);
localFaceOwners[localFacei] = newOwn;
localFaceNeighbours[localFacei] = -1;
}
}
}
}
// Create the local mesh
localMeshPtr.reset
(
new polyMesh
(
IOobject
(
"local" + mesh.name().capitalise(),
mesh.time().timeName(),
mesh.time(),
IOobject::NO_READ
),
move(localPoints),
move(localFaces),
move(localFaceOwners),
move(localFaceNeighbours),
false
)
);
// Add a dummy patch to the target mesh
List<polyPatch*> patches(1);
patches[0] = new polyPatch
(
"defaultFaces",
localMeshPtr().nFaces() - localMeshPtr().nInternalFaces(),
localMeshPtr().nInternalFaces(),
0,
localMeshPtr().boundaryMesh(),
word::null
);
localMeshPtr().addPatches(patches);
// Force calculation of tet-base points used for point-in-cell
(void) localMeshPtr().tetBasePtIs();
return localProcCells;
}
void Foam::meshToMesh::trimLocalTgt()
{
// Determine which local target cells are actually used
boolList oldLocalTgtCellIsUsed(localTgtProcCellsPtr_().size(), false);
forAll(srcLocalTgtCells_, srcCelli)
{
forAll(srcLocalTgtCells_[srcCelli], i)
{
oldLocalTgtCellIsUsed[srcLocalTgtCells_[srcCelli][i]] = true;
}
}
// Trim the target map
labelList oldToNewLocalTgtCell, newToOldLocalTgtCell;
patchToPatchTools::trimDistributionMap
(
oldLocalTgtCellIsUsed,
tgtMapPtr_(),
oldToNewLocalTgtCell,
newToOldLocalTgtCell
);
if (debug)
{
Pout<< "Trim from " << oldToNewLocalTgtCell.size() << " to "
<< newToOldLocalTgtCell.size() << " cells" << endl;
}
// Renumber the source addressing
forAll(srcLocalTgtCells_, srcCelli)
{
forAll(srcLocalTgtCells_[srcCelli], i)
{
srcLocalTgtCells_[srcCelli][i] =
oldToNewLocalTgtCell[srcLocalTgtCells_[srcCelli][i]];
}
}
// Trim the target addressing
tgtLocalSrcCells_ =
labelListList(tgtLocalSrcCells_, newToOldLocalTgtCell);
localTgtProcCellsPtr_() =
List<remote>(localTgtProcCellsPtr_(), newToOldLocalTgtCell);
// Trim the target weights
tgtWeights_ = scalarListList(tgtWeights_, newToOldLocalTgtCell);
// Trim the stored local target mesh
const polyMesh& oldLocalTgtMesh = localTgtMeshPtr_();
const labelList& oldLocalTgtFaceOwner =
oldLocalTgtMesh.faceOwner();
labelList oldLocalTgtFaceNeighbour(oldLocalTgtFaceOwner.size(), -1);
SubList<label>(oldLocalTgtFaceNeighbour, oldLocalTgtMesh.nInternalFaces()) =
oldLocalTgtMesh.faceNeighbour();
// ...
labelList newToOldLocalTgtFace(identity(oldLocalTgtMesh.nFaces()));
labelList oldToNewLocalTgtFace;
{
label i0 = 0;
label i1 = newToOldLocalTgtFace.size();
label iEnd = newToOldLocalTgtFace.size();
while (i0 < i1)
{
label& oldLocalTgtFacei0 = newToOldLocalTgtFace[i0];
label& oldLocalTgtFacei1 = newToOldLocalTgtFace[i1 - 1];
label& oldLocalTgtFaceiEnd = newToOldLocalTgtFace[iEnd - 1];
const label newLocalTgtOwni0 =
oldLocalTgtFaceOwner[oldLocalTgtFacei0] != -1
? oldToNewLocalTgtCell
[oldLocalTgtFaceOwner[oldLocalTgtFacei0]]
: -1;
const label newLocalTgtOwni1 =
oldLocalTgtFaceOwner[oldLocalTgtFacei1] != -1
? oldToNewLocalTgtCell
[oldLocalTgtFaceOwner[oldLocalTgtFacei1]]
: -1;
const label newLocalTgtNbri0 =
oldLocalTgtFaceNeighbour[oldLocalTgtFacei0] != -1
? oldToNewLocalTgtCell
[oldLocalTgtFaceNeighbour[oldLocalTgtFacei0]]
: -1;
const label newLocalTgtNbri1 =
oldLocalTgtFaceNeighbour[oldLocalTgtFacei1] != -1
? oldToNewLocalTgtCell
[oldLocalTgtFaceNeighbour[oldLocalTgtFacei1]]
: -1;
const bool used0 =
newLocalTgtOwni0 != -1 || newLocalTgtNbri0 != -1;
const bool used1 =
newLocalTgtOwni1 != -1 || newLocalTgtNbri1 != -1;
const bool internal0 =
newLocalTgtOwni0 != -1 && newLocalTgtNbri0 != -1;
const bool internal1 =
newLocalTgtOwni1 != -1 && newLocalTgtNbri1 != -1;
// If face 0 is not used, move it to the end to remove it
if (!used0)
{
Swap(oldLocalTgtFacei0, oldLocalTgtFaceiEnd);
if (i1 == iEnd) i1 --;
iEnd --;
}
// If face 1 is not used, move it to the end to remove it
else if (!used1)
{
Swap(oldLocalTgtFacei1, oldLocalTgtFaceiEnd);
if (i1 == iEnd) i1 --;
iEnd --;
}
// Both are internal faces. Face 0 is fine, but face 1 might be out
// of order. So move to the next face 0.
else if (internal0 && internal1)
{
i0 ++;
}
// Both are boundary faces. Face 0 might be out of order, but face
// 1 is fine. So move to the next face 1.
else if (!internal0 && !internal1)
{
i1 --;
}
// Face 0 is an internal face and face 1 is a boundary face. Both
// are fine. So move to the next two faces.
else if (internal0 && !internal1)
{
i0 ++;
i1 --;
}
// Face 0 is a boundary face and face 1 is an internal face. Both
// are out of order. So swap and then move to the next two faces.
else if (!internal0 && internal1)
{
Swap(oldLocalTgtFacei0, oldLocalTgtFacei1);
i0 ++;
i1 --;
}
}
newToOldLocalTgtFace.resize(iEnd);
oldToNewLocalTgtFace =
invert(oldLocalTgtMesh.nFaces(), newToOldLocalTgtFace);
}
if (debug)
{
Pout<< "Trim from " << oldToNewLocalTgtFace.size() << " to "
<< newToOldLocalTgtFace.size() << " faces" << endl;
}
// Create trimmed mesh primitives
pointField newLocalTgtPoints(oldLocalTgtMesh.points());
faceList newLocalTgtFaces(oldLocalTgtMesh.faces(), newToOldLocalTgtFace);
labelList newLocalTgtFaceOwner
(
oldLocalTgtFaceOwner,
static_cast<const labelUList&>(newToOldLocalTgtFace)
);
inplaceRenumber(oldToNewLocalTgtCell, newLocalTgtFaceOwner);
labelList newLocalTgtFaceNeighbour
(
oldLocalTgtFaceNeighbour,
static_cast<const labelUList&>(newToOldLocalTgtFace)
);
inplaceRenumber(oldToNewLocalTgtCell, newLocalTgtFaceNeighbour);
// Check the trimmed mesh structure and flip any faces that have a
// neighbour but not an owner
{
label newLocalTgtNInternalFaces = 0;
bool internal0 = true;
forAll(newLocalTgtFaces, newLocalTgtFacei)
{
face& newLocalTgtF = newLocalTgtFaces[newLocalTgtFacei];
label& newLocalTgtOwni = newLocalTgtFaceOwner[newLocalTgtFacei];
label& newLocalTgtNbri = newLocalTgtFaceNeighbour[newLocalTgtFacei];
// Check that internal and boundary faces are in order
const bool internal =
newLocalTgtOwni != -1 && newLocalTgtNbri != -1;
if (internal0 && !internal)
{
newLocalTgtNInternalFaces = newLocalTgtFacei;
internal0 = false;
}
if (!internal0 && internal)
{
FatalErrorInFunction
<< "Trimmed mesh has boundary faces before internal faces"
<< exit(FatalError);
}
// Flip any face that has a neighbour but not an owner
const bool flip =
newLocalTgtOwni == -1 && newLocalTgtNbri != -1;
if (flip)
{
newLocalTgtF = newLocalTgtF.reverseFace();
Swap(newLocalTgtOwni, newLocalTgtNbri);
}
}
newLocalTgtFaceNeighbour.resize(newLocalTgtNInternalFaces);
}
// Create the local mesh
localTgtMeshPtr_.reset
(
new polyMesh
(
IOobject
(
"trimmed" + oldLocalTgtMesh.name().capitalise(),
oldLocalTgtMesh.time().timeName(),
oldLocalTgtMesh.time(),
IOobject::NO_READ
),
move(newLocalTgtPoints),
move(newLocalTgtFaces),
move(newLocalTgtFaceOwner),
move(newLocalTgtFaceNeighbour),
false
)
);
// Add a dummy patch to the target mesh
List<polyPatch*> patches(1);
patches[0] = new polyPatch
(
"defaultFaces",
localTgtMeshPtr_().nFaces() - localTgtMeshPtr_().nInternalFaces(),
localTgtMeshPtr_().nInternalFaces(),
0,
localTgtMeshPtr_().boundaryMesh(),
word::null
);
localTgtMeshPtr_().addPatches(patches);
// Force calculation of tet-base points used for point-in-cell
(void) localTgtMeshPtr_().tetBasePtIs();
}
// ************************************************************************* //

View File

@ -0,0 +1,200 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2012-2022 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/>.
\*---------------------------------------------------------------------------*/
#include "meshToMesh.H"
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
template<class Type>
void Foam::meshToMesh::add
(
UList<Type>& fld,
const label offset
)
{
forAll(fld, i)
{
fld[i] += offset;
}
}
template<class Type>
void Foam::meshToMesh::mapSrcToTgt
(
const UList<Type>& srcField,
List<Type>& result
) const
{
if (result.size() != tgtLocalSrcCells_.size())
{
FatalErrorInFunction
<< "Supplied field size is not equal to target mesh size" << nl
<< " source mesh = " << srcLocalTgtCells_.size() << nl
<< " target mesh = " << tgtLocalSrcCells_.size() << nl
<< " supplied field = " << result.size()
<< abort(FatalError);
}
if (!isSingleProcess())
{
const distributionMap& map = srcMapPtr_();
List<Type> work(srcField);
map.distribute(work);
forAll(result, tgtCelli)
{
if (tgtLocalSrcCells_[tgtCelli].size())
{
result[tgtCelli] *= (1.0 - sum(tgtWeights_[tgtCelli]));
forAll(tgtLocalSrcCells_[tgtCelli], i)
{
result[tgtCelli] +=
tgtWeights_[tgtCelli][i]
*work[tgtLocalSrcCells_[tgtCelli][i]];
}
}
}
}
else
{
forAll(result, tgtCelli)
{
if (tgtLocalSrcCells_[tgtCelli].size())
{
result[tgtCelli] *= (1.0 - sum(tgtWeights_[tgtCelli]));
forAll(tgtLocalSrcCells_[tgtCelli], i)
{
result[tgtCelli] +=
tgtWeights_[tgtCelli][i]
*srcField[tgtLocalSrcCells_[tgtCelli][i]];
}
}
}
}
}
template<class Type>
Foam::tmp<Foam::Field<Type>> Foam::meshToMesh::mapSrcToTgt
(
const Field<Type>& srcField
) const
{
tmp<Field<Type>> tresult
(
new Field<Type>
(
tgtLocalSrcCells_.size(),
Zero
)
);
mapSrcToTgt(srcField, tresult.ref());
return tresult;
}
template<class Type>
void Foam::meshToMesh::mapTgtToSrc
(
const UList<Type>& tgtField,
List<Type>& result
) const
{
if (result.size() != srcLocalTgtCells_.size())
{
FatalErrorInFunction
<< "Supplied field size is not equal to source mesh size" << nl
<< " source mesh = " << srcLocalTgtCells_.size() << nl
<< " target mesh = " << tgtLocalSrcCells_.size() << nl
<< " supplied field = " << result.size()
<< abort(FatalError);
}
if (!isSingleProcess())
{
const distributionMap& map = tgtMapPtr_();
List<Type> work(tgtField);
map.distribute(work);
forAll(result, srcCelli)
{
if (srcLocalTgtCells_[srcCelli].size())
{
result[srcCelli] *= (1.0 - sum(srcWeights_[srcCelli]));
forAll(srcLocalTgtCells_[srcCelli], i)
{
result[srcCelli] +=
srcWeights_[srcCelli][i]
*work[srcLocalTgtCells_[srcCelli][i]];
}
}
}
}
else
{
forAll(result, srcCelli)
{
if (srcLocalTgtCells_[srcCelli].size())
{
result[srcCelli] *= (1.0 - sum(srcWeights_[srcCelli]));
forAll(srcLocalTgtCells_[srcCelli], i)
{
result[srcCelli] +=
srcWeights_[srcCelli][i]
*tgtField[srcLocalTgtCells_[srcCelli][i]];
}
}
}
}
}
template<class Type>
Foam::tmp<Foam::Field<Type>> Foam::meshToMesh::mapTgtToSrc
(
const Field<Type>& tgtField
) const
{
tmp<Field<Type>> tresult
(
new Field<Type>
(
srcLocalTgtCells_.size(),
Zero
)
);
mapTgtToSrc(tgtField, tresult.ref());
return tresult;
}
// ************************************************************************* //

View File

@ -542,13 +542,13 @@ void Foam::patchToPatches::intersection::initialise
} }
Foam::labelList Foam::patchToPatches::intersection::subsetLocalTgt Foam::labelList Foam::patchToPatches::intersection::trimLocalTgt
( (
const primitiveOldTimePatch& localTgtPatch const primitiveOldTimePatch& localTgtPatch
) )
{ {
const labelList newToOldLocalTgtFace = const labelList newToOldLocalTgtFace =
patchToPatch::subsetLocalTgt(localTgtPatch); patchToPatch::trimLocalTgt(localTgtPatch);
tgtCouples_ = List<DynamicList<couple>>(tgtCouples_, newToOldLocalTgtFace); tgtCouples_ = List<DynamicList<couple>>(tgtCouples_, newToOldLocalTgtFace);
@ -563,7 +563,12 @@ void Foam::patchToPatches::intersection::rDistributeTgt
{ {
patchToPatch::rDistributeTgt(tgtPatch); patchToPatch::rDistributeTgt(tgtPatch);
rDistributeListList(tgtPatch.size(), tgtMapPtr_(), tgtCouples_); patchToPatchTools::rDistributeListList
(
tgtPatch.size(),
tgtMapPtr_(),
tgtCouples_
);
} }

View File

@ -328,9 +328,9 @@ private:
const primitiveOldTimePatch& tgtPatch const primitiveOldTimePatch& tgtPatch
); );
//- Subset the local target patch so that only parts that actually //- Trim the local target patch so that only parts that actually
// intersect the source remain // intersect the source remain
virtual labelList subsetLocalTgt virtual labelList trimLocalTgt
( (
const primitiveOldTimePatch& localTgtPatch const primitiveOldTimePatch& localTgtPatch
); );

View File

@ -191,13 +191,13 @@ void Foam::patchToPatches::inverseDistance::initialise
} }
Foam::labelList Foam::patchToPatches::inverseDistance::subsetLocalTgt Foam::labelList Foam::patchToPatches::inverseDistance::trimLocalTgt
( (
const primitiveOldTimePatch& localTgtPatch const primitiveOldTimePatch& localTgtPatch
) )
{ {
const labelList newToOldLocalTgtFace = const labelList newToOldLocalTgtFace =
patchToPatch::subsetLocalTgt(localTgtPatch); patchToPatch::trimLocalTgt(localTgtPatch);
tgtWeights_ = List<DynamicList<scalar>>(tgtWeights_, newToOldLocalTgtFace); tgtWeights_ = List<DynamicList<scalar>>(tgtWeights_, newToOldLocalTgtFace);
@ -212,7 +212,12 @@ void Foam::patchToPatches::inverseDistance::rDistributeTgt
{ {
patchToPatch::rDistributeTgt(tgtPatch); patchToPatch::rDistributeTgt(tgtPatch);
rDistributeListList(tgtPatch.size(), tgtMapPtr_(), tgtWeights_); patchToPatchTools::rDistributeListList
(
tgtPatch.size(),
tgtMapPtr_(),
tgtWeights_
);
} }

View File

@ -114,9 +114,9 @@ class inverseDistance
const primitiveOldTimePatch& tgtPatch const primitiveOldTimePatch& tgtPatch
); );
//- Subset the local target patch so that only parts that actually //- Trim the local target patch so that only parts that actually
// intersect the source remain // intersect the source remain
virtual labelList subsetLocalTgt virtual labelList trimLocalTgt
( (
const primitiveOldTimePatch& localTgtPatch const primitiveOldTimePatch& localTgtPatch
); );

View File

@ -200,13 +200,13 @@ void Foam::patchToPatches::nearest::initialise
} }
Foam::labelList Foam::patchToPatches::nearest::subsetLocalTgt Foam::labelList Foam::patchToPatches::nearest::trimLocalTgt
( (
const primitiveOldTimePatch& localTgtPatch const primitiveOldTimePatch& localTgtPatch
) )
{ {
const labelList newToOldLocalTgtFace = const labelList newToOldLocalTgtFace =
patchToPatch::subsetLocalTgt(localTgtPatch); patchToPatch::trimLocalTgt(localTgtPatch);
tgtDistances_ = List<scalar>(tgtDistances_, newToOldLocalTgtFace); tgtDistances_ = List<scalar>(tgtDistances_, newToOldLocalTgtFace);
@ -233,7 +233,12 @@ void Foam::patchToPatches::nearest::rDistributeTgt
patchToPatch::rDistributeTgt(tgtPatch); patchToPatch::rDistributeTgt(tgtPatch);
// Reverse distribute the distances // Reverse distribute the distances
rDistributeListList(tgtPatch.size(), tgtMapPtr_(), tgtDistances); patchToPatchTools::rDistributeListList
(
tgtPatch.size(),
tgtMapPtr_(),
tgtDistances
);
// If there is more than one address, remove all but the closest // If there is more than one address, remove all but the closest
tgtDistances_.resize(tgtLocalSrcFaces_.size()); tgtDistances_.resize(tgtLocalSrcFaces_.size());

View File

@ -105,9 +105,9 @@ protected:
const primitiveOldTimePatch& tgtPatch const primitiveOldTimePatch& tgtPatch
); );
//- Subset the local target patch so that only parts that actually //- Trim the local target patch so that only parts that actually
// intersect the source remain // intersect the source remain
virtual labelList subsetLocalTgt virtual labelList trimLocalTgt
( (
const primitiveOldTimePatch& localTgtPatch const primitiveOldTimePatch& localTgtPatch
); );

View File

@ -24,6 +24,7 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "patchToPatch.H" #include "patchToPatch.H"
#include "patchToPatchTools.H"
#include "cpuTime.H" #include "cpuTime.H"
#include "distributionMap.H" #include "distributionMap.H"
#include "globalIndex.H" #include "globalIndex.H"
@ -78,75 +79,6 @@ namespace Foam
{ {
return treeBoundBox(min(a.min(), b.min()), max(a.max(), b.max())); return treeBoundBox(min(a.min(), b.min()), max(a.max(), b.max()));
} }
void subsetDistributionMap
(
const boolList& oldIsUsed,
distributionMap& map,
labelList& oldToNew,
labelList& newToOld
)
{
// Create re-indexing
oldToNew.resize(map.constructSize());
newToOld.resize(count(oldIsUsed, true));
oldToNew = -1;
newToOld = -1;
{
label newi = 0;
forAll(oldIsUsed, oldi)
{
if (oldIsUsed[oldi])
{
oldToNew[oldi] = newi;
newToOld[newi] = oldi;
++ newi;
}
}
}
// Per-processor used list for construction
List<boolList> allOldIsUsed(Pstream::nProcs());
forAll(map.constructMap(), proci)
{
allOldIsUsed[proci] =
UIndirectList<bool>(oldIsUsed, map.constructMap()[proci]);
}
// Communicate to form a per-processor used list for subsetting
List<boolList> allSubOldIsUsed(Pstream::nProcs());
Pstream::exchange<boolList, bool>(allOldIsUsed, allSubOldIsUsed);
// Subset the sub map
forAll(map.subMap(), proci)
{
label newi = 0;
forAll(map.subMap()[proci], oldi)
{
if (allSubOldIsUsed[proci][oldi])
{
map.subMap()[proci][newi ++] =
map.subMap()[proci][oldi];
}
}
map.subMap()[proci].resize(newi);
}
// Subset and renumber the construct map
forAll(map.constructMap(), proci)
{
label newi = 0;
forAll(map.constructMap()[proci], oldi)
{
if (allOldIsUsed[proci][oldi])
{
map.constructMap()[proci][newi ++] =
oldToNew[map.constructMap()[proci][oldi]];
}
}
map.constructMap()[proci].resize(newi);
}
}
} }
@ -161,58 +93,6 @@ namespace Foam
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::List<Foam::List<Foam::remote>>
Foam::patchToPatch::localFacesToProcFaces
(
const List<DynamicList<label>>& localFaces,
const List<remote>& map
)
{
List<List<remote>> result(localFaces.size());
forAll(localFaces, thisFacei)
{
result[thisFacei].resize(localFaces[thisFacei].size());
forAll(localFaces[thisFacei], i)
{
result[thisFacei][i] =
isNull(map)
? remote({Pstream::myProcNo(), localFaces[thisFacei][i]})
: map[localFaces[thisFacei][i]];
}
}
return result;
}
Foam::List<Foam::DynamicList<Foam::label>>
Foam::patchToPatch::procFacesToLocalFaces
(
const List<List<remote>>& procFaces,
const HashTable<label, remote, Hash<remote>>& map
)
{
List<DynamicList<label>> result(procFaces.size());
forAll(procFaces, tgtFacei)
{
result[tgtFacei].resize(procFaces[tgtFacei].size());
forAll(procFaces[tgtFacei], i)
{
result[tgtFacei][i] =
isNull(map)
? procFaces[tgtFacei][i].elementi
: map[procFaces[tgtFacei][i]];
}
}
return result;
}
Foam::treeBoundBox Foam::patchToPatch::srcBox Foam::treeBoundBox Foam::patchToPatch::srcBox
( (
const primitiveOldTimePatch& srcPatch, const primitiveOldTimePatch& srcPatch,
@ -777,7 +657,7 @@ void Foam::patchToPatch::initialise
} }
Foam::labelList Foam::patchToPatch::subsetLocalTgt Foam::labelList Foam::patchToPatch::trimLocalTgt
( (
const primitiveOldTimePatch& localTgtPatch const primitiveOldTimePatch& localTgtPatch
) )
@ -792,9 +672,9 @@ Foam::labelList Foam::patchToPatch::subsetLocalTgt
} }
} }
// Subset the target map // Trim the target map
labelList oldToNewLocalTgtFace, newToOldLocalTgtFace; labelList oldToNewLocalTgtFace, newToOldLocalTgtFace;
subsetDistributionMap patchToPatchTools::trimDistributionMap
( (
oldLocalTgtFaceIsUsed, oldLocalTgtFaceIsUsed,
tgtMapPtr_(), tgtMapPtr_(),
@ -812,7 +692,7 @@ Foam::labelList Foam::patchToPatch::subsetLocalTgt
} }
} }
// Subset the local target faces // Trim the local target faces
tgtLocalSrcFaces_ = tgtLocalSrcFaces_ =
List<DynamicList<label>>(tgtLocalSrcFaces_, newToOldLocalTgtFace); List<DynamicList<label>>(tgtLocalSrcFaces_, newToOldLocalTgtFace);
localTgtProcFacesPtr_() = localTgtProcFacesPtr_() =
@ -827,32 +707,25 @@ void Foam::patchToPatch::distributeSrc
const primitiveOldTimePatch& srcPatch const primitiveOldTimePatch& srcPatch
) )
{ {
distributePatch(srcMapPtr_(), localSrcProcFacesPtr_()); localSrcProcFacesPtr_.reset
(
new List<remote>
(
patchToPatchTools::distributeAddressing(srcMapPtr_())
)
);
} }
void Foam::patchToPatch::rDistributeTgt(const primitiveOldTimePatch& tgtPatch) void Foam::patchToPatch::rDistributeTgt(const primitiveOldTimePatch& tgtPatch)
{ {
// Create a map from source procFace to local source face patchToPatchTools::rDistributeTgtAddressing
HashTable<label, remote, Hash<remote>> srcProcFaceToLocal;
forAll(localSrcProcFacesPtr_(), localSrcFacei)
{
srcProcFaceToLocal.insert
( (
localSrcProcFacesPtr_()[localSrcFacei], tgtPatch.size(),
localSrcFacei tgtMapPtr_(),
localSrcProcFacesPtr_(),
tgtLocalSrcFaces_
); );
}
// Collect the source procFaces on the target and convert to local
// source face addressing
List<List<remote>> tgtSrcProcFaces =
localFacesToProcFaces(tgtLocalSrcFaces_);
rDistributeListList(tgtPatch.size(), tgtMapPtr_(), tgtSrcProcFaces);
tgtLocalSrcFaces_ =
procFacesToLocalFaces(tgtSrcProcFaces, srcProcFaceToLocal);
} }
@ -892,8 +765,8 @@ Foam::patchToPatch::patchToPatch(const bool reverse)
tgtLocalSrcFaces_(), tgtLocalSrcFaces_(),
srcMapPtr_(nullptr), srcMapPtr_(nullptr),
tgtMapPtr_(nullptr), tgtMapPtr_(nullptr),
localSrcProcFacesPtr_(new List<remote>()), localSrcProcFacesPtr_(nullptr),
localTgtProcFacesPtr_(new List<remote>()) localTgtProcFacesPtr_(nullptr)
{} {}
@ -997,7 +870,12 @@ void Foam::patchToPatch::update
<< " target faces" << incrIndent << endl; << " target faces" << incrIndent << endl;
// Determine if patches are present on multiple processors // Determine if patches are present on multiple processors
calcSingleProcess(srcPatch, tTgtPatch); singleProcess_ =
patchToPatchTools::singleProcess
(
srcPatch.size(),
tTgtPatch.size()
);
// Do intersection in serial or parallel as appropriate // Do intersection in serial or parallel as appropriate
if (isSingleProcess()) if (isSingleProcess())
@ -1027,7 +905,7 @@ void Foam::patchToPatch::update
// faces will get distributed that ultimately are not used. These will // faces will get distributed that ultimately are not used. These will
// be filtered out after the intersection has been completed. // be filtered out after the intersection has been completed.
tgtMapPtr_ = tgtMapPtr_ =
patchDistributionMap patchToPatchTools::constructDistributionMap
( (
tgtPatchSendFaces tgtPatchSendFaces
( (
@ -1037,16 +915,16 @@ void Foam::patchToPatch::update
tTgtPatch tTgtPatch
) )
); );
tmpNrc<PrimitiveOldTimePatch<faceList, pointField>> localTTgtPatchPtr = autoPtr<PrimitiveOldTimePatch<faceList, pointField>> localTTgtPatchPtr;
tmpNrc<PrimitiveOldTimePatch<faceList, pointField>> localTgtProcFacesPtr_.reset
( (
new PrimitiveOldTimePatch<faceList, pointField> new List<remote>
( (
distributePatch distributePatch
( (
tgtMapPtr_(), tgtMapPtr_(),
tTgtPatch, tTgtPatch,
localTgtProcFacesPtr_() localTTgtPatchPtr
) )
) )
); );
@ -1082,11 +960,19 @@ void Foam::patchToPatch::update
); );
} }
// Subset the local target patch // Trim the local target patch
subsetLocalTgt(localTTgtPatch); trimLocalTgt(localTTgtPatch);
// Distribute the source patch // Distribute the source patch
srcMapPtr_ = patchDistributionMap(srcPatchSendFaces()); srcMapPtr_ =
patchToPatchTools::constructDistributionMap
(
patchToPatchTools::procSendIndices
(
tgtLocalSrcFaces_,
localTgtProcFacesPtr_()
)
);
distributeSrc(srcPatch); distributeSrc(srcPatch);
// Reverse distribute coupling data back to the target // Reverse distribute coupling data back to the target

View File

@ -89,49 +89,6 @@ protected:
// Private Member Functions // Private Member Functions
// Indexing
//- Transfer list-list b into list-list a
template<class SubListA, class SubListB>
static inline void transferListList
(
List<SubListA>& a,
List<SubListB>& b
);
//- Reverse distribute a list-list given the map
template<class Type>
static inline void rDistributeListList
(
const label size,
const distributionMap& map,
List<List<Type>>& data
);
//- Reverse distribute a dynamic list-list given the map
template<class Type>
static inline void rDistributeListList
(
const label size,
const distributionMap& map,
List<DynamicList<Type>>& data
);
//- Map local faces to proc faces
static List<List<remote>> localFacesToProcFaces
(
const List<DynamicList<label>>& localFaces,
const List<remote>& map = NullObjectRef<List<remote>>()
);
//- Map proc faces to local faces
static List<DynamicList<label>> procFacesToLocalFaces
(
const List<List<remote>>& procFaces,
const HashTable<label, remote, Hash<remote>>& map
);
// Searching // Searching
//- Get the bound box for a source face //- Get the bound box for a source face
@ -226,15 +183,6 @@ protected:
// Parallel functionality // Parallel functionality
//- Determine whether or not the intersection of the given patches
// is on a single process. Set singleProcess_ to that process if
// so, and to -1 if not.
void calcSingleProcess
(
const primitiveOldTimePatch& srcPatch,
const primitiveOldTimePatch& tgtPatch
);
//- Determine which target faces need to be sent to the source. //- Determine which target faces need to be sent to the source.
// This is done before intersection. Bound boxes are used to // This is done before intersection. Bound boxes are used to
// estimate what faces will intersect. // estimate what faces will intersect.
@ -246,33 +194,14 @@ protected:
const primitiveOldTimePatch& tgtPatch const primitiveOldTimePatch& tgtPatch
) const; ) const;
//- Determine which source faces need to be sent to the target.
// This is done after intersection, using the addressing generated
// by the intersection.
labelListList srcPatchSendFaces() const;
//- Create a distribution map from a list-list of faces to be sent
// (i.e., the result of calling one of the above methods).
autoPtr<distributionMap> patchDistributionMap
(
labelListList&& sendFaces
) const;
//- Distribute a patch given its distribution map //- Distribute a patch given its distribution map
PrimitiveOldTimePatch<faceList, pointField> distributePatch static List<remote> distributePatch
( (
const distributionMap& map, const distributionMap& map,
const primitiveOldTimePatch& patch, const primitiveOldTimePatch& patch,
List<remote>& localProcFaces autoPtr<PrimitiveOldTimePatch<faceList, pointField>>&
) const; localPatchPtr
);
//- As above, but when you want the proc-faces without the
// associated patch geometry
void distributePatch
(
const distributionMap& map,
List<remote>& localProcFaces
) const;
// Hooks // Hooks
@ -286,9 +215,9 @@ protected:
const primitiveOldTimePatch& tgtPatch const primitiveOldTimePatch& tgtPatch
); );
//- Subset the local target patch so that only parts that actually //- Trim the local target patch so that only parts that actually
// intersect the source remain // intersect the source remain
virtual labelList subsetLocalTgt virtual labelList trimLocalTgt
( (
const primitiveOldTimePatch& localTgtPatch const primitiveOldTimePatch& localTgtPatch
); );

View File

@ -24,63 +24,7 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "patchToPatch.H" #include "patchToPatch.H"
#include "patchToPatchTools.H"
// * * * * * * * * * * * Private Static Member Functions * * * * * * * * * * //
template<class SubListA, class SubListB>
inline void Foam::patchToPatch::transferListList
(
List<SubListA>& a,
List<SubListB>& b
)
{
a.resize(b.size());
forAll(a, i)
{
a[i].transfer(b[i]);
}
}
template<class Type>
inline void Foam::patchToPatch::rDistributeListList
(
const label size,
const distributionMap& map,
List<List<Type>>& data
)
{
distributionMapBase::distribute
(
Pstream::commsTypes::nonBlocking,
List<labelPair>(),
size,
map.constructMap(),
false,
map.subMap(),
false,
data,
ListAppendEqOp<Type>(),
flipOp(),
List<Type>()
);
}
template<class Type>
inline void Foam::patchToPatch::rDistributeListList
(
const label size,
const distributionMap& map,
List<DynamicList<Type>>& data
)
{
List<List<Type>> tData;
transferListList(tData, data);
rDistributeListList(size, map, tData);
transferListList(data, tData);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
@ -107,8 +51,12 @@ Foam::patchToPatch::srcTgtProcFaces() const
{ {
return return
isSingleProcess() isSingleProcess()
? localFacesToProcFaces(srcLocalTgtFaces_) ? patchToPatchTools::localToRemote(srcLocalTgtFaces_)
: localFacesToProcFaces(srcLocalTgtFaces_, localTgtProcFacesPtr_()); : patchToPatchTools::localToRemote
(
srcLocalTgtFaces_,
localTgtProcFacesPtr_()
);
} }
@ -117,8 +65,12 @@ Foam::patchToPatch::tgtSrcProcFaces() const
{ {
return return
isSingleProcess() isSingleProcess()
? localFacesToProcFaces(tgtLocalSrcFaces_) ? patchToPatchTools::localToRemote(tgtLocalSrcFaces_)
: localFacesToProcFaces(tgtLocalSrcFaces_, localSrcProcFacesPtr_()); : patchToPatchTools::localToRemote
(
tgtLocalSrcFaces_,
localSrcProcFacesPtr_()
);
} }

View File

@ -31,44 +31,6 @@ License
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::patchToPatch::calcSingleProcess
(
const primitiveOldTimePatch& srcPatch,
const primitiveOldTimePatch& tgtPatch
)
{
singleProcess_ = 0;
if (Pstream::parRun())
{
boolList procHasFaces(Pstream::nProcs(), false);
if ((srcPatch.size() > 0) || (tgtPatch.size() > 0))
{
procHasFaces[Pstream::myProcNo()] = true;
}
Pstream::gatherList(procHasFaces);
Pstream::scatterList(procHasFaces);
const label nProcsHaveFaces = count(procHasFaces, true);
if (nProcsHaveFaces == 0)
{
singleProcess_ = 0;
}
if (nProcsHaveFaces == 1)
{
singleProcess_ = findIndex(procHasFaces, true);
}
if (nProcsHaveFaces > 1)
{
singleProcess_ = -1;
}
}
}
Foam::labelListList Foam::patchToPatch::tgtPatchSendFaces Foam::labelListList Foam::patchToPatch::tgtPatchSendFaces
( (
const primitiveOldTimePatch& srcPatch, const primitiveOldTimePatch& srcPatch,
@ -127,103 +89,12 @@ Foam::labelListList Foam::patchToPatch::tgtPatchSendFaces
} }
Foam::labelListList Foam::patchToPatch::srcPatchSendFaces() const Foam::List<Foam::remote> Foam::patchToPatch::distributePatch
{
// Send a source face to a proc if target face on that proc intersects it
List<labelHashSet> resultDyn(Pstream::nProcs());
forAll(tgtLocalSrcFaces_, tgtFacei)
{
const label tgtProci = localTgtProcFacesPtr_()[tgtFacei].proci;
forAll(tgtLocalSrcFaces_[tgtFacei], i)
{
const label srcFacei = tgtLocalSrcFaces_[tgtFacei][i];
resultDyn[tgtProci].insert(srcFacei);
}
}
// Transfer to non-dynamic storage
labelListList result(Pstream::nProcs());
forAll(result, proci)
{
result[proci] = resultDyn[proci].toc();
}
return result;
}
Foam::autoPtr<Foam::distributionMap> Foam::patchToPatch::patchDistributionMap
(
labelListList&& sendFaces
) const
{
// Figure out how many target faces are to be received
labelList nReceiveFaces(Pstream::nProcs());
{
labelListList nSendFaces(Pstream::nProcs());
nSendFaces[Pstream::myProcNo()].setSize(Pstream::nProcs());
forAll(sendFaces, proci)
{
nSendFaces[Pstream::myProcNo()][proci] =
sendFaces[proci].size();
}
Pstream::gatherList(nSendFaces);
Pstream::scatterList(nSendFaces);
forAll(sendFaces, proci)
{
nReceiveFaces[proci] =
nSendFaces[proci][Pstream::myProcNo()];
}
}
// Determine order of receiving
labelListList receiveFaces(Pstream::nProcs());
// Local faces first
receiveFaces[Pstream::myProcNo()] =
identity(sendFaces[Pstream::myProcNo()].size());
// Remote faces next
label localFacei = receiveFaces[Pstream::myProcNo()].size();
forAll(receiveFaces, proci)
{
if (proci != Pstream::myProcNo())
{
const label n = nReceiveFaces[proci];
receiveFaces[proci].setSize(n);
for (label i = 0; i < n; i ++)
{
receiveFaces[proci][i] = localFacei ++;
}
}
}
// Construct and return the map
return
autoPtr<distributionMap>
(
new distributionMap
(
localFacei,
move(sendFaces),
move(receiveFaces)
)
);
}
Foam::PrimitiveOldTimePatch<Foam::faceList, Foam::pointField>
Foam::patchToPatch::distributePatch
( (
const distributionMap& map, const distributionMap& map,
const primitiveOldTimePatch& patch, const primitiveOldTimePatch& patch,
List<remote>& localProcFaces autoPtr<PrimitiveOldTimePatch<faceList, pointField>>& localPatchPtr
) const )
{ {
static const label thisProci = Pstream::myProcNo(); static const label thisProci = Pstream::myProcNo();
@ -294,6 +165,7 @@ Foam::patchToPatch::distributePatch
} }
// Allocate // Allocate
List<remote> localProcFaces;
faceList localFaces; faceList localFaces;
pointField localPoints; pointField localPoints;
pointField localPoints0; pointField localPoints0;
@ -313,37 +185,9 @@ Foam::patchToPatch::distributePatch
} }
} }
// Renumber and flatten // Construct the result
label localFacei = 0, localPointi = 0; label localFacei = 0, localPointi = 0;
// Local data first
{
const labelList& fis = procLocalFaceis[thisProci];
const faceList& fs = procLocalFaces[thisProci];
forAll(fis, i)
{
localProcFaces[localFacei] = {thisProci, fis[i]};
localFaces[localFacei] = face(fs[i] + localPointi);
localFacei ++;
}
const pointField& ps = procLocalPoints[thisProci];
const pointField& ps0 = procLocalPoints0[thisProci];
forAll(ps, i)
{
localPoints[localPointi] = ps[i];
if (patch.has0())
{
localPoints0[localPointi] = ps0[i];
}
localPointi ++;
}
}
// Remote data after
forAll(procLocalFaces, proci) forAll(procLocalFaces, proci)
{
if (proci != thisProci)
{ {
const labelList& fis = procLocalFaceis[proci]; const labelList& fis = procLocalFaceis[proci];
const faceList& fs = procLocalFaces[proci]; const faceList& fs = procLocalFaces[proci];
@ -366,103 +210,25 @@ Foam::patchToPatch::distributePatch
localPointi ++; localPointi ++;
} }
} }
}
return // Construct the local patch
localPatchPtr.reset
(
patch.has0() patch.has0()
? PrimitiveOldTimePatch<faceList, pointField> ? new PrimitiveOldTimePatch<faceList, pointField>
( (
localFaces, localFaces,
localPoints, localPoints,
localPoints0 localPoints0
) )
: PrimitiveOldTimePatch<faceList, pointField> : new PrimitiveOldTimePatch<faceList, pointField>
( (
localFaces, localFaces,
localPoints localPoints
)
); );
}
return localProcFaces;
void Foam::patchToPatch::distributePatch
(
const distributionMap& map,
List<remote>& localProcFaces
) const
{
static const label thisProci = Pstream::myProcNo();
// Exchange per-processor data
List<labelList> procLocalFaceis(Pstream::nProcs());
{
PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
// Send
for (label proci = 0; proci < Pstream::nProcs(); proci++)
{
const labelList& sendFaceis = map.subMap()[proci];
if (proci != thisProci && sendFaceis.size())
{
UOPstream(proci, pBufs)() << sendFaceis;
}
}
pBufs.finishedSends();
// Map local data
{
const labelList& sendFaceis = map.subMap()[thisProci];
procLocalFaceis[thisProci] = sendFaceis;
}
// Receive remote data
for (label proci = 0; proci < Pstream::nProcs(); proci++)
{
if (proci != thisProci && map.constructMap()[proci].size())
{
UIPstream(proci, pBufs)() >> procLocalFaceis[proci];
}
}
}
// Allocate
{
label nLocalFaces = 0;
forAll(procLocalFaceis, proci)
{
nLocalFaces += procLocalFaceis[proci].size();
}
localProcFaces.setSize(nLocalFaces);
}
// Renumber and flatten
label localFacei = 0;
// Local data first
{
const labelList& fis = procLocalFaceis[thisProci];
forAll(fis, i)
{
localProcFaces[localFacei] = {thisProci, fis[i]};
localFacei ++;
}
}
// Remote data after
forAll(procLocalFaceis, proci)
{
if (proci != thisProci)
{
const labelList& fis = procLocalFaceis[proci];
forAll(fis, i)
{
localProcFaces[localFacei] = {proci, fis[i]};
localFacei ++;
}
}
}
} }

View File

@ -86,13 +86,13 @@ bool Foam::patchToPatches::rays::intersectFaces
} }
Foam::labelList Foam::patchToPatches::rays::subsetLocalTgt Foam::labelList Foam::patchToPatches::rays::trimLocalTgt
( (
const primitiveOldTimePatch& localTgtPatch const primitiveOldTimePatch& localTgtPatch
) )
{ {
const labelList newToOldLocalTgtFace = const labelList newToOldLocalTgtFace =
patchToPatch::subsetLocalTgt(localTgtPatch); patchToPatch::trimLocalTgt(localTgtPatch);
localTgtPatchPtr_.reset localTgtPatchPtr_.reset
( (
@ -113,11 +113,11 @@ void Foam::patchToPatches::rays::distributeSrc
const primitiveOldTimePatch& srcPatch const primitiveOldTimePatch& srcPatch
) )
{ {
localSrcPatchPtr_.reset localSrcProcFacesPtr_.reset
( (
new PrimitiveOldTimePatch<faceList, pointField> new List<remote>
( (
distributePatch(srcMapPtr_(), srcPatch, localSrcProcFacesPtr_()) distributePatch(srcMapPtr_(), srcPatch, localSrcPatchPtr_)
) )
); );
} }

View File

@ -94,7 +94,7 @@ class rays
//- Subset the local target patch so that only parts that actually //- Subset the local target patch so that only parts that actually
// intersect the source remain // intersect the source remain
virtual labelList subsetLocalTgt virtual labelList trimLocalTgt
( (
const primitiveOldTimePatch& localTgtPatch const primitiveOldTimePatch& localTgtPatch
); );

View File

@ -0,0 +1,402 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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/>.
\*---------------------------------------------------------------------------*/
#include "patchToPatchTools.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Foam::label Foam::patchToPatchTools::singleProcess
(
const label srcSize,
const label tgtSize
)
{
label result = 0;
if (Pstream::parRun())
{
boolList procHasElements(Pstream::nProcs(), false);
if (srcSize > 0 || tgtSize > 0)
{
procHasElements[Pstream::myProcNo()] = true;
}
Pstream::gatherList(procHasElements);
Pstream::scatterList(procHasElements);
const label nProcsHaveElements = count(procHasElements, true);
if (nProcsHaveElements == 0)
{
result = 0;
}
if (nProcsHaveElements == 1)
{
result = findIndex(procHasElements, true);
}
if (nProcsHaveElements > 1)
{
result = -1;
}
}
return result;
}
Foam::autoPtr<Foam::distributionMap>
Foam::patchToPatchTools::constructDistributionMap
(
const labelListList& procSendIndices
)
{
// Communicate the size of all the transfers
labelListList transferSizes(Pstream::nProcs());
transferSizes[Pstream::myProcNo()].setSize(Pstream::nProcs());
forAll(procSendIndices, proci)
{
transferSizes[Pstream::myProcNo()][proci] =
procSendIndices[proci].size();
}
Pstream::gatherList(transferSizes);
Pstream::scatterList(transferSizes);
// Determine order of receiving
labelListList constructMap(Pstream::nProcs());
label index = constructMap[Pstream::myProcNo()].size();
forAll(constructMap, proci)
{
const label n = transferSizes[proci][Pstream::myProcNo()];
constructMap[proci].setSize(n);
for (label i = 0; i < n; i ++)
{
constructMap[proci][i] = index ++;
}
}
// Construct and return the map
return
autoPtr<distributionMap>
(
new distributionMap
(
index,
labelListList(procSendIndices),
move(constructMap)
)
);
}
Foam::List<Foam::remote> Foam::patchToPatchTools::distributeAddressing
(
const distributionMap& map
)
{
static const label thisProci = Pstream::myProcNo();
// Exchange per-processor data
List<labelList> procLocalFaceis(Pstream::nProcs());
{
PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
// Send
for (label proci = 0; proci < Pstream::nProcs(); proci++)
{
const labelList& sendFaceis = map.subMap()[proci];
if (proci != thisProci && sendFaceis.size())
{
UOPstream(proci, pBufs)() << sendFaceis;
}
}
pBufs.finishedSends();
// Map local data
{
const labelList& sendFaceis = map.subMap()[thisProci];
procLocalFaceis[thisProci] = sendFaceis;
}
// Receive remote data
for (label proci = 0; proci < Pstream::nProcs(); proci++)
{
if (proci != thisProci && map.constructMap()[proci].size())
{
UIPstream(proci, pBufs)() >> procLocalFaceis[proci];
}
}
}
// Allocate
List<remote> localProcFaces;
{
label nLocalFaces = 0;
forAll(procLocalFaceis, proci)
{
nLocalFaces += procLocalFaceis[proci].size();
}
localProcFaces.setSize(nLocalFaces);
}
// Construct the result
label localFacei = 0;
forAll(procLocalFaceis, proci)
{
const labelList& fis = procLocalFaceis[proci];
forAll(fis, i)
{
localProcFaces[localFacei] = {proci, fis[i]};
localFacei ++;
}
}
return localProcFaces;
}
Foam::labelListList Foam::patchToPatchTools::procSendIndices
(
const labelListList& tgtLocalSrcFaces,
const List<remote>& localTgtProcFaces
)
{
// Send a source face to a proc if target face on that proc intersects it
List<labelHashSet> resultDyn(Pstream::nProcs());
forAll(tgtLocalSrcFaces, tgtFacei)
{
const label tgtProci = localTgtProcFaces[tgtFacei].proci;
forAll(tgtLocalSrcFaces[tgtFacei], i)
{
const label srcFacei = tgtLocalSrcFaces[tgtFacei][i];
resultDyn[tgtProci].insert(srcFacei);
}
}
// Transfer to non-dynamic storage
labelListList result(Pstream::nProcs());
forAll(result, proci)
{
result[proci] = resultDyn[proci].toc();
}
return result;
}
Foam::labelListList Foam::patchToPatchTools::procSendIndices
(
const List<DynamicList<label>>& tgtLocalSrcFaces,
const List<remote>& localTgtProcFaces
)
{
return
procSendIndices
(
labelListList(tgtLocalSrcFaces),
localTgtProcFaces
);
}
void Foam::patchToPatchTools::trimDistributionMap
(
const boolList& oldIsUsed,
distributionMap& map,
labelList& oldToNew,
labelList& newToOld
)
{
// Create re-indexing
oldToNew.resize(oldIsUsed.size());
newToOld.resize(count(oldIsUsed, true));
oldToNew = -1;
newToOld = -1;
label newi = 0;
forAll(oldIsUsed, oldi)
{
if (oldIsUsed[oldi])
{
oldToNew[oldi] = newi;
newToOld[newi] = oldi;
++ newi;
}
}
// Per-processor used list for construction
List<boolList> allOldIsUsed(Pstream::nProcs());
forAll(map.constructMap(), proci)
{
allOldIsUsed[proci] =
UIndirectList<bool>(oldIsUsed, map.constructMap()[proci]);
}
// Communicate to form a per-processor used list for subsetting
List<boolList> allSubOldIsUsed(Pstream::nProcs());
Pstream::exchange<boolList, bool>(allOldIsUsed, allSubOldIsUsed);
// Subset the sub map
forAll(map.subMap(), proci)
{
label newi = 0;
forAll(map.subMap()[proci], oldi)
{
if (allSubOldIsUsed[proci][oldi])
{
map.subMap()[proci][newi ++] =
map.subMap()[proci][oldi];
}
}
map.subMap()[proci].resize(newi);
}
// Subset and renumber the construct map
forAll(map.constructMap(), proci)
{
label newi = 0;
forAll(map.constructMap()[proci], oldi)
{
if (allOldIsUsed[proci][oldi])
{
map.constructMap()[proci][newi ++] =
oldToNew[map.constructMap()[proci][oldi]];
}
}
map.constructMap()[proci].resize(newi);
}
}
Foam::List<Foam::List<Foam::remote>> Foam::patchToPatchTools::localToRemote
(
const labelListList& indices,
const List<remote>& indexToProcIndex
)
{
List<List<remote>> result(indices.size());
forAll(indices, thisFacei)
{
result[thisFacei].resize(indices[thisFacei].size());
forAll(indices[thisFacei], i)
{
result[thisFacei][i] =
isNull(indexToProcIndex)
? remote(Pstream::myProcNo(), indices[thisFacei][i])
: indexToProcIndex[indices[thisFacei][i]];
}
}
return result;
}
Foam::List<Foam::List<Foam::remote>> Foam::patchToPatchTools::localToRemote
(
const List<DynamicList<label>>& indices,
const List<remote>& indexToProcIndex
)
{
return
localToRemote
(
labelListList(indices),
indexToProcIndex
);
}
void Foam::patchToPatchTools::rDistributeTgtAddressing
(
const label tgtSize,
const distributionMap& tgtMap,
const List<remote>& localSrcProcFaces,
labelListList& tgtLocalSrcFaces
)
{
// Create a map from source procFace to local source face
HashTable<label, remote, Hash<remote>> srcProcFaceToLocal;
forAll(localSrcProcFaces, localSrcFacei)
{
srcProcFaceToLocal.insert
(
localSrcProcFaces[localSrcFacei],
localSrcFacei
);
}
// Collect the source procFaces on the target and convert to local
// source face addressing
List<List<remote>> tgtSrcProcFaces = localToRemote(tgtLocalSrcFaces);
rDistributeListList(tgtSize, tgtMap, tgtSrcProcFaces);
tgtLocalSrcFaces.resize(tgtSize);
forAll(tgtSrcProcFaces, tgtFacei)
{
tgtLocalSrcFaces[tgtFacei].resize(tgtSrcProcFaces[tgtFacei].size());
forAll(tgtSrcProcFaces[tgtFacei], i)
{
tgtLocalSrcFaces[tgtFacei][i] =
srcProcFaceToLocal[tgtSrcProcFaces[tgtFacei][i]];
}
}
}
void Foam::patchToPatchTools::rDistributeTgtAddressing
(
const label tgtSize,
const distributionMap& tgtMap,
const List<remote>& localSrcProcFaces,
List<DynamicList<label>>& tgtLocalSrcFaces
)
{
labelListList tTgtLocalSrcFaces;
transferListList(tTgtLocalSrcFaces, tgtLocalSrcFaces);
rDistributeTgtAddressing
(
tgtSize,
tgtMap,
localSrcProcFaces,
tTgtLocalSrcFaces
);
transferListList(tgtLocalSrcFaces, tTgtLocalSrcFaces);
}
// ************************************************************************* //

View File

@ -0,0 +1,153 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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/>.
Namespace
Foam::patchToPatchTools
\*---------------------------------------------------------------------------*/
#ifndef patchToPatchTools_H
#define patchToPatchTools_H
#include "distributionMap.H"
#include "remote.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace patchToPatchTools
{
//- Determine whether this intersection is confined to a single processor or
// not, purely from the numbers of elements in the geometry
label singleProcess(const label srcSize, const label tgtSize);
//- Turn a list of send-to-processor indices into a distribution map
autoPtr<distributionMap> constructDistributionMap
(
const labelListList& procSendIndices
);
//- Construct local addressing from the given distribution map. The result is a
// list of remote element indices that correspond to the elements that were
// sent by the distribution map.
List<remote> distributeAddressing(const distributionMap& map);
//- Given a local intersection addressing, determine what elements need sending
// to each processor
labelListList procSendIndices
(
const labelListList& tgtLocalSrcFaces,
const List<remote>& localTgtProcFaces
);
//- Dynamic list overload of the above
labelListList procSendIndices
(
const List<DynamicList<label>>& tgtLocalSrcFaces,
const List<remote>& localTgtProcFaces
);
//- Trim unused elements from a distribution map. Return the map and maps
// between the old and new construct list.
void trimDistributionMap
(
const boolList& oldIsUsed,
distributionMap& map,
labelList& oldToNew,
labelList& newToOld
);
//- Transfer list-list b into list-list a
template<class SubListA, class SubListB>
static inline void transferListList
(
List<SubListA>& a,
List<SubListB>& b
);
//- Reverse distribute a list-list given the map
template<class Type>
static inline void rDistributeListList
(
const label size,
const distributionMap& map,
List<List<Type>>& data
);
//- Dynamic list overload of the above
template<class Type>
static inline void rDistributeListList
(
const label size,
const distributionMap& map,
List<DynamicList<Type>>& data
);
//- Map local to remote indices
List<List<remote>> localToRemote
(
const labelListList& indices,
const List<remote>& indexToProcIndex = NullObjectRef<List<remote>>()
);
//- Dynamic list overload of the above
List<List<remote>> localToRemote
(
const List<DynamicList<label>>& indices,
const List<remote>& indexToProcIndex = NullObjectRef<List<remote>>()
);
//- Reverse distribute a set of target addressing
void rDistributeTgtAddressing
(
const label tgtSize,
const distributionMap& tgtMap,
const List<remote>& localSrcProcFaces,
labelListList& tgtLocalSrcFaces
);
//- Dynamic list overload of the above
void rDistributeTgtAddressing
(
const label tgtSize,
const distributionMap& tgtMap,
const List<remote>& localSrcProcFaces,
List<DynamicList<label>>& tgtLocalSrcFaces
);
} // End namespace patchToPatchTools
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "patchToPatchToolsTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2022 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/>.
\*---------------------------------------------------------------------------*/
#include "patchToPatchTools.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class SubListA, class SubListB>
void Foam::patchToPatchTools::transferListList
(
List<SubListA>& a,
List<SubListB>& b
)
{
a.resize(b.size());
forAll(a, i)
{
a[i].transfer(b[i]);
}
}
template<class Type>
void Foam::patchToPatchTools::rDistributeListList
(
const label size,
const distributionMap& map,
List<List<Type>>& data
)
{
distributionMapBase::distribute
(
Pstream::commsTypes::nonBlocking,
List<labelPair>(),
size,
map.constructMap(),
false,
map.subMap(),
false,
data,
ListAppendEqOp<Type>(),
flipOp(),
List<Type>()
);
}
template<class Type>
void Foam::patchToPatchTools::rDistributeListList
(
const label size,
const distributionMap& map,
List<DynamicList<Type>>& data
)
{
List<List<Type>> tData;
transferListList(tData, data);
rDistributeListList(size, map, tData);
transferListList(data, tData);
}
// ************************************************************************* //

View File

@ -60,19 +60,5 @@ $(surfWriters)/proxy/proxySurfaceWriter.C
$(surfWriters)/raw/rawSurfaceWriter.C $(surfWriters)/raw/rawSurfaceWriter.C
$(surfWriters)/vtk/vtkSurfaceWriter.C $(surfWriters)/vtk/vtkSurfaceWriter.C
meshToMesh/meshToMesh.C
meshToMesh/meshToMeshParallelOps.C
meshToMesh/patchToPatchFvPatchFieldMapper.C
meshToMeshMethods = meshToMesh/calcMethod
$(meshToMeshMethods)/meshToMeshMethod/meshToMeshMethod.C
$(meshToMeshMethods)/meshToMeshMethod/meshToMeshMethodNew.C
$(meshToMeshMethods)/cellVolumeWeight/cellVolumeWeightMethod.C
$(meshToMeshMethods)/direct/directMethod.C
$(meshToMeshMethods)/mapNearest/mapNearestMethod.C
meshToMesh0/meshToMesh0.C
meshToMesh0/calculateMeshToMesh0Addressing.C
meshToMesh0/calculateMeshToMesh0Weights.C
LIB = $(FOAM_LIBBIN)/libsampling LIB = $(FOAM_LIBBIN)/libsampling

View File

@ -1,462 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2012-2022 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/>.
\*---------------------------------------------------------------------------*/
#include "meshToMesh.H"
#include "globalIndex.H"
#include "meshToMeshMethod.H"
#include "PatchTools.H"
#include "processorPolyPatch.H"
#include "Time.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(meshToMesh, 0);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::scalar Foam::meshToMesh::calculate(const word& methodName)
{
Info<< "Creating mesh-to-mesh addressing for " << srcRegion_.name()
<< " and " << tgtRegion_.name() << " regions using "
<< methodName << endl;
singleMeshProc_ = calcDistribution(srcRegion_, tgtRegion_);
autoPtr<meshToMeshMethod> methodPtr;
scalar V = 0;
if (singleMeshProc_ == -1)
{
// create global indexing for src and tgt meshes
globalIndex globalSrcCells(srcRegion_.nCells());
globalIndex globalTgtCells(tgtRegion_.nCells());
// Create processor map of overlapping cells. This map gets
// (possibly remote) cells from the tgt mesh such that they (together)
// cover all of the src mesh
autoPtr<distributionMap> mapPtr = calcProcMap(srcRegion_, tgtRegion_);
const distributionMap& map = mapPtr();
// Distribute the target geometry to create local-target geometry
// (i.e., local to the source)
pointField localTgtPoints;
faceList localTgtFaces;
labelList localTgtFaceOwners;
labelList localTgtFaceNeighbours;
labelList localTgtGlobalCellIDs;
distributeAndMergeCells
(
map,
tgtRegion_,
globalTgtCells,
localTgtPoints,
localTgtFaces,
localTgtFaceOwners,
localTgtFaceNeighbours,
localTgtGlobalCellIDs
);
// create a new target mesh
polyMesh localTgtMesh
(
IOobject
(
"localTgt",
tgtRegion_.time().timeName(),
tgtRegion_.time(),
IOobject::NO_READ
),
move(localTgtPoints),
move(localTgtFaces),
move(localTgtFaceOwners),
move(localTgtFaceNeighbours),
false
);
// create some dummy patch info and add to the new target mesh
List<polyPatch*> patches(1);
patches[0] = new polyPatch
(
"defaultFaces",
localTgtMesh.nFaces() - localTgtMesh.nInternalFaces(),
localTgtMesh.nInternalFaces(),
0,
localTgtMesh.boundaryMesh(),
word::null
);
localTgtMesh.addPatches(patches);
// force calculation of tet-base points used for point-in-cell
(void)localTgtMesh.tetBasePtIs();
if (debug)
{
Pout<< "Created local target mesh:" << nl
<< " cells = " << tgtRegion_.nCells()
<< ", local cells = " << localTgtMesh.nCells() << nl
<< " faces = " << tgtRegion_.nFaces()
<< ", local faces = " << localTgtMesh.nFaces() << endl;
if (debug > 1)
{
Pout<< "Writing local target mesh: "
<< localTgtMesh.name() << endl;
localTgtMesh.write();
}
}
// Do the intersection
methodPtr = meshToMeshMethod::New(methodName, srcRegion_, localTgtMesh);
V = methodPtr->calculate
(
srcToTgtCellAddr_,
srcToTgtCellWght_,
tgtToSrcCellAddr_,
tgtToSrcCellWght_
);
// per source cell the target cell address in localTgtMesh mesh
forAll(srcToTgtCellAddr_, i)
{
labelList& addressing = srcToTgtCellAddr_[i];
forAll(addressing, addrI)
{
addressing[addrI] = localTgtGlobalCellIDs[addressing[addrI]];
}
}
// convert target addresses in localTgtMesh into global cell numbering
forAll(tgtToSrcCellAddr_, i)
{
labelList& addressing = tgtToSrcCellAddr_[i];
forAll(addressing, addrI)
{
addressing[addrI] = globalSrcCells.toGlobal(addressing[addrI]);
}
}
// send the connectivity back to the target
distributionMapBase::distribute
(
Pstream::commsTypes::nonBlocking,
List<labelPair>(),
tgtRegion_.nCells(),
map.constructMap(),
false,
map.subMap(),
false,
tgtToSrcCellAddr_,
ListAppendEqOp<label>(),
flipOp(),
labelList()
);
distributionMapBase::distribute
(
Pstream::commsTypes::nonBlocking,
List<labelPair>(),
tgtRegion_.nCells(),
map.constructMap(),
false,
map.subMap(),
false,
tgtToSrcCellWght_,
ListAppendEqOp<scalar>(),
flipOp(),
scalarList()
);
// weights normalisation
methodPtr->normalise
(
srcRegion_,
srcToTgtCellAddr_,
srcToTgtCellWght_
);
methodPtr->normalise
(
tgtRegion_,
tgtToSrcCellAddr_,
tgtToSrcCellWght_
);
// cache maps and reset addresses
List<Map<label>> cMap;
srcMapPtr_.reset
(
new distributionMap(globalSrcCells, tgtToSrcCellAddr_, cMap)
);
tgtMapPtr_.reset
(
new distributionMap(globalTgtCells, srcToTgtCellAddr_, cMap)
);
// collect volume intersection contributions
reduce(V, sumOp<scalar>());
}
else
{
// Do the intersection
methodPtr = meshToMeshMethod::New(methodName, srcRegion_, tgtRegion_);
V = methodPtr->calculate
(
srcToTgtCellAddr_,
srcToTgtCellWght_,
tgtToSrcCellAddr_,
tgtToSrcCellWght_
);
// Normalise the weights
methodPtr->normalise
(
srcRegion_,
srcToTgtCellAddr_,
srcToTgtCellWght_
);
methodPtr->normalise
(
tgtRegion_,
tgtToSrcCellAddr_,
tgtToSrcCellWght_
);
}
Info<< " Overlap volume: " << V << endl;
return V;
}
void Foam::meshToMesh::calculatePatchToPatches(const word& methodName)
{
if (!patchToPatches_.empty())
{
FatalErrorInFunction
<< "patchToPatches already calculated"
<< exit(FatalError);
}
const word& patchToPatchType =
meshToMeshMethod::New
(
methodName,
NullObjectRef<polyMesh>(),
NullObjectRef<polyMesh>()
)->patchToPatchMethod();
patchToPatches_.setSize(srcPatchID_.size());
forAll(srcPatchID_, i)
{
const label srcPatchi = srcPatchID_[i];
const label tgtPatchi = tgtPatchID_[i];
const polyPatch& srcPP = srcRegion_.boundaryMesh()[srcPatchi];
const polyPatch& tgtPP = tgtRegion_.boundaryMesh()[tgtPatchi];
Info<< "Creating patchToPatch between source patch "
<< srcPP.name() << " and target patch " << tgtPP.name()
<< " using " << patchToPatchType << endl;
Info<< incrIndent;
patchToPatches_.set(i, patchToPatch::New(patchToPatchType, true));
patchToPatches_[i].update
(
srcPP,
PatchTools::pointNormals(srcRegion_, srcPP),
tgtPP
);
Info<< decrIndent;
}
}
void Foam::meshToMesh::constructNoCuttingPatches
(
const word& methodName,
const bool interpAllPatches
)
{
if (interpAllPatches)
{
const polyBoundaryMesh& srcBM = srcRegion_.boundaryMesh();
const polyBoundaryMesh& tgtBM = tgtRegion_.boundaryMesh();
DynamicList<label> srcPatchID(srcBM.size());
DynamicList<label> tgtPatchID(tgtBM.size());
forAll(srcBM, patchi)
{
const polyPatch& pp = srcBM[patchi];
// We want to map all the global patches, including constraint
// patches (since they might have mappable properties, e.g.
// jumpCyclic). We'll fix the value afterwards.
if (!isA<processorPolyPatch>(pp))
{
srcPatchID.append(pp.index());
label tgtPatchi = tgtBM.findPatchID(pp.name());
if (tgtPatchi != -1)
{
tgtPatchID.append(tgtPatchi);
}
else
{
FatalErrorInFunction
<< "Source patch " << pp.name()
<< " not found in target mesh. "
<< "Available target patches are " << tgtBM.names()
<< exit(FatalError);
}
}
}
srcPatchID_.transfer(srcPatchID);
tgtPatchID_.transfer(tgtPatchID);
}
// calculate volume addressing and weights
calculate(methodName);
// calculate patch addressing and weights
calculatePatchToPatches(methodName);
}
void Foam::meshToMesh::constructFromCuttingPatches
(
const word& methodName,
const HashTable<word>& patchMap,
const wordList& cuttingPatches
)
{
srcPatchID_.setSize(patchMap.size());
tgtPatchID_.setSize(patchMap.size());
label i = 0;
forAllConstIter(HashTable<word>, patchMap, iter)
{
const word& tgtPatchName = iter.key();
const word& srcPatchName = iter();
const polyPatch& srcPatch = srcRegion_.boundaryMesh()[srcPatchName];
const polyPatch& tgtPatch = tgtRegion_.boundaryMesh()[tgtPatchName];
srcPatchID_[i] = srcPatch.index();
tgtPatchID_[i] = tgtPatch.index();
i++;
}
// calculate volume addressing and weights
calculate(methodName);
// calculate patch addressing and weights
calculatePatchToPatches(methodName);
// set IDs of cutting patches on target mesh
cuttingPatches_.setSize(cuttingPatches.size());
forAll(cuttingPatches_, i)
{
const word& patchName = cuttingPatches[i];
cuttingPatches_[i] = tgtRegion_.boundaryMesh().findPatchID(patchName);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::meshToMesh::meshToMesh
(
const polyMesh& src,
const polyMesh& tgt,
const word& methodName,
bool interpAllPatches
)
:
srcRegion_(src),
tgtRegion_(tgt),
srcPatchID_(),
tgtPatchID_(),
patchToPatches_(),
cuttingPatches_(),
srcToTgtCellAddr_(),
tgtToSrcCellAddr_(),
srcToTgtCellWght_(),
tgtToSrcCellWght_(),
singleMeshProc_(-1),
srcMapPtr_(nullptr),
tgtMapPtr_(nullptr)
{
constructNoCuttingPatches(methodName, interpAllPatches);
}
Foam::meshToMesh::meshToMesh
(
const polyMesh& src,
const polyMesh& tgt,
const word& methodName,
const HashTable<word>& patchMap,
const wordList& cuttingPatches
)
:
srcRegion_(src),
tgtRegion_(tgt),
srcPatchID_(),
tgtPatchID_(),
patchToPatches_(),
cuttingPatches_(),
srcToTgtCellAddr_(),
tgtToSrcCellAddr_(),
srcToTgtCellWght_(),
tgtToSrcCellWght_(),
singleMeshProc_(-1),
srcMapPtr_(nullptr),
tgtMapPtr_(nullptr)
{
constructFromCuttingPatches
(
methodName,
patchMap,
cuttingPatches
);
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::meshToMesh::~meshToMesh()
{}
// ************************************************************************* //

View File

@ -1,833 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2012-2022 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/>.
\*---------------------------------------------------------------------------*/
#include "meshToMesh.H"
#include "OFstream.H"
#include "Time.H"
#include "globalIndex.H"
#include "processorPolyPatch.H"
#include "SubField.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::label Foam::meshToMesh::calcDistribution
(
const polyMesh& src,
const polyMesh& tgt
) const
{
label proci = 0;
if (Pstream::parRun())
{
List<label> cellsPresentOnProc(Pstream::nProcs(), 0);
if ((src.nCells() > 0) || (tgt.nCells() > 0))
{
cellsPresentOnProc[Pstream::myProcNo()] = 1;
}
else
{
cellsPresentOnProc[Pstream::myProcNo()] = 0;
}
Pstream::gatherList(cellsPresentOnProc);
Pstream::scatterList(cellsPresentOnProc);
label nHaveCells = sum(cellsPresentOnProc);
if (nHaveCells > 1)
{
proci = -1;
if (debug)
{
InfoInFunction
<< "Meshes split across multiple processors" << endl;
}
}
else if (nHaveCells == 1)
{
proci = findIndex(cellsPresentOnProc, 1);
if (debug)
{
InfoInFunction
<< "Meshes local to processor" << proci << endl;
}
}
}
return proci;
}
Foam::label Foam::meshToMesh::calcOverlappingProcs
(
const List<boundBox>& procBb,
const boundBox& bb,
boolList& overlaps
) const
{
overlaps = false;
label nOverlaps = 0;
forAll(procBb, proci)
{
const boundBox& bbp = procBb[proci];
if (bbp.overlaps(bb))
{
overlaps[proci] = true;
nOverlaps++;
}
}
return nOverlaps;
}
Foam::autoPtr<Foam::distributionMap> Foam::meshToMesh::calcProcMap
(
const polyMesh& src,
const polyMesh& tgt
) const
{
// get decomposition of cells on src mesh
List<boundBox> procBb(Pstream::nProcs());
if (src.nCells() > 0)
{
// bounding box for my mesh - do not parallel reduce
procBb[Pstream::myProcNo()] = boundBox(src.points(), false);
// slightly increase size of bounding boxes to allow for cases where
// bounding boxes are perfectly aligned
procBb[Pstream::myProcNo()].inflate(0.01);
}
else
{
procBb[Pstream::myProcNo()] = boundBox();
}
Pstream::gatherList(procBb);
Pstream::scatterList(procBb);
if (debug)
{
InfoInFunction
<< "Determining extent of src mesh per processor:" << nl
<< "\tproc\tbb" << endl;
forAll(procBb, proci)
{
Info<< '\t' << proci << '\t' << procBb[proci] << endl;
}
}
// determine which cells of tgt mesh overlaps src mesh per proc
const cellList& cells = tgt.cells();
const faceList& faces = tgt.faces();
const pointField& points = tgt.points();
labelListList sendMap;
{
// per processor indices into all segments to send
List<DynamicList<label>> dynSendMap(Pstream::nProcs());
label iniSize = floor(tgt.nCells()/Pstream::nProcs());
forAll(dynSendMap, proci)
{
dynSendMap[proci].setCapacity(iniSize);
}
// work array - whether src processor bb overlaps the tgt cell bounds
boolList procBbOverlaps(Pstream::nProcs());
forAll(cells, celli)
{
const cell& c = cells[celli];
// determine bounding box of tgt cell
boundBox cellBb(point::max, point::min);
forAll(c, facei)
{
const face& f = faces[c[facei]];
forAll(f, fp)
{
cellBb.min() = min(cellBb.min(), points[f[fp]]);
cellBb.max() = max(cellBb.max(), points[f[fp]]);
}
}
// find the overlapping tgt cells on each src processor
(void)calcOverlappingProcs(procBb, cellBb, procBbOverlaps);
forAll(procBbOverlaps, proci)
{
if (procBbOverlaps[proci])
{
dynSendMap[proci].append(celli);
}
}
}
// convert dynamicList to labelList
sendMap.setSize(Pstream::nProcs());
forAll(sendMap, proci)
{
sendMap[proci].transfer(dynSendMap[proci]);
}
}
if (debug)
{
Pout<< "Of my " << cells.size() << " target cells I need to send to:"
<< nl << "\tproc\tcells" << endl;
forAll(sendMap, proci)
{
Pout<< '\t' << proci << '\t' << sendMap[proci].size() << endl;
}
}
// send over how many tgt cells I need to receive from each processor
labelListList sendSizes(Pstream::nProcs());
sendSizes[Pstream::myProcNo()].setSize(Pstream::nProcs());
forAll(sendMap, proci)
{
sendSizes[Pstream::myProcNo()][proci] = sendMap[proci].size();
}
Pstream::gatherList(sendSizes);
Pstream::scatterList(sendSizes);
// determine order of receiving
labelListList constructMap(Pstream::nProcs());
label segmentI = 0;
forAll(constructMap, proci)
{
// what I need to receive is what other processor is sending to me
label nRecv = sendSizes[proci][Pstream::myProcNo()];
constructMap[proci].setSize(nRecv);
for (label i = 0; i < nRecv; i++)
{
constructMap[proci][i] = segmentI++;
}
}
autoPtr<distributionMap> mapPtr
(
new distributionMap
(
segmentI, // size after construction
move(sendMap),
move(constructMap)
)
);
return mapPtr;
}
void Foam::meshToMesh::distributeCells
(
const distributionMap& map,
const polyMesh& tgtMesh,
const globalIndex& globalTgtCells,
List<pointField>& points,
List<label>& nInternalFaces,
List<faceList>& faces,
List<labelList>& faceOwner,
List<labelList>& faceNeighbour,
List<labelList>& globalCellIDs,
List<labelList>& nbrProcIDs,
List<labelList>& procLocalFaceIDs
) const
{
PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
points.setSize(Pstream::nProcs());
nInternalFaces.setSize(Pstream::nProcs(), 0);
faces.setSize(Pstream::nProcs());
faceOwner.setSize(Pstream::nProcs());
faceNeighbour.setSize(Pstream::nProcs());
globalCellIDs.setSize(Pstream::nProcs());
nbrProcIDs.setSize(Pstream::nProcs());;
procLocalFaceIDs.setSize(Pstream::nProcs());;
for (label domain = 0; domain < Pstream::nProcs(); domain++)
{
const labelList& sendElems = map.subMap()[domain];
if (sendElems.size())
{
// reverse cell map
labelList reverseCellMap(tgtMesh.nCells(), -1);
forAll(sendElems, subCelli)
{
reverseCellMap[sendElems[subCelli]] = subCelli;
}
DynamicList<face> subFaces(tgtMesh.nFaces());
DynamicList<label> subFaceOwner(tgtMesh.nFaces());
DynamicList<label> subFaceNeighbour(tgtMesh.nFaces());
DynamicList<label> subNbrProcIDs(tgtMesh.nFaces());
DynamicList<label> subProcLocalFaceIDs(tgtMesh.nFaces());
label nInternal = 0;
// internal faces
forAll(tgtMesh.faceNeighbour(), facei)
{
label own = tgtMesh.faceOwner()[facei];
label nbr = tgtMesh.faceNeighbour()[facei];
label subOwn = reverseCellMap[own];
label subNbr = reverseCellMap[nbr];
if (subOwn != -1 && subNbr != -1)
{
nInternal++;
if (subOwn < subNbr)
{
subFaces.append(tgtMesh.faces()[facei]);
subFaceOwner.append(subOwn);
subFaceNeighbour.append(subNbr);
subNbrProcIDs.append(-1);
subProcLocalFaceIDs.append(-1);
}
else
{
subFaces.append(tgtMesh.faces()[facei].reverseFace());
subFaceOwner.append(subNbr);
subFaceNeighbour.append(subOwn);
subNbrProcIDs.append(-1);
subProcLocalFaceIDs.append(-1);
}
}
}
// boundary faces for new region
forAll(tgtMesh.faceNeighbour(), facei)
{
label own = tgtMesh.faceOwner()[facei];
label nbr = tgtMesh.faceNeighbour()[facei];
label subOwn = reverseCellMap[own];
label subNbr = reverseCellMap[nbr];
if (subOwn != -1 && subNbr == -1)
{
subFaces.append(tgtMesh.faces()[facei]);
subFaceOwner.append(subOwn);
subFaceNeighbour.append(subNbr);
subNbrProcIDs.append(-1);
subProcLocalFaceIDs.append(-1);
}
else if (subOwn == -1 && subNbr != -1)
{
subFaces.append(tgtMesh.faces()[facei].reverseFace());
subFaceOwner.append(subNbr);
subFaceNeighbour.append(subOwn);
subNbrProcIDs.append(-1);
subProcLocalFaceIDs.append(-1);
}
}
// boundary faces of existing region
forAll(tgtMesh.boundaryMesh(), patchi)
{
const polyPatch& pp = tgtMesh.boundaryMesh()[patchi];
label nbrProci = -1;
// store info for faces on processor patches
if (isA<processorPolyPatch>(pp))
{
const processorPolyPatch& ppp =
dynamic_cast<const processorPolyPatch&>(pp);
nbrProci = ppp.neighbProcNo();
}
forAll(pp, i)
{
label facei = pp.start() + i;
label own = tgtMesh.faceOwner()[facei];
if (reverseCellMap[own] != -1)
{
subFaces.append(tgtMesh.faces()[facei]);
subFaceOwner.append(reverseCellMap[own]);
subFaceNeighbour.append(-1);
subNbrProcIDs.append(nbrProci);
subProcLocalFaceIDs.append(i);
}
}
}
// reverse point map
labelList reversePointMap(tgtMesh.nPoints(), -1);
DynamicList<point> subPoints(tgtMesh.nPoints());
forAll(subFaces, subFacei)
{
face& f = subFaces[subFacei];
forAll(f, fp)
{
label pointi = f[fp];
if (reversePointMap[pointi] == -1)
{
reversePointMap[pointi] = subPoints.size();
subPoints.append(tgtMesh.points()[pointi]);
}
f[fp] = reversePointMap[pointi];
}
}
// tgt cells into global numbering
labelList globalElems(sendElems.size());
forAll(sendElems, i)
{
if (debug)
{
Pout<< "tgtProc:" << Pstream::myProcNo()
<< " sending tgt cell " << sendElems[i]
<< "[" << globalTgtCells.toGlobal(sendElems[i]) << "]"
<< " to srcProc " << domain << endl;
}
globalElems[i] = globalTgtCells.toGlobal(sendElems[i]);
}
// pass data
if (domain == Pstream::myProcNo())
{
// allocate my own data
points[Pstream::myProcNo()] = subPoints;
nInternalFaces[Pstream::myProcNo()] = nInternal;
faces[Pstream::myProcNo()] = subFaces;
faceOwner[Pstream::myProcNo()] = subFaceOwner;
faceNeighbour[Pstream::myProcNo()] = subFaceNeighbour;
globalCellIDs[Pstream::myProcNo()] = globalElems;
nbrProcIDs[Pstream::myProcNo()] = subNbrProcIDs;
procLocalFaceIDs[Pstream::myProcNo()] = subProcLocalFaceIDs;
}
else
{
// send data to other processor domains
UOPstream toDomain(domain, pBufs);
toDomain
<< subPoints
<< nInternal
<< subFaces
<< subFaceOwner
<< subFaceNeighbour
<< globalElems
<< subNbrProcIDs
<< subProcLocalFaceIDs;
}
}
}
// Start receiving
pBufs.finishedSends();
// Consume
for (label domain = 0; domain < Pstream::nProcs(); domain++)
{
const labelList& recvElems = map.constructMap()[domain];
if (domain != Pstream::myProcNo() && recvElems.size())
{
UIPstream str(domain, pBufs);
str >> points[domain]
>> nInternalFaces[domain]
>> faces[domain]
>> faceOwner[domain]
>> faceNeighbour[domain]
>> globalCellIDs[domain]
>> nbrProcIDs[domain]
>> procLocalFaceIDs[domain];
}
if (debug)
{
Pout<< "Target mesh send sizes[" << domain << "]"
<< ": points="<< points[domain].size()
<< ", faces=" << faces[domain].size()
<< ", nInternalFaces=" << nInternalFaces[domain]
<< ", faceOwn=" << faceOwner[domain].size()
<< ", faceNbr=" << faceNeighbour[domain].size()
<< ", globalCellIDs=" << globalCellIDs[domain].size() << endl;
}
}
}
void Foam::meshToMesh::distributeAndMergeCells
(
const distributionMap& map,
const polyMesh& tgt,
const globalIndex& globalTgtCells,
pointField& tgtPoints,
faceList& tgtFaces,
labelList& tgtFaceOwners,
labelList& tgtFaceNeighbours,
labelList& tgtGlobalCellIDs
) const
{
// Exchange per-processor data
List<pointField> allPoints;
List<label> allNInternalFaces;
List<faceList> allFaces;
List<labelList> allFaceOwners;
List<labelList> allFaceNeighbours;
List<labelList> allGlobalCellIDs;
// Per target mesh face the neighbouring proc and index in
// processor patch (all -1 for normal boundary face)
List<labelList> allNbrProcIDs;
List<labelList> allProcLocalFaceIDs;
distributeCells
(
map,
tgt,
globalTgtCells,
allPoints,
allNInternalFaces,
allFaces,
allFaceOwners,
allFaceNeighbours,
allGlobalCellIDs,
allNbrProcIDs,
allProcLocalFaceIDs
);
// Convert lists into format that can be used to generate a valid polyMesh
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Points and cells are collected into single flat lists:
// - i.e. proc0, proc1 ... procN
//
// Faces need to be sorted after collection to that internal faces are
// contiguous, followed by all boundary faces
//
// Processor patch faces between included cells on neighbouring processors
// are converted into internal faces
//
// Face list structure:
// - Per processor:
// - internal faces
// - processor faces that have been converted into internal faces
// - Followed by all boundary faces
// - from 'normal' boundary faces
// - from singularly-sided processor patch faces
// Number of internal+coupled faces
labelList allNIntCoupledFaces(allNInternalFaces);
// Starting offset for points
label nPoints = 0;
labelList pointOffset(Pstream::nProcs(), 0);
forAll(allPoints, proci)
{
pointOffset[proci] = nPoints;
nPoints += allPoints[proci].size();
}
// Starting offset for cells
label nCells = 0;
labelList cellOffset(Pstream::nProcs(), 0);
forAll(allGlobalCellIDs, proci)
{
cellOffset[proci] = nCells;
nCells += allGlobalCellIDs[proci].size();
}
// Count any coupled faces
typedef FixedList<label, 3> label3;
typedef HashTable<label, label3, label3::Hash<>> procCoupleInfo;
procCoupleInfo procFaceToGlobalCell;
forAll(allNbrProcIDs, proci)
{
const labelList& nbrProci = allNbrProcIDs[proci];
const labelList& localFacei = allProcLocalFaceIDs[proci];
forAll(nbrProci, i)
{
if (nbrProci[i] != -1 && localFacei[i] != -1)
{
label3 key;
key[0] = min(proci, nbrProci[i]);
key[1] = max(proci, nbrProci[i]);
key[2] = localFacei[i];
procCoupleInfo::const_iterator fnd =
procFaceToGlobalCell.find(key);
if (fnd == procFaceToGlobalCell.end())
{
procFaceToGlobalCell.insert(key, -1);
}
else
{
if (debug)
{
Pout<< "Additional internal face between procs:"
<< key[0] << " and " << key[1]
<< " across local face " << key[2] << endl;
}
allNIntCoupledFaces[proci]++;
}
}
}
}
// Starting offset for internal faces
label nIntFaces = 0;
label nFacesTotal = 0;
labelList internalFaceOffset(Pstream::nProcs(), 0);
forAll(allNIntCoupledFaces, proci)
{
label nCoupledFaces =
allNIntCoupledFaces[proci] - allNInternalFaces[proci];
internalFaceOffset[proci] = nIntFaces;
nIntFaces += allNIntCoupledFaces[proci];
nFacesTotal += allFaceOwners[proci].size() - nCoupledFaces;
}
tgtPoints.setSize(nPoints);
tgtFaces.setSize(nFacesTotal);
tgtFaceOwners.setSize(nFacesTotal);
tgtFaceNeighbours.setSize(nFacesTotal);
tgtGlobalCellIDs.setSize(nCells);
// Insert points
forAll(allPoints, proci)
{
const pointField& pts = allPoints[proci];
SubList<point>(tgtPoints, pts.size(), pointOffset[proci]) = pts;
}
// Insert cellIDs
forAll(allGlobalCellIDs, proci)
{
const labelList& cellIDs = allGlobalCellIDs[proci];
SubList<label>(tgtGlobalCellIDs, cellIDs.size(), cellOffset[proci]) =
cellIDs;
}
// Insert internal faces (from internal faces)
forAll(allFaces, proci)
{
const faceList& fcs = allFaces[proci];
const labelList& faceOs = allFaceOwners[proci];
const labelList& faceNs = allFaceNeighbours[proci];
SubList<face> slice
(
tgtFaces,
allNInternalFaces[proci],
internalFaceOffset[proci]
);
slice = SubList<face>(fcs, allNInternalFaces[proci]);
forAll(slice, i)
{
add(slice[i], pointOffset[proci]);
}
SubField<label> ownSlice
(
tgtFaceOwners,
allNInternalFaces[proci],
internalFaceOffset[proci]
);
ownSlice = SubField<label>(faceOs, allNInternalFaces[proci]);
add(ownSlice, cellOffset[proci]);
SubField<label> nbrSlice
(
tgtFaceNeighbours,
allNInternalFaces[proci],
internalFaceOffset[proci]
);
nbrSlice = SubField<label>(faceNs, allNInternalFaces[proci]);
add(nbrSlice, cellOffset[proci]);
internalFaceOffset[proci] += allNInternalFaces[proci];
}
// Insert internal faces (from coupled face-pairs)
forAll(allNbrProcIDs, proci)
{
const labelList& nbrProci = allNbrProcIDs[proci];
const labelList& localFacei = allProcLocalFaceIDs[proci];
const labelList& faceOs = allFaceOwners[proci];
const faceList& fcs = allFaces[proci];
forAll(nbrProci, i)
{
if (nbrProci[i] != -1 && localFacei[i] != -1)
{
label3 key;
key[0] = min(proci, nbrProci[i]);
key[1] = max(proci, nbrProci[i]);
key[2] = localFacei[i];
procCoupleInfo::iterator fnd = procFaceToGlobalCell.find(key);
if (fnd != procFaceToGlobalCell.end())
{
label tgtFacei = fnd();
if (tgtFacei == -1)
{
// on first visit store the new cell on this side
fnd() = cellOffset[proci] + faceOs[i];
}
else
{
// get owner and neighbour in new cell numbering
label newOwn = cellOffset[proci] + faceOs[i];
label newNbr = fnd();
label tgtFacei = internalFaceOffset[proci]++;
if (debug)
{
Pout<< " proc " << proci
<< "\tinserting face:" << tgtFacei
<< " connection between owner " << newOwn
<< " and neighbour " << newNbr
<< endl;
}
if (newOwn < newNbr)
{
// we have correct orientation
tgtFaces[tgtFacei] = fcs[i];
tgtFaceOwners[tgtFacei] = newOwn;
tgtFaceNeighbours[tgtFacei] = newNbr;
}
else
{
// reverse orientation
tgtFaces[tgtFacei] = fcs[i].reverseFace();
tgtFaceOwners[tgtFacei] = newNbr;
tgtFaceNeighbours[tgtFacei] = newOwn;
}
add(tgtFaces[tgtFacei], pointOffset[proci]);
// mark with unique value
fnd() = -2;
}
}
}
}
}
forAll(allNbrProcIDs, proci)
{
const labelList& nbrProci = allNbrProcIDs[proci];
const labelList& localFacei = allProcLocalFaceIDs[proci];
const labelList& faceOs = allFaceOwners[proci];
const labelList& faceNs = allFaceNeighbours[proci];
const faceList& fcs = allFaces[proci];
forAll(nbrProci, i)
{
// coupled boundary face
if (nbrProci[i] != -1 && localFacei[i] != -1)
{
label3 key;
key[0] = min(proci, nbrProci[i]);
key[1] = max(proci, nbrProci[i]);
key[2] = localFacei[i];
label tgtFacei = procFaceToGlobalCell[key];
if (tgtFacei == -1)
{
FatalErrorInFunction
<< "Unvisited " << key
<< abort(FatalError);
}
else if (tgtFacei != -2)
{
label newOwn = cellOffset[proci] + faceOs[i];
label tgtFacei = nIntFaces++;
if (debug)
{
Pout<< " proc " << proci
<< "\tinserting boundary face:" << tgtFacei
<< " from coupled face " << key
<< endl;
}
tgtFaces[tgtFacei] = fcs[i];
add(tgtFaces[tgtFacei], pointOffset[proci]);
tgtFaceOwners[tgtFacei] = newOwn;
tgtFaceNeighbours[tgtFacei] = -1;
}
}
// normal boundary face
else
{
label own = faceOs[i];
label nbr = faceNs[i];
if ((own != -1) && (nbr == -1))
{
label newOwn = cellOffset[proci] + faceOs[i];
label tgtFacei = nIntFaces++;
tgtFaces[tgtFacei] = fcs[i];
add(tgtFaces[tgtFacei], pointOffset[proci]);
tgtFaceOwners[tgtFacei] = newOwn;
tgtFaceNeighbours[tgtFacei] = -1;
}
}
}
}
}
// ************************************************************************* //

View File

@ -1,344 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2012-2022 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/>.
\*---------------------------------------------------------------------------*/
#include "meshToMesh.H"
#include "fvMesh.H"
#include "volFields.H"
#include "directFvPatchFieldMapper.H"
#include "calculatedFvPatchField.H"
#include "patchToPatchFvPatchFieldMapper.H"
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
template<class Type>
void Foam::meshToMesh::add
(
UList<Type>& fld,
const label offset
) const
{
forAll(fld, i)
{
fld[i] += offset;
}
}
template<class Type>
void Foam::meshToMesh::mapSrcToTgt
(
const UList<Type>& srcField,
List<Type>& result
) const
{
if (result.size() != tgtToSrcCellAddr_.size())
{
FatalErrorInFunction
<< "Supplied field size is not equal to target mesh size" << nl
<< " source mesh = " << srcToTgtCellAddr_.size() << nl
<< " target mesh = " << tgtToSrcCellAddr_.size() << nl
<< " supplied field = " << result.size()
<< abort(FatalError);
}
if (singleMeshProc_ == -1)
{
const distributionMap& map = srcMapPtr_();
List<Type> work(srcField);
map.distribute(work);
forAll(result, celli)
{
const labelList& srcAddress = tgtToSrcCellAddr_[celli];
const scalarList& srcWeight = tgtToSrcCellWght_[celli];
if (srcAddress.size())
{
result[celli] *= (1.0 - sum(srcWeight));
forAll(srcAddress, i)
{
result[celli] += srcWeight[i]*work[srcAddress[i]];
}
}
}
}
else
{
forAll(result, celli)
{
const labelList& srcAddress = tgtToSrcCellAddr_[celli];
const scalarList& srcWeight = tgtToSrcCellWght_[celli];
if (srcAddress.size())
{
result[celli] *= (1.0 - sum(srcWeight));
forAll(srcAddress, i)
{
result[celli] += srcWeight[i]*srcField[srcAddress[i]];
}
}
}
}
}
template<class Type>
Foam::tmp<Foam::Field<Type>> Foam::meshToMesh::mapSrcToTgt
(
const Field<Type>& srcField
) const
{
tmp<Field<Type>> tresult
(
new Field<Type>
(
tgtToSrcCellAddr_.size(),
Zero
)
);
mapSrcToTgt(srcField, tresult.ref());
return tresult;
}
template<class Type>
void Foam::meshToMesh::mapTgtToSrc
(
const UList<Type>& tgtField,
List<Type>& result
) const
{
if (result.size() != srcToTgtCellAddr_.size())
{
FatalErrorInFunction
<< "Supplied field size is not equal to source mesh size" << nl
<< " source mesh = " << srcToTgtCellAddr_.size() << nl
<< " target mesh = " << tgtToSrcCellAddr_.size() << nl
<< " supplied field = " << result.size()
<< abort(FatalError);
}
if (singleMeshProc_ == -1)
{
const distributionMap& map = tgtMapPtr_();
List<Type> work(tgtField);
map.distribute(work);
forAll(result, celli)
{
const labelList& tgtAddress = srcToTgtCellAddr_[celli];
const scalarList& tgtWeight = srcToTgtCellWght_[celli];
if (tgtAddress.size())
{
result[celli] *= (1.0 - sum(tgtWeight));
forAll(tgtAddress, i)
{
result[celli] += tgtWeight[i]*work[tgtAddress[i]];
}
}
}
}
else
{
forAll(result, celli)
{
const labelList& tgtAddress = srcToTgtCellAddr_[celli];
const scalarList& tgtWeight = srcToTgtCellWght_[celli];
if (tgtAddress.size())
{
result[celli] *= (1.0 - sum(tgtWeight));
forAll(tgtAddress, i)
{
result[celli] += tgtWeight[i]*tgtField[tgtAddress[i]];
}
}
}
}
}
template<class Type>
Foam::tmp<Foam::Field<Type>> Foam::meshToMesh::mapTgtToSrc
(
const Field<Type>& tgtField
) const
{
tmp<Field<Type>> tresult
(
new Field<Type>
(
srcToTgtCellAddr_.size(),
Zero
)
);
mapTgtToSrc(tgtField, tresult.ref());
return tresult;
}
template<class Type>
void Foam::meshToMesh::mapSrcToTgt
(
const GeometricField<Type, fvPatchField, volMesh>& field,
GeometricField<Type, fvPatchField, volMesh>& result
) const
{
mapSrcToTgt(field, result.primitiveFieldRef());
typename GeometricField<Type, fvPatchField, volMesh>::
Boundary& resultBf = result.boundaryFieldRef();
forAll(patchToPatches(), i)
{
label srcPatchi = srcPatchID_[i];
label tgtPatchi = tgtPatchID_[i];
const fvPatchField<Type>& srcField = field.boundaryField()[srcPatchi];
fvPatchField<Type>& tgtField = resultBf[tgtPatchi];
// Clone and map (since rmap does not do general mapping)
tmp<fvPatchField<Type>> tnewTgt
(
fvPatchField<Type>::New
(
srcField,
tgtField.patch(),
result(),
patchToPatchFvPatchFieldMapper(patchToPatches()[i], true)
)
);
// Transfer all mapped quantities (value and e.g. gradient) onto
// tgtField. Value will get overwritten below.
tgtField.rmap(tnewTgt(), identity(tgtField.size()));
}
forAll(cuttingPatches_, i)
{
label patchi = cuttingPatches_[i];
fvPatchField<Type>& pf = resultBf[patchi];
pf == pf.patchInternalField();
}
}
template<class Type>
Foam::tmp<Foam::GeometricField<Type, Foam::fvPatchField, Foam::volMesh>>
Foam::meshToMesh::mapSrcToTgt
(
const GeometricField<Type, fvPatchField, volMesh>& field
) const
{
typedef GeometricField<Type, fvPatchField, volMesh> fieldType;
const fvMesh& tgtMesh = static_cast<const fvMesh&>(tgtRegion_);
const fvBoundaryMesh& tgtBm = tgtMesh.boundary();
const typename fieldType::Boundary& srcBfld =
field.boundaryField();
PtrList<fvPatchField<Type>> tgtPatchFields(tgtBm.size());
// construct tgt boundary patch types as copy of 'field' boundary types
// note: this will provide place holders for fields with additional
// entries, but these values will need to be reset
forAll(tgtPatchID_, i)
{
label srcPatchi = srcPatchID_[i];
label tgtPatchi = tgtPatchID_[i];
if (!tgtPatchFields.set(tgtPatchi))
{
tgtPatchFields.set
(
tgtPatchi,
fvPatchField<Type>::New
(
srcBfld[srcPatchi],
tgtMesh.boundary()[tgtPatchi],
DimensionedField<Type, volMesh>::null(),
directFvPatchFieldMapper
(
labelList(tgtMesh.boundary()[tgtPatchi].size(), -1)
)
)
);
}
}
// Any unset tgtPatchFields become calculated
forAll(tgtPatchFields, tgtPatchi)
{
if (!tgtPatchFields.set(tgtPatchi))
{
// Note: use factory New method instead of direct generation of
// calculated so we keep constraints
tgtPatchFields.set
(
tgtPatchi,
fvPatchField<Type>::New
(
calculatedFvPatchField<Type>::typeName,
tgtMesh.boundary()[tgtPatchi],
DimensionedField<Type, volMesh>::null()
)
);
}
}
tmp<fieldType> tresult
(
new fieldType
(
IOobject
(
typedName("interpolate(" + field.name() + ")"),
tgtMesh.time().timeName(),
tgtMesh,
IOobject::NO_READ,
IOobject::NO_WRITE
),
tgtMesh,
field.dimensions(),
Field<Type>(tgtMesh.nCells(), Zero),
tgtPatchFields
)
);
mapSrcToTgt(field, tresult.ref());
return tresult;
}
// ************************************************************************* //

View File

@ -1,344 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2020 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/>.
\*---------------------------------------------------------------------------*/
#include "meshToMesh0.H"
#include "treeDataCell.H"
#include "treeDataFace.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::meshToMesh0::calcAddressing()
{
if (debug)
{
InfoInFunction
<< "Calculating mesh-to-mesh cell addressing" << endl;
}
// Set reference to cells
const cellList& fromCells = fromMesh_.cells();
const pointField& fromPoints = fromMesh_.points();
// In an attempt to preserve the efficiency of linear search and prevent
// failure, a RESCUE mechanism will be set up. Here, we shall mark all
// cells next to the solid boundaries. If such a cell is found as the
// closest, the relationship between the origin and cell will be examined.
// If the origin is outside the cell, a global n-squared search is
// triggered.
// SETTING UP RESCUE
// Visit all boundaries and mark the cell next to the boundary.
if (debug)
{
InfoInFunction << "Setting up rescue" << endl;
}
List<bool> boundaryCell(fromCells.size(), false);
// Set reference to boundary
const polyPatchList& patchesFrom = fromMesh_.boundaryMesh();
forAll(patchesFrom, patchi)
{
// Get reference to cells next to the boundary
const labelUList& bCells = patchesFrom[patchi].faceCells();
forAll(bCells, facei)
{
boundaryCell[bCells[facei]] = true;
}
}
treeBoundBox meshBb(fromPoints);
scalar typDim = meshBb.avgDim()/(2.0*cbrt(scalar(fromCells.size())));
treeBoundBox shiftedBb
(
meshBb.min(),
meshBb.max() + vector(typDim, typDim, typDim)
);
if (debug)
{
Info<< "\nMesh\n"
<< " bounding box : " << meshBb << nl
<< " bounding box (shifted) : " << shiftedBb << nl
<< " typical dimension :" << shiftedBb.typDim() << endl;
}
indexedOctree<treeDataCell> oc
(
treeDataCell(false, fromMesh_, polyMesh::CELL_TETS),
shiftedBb, // overall bounding box
8, // maxLevel
10, // leafsize
6.0 // duplicity
);
if (debug)
{
oc.print(Pout, false, 0);
}
cellAddresses
(
cellAddressing_,
toMesh_.cellCentres(),
fromMesh_,
boundaryCell,
oc
);
forAll(toMesh_.boundaryMesh(), patchi)
{
const polyPatch& toPatch = toMesh_.boundaryMesh()[patchi];
if (cuttingPatches_.found(toPatch.name()))
{
boundaryAddressing_[patchi].setSize(toPatch.size());
cellAddresses
(
boundaryAddressing_[patchi],
toPatch.faceCentres(),
fromMesh_,
boundaryCell,
oc
);
}
else if
(
patchMap_.found(toPatch.name())
&& fromMeshPatches_.found(patchMap_.find(toPatch.name())())
)
{
const polyPatch& fromPatch = fromMesh_.boundaryMesh()
[
fromMeshPatches_.find(patchMap_.find(toPatch.name())())()
];
if (fromPatch.empty())
{
WarningInFunction
<< "Source patch " << fromPatch.name()
<< " has no faces. Not performing mapping for it."
<< endl;
boundaryAddressing_[patchi].setSize(toPatch.size());
boundaryAddressing_[patchi] = -1;
}
else
{
treeBoundBox wallBb(fromPatch.localPoints());
scalar typDim =
wallBb.avgDim()/(2.0*sqrt(scalar(fromPatch.size())));
treeBoundBox shiftedBb
(
wallBb.min(),
wallBb.max() + vector(typDim, typDim, typDim)
);
// Note: allow more levels than in meshSearch. Assume patch
// is not as big as all boundary faces
indexedOctree<treeDataFace> oc
(
treeDataFace(false, fromPatch),
shiftedBb, // overall search domain
12, // maxLevel
10, // leafsize
6.0 // duplicity
);
const vectorField::subField centresToBoundary =
toPatch.faceCentres();
boundaryAddressing_[patchi].setSize(toPatch.size());
const scalar distSqr = sqr(wallBb.mag());
forAll(toPatch, toi)
{
boundaryAddressing_[patchi][toi] = oc.findNearest
(
centresToBoundary[toi],
distSqr
).index();
}
}
}
}
if (debug)
{
InfoInFunction
<< "Finished calculating mesh-to-mesh cell addressing" << endl;
}
}
void Foam::meshToMesh0::cellAddresses
(
labelList& cellAddressing_,
const pointField& points,
const fvMesh& fromMesh,
const List<bool>& boundaryCell,
const indexedOctree<treeDataCell>& oc
) const
{
// The implemented search method is a simple neighbour array search.
// It starts from a cell zero, searches its neighbours and finds one
// which is nearer to the target point than the current position.
// The location of the "current position" is reset to that cell and
// search through the neighbours continues. The search is finished
// when all the neighbours of the cell are farther from the target
// point than the current cell
// Set curCell label to zero (start)
label curCell = 0;
// Set reference to cell to cell addressing
const vectorField& centresFrom = fromMesh.cellCentres();
const labelListList& cc = fromMesh.cellCells();
forAll(points, toi)
{
// Pick up target position
const vector& p = points[toi];
// Set the sqr-distance
scalar distSqr = magSqr(p - centresFrom[curCell]);
bool closer;
do
{
closer = false;
// Set the current list of neighbouring cells
const labelList& neighbours = cc[curCell];
forAll(neighbours, ni)
{
const scalar curDistSqr =
magSqr(p - centresFrom[neighbours[ni]]);
// Search through all the neighbours.
// If the cell is closer, reset current cell and distance
if (curDistSqr < (1 - small)*distSqr)
{
curCell = neighbours[ni];
distSqr = curDistSqr;
closer = true; // A closer neighbour has been found
}
}
} while (closer);
cellAddressing_[toi] = -1;
// Check point is actually in the nearest cell
if (fromMesh.pointInCell(p, curCell))
{
cellAddressing_[toi] = curCell;
}
else
{
// If curCell is a boundary cell then the point maybe either outside
// the domain or in an other region of the doamin, either way use
// the octree search to find it.
if (boundaryCell[curCell])
{
cellAddressing_[toi] = oc.findInside(p);
if (cellAddressing_[toi] != -1)
{
curCell = cellAddressing_[toi];
}
}
else
{
// If not on the boundary search the neighbours
bool found = false;
// Set the current list of neighbouring cells
const labelList& neighbours = cc[curCell];
forAll(neighbours, ni)
{
// Search through all the neighbours.
// If point is in neighbour reset current cell
if (fromMesh.pointInCell(p, neighbours[ni]))
{
cellAddressing_[toi] = neighbours[ni];
found = true;
break;
}
}
if (!found)
{
// If still not found search the neighbour-neighbours
// Set the current list of neighbouring cells
const labelList& neighbours = cc[curCell];
forAll(neighbours, ni)
{
// Set the current list of neighbour-neighbouring cells
const labelList& nn = cc[neighbours[ni]];
forAll(nn, ni)
{
// Search through all the neighbours.
// If point is in neighbour reset current cell
if (fromMesh.pointInCell(p, nn[ni]))
{
cellAddressing_[toi] = nn[ni];
found = true;
break;
}
}
if (found) break;
}
}
if (!found)
{
// Still not found so use the octree
cellAddressing_[toi] = oc.findInside(p);
if (cellAddressing_[toi] != -1)
{
curCell = cellAddressing_[toi];
}
}
}
}
}
}
// ************************************************************************* //

View File

@ -1,271 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2018 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/>.
\*---------------------------------------------------------------------------*/
#include "meshToMesh0.H"
#include "tetOverlapVolume.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::meshToMesh0::calculateInverseDistanceWeights() const
{
if (debug)
{
InfoInFunction
<< "Calculating inverse distance weighting factors" << endl;
}
if (inverseDistanceWeightsPtr_)
{
FatalErrorInFunction
<< "weighting factors already calculated"
<< exit(FatalError);
}
//- Initialise overlap volume to zero
V_ = 0.0;
inverseDistanceWeightsPtr_ = new scalarListList(toMesh_.nCells());
scalarListList& invDistCoeffs = *inverseDistanceWeightsPtr_;
// get reference to source mesh data
const labelListList& cc = fromMesh_.cellCells();
const vectorField& centreFrom = fromMesh_.C();
const vectorField& centreTo = toMesh_.C();
forAll(cellAddressing_, celli)
{
if (cellAddressing_[celli] != -1)
{
const vector& target = centreTo[celli];
scalar m = mag(target - centreFrom[cellAddressing_[celli]]);
const labelList& neighbours = cc[cellAddressing_[celli]];
// if the nearest cell is a boundary cell or there is a direct hit,
// pick up the value
label directCelli = -1;
if (m < directHitTol || neighbours.empty())
{
directCelli = celli;
}
else
{
forAll(neighbours, ni)
{
scalar nm = mag(target - centreFrom[neighbours[ni]]);
if (nm < directHitTol)
{
directCelli = neighbours[ni];
break;
}
}
}
if (directCelli != -1)
{
// Direct hit
invDistCoeffs[directCelli].setSize(1);
invDistCoeffs[directCelli][0] = 1.0;
V_ += fromMesh_.V()[cellAddressing_[directCelli]];
}
else
{
invDistCoeffs[celli].setSize(neighbours.size() + 1);
// The first coefficient corresponds to the centre cell.
// The rest is ordered in the same way as the cellCells list.
scalar invDist = 1.0/m;
invDistCoeffs[celli][0] = invDist;
scalar sumInvDist = invDist;
// now add the neighbours
forAll(neighbours, ni)
{
invDist = 1.0/mag(target - centreFrom[neighbours[ni]]);
invDistCoeffs[celli][ni + 1] = invDist;
sumInvDist += invDist;
}
// divide by the total inverse-distance
forAll(invDistCoeffs[celli], i)
{
invDistCoeffs[celli][i] /= sumInvDist;
}
V_ +=
invDistCoeffs[celli][0]
*fromMesh_.V()[cellAddressing_[celli]];
for (label i = 1; i < invDistCoeffs[celli].size(); i++)
{
V_ +=
invDistCoeffs[celli][i]*fromMesh_.V()[neighbours[i-1]];
}
}
}
}
}
void Foam::meshToMesh0::calculateInverseVolumeWeights() const
{
if (debug)
{
InfoInFunction
<< "Calculating inverse volume weighting factors" << endl;
}
if (inverseVolumeWeightsPtr_)
{
FatalErrorInFunction
<< "weighting factors already calculated"
<< exit(FatalError);
}
//- Initialise overlap volume to zero
V_ = 0.0;
inverseVolumeWeightsPtr_ = new scalarListList(toMesh_.nCells());
scalarListList& invVolCoeffs = *inverseVolumeWeightsPtr_;
const labelListList& cellToCell = cellToCellAddressing();
tetOverlapVolume overlapEngine;
forAll(cellToCell, celli)
{
const labelList& overlapCells = cellToCell[celli];
if (overlapCells.size() > 0)
{
invVolCoeffs[celli].setSize(overlapCells.size());
forAll(overlapCells, j)
{
label cellFrom = overlapCells[j];
treeBoundBox bbFromMesh
(
pointField
(
fromMesh_.points(),
fromMesh_.cellPoints()[cellFrom]
)
);
scalar v = overlapEngine.cellCellOverlapVolumeMinDecomp
(
toMesh_,
celli,
fromMesh_,
cellFrom,
bbFromMesh
);
invVolCoeffs[celli][j] = v/toMesh_.V()[celli];
V_ += v;
}
}
}
}
void Foam::meshToMesh0::calculateCellToCellAddressing() const
{
if (debug)
{
InfoInFunction
<< "Calculating cell to cell addressing" << endl;
}
if (cellToCellAddressingPtr_)
{
FatalErrorInFunction
<< "addressing already calculated"
<< exit(FatalError);
}
//- Initialise overlap volume to zero
V_ = 0.0;
tetOverlapVolume overlapEngine;
cellToCellAddressingPtr_ = new labelListList(toMesh_.nCells());
labelListList& cellToCell = *cellToCellAddressingPtr_;
forAll(cellToCell, iTo)
{
const labelList overLapCells =
overlapEngine.overlappingCells(fromMesh_, toMesh_, iTo);
if (overLapCells.size() > 0)
{
cellToCell[iTo].setSize(overLapCells.size());
forAll(overLapCells, j)
{
cellToCell[iTo][j] = overLapCells[j];
V_ += fromMesh_.V()[overLapCells[j]];
}
}
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
const Foam::scalarListList& Foam::meshToMesh0::inverseDistanceWeights() const
{
if (!inverseDistanceWeightsPtr_)
{
calculateInverseDistanceWeights();
}
return *inverseDistanceWeightsPtr_;
}
const Foam::scalarListList& Foam::meshToMesh0::inverseVolumeWeights() const
{
if (!inverseVolumeWeightsPtr_)
{
calculateInverseVolumeWeights();
}
return *inverseVolumeWeightsPtr_;
}
const Foam::labelListList& Foam::meshToMesh0::cellToCellAddressing() const
{
if (!cellToCellAddressingPtr_)
{
calculateCellToCellAddressing();
}
return *cellToCellAddressingPtr_;
}
// ************************************************************************* //

View File

@ -1,202 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2018 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/>.
\*---------------------------------------------------------------------------*/
#include "meshToMesh0.H"
#include "processorFvPatch.H"
#include "demandDrivenData.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(meshToMesh0, 0);
}
const Foam::scalar Foam::meshToMesh0::directHitTol = 1e-5;
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::meshToMesh0::meshToMesh0
(
const fvMesh& meshFrom,
const fvMesh& meshTo,
const HashTable<word>& patchMap,
const wordList& cuttingPatchNames
)
:
fromMesh_(meshFrom),
toMesh_(meshTo),
patchMap_(patchMap),
cellAddressing_(toMesh_.nCells()),
boundaryAddressing_(toMesh_.boundaryMesh().size()),
inverseDistanceWeightsPtr_(nullptr),
inverseVolumeWeightsPtr_(nullptr),
cellToCellAddressingPtr_(nullptr),
V_(0.0)
{
forAll(fromMesh_.boundaryMesh(), patchi)
{
fromMeshPatches_.insert
(
fromMesh_.boundaryMesh()[patchi].name(),
patchi
);
}
forAll(toMesh_.boundaryMesh(), patchi)
{
toMeshPatches_.insert
(
toMesh_.boundaryMesh()[patchi].name(),
patchi
);
}
forAll(cuttingPatchNames, i)
{
if (toMeshPatches_.found(cuttingPatchNames[i]))
{
cuttingPatches_.insert
(
cuttingPatchNames[i],
toMeshPatches_.find(cuttingPatchNames[i])()
);
}
else
{
WarningInFunction
<< "Cannot find cutting-patch " << cuttingPatchNames[i]
<< " in destination mesh" << endl;
}
}
forAll(toMesh_.boundaryMesh(), patchi)
{
// Add the processor patches in the toMesh to the cuttingPatches list
if (isA<processorPolyPatch>(toMesh_.boundaryMesh()[patchi]))
{
cuttingPatches_.insert
(
toMesh_.boundaryMesh()[patchi].name(),
patchi
);
}
}
calcAddressing();
}
Foam::meshToMesh0::meshToMesh0
(
const fvMesh& meshFrom,
const fvMesh& meshTo
)
:
fromMesh_(meshFrom),
toMesh_(meshTo),
cellAddressing_(toMesh_.nCells()),
boundaryAddressing_(toMesh_.boundaryMesh().size()),
inverseDistanceWeightsPtr_(nullptr),
inverseVolumeWeightsPtr_(nullptr),
cellToCellAddressingPtr_(nullptr),
V_(0.0)
{
// check whether both meshes have got the same number
// of boundary patches
if (fromMesh_.boundary().size() != toMesh_.boundary().size())
{
FatalErrorInFunction
<< "Incompatible meshes: different number of patches, "
<< "fromMesh = " << fromMesh_.boundary().size()
<< ", toMesh = " << toMesh_.boundary().size()
<< exit(FatalError);
}
forAll(fromMesh_.boundaryMesh(), patchi)
{
if
(
fromMesh_.boundaryMesh()[patchi].name()
!= toMesh_.boundaryMesh()[patchi].name()
)
{
FatalErrorInFunction
<< "Incompatible meshes: different patch names for patch "
<< patchi
<< ", fromMesh = " << fromMesh_.boundary()[patchi].name()
<< ", toMesh = " << toMesh_.boundary()[patchi].name()
<< exit(FatalError);
}
if
(
fromMesh_.boundaryMesh()[patchi].type()
!= toMesh_.boundaryMesh()[patchi].type()
)
{
FatalErrorInFunction
<< "Incompatible meshes: different patch types for patch "
<< patchi
<< ", fromMesh = " << fromMesh_.boundary()[patchi].type()
<< ", toMesh = " << toMesh_.boundary()[patchi].type()
<< exit(FatalError);
}
fromMeshPatches_.insert
(
fromMesh_.boundaryMesh()[patchi].name(),
patchi
);
toMeshPatches_.insert
(
toMesh_.boundaryMesh()[patchi].name(),
patchi
);
patchMap_.insert
(
toMesh_.boundaryMesh()[patchi].name(),
fromMesh_.boundaryMesh()[patchi].name()
);
}
calcAddressing();
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::meshToMesh0::~meshToMesh0()
{
deleteDemandDrivenData(inverseDistanceWeightsPtr_);
deleteDemandDrivenData(inverseVolumeWeightsPtr_);
deleteDemandDrivenData(cellToCellAddressingPtr_);
}
// ************************************************************************* //

View File

@ -4,11 +4,14 @@ cd ${0%/*} || exit 1 # Run from this directory
# Source tutorial run functions # Source tutorial run functions
. $WM_PROJECT_DIR/bin/tools/CleanFunctions . $WM_PROJECT_DIR/bin/tools/CleanFunctions
cd hopperInitialState (
cleanCase cd hopperInitialState || exit 1
cleanCase
)
cd ../hopperEmptying (
cleanCase cd hopperEmptying || exit 1
rm -rf 0 cleanCase
)
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------

View File

@ -4,20 +4,21 @@ cd ${0%/*} || exit 1 # Run from this directory
# Source tutorial run functions # Source tutorial run functions
. $WM_PROJECT_DIR/bin/tools/RunFunctions . $WM_PROJECT_DIR/bin/tools/RunFunctions
cd hopperInitialState (
runApplication blockMesh cd hopperInitialState || exit 1
runApplication decomposePar runApplication blockMesh
runParallel $(getApplication) runApplication decomposePar
runApplication reconstructPar -latestTime runParallel $(getApplication)
cd .. runApplication reconstructPar
)
cd hopperEmptying (
rm -rf 0 cd hopperEmptying || exit 1
cp -R 0.orig 0 runApplication blockMesh
runApplication blockMesh runApplication decomposePar
runApplication mapFields ../hopperInitialState -sourceTime latestTime runParallel mapFieldsPar ../hopperInitialState -sourceTime latestTime
runApplication decomposePar runParallel $(getApplication)
runParallel $(getApplication) runApplication reconstructPar
runApplication reconstructPar )
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------