/*========================================================================= Program: Visualization Toolkit Module: vtkLagrangianBasicIntegrationModel.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 vtkLagrangianBasicIntegrationModel * @brief vtkFunctionSet abstract implementation to be used * in the vtkLagrangianParticleTracker integrator. * * This vtkFunctionSet abstract implementation * is meant to be used as a parameter of vtkLagrangianParticleTracker. * It manages multiple dataset locators in order to evaluate the * vtkFunctionSet::FunctionValues method. * The actual FunctionValues implementation should be found in the class inheriting * this class. * Input Arrays to process are expected as follows: * Index 0 : "SurfaceType" array of surface input of the particle tracker * * Inherited classes MUST implement * int FunctionValues(vtkDataSet* detaSet, vtkIdType cellId, double* weights, * double * x, double * f); * to define how the integration works. * * Inherited classes could reimplement InitializeVariablesParticleData and * InsertVariablesParticleData to add new UserVariables to integrate with. * * Inherited classes could reimplement InteractWithSurface or other surface interaction methods * to change the way particles interact with surfaces. * * Inherited classes could reimplement IntersectWithLine to use a specific algorithm * to intersect particles and surface cells. * * Inherited classes could reimplement CheckFreeFlightTermination to set * the way particles terminate in free flight. * * Inherited classes could reimplement Initialize*Data and Insert*Data in order * to customize the output of the tracker * * @sa * vtkLagrangianParticleTracker vtkLagrangianParticle * vtkLagrangianMatidaIntegrationModel */ #ifndef vtkLagrangianBasicIntegrationModel_h #define vtkLagrangianBasicIntegrationModel_h #include "vtkFiltersFlowPathsModule.h" // For export macro #include "vtkFunctionSet.h" #include "vtkNew.h" // For arrays #include "vtkWeakPointer.h" // For weak pointer #include // for array indexes #include // for mutexes #include // for new particles class vtkAbstractArray; class vtkAbstractCellLocator; class vtkCell; class vtkCellData; class vtkDataArray; class vtkDataObject; class vtkDataSet; class vtkDataSetsType; class vtkDoubleArray; class vtkFieldData; class vtkGenericCell; class vtkInitialValueProblemSolver; class vtkIntArray; class vtkLagrangianParticle; class vtkLagrangianParticleTracker; class vtkLocatorsType; class vtkMultiBlockDataSet; class vtkMultiPieceDataSet; class vtkPointData; class vtkPolyData; class vtkStringArray; class vtkSurfaceType; struct vtkLagrangianThreadedData; class VTKFILTERSFLOWPATHS_EXPORT vtkLagrangianBasicIntegrationModel : public vtkFunctionSet { public: vtkTypeMacro(vtkLagrangianBasicIntegrationModel, vtkFunctionSet); void PrintSelf(ostream& os, vtkIndent indent) override; typedef enum SurfaceType { SURFACE_TYPE_MODEL = 0, SURFACE_TYPE_TERM = 1, SURFACE_TYPE_BOUNCE = 2, SURFACE_TYPE_BREAK = 3, SURFACE_TYPE_PASS = 4 } SurfaceType; typedef enum VariableStep { VARIABLE_STEP_PREV = -1, VARIABLE_STEP_CURRENT = 0, VARIABLE_STEP_NEXT = 1, } VariableStep; typedef std::pair PassThroughParticlesItem; typedef std::queue PassThroughParticlesType; using Superclass::FunctionValues; /** * Evaluate integration model velocity f at position x. * Look for the cell containing the position x in all its added datasets * if found this will call * FunctionValues(vtkDataSet* detaSet, vtkIdType cellId, double* x, double* f) * This method is thread safe. */ int FunctionValues(double* x, double* f, void* userData) override; //@{ /** * Set/Get the locator used to locate cells in the datasets. * Only the locator class matter here, as it is used only to * create NewInstance of it. * Default is a vtkCellLocator. */ virtual void SetLocator(vtkAbstractCellLocator* locator); vtkGetObjectMacro(Locator, vtkAbstractCellLocator); //@} //@{ /** * Get the state of the current locators */ vtkGetMacro(LocatorsBuilt, bool); vtkSetMacro(LocatorsBuilt, bool); //@} /** * Set the parent tracker. */ virtual void SetTracker(vtkLagrangianParticleTracker* Tracker); //@{ /** * Add a dataset to locate cells in * This create a specific locator for the provided dataset * using the Locator member of this class * The surface flag allow to manage surfaces datasets for surface interaction * instead of flow datasets * surfaceFlatIndex, used only with composite surface, in order to identify the * flatIndex of the surface for particle interaction */ virtual void AddDataSet( vtkDataSet* dataset, bool surface = false, unsigned int surfaceFlatIndex = 0); virtual void ClearDataSets(bool surface = false); //@} //@{ /** * Set/Get the Use of initial integration input array to process */ vtkSetMacro(UseInitialIntegrationTime, bool); vtkGetMacro(UseInitialIntegrationTime, bool); vtkBooleanMacro(UseInitialIntegrationTime, bool); //@} //@{ /** * Get the tolerance to use with this model. */ vtkGetMacro(Tolerance, double); //@} /** * Interact the current particle with a surfaces * Return a particle to record as interaction point if not nullptr * Uses SurfaceType array from the intersected surface cell * to compute the interaction. * MODEL : * vtkLagrangianBasicIntegrationModel::InteractWithSurface * method will be used, usually defined in inherited classes * TERM : * vtkLagrangianBasicIntegrationModel::Terminate method will be used * BOUNCE : * vtkLagrangianBasicIntegrationModel::Bounce method will be used * BREAK_UP : * vtkLagrangianBasicIntegrationModel::BreakUp method will be used * PASS : The interaction will be recorded * with no effect on the particle */ virtual vtkLagrangianParticle* ComputeSurfaceInteraction(vtkLagrangianParticle* particle, std::queue& particles, unsigned int& interactedSurfaceFlatIndex, PassThroughParticlesType& passThroughParticles); /** * Set a input array to process at a specific index, identified by a port, * connection, fieldAssociation and a name. * Each inherited class can specify their own input array to process */ virtual void SetInputArrayToProcess( int idx, int port, int connection, int fieldAssociation, const char* name); /** * Look for a dataset in this integration model * containing the point x. return false if out of domain, * return true and data to recover the cell if in domain. * does not filter out ghost cells. * convenience method with less outputs. * Provide a particle if a dataset/locator cache can be used. * This method is thread-safe. */ virtual bool FindInLocators(double* x, vtkLagrangianParticle* particle, vtkDataSet*& dataset, vtkIdType& cellId, vtkAbstractCellLocator*& loc, double*& weights); //@{ /** * Convienience methods to call FindInLocators with less arguments * THESE METHODS ARE NOT THREAD-SAFE */ virtual bool FindInLocators( double* x, vtkLagrangianParticle* particle, vtkDataSet*& dataset, vtkIdType& cellId); virtual bool FindInLocators(double* x, vtkLagrangianParticle* particle); //@} /** * Initialize a particle by setting user variables and perform any user * model specific operation. empty in basic implementation. */ virtual void InitializeParticle(vtkLagrangianParticle* vtkNotUsed(particle)) {} /** * Method to be reimplemented if needed in inherited classes. * Allows a inherited class to check if adaptive step reintegration * should be done or not, this method is called just before * potentially performing adaptative step reintegration, * the current particle is passed as an argument. * This method always returns true in this basis class. */ virtual bool CheckAdaptiveStepReintegration(vtkLagrangianParticle* vtkNotUsed(particle)) { return true; } /** * Method to be reimplemented if needed in inherited classes. * Allows a inherited class to check if a particle * should be terminated only based on particle parameters. * This method should return true if the particle must be terminated, false otherwise. * It always returns false in this basis class. * This method is thread-safe, its reimplementation should still be thread-safe. */ virtual bool CheckFreeFlightTermination(vtkLagrangianParticle* vtkNotUsed(particle)) { return false; } //@{ /** * Set/Get Non Planar Quad Support */ vtkSetMacro(NonPlanarQuadSupport, bool); vtkGetMacro(NonPlanarQuadSupport, bool); vtkBooleanMacro(NonPlanarQuadSupport, bool); //@} /** * Get the seed arrays expected name * Used Only be the vtkLagrangianSeedHelper in ParaView plugins */ virtual vtkStringArray* GetSeedArrayNames(); /** * Get the seed arrays expected number of components * Used Only be the vtkLagrangianSeedHelper in ParaView plugins */ virtual vtkIntArray* GetSeedArrayComps(); /** * Get the seed arrays expected type * Used Only be the vtkLagrangianSeedHelper in ParaView plugins */ virtual vtkIntArray* GetSeedArrayTypes(); /** * Get the surface arrays expected name * Used Only be the vtkLagrangianSurfaceHelper in ParaView plugins */ virtual vtkStringArray* GetSurfaceArrayNames(); /** * Get the surface arrays expected type * Used Only be the vtkLagrangianSurfaceHelper in ParaView plugins */ virtual vtkIntArray* GetSurfaceArrayTypes(); /** * Get the surface arrays expected values and associated enums * Used Only be the vtkLagrangianSurfaceHelper in ParaView plugins */ virtual vtkStringArray* GetSurfaceArrayEnumValues(); /** * Get the surface arrays default values for each leaf * Used Only be the vtkLagrangianSurfaceHelper in ParaView plugins */ virtual vtkDoubleArray* GetSurfaceArrayDefaultValues(); /** * Get the seed array expected number of components * Used Only be the vtkLagrangianSurfaceHelper in ParaView plugins */ virtual vtkIntArray* GetSurfaceArrayComps(); //@{ /** * Get the maximum weights size necessary for calling * FindInLocators with weights */ virtual int GetWeightsSize(); //@} /** * Let the model define it's own way to integrate * Signature is very close to the integrator method signature * output is expected to be the same, * see vtkInitialValueProblemSolver::ComputeNextStep for reference * xcur is the current particle variables * xnext is the next particle variable * t is the current integration time * delT is the timeStep, which is also an output for adaptative algorithm * delTActual is the time step output corresponding to the actual movement of the particle * minStep is the minimum step time for adaptative algorithm * maxStep is the maximum step time for adaptative algorithm * maxError is the maximum acceptable error * error is the output of actual error * integrationResult is the result of the integration, see * vtkInitialValueProblemSolver::ErrorCodes for error report * otherwise it should be zero. be aware that only stagnating OUT_OF_DOMAIN * will be considered actual out of domain error. * Return true if manual integration was used, false otherwise * Simply return false in vtkLagrangianBasicIntegrationModel * implementation. * This method is thread-safe, its reimplementation should still be thread-safe. */ virtual bool ManualIntegration(vtkInitialValueProblemSolver* integrator, double* xcur, double* xnext, double t, double& delT, double& delTActual, double minStep, double maxStep, double maxError, double cellLength, double& error, int& integrationResult, vtkLagrangianParticle* particle); /** * Method called by parallel algorithm * after receiving a particle from stream if PManualShift flag has been set to true * on the particle. Does nothing in base implementation */ virtual void ParallelManualShift(vtkLagrangianParticle* vtkNotUsed(particle)) {} /** * Let the model allocate and initialize a threaded data. * This method is thread-safe, its reimplementation should still be thread-safe. */ virtual void InitializeThreadedData(vtkLagrangianThreadedData* vtkNotUsed(data)) {} /** * Let the model finalize and deallocate a user data at thread level * This method is called serially for each thread and does not require to be thread safe. */ virtual void FinalizeThreadedData(vtkLagrangianThreadedData* vtkNotUsed(data)) {} /** * Enable model post process on output * Return true if successful, false otherwise * Empty and Always return true with basic model */ virtual bool FinalizeOutputs( vtkPolyData* vtkNotUsed(particlePathsOutput), vtkDataObject* vtkNotUsed(interractionOutput)) { return true; } /** * Enable model to modify particle before integration */ virtual void PreIntegrate(std::queue& vtkNotUsed(particles)) {} /** * Get a seed array, as set in setInputArrayToProcess * from the provided seed point data */ virtual vtkAbstractArray* GetSeedArray(int idx, vtkPointData* pointData); /** * Set/Get the number of tracked user data attached to the particles. * Tracked user data are data that are related to each particle position * but are not integrated like the user variables. * They are not saved in the particle path. * Default is 0. */ vtkSetMacro(NumberOfTrackedUserData, int); vtkGetMacro(NumberOfTrackedUserData, int); /** * Method used by the LPT to initialize data insertion in the provided * vtkFieldData. It initializes Id, ParentID, SeedID and Termination. * Reimplement as needed in acccordance with InsertPathData. */ virtual void InitializePathData(vtkFieldData* data); /** * Method used by the LPT to initialize data insertion in the provided * vtkFieldData. It initializes Interaction. * Reimplement as needed in acccordance with InsertInteractionData. */ virtual void InitializeInteractionData(vtkFieldData* data); /** * Method used by the LPT to initialize data insertion in the provided * vtkFieldData. It initializes StepNumber, ParticleVelocity, IntegrationTime. * Reimplement as needed in acccordance with InsertParticleData. */ virtual void InitializeParticleData(vtkFieldData* particleData, int maxTuples = 0); /** * Method used by the LPT to insert data from the partice into * the provided vtkFieldData. It inserts Id, ParentID, SeedID and Termination. * Reimplement as needed in acccordance with InitializePathData. */ virtual void InsertPathData(vtkLagrangianParticle* particle, vtkFieldData* data); /** * Method used by the LPT to insert data from the partice into * the provided vtkFieldData. It inserts Interaction. * Reimplement as needed in acccordance with InitializeInteractionData. */ virtual void InsertInteractionData(vtkLagrangianParticle* particle, vtkFieldData* data); /** * Method used by the LPT to insert data from the partice into * the provided vtkFieldData. It inserts StepNumber, ParticleVelocity, IntegrationTime. * stepEnum enables to select which data to insert, Prev, Current or Next. * Reimplement as needed in acccordance with InitializeParticleData. */ virtual void InsertParticleData( vtkLagrangianParticle* particle, vtkFieldData* data, int stepEnum); /** * Method used by the LPT to insert data from the partice into * the provided vtkFieldData. It inserts all arrays from the original SeedData. * Reimplement as needed. */ virtual void InsertParticleSeedData(vtkLagrangianParticle* particle, vtkFieldData* data); /** * Method to be reimplemented if needed in inherited classes. * Allows a inherited class to take action just before a particle is deleted * This can be practical when working with vtkLagrangianParticle::TemporaryUserData. * This can be called with not fully initialized particle. */ virtual void ParticleAboutToBeDeleted(vtkLagrangianParticle* vtkNotUsed(particle)) {} protected: vtkLagrangianBasicIntegrationModel(); ~vtkLagrangianBasicIntegrationModel() override; /** * Actually compute the integration model velocity field * pure abstract, to be implemented in inherited class * This method implementation should be thread-safe */ virtual int FunctionValues(vtkLagrangianParticle* particle, vtkDataSet* dataSet, vtkIdType cellId, double* weights, double* x, double* f) = 0; /** * Look in the given dataset and associated locator to see if it contains * the point x, if so return the cellId and output the cell containing the point * and the weights of the point in the cell * This method is thread-safe, its reimplementation should also be. */ virtual vtkIdType FindInLocator(vtkDataSet* dataSet, vtkAbstractCellLocator* locator, double* x, vtkGenericCell* cell, double* weights); /** * Terminate a particle, by positioning flags. * Return true to record the interaction, false otherwise * This method is thread-safe. */ virtual bool TerminateParticle(vtkLagrangianParticle* particle); /** * Bounce a particle, using the normal of the cell it bounces on. * Return true to record the interaction, false otherwise * This method is thread-safe. */ virtual bool BounceParticle( vtkLagrangianParticle* particle, vtkDataSet* surface, vtkIdType cellId); /** * Breakup a particle at intersection point, by terminating it and creating two * new particle using the intersected cells normals * Return true to record the interaction, false otherwise * This method is thread-safe and uses vtkLagrangianBasicIntegrationModel::ParticleQueueMutex * to access the particles queue, its reimplementation should also be. */ virtual bool BreakParticle(vtkLagrangianParticle* particle, vtkDataSet* surface, vtkIdType cellId, std::queue& particles); /** * Call vtkLagrangianBasicIntegrationModel::Terminate * This method is to be reimplemented in inherited classes willing * to implement specific particle surface interactions * Return true to record the interaction, false otherwise * This method is thread-safe and should use * vtkLagrangianBasicIntegrationModel::ParticleQueueMutex * to add particles to the particles queue, see BreakParticle for an example. */ virtual bool InteractWithSurface(int surfaceType, vtkLagrangianParticle* particle, vtkDataSet* surface, vtkIdType cellId, std::queue& particles); /** * Call vtkCell::IntersectWithLine * This method is to be reimplemented in inherited classes willing * to implement specific line/surface intersection * This method is thread-safe. */ virtual bool IntersectWithLine(vtkLagrangianParticle* particle, vtkCell* cell, double p1[3], double p2[3], double tol, double& t, double x[3]); /** * compute all particle variables using interpolation factor * This method is thread-safe. */ virtual void InterpolateNextParticleVariables( vtkLagrangianParticle* particle, double interpolationFactor, bool forceInside = false); /** * Given a particle, check if it perforate a surface cell * ie : interact with next step after interacting with it * This method is thread-safe. */ virtual bool CheckSurfacePerforation( vtkLagrangianParticle* particle, vtkDataSet* surface, vtkIdType cellId); /** * Get a seed array, as set in setInputArrayToProcess * from the provided particle seed data * Access then the first tuple to access the data * This method is thread-safe. */ virtual vtkAbstractArray* GetSeedArray(int idx, vtkLagrangianParticle* particle); /** * Directly get a double value from flow or surface data * as defined in SetInputArrayToProcess. * Make sure that data pointer is large enough using * GetFlowOrSurfaceDataNumberOfComponents if needed. * This method is thread-safe. */ virtual bool GetFlowOrSurfaceData(vtkLagrangianParticle* particle, int idx, vtkDataSet* flowDataSet, vtkIdType tupleId, double* weights, double* data); /** * Recover the number of components for a specified array index * if it has been set using SetInputArrayToProcess, * with provided dataset. * Returns -1 in case of error. * This method is thread-safe. */ virtual int GetFlowOrSurfaceDataNumberOfComponents(int idx, vtkDataSet* dataSet); /** * Recover a field association for a specified array index * if it has been set using SetInputArrayToProcess * This method is thread-safe. */ virtual int GetFlowOrSurfaceDataFieldAssociation(int idx); /** * Method used by ParaView surface helper to get default * values for each leaf of each dataset of surface * nComponents could be retrieved with arrayName but is * given for simplication purposes. * it is your responsibility to initialize all components of * defaultValues[nComponent] */ virtual void ComputeSurfaceDefaultValues( const char* arrayName, vtkDataSet* dataset, int nComponent, double* defaultValues); vtkAbstractCellLocator* Locator; bool LocatorsBuilt; vtkLocatorsType* Locators; vtkDataSetsType* DataSets; std::vector SharedWeights; struct ArrayVal { int val[3]; }; typedef std::pair ArrayMapVal; std::map InputArrays; typedef struct SurfaceArrayDescription { int nComp; int type; std::vector > enumValues; } SurfaceArrayDescription; std::map SurfaceArrayDescriptions; vtkSurfaceType* Surfaces; vtkLocatorsType* SurfaceLocators; double Tolerance; bool NonPlanarQuadSupport; bool UseInitialIntegrationTime; int NumberOfTrackedUserData = 0; vtkNew SeedArrayNames; vtkNew SeedArrayComps; vtkNew SeedArrayTypes; vtkNew SurfaceArrayNames; vtkNew SurfaceArrayComps; vtkNew SurfaceArrayTypes; vtkNew SurfaceArrayEnumValues; vtkNew SurfaceArrayDefaultValues; vtkWeakPointer Tracker; std::mutex ParticleQueueMutex; private: vtkLagrangianBasicIntegrationModel(const vtkLagrangianBasicIntegrationModel&) = delete; void operator=(const vtkLagrangianBasicIntegrationModel&) = delete; }; #endif