/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation Copyright (C) 2021 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . Application particleTracks Group grpPostProcessingUtilities Description Generate particle tracks for cases that were computed using a tracked-parcel-type cloud. \*---------------------------------------------------------------------------*/ #include "argList.H" #include "Cloud.H" #include "IOdictionary.H" #include "fvMesh.H" #include "Time.H" #include "timeSelector.H" #include "OFstream.H" #include "passiveParticleCloud.H" #include "writer.H" #include "ListOps.H" #define createTrack(field, trackValues) \ createTrackField \ ( \ field, \ sampleFrequency, \ maxPositions, \ startIds, \ allOrigProcs, \ allOrigIds, \ trackValues \ ); #define setFields(fields, fieldNames) \ setTrackFields \ ( \ obr, \ fields, \ fieldNames, \ nTracks, \ startIds, \ allOrigProcs, \ allOrigIds, \ maxPositions, \ sampleFrequency \ ); #define writeFields(fields, fieldNames, tracks, times, dirs) \ writeTrackFields \ ( \ fields, \ fieldNames, \ tracks, \ times, \ dirs, \ setFormat, \ formatOptions, \ cloudName \ ); using namespace Foam; template void createTrackField ( const Field& values, const label sampleFrequency, const label maxPositions, const labelList& startIds, const List& allOrigProcs, const List& allOrigIds, List>& trackValues ) { List> procField(Pstream::nProcs()); procField[Pstream::myProcNo()] = values; Pstream::gatherList(procField); if (!Pstream::master()) { return; } const label nTracks = trackValues.size(); forAll(procField, proci) { forAll(procField[proci], i) { const label globalId = startIds[allOrigProcs[proci][i]] + allOrigIds[proci][i]; if (globalId % sampleFrequency == 0) { const label trackId = globalId/sampleFrequency; if ( trackId < nTracks && trackValues[trackId].size() < maxPositions ) { trackValues[trackId].append(procField[proci][i]); } } } } } template void writeTrackFields ( List>>& fieldValues, const wordList& fieldNames, const PtrList& tracks, const List& times, const List& dirs, const word& setFormat, const dictionary& formatOptions, const word& cloudName ) { if (fieldValues.empty()) { return; } auto writerPtr = writer::New(setFormat, formatOptions); const fileName outFile(writerPtr().getFileName(tracks[0], wordList(0))); const fileName outPath ( functionObject::outputPrefix/cloud::prefix/cloudName/"particleTracks" ); mkDir(outPath); OFstream os(outPath/(pTraits::typeName & "tracks." + outFile.ext())); Info<< "Writing " << pTraits::typeName << " particle tracks in " << setFormat << " format to " << os.name() << endl; List>> fields(fieldValues.size()); forAll(fields, fieldi) { fields[fieldi].setSize(fieldValues[fieldi].size()); forAll(fields[fieldi], tracki) { fields[fieldi][tracki].transfer(fieldValues[fieldi][tracki]); } } writerPtr().write(true, times, tracks, fieldNames, fields, os); } template Foam::label setTrackFields ( const objectRegistry& obr, List>>& fields, List& fieldNames, const label nTracks, const labelList& startIds, const List& allOrigProcs, const List& allOrigIds, const label maxPositions, const label sampleFrequency ) { const auto availableFieldPtrs = obr.lookupClass>(); fieldNames = availableFieldPtrs.toc(); if (Pstream::parRun()) { Pstream::combineGather(fieldNames, ListOps::uniqueEqOp()); Pstream::combineScatter(fieldNames); Foam::sort(fieldNames); } const label nFields = fieldNames.size(); if (fields.empty()) { fields.setSize(nFields); fieldNames.setSize(nFields); forAll(fields, i) { fields[i].setSize(nTracks); } } forAll(fieldNames, fieldi) { const word& fieldName = fieldNames[fieldi]; const auto* fldPtr = obr.cfindObject>(fieldName); createTrack ( fldPtr ? static_cast>(*fldPtr) : Field(), fields[fieldi] ); } return nFields; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // int main(int argc, char *argv[]) { argList::addNote ( "Generate a file of particle tracks for cases that were" " computed using a tracked-parcel-type cloud" ); timeSelector::addOptions(); #include "addRegionOption.H" #include "setRootCase.H" #include "createTime.H" instantList timeDirs = timeSelector::select0(runTime, args); #include "createNamedMesh.H" #include "createFields.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // const fileName vtkPath(runTime.rootPath()/runTime.globalCaseName()/"VTK"); mkDir(vtkPath); Info<< "Scanning times to determine track data for cloud " << cloudName << nl << endl; labelList maxIds(Pstream::nProcs(), -1); forAll(timeDirs, timei) { runTime.setTime(timeDirs[timei], timei); Info<< "Time = " << runTime.timeName() << endl; Info<< " Reading particle positions" << endl; passiveParticleCloud myCloud(mesh, cloudName); Info<< " Read " << returnReduce(myCloud.size(), sumOp