-
Notifications
You must be signed in to change notification settings - Fork 14
Example 8: SIR Model with contact network
We consider here a simple SIR model (with two transitions, S-->I with rate: beta*I/N and I-->R with rate gamma). In a mixing population, each individual has the same probability to contact all others. If we consider a contact network between individuals of the population, an individual may contact a few other individuals.
A network is a graph with nodes and edges. In the individual contact network model, a node represents an individual and an edge indicates a contact between two individuals. In the general case, we consider a node is a sub-population (the population is divided by the equivalence relation R: aRb = (a.node == b.node)). In each node, we consider an individual contacts all others of the same node (so a node is homogeneously mixed).
The probability of infection (or the force of infection) of a susceptible in a contact network model is formulated by the number of infectious individuals having contact with itself.
There are several commonly considered topology for a network of contacts: #random (Erdos Renyi random graph), #scalefree (Barabasi Albert graph), #smallworld (Watts and Strogatz model)
The network of contacts is studied at individual-level, each node is corresponding to an individual in the population. We specify the model by composing two concerns: a spatial concern and a SIR concern. The probability of infection (lambda) that should take into account the number of infectious contacts of each individual is specified through functional transition rate. The code below is used the Kendrick API. We consider further developping a DSL targeted to network models in order to simplify the specification and make it more accessible for epidemiologists.
|model network spatialConcern sirConcern simulator db f|
model := KEModel new population: (KEPopulation size: 100).
sirConcern := KEModelPart new.
sirConcern addAttribute: #status value: #(S I R).
sirConcern addParameters: { #beta. #gamma. #lambda }.
sirConcern addTransitionFrom: { #status->#S } to: { #status->#I } probability: 'lambda'.
sirConcern addTransitionFrom: { #status->#I } to: { #status->#R } probability: 'gamma'.
spatialConcern := KEModelPart new.
network := KEContactNetwork nodes: 100 topology: { #random. #p->0.02 }.
spatialConcern addParameter: #network value: network.
spatialConcern addAttribute: #node value: network allContacts.
model integrate: sirConcern.
model integrate: spatialConcern.
model atParameter: #lambda assignValue: [ :aModel||node|
node := aModel currentCompartment at: #node.
((aModel atParameter: #network)
contactsOf: {aModel. #node->node. #status->#I})
*(aModel atParameter: #beta)/(aModel atParameter: #N)
].
model atParameter: #beta assignValue: 100.
model atParameter: #gamma assignValue: 0.1.
1 to: 99 do: [:i| model atCompartment: {#status->#S. #node->i asString asSymbol} put: 1].
model atCompartment: { #status->#I. #node->#'100' } put: 1.
simulator := KESimulator new: #IBM from: 0.0 to: 50 by: 0.1.
simulator executeOn: model.
f := [:name| |d tmp|
tmp := (simulator allTimesSeriesContaining: name) collect: [ :e| e value ].
d := OrderedCollection new.
1 to: tmp first data size do: [ :k| d add: (tmp collect: [:e| e data at: k]) sum ].
(KETimeSeries from: d withIndex: tmp first index) compartment: (STON fromString: name)
].
db := KEDiagramBuilder new data: { (f value: '{#status:#I}') }.
db open.
db := KENetworkBuilder new
data: simulator timeSeries;
network: (model atParameter: #network);
status: #(#S #I #R);
colors: #(#green #red #blue);
viewDataAtTime: 12.5;
legend: 'random network, p = 0.02'.
db open
We study each node of the network as a spatial subpopulation. In a node, an individual contacts all others (a node is supposed to be homogeneous).
|model network spatialConcern sirConcern simulator db|
model := KEModel new population: (KEPopulation size: 1000).
sirConcern := KEModelPart new.
sirConcern addAttribute: #status value: #(S I R).
sirConcern addParameters: { #beta. #gamma. #lambda }.
sirConcern addTransitionFrom: { #status->#S } to: { #status->#I } probability: 'lambda'.
sirConcern addTransitionFrom: { #status->#I } to: { #status->#R } probability: 'gamma'.
spatialConcern := KEModelPart new.
network := KEContactNetwork nodes: 10 topology: { #random. #p->0.2 }.
spatialConcern addParameter: #network value: network.
spatialConcern addAttribute: #node value: network allContacts.
model integrate: sirConcern.
model integrate: spatialConcern.
model atParameter: #lambda assignValue: [ :aModel||node|
node := aModel currentCompartment at: #node.
((aModel atParameter: #network) contactsOf: {aModel. #node->node. #status->#I})*(aModel atParameter: #beta)/(aModel atParameter: #N)
].
model atParameter: #beta assignValue: 1.
model atParameter: #gamma assignValue: 0.1.
2 to: 10 do: [:i| model atCompartment: {#status->#S. #node->i asString asSymbol} put: 100].
model atCompartment: { #status->#I. #node->#'1' } put: 1.
model atCompartment: { #status->#S. #node->#'1' } put: 99.
simulator := KESimulator new: #IBM from: 0.0 to: 50 by: 0.1.
simulator executeOn: model.
db := KENetworkBuilder new
data: simulator timeSeries;
network: (model atParameter: #network);
status: #(I); viewDataAtTime: 50.
db open
Visualising the infectious prevalence in each node of the network: