Particle based simulation tutorialΒΆ

Import LavaVu and create a Viewer object

import lavavu
lv = lavavu.Viewer()

Create an initial set of random points and velocities

import random
import math

count = 1000 #Number of particles
positions = []
velocity = []

for p in range(0,count):
    #Random positions, with height offset of 3 added to y component
    positions.append([random.uniform(-0.05,0.05), 3 + random.uniform(-0.05,.05), random.uniform(-0.05,0.05)])

    #Random velocities for the points
    velocity.append([random.gauss(0,0.04), random.gauss(0,0.04), random.gauss(0,0.04)])

Apply some global settings

#lv["gpucache"] = True        #Enable caching all data on GPU for speed (don't use for very large datasets)
lv["background"] = "#eeeeee" #Set the plot background colour
lv["axis"] = False
lv["border"] = False

#Here we can set a fixed bounding box
#Without this the bounding box is elastic and calculated to fit the geometry
#lv["min"] = [-1.0, -1.0, -1.0]
#lv["max"] = [1.0, 1.0, 1.0]

Plot ground plane and axis lines as an example of some static data (not time-varying)

This data remains in the visualisation regardless of the time step and must be loaded first, before the time varying data.

lines = lv.lines(colours="red green blue")
lines.vertices([[-1.0, 0, 0], [1.0, 0, 0], [0, -1.0, 0], [0, 1.0, 0], [0, 0, -1.0], [0, 0, 1.0]])

ground = lv.triangles("ground", colour="darkgrey", cullface=False, dims=[2,2]);
gplane = [[-5, -5], [5, 5]]
ground.vertices([[gplane[0][0], 0.0, gplane[0][1]],
                 [gplane[1][0], 0.0, gplane[0][1]],
                 [gplane[0][0], 0.0, gplane[1][1]],
                 [gplane[1][0], 0.0, gplane[1][1]]])

#Load an initial camera viewpoint
lv.translation(0.0, 0.0, -15)
lv.rotation(32, 0, 0)

#Plot an image to view the static elements we've loaded
lv.display(resolution=(500,400))

Setup the particle drawing object

#Plot time varying data: points
points = lv.points(pointsize=5, pointtype="shiny", opacity=0.75)

#Apply a colourmap, cubehelix() generates a map with monotonically increasing luminance
cmap = points.colourmap(lavavu.cubehelix()) #, range=[0,0.1])

#Add a colour bar
points.colourbar();
#Add a tracer visualisation to track particle positions
tracercount = len(positions)//50 #Track only the first 10% of the particles
tracers = lv.tracers(steps=300, scaling=0.5, glyphs=2, colourmap="red orange green")

Advect the random particles and reload the positions for each time step

#This is a very simple toy particle simulation for the sake of demonstration only
steps = 300 #Number of steps to run for
for s in range(steps):
    values = []
    #Loop through particles
    for i in range(len(positions)):
        p = positions[i]
        v = velocity[i]

        for c in range(3):
            #Advect
            p[c] = p[c] + v[c]

            #Apply drag
            v[c] *= 0.99

        #Bounce off floor
        if p[1] < 0:
            p[1] = 0
            v[1] = -0.9*v[1]

        #Gravity
        v[1] -= 0.001

        #Values for the points: velocity magnitude
        #these values will be used to colour the points
        values.append(math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]))

    #Add a new time step
    lv.addstep()

    #Load updated positions and values
    points.vertices(positions)
    points.values(values, "velocitymag")

    #Load tracer position data
    tracers.vertices(positions[0:tracercount])

Get the timestep information

lv.timestep(0)
steps = lv.timesteps()
print(len(steps))
print(steps[0:10])
300
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Filters allow elements to be filtered based on their data sets

#Clear filters
points["filters"] = []

#Filter to a range of values
myfilter = points.includemap('velocitymag', (0.0, 1.0))

#points["opacitymap"] = "rgba(0,0,0,0) rgba(0,0,0,1)"
#points["opacityby"] = "velocitymag"

Build a control panel to view and modify the visualisation

lv.control.Panel()
lv.control.TimeStepper()
lv.control.Range("scalepoints", range=(1,10))
points.control.Range("pointsize")
points.control.Range("opacity")
points.control.Filter(myfilter)
points.control.List("pointtype")
tracers.control.Checkbox("flat", label="Flat Tracers")
tracers.control.Range("scaling", label="Tracer Arrow Size", range=(0.1,3), step=0.1)
lv.control.ObjectList()
lv.control.show()

Timestep:


Scalepoints:


Pointsize:


Opacity:


Velocitymag:



Pointtype:



Tracer Arrow Size:


Objects:






Get the current camera state

This outputs the commands necessary to restore the camera to its current settings, which can be copied and pasted to save a viewpoint for re-use

lv.camera()
lv.translation(0.0, 0.0, -15.0)
lv.rotation(32.0, 0.0, 0.0)
{'translate': [0.0, 0.0, -15.0],
 'rotate': [0.276, 0.0, 0.0, 0.961],
 'xyzrotate': [32.0, 0.0, 0.0],
 'aperture': 45.0}

Create a video animation

Before generating a video we change adjust the visualistion in the interactive view, or use a previously saved camera to set a nicer viewpoint as follows:

lv.translation(-0.0697, 0.569, -14.536)
lv.rotation(0.14, 0.3, 0.02, 0.94)
lv.video_steps(resolution=(500,400), fps=30)
#lv.webgl()