Continuous space social distancing for COVID-19

This is a model similar to our SIR model for the spread of COVID-19. But instead of having different cities, we let agents move in one continuous space and transfer the disease if they come into contact with one another. This model is partly inspired by this article, and can complement the SIR graph model. The graph model can model virus transfer between cities, whilst this model can be used to study what happens within a city.

The example here serves additionally as an introduction to using continuous space, modelling billiard-like collisions in that space, and animating the agent motion in the space. Notice that a detailed description of the basics of the model regarding disease spreading exists in the SIR example, and is not repeated here.

It is also available from the Models module as Models.social_distancing.

Moving agents in continuous space

Let us first create a simple model where balls move around in a continuous space. We need to create agents that comply with ContinuousSpace, i.e. they have a pos and vel fields, both of which are tuples of float numbers.

using Agents, Random, AgentsPlots, Plots
mutable struct Agent <: AbstractAgent
    id::Int
    pos::NTuple{2,Float64}
    vel::NTuple{2,Float64}
    mass::Float64
end

The mass field will come in handy later on, when we implement social isolation (i.e. that some agents don't move and can't be moved).

Let's also initialize a trivial model with continuous space

function ball_model(; speed = 0.002)
    space2d = ContinuousSpace(2; periodic = true, extend = (1, 1))
    model = ABM(Agent, space2d, properties = Dict(:dt => 1.0))

    # And add some agents to the model
    for ind in 1:500
        pos = Tuple(rand(2))
        vel = sincos(2π * rand()) .* speed
        add_agent!(pos, model, vel, 1.0)
    end
    index!(model)
    return model
end

model = ball_model()
AgentBasedModel with 500 agents of type Agent
 space: 2-dimensional periodic ContinuousSpace
 scheduler: fastest
 properties: Dict(:dt => 1.0)

We took advantage of the functionality of add_agent! that creates the agents automatically. For now all agents have the same absolute speed, and mass. We index! the model, to make finding space neighbors faster.

The agent step function for now is trivial. It is just move_agent! in continuous space

agent_step!(agent, model) = move_agent!(agent, model, model.dt)

dt is our time resolution, but we will talk about this more later! Cool, let's see now how this model evolves.

e = model.space.extend
anim = @animate for i in 1:2:100
    p1 = plotabm(
        model,
        as = 4,
        showaxis = false,
        grid = false,
        xlims = (0, e[1]),
        ylims = (0, e[2]),
    )

    title!(p1, "step $(i)")
    step!(model, agent_step!, 2)
end
gif(anim, "socialdist1.gif", fps = 25)