openfoam/applications/utilities/mesh/generation/foamyMesh/cellSizeAndAlignmentGrid/cellSizeAndAlignmentGrid.C
Mark Olesen 3e43edf056 ENH: unify use of dictionary method names
- previously introduced `getOrDefault` as a dictionary _get_ method,
  now complete the transition and use it everywhere instead of
  `lookupOrDefault`. This avoids mixed usage of the two methods that
  are identical in behaviour, makes for shorter names, and promotes
  the distinction between "lookup" access (ie, return a token stream,
  locate and return an entry) and "get" access (ie, the above with
  conversion to concrete types such as scalar, label etc).
2020-06-02 17:26:03 +02:00

717 lines
18 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2012-2016 OpenFOAM Foundation
Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
Application
Test-distributedDelaunayMesh
Description
\*---------------------------------------------------------------------------*/
#include "CGALTriangulation3DKernel.H"
#include "indexedVertex.H"
#include "indexedCell.H"
#include "argList.H"
#include "Time.H"
#include "DistributedDelaunayMesh.H"
#include "backgroundMeshDecomposition.H"
#include "searchableSurfaces.H"
#include "conformationSurfaces.H"
#include "PrintTable.H"
#include "Random.H"
#include "boundBox.H"
#include "point.H"
#include "cellShapeControlMesh.H"
#include "triadField.H"
#include "scalarIOField.H"
#include "pointIOField.H"
#include "triadIOField.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
template<class Triangulation, class Type>
Foam::tmp<Foam::Field<Type>> filterFarPoints
(
const Triangulation& mesh,
const Field<Type>& field
)
{
tmp<Field<Type>> tNewField(new Field<Type>(field.size()));
Field<Type>& newField = tNewField.ref();
label added = 0;
label count = 0;
for
(
typename Triangulation::Finite_vertices_iterator vit =
mesh.finite_vertices_begin();
vit != mesh.finite_vertices_end();
++vit
)
{
if (vit->real())
{
newField[added++] = field[count];
}
count++;
}
newField.resize(added);
return tNewField;
}
template<class T>
autoPtr<mapDistribute> buildMap
(
const T& mesh,
labelListList& pointPoints
)
{
pointPoints.setSize(mesh.vertexCount());
globalIndex globalIndexing(mesh.vertexCount());
for
(
typename T::Finite_vertices_iterator vit = mesh.finite_vertices_begin();
vit != mesh.finite_vertices_end();
++vit
)
{
if (!vit->real())
{
continue;
}
std::list<typename T::Vertex_handle> adjVerts;
mesh.finite_adjacent_vertices(vit, std::back_inserter(adjVerts));
DynamicList<label> indices(adjVerts.size());
for
(
typename std::list<typename T::Vertex_handle>::const_iterator
adjVertI = adjVerts.begin();
adjVertI != adjVerts.end();
++adjVertI
)
{
typename T::Vertex_handle vh = *adjVertI;
if (!vh->farPoint())
{
indices.append
(
globalIndexing.toGlobal(vh->procIndex(), vh->index())
);
}
}
pointPoints[vit->index()].transfer(indices);
}
List<Map<label>> compactMap;
return autoPtr<mapDistribute>::New
(
globalIndexing,
pointPoints,
compactMap
);
}
template<class T>
Foam::tmp<Foam::triadField> buildAlignmentField(const T& mesh)
{
tmp<triadField> tAlignments
(
new triadField(mesh.vertexCount(), triad::unset)
);
triadField& alignments = tAlignments.ref();
for
(
typename T::Finite_vertices_iterator vit = mesh.finite_vertices_begin();
vit != mesh.finite_vertices_end();
++vit
)
{
if (!vit->real())
{
continue;
}
alignments[vit->index()] = vit->alignment();
}
return tAlignments;
}
template<class T>
Foam::tmp<Foam::pointField> buildPointField(const T& mesh)
{
tmp<pointField> tPoints
(
new pointField(mesh.vertexCount(), point(GREAT, GREAT, GREAT))
);
pointField& points = tPoints.ref();
for
(
typename T::Finite_vertices_iterator vit = mesh.finite_vertices_begin();
vit != mesh.finite_vertices_end();
++vit
)
{
if (!vit->real())
{
continue;
}
points[vit->index()] = topoint(vit->point());
}
return tPoints;
}
void refine
(
cellShapeControlMesh& mesh,
const conformationSurfaces& geometryToConformTo,
const label maxRefinementIterations,
const scalar defaultCellSize
)
{
for (label iter = 0; iter < maxRefinementIterations; ++iter)
{
DynamicList<point> ptsToInsert;
for
(
CellSizeDelaunay::Finite_cells_iterator cit =
mesh.finite_cells_begin();
cit != mesh.finite_cells_end();
++cit
)
{
const point newPoint =
topoint
(
CGAL::centroid
(
cit->vertex(0)->point(),
cit->vertex(1)->point(),
cit->vertex(2)->point(),
cit->vertex(3)->point()
)
);
if (geometryToConformTo.inside(newPoint))
{
ptsToInsert.append(newPoint);
}
}
Info<< " Adding " << returnReduce(ptsToInsert.size(), sumOp<label>())
<< endl;
forAll(ptsToInsert, ptI)
{
mesh.insert
(
ptsToInsert[ptI],
defaultCellSize,
triad::unset,
Vb::vtInternal
);
}
}
}
int main(int argc, char *argv[])
{
#include "setRootCase.H"
#include "createTime.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
label maxRefinementIterations = 2;
label maxSmoothingIterations = 200;
scalar minResidual = 0;
scalar defaultCellSize = 0.001;
scalar nearFeatDistSqrCoeff = 1e-8;
// Need to decouple vertex and cell type from this class?
// Vertex must have:
// + index
// + procIndex
// - type should be optional
cellShapeControlMesh mesh(runTime);
IOdictionary foamyHexMeshDict
(
IOobject
(
"foamyHexMeshDict",
runTime.system(),
runTime,
IOobject::MUST_READ,
IOobject::NO_WRITE
)
);
Random rndGen(64293*Pstream::myProcNo());
searchableSurfaces allGeometry
(
IOobject
(
"cvSearchableSurfaces",
runTime.constant(),
"triSurface",
runTime,
IOobject::MUST_READ,
IOobject::NO_WRITE
),
foamyHexMeshDict.subDict("geometry"),
foamyHexMeshDict.getOrDefault("singleRegionName", true)
);
conformationSurfaces geometryToConformTo
(
runTime,
rndGen,
allGeometry,
foamyHexMeshDict.subDict("surfaceConformation")
);
autoPtr<backgroundMeshDecomposition> bMesh;
if (Pstream::parRun())
{
bMesh.set
(
new backgroundMeshDecomposition
(
runTime,
rndGen,
geometryToConformTo,
foamyHexMeshDict.subDict("backgroundMeshDecomposition")
)
);
}
// Nice to have IO for the delaunay mesh
// IO depend on vertex type.
//
// Define a delaunay mesh as:
// + list of points of the triangulation
// + optionally a list of cells
Info<< nl << "Loop over surfaces" << endl;
forAll(geometryToConformTo.surfaces(), sI)
{
const label surfI = geometryToConformTo.surfaces()[sI];
const searchableSurface& surface =
geometryToConformTo.geometry()[surfI];
Info<< nl << "Inserting points from surface " << surface.name()
<< " (" << surface.type() << ")" << endl;
const tmp<pointField> tpoints(surface.points());
const pointField& points = tpoints();
Info<< " Number of points = " << points.size() << endl;
forAll(points, pI)
{
// Is the point in the extendedFeatureEdgeMesh? If so get the
// point normal, otherwise get the surface normal from
// searchableSurface
pointIndexHit info;
label infoFeature;
geometryToConformTo.findFeaturePointNearest
(
points[pI],
nearFeatDistSqrCoeff,
info,
infoFeature
);
autoPtr<triad> pointAlignment;
if (info.hit())
{
const extendedFeatureEdgeMesh& features =
geometryToConformTo.features()[infoFeature];
vectorField norms = features.featurePointNormals(info.index());
// Create a triad from these norms.
pointAlignment.set(new triad());
forAll(norms, nI)
{
pointAlignment() += norms[nI];
}
pointAlignment().normalize();
pointAlignment().orthogonalize();
}
else
{
geometryToConformTo.findEdgeNearest
(
points[pI],
nearFeatDistSqrCoeff,
info,
infoFeature
);
if (info.hit())
{
const extendedFeatureEdgeMesh& features =
geometryToConformTo.features()[infoFeature];
vectorField norms = features.edgeNormals(info.index());
// Create a triad from these norms.
pointAlignment.set(new triad());
forAll(norms, nI)
{
pointAlignment() += norms[nI];
}
pointAlignment().normalize();
pointAlignment().orthogonalize();
}
else
{
pointField ptField(1, points[pI]);
scalarField distField(1, nearFeatDistSqrCoeff);
List<pointIndexHit> infoList(1, pointIndexHit());
surface.findNearest(ptField, distField, infoList);
vectorField normals(1);
surface.getNormal(infoList, normals);
pointAlignment.set(new triad(normals[0]));
}
}
if (Pstream::parRun())
{
if (bMesh().positionOnThisProcessor(points[pI]))
{
CellSizeDelaunay::Vertex_handle vh = mesh.insert
(
points[pI],
defaultCellSize,
pointAlignment(),
Vb::vtInternalNearBoundary
);
}
}
else
{
CellSizeDelaunay::Vertex_handle vh = mesh.insert
(
points[pI],
defaultCellSize,
pointAlignment(),
Vb::vtInternalNearBoundary
);
}
}
}
// Refine the mesh
refine
(
mesh,
geometryToConformTo,
maxRefinementIterations,
defaultCellSize
);
if (Pstream::parRun())
{
mesh.distribute(bMesh);
}
labelListList pointPoints;
autoPtr<mapDistribute> meshDistributor = buildMap(mesh, pointPoints);
triadField alignments(buildAlignmentField(mesh));
pointField points(buildPointField(mesh));
mesh.printInfo(Info);
// Setup the sizes and alignments on each point
triadField fixedAlignments(mesh.vertexCount(), triad::unset);
for
(
CellSizeDelaunay::Finite_vertices_iterator vit =
mesh.finite_vertices_begin();
vit != mesh.finite_vertices_end();
++vit
)
{
if (vit->nearBoundary())
{
fixedAlignments[vit->index()] = vit->alignment();
}
}
Info<< nl << "Smoothing alignments" << endl;
for (label iter = 0; iter < maxSmoothingIterations; iter++)
{
Info<< "Iteration " << iter;
meshDistributor().distribute(points);
meshDistributor().distribute(alignments);
scalar residual = 0;
triadField triadAv(alignments.size(), triad::unset);
forAll(pointPoints, pI)
{
const labelList& pPoints = pointPoints[pI];
if (pPoints.empty())
{
continue;
}
const triad& oldTriad = alignments[pI];
triad& newTriad = triadAv[pI];
// Enforce the boundary conditions
const triad& fixedAlignment = fixedAlignments[pI];
forAll(pPoints, adjPointi)
{
const label adjPointIndex = pPoints[adjPointi];
scalar dist = mag(points[pI] - points[adjPointIndex]);
// dist = max(dist, SMALL);
triad tmpTriad = alignments[adjPointIndex];
for (direction dir = 0; dir < 3; dir++)
{
if (tmpTriad.set(dir))
{
tmpTriad[dir] *= (1.0/dist);
}
}
newTriad += tmpTriad;
}
newTriad.normalize();
newTriad.orthogonalize();
// newTriad = newTriad.sortxyz();
label nFixed = 0;
forAll(fixedAlignment, dirI)
{
if (fixedAlignment.set(dirI))
{
nFixed++;
}
}
if (nFixed == 1)
{
forAll(fixedAlignment, dirI)
{
if (fixedAlignment.set(dirI))
{
newTriad.align(fixedAlignment[dirI]);
}
}
}
else if (nFixed == 2)
{
forAll(fixedAlignment, dirI)
{
if (fixedAlignment.set(dirI))
{
newTriad[dirI] = fixedAlignment[dirI];
}
else
{
newTriad[dirI] = triad::unset[dirI];
}
}
newTriad.orthogonalize();
}
else if (nFixed == 3)
{
forAll(fixedAlignment, dirI)
{
if (fixedAlignment.set(dirI))
{
newTriad[dirI] = fixedAlignment[dirI];
}
}
}
for (direction dir = 0; dir < 3; ++dir)
{
if
(
newTriad.set(dir)
&& oldTriad.set(dir)
//&& !fixedAlignment.set(dir)
)
{
scalar dotProd = (oldTriad[dir] & newTriad[dir]);
scalar diff = mag(dotProd) - 1.0;
residual += mag(diff);
}
}
}
forAll(alignments, pI)
{
alignments[pI] = triadAv[pI].sortxyz();
}
reduce(residual, sumOp<scalar>());
Info<< ", Residual = " << residual << endl;
if (residual <= minResidual)
{
break;
}
}
// Write alignments to a .obj file
OFstream str(runTime.path()/"alignments.obj");
forAll(alignments, pI)
{
const triad& tri = alignments[pI];
if (tri.set())
{
forAll(tri, dirI)
{
meshTools::writeOBJ(str, points[pI], tri[dirI] + points[pI]);
}
}
}
// Remove the far points
pointIOField pointsIO
(
IOobject
(
"points",
runTime.constant(),
runTime,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
filterFarPoints(mesh, points)
);
scalarField sizes(points.size(), defaultCellSize);
scalarIOField sizesIO
(
IOobject
(
"sizes",
runTime.constant(),
runTime,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
filterFarPoints(mesh, sizes)
);
triadIOField alignmentsIO
(
IOobject
(
"alignments",
runTime.constant(),
runTime,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
filterFarPoints(mesh, alignments)
);
pointsIO.write();
sizesIO.write();
alignmentsIO.write();
Info<< nl;
runTime.printExecutionTime(Info);
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //