# Flocking

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 the 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 set to Static which means that the agents number will remain fixed during simulation.

boids = con_2d_agents(200, shape = :arrow, pos = Vect(0.0,0.0),
vel=Vect(0.0,0.0), orientation = 0.0, keeps_record_of = Set([:pos, :vel, :orientation]))
model = create_2d_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 = model.size
for boid in model.agents
boid.pos = Vect(rand()*xdim, rand()*ydim)
boid.orientation = rand()*2*3.14
boid.vel = Vect(-sin(boid.orientation), cos(boid.orientation))
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

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) # For a Vect all coordinates must be of same type
sep_force = Vect(0.0,0.0)
aln_force = Vect(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 = calculate_direction(boid.vel)
boid.pos += boid.vel*dt
end
end

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

## Step 4: Visualisation

In order to draw the model at a specific frame, say 4th, one can use draw_frame(model, frame = 4 ). If one wants to see the animation of the model run, it can be done as

animate_sim(model)

After defining the step_rule! function we can also choose to create an interactive application (which currently works in Jupyter with WebIO installation) as follows. 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_2d_agents(200, shape = :arrow, pos = Vect(0.0,0.0),
vel=Vect(0.0,0.0), orientation = 0.0, keeps_record_of = Set([:pos, :vel, :orientation]))

model = create_2d_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) 

## 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