/*========================================================================= Program: Visualization Toolkit Module: vtkTemporalStreamTracer.h Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /** * @class vtkTemporalStreamTracer * @brief A Parallel Particle tracer for unsteady vector fields * * vtkTemporalStreamTracer is a filter that integrates a vector field to generate * * * @sa * vtkRibbonFilter vtkRuledSurfaceFilter vtkInitialValueProblemSolver * vtkRungeKutta2 vtkRungeKutta4 vtkRungeKutta45 vtkStreamTracer * * This class is deprecated. * Use instead one of the following classes: vtkParticleTracerBase * vtkParticleTracer vtkParticlePathFilter vtkStreaklineFilter * See https://blog.kitware.com/improvements-in-path-tracing-in-vtk/ */ #ifndef vtkTemporalStreamTracer_h #define vtkTemporalStreamTracer_h #include "vtkConfigure.h" // For legacy defines #include "vtkSetGet.h" // For legacy macros #ifndef VTK_LEGACY_REMOVE #include "vtkFiltersFlowPathsModule.h" // For export macro #include "vtkSmartPointer.h" // For protected ivars. #include "vtkStreamTracer.h" #include // STL Header #include // STL Header class vtkMultiProcessController; class vtkMultiBlockDataSet; class vtkDataArray; class vtkDoubleArray; class vtkGenericCell; class vtkIntArray; class vtkTemporalInterpolatedVelocityField; class vtkPoints; class vtkCellArray; class vtkDoubleArray; class vtkFloatArray; class vtkIntArray; class vtkCharArray; class vtkAbstractParticleWriter; namespace vtkTemporalStreamTracerNamespace { typedef struct { double x[4]; } Position; typedef struct { // These are used during iteration Position CurrentPosition; int CachedDataSetId[2]; vtkIdType CachedCellId[2]; int LocationState; // These are computed scalars we might display int SourceID; int TimeStepAge; int InjectedPointId; int InjectedStepId; int UniqueParticleId; // These are useful to track for debugging etc int ErrorCode; float age; // these are needed across time steps to compute vorticity float rotation; float angularVel; float time; float speed; } ParticleInformation; typedef std::vector ParticleVector; typedef ParticleVector::iterator ParticleIterator; typedef std::list ParticleDataList; typedef ParticleDataList::iterator ParticleListIterator; }; class VTKFILTERSFLOWPATHS_EXPORT vtkTemporalStreamTracer : public vtkStreamTracer { public: vtkTypeMacro(vtkTemporalStreamTracer, vtkStreamTracer); void PrintSelf(ostream& os, vtkIndent indent) override; /** * Construct object using 2nd order Runge Kutta */ static vtkTemporalStreamTracer* New(); //@{ /** * Set/Get the TimeStep. This is the primary means of advancing * the particles. The TimeStep should be animated and this will drive * the pipeline forcing timesteps to be fetched from upstream. */ vtkSetMacro(TimeStep, unsigned int); vtkGetMacro(TimeStep, unsigned int); //@} //@{ /** * To get around problems with the Paraview Animation controls * we can just animate the time step and ignore the TIME_ requests */ vtkSetMacro(IgnorePipelineTime, vtkTypeBool); vtkGetMacro(IgnorePipelineTime, vtkTypeBool); vtkBooleanMacro(IgnorePipelineTime, vtkTypeBool); //@} //@{ /** * If the data source does not have the correct time values * present on each time step - setting this value to non unity can * be used to adjust the time step size from 1s pre step to * 1x_TimeStepResolution : Not functional in this version. * Broke it @todo, put back time scaling */ vtkSetMacro(TimeStepResolution, double); vtkGetMacro(TimeStepResolution, double); //@} //@{ /** * When animating particles, it is nice to inject new ones every Nth step * to produce a continuous flow. Setting ForceReinjectionEveryNSteps to a * non zero value will cause the particle source to reinject particles * every Nth step even if it is otherwise unchanged. * Note that if the particle source is also animated, this flag will be * redundant as the particles will be reinjected whenever the source changes * anyway */ vtkSetMacro(ForceReinjectionEveryNSteps, int); vtkGetMacro(ForceReinjectionEveryNSteps, int); //@} enum Units { TERMINATION_TIME_UNIT, TERMINATION_STEP_UNIT }; //@{ /** * Setting TerminationTime to a positive value will cause particles * to terminate when the time is reached. Use a vlue of zero to * disable termination. The units of time should be consistent with the * primary time variable. */ vtkSetMacro(TerminationTime, double); vtkGetMacro(TerminationTime, double); //@} //@{ /** * The units of TerminationTime may be actual 'Time' units as described * by the data, or just TimeSteps of iteration. */ vtkSetMacro(TerminationTimeUnit, int); vtkGetMacro(TerminationTimeUnit, int); void SetTerminationTimeUnitToTimeUnit() { this->SetTerminationTimeUnit(TERMINATION_TIME_UNIT); } void SetTerminationTimeUnitToStepUnit() { this->SetTerminationTimeUnit(TERMINATION_STEP_UNIT); } //@} //@{ /** * if StaticSeeds is set and the mesh is static, * then every time particles are injected we can re-use the same * injection information. We classify particles according to * processor just once before start. * If StaticSeeds is set and a moving seed source is specified * the motion will be ignored and results will not be as expected. */ vtkSetMacro(StaticSeeds, vtkTypeBool); vtkGetMacro(StaticSeeds, vtkTypeBool); vtkBooleanMacro(StaticSeeds, vtkTypeBool); //@} //@{ /** * if StaticMesh is set, many optimizations for cell caching * can be assumed. if StaticMesh is not set, the algorithm * will attempt to find out if optimizations can be used, but * setting it to true will force all optimizations. * Do not Set StaticMesh to true if a dynamic mesh is being used * as this will invalidate all results. */ vtkSetMacro(StaticMesh, vtkTypeBool); vtkGetMacro(StaticMesh, vtkTypeBool); vtkBooleanMacro(StaticMesh, vtkTypeBool); //@} //@{ /** * Set/Get the Writer associated with this Particle Tracer * Ideally a parallel IO capable vtkH5PartWriter should be used * which will collect particles from all parallel processes * and write them to a single HDF5 file. */ virtual void SetParticleWriter(vtkAbstractParticleWriter* pw); vtkGetObjectMacro(ParticleWriter, vtkAbstractParticleWriter); //@} //@{ /** * Set/Get the filename to be used with the particle writer when * dumping particles to disk */ vtkSetStringMacro(ParticleFileName); vtkGetStringMacro(ParticleFileName); //@} //@{ /** * Set/Get the filename to be used with the particle writer when * dumping particles to disk */ vtkSetMacro(EnableParticleWriting, vtkTypeBool); vtkGetMacro(EnableParticleWriting, vtkTypeBool); vtkBooleanMacro(EnableParticleWriting, vtkTypeBool); //@} //@{ /** * Provide support for multiple see sources */ void AddSourceConnection(vtkAlgorithmOutput* input); void RemoveAllSources(); //@} protected: VTK_LEGACY(vtkTemporalStreamTracer()); ~vtkTemporalStreamTracer() override; // // Make sure the pipeline knows what type we expect as input // int FillInputPortInformation(int port, vtkInformation* info) override; // // The usual suspects // vtkTypeBool ProcessRequest(vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector) override; // // Store any information we need in the output and fetch what we can // from the input // int RequestInformation(vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector) override; // // Compute input time steps given the output step // int RequestUpdateExtent(vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector) override; // // what the pipeline calls for each time step // int RequestData(vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector) override; // // these routines are internally called to actually generate the output // virtual int ProcessInput(vtkInformationVector** inputVector); virtual int GenerateOutput( vtkInformationVector** inputVector, vtkInformationVector* outputVector); // // Initialization of input (vector-field) geometry // int InitializeInterpolator(); int SetTemporalInput(vtkDataObject* td, int index); // /** * inside our data. Add good ones to passed list and set count to the * number that passed */ void TestParticles(vtkTemporalStreamTracerNamespace::ParticleVector& candidates, vtkTemporalStreamTracerNamespace::ParticleVector& passed, int& count); /** * all the injection/seed points according to which processor * they belong to. This saves us retesting at every injection time * providing 1) The volumes are static, 2) the seed points are static * If either are non static, then this step is skipped. */ virtual void AssignSeedsToProcessors(vtkDataSet* source, int sourceID, int ptId, vtkTemporalStreamTracerNamespace::ParticleVector& LocalSeedPoints, int& LocalAssignedCount); /** * give each one a unique ID. We need to use MPI to find out * who is using which numbers. */ virtual void AssignUniqueIds(vtkTemporalStreamTracerNamespace::ParticleVector& LocalSeedPoints); /** * and sending between processors, into a list, which is used as the master * list on this processor */ void UpdateParticleList(vtkTemporalStreamTracerNamespace::ParticleVector& candidates); /** * this is used during classification of seed points and also between iterations * of the main loop as particles leave each processor domain */ virtual void TransmitReceiveParticles( vtkTemporalStreamTracerNamespace::ParticleVector& outofdomain, vtkTemporalStreamTracerNamespace::ParticleVector& received, bool removeself); /** * particle between the two times supplied. */ void IntegrateParticle(vtkTemporalStreamTracerNamespace::ParticleListIterator& it, double currenttime, double terminationtime, vtkInitialValueProblemSolver* integrator); /** * and sent to the other processors for possible continuation. * These routines manage the collection and sending after each main iteration. * RetryWithPush adds a small pusj to aparticle along it's current velocity * vector, this helps get over cracks in dynamic/rotating meshes */ bool RetryWithPush( vtkTemporalStreamTracerNamespace::ParticleInformation& info, double velocity[3], double delT); // if the particle is added to send list, then returns value is 1, // if it is kept on this process after a retry return value is 0 bool SendParticleToAnotherProcess( vtkTemporalStreamTracerNamespace::ParticleInformation& info, double point1[4], double delT); void AddParticleToMPISendList(vtkTemporalStreamTracerNamespace::ParticleInformation& info); /** * In dnamic meshes, particles might leave the domain and need to be extrapolated across * a gap between the meshes before they re-renter another domain * dodgy rotating meshes need special care.... */ bool ComputeDomainExitLocation( double pos[4], double p2[4], double intersection[4], vtkGenericCell* cell); // // // Track internally which round of RequestData it is--between 0 and 2 int RequestIndex; // Track which process we are int UpdatePieceId; int UpdateNumPieces; // Important for Caching of Cells/Ids/Weights etc int AllFixedGeometry; vtkTypeBool StaticMesh; vtkTypeBool StaticSeeds; // Support 'pipeline' time or manual SetTimeStep unsigned int TimeStep; unsigned int ActualTimeStep; vtkTypeBool IgnorePipelineTime; unsigned int NumberOfInputTimeSteps; std::vector InputTimeValues; std::vector OutputTimeValues; // more time management double EarliestTime; double CurrentTimeSteps[2]; double TimeStepResolution; // Particle termination after time double TerminationTime; int TerminationTimeUnit; // Particle injection+Reinjection int ForceReinjectionEveryNSteps; bool ReinjectionFlag; int ReinjectionCounter; vtkTimeStamp ParticleInjectionTime; // Particle writing to disk vtkAbstractParticleWriter* ParticleWriter; char* ParticleFileName; vtkTypeBool EnableParticleWriting; // The main lists which are held during operation- between time step updates unsigned int NumberOfParticles; vtkTemporalStreamTracerNamespace::ParticleDataList ParticleHistories; vtkTemporalStreamTracerNamespace::ParticleVector LocalSeeds; // // Scalar arrays that are generated as each particle is updated // vtkSmartPointer ParticleAge; vtkSmartPointer ParticleIds; vtkSmartPointer ParticleSourceIds; vtkSmartPointer InjectedPointIds; vtkSmartPointer InjectedStepIds; vtkSmartPointer ErrorCodeArray; vtkSmartPointer ParticleVorticity; vtkSmartPointer ParticleRotation; vtkSmartPointer ParticleAngularVel; vtkSmartPointer cellVectors; vtkSmartPointer OutputPointData; int InterpolationCount; // The output geometry vtkSmartPointer ParticleCells; vtkSmartPointer OutputCoordinates; // List used for transmitting between processors during parallel operation vtkTemporalStreamTracerNamespace::ParticleVector MPISendList; // The velocity interpolator vtkSmartPointer Interpolator; // The input datasets which are stored by time step 0 and 1 vtkSmartPointer InputDataT[2]; vtkSmartPointer DataReferenceT[2]; // Cache bounds info for each dataset we will use repeatedly typedef struct { double b[6]; } bounds; std::vector CachedBounds[2]; // utility function we use to test if a point is inside any of our local datasets bool InsideBounds(double point[]); // global Id counter used to give particles a stamp vtkIdType UniqueIdCounter; vtkIdType UniqueIdCounterMPI; // for debugging only; int substeps; private: /** * Hide this because we require a new interpolator type */ void SetInterpolatorPrototype(vtkAbstractInterpolatedVelocityField*) {} private: vtkTemporalStreamTracer(const vtkTemporalStreamTracer&) = delete; void operator=(const vtkTemporalStreamTracer&) = delete; }; #endif // VTK_LEGACY_REMOVE #endif