diff --git a/etc/caseDicts/postProcessing/visualization/streamlines.cfg b/etc/caseDicts/postProcessing/visualization/streamlines.cfg index 2ffcf1ee8f..86208b5ce1 100644 --- a/etc/caseDicts/postProcessing/visualization/streamlines.cfg +++ b/etc/caseDicts/postProcessing/visualization/streamlines.cfg @@ -13,7 +13,7 @@ executeControl writeTime; writeControl writeTime; setFormat vtk; -trackForward true; +direction forward; // (forward | backward | bidirectional) lifeTime 10000; nSubCycle 5; diff --git a/src/functionObjects/field/streamLine/streamLine.C b/src/functionObjects/field/streamLine/streamLine.C index 8cc0b59f60..b93ffe9f7f 100644 --- a/src/functionObjects/field/streamLine/streamLine.C +++ b/src/functionObjects/field/streamLine/streamLine.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation - Copyright (C) 2015-2020 OpenCFD Ltd. + Copyright (C) 2015-2022 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -92,22 +92,14 @@ void Foam::functionObjects::streamLine::track() Log << " seeded " << nSeeds << " particles" << endl; - // Read or lookup fields - PtrList vsFlds; + // Field interpolators + // Velocity interpolator PtrList> vsInterp; - PtrList vvFlds; PtrList> vvInterp; - label UIndex = -1; - - initInterpolations + refPtr> UInterp ( - nSeeds, - UIndex, - vsFlds, - vsInterp, - vvFlds, - vvInterp + initInterpolations(nSeeds, vsInterp, vvInterp) ); // Additional particle info @@ -116,7 +108,7 @@ void Foam::functionObjects::streamLine::track() particles, vsInterp, vvInterp, - UIndex, // index of U in vvInterp + UInterp.cref(), // velocity interpolator (possibly within vvInterp) nSubCycle_, // automatic track control:step through cells in steps? trackLength_, // fixed track length @@ -174,7 +166,6 @@ bool Foam::functionObjects::streamLine::read(const dictionary& dict) trackLength_ = VGREAT; nSubCycle_ = max(nSubCycle_, 1); - Info<< " automatic track length specified through" << " number of sub cycles : " << nSubCycle_ << nl << endl; diff --git a/src/functionObjects/field/streamLine/streamLine.H b/src/functionObjects/field/streamLine/streamLine.H index 4d08615edb..2097274f75 100644 --- a/src/functionObjects/field/streamLine/streamLine.H +++ b/src/functionObjects/field/streamLine/streamLine.H @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2015-2020 OpenCFD Ltd. + Copyright (C) 2015-2022 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -52,13 +52,15 @@ Usage type streamLine; libs (fieldFunctionObjects); - // Mandatory entries (runtime modifiable) - U ; - fields ( ... ); - setFormat vtk; - direction bidirectional; - lifeTime 10000; + // Optional entries + U ; + direction forward; cloud particleTracks; + + // Mandatory entries (runtime modifiable) + fields ( ... ); + setFormat vtk; + lifeTime 10000; seedSampleSet { type uniform; @@ -87,20 +89,20 @@ Usage Property | Description | Type | Req'd | Dflt type | Type name: streamLine | word | yes | - libs | Library name: fieldFunctionObjects | word | yes | - - U | Name of tracking velocity field | word | yes | - + U | Name of tracking velocity field | word | no | U fields | Names of operand fields to sample | wordList | yes | - setFormat | Type of output data | word | yes | - - direction | Direction to track | vector | yes | - + direction | Direction (enum) to track | word | no | forward lifetime | Maximum number of particle tracking steps | label | yes | - - cloud | Name of cloud | word | yes | - - seedSampleSet| Name of seeding method (see below) | word | yes | - - bounds | Bounding box to trim tracks | vector | no | invertedBox + cloud | Cloud name to use for streamlines | word | no | typeName + seedSampleSet| Seeding description (see below) | dict | yes | - + bounds | Bounding box to trim tracks | vector pair | no | - trackLength | Tracking segment length | scalar | no | VGREAT nSubCycle | Number of tracking steps per cell | label | no | 1 interpolationScheme | Interp. scheme for sample | word | no | cellPoint \endtable - Options for the \c seedSampleSet entry: + Example types for the \c seedSampleSet sub-dict: \verbatim uniform | uniform particle seeding cloud | cloud of points @@ -120,9 +122,9 @@ Usage Options for the \c direction entry: \verbatim - bidirectional forward backward + bidirectional \endverbatim The inherited entries are elaborated in: @@ -147,8 +149,8 @@ SourceFiles \*---------------------------------------------------------------------------*/ -#ifndef functionObjects_streamLine_H -#define functionObjects_streamLine_H +#ifndef Foam_functionObjects_streamLine_H +#define Foam_functionObjects_streamLine_H #include "streamLineBase.H" @@ -157,6 +159,7 @@ SourceFiles namespace Foam { +// Forward Declarations class objectRegistry; class dictionary; @@ -169,7 +172,7 @@ namespace functionObjects class streamLine : - public streamLineBase + public functionObjects::streamLineBase { // Private Data diff --git a/src/functionObjects/field/streamLine/streamLineBase.C b/src/functionObjects/field/streamLine/streamLineBase.C index 2540476981..d29e436313 100644 --- a/src/functionObjects/field/streamLine/streamLineBase.C +++ b/src/functionObjects/field/streamLine/streamLineBase.C @@ -139,17 +139,16 @@ Foam::functionObjects::streamLineBase::wallPatch() const } -void Foam::functionObjects::streamLineBase::initInterpolations +Foam::refPtr> +Foam::functionObjects::streamLineBase::initInterpolations ( const label nSeeds, - label& UIndex, - PtrList& vsFlds, PtrList>& vsInterp, - PtrList& vvFlds, PtrList>& vvInterp ) { - // Read fields + refPtr> UInterp; + label nScalar = 0; label nVector = 0; @@ -166,7 +165,7 @@ void Foam::functionObjects::streamLineBase::initInterpolations else { FatalErrorInFunction - << "Cannot find field " << fieldName << nl + << "Cannot find scalar/vector field " << fieldName << nl << "Valid scalar fields: " << flatOutput(mesh_.sortedNames()) << nl << "Valid vector fields: " @@ -174,9 +173,9 @@ void Foam::functionObjects::streamLineBase::initInterpolations << exit(FatalError); } } - vsInterp.setSize(nScalar); + vsInterp.resize(nScalar); + vvInterp.resize(nVector); nScalar = 0; - vvInterp.setSize(nVector); nVector = 0; for (const word& fieldName : fields_) @@ -186,76 +185,77 @@ void Foam::functionObjects::streamLineBase::initInterpolations const volScalarField& f = lookupObject(fieldName); vsInterp.set ( - nScalar++, - interpolation::New - ( - interpolationScheme_, - f - ) + nScalar, + interpolation::New(interpolationScheme_, f) ); + ++nScalar; } else if (foundObject(fieldName)) { const volVectorField& f = lookupObject(fieldName); - if (f.name() == UName_) - { - UIndex = nVector; - } - vvInterp.set ( - nVector++, - interpolation::New - ( - interpolationScheme_, - f - ) + nVector, + interpolation::New(interpolationScheme_, f) ); + + if (f.name() == UName_) + { + // Velocity is part of sampled velocity fields + UInterp.cref(vvInterp[nVector]); + } + + ++nVector; } } + if (!UInterp) + { + // Velocity was not in sampled velocity fields + UInterp.reset + ( + interpolation::New + ( + interpolationScheme_, + // Fatal if missing + lookupObject(UName_) + ) + ); + } + // Store the names - scalarNames_.setSize(vsInterp.size()); + scalarNames_.resize(vsInterp.size()); forAll(vsInterp, i) { scalarNames_[i] = vsInterp[i].psi().name(); } - vectorNames_.setSize(vvInterp.size()); + vectorNames_.resize(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) - { - FatalErrorInFunction - << "Cannot find field to move particles with : " << UName_ << nl - << "This field has to be present in the sampled fields " << fields_ - << " and in the objectRegistry." - << exit(FatalError); - } - // Sampled data // ~~~~~~~~~~~~ // Size to maximum expected sizes. allTracks_.clear(); allTracks_.setCapacity(nSeeds); - allScalars_.setSize(vsInterp.size()); + allScalars_.resize(vsInterp.size()); forAll(allScalars_, i) { allScalars_[i].clear(); allScalars_[i].setCapacity(nSeeds); } - allVectors_.setSize(vvInterp.size()); + allVectors_.resize(vvInterp.size()); forAll(allVectors_, i) { allVectors_[i].clear(); allVectors_[i].setCapacity(nSeeds); } + + return UInterp; } @@ -877,22 +877,13 @@ bool Foam::functionObjects::streamLineBase::read(const dictionary& dict) Info<< type() << " " << name() << ":" << nl; UName_ = dict.getOrDefault("U", "U"); + Info<< " Employing velocity field " << UName_ << endl; if (fields_.empty()) { dict.readEntry("fields", fields_); - - if (!fields_.found(UName_)) - { - FatalIOErrorInFunction(dict) - << "Velocity field for tracking " << UName_ - << " should be present in the list of fields " << fields_ - << exit(FatalIOError); - } } - Info<< " Employing velocity field " << UName_ << endl; - bool trackForward; if (dict.readIfPresent("trackForward", trackForward)) { @@ -912,7 +903,12 @@ bool Foam::functionObjects::streamLineBase::read(const dictionary& dict) } else { - trackDir_ = trackDirTypeNames.get("direction", dict); + trackDir_ = trackDirTypeNames.getOrDefault + ( + "direction", + dict, + trackDirType::FORWARD + ); } dict.readEntry("lifeTime", lifeTime_); if (lifeTime_ < 1) diff --git a/src/functionObjects/field/streamLine/streamLineBase.H b/src/functionObjects/field/streamLine/streamLineBase.H index 75c8d4ad62..bd9bafe8e6 100644 --- a/src/functionObjects/field/streamLine/streamLineBase.H +++ b/src/functionObjects/field/streamLine/streamLineBase.H @@ -75,9 +75,9 @@ public: //- Enumeration defining the track direction enum trackDirType : char { - FORWARD, - BACKWARD, - BIDIRECTIONAL + FORWARD, //!< Use "forward" tracking + BACKWARD, //!< Use "backward" tracking + BIDIRECTIONAL, //!< Use "bidirectional" tracking }; //- Names for the trackDir @@ -104,7 +104,7 @@ protected: //- Interpolation scheme to use word interpolationScheme_; - //- Whether to use +u or -u or both + //- Whether to use +U, -U or both trackDirType trackDir_; //- Maximum lifetime (= number of cells) of particle @@ -129,7 +129,7 @@ protected: wordList vectorNames_; - // Demand driven + // Demand Driven //- File writer for tracks data mutable autoPtr trackWriterPtr_; @@ -159,14 +159,13 @@ protected: //- Construct patch out of all wall patch faces autoPtr wallPatch() const; - //- Initialise fields, interpolators and track storage - void initInterpolations + //- Initialise interpolators and track storage + // Return velocity interpolator: standalone or part of vector + // interpolators + refPtr> initInterpolations ( const label nSeeds, - label& UIndex, - PtrList& vsFlds, PtrList>& vsInterp, - PtrList& vvFlds, PtrList>& vvInterp ); diff --git a/src/functionObjects/field/streamLine/streamLineParticle.C b/src/functionObjects/field/streamLine/streamLineParticle.C index 880a90912f..ed61271c1a 100644 --- a/src/functionObjects/field/streamLine/streamLineParticle.C +++ b/src/functionObjects/field/streamLine/streamLineParticle.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2019 OpenCFD Ltd. + Copyright (C) 2019-2022 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -35,48 +35,67 @@ License Foam::vector Foam::streamLineParticle::interpolateFields ( const trackingData& td, - const point& position, - const label celli, - const label facei + const barycentric& tetCoords, + const tetIndices& tetIs ) { - if (celli == -1) + if (tetIs.cell() < 0) { FatalErrorInFunction - << "Cell:" << celli << abort(FatalError); + << "Invalid cell (-1)" << abort(FatalError); } - sampledScalars_.setSize(td.vsInterp_.size()); - forAll(td.vsInterp_, scalari) + const point position + ( + tetIs.barycentricToPoint(this->mesh(), tetCoords) + ); + + bool foundU = false; + vector U(Zero); + + // If current position is different + if + ( + sampledPositions_.empty() + || magSqr(sampledPositions_.last() - position) > Foam::sqr(SMALL) + ) { - sampledScalars_[scalari].append - ( - td.vsInterp_[scalari].interpolate + // Store new location + sampledPositions_.append(position); + + // Scalar fields + sampledScalars_.resize(td.vsInterp_.size()); + forAll(td.vsInterp_, i) + { + sampledScalars_[i].append ( - position, - celli, - facei - ) - ); + td.vsInterp_[i].interpolate(tetCoords, tetIs, tetIs.face()) + ); + } + + // Vector fields + sampledVectors_.resize(td.vvInterp_.size()); + forAll(td.vvInterp_, i) + { + sampledVectors_[i].append + ( + td.vvInterp_[i].interpolate(tetCoords, tetIs, tetIs.face()) + ); + + if (td.vvInterp_.get(i) == &(td.UInterp_)) + { + foundU = true; + U = sampledVectors_[i].last(); + } + } } - sampledVectors_.setSize(td.vvInterp_.size()); - forAll(td.vvInterp_, vectori) + if (!foundU) { - sampledVectors_[vectori].append - ( - td.vvInterp_[vectori].interpolate - ( - position, - celli, - facei - ) - ); + U = td.UInterp_.interpolate(tetCoords, tetIs, tetIs.face()); } - const DynamicList& U = sampledVectors_[td.UIndex_]; - - return U.last(); + return U; } @@ -116,12 +135,12 @@ Foam::streamLineParticle::streamLineParticle >> sampledPositions_ >> sampledScalars >> sampledVectors; - sampledScalars_.setSize(sampledScalars.size()); + sampledScalars_.resize(sampledScalars.size()); forAll(sampledScalars, i) { sampledScalars_[i].transfer(sampledScalars[i]); } - sampledVectors_.setSize(sampledVectors.size()); + sampledVectors_.resize(sampledVectors.size()); forAll(sampledVectors, i) { sampledVectors_[i].transfer(sampledVectors[i]); @@ -162,6 +181,7 @@ bool Foam::streamLineParticle::move while (td.keepParticle && !td.switchProcessor && lifeTime_ > 0) { + // Set the lagrangian time-step scalar dt = maxDt; // Cross cell in steps: @@ -171,16 +191,11 @@ bool Foam::streamLineParticle::move { --lifeTime_; - // Store current position and sampled velocity. - sampledPositions_.append(position()); - vector U = interpolateFields(td, position(), cell(), face()); + // Store current position, return sampled velocity + vector U = + interpolateFields(td, coordinates(), currentTetIndices()); - if (!trackForward_) - { - U = -U; - } - - scalar magU = mag(U); + const scalar magU = mag(U); if (magU < SMALL) { @@ -189,6 +204,11 @@ bool Foam::streamLineParticle::move break; } + if (!trackForward_) + { + U = -U; + } + U /= magU; if (td.trackLength_ < GREAT) @@ -240,8 +260,7 @@ bool Foam::streamLineParticle::move else { // Normal exit. Store last position and fields - sampledPositions_.append(position()); - interpolateFields(td, position(), cell(), face()); + (void)interpolateFields(td, coordinates(), currentTetIndices()); if (debug) { @@ -252,21 +271,20 @@ bool Foam::streamLineParticle::move } // Transfer particle data into trackingData. - td.allPositions_.append(vectorList()); - vectorList& top = td.allPositions_.last(); - top.transfer(sampledPositions_); + { + td.allPositions_.append(vectorList()); + td.allPositions_.last().transfer(sampledPositions_); + } forAll(sampledScalars_, i) { td.allScalars_[i].append(scalarList()); - scalarList& top = td.allScalars_[i].last(); - top.transfer(sampledScalars_[i]); + td.allScalars_[i].last().transfer(sampledScalars_[i]); } forAll(sampledVectors_, i) { td.allVectors_[i].append(vectorList()); - vectorList& top = td.allVectors_[i].last(); - top.transfer(sampledVectors_[i]); + td.allVectors_[i].last().transfer(sampledVectors_[i]); } } @@ -406,6 +424,7 @@ void Foam::streamLineParticle::writeFields(const Cloud& c) particle::writeFields(c); const label np = c.size(); + const bool valid = c.size(); IOField