/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2015-2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see .
\*---------------------------------------------------------------------------*/
#include "shellSurfaces.H"
#include "searchableSurface.H"
#include "boundBox.H"
#include "triSurfaceMesh.H"
#include "refinementSurfaces.H"
#include "searchableSurfaces.H"
#include "orientedSurface.H"
#include "pointIndexHit.H"
#include "volumeType.H"
#include "distributedTriSurfaceMesh.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
const Foam::Enum
<
Foam::shellSurfaces::refineMode
>
Foam::shellSurfaces::refineModeNames_
({
{ refineMode::INSIDE, "inside" },
{ refineMode::OUTSIDE, "outside" },
{ refineMode::DISTANCE, "distance" },
});
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::shellSurfaces::setAndCheckLevels
(
const label shellI,
const List>& distLevels
)
{
const searchableSurface& shell = allGeometry_[shells_[shellI]];
if (modes_[shellI] != DISTANCE && distLevels.size() != 1)
{
FatalErrorInFunction
<< "For refinement mode "
<< refineModeNames_[modes_[shellI]]
<< " specify only one distance+level."
<< " (its distance gets discarded)"
<< exit(FatalError);
}
// Extract information into separate distance and level
distances_[shellI].setSize(distLevels.size());
levels_[shellI].setSize(distLevels.size());
forAll(distLevels, j)
{
distances_[shellI][j] = distLevels[j].first();
levels_[shellI][j] = distLevels[j].second();
if (levels_[shellI][j] < -1)
{
FatalErrorInFunction
<< "Shell " << shell.name()
<< " has illegal refinement level "
<< levels_[shellI][j]
<< exit(FatalError);
}
// Check in incremental order
if (j > 0)
{
if
(
(distances_[shellI][j] <= distances_[shellI][j-1])
|| (levels_[shellI][j] > levels_[shellI][j-1])
)
{
FatalErrorInFunction
<< "For refinement mode "
<< refineModeNames_[modes_[shellI]]
<< " : Refinement should be specified in order"
<< " of increasing distance"
<< " (and decreasing refinement level)." << endl
<< "Distance:" << distances_[shellI][j]
<< " refinementLevel:" << levels_[shellI][j]
<< exit(FatalError);
}
}
}
if (modes_[shellI] == DISTANCE)
{
if (!dryRun_)
{
Info<< "Refinement level according to distance to "
<< shell.name() << endl;
forAll(levels_[shellI], j)
{
Info<< " level " << levels_[shellI][j]
<< " for all cells within " << distances_[shellI][j]
<< " metre." << endl;
}
}
}
else
{
if (!shell.hasVolumeType())
{
FatalErrorInFunction
<< "Shell " << shell.name()
<< " does not support testing for "
<< refineModeNames_[modes_[shellI]] << endl
<< "Probably it is not closed."
<< exit(FatalError);
}
if (!dryRun_)
{
if (modes_[shellI] == INSIDE)
{
Info<< "Refinement level " << levels_[shellI][0]
<< " for all cells inside " << shell.name() << endl;
}
else
{
Info<< "Refinement level " << levels_[shellI][0]
<< " for all cells outside " << shell.name() << endl;
}
}
}
}
// Specifically orient triSurfaces using a calculated point outside.
// Done since quite often triSurfaces not of consistent orientation which
// is (currently) necessary for sideness calculation
void Foam::shellSurfaces::orient()
{
// Determine outside point.
boundBox overallBb;
bool hasSurface = false;
forAll(shells_, shellI)
{
const searchableSurface& s = allGeometry_[shells_[shellI]];
if (modes_[shellI] != DISTANCE && isA(s))
{
hasSurface = true;
const triSurfaceMesh& shell = refCast(s);
if (shell.triSurface::size())
{
// Assume surface is compact!
overallBb.add(s.bounds());
}
}
}
if (returnReduceOr(hasSurface))
{
const point outsidePt = overallBb.max() + overallBb.span();
//Info<< "Using point " << outsidePt << " to orient shells" << endl;
forAll(shells_, shellI)
{
const searchableSurface& s = allGeometry_[shells_[shellI]];
if (modes_[shellI] != DISTANCE && isA(s))
{
triSurfaceMesh& shell = const_cast
(
refCast(s)
);
bool anyFlipped = false;
if (!isA(s))
{
anyFlipped = orientedSurface::orient
(
shell,
outsidePt,
true
);
}
else
{
// TBD. - determine nearest with normal
// - override nearest only if better normal
// Make sure that normals are consistent. Does not change
// anything if surface is consistent.
orientedSurface::orientConsistent(shell);
List info;
vectorField normal;
labelList region;
s.findNearest
(
pointField(1, outsidePt),
scalarField(1, GREAT),
info,
normal,
region
);
if ((normal[0] & (info[0].point()-outsidePt)) > 0)
{
shell.flip();
anyFlipped = true;
}
}
if (anyFlipped && !dryRun_)
{
Info<< "shellSurfaces : Flipped orientation of surface "
<< s.name()
<< " so point " << outsidePt << " is outside." << endl;
}
}
}
}
}
// Find maximum level of a shell.
void Foam::shellSurfaces::findHigherLevel
(
const pointField& pt,
const label shellI,
labelList& maxLevel
) const
{
const labelList& levels = levels_[shellI];
if (modes_[shellI] == DISTANCE)
{
// Distance mode.
const scalarField& distances = distances_[shellI];
// Collect all those points that have a current maxLevel less than
// (any of) the shell. Also collect the furthest distance allowable
// to any shell with a higher level.
labelList candidateMap(pt.size());
scalarField candidateDistSqr(pt.size());
label candidateI = 0;
forAll(maxLevel, pointi)
{
forAllReverse(levels, levelI)
{
if (levels[levelI] > maxLevel[pointi])
{
candidateMap[candidateI] = pointi;
candidateDistSqr[candidateI] = sqr(distances[levelI]);
candidateI++;
break;
}
}
}
candidateMap.setSize(candidateI);
candidateDistSqr.setSize(candidateI);
// Do the expensive nearest test only for the candidate points.
List nearInfo;
allGeometry_[shells_[shellI]].findNearest
(
pointField(pt, candidateMap),
candidateDistSqr,
nearInfo
);
// Update maxLevel
forAll(nearInfo, i)
{
if (nearInfo[i].hit())
{
const label pointi = candidateMap[i];
// Check which level it actually is in.
const label minDistI = findLower
(
distances,
nearInfo[i].point().dist(pt[pointi])
);
// pt is inbetween shell[minDistI] and shell[minDistI+1]
maxLevel[pointi] = levels[minDistI+1];
}
}
}
else
{
// Inside/outside mode
// Collect all those points that have a current maxLevel less than the
// shell.
pointField candidates(pt.size());
labelList candidateMap(pt.size());
label candidateI = 0;
forAll(maxLevel, pointi)
{
if (levels[0] > maxLevel[pointi])
{
candidates[candidateI] = pt[pointi];
candidateMap[candidateI] = pointi;
candidateI++;
}
}
candidates.setSize(candidateI);
candidateMap.setSize(candidateI);
// Do the expensive nearest test only for the candidate points.
List volType;
allGeometry_[shells_[shellI]].getVolumeType(candidates, volType);
forAll(volType, i)
{
label pointi = candidateMap[i];
if
(
(
modes_[shellI] == INSIDE
&& volType[i] == volumeType::INSIDE
)
|| (
modes_[shellI] == OUTSIDE
&& volType[i] == volumeType::OUTSIDE
)
)
{
maxLevel[pointi] = levels[0];
}
}
}
}
void Foam::shellSurfaces::findHigherGapLevel
(
const pointField& pt,
const labelList& ptLevel,
const label shellI,
labelList& gapShell,
List>& gapInfo,
List& gapMode
) const
{
//TBD: hardcoded for region 0 information
const FixedList