Flocking 3D

using EasyABM

Step 1: Create Agents and Model

Lets create 200 agents with properties shape, pos, vel and orientation (The orientation property is used internally by EasyABM to draw the direction agent is facing). The position pos is only accepted as a Vect which is an inbuilt vector type in EasyABM. It is also recommended for both convenience as well as performance to use Vect type for any vectorial properties in the model such as velocity and forces. The keeps_record_of argument is set of properties that an agent will record during time evolution. The model is defined with properties:

  • min_dis : The distance between boids below which they start repelling each other.
  • coh_fac : The proportionality constant for the cohere force.
  • sep_fac : The proportionality constant for the separation force.
  • aln_fac : The proportionality constant for the alignment force.
  • vis_range : The visual range of boids.
  • dt : The proportionality constant between change in position and velocity.

The argument agents_type is Static which means that the agents number will remain fixed during simulation.

boids = con_3d_agents(200, shape = :cone, pos = Vect(0.0,0.0,0.0), 
    vel=Vect(0.0,0.0,0.0), orientation = Vect(0.0,0.0,0.0), keeps_record_of = Set([:pos, :orientation]))
model = create_3d_model(boids, agents_type=Static, 
    space_type = Periodic, min_dis = 0.3, coh_fac = 0.05, 
    sep_fac = 0.5, dt= 0.1, vis_range = 2.0, aln_fac = 0.35)

Step 2: Initialise the model

In this step we set the positions, velocities and orientations of boids and initialise the model.

function initialiser!(model)
    xdim, ydim, zdim = model.size
    for boid in model.agents
        boid.pos = Vect(rand()*xdim, rand()*ydim, rand()*zdim)
        boid.vel = Vect(1-2*rand(), 1-2*rand(), 1-2*rand())
        boid.vel /= veclength(boid.vel)
        boid.orientation = boid.vel
    end
end
init_model!(model, initialiser = initialiser!)

Step 3: Defining the step_rule! and running the model

In this step we implement the step logic of the flocking model in the step_rule! function and run the model for 500 steps.


const ep = 0.00001 # to avoid division by zero

function step_rule!(model)
    dt = model.properties.dt
    for boid in model.agents
        nbrs = neighbors(boid, model, model.properties.vis_range)
        coh_force = Vect(0.0,0.0,0.0) 
        sep_force = Vect(0.0,0.0,0.0) 
        aln_force = Vect(0.0,0.0,0.0)
        num = 0
        for nbr in nbrs
            num+=1
            vec = nbr.pos - boid.pos
            coh_force += vec
            if veclength(vec)< model.properties.min_dis
                sep_force -= vec
            end
            aln_force += nbr.vel
        end
        aln_force = num>0 ? (aln_force / num - boid.vel) * model.properties.aln_fac : aln_force
        num = max(1, num)
        coh_force *= (model.properties.coh_fac/num)
        sep_force *=  model.properties.sep_fac
        boid.vel  += (coh_force + sep_force) + aln_force
        boid.vel  /= (veclength(boid.vel) + ep)
        boid.orientation = boid.vel
        boid.pos += boid.vel*dt
    end
end

run_model!(model, steps=500, step_rule = step_rule!)

Step 4: Visualisation

The following code will draw the state of the model at frame number 4.

draw_frame(model, frame=4)

If one wants to see the animation of the model run, it can be done as

animate_sim(model)

png

After defining the step_rule! function we can also choose to create an interactive application (which currently works in Jupyter with WebIO installation) as shown below. It is recommended to define a fresh model and not initialise it with init_model! or run with run_model! before creating interactive app.

boids = con_3d_agents(200, shape = :cone, pos = Vect(0.0,0.0,0.0), 
    vel=Vect(0.0,0.0,0.0), orientation = Vect(0.0,0.0,0.0), keeps_record_of = Set([:pos, :orientation]))
    
model = create_3d_model(boids, agents_type=Static, 
    space_type = Periodic, min_dis = 0.3, coh_fac = 0.05, 
    sep_fac = 0.5, dt= 0.1, vis_range = 2.0, aln_fac = 0.35)

create_interactive_app(model, initialiser= initialiser!,
    step_rule= step_rule!,
    model_controls=[(:min_dis, "slider", 0.01:0.1:1.0),
        (:coh_fac, "slider", 0.01:0.01:1.0),
        (:sep_fac, "slider", 0.01:0.01:1.0),
        (:aln_fac, "slider", 0.01:0.01:1.0),
        (:vis_range, "slider", 0.5:0.5:4.0)], frames=400) 

png

Step 5: Fetch Data

It is easy to fetch any data recorded during simulation. For example, the data of average velocity of agents at each time step can be obtained as -

df = get_agents_avg_props(model, agent -> agent.vel, labels = ["average velocity"])

Individual agent data recorded during model run can be obtained as

df = get_agent_data(model.agents[1], model).record

References

1.) https://en.wikipedia.org/wiki/Flocking