diff --git a/src/postProcessing/functionObjects/field/Make/files b/src/postProcessing/functionObjects/field/Make/files index 1ea75cf3ffffd1db870f44c9fa74fdb58afd050f..ba19ff80a06e3d3c1aceb2b7ea0e0aee97ebc708 100644 --- a/src/postProcessing/functionObjects/field/Make/files +++ b/src/postProcessing/functionObjects/field/Make/files @@ -29,6 +29,11 @@ streamLine/streamLineParticle.C streamLine/streamLineParticleCloud.C streamLine/streamLineFunctionObject.C +wallBoundedStreamLine/wallBoundedStreamLine.C +wallBoundedStreamLine/wallBoundedStreamLineFunctionObject.C +wallBoundedStreamLine/wallBoundedStreamLineParticle.C +wallBoundedStreamLine/wallBoundedStreamLineParticleCloud.C + surfaceInterpolateFields/surfaceInterpolateFields.C surfaceInterpolateFields/surfaceInterpolateFieldsFunctionObject.C diff --git a/src/postProcessing/functionObjects/field/wallBoundedStreamLine/controlDict b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/controlDict new file mode 100644 index 0000000000000000000000000000000000000000..4a3f4fe29c407c9526ce20dc3bdd03eae0b0c9c2 --- /dev/null +++ b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/controlDict @@ -0,0 +1,144 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: 2.0.0 | +| \\ / A nd | Web: www.OpenFOAM.org | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object controlDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +application icoFoam; + +startFrom startTime; + +startTime 0; + +stopAt endTime; + +endTime 0.5; + +deltaT 0.005; + +writeControl timeStep; + +writeInterval 20; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 6; + +writeCompression off; + +timeFormat general; + +timePrecision 6; + +runTimeModifiable true; + +functions +{ + readFields + { + // Where to load it from (if not already in solver) + functionObjectLibs ("libfieldFunctionObjects.so"); + + type readFields; + fields (p U k); + } + + near + { + // Where to load it from + functionObjectLibs ("libfieldFunctionObjects.so"); + + type nearWallFields; + + // Output every + outputControl outputTime; + //outputInterval 1; + + // Fields to be sampled. Per field original name and mapped field to + // create. + fields + ( + (U UNear) + ); + + // Patches/groups to sample (regular expressions) + patches (motorBike); + + // Distance to sample + distance 0.001; + } + streamLines + { + // Where to load it from (if not already in solver) + functionObjectLibs ("libfieldFunctionObjects.so"); + type wallBoundedStreamLine; + + // Output every + outputControl timeStep; //outputTime; + // outputInterval 10; + + setFormat vtk; //gnuplot; //xmgr; //raw; //jplot; + + // Velocity field to use for tracking. + UName UNear; + + // Interpolation method. Default is cellPoint. See sampleDict. + //interpolationScheme pointMVC; + + // Tracked forwards (+U) or backwards (-U) + trackForward true; + + interpolationScheme cellPoint; + + // Names of fields to sample. Should contain above velocity field! + fields (p U k UNear); + + // Steps particles can travel before being removed + lifeTime 100; + + // Cloud name to use + cloudName particleTracks; + + // Seeding method. See the sampleSets in sampleDict. + seedSampleSet patchSeed; //cloud;//triSurfaceMeshPointSet; + + uniformCoeffs + { + type uniform; + axis x; //distance; + + start (0.0035 0.0999 0.0001); + end (0.0035 0.0999 0.0099); + nPoints 20; + } + + cloudCoeffs + { + type cloud; + axis x; //distance; + points ((0.351516548679288 -0.0116085375585099 1.24)); + } + patchSeedCoeffs + { + type patchSeed;//patchCloud; //cloud; //uniform; + patches (motorBike); + axis x; //distance; + maxPoints 20000; + } + } +} + + +// ************************************************************************* // diff --git a/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLine.C b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLine.C new file mode 100644 index 0000000000000000000000000000000000000000..4b6f6761f72e32790df85fa8ad0ee8dd98963496 --- /dev/null +++ b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLine.C @@ -0,0 +1,887 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2011-2012 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 "Pstream.H" +#include "functionObjectList.H" +#include "wallBoundedStreamLine.H" +#include "fvMesh.H" +#include "wallBoundedStreamLineParticleCloud.H" +#include "ReadFields.H" +#include "meshSearch.H" +#include "sampledSet.H" +#include "globalIndex.H" +#include "mapDistribute.H" +#include "interpolationCellPoint.H" +#include "PatchTools.H" +#include "meshSearchMeshObject.H" +#include "faceSet.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +defineTypeNameAndDebug(Foam::wallBoundedStreamLine, 0); + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::autoPtr<Foam::indirectPrimitivePatch> +Foam::wallBoundedStreamLine::wallPatch() const +{ + const fvMesh& mesh = dynamic_cast<const fvMesh&>(obr_); + + const polyBoundaryMesh& patches = mesh.boundaryMesh(); + + label nFaces = 0; + + forAll(patches, patchI) + { + //if (!polyPatch::constraintType(patches[patchI].type())) + if (isA<wallPolyPatch>(patches[patchI])) + { + nFaces += patches[patchI].size(); + } + } + + labelList addressing(nFaces); + + nFaces = 0; + + forAll(patches, patchI) + { + //if (!polyPatch::constraintType(patches[patchI].type())) + if (isA<wallPolyPatch>(patches[patchI])) + { + const polyPatch& pp = patches[patchI]; + + forAll(pp, i) + { + addressing[nFaces++] = pp.start()+i; + } + } + } + + return autoPtr<indirectPrimitivePatch> + ( + new indirectPrimitivePatch + ( + IndirectList<face> + ( + mesh.faces(), + addressing + ), + mesh.points() + ) + ); +} + + +Foam::tetIndices Foam::wallBoundedStreamLine::findNearestTet +( + const PackedBoolList& isWallPatch, + const point& seedPt, + const label cellI +) const +{ + const fvMesh& mesh = dynamic_cast<const fvMesh&>(obr_); + + const cell& cFaces = mesh.cells()[cellI]; + + label minFaceI = -1; + label minTetPtI = -1; + scalar minDistSqr = sqr(GREAT); + + forAll(cFaces, cFaceI) + { + label faceI = cFaces[cFaceI]; + + if (isWallPatch[faceI]) + { + const face& f = mesh.faces()[faceI]; + const label fp0 = mesh.tetBasePtIs()[faceI]; + const point& basePoint = mesh.points()[f[fp0]]; + + label fp = f.fcIndex(fp0); + for (label i = 2; i < f.size(); i++) + { + const point& thisPoint = mesh.points()[f[fp]]; + label nextFp = f.fcIndex(fp); + const point& nextPoint = mesh.points()[f[nextFp]]; + + const triPointRef tri(basePoint, thisPoint, nextPoint); + + scalar d2 = magSqr(tri.centre() - seedPt); + if (d2 < minDistSqr) + { + minDistSqr = d2; + minFaceI = faceI; + minTetPtI = i-1; + } + fp = nextFp; + } + } + } + + // Put particle in tet + return tetIndices + ( + cellI, + minFaceI, + minTetPtI, + mesh + ); +} + + +void Foam::wallBoundedStreamLine::track() +{ + const Time& runTime = obr_.time(); + const fvMesh& mesh = dynamic_cast<const fvMesh&>(obr_); + + + // Determine the 'wall' patches + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // These are the faces that need to be followed + + autoPtr<indirectPrimitivePatch> boundaryPatch(wallPatch()); + PackedBoolList isWallPatch(mesh.nFaces()); + forAll(boundaryPatch().addressing(), i) + { + isWallPatch[boundaryPatch().addressing()[i]] = 1; + } + + + + // Find nearest wall particle for the seedPoints + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + IDLList<wallBoundedStreamLineParticle> initialParticles; + wallBoundedStreamLineParticleCloud particles + ( + mesh, + cloudName_, + initialParticles + ); + + { + // Get the seed points + // ~~~~~~~~~~~~~~~~~~~ + + const sampledSet& seedPoints = sampledSetPtr_(); + + + forAll(seedPoints, i) + { + const point& seedPt = seedPoints[i]; + label cellI = seedPoints.cells()[i]; + + tetIndices ids(findNearestTet(isWallPatch, seedPt, cellI)); + + if (ids.face() != -1 && isWallPatch[ids.face()]) + { + //Pout<< "Seeding particle :" << nl + // << " seedPt:" << seedPt << nl + // << " face :" << ids.face() << nl + // << " at :" << mesh.faceCentres()[ids.face()] << nl + // << " cell :" << mesh.cellCentres()[ids.cell()] << nl + // << endl; + + particles.addParticle + ( + new wallBoundedStreamLineParticle + ( + mesh, + ids.faceTri(mesh).centre(), + ids.cell(), + ids.face(), // tetFace + ids.tetPt(), + -1, // not on a mesh edge + -1, // not on a diagonal edge + lifeTime_ // lifetime + ) + ); + } + else + { + Pout<< type() << " : ignoring seed " << seedPt + << " since not in wall cell." << endl; + } + } + } + + label nSeeds = returnReduce(particles.size(), sumOp<label>()); + + Info<< type() << " : seeded " << nSeeds << " particles." << endl; + + + + // Read or lookup fields + PtrList<volScalarField> vsFlds; + PtrList<interpolation<scalar> > vsInterp; + PtrList<volVectorField> vvFlds; + PtrList<interpolation<vector> > vvInterp; + + label UIndex = -1; + + if (loadFromFiles_) + { + IOobjectList allObjects(mesh, runTime.timeName()); + + IOobjectList objects(2*fields_.size()); + forAll(fields_, i) + { + objects.add(*allObjects[fields_[i]]); + } + + ReadFields(mesh, objects, vsFlds); + vsInterp.setSize(vsFlds.size()); + forAll(vsFlds, i) + { + vsInterp.set + ( + i, + interpolation<scalar>::New + ( + interpolationScheme_, + vsFlds[i] + ) + ); + } + ReadFields(mesh, objects, vvFlds); + vvInterp.setSize(vvFlds.size()); + forAll(vvFlds, i) + { + vvInterp.set + ( + i, + interpolation<vector>::New + ( + interpolationScheme_, + vvFlds[i] + ) + ); + } + } + else + { + label nScalar = 0; + label nVector = 0; + + forAll(fields_, i) + { + if (mesh.foundObject<volScalarField>(fields_[i])) + { + nScalar++; + } + else if (mesh.foundObject<volVectorField>(fields_[i])) + { + nVector++; + } + else + { + FatalErrorIn("wallBoundedStreamLine::execute()") + << "Cannot find field " << fields_[i] << endl + << "Valid scalar fields are:" + << mesh.names(volScalarField::typeName) << endl + << "Valid vector fields are:" + << mesh.names(volVectorField::typeName) + << exit(FatalError); + } + } + vsInterp.setSize(nScalar); + nScalar = 0; + vvInterp.setSize(nVector); + nVector = 0; + + forAll(fields_, i) + { + if (mesh.foundObject<volScalarField>(fields_[i])) + { + const volScalarField& f = mesh.lookupObject<volScalarField> + ( + fields_[i] + ); + vsInterp.set + ( + nScalar++, + interpolation<scalar>::New + ( + interpolationScheme_, + f + ) + ); + } + else if (mesh.foundObject<volVectorField>(fields_[i])) + { + const volVectorField& f = mesh.lookupObject<volVectorField> + ( + fields_[i] + ); + + if (f.name() == UName_) + { + UIndex = nVector; + } + + vvInterp.set + ( + nVector++, + interpolation<vector>::New + ( + interpolationScheme_, + f + ) + ); + } + } + } + + // Store the names + scalarNames_.setSize(vsInterp.size()); + forAll(vsInterp, i) + { + scalarNames_[i] = vsInterp[i].psi().name(); + } + vectorNames_.setSize(vvInterp.size()); + forAll(vvInterp, i) + { + vectorNames_[i] = vvInterp[i].psi().name(); + } + + // Check that we know the index of U in the interpolators. + + if (UIndex == -1) + { + FatalErrorIn("wallBoundedStreamLine::execute()") + << "Cannot find field to move particles with : " << UName_ + << endl + << "This field has to be present in the sampled fields " + << fields_ + << " and in the objectRegistry." << endl + << exit(FatalError); + } + + // Sampled data + // ~~~~~~~~~~~~ + + // Size to maximum expected sizes. + allTracks_.clear(); + allTracks_.setCapacity(nSeeds); + allScalars_.setSize(vsInterp.size()); + forAll(allScalars_, i) + { + allScalars_[i].clear(); + allScalars_[i].setCapacity(nSeeds); + } + allVectors_.setSize(vvInterp.size()); + forAll(allVectors_, i) + { + allVectors_[i].clear(); + allVectors_[i].setCapacity(nSeeds); + } + + + // additional particle info + wallBoundedStreamLineParticle::trackingData td + ( + particles, + vsInterp, + vvInterp, + UIndex, // index of U in vvInterp + trackForward_, // track in +u direction? + isWallPatch, // which faces are to follow + + allTracks_, + allScalars_, + allVectors_ + ); + + + // Set very large dt. Note: cannot use GREAT since 1/GREAT is SMALL + // which is a trigger value for the tracking... + const scalar trackTime = Foam::sqrt(GREAT); + + // Track + particles.move(td, trackTime); +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::wallBoundedStreamLine::wallBoundedStreamLine +( + const word& name, + const objectRegistry& obr, + const dictionary& dict, + const bool loadFromFiles +) +: + dict_(dict), + name_(name), + obr_(obr), + loadFromFiles_(loadFromFiles), + active_(true) +{ + // Only active if a fvMesh is available + if (isA<fvMesh>(obr_)) + { + read(dict_); + } + else + { + active_ = false; + WarningIn + ( + "wallBoundedStreamLine::wallBoundedStreamLine\n" + "(\n" + "const word&,\n" + "const objectRegistry&,\n" + "const dictionary&,\n" + "const bool\n" + ")" + ) << "No fvMesh available, deactivating." + << nl << endl; + } +} + + +// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // + +Foam::wallBoundedStreamLine::~wallBoundedStreamLine() +{} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::wallBoundedStreamLine::read(const dictionary& dict) +{ + if (active_) + { + //dict_ = dict; + dict.lookup("fields") >> fields_; + if (dict.found("UName")) + { + dict.lookup("UName") >> UName_; + } + else + { + UName_ = "U"; + if (dict.found("U")) + { + IOWarningIn + ( + "wallBoundedStreamLine::read(const dictionary&)", + dict + ) << "Using deprecated entry \"U\"." + << " Please use \"UName\" instead." + << endl; + dict.lookup("U") >> UName_; + } + } + + if (findIndex(fields_, UName_) == -1) + { + FatalIOErrorIn + ( + "wallBoundedStreamLine::read(const dictionary&)", + dict + ) << "Velocity field for tracking " << UName_ + << " should be present in the list of fields " << fields_ + << exit(FatalIOError); + } + + + dict.lookup("trackForward") >> trackForward_; + dict.lookup("lifeTime") >> lifeTime_; + if (lifeTime_ < 1) + { + FatalErrorIn(":wallBoundedStreamLine::read(const dictionary&)") + << "Illegal value " << lifeTime_ << " for lifeTime" + << exit(FatalError); + } + + + interpolationScheme_ = dict.lookupOrDefault + ( + "interpolationScheme", + interpolationCellPoint<scalar>::typeName + ); + + //Info<< typeName << " using interpolation " << interpolationScheme_ + // << endl; + + cloudName_ = dict.lookupOrDefault<word> + ( + "cloudName", + "wallBoundedStreamLine" + ); + dict.lookup("seedSampleSet") >> seedSet_; + + const fvMesh& mesh = dynamic_cast<const fvMesh&>(obr_); + + const dictionary& coeffsDict = dict.subDict(seedSet_ + "Coeffs"); + + sampledSetPtr_ = sampledSet::New + ( + seedSet_, + mesh, + meshSearchMeshObject::New(mesh), + coeffsDict + ); + coeffsDict.lookup("axis") >> sampledSetAxis_; + + scalarFormatterPtr_ = writer<scalar>::New(dict.lookup("setFormat")); + vectorFormatterPtr_ = writer<vector>::New(dict.lookup("setFormat")); + + + // Make sure that the mesh is trackable + if (debug) + { + // 1. positive volume decomposition tets + faceSet faces(mesh, "lowQualityTetFaces", mesh.nFaces()/100+1); + if + ( + polyMeshTetDecomposition::checkFaceTets + ( + mesh, + polyMeshTetDecomposition::minTetQuality, + true, + &faces + ) + ) + { + label nFaces = returnReduce(faces.size(), sumOp<label>()); + + FatalErrorIn("wallBoundedStreamLine::track()") + << "Found " << nFaces + <<" faces with low quality or negative volume " + << "decomposition tets. Writing to faceSet " << faces.name() + << exit(FatalError); + } + + // 2. all edges on a cell having two faces + EdgeMap<label> numFacesPerEdge; + forAll(mesh.cells(), cellI) + { + const cell& cFaces = mesh.cells()[cellI]; + + numFacesPerEdge.clear(); + + forAll(cFaces, cFaceI) + { + label faceI = cFaces[cFaceI]; + const face& f = mesh.faces()[faceI]; + forAll(f, fp) + { + const edge e(f[fp], f.nextLabel(fp)); + EdgeMap<label>::iterator eFnd = + numFacesPerEdge.find(e); + if (eFnd != numFacesPerEdge.end()) + { + eFnd()++; + } + else + { + numFacesPerEdge.insert(e, 1); + } + } + } + + forAllConstIter(EdgeMap<label>, numFacesPerEdge, iter) + { + if (iter() != 2) + { + FatalErrorIn + ( + "wallBoundedStreamLine::read(..)" + ) << "problem cell:" << cellI + << abort(FatalError); + } + } + } + } + } +} + + +void Foam::wallBoundedStreamLine::execute() +{} + + +void Foam::wallBoundedStreamLine::end() +{} + + +void Foam::wallBoundedStreamLine::write() +{ + if (active_) + { + const Time& runTime = obr_.time(); + const fvMesh& mesh = dynamic_cast<const fvMesh&>(obr_); + + + // Do all injection and tracking + track(); + + + if (Pstream::parRun()) + { + // Append slave tracks to master ones + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + globalIndex globalTrackIDs(allTracks_.size()); + + // Construct a distribution map to pull all to the master. + labelListList sendMap(Pstream::nProcs()); + labelListList recvMap(Pstream::nProcs()); + + if (Pstream::master()) + { + // Master: receive all. My own first, then consecutive + // processors. + label trackI = 0; + + forAll(recvMap, procI) + { + labelList& fromProc = recvMap[procI]; + fromProc.setSize(globalTrackIDs.localSize(procI)); + forAll(fromProc, i) + { + fromProc[i] = trackI++; + } + } + } + + labelList& toMaster = sendMap[0]; + toMaster.setSize(globalTrackIDs.localSize()); + forAll(toMaster, i) + { + toMaster[i] = i; + } + + const mapDistribute distMap + ( + globalTrackIDs.size(), + sendMap.xfer(), + recvMap.xfer() + ); + + + // Distribute the track positions. Note: use scheduled comms + // to prevent buffering. + mapDistribute::distribute + ( + Pstream::scheduled, + distMap.schedule(), + distMap.constructSize(), + distMap.subMap(), + distMap.constructMap(), + allTracks_ + ); + + // Distribute the scalars + forAll(allScalars_, scalarI) + { + mapDistribute::distribute + ( + Pstream::scheduled, + distMap.schedule(), + distMap.constructSize(), + distMap.subMap(), + distMap.constructMap(), + allScalars_[scalarI] + ); + } + // Distribute the vectors + forAll(allVectors_, vectorI) + { + mapDistribute::distribute + ( + Pstream::scheduled, + distMap.schedule(), + distMap.constructSize(), + distMap.subMap(), + distMap.constructMap(), + allVectors_[vectorI] + ); + } + } + + + label n = 0; + forAll(allTracks_, trackI) + { + n += allTracks_[trackI].size(); + } + + Info<< "Tracks:" << allTracks_.size() + << " total samples:" << n << endl; + + + // Massage into form suitable for writers + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + if (Pstream::master() && allTracks_.size()) + { + // Make output directory + + fileName vtkPath + ( + Pstream::parRun() + ? runTime.path()/".."/"sets"/name() + : runTime.path()/"sets"/name() + ); + if (mesh.name() != fvMesh::defaultRegion) + { + vtkPath = vtkPath/mesh.name(); + } + vtkPath = vtkPath/mesh.time().timeName(); + + mkDir(vtkPath); + + // Convert track positions + + PtrList<coordSet> tracks(allTracks_.size()); + forAll(allTracks_, trackI) + { + tracks.set + ( + trackI, + new coordSet + ( + "track" + Foam::name(trackI), + sampledSetAxis_ //"xyz" + ) + ); + tracks[trackI].transfer(allTracks_[trackI]); + } + + // Convert scalar values + + if (allScalars_.size() > 0) + { + List<List<scalarField> > scalarValues(allScalars_.size()); + + forAll(allScalars_, scalarI) + { + DynamicList<scalarList>& allTrackVals = + allScalars_[scalarI]; + scalarValues[scalarI].setSize(allTrackVals.size()); + + forAll(allTrackVals, trackI) + { + scalarList& trackVals = allTrackVals[trackI]; + scalarValues[scalarI][trackI].transfer(trackVals); + } + } + + fileName vtkFile + ( + vtkPath + / scalarFormatterPtr_().getFileName + ( + tracks[0], + scalarNames_ + ) + ); + + Info<< "Writing data to " << vtkFile.path() << endl; + + scalarFormatterPtr_().write + ( + true, // writeTracks + tracks, + scalarNames_, + scalarValues, + OFstream(vtkFile)() + ); + } + + // Convert vector values + + if (allVectors_.size() > 0) + { + List<List<vectorField> > vectorValues(allVectors_.size()); + + forAll(allVectors_, vectorI) + { + DynamicList<vectorList>& allTrackVals = + allVectors_[vectorI]; + vectorValues[vectorI].setSize(allTrackVals.size()); + + forAll(allTrackVals, trackI) + { + vectorList& trackVals = allTrackVals[trackI]; + vectorValues[vectorI][trackI].transfer(trackVals); + } + } + + fileName vtkFile + ( + vtkPath + / vectorFormatterPtr_().getFileName + ( + tracks[0], + vectorNames_ + ) + ); + + //Info<< "Writing vector data to " << vtkFile << endl; + + vectorFormatterPtr_().write + ( + true, // writeTracks + tracks, + vectorNames_, + vectorValues, + OFstream(vtkFile)() + ); + } + } + } +} + + +void Foam::wallBoundedStreamLine::updateMesh(const mapPolyMesh&) +{ + read(dict_); +} + + +void Foam::wallBoundedStreamLine::movePoints(const pointField&) +{ + // Moving mesh affects the search tree + read(dict_); +} + + +//void Foam::wallBoundedStreamLine::readUpdate +//(const polyMesh::readUpdateState state) +//{ +// if (state != UNCHANGED) +// { +// read(dict_); +// } +//} + + +// ************************************************************************* // diff --git a/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLine.H b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLine.H new file mode 100644 index 0000000000000000000000000000000000000000..6a39d25b37da1948004ddf1973c8cc1cb78b2b3e --- /dev/null +++ b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLine.H @@ -0,0 +1,226 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2011-2012 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::wallBoundedStreamLine + +Description + Generation of streamlines. Samples along track of passive particle. + +SourceFiles + wallBoundedStreamLine.C + +\*---------------------------------------------------------------------------*/ + +#ifndef wallBoundedStreamLine_H +#define wallBoundedStreamLine_H + +#include "volFieldsFwd.H" +#include "pointFieldFwd.H" +#include "Switch.H" +#include "DynamicList.H" +#include "scalarList.H" +#include "vectorList.H" +#include "polyMesh.H" +#include "writer.H" +#include "indirectPrimitivePatch.H" +#include "tetIndices.H" + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +// Forward declaration of classes +class objectRegistry; +class dictionary; +class mapPolyMesh; +class meshSearch; +class sampledSet; + +/*---------------------------------------------------------------------------*\ + Class wallBoundedStreamLine Declaration +\*---------------------------------------------------------------------------*/ + +class wallBoundedStreamLine +{ + // Private data + + //- Input dictionary + dictionary dict_; + + //- Name of this set of field averages. + word name_; + + //- Database this class is registered to + const objectRegistry& obr_; + + //- Load fields from files (not from objectRegistry) + bool loadFromFiles_; + + //- On/off switch + bool active_; + + + //- List of fields to sample + wordList fields_; + + //- Field to transport particle with + word UName_; + + //- Interpolation scheme to use + word interpolationScheme_; + + //- Whether to use +u or -u + bool trackForward_; + + //- Maximum lifetime (= number of cells) of particle + label lifeTime_; + + //- Optional specified name of particles + word cloudName_; + + //- Type of seed + word seedSet_; + + //- Names of scalar fields + wordList scalarNames_; + + //- Names of vector fields + wordList vectorNames_; + + + // Demand driven + + //- Mesh searching enigne + autoPtr<meshSearch> meshSearchPtr_; + + //- Seed set engine + autoPtr<sampledSet> sampledSetPtr_; + + //- Axis of the sampled points to output + word sampledSetAxis_; + + //- File output writer + autoPtr<writer<scalar> > scalarFormatterPtr_; + + autoPtr<writer<vector> > vectorFormatterPtr_; + + + // Generated data + + //- All tracks. Per particle the points it passed through + DynamicList<List<point> > allTracks_; + + //- Per scalarField, per particle, the sampled value. + List<DynamicList<scalarList> > allScalars_; + + //- Per scalarField, per particle, the sampled value. + List<DynamicList<vectorList> > allVectors_; + + + //- Construct patch out of all wall patch faces + autoPtr<indirectPrimitivePatch> wallPatch() const; + + //- Find wall tet on cell + tetIndices findNearestTet + ( + const PackedBoolList& isWallPatch, + const point& seedPt, + const label cellI + ) const; + + //- Do all seeding and tracking + void track(); + + //- Disallow default bitwise copy construct + wallBoundedStreamLine(const wallBoundedStreamLine&); + + //- Disallow default bitwise assignment + void operator=(const wallBoundedStreamLine&); + + +public: + + //- Runtime type information + TypeName("wallBoundedStreamLine"); + + + // Constructors + + //- Construct for given objectRegistry and dictionary. + // Allow the possibility to load fields from files + wallBoundedStreamLine + ( + const word& name, + const objectRegistry&, + const dictionary&, + const bool loadFromFiles = false + ); + + + //- Destructor + virtual ~wallBoundedStreamLine(); + + + // Member Functions + + //- Return name of the set of field averages + virtual const word& name() const + { + return name_; + } + + //- Read the field average data + virtual void read(const dictionary&); + + //- Execute the averaging + virtual void execute(); + + //- Execute the averaging at the final time-loop, currently does nothing + virtual void end(); + + //- Calculate the field average data and write + virtual void write(); + + //- Update for changes of mesh + virtual void updateMesh(const mapPolyMesh&); + + //- Update for mesh point-motion + virtual void movePoints(const pointField&); + + ////- Update for changes of mesh due to readUpdate + //virtual void readUpdate(const polyMesh::readUpdateState state); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineFunctionObject.C b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineFunctionObject.C new file mode 100644 index 0000000000000000000000000000000000000000..eaa1cc7eaebbe0a7951ce5a1918021b7a51c21c2 --- /dev/null +++ b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineFunctionObject.C @@ -0,0 +1,42 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2011-2012 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 "wallBoundedStreamLineFunctionObject.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + defineNamedTemplateTypeNameAndDebug(wallBoundedStreamLineFunctionObject, 0); + + addToRunTimeSelectionTable + ( + functionObject, + wallBoundedStreamLineFunctionObject, + dictionary + ); +} + +// ************************************************************************* // diff --git a/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineFunctionObject.H b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineFunctionObject.H new file mode 100644 index 0000000000000000000000000000000000000000..0ce95cc592a86b962edffd38271c508ab6795ed0 --- /dev/null +++ b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineFunctionObject.H @@ -0,0 +1,55 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2011-2012 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/>. + +Typedef + Foam::wallBoundedStreamLineFunctionObject + +Description + FunctionObject wrapper around wallBoundedStreamLines + to allow them to be created via + the functions entry within controlDict. + +SourceFiles + wallBoundedStreamLineFunctionObject.C + +\*---------------------------------------------------------------------------*/ + +#ifndef wallBoundedStreamLineFunctionObject_H +#define wallBoundedStreamLineFunctionObject_H + +#include "wallBoundedStreamLine.H" +#include "OutputFilterFunctionObject.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + typedef OutputFilterFunctionObject<wallBoundedStreamLine> + wallBoundedStreamLineFunctionObject; +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticle.C b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticle.C new file mode 100644 index 0000000000000000000000000000000000000000..cd7605a56edd2c0b27eb172a48a9e0e060ec70cc --- /dev/null +++ b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticle.C @@ -0,0 +1,1324 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2011-2012 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 "wallBoundedStreamLineParticle.H" +#include "vectorFieldIOField.H" +#include "polyMesh.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ +// defineParticleTypeNameAndDebug(wallBoundedStreamLineParticle, 0); +} + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +//// Check position is inside tet +//void Foam::wallBoundedStreamLineParticle::checkInside() const +//{ +// const tetIndices ti(currentTetIndices()); +// const tetPointRef tpr(ti.tet(mesh_)); +// if (!tpr.inside(position())) +// { +// FatalErrorIn("wallBoundedStreamLineParticle::checkInside(..)") +// << "Particle:" //<< static_cast<const particle&>(*this) +// << info() +// << "is not inside " << tpr +// << abort(FatalError); +// } +//} +// +// +//void Foam::wallBoundedStreamLineParticle::checkOnEdge() const +//{ +// // Check that edge (as indicated by meshEdgeStart_, diagEdge_) is +// // indeed one that contains the position. +// const edge e = currentEdge(); +// +// linePointRef ln(e.line(mesh_.points())); +// +// pointHit ph(ln.nearestDist(position())); +// +// if (ph.distance() > 1E-6) +// { +// FatalErrorIn +// ( +// "wallBoundedStreamLineParticle::checkOnEdge()" +// ) << "Problem :" +// << " particle:" //<< static_cast<const particle&>(*this) +// << info() +// << "edge:" << e +// << " at:" << ln +// << " distance:" << ph.distance() +// << abort(FatalError); +// } +//} +// +// +//void Foam::wallBoundedStreamLineParticle::checkOnTriangle(const point& p) +//const +//{ +// const triFace tri(currentTetIndices().faceTriIs(mesh_)); +// pointHit ph = tri.nearestPoint(p, mesh_.points()); +// if (ph.distance() > 1e-9) +// { +// FatalErrorIn +// ( +// "wallBoundedStreamLineParticle::checkOnTriangle(const point&)" +// ) << "Problem :" +// << " particle:" //<< static_cast<const particle&>(*this) +// << info() +// << "point:" << p +// << " distance:" << ph.distance() +// << abort(FatalError); +// } +//} + + +// Construct the edge the particle is on (according to meshEdgeStart_, +// diagEdge_) +Foam::edge Foam::wallBoundedStreamLineParticle::currentEdge() const +{ + if ((meshEdgeStart_ != -1) == (diagEdge_ != -1)) + { + FatalErrorIn("wallBoundedStreamLineParticle::currentEdge() const") + << "Particle:" //<< static_cast<const particle&>(*this) + << info() + << "cannot both be on a mesh edge and a face-diagonal edge." + << " meshEdgeStart_:" << meshEdgeStart_ + << " diagEdge_:" << diagEdge_ + << abort(FatalError); + } + + const Foam::face& f = mesh_.faces()[tetFace()]; + + if (meshEdgeStart_ != -1) + { + return edge(f[meshEdgeStart_], f.nextLabel(meshEdgeStart_)); + } + else + { + label faceBasePtI = mesh_.tetBasePtIs()[tetFace()]; + label diagPtI = (faceBasePtI+diagEdge_)%f.size(); + return edge(f[faceBasePtI], f[diagPtI]); + } +} + + +void Foam::wallBoundedStreamLineParticle::crossEdgeConnectedFace +( + const edge& meshEdge +) +{ + //label oldFaceI = tetFace(); + + // Update tetFace, tetPt + particle::crossEdgeConnectedFace(cell(), tetFace(), tetPt(), meshEdge); + + // Update face to be same as tracking one + face() = tetFace(); + + + // And adapt meshEdgeStart_. + const Foam::face& f = mesh_.faces()[tetFace()]; + label fp = findIndex(f, meshEdge[0]); + + if (f.nextLabel(fp) == meshEdge[1]) + { + meshEdgeStart_ = fp; + } + else + { + label fpMin1 = f.rcIndex(fp); + + if (f[fpMin1] == meshEdge[1]) + { + meshEdgeStart_ = fpMin1; + } + else + { + FatalErrorIn + ( + "wallBoundedStreamLineParticle::crossEdgeConnectedFace" + "(const edge&)" + ) << "Problem :" + << " particle:" //<< static_cast<const particle&>(*this) + << info() + << "face:" << tetFace() + << " verts:" << f + << " meshEdge:" << meshEdge + << abort(FatalError); + } + } + + diagEdge_ = -1; + + //Pout<< " crossed meshEdge " + // << meshEdge.line(mesh().points()) + // << " from face:" << oldFaceI + // << " to face:" << tetFace() << endl; + + + // Check that still on same mesh edge + + const edge eNew(f[meshEdgeStart_], f.nextLabel(meshEdgeStart_)); + if (eNew != meshEdge) + { + FatalErrorIn + ( + "wallBoundedStreamLineParticle::crossEdgeConnectedFace" + "(const edge&)" + ) << "Problem" << abort(FatalError); + } + + + // Check that edge (as indicated by meshEdgeStart_) is indeed one that + // contains the position. + //checkOnEdge(); +} + + +void Foam::wallBoundedStreamLineParticle::crossDiagonalEdge() +{ + if (diagEdge_ == -1) + { + FatalErrorIn("wallBoundedStreamLineParticle::crossDiagonalEdge()") + << "Particle:" //<< static_cast<const particle&>(*this) + << info() + << "not on a diagonal edge" << abort(FatalError); + } + if (meshEdgeStart_ != -1) + { + FatalErrorIn("wallBoundedStreamLineParticle::crossDiagonalEdge()") + << "Particle:" //<< static_cast<const particle&>(*this) + << info() + << "meshEdgeStart_:" << meshEdgeStart_ << abort(FatalError); + } + + //label oldTetPt = tetPt(); + + const Foam::face& f = mesh_.faces()[tetFace()]; + + // tetPtI starts from 1, goes up to f.size()-2 + + if (tetPt() == diagEdge_) + { + tetPt() = f.rcIndex(tetPt()); + } + else + { + label nextTetPt = f.fcIndex(tetPt()); + if (diagEdge_ == nextTetPt) + { + tetPt() = nextTetPt; + } + else + { + FatalErrorIn("wallBoundedStreamLineParticle::crossDiagonalEdge()") + << "Particle:" //<< static_cast<const particle&>(*this) + << info() + << "tetPt:" << tetPt() + << " diagEdge:" << diagEdge_ << abort(FatalError); + } + } + + meshEdgeStart_ = -1; + + //Pout<< " crossed diagonalEdge " + // << currentEdge().line(mesh().points()) + // << " from tetPt:" << oldTetPt + // << " to tetPt:" << tetPt() << endl; +} + + +//- Track through a single triangle. +// Gets passed tet+triangle the particle is in. Updates position() but nothing +// else. Returns the triangle edge the particle is now on. +Foam::scalar Foam::wallBoundedStreamLineParticle::trackFaceTri +( + const vector& endPosition, + label& minEdgeI +) +{ + // Track p from position to endPosition + const triFace tri(currentTetIndices().faceTriIs(mesh_)); + vector n = tri.normal(mesh_.points()); + //if (mag(n) < sqr(SMALL)) + //{ + // FatalErrorIn("wallBoundedStreamLineParticle::trackFaceTri(..)") + // << "Small triangle." //<< static_cast<const particle&>(*this) + // << info() + // << "n:" << n + // << abort(FatalError); + //} + n /= mag(n)+VSMALL; + + // Check which edge intersects the trajectory. + // Project trajectory onto triangle + minEdgeI = -1; + scalar minS = 1; // end position + + //const point oldPosition(position()); + + + edge currentE(-1, -1); + if (meshEdgeStart_ != -1 || diagEdge_ != -1) + { + currentE = currentEdge(); + } + + // Determine path along line position+s*d to see where intersections + // are. + + forAll(tri, i) + { + label j = tri.fcIndex(i); + + const point& pt0 = mesh_.points()[tri[i]]; + const point& pt1 = mesh_.points()[tri[j]]; + + if (edge(tri[i], tri[j]) == currentE) + { + // Do not check particle is on + continue; + } + + // Outwards pointing normal + vector edgeNormal = (pt1-pt0)^n; + + //if (mag(edgeNormal) < SMALL) + //{ + // FatalErrorIn("wallBoundedStreamLineParticle::trackFaceTri(..)") + // << "Edge not perpendicular to triangle." + // //<< static_cast<const particle&>(*this) + // << info() + // << "triangle n:" << n + // << " edgeNormal:" << edgeNormal + // << " on tri:" << tri + // << " at:" << pt0 + // << " at:" << pt1 + // << abort(FatalError); + //} + + + edgeNormal /= mag(edgeNormal)+VSMALL; + + // Determine whether position and end point on either side of edge. + scalar sEnd = (endPosition-pt0)&edgeNormal; + if (sEnd >= 0) + { + // endPos is outside triangle. position() should always be + // inside. + scalar sStart = (position()-pt0)&edgeNormal; + if (mag(sEnd-sStart) > VSMALL) + { + scalar s = sStart/(sStart-sEnd); + + if (s >= 0 && s < minS) + { + minS = s; + minEdgeI = i; + } + } + } + } + + if (minEdgeI != -1) + { + position() += minS*(endPosition-position()); + } + else + { + // Did not hit any edge so tracked to the end position. Set position + // without any calculation to avoid truncation errors. + position() = endPosition; + minS = 1.0; + } + + // Project position() back onto plane of triangle + const point& triPt = mesh_.points()[tri[0]]; + position() -= ((position()-triPt)&n)*n; + + + //Pout<< " tracked from:" << oldPosition << " to:" << position() + // << " projectedEnd:" << endPosition + // << " at s:" << minS << endl; + //if (minEdgeI != -1) + //{ + // Pout<< " on edge:" << minEdgeI + // << " on edge:" + // << mesh_.points()[tri[minEdgeI]] + // << mesh_.points()[tri[tri.fcIndex(minEdgeI)]] + // << endl; + //} + + return minS; +} + + +// See if the current triangle has got a point on the +// correct side of the edge. +bool Foam::wallBoundedStreamLineParticle::isTriAlongTrack +( + const point& endPosition +) const +{ + const triFace triVerts(currentTetIndices().faceTriIs(mesh_)); + const edge currentE = currentEdge(); + + //if (debug) + { + if + ( + currentE[0] == currentE[1] + || findIndex(triVerts, currentE[0]) == -1 + || findIndex(triVerts, currentE[1]) == -1 + ) + { + FatalErrorIn + ( + "wallBoundedStreamLineParticle::isTriAlongTrack" + "(const point&)" + ) << "Edge " << currentE << " not on triangle " << triVerts + << info() + << abort(FatalError); + } + } + + + const vector dir = endPosition-position(); + + // Get normal of currentE + vector n = triVerts.normal(mesh_.points()); + n /= mag(n); + + forAll(triVerts, i) + { + label j = triVerts.fcIndex(i); + const point& pt0 = mesh_.points()[triVerts[i]]; + const point& pt1 = mesh_.points()[triVerts[j]]; + + if (edge(triVerts[i], triVerts[j]) == currentE) + { + vector edgeNormal = (pt1-pt0)^n; + return (dir&edgeNormal) < 0; + } + } + + FatalErrorIn + ( + "wallBoundedStreamLineParticle::isTriAlongTrack" + "(const point&)" + ) << "Problem" << abort(FatalError); + + return false; +} + + +void Foam::wallBoundedStreamLineParticle::patchInteraction +( + trackingData& td, + const scalar trackFraction +) +{ + typedef trackingData::cloudType cloudType; + typedef cloudType::particleType particleType; + + particleType& p = static_cast<particleType&>(*this); + p.hitFace(td); + + if (!internalFace(faceI_)) + { + label origFaceI = faceI_; + label patchI = patch(faceI_); + + // No action taken for tetPtI_ for tetFaceI_ here, handled by + // patch interaction call or later during processor transfer. + + + // Dummy tet indices. What to do here? + tetIndices faceHitTetIs; + + if + ( + !p.hitPatch + ( + mesh_.boundaryMesh()[patchI], + td, + patchI, + trackFraction, + faceHitTetIs + ) + ) + { + // Did patch interaction model switch patches? + // Note: recalculate meshEdgeStart_, diagEdge_! + if (faceI_ != origFaceI) + { + patchI = patch(faceI_); + } + + const polyPatch& patch = mesh_.boundaryMesh()[patchI]; + + if (isA<wedgePolyPatch>(patch)) + { + p.hitWedgePatch + ( + static_cast<const wedgePolyPatch&>(patch), td + ); + } + else if (isA<symmetryPolyPatch>(patch)) + { + p.hitSymmetryPatch + ( + static_cast<const symmetryPolyPatch&>(patch), td + ); + } + else if (isA<cyclicPolyPatch>(patch)) + { + p.hitCyclicPatch + ( + static_cast<const cyclicPolyPatch&>(patch), td + ); + } + else if (isA<processorPolyPatch>(patch)) + { + p.hitProcessorPatch + ( + static_cast<const processorPolyPatch&>(patch), td + ); + } + else if (isA<wallPolyPatch>(patch)) + { + p.hitWallPatch + ( + static_cast<const wallPolyPatch&>(patch), td, faceHitTetIs + ); + } + else + { + p.hitPatch(patch, td); + } + } + } +} + +//- Track particle to a given position and returns 1.0 if the +// trajectory is completed without hitting a face otherwise +// stops at the face and returns the fraction of the trajectory +// completed. +// on entry 'stepFraction()' should be set to the fraction of the +// time-step at which the tracking starts. +Foam::scalar Foam::wallBoundedStreamLineParticle::trackToEdge +( + trackingData& td, + const vector& endPosition +) +{ + // Are we on a track face? If not we do a topological walk. + + // Particle: + // - cell_ always set + // - tetFace_, tetPt_ always set (these identify tet particle is in) + // - optionally meshEdgeStart_ or diagEdge_ set (edge particle is on) + + //checkInside(); + //checkOnTriangle(position()); + //if (meshEdgeStart_ != -1 || diagEdge_ != -1) + //{ + // checkOnEdge(); + //} + + scalar trackFraction = 0.0; + + if (!td.isWallPatch_[tetFace()]) + { + // Don't track across face. Just walk in cell. Particle is on + // mesh edge (as indicated by meshEdgeStart_). + const edge meshEdge(currentEdge()); + + // If internal face check whether to go to neighbour cell or just + // check to the other internal tet on the edge. + if (mesh_.isInternalFace(tetFace())) + { + label nbrCellI = + ( + cellI_ == mesh_.faceOwner()[faceI_] + ? mesh_.faceNeighbour()[faceI_] + : mesh_.faceOwner()[faceI_] + ); + // Check angle to nbrCell tet. Is it in the direction of the + // endposition? I.e. since volume of nbr tet is positive the + // tracking direction should be into the tet. + tetIndices nbrTi(nbrCellI, tetFaceI_, tetPtI_, mesh_); + if ((nbrTi.faceTri(mesh_).normal() & (endPosition-position())) < 0) + { + // Change into nbrCell. No need to change tetFace, tetPt. + //Pout<< " crossed from cell:" << cellI_ + // << " into " << nbrCellI << endl; + cellI_ = nbrCellI; + patchInteraction(td, trackFraction); + } + else + { + // Walk to other face on edge. Changes tetFace, tetPt but not + // cell. + crossEdgeConnectedFace(meshEdge); + patchInteraction(td, trackFraction); + } + } + else + { + // Walk to other face on edge. This might give loop since + // particle should have been removed? + crossEdgeConnectedFace(meshEdge); + patchInteraction(td, trackFraction); + } + } + else + { + // We're inside a tet on the wall. Check if the current tet is + // the one to cross. If not we cross into the neighbouring triangle. + + if (mesh_.isInternalFace(tetFace())) + { + FatalErrorIn + ( + "wallBoundedStreamLineParticle::trackToEdge" + "(trackingData&, const vector&)" + ) << "Can only track on boundary faces." + << " Face:" << tetFace() + << " at:" << mesh_.faceCentres()[tetFace()] + << abort(FatalError); + } + + point projectedEndPosition = endPosition; + // Remove normal component + { + const triFace tri(currentTetIndices().faceTriIs(mesh_)); + vector n = tri.normal(mesh_.points()); + n /= mag(n); + const point& basePt = mesh_.points()[tri[0]]; + projectedEndPosition -= ((projectedEndPosition-basePt)&n)*n; + } + + + bool doTrack = false; + if (meshEdgeStart_ == -1 && diagEdge_ == -1) + { + // We're starting and not yet on an edge. + doTrack = true; + } + else + { + // See if the current triangle has got a point on the + // correct side of the edge. + doTrack = isTriAlongTrack(projectedEndPosition); + } + + + if (doTrack) + { + // Track across triangle. Return triangle edge crossed. + label triEdgeI = -1; + trackFraction = trackFaceTri(projectedEndPosition, triEdgeI); + + if (triEdgeI == -1) + { + // Reached endpoint + //checkInside(); + return trackFraction; + } + + const tetIndices ti(currentTetIndices()); + + // Triangle (faceTriIs) gets constructed from + // f[faceBasePtI_], + // f[facePtAI_], + // f[facePtBI_] + // + // So edge indices are: + // 0 : edge between faceBasePtI_ and facePtAI_ + // 1 : edge between facePtAI_ and facePtBI_ (is always a real edge) + // 2 : edge between facePtBI_ and faceBasePtI_ + + const Foam::face& f = mesh_.faces()[ti.face()]; + const label fp0 = ti.faceBasePt(); + + if (triEdgeI == 0) + { + if (ti.facePtA() == f.fcIndex(fp0)) + { + //Pout<< "Real edge." << endl; + diagEdge_ = -1; + meshEdgeStart_ = fp0; + //checkOnEdge(); + crossEdgeConnectedFace(currentEdge()); + patchInteraction(td, trackFraction); + } + else if (ti.facePtA() == f.rcIndex(fp0)) + { + //Note: should not happen since boundary face so owner + //Pout<< "Real edge." << endl; + FatalErrorIn("shold not happend") << info() + << abort(FatalError); + + diagEdge_ = -1; + meshEdgeStart_ = f.rcIndex(fp0); + //checkOnEdge(); + crossEdgeConnectedFace(currentEdge()); + patchInteraction(td, trackFraction); + } + else + { + // Get index of triangle on other side of edge. + diagEdge_ = ti.facePtA()-fp0; + if (diagEdge_ < 0) + { + diagEdge_ += f.size(); + } + meshEdgeStart_ = -1; + //checkOnEdge(); + crossDiagonalEdge(); + } + } + else if (triEdgeI == 1) + { + //Pout<< "Real edge." << endl; + diagEdge_ = -1; + meshEdgeStart_ = ti.facePtA(); + //checkOnEdge(); + crossEdgeConnectedFace(currentEdge()); + patchInteraction(td, trackFraction); + } + else // if (triEdgeI == 2) + { + if (ti.facePtB() == f.rcIndex(fp0)) + { + //Pout<< "Real edge." << endl; + diagEdge_ = -1; + meshEdgeStart_ = ti.facePtB(); + //checkOnEdge(); + crossEdgeConnectedFace(currentEdge()); + patchInteraction(td, trackFraction); + } + else if (ti.facePtB() == f.fcIndex(fp0)) + { + //Note: should not happen since boundary face so owner + //Pout<< "Real edge." << endl; + FatalErrorIn("shold not happend") << info() + << abort(FatalError); + + diagEdge_ = -1; + meshEdgeStart_ = fp0; + //checkOnEdge(); + crossEdgeConnectedFace(currentEdge()); + patchInteraction(td, trackFraction); + } + else + { + //Pout<< "Triangle edge." << endl; + // Get index of triangle on other side of edge. + diagEdge_ = ti.facePtB()-fp0; + if (diagEdge_ < 0) + { + diagEdge_ += f.size(); + } + meshEdgeStart_ = -1; + //checkOnEdge(); + crossDiagonalEdge(); + } + } + } + else + { + // Current tet is not the right one. Check the neighbour tet. + + if (meshEdgeStart_ != -1) + { + // Particle is on mesh edge so change into other face on cell + crossEdgeConnectedFace(currentEdge()); + //checkOnEdge(); + patchInteraction(td, trackFraction); + } + else + { + // Particle is on diagonal edge so change into the other + // triangle. + crossDiagonalEdge(); + //checkOnEdge(); + } + } + } + + //checkInside(); + + return trackFraction; +} + + +Foam::vector Foam::wallBoundedStreamLineParticle::interpolateFields +( + const trackingData& td, + const point& position, + const label cellI, + const label faceI +) +{ + if (cellI == -1) + { + FatalErrorIn("wallBoundedStreamLineParticle::interpolateFields(..)") + << "Cell:" << cellI << abort(FatalError); + } + + const tetIndices ti = currentTetIndices(); + + + sampledScalars_.setSize(td.vsInterp_.size()); + forAll(td.vsInterp_, scalarI) + { + sampledScalars_[scalarI].append + ( + td.vsInterp_[scalarI].interpolate + ( + position, + ti, //cellI, + faceI + ) + ); + } + + sampledVectors_.setSize(td.vvInterp_.size()); + forAll(td.vvInterp_, vectorI) + { + sampledVectors_[vectorI].append + ( + td.vvInterp_[vectorI].interpolate + ( + position, + ti, //cellI, + faceI + ) + ); + } + + const DynamicList<vector>& U = sampledVectors_[td.UIndex_]; + + return U.last(); +} + + +Foam::vector Foam::wallBoundedStreamLineParticle::sample +( + trackingData& td +) +{ + sampledPositions_.append(position()); + vector U = interpolateFields(td, position(), cell(), tetFace()); + + if (!td.trackForward_) + { + U = -U; + } + + scalar magU = mag(U); + + if (magU < SMALL) + { + // Stagnant particle. Might as well stop + lifeTime_ = 0; + } + + U /= magU; + + return U; +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::wallBoundedStreamLineParticle::wallBoundedStreamLineParticle +( + const polyMesh& mesh, + const vector& position, + const label cellI, + const label tetFaceI, + const label tetPtI, + const label meshEdgeStart, + const label diagEdge, + const label lifeTime +) +: + particle(mesh, position, cellI, tetFaceI, tetPtI), + meshEdgeStart_(meshEdgeStart), + diagEdge_(diagEdge), + lifeTime_(lifeTime) +{ + //checkInside(); + + //if (meshEdgeStart_ != -1 || diagEdge_ != -1) + //{ + // checkOnEdge(); + //} + + // Unfortunately have no access to trackdata so cannot check if particle + // is on a wallPatch or has an mesh edge set (either of which is + // a requirement). +} + + +Foam::wallBoundedStreamLineParticle::wallBoundedStreamLineParticle +( + const polyMesh& mesh, + Istream& is, + bool readFields +) +: + particle(mesh, is, readFields) +{ + if (readFields) + { + List<scalarList> sampledScalars; + List<vectorList> sampledVectors; + + is >> meshEdgeStart_ >> diagEdge_ >> lifeTime_ + >> sampledPositions_ >> sampledScalars >> sampledVectors; + + sampledScalars_.setSize(sampledScalars.size()); + forAll(sampledScalars, i) + { + sampledScalars_[i].transfer(sampledScalars[i]); + } + sampledVectors_.setSize(sampledVectors.size()); + forAll(sampledVectors, i) + { + sampledVectors_[i].transfer(sampledVectors[i]); + } + } + + // Check state of Istream + is.check + ( + "wallBoundedStreamLineParticle::wallBoundedStreamLineParticle" + "(const Cloud<wallBoundedStreamLineParticle>&, Istream&, bool)" + ); +} + + +Foam::wallBoundedStreamLineParticle::wallBoundedStreamLineParticle +( + const wallBoundedStreamLineParticle& p +) +: + particle(p), + meshEdgeStart_(p.meshEdgeStart_), + diagEdge_(p.diagEdge_), + lifeTime_(p.lifeTime_), + sampledPositions_(p.sampledPositions_), + sampledScalars_(p.sampledScalars_), + sampledVectors_(p.sampledVectors_) +{} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +bool Foam::wallBoundedStreamLineParticle::move +( + trackingData& td, + const scalar trackTime +) +{ + wallBoundedStreamLineParticle& p = static_cast + < + wallBoundedStreamLineParticle& + >(*this); + + + // Check position is inside tet + //checkInside(); + + td.switchProcessor = false; + td.keepParticle = true; + + scalar tEnd = (1.0 - stepFraction())*trackTime; + scalar maxDt = mesh_.bounds().mag(); + + while + ( + td.keepParticle + && !td.switchProcessor + && lifeTime_ > 0 + ) + { + // set the lagrangian time-step + scalar dt = maxDt; + + --lifeTime_; + + // Update sampled velocity and fields if position changed + vector U = sample(td); + + // !user parameter! + if (dt < SMALL) + { + // Force removal + lifeTime_ = 0; + break; + } + + + scalar fraction = trackToEdge(td, position() + dt*U); + dt *= fraction; + + tEnd -= dt; + stepFraction() = 1.0 - tEnd/trackTime; + + + if (tEnd <= ROOTVSMALL) + { + // Force removal + lifeTime_ = 0; + } + + if + ( + !td.keepParticle + || td.switchProcessor + || lifeTime_ == 0 + ) + { + break; + } + } + + + if (!td.keepParticle || lifeTime_ == 0) + { + if (lifeTime_ == 0) + { + if (debug) + { + Pout<< "wallBoundedStreamLineParticle :" + << " Removing stagnant particle:" + << p.position() + << " sampled positions:" << sampledPositions_.size() + << endl; + } + td.keepParticle = false; + } + else + { + // Normal exit. Store last position and fields + sample(td); + + if (debug) + { + Pout<< "wallBoundedStreamLineParticle : Removing particle:" + << p.position() + << " sampled positions:" << sampledPositions_.size() + << endl; + } + } + + // Transfer particle data into trackingData. + { + //td.allPositions_.append(sampledPositions_); + td.allPositions_.append(vectorList()); + vectorList& top = td.allPositions_.last(); + top.transfer(sampledPositions_); + } + + forAll(sampledScalars_, i) + { + //td.allScalars_[i].append(sampledScalars_[i]); + td.allScalars_[i].append(scalarList()); + scalarList& top = td.allScalars_[i].last(); + top.transfer(sampledScalars_[i]); + } + forAll(sampledVectors_, i) + { + //td.allVectors_[i].append(sampledVectors_[i]); + td.allVectors_[i].append(vectorList()); + vectorList& top = td.allVectors_[i].last(); + top.transfer(sampledVectors_[i]); + } + } + + return td.keepParticle; +} + + +bool Foam::wallBoundedStreamLineParticle::hitPatch +( + const polyPatch&, + trackingData& td, + const label patchI, + const scalar trackFraction, + const tetIndices& tetIs +) +{ + // Disable generic patch interaction + return false; +} + + +void Foam::wallBoundedStreamLineParticle::hitWedgePatch +( + const wedgePolyPatch& pp, + trackingData& td +) +{ + // Remove particle + td.keepParticle = false; +} + + +void Foam::wallBoundedStreamLineParticle::hitSymmetryPatch +( + const symmetryPolyPatch& pp, + trackingData& td +) +{ + // Remove particle + td.keepParticle = false; +} + + +void Foam::wallBoundedStreamLineParticle::hitCyclicPatch +( + const cyclicPolyPatch& pp, + trackingData& td +) +{ + // Remove particle + td.keepParticle = false; +} + + +void Foam::wallBoundedStreamLineParticle::hitProcessorPatch +( + const processorPolyPatch& pp, + trackingData& td +) +{ + // Switch particle + td.switchProcessor = true; + + // Adapt edgeStart_ for other side. + // E.g. if edgeStart_ is 1 then the edge is between vertex 1 and 2 so + // on the other side between 2 and 3 so edgeStart_ should be + // f.size()-edgeStart_-1. + + const Foam::face& f = mesh_.faces()[face()]; + + if (meshEdgeStart_ != -1) + { + meshEdgeStart_ = f.size()-meshEdgeStart_-1; + } + else + { + // diagEdge_ is relative to faceBasePt + diagEdge_ = f.size()-diagEdge_; + } +} + + +void Foam::wallBoundedStreamLineParticle::hitWallPatch +( + const wallPolyPatch& wpp, + trackingData& td, + const tetIndices& +) +{} + + +void Foam::wallBoundedStreamLineParticle::hitPatch +( + const polyPatch& wpp, + trackingData& td +) +{ + // Remove particle + td.keepParticle = false; +} + + +void Foam::wallBoundedStreamLineParticle::readFields +( + Cloud<wallBoundedStreamLineParticle>& c +) +{ + if (!c.size()) + { + return; + } + + particle::readFields(c); + + IOField<label> meshEdgeStart + ( + c.fieldIOobject("meshEdgeStart", IOobject::MUST_READ) + ); + + IOField<label> diagEdge + ( + c.fieldIOobject("diagEdge_", IOobject::MUST_READ) + ); + c.checkFieldIOobject(c, diagEdge); + + IOField<label> lifeTime + ( + c.fieldIOobject("lifeTime", IOobject::MUST_READ) + ); + c.checkFieldIOobject(c, lifeTime); + + vectorFieldIOField sampledPositions + ( + c.fieldIOobject("sampledPositions", IOobject::MUST_READ) + ); + c.checkFieldIOobject(c, sampledPositions); + + label i = 0; + forAllIter(Cloud<wallBoundedStreamLineParticle>, c, iter) + { + iter().meshEdgeStart_ = meshEdgeStart[i]; + iter().diagEdge_ = diagEdge[i]; + iter().lifeTime_ = lifeTime[i]; + iter().sampledPositions_.transfer(sampledPositions[i]); + i++; + } +} + + +void Foam::wallBoundedStreamLineParticle::writeFields +( + const Cloud<wallBoundedStreamLineParticle>& c +) +{ + particle::writeFields(c); + + label np = c.size(); + + IOField<label> meshEdgeStart + ( + c.fieldIOobject("meshEdgeStart", IOobject::NO_READ), + np + ); + IOField<label> diagEdge + ( + c.fieldIOobject("diagEdge", IOobject::NO_READ), + np + ); + IOField<label> lifeTime + ( + c.fieldIOobject("lifeTime", IOobject::NO_READ), + np + ); + vectorFieldIOField sampledPositions + ( + c.fieldIOobject("sampledPositions", IOobject::NO_READ), + np + ); + + label i = 0; + forAllConstIter(Cloud<wallBoundedStreamLineParticle>, c, iter) + { + meshEdgeStart[i] = iter().meshEdgeStart_; + diagEdge[i] = iter().diagEdge_; + lifeTime[i] = iter().lifeTime_; + sampledPositions[i] = iter().sampledPositions_; + i++; + } + + meshEdgeStart.write(); + diagEdge.write(); + lifeTime.write(); + sampledPositions.write(); +} + + +// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * // + +Foam::Ostream& Foam::operator<< +( + Ostream& os, + const wallBoundedStreamLineParticle& p +) +{ + os << static_cast<const particle&>(p) + << token::SPACE << p.meshEdgeStart_ + << token::SPACE << p.diagEdge_ + << token::SPACE << p.lifeTime_ + << token::SPACE << p.sampledPositions_ + << token::SPACE << p.sampledScalars_ + << token::SPACE << p.sampledVectors_; + + // Check state of Ostream + os.check + ( + "Ostream& operator<<(Ostream&, const wallBoundedStreamLineParticle&)" + ); + + return os; +} + + +Foam::Ostream& Foam::operator<< +( + Ostream& os, + const InfoProxy<wallBoundedStreamLineParticle>& ip +) +{ + const wallBoundedStreamLineParticle& p = ip.t_; + + tetPointRef tpr(p.currentTet()); + + os << " " << static_cast<const particle&>(p) << nl + << " tet:" << nl; + os << " "; + meshTools::writeOBJ(os, tpr.a()); + os << " "; + meshTools::writeOBJ(os, tpr.b()); + os << " "; + meshTools::writeOBJ(os, tpr.c()); + os << " "; + meshTools::writeOBJ(os, tpr.d()); + os << " l 1 2" << nl + << " l 1 3" << nl + << " l 1 4" << nl + << " l 2 3" << nl + << " l 2 4" << nl + << " l 3 4" << nl; + os << " "; + meshTools::writeOBJ(os, p.position()); + + return os; +} + + + +// ************************************************************************* // diff --git a/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticle.H b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticle.H new file mode 100644 index 0000000000000000000000000000000000000000..c0e19dd090a55e559f54e9b9d1e0d2562a307e05 --- /dev/null +++ b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticle.H @@ -0,0 +1,368 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2011-2012 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::wallBoundedStreamLineParticle + +Description + Particle class that samples fields as it passes through. Used in streamline + calculation. + +SourceFiles + wallBoundedStreamLineParticle.C + +\*---------------------------------------------------------------------------*/ + +#ifndef wallBoundedStreamLineParticle_H +#define wallBoundedStreamLineParticle_H + +#include "particle.H" +#include "autoPtr.H" +#include "interpolation.H" +#include "vectorList.H" +#include "InfoProxy.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +class wallBoundedStreamLineParticleCloud; + +/*---------------------------------------------------------------------------*\ + Class wallBoundedStreamLineParticle Declaration +\*---------------------------------------------------------------------------*/ + +class wallBoundedStreamLineParticle +: + public particle +{ + +public: + + //- Class used to pass tracking data to the trackToFace function + class trackingData + : + public particle::TrackingData<Cloud<wallBoundedStreamLineParticle> > + { + + public: + + + const PtrList<interpolation<scalar> >& vsInterp_; + const PtrList<interpolation<vector> >& vvInterp_; + const label UIndex_; + const bool trackForward_; + + const PackedBoolList& isWallPatch_; + + DynamicList<vectorList>& allPositions_; + List<DynamicList<scalarList> >& allScalars_; + List<DynamicList<vectorList> >& allVectors_; + + + // Constructors + + trackingData + ( + Cloud<wallBoundedStreamLineParticle>& cloud, + const PtrList<interpolation<scalar> >& vsInterp, + const PtrList<interpolation<vector> >& vvInterp, + const label UIndex, + const bool trackForward, + const PackedBoolList& isWallPatch, + + DynamicList<List<point> >& allPositions, + List<DynamicList<scalarList> >& allScalars, + List<DynamicList<vectorList> >& allVectors + ) + : + particle::TrackingData<Cloud<wallBoundedStreamLineParticle> > + ( + cloud + ), + vsInterp_(vsInterp), + vvInterp_(vvInterp), + UIndex_(UIndex), + trackForward_(trackForward), + isWallPatch_(isWallPatch), + + allPositions_(allPositions), + allScalars_(allScalars), + allVectors_(allVectors) + {} + }; + + +private: + + // Private data + + //- Particle is on mesh edge: + // const face& f = mesh.faces()[tetFace()] + // const edge e(f[meshEdgeStart_], f.nextLabel(meshEdgeStart_)); + // Note that this real edge + // is also one of the edges of the face-triangle (from + // tetFace()+tetPt()). + label meshEdgeStart_; + + //- Particle is on diagonal edge: + // const face& f = mesh.faces()[tetFace()] + // label faceBasePtI = mesh.tetBasePtIs()[faceI]; + // label diagPtI = (faceBasePtI+diagEdge_)%f.size(); + // const edge e(f[faceBasePtI], f[diagPtI]); + label diagEdge_; + + //- Lifetime of particle. Particle dies when reaches 0. + label lifeTime_; + + //- sampled positions + DynamicList<point> sampledPositions_; + + //- sampled scalars + List<DynamicList<scalar> > sampledScalars_; + + //- sampled vectors + List<DynamicList<vector> > sampledVectors_; + + + // Private Member Functions + + //- Construct current edge + edge currentEdge() const; + + //- Check if inside current tet + //void checkInside() const; + + //- Check if on current edge + //void checkOnEdge() const; + + //- Check if point on triangle + //void checkOnTriangle(const point&) const; + + //- Cross mesh edge into different face on same cell + void crossEdgeConnectedFace(const edge& meshEdge); + + //- Cross diagonal edge into different triangle on same face,cell + void crossDiagonalEdge(); + + //- Track through single triangle + scalar trackFaceTri(const vector& endPosition, label& minEdgeI); + + //- Do all patch interaction + void patchInteraction(trackingData& td, const scalar trackFraction); + + //- Is current triangle in the track direction + bool isTriAlongTrack(const point& endPosition) const; + + //- Equivalent of trackToFace + scalar trackToEdge + ( + trackingData& td, + const vector& endPosition + ); + + //- Interpolate all quantities; return interpolated velocity. + vector interpolateFields + ( + const trackingData&, + const point&, + const label cellI, + const label faceI + ); + + //- Interpolate all quantities, return updated velocity. + vector sample(trackingData& td); + +public: + + // Constructors + + //- Construct from components + wallBoundedStreamLineParticle + ( + const polyMesh& c, + const vector& position, + const label cellI, + const label tetFaceI, + const label tetPtI, + const label meshEdgeStart, + const label diagEdge, + const label lifeTime + ); + + //- Construct from Istream + wallBoundedStreamLineParticle + ( + const polyMesh& c, + Istream& is, + bool readFields = true + ); + + //- Construct copy + wallBoundedStreamLineParticle(const wallBoundedStreamLineParticle& p); + + //- Construct and return a clone + autoPtr<particle> clone() const + { + return autoPtr<particle>(new wallBoundedStreamLineParticle(*this)); + } + + //- Factory class to read-construct particles used for + // parallel transfer + class iNew + { + const polyMesh& mesh_; + + public: + + iNew(const polyMesh& mesh) + : + mesh_(mesh) + {} + + autoPtr<wallBoundedStreamLineParticle> operator() + ( + Istream& is + ) const + { + return autoPtr<wallBoundedStreamLineParticle> + ( + new wallBoundedStreamLineParticle(mesh_, is, true) + ); + } + }; + + + // Member Functions + + // Tracking + + //- Track all particles to their end point + bool move(trackingData&, const scalar trackTime); + + + //- Overridable function to handle the particle hitting a patch + // Executed before other patch-hitting functions + bool hitPatch + ( + const polyPatch&, + trackingData& td, + const label patchI, + const scalar trackFraction, + const tetIndices& tetIs + ); + + //- Overridable function to handle the particle hitting a wedge + void hitWedgePatch + ( + const wedgePolyPatch&, + trackingData& td + ); + + //- Overridable function to handle the particle hitting a + // symmetryPlane + void hitSymmetryPatch + ( + const symmetryPolyPatch&, + trackingData& td + ); + + //- Overridable function to handle the particle hitting a cyclic + void hitCyclicPatch + ( + const cyclicPolyPatch&, + trackingData& td + ); + + //- Overridable function to handle the particle hitting a + //- processorPatch + void hitProcessorPatch + ( + const processorPolyPatch&, + trackingData& td + ); + + //- Overridable function to handle the particle hitting a wallPatch + void hitWallPatch + ( + const wallPolyPatch&, + trackingData& td, + const tetIndices& + ); + + //- Overridable function to handle the particle hitting a polyPatch + void hitPatch + ( + const polyPatch&, + trackingData& td + ); + + + // Info + + //- Return info proxy. + // Used to print particle information to a stream + InfoProxy<wallBoundedStreamLineParticle> info() const + { + return *this; + } + + + // I-O + + //- Read + static void readFields(Cloud<wallBoundedStreamLineParticle>&); + + //- Write + static void writeFields + ( + const Cloud<wallBoundedStreamLineParticle>& + ); + + + // Ostream Operator + + friend Ostream& operator<< + ( + Ostream&, + const wallBoundedStreamLineParticle& + ); + + friend Ostream& operator<< + ( + Ostream&, + const InfoProxy<wallBoundedStreamLineParticle>& + ); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticleCloud.C b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticleCloud.C new file mode 100644 index 0000000000000000000000000000000000000000..dbe7ce827665f75efc529d49e258af19570ea7e6 --- /dev/null +++ b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticleCloud.C @@ -0,0 +1,64 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2011-2012 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 "wallBoundedStreamLineParticleCloud.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + defineTemplateTypeNameAndDebug(Cloud<wallBoundedStreamLineParticle>, 0); +} + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::wallBoundedStreamLineParticleCloud::wallBoundedStreamLineParticleCloud +( + const polyMesh& mesh, + const word& cloudName, + bool readFields +) +: + Cloud<wallBoundedStreamLineParticle>(mesh, cloudName, false) +{ + if (readFields) + { + wallBoundedStreamLineParticle::readFields(*this); + } +} + + +Foam::wallBoundedStreamLineParticleCloud::wallBoundedStreamLineParticleCloud +( + const polyMesh& mesh, + const word& cloudName, + const IDLList<wallBoundedStreamLineParticle>& particles +) +: + Cloud<wallBoundedStreamLineParticle>(mesh, cloudName, particles) +{} + + +// ************************************************************************* // diff --git a/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticleCloud.H b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticleCloud.H new file mode 100644 index 0000000000000000000000000000000000000000..b91e9b5d8fa32007dc01cfe6e0426a9199027449 --- /dev/null +++ b/src/postProcessing/functionObjects/field/wallBoundedStreamLine/wallBoundedStreamLineParticleCloud.H @@ -0,0 +1,99 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2011-2012 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::wallBoundedStreamLineParticleCloud + +Description + A Cloud of streamLine particles + +SourceFiles + streamLineCloud.C + +\*---------------------------------------------------------------------------*/ + +#ifndef streamLineCloud_H +#define streamLineCloud_H + +#include "Cloud.H" +#include "wallBoundedStreamLineParticle.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class streamLineCloud Declaration +\*---------------------------------------------------------------------------*/ + +class wallBoundedStreamLineParticleCloud +: + public Cloud<wallBoundedStreamLineParticle> +{ + // Private Member Functions + + //- Disallow default bitwise copy construct + wallBoundedStreamLineParticleCloud + ( + const wallBoundedStreamLineParticleCloud& + ); + + //- Disallow default bitwise assignment + void operator=(const wallBoundedStreamLineParticleCloud&); + + +public: + + //- Type of parcel the cloud was instantiated for + typedef wallBoundedStreamLineParticle parcelType; + + // Constructors + + //- Construct given mesh + wallBoundedStreamLineParticleCloud + ( + const polyMesh&, + const word& cloudName = "defaultCloud", + bool readFields = true + ); + + //- Construct from mesh, cloud name, and a list of particles + wallBoundedStreamLineParticleCloud + ( + const polyMesh& mesh, + const word& cloudName, + const IDLList<wallBoundedStreamLineParticle>& particles + ); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* //