/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2013 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 . \*---------------------------------------------------------------------------*/ #include "motionSmootherAlgo.H" #include "twoDPointCorrector.H" #include "faceSet.H" #include "pointSet.H" #include "fixedValuePointPatchFields.H" #include "pointConstraints.H" #include "syncTools.H" #include "meshTools.H" #include "OFstream.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(motionSmootherAlgo, 0); } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::motionSmootherAlgo::testSyncPositions ( const pointField& fld, const scalar maxMag ) const { pointField syncedFld(fld); syncTools::syncPointPositions ( mesh_, syncedFld, minEqOp(), // combine op point(GREAT,GREAT,GREAT) // null ); forAll(syncedFld, i) { if (mag(syncedFld[i] - fld[i]) > maxMag) { FatalErrorIn ( "motionSmootherAlgo::testSyncPositions" "(" "const pointField&, " "const scalar" ")" ) << "On point " << i << " point:" << fld[i] << " synchronised point:" << syncedFld[i] << abort(FatalError); } } } void Foam::motionSmootherAlgo::checkFld(const pointScalarField& fld) { forAll(fld, pointI) { const scalar val = fld[pointI]; if ((val > -GREAT) && (val < GREAT)) {} else { FatalErrorIn ( "motionSmootherAlgo::checkFld" "(const pointScalarField&)" ) << "Problem : point:" << pointI << " value:" << val << abort(FatalError); } } } Foam::labelHashSet Foam::motionSmootherAlgo::getPoints ( const labelHashSet& faceLabels ) const { labelHashSet usedPoints(mesh_.nPoints()/100); forAllConstIter(labelHashSet, faceLabels, iter) { const face& f = mesh_.faces()[iter.key()]; forAll(f, fp) { usedPoints.insert(f[fp]); } } return usedPoints; } Foam::tmp Foam::motionSmootherAlgo::calcEdgeWeights ( const pointField& points ) const { const edgeList& edges = mesh_.edges(); tmp twght(new scalarField(edges.size())); scalarField& wght = twght(); forAll(edges, edgeI) { wght[edgeI] = 1.0/(edges[edgeI].mag(points)+SMALL); } return twght; } // Smooth on selected points (usually patch points) void Foam::motionSmootherAlgo::minSmooth ( const scalarField& edgeWeights, const PackedBoolList& isAffectedPoint, const labelList& meshPoints, const pointScalarField& fld, pointScalarField& newFld ) const { tmp tavgFld = avg ( fld, edgeWeights //scalarField(mesh_.nEdges(), 1.0) // uniform weighting ); const pointScalarField& avgFld = tavgFld(); forAll(meshPoints, i) { label pointI = meshPoints[i]; if (isAffectedPoint.get(pointI) == 1) { newFld[pointI] = min ( fld[pointI], 0.5*fld[pointI] + 0.5*avgFld[pointI] ); } } // Single and multi-patch constraints pointConstraints::New(mesh()).constrain(newFld, false); } // Smooth on all internal points void Foam::motionSmootherAlgo::minSmooth ( const scalarField& edgeWeights, const PackedBoolList& isAffectedPoint, const pointScalarField& fld, pointScalarField& newFld ) const { tmp tavgFld = avg ( fld, edgeWeights //scalarField(mesh_.nEdges(), 1.0) // uniform weighting ); const pointScalarField& avgFld = tavgFld(); forAll(fld, pointI) { if (isAffectedPoint.get(pointI) == 1 && isInternalPoint(pointI)) { newFld[pointI] = min ( fld[pointI], 0.5*fld[pointI] + 0.5*avgFld[pointI] ); } } // Single and multi-patch constraints pointConstraints::New(mesh()).constrain(newFld, false); } // Scale on all internal points void Foam::motionSmootherAlgo::scaleField ( const labelHashSet& pointLabels, const scalar scale, pointScalarField& fld ) const { forAllConstIter(labelHashSet, pointLabels, iter) { if (isInternalPoint(iter.key())) { fld[iter.key()] *= scale; } } // Single and multi-patch constraints pointConstraints::New(mesh()).constrain(fld, false); } // Scale on selected points (usually patch points) void Foam::motionSmootherAlgo::scaleField ( const labelList& meshPoints, const labelHashSet& pointLabels, const scalar scale, pointScalarField& fld ) const { forAll(meshPoints, i) { label pointI = meshPoints[i]; if (pointLabels.found(pointI)) { fld[pointI] *= scale; } } } // Lower on internal points void Foam::motionSmootherAlgo::subtractField ( const labelHashSet& pointLabels, const scalar f, pointScalarField& fld ) const { forAllConstIter(labelHashSet, pointLabels, iter) { if (isInternalPoint(iter.key())) { fld[iter.key()] = max(0.0, fld[iter.key()]-f); } } // Single and multi-patch constraints pointConstraints::New(mesh()).constrain(fld); } // Scale on selected points (usually patch points) void Foam::motionSmootherAlgo::subtractField ( const labelList& meshPoints, const labelHashSet& pointLabels, const scalar f, pointScalarField& fld ) const { forAll(meshPoints, i) { label pointI = meshPoints[i]; if (pointLabels.found(pointI)) { fld[pointI] = max(0.0, fld[pointI]-f); } } } bool Foam::motionSmootherAlgo::isInternalPoint(const label pointI) const { return isInternalPoint_.get(pointI) == 1; } void Foam::motionSmootherAlgo::getAffectedFacesAndPoints ( const label nPointIter, const faceSet& wrongFaces, labelList& affectedFaces, PackedBoolList& isAffectedPoint ) const { isAffectedPoint.setSize(mesh_.nPoints()); isAffectedPoint = 0; faceSet nbrFaces(mesh_, "checkFaces", wrongFaces); // Find possible points influenced by nPointIter iterations of // scaling and smoothing by doing pointCellpoint walk. // Also update faces-to-be-checked to extend one layer beyond the points // that will get updated. for (label i = 0; i < nPointIter; i++) { pointSet nbrPoints(mesh_, "grownPoints", getPoints(nbrFaces.toc())); forAllConstIter(pointSet, nbrPoints, iter) { const labelList& pCells = mesh_.pointCells(iter.key()); forAll(pCells, pCellI) { const cell& cFaces = mesh_.cells()[pCells[pCellI]]; forAll(cFaces, cFaceI) { nbrFaces.insert(cFaces[cFaceI]); } } } nbrFaces.sync(mesh_); if (i == nPointIter - 2) { forAllConstIter(faceSet, nbrFaces, iter) { const face& f = mesh_.faces()[iter.key()]; forAll(f, fp) { isAffectedPoint.set(f[fp], 1); } } } } affectedFaces = nbrFaces.toc(); } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::motionSmootherAlgo::motionSmootherAlgo ( polyMesh& mesh, pointMesh& pMesh, indirectPrimitivePatch& pp, pointVectorField& displacement, pointScalarField& scale, pointField& oldPoints, const labelList& adaptPatchIDs, const dictionary& paramDict ) : mesh_(mesh), pMesh_(pMesh), pp_(pp), displacement_(displacement), scale_(scale), oldPoints_(oldPoints), adaptPatchIDs_(adaptPatchIDs), paramDict_(paramDict), isInternalPoint_(mesh_.nPoints(), 1) { updateMesh(); } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::motionSmootherAlgo::~motionSmootherAlgo() {} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // const Foam::polyMesh& Foam::motionSmootherAlgo::mesh() const { return mesh_; } const Foam::pointMesh& Foam::motionSmootherAlgo::pMesh() const { return pMesh_; } const Foam::indirectPrimitivePatch& Foam::motionSmootherAlgo::patch() const { return pp_; } const Foam::labelList& Foam::motionSmootherAlgo::adaptPatchIDs() const { return adaptPatchIDs_; } const Foam::dictionary& Foam::motionSmootherAlgo::paramDict() const { return paramDict_; } void Foam::motionSmootherAlgo::correct() { oldPoints_ = mesh_.points(); scale_ = 1.0; // No need to update twoDmotion corrector since only holds edge labels // which will remain the same as before. So unless the mesh was distorted // severely outside of motionSmootherAlgo there will be no need. } void Foam::motionSmootherAlgo::setDisplacementPatchFields ( const labelList& patchIDs, pointVectorField& displacement ) { // Adapt the fixedValue bc's (i.e. copy internal point data to // boundaryField for all affected patches) forAll(patchIDs, i) { label patchI = patchIDs[i]; displacement.boundaryField()[patchI] == displacement.boundaryField()[patchI].patchInternalField(); } // Make consistent with non-adapted bc's by evaluating those now and // resetting the displacement from the values. // Note that we're just doing a correctBoundaryConditions with // fixedValue bc's first. labelHashSet adaptPatchSet(patchIDs); const lduSchedule& patchSchedule = displacement.mesh().globalData(). patchSchedule(); forAll(patchSchedule, patchEvalI) { label patchI = patchSchedule[patchEvalI].patch; if (!adaptPatchSet.found(patchI)) { if (patchSchedule[patchEvalI].init) { displacement.boundaryField()[patchI] .initEvaluate(Pstream::scheduled); } else { displacement.boundaryField()[patchI] .evaluate(Pstream::scheduled); } } } // Multi-patch constraints pointConstraints::New(displacement.mesh()()).constrainCorners(displacement); // Adapt the fixedValue bc's (i.e. copy internal point data to // boundaryField for all affected patches) to take the changes caused // by multi-corner constraints into account. forAll(patchIDs, i) { label patchI = patchIDs[i]; displacement.boundaryField()[patchI] == displacement.boundaryField()[patchI].patchInternalField(); } } void Foam::motionSmootherAlgo::setDisplacementPatchFields() { setDisplacementPatchFields(adaptPatchIDs_, displacement_); } void Foam::motionSmootherAlgo::setDisplacement ( const labelList& patchIDs, const indirectPrimitivePatch& pp, pointField& patchDisp, pointVectorField& displacement ) { const polyMesh& mesh = displacement.mesh()(); // See comment in .H file about shared points. // We want to disallow effect of loose coupled points - we only // want to see effect of proper fixedValue boundary conditions const labelList& cppMeshPoints = mesh.globalData().coupledPatch().meshPoints(); forAll(cppMeshPoints, i) { displacement[cppMeshPoints[i]] = vector::zero; } const labelList& ppMeshPoints = pp.meshPoints(); // Set internal point data from displacement on combined patch points. forAll(ppMeshPoints, patchPointI) { displacement[ppMeshPoints[patchPointI]] = patchDisp[patchPointI]; } // Adapt the fixedValue bc's (i.e. copy internal point data to // boundaryField for all affected patches) setDisplacementPatchFields(patchIDs, displacement); if (debug) { OFstream str(mesh.db().path()/"changedPoints.obj"); label nVerts = 0; forAll(ppMeshPoints, patchPointI) { const vector& newDisp = displacement[ppMeshPoints[patchPointI]]; if (mag(newDisp-patchDisp[patchPointI]) > SMALL) { const point& pt = mesh.points()[ppMeshPoints[patchPointI]]; meshTools::writeOBJ(str, pt); nVerts++; //Pout<< "Point:" << pt // << " oldDisp:" << patchDisp[patchPointI] // << " newDisp:" << newDisp << endl; } } Pout<< "Written " << nVerts << " points that are changed to file " << str.name() << endl; } // Now reset input displacement forAll(ppMeshPoints, patchPointI) { patchDisp[patchPointI] = displacement[ppMeshPoints[patchPointI]]; } } void Foam::motionSmootherAlgo::setDisplacement(pointField& patchDisp) { setDisplacement(adaptPatchIDs_, pp_, patchDisp, displacement_); } // correctBoundaryConditions with fixedValue bc's first. void Foam::motionSmootherAlgo::correctBoundaryConditions ( pointVectorField& displacement ) const { labelHashSet adaptPatchSet(adaptPatchIDs_); const lduSchedule& patchSchedule = mesh_.globalData().patchSchedule(); // 1. evaluate on adaptPatches forAll(patchSchedule, patchEvalI) { label patchI = patchSchedule[patchEvalI].patch; if (adaptPatchSet.found(patchI)) { if (patchSchedule[patchEvalI].init) { displacement.boundaryField()[patchI] .initEvaluate(Pstream::blocking); } else { displacement.boundaryField()[patchI] .evaluate(Pstream::blocking); } } } // 2. evaluate on non-AdaptPatches forAll(patchSchedule, patchEvalI) { label patchI = patchSchedule[patchEvalI].patch; if (!adaptPatchSet.found(patchI)) { if (patchSchedule[patchEvalI].init) { displacement.boundaryField()[patchI] .initEvaluate(Pstream::blocking); } else { displacement.boundaryField()[patchI] .evaluate(Pstream::blocking); } } } // Multi-patch constraints pointConstraints::New(displacement.mesh()()).constrainCorners(displacement); // Correct for problems introduced by corner constraints syncTools::syncPointList ( mesh_, displacement, maxMagEqOp(), // combine op vector::zero // null value ); } void Foam::motionSmootherAlgo::modifyMotionPoints(pointField& newPoints) const { // Correct for 2-D motion const twoDPointCorrector& twoDCorrector = twoDPointCorrector::New(mesh_); if (twoDCorrector.required()) { Info<< "Correcting 2-D mesh motion"; if (mesh_.globalData().parallel()) { WarningIn("motionSmootherAlgo::modifyMotionPoints(pointField&)") << "2D mesh-motion probably not correct in parallel" << endl; } // We do not want to move 3D planes so project all points onto those const pointField& oldPoints = mesh_.points(); const edgeList& edges = mesh_.edges(); const labelList& neIndices = twoDCorrector.normalEdgeIndices(); const vector& pn = twoDCorrector.planeNormal(); forAll(neIndices, i) { const edge& e = edges[neIndices[i]]; point& pStart = newPoints[e.start()]; pStart += pn*(pn & (oldPoints[e.start()] - pStart)); point& pEnd = newPoints[e.end()]; pEnd += pn*(pn & (oldPoints[e.end()] - pEnd)); } // Correct tangentially twoDCorrector.correctPoints(newPoints); Info<< " ...done" << endl; } if (debug) { Pout<< "motionSmootherAlgo::modifyMotionPoints :" << " testing sync of newPoints." << endl; testSyncPositions(newPoints, 1e-6*mesh_.bounds().mag()); } } void Foam::motionSmootherAlgo::movePoints() { // Make sure to clear out tetPtIs since used in checks (sometimes, should // really check) mesh_.clearAdditionalGeom(); pp_.movePoints(mesh_.points()); } Foam::scalar Foam::motionSmootherAlgo::setErrorReduction ( const scalar errorReduction ) { scalar oldErrorReduction = readScalar(paramDict_.lookup("errorReduction")); paramDict_.remove("errorReduction"); paramDict_.add("errorReduction", errorReduction); return oldErrorReduction; } bool Foam::motionSmootherAlgo::scaleMesh ( labelList& checkFaces, const bool smoothMesh, const label nAllowableErrors ) { List emptyBaffles; return scaleMesh ( checkFaces, emptyBaffles, smoothMesh, nAllowableErrors ); } bool Foam::motionSmootherAlgo::scaleMesh ( labelList& checkFaces, const List& baffles, const bool smoothMesh, const label nAllowableErrors ) { return scaleMesh ( checkFaces, baffles, paramDict_, paramDict_, smoothMesh, nAllowableErrors ); } Foam::tmp Foam::motionSmootherAlgo::curPoints() const { // Set newPoints as old + scale*displacement // Create overall displacement with same b.c.s as displacement_ wordList actualPatchTypes; { const pointBoundaryMesh& pbm = displacement_.mesh().boundary(); actualPatchTypes.setSize(pbm.size()); forAll(pbm, patchI) { actualPatchTypes[patchI] = pbm[patchI].type(); } } wordList actualPatchFieldTypes; { const pointVectorField::GeometricBoundaryField& pfld = displacement_.boundaryField(); actualPatchFieldTypes.setSize(pfld.size()); forAll(pfld, patchI) { if (isA >(pfld[patchI])) { // Get rid of funny actualPatchFieldTypes[patchI] = fixedValuePointPatchField::typeName; } else { actualPatchFieldTypes[patchI] = pfld[patchI].type(); } } } pointVectorField totalDisplacement ( IOobject ( "totalDisplacement", mesh_.time().timeName(), mesh_, IOobject::NO_READ, IOobject::NO_WRITE, false ), scale_*displacement_, actualPatchFieldTypes, actualPatchTypes ); correctBoundaryConditions(totalDisplacement); if (debug) { Pout<< "scaleMesh : testing sync of totalDisplacement" << endl; testSyncField ( totalDisplacement, maxMagEqOp(), vector::zero, // null value 1e-6*mesh_.bounds().mag() ); } tmp tnewPoints(oldPoints_ + totalDisplacement.internalField()); // Correct for 2-D motion modifyMotionPoints(tnewPoints()); return tnewPoints; } bool Foam::motionSmootherAlgo::scaleMesh ( labelList& checkFaces, const List& baffles, const dictionary& paramDict, const dictionary& meshQualityDict, const bool smoothMesh, const label nAllowableErrors ) { if (!smoothMesh && adaptPatchIDs_.empty()) { FatalErrorIn ( "motionSmootherAlgo::scaleMesh" "(" "labelList&, " "const List&, " "const dictionary&, " "const dictionary&, " "const bool, " "const label" ")" ) << "You specified both no movement on the internal mesh points" << " (smoothMesh = false)" << nl << "and no movement on the patch (adaptPatchIDs is empty)" << nl << "Hence nothing to adapt." << exit(FatalError); } if (debug) { // Had a problem with patches moved non-synced. Check transformations. const polyBoundaryMesh& patches = mesh_.boundaryMesh(); Pout<< "Entering scaleMesh : coupled patches:" << endl; forAll(patches, patchI) { if (patches[patchI].coupled()) { const coupledPolyPatch& pp = refCast(patches[patchI]); Pout<< '\t' << patchI << '\t' << pp.name() << " parallel:" << pp.parallel() << " separated:" << pp.separated() << " forwardT:" << pp.forwardT().size() << endl; } } } const scalar errorReduction = readScalar(paramDict.lookup("errorReduction")); const label nSmoothScale = readLabel(paramDict.lookup("nSmoothScale")); // Note: displacement_ should already be synced already from setDisplacement // but just to make sure. syncTools::syncPointList ( mesh_, displacement_, maxMagEqOp(), vector::zero // null value ); Info<< "Moving mesh using displacement scaling :" << " min:" << gMin(scale_.internalField()) << " max:" << gMax(scale_.internalField()) << endl; // Get points using current displacement and scale. Optionally 2D corrected. pointField newPoints(curPoints()); // Move. No need to do 2D correction since curPoints already done that. mesh_.movePoints(newPoints); movePoints(); // Check. Returns parallel number of incorrect faces. faceSet wrongFaces(mesh_, "wrongFaces", mesh_.nFaces()/100+100); checkMesh(false, mesh_, meshQualityDict, checkFaces, baffles, wrongFaces); if (returnReduce(wrongFaces.size(), sumOp