3D particle diffusion animation.
Animated 3D Particle Diffusion
This model uses a random walk approach to model and animate the diffusion of particles within a rectangular channel. Using principles from Fickian diffusion, each particle’s path is randomly generated and then plotted over time. Longitudinal flow patterns can be applied as well, with velocity profiles calculated using the log-law equation. Particles can be released either all at once or staggered over time. Coding was done in Anaconda’s Spyder environment using the Matplotlib and Numpy libraries. Saving animations requires them to be written using FFMpeg.
The movement of a particle is created by generating a series of random ‘steps’, and moving the particle along this path. Steps are initially computed in spherical coordinates, and then converted back to cartesian coordinates to displace the particle. A spherical coordinate system was selected as it more accurately represents movement due to diffusion in 3D. Given an environment where the diffusion coefficient is the same in every direction, then the probability for the particle to move a certain distance is the same in any direction. In spherical coordinates, this means that for a certain radial length, the probability distribution for the azimuthal and polar angle are uniform. To visualize, plotting a single standard deviation away from the origin would create a sphere. In cartesian coordinates, 3D random walk models will usually generate a step distance in each axis (x,y,z). However, this sampling method causes an issue with corner cases. If a single standard deviation from the origin were to be plotted , the resulting shape would be a cube. As such, a particle whose steps are generated in a cartesian system is actually likely to move a further distance in every direction except those parallel to the x,y, and z-axes than one whose steps are generated in a spherical system.
To elaborate on the above, particle paths are created by first sampling a step distance from a Gaussian Distribution with a standard deviation of sqrt(2Dt) as described by Fischer et al. (1979). This gives the absolute distance that the particle will be displaced from its original location. A polar angle (phi) and azimuthal angle (theta) are then selected from a uniform random distribution between 0 and 2pi. Again, the distributions for these angles are uniform due to the assumption that the diffusive coefficient is equal in every direction. Using the step distance as the radial distance in a spherical coordinate system and the particle’s current position as the origin, the above can then be converted into cartesian coordinates relative to its own position. These x,y, and z-directional steps are found after each time step for every particle, and summarized in an array.
Once all time steps have been calculated, the step array is cummulatively added along its time steps to create a path array of the particle’s coordinates over time. After this initial path is found, the boundary conditions are checked. If particle’s are outside of the channel dimensions, then it is reflected across the boundaries until they are back inside the channel. Since step conditions are random, no difference was observed whether these checks occurred each time step, or all checks were done at the end. As such, boundary checks were done at the end of step generation to reduce the computational load.
Using the particles’ z-coordinates, a corresponding velocity array is calculated according the the log-law equation. For each particle at each time step, an x-directional displacement is found. The values initially used in the model are typical for mean longitudinal and shear velocity for the Fraser River. The particle path array is then transformed one last time by displacing its x-coordinates using the velocity array. The path array is plotted for each time step, and Matplotlib’s FuncAnimation class is used to animate these plots over time.
If a staggered release is set to true, then a specified number of particles is repeatedly released after a certain number of time steps. As the initial step array is randomly generated, this staggering effect is simply achieved by writing an array of zeros to step values for each particle until they are released. The same is done for the velocity array, and the two staggered arrays are combined in the manner as above to generate the final coordinate array. For staggered release, each released particle group is drawn with a different color for ease of viewing.
Simply run the code to display the animation! Play around with some of adjustable model parameters, and see what happens. There are options to draw the water outline, plot a streamline, and also to save the resulting figure. The current random walk model is only affected by velocity, and so the streamline will generally be a straight line from the origin. In the future, I will be looking to add buoyancy and possibly pressure effects which will result in more interesting streamlines. When velocity is set to 0, there is also a special case where water will not be drawn and the particles are only moving due to diffusion.
As a warning, saving the animation will significantly increase the run time. Saved animations will not be exactly the same as ones shown in the console due to the nature of FFMpeg’s writing process. The frame interval and repeat delay arguments in FuncAnimation will not affect the mp4 saved by FFMpeg. To change the frame interval, adjust the fps argument in the writer variable accordingly. To freeze the final position on screen, you can either copy and repeat the final frame (there are a few tutorials online on how to do this) or simply pause the video. If the save function is not working, verify that it is installed in your system, then check the file path used. To change the file path, paste the following code snippet somewhere below the matplotlib import declarations:
plt.rcParams[‘animation.ffmpeg_path’] =r’Replace this string with the application location for FFMpeg on your computer’
Hope you enjoy, feel free to suggeset any improvements!