SIR model
In the agent-based modeling approach an epidemiological model, like SIR model can have a large number of parameters depending upon the requirements. Below we will implement a basic SIR model in EasyABM.
using EasyABM
Step 1: Create Agents and Model
In our SIR model there will be four type of agents - agentS (susceptible), agentI (infectious), agentR (recovered) and agentD (dead). We assume that the recovered agents become immune and do not get infected again. We create 500 2d agents all of type agentS (later in the initilisation step will set the type of some agents to be agentI). The not_well_since
property of an agent is the time since the agent got infected. Our model has parameters initially_sick
(number of agents initially sick), sickness_duration
(duration of sickness), infection_prob
(probability of infection when an infected agent comes in contact with a susceptible agent) and death_prob
(the probability of death from infection).
@enum AgentType begin
agentS=1
agentI=2
agentR=3
agentD=4
end
agents = grid_2d_agents(500, pos = Vect(1,1), color=:green,
atype = agentS, not_well_since = 0,
keeps_record_of = [:atype, :color, :pos]);
model = create_2d_model(agents, size=(50,50),
agents_type = Static, # agents don't take birth or die
space_type = Periodic, initially_sick = 10,
sickness_duration = 21, infection_prob = 0.8,
death_prob=0.05);
Step 2: Initialise the model
In the second step we initialise the agents by defining initialiser!
function and sending it as an argument to init_model!
. In the initialiser!
function we set first initially_sick
number of agents to be of type agentI and set their color to :red
. All other agents are set to be of type agentS with color :green
. We also set each agents position at the center of a randomly selected patch.
function initialiser!(model)
for (i,agent) in enumerate(model.agents)
if i<=model.parameters.initially_sick
agent.atype = agentI
agent.color = :red
else
agent.atype = agentS
agent.color = :green
end
agent.not_well_since = 0
x = rand(1:model.size[1])
y = rand(1:model.size[2])
agent.pos = Vect(x, y) # center of a random patch
end
end
init_model!(model, initialiser = initialiser!)
Step 3: Run the model
In this step we implement the step logic of the SIR model in the step_rule!
function and run the model for 100 steps.
function die_or_recover(agent, death_prob)
if rand()<death_prob
agent.atype = agentD
agent.color = :black
else
agent.atype = agentR
agent.color = :yellow
end
agent.not_well_since = 0
end
function infect_neighbors(agent, nbrs, infection_prob)
for nbr in nbrs
if (nbr.atype ==agentS) && (rand()< infection_prob)
nbr.atype = agentI
nbr.not_well_since = 0
nbr.color = :red
end
end
end
function change_position(agent)
dx =rand(-1:1)
dy =rand(-1:1)
agent.pos += Vect(dx,dy)
end
function step_rule!(model)
parameters = model.parameters
for agent in model.agents
nbrs = grid_neighbors(agent, model, 1) #immediate neighbors on grid
if agent.atype == agentI
agent.not_well_since +=1
if agent.not_well_since > parameters.sickness_duration
die_or_recover(agent, parameters.death_prob)
elseif agent.not_well_since>1
infect_neighbors(agent, nbrs, parameters.infection_prob)
end
end
if agent.atype !=agentD
change_position(agent)
end
end
end
run_model!(model, steps=100, step_rule = step_rule! )
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
create_interactive_app(model, initialiser= initialiser!,
step_rule= step_rule!,
model_controls=[(:initially_sick, :s, 1:500),
(:sickness_duration, :s, 5:25),
(:infection_prob, :s, 0.01:0.01:1.0),
(:death_prob, :s, 0.01:0.01:0.1)
],
agent_plots = Dict("Susceptible"=> ag -> ag.atype == agentS ? 1 : 0,
"Infected" => ag -> ag.atype == agentI ? 1 : 0,
"Recovered" => ag -> ag.atype == agentR ? 1 : 0
),
frames=70)
Step 4: Fetch Data
The following line of code fetches data of average number of susceptible, infectious and recovered agents. The function get_agents_avg_props
averages over all agents the values returned by functions sent to it as arguments.
df = get_agents_avg_props(model,
ag -> ag.atype == agentS ? 1 : 0,
ag -> ag.atype == agentI ? 1 : 0,
ag -> ag.atype == agentR ? 1 : 0,
labels = ["Susceptible", "Infected", "Recovered"],
plot_result = true
)