Pics dev


The filter that does particle advection in VisIt is the "PICS" filter. PICS = Parallel Integral Curve System. The most prominent filter that inherits from PICS is the streamline filter. The Poincare filter also inherits from it.

This page gives an overview of how to write a derived type of PICS for doing custom analysis using particle advection.

There are two main pieces:

  1. Create a derived type of avtIntegralCurve that analyzes the IC in the way you want.
  2. Inheriting your filter from avtPICSFilter

Then you can plug your filter into the pipeline and it should do your custom analysis.

Custom integral curve analyzer

The PICS filter passes around an avtIntegralCurve. You must inherit from this class and include your own methods for analyzing the trajectory. The analysis is done step-by-step.

Here are the methods you must have:

   // Constructors
   avt<YourType>IC( const avtIVPSolver* model,
                     Direction dir,
                     const double& t_start,
                     const avtVector &p_start,
                     long ID );
   virtual void AnalyzeStep( avtIVPStep &step, avtIVPField *field);
   virtual void Serialize(MemStream::Mode mode, MemStream &buff,
                               avtIVPSolver *solver);


The constructors take arguments that are ultimately passed down to the base type. You must have both flavors (the one with a bunch of arguments and the one with no arguments) ... they are both used and used in different places.


This is the key method where you can analyze what happened in the last step. If you need to pull out data values, you can do that with the avtIVPField argument.


Your integral curve may very well be passed from processor to processor. This method contains the directions for communicating your custom data members.

Inheriting from PICS


You must reimplement 5 methods:

   virtual avtIntegralCurve          *CreateIntegralCurve();
   virtual avtIntegralCurve          *CreateIntegralCurve(
                                       const avtIVPSolver* model,
                                       avtIntegralCurve::Direction dir,
                                       const double& t_start,
                                       const avtVector &p_start, long ID);
   virtual std::vector<avtVector>  GetInitialLocations();
   virtual CommunicationPattern    GetCommunicationPattern();
   virtual void                    CreateIntegralCurveOutput(
                                          std::vector<avtIntegralCurve*> &);


These two methods are for instantiating your custom analyzer.


This method has the initial particle locations.


There are three types of communication:

  1. RestoreSequence: you are recording state step by step and you want to restore the sequence of steps on the processor that contained the original seed location. This goes hand-in-hand with avtStateRecorderIntegralCurves, which are a derived type of avtIntegralCurve.
  2. LeaveOnCurrentProcessor: you are tracking a property of the IC and you don't care which processor the IC ends up on.
  3. ReturnToOriginatingProcessor: you are tracking a property of the IC ... but you want the final value to be returned to the processor that contained the original seed location. THIS OPTION IS NOT IMPLEMENTED.


This creates the output of the filter based on the final integral curves. Your filter will create a new output avtDataset based on the curves it receives. Note that these curves might be located on any of VisIt's processors for certain communication patterns.

Default values

There are many defaults in the PICS filters ... tolerances, etc. You can set those methods as your filter is instantiated.


I am looking at flow through a nuclear reactor. The flow has a strong Z-bias. A key question is how much movement occurs in the X/Y plane, since that movement helps cool the reactor. I am writing an operator to measure how much X/Y movement occurs. The input to my operator is the 3D reactor data. The output will be a 2D plane. The operator will take a user-settable number of points in X and Y. Imagine this is 100x100. Then I will place points equally along a Z=0 plane. (The Z=0 plane is where coolant enters the reactor.) I want each point to be advected until it exits the assembly. As the point advects, I want to measure how much X/Y movement occurs (the distance travelled ignoring Z). After each of the 10,000 particles exits, I'll make a new mesh, which is 2D. The field on this mesh will be the distance travelled. So, for location (-2, 5), the scalar field will correspond to the particle originating at (-2,5,0) and moving through the assembly. The value will be the X/Y movement.

This code was implemented as operators/ChannelComm.