Regression in extractEulerianParticles
Summary
The extractEulerianParticles
function object reports a much larger number of droplets/particles than actually present.
Steps to reproduce
Run the attached small test case on master (commit e651d635).
Example case
Box domain, two rows of droplets (3 + 2) initialized, constant velocity in -z direction (adaptation of a single-droplet case by Sptizenberger et al., 2020).
What is the current bug behaviour?
The extractEulerianParticles
function object reports 160 particles instead of easily manually identifiable five. Consequently, the particle data in <time>/lagrangian/eulerianParticleCloud/*
is incorrect, where *
are files: coordinates d origId origProcId positions soi tag U.
What is the expected correct behavior?
The five identified particles with correct statistics in <time>/lagrangian/eulerianParticleCloud/*
.
e.g the volumes of the 5 particles are 5(5.01041e-05 5.05945e-05 5.01041e-05 5.14354e-05 5.14354e-05)
Relevant logs and/or images
in the log.interFoam
at the last timestep, the correct behaviour is:
extractEulerianParticles extractEulerianParticles output:
Collected particles : 5
Collected volume : 3.42031e-13
Discarded particles : 0
Discarded volume : 0
Particles in progress : 0
current behaviour is:
extractEulerianParticles extractEulerianParticles output:
Collected particles : 160
Collected volume : 3.42031e-13
Discarded particles : 0
Discarded volume : 0
Particles in progress : 0
Environment information
- OpenFOAM version : master (commit e651d635)
- Operating system : Ubuntu 22.04
- Compiler : gcc, Open MPI 4.1.2
Possible fixes
I tracked the issue down to a regression in regionSplit2D.C
where the parchEdgeFaceRegion
was replaced by the edgeTopoDistanceData
, more specifically in the call to
PatchEdgeFaceWave
<
indirectPrimitivePatch,
edgeTopoDistanceData<label>
>
(
mesh,
patch,
changedEdges,
changedRegions,
allEdgeInfo,
allFaceInfo,
returnReduce(patch.nEdges(), sumOp<label>())
);
which results in numerous member data updates, one of which is the following method call to update the face data
template
<
class PrimitivePatchType,
class Type,
class TrackingData
>
bool Foam::PatchEdgeFaceWave<PrimitivePatchType, Type, TrackingData>::
updateFace
(
const label facei,
const label neighbourEdgeI,
const Type& neighbourInfo,
Type& faceInfo
)
{
nEvals_++;
bool wasValid = faceInfo.valid(td_);
bool propagate =
faceInfo.updateFace
(
mesh_,
patch_,
facei,
neighbourEdgeI,
neighbourInfo,
propagationTol_,
td_
);
which, in turn, calls the templated type’s faceInfo.updateFace(...)
member.
The trouble is that parchEdgeFaceRegion
and edgeTopoDistanceData
implement different logic in the updateFace(...)
member.
In edgeTopoDistanceData
the region_
member is operated on:
template<class TrackingData>
inline bool Foam::patchEdgeFaceRegion::updateFace
(
const polyMesh& mesh,
const indirectPrimitivePatch& patch,
const label facei,
const label edgeI,
const patchEdgeFaceRegion& edgeInfo,
const scalar tol,
TrackingData& td
)
{
return update(edgeInfo, tol, td);
}
which calls
// Update this with w2 if w2 nearer to pt.
template<class TrackingData>
inline bool Foam::patchEdgeFaceRegion::update
(
const patchEdgeFaceRegion& w2,
const scalar tol,
TrackingData& td
)
{
if (!w2.valid(td))
{
FatalErrorInFunction
<< "problem." << abort(FatalError);
}
if (w2.region_ == -2 || region_ == -2)
{
// Blocked edge/face
return false;
}
if (!valid(td))
{
// current not yet set so use any value
operator=(w2);
return true;
}
else
{
if (w2.region_ < region_)
{
operator=(w2);
return true;
}
else
{
return false;
}
}
}
The equivalent of region_
in edgeTopoDistanceData
would be data_
, however the function
template<class Type, class PrimitivePatchType>
template<class TrackingData>
inline bool Foam::edgeTopoDistanceData<Type, PrimitivePatchType>::updateFace
(
const polyMesh& mesh,
const PrimitivePatchType& patch,
const label facei,
const label edgeI,
const edgeTopoDistanceData<Type, PrimitivePatchType>& edgeInfo,
const scalar tol,
TrackingData& td
)
{
// From edge to face
if (distance_ == -1)
{
this->operator=(edgeInfo);
return true;
}
return false;
}
only checks for distance_
. I suppose this is the logic needed in fluxSummary
, meshRefinementBaffles
and other places where edgeTopoDistanceData
is used. In contrast, patchEdgeFaceRegion
doesn’t seem to be used anywhere else. The regression removed the only usage of it (while not deleting it from the repository entirely) which might suggest that it was unintentional (?).
When I made the change in regionSplit2D
to once again use the patchEdgeFaceRegion
, the issue was resolved. I have a branch with these changes, which I'd be happy to share with you if given push rights to the repo.