The most powerful feature of Epigrass is the ability to use custom models. It allows the user to specify intra-node dynamics and, in doing so, break away from the limitations imposed by the built-in models.
By learning to write his/her own models, the user begins to realize the full potential of Epigrass, which goes beyond being a platform to simulate networked epidemics. In reality Epigrass can be used to model any distributed dynamical system taking place on a set of nodes (connected or not).
The best way to get started in writing custom models is to look at the example distributed with Epigrass. It can be found in demos/CustomModel_example.py:
# This is a custom model to used in place of Epigrass' built-in models. Custom
# models must always be on a file named CustomModel.py and contain at least
# a function named Model. Both the File name and the function Names are case-sensitive,
# so be careful. Please refer to the manual for intructions on how to write your
# own custom models.
def Model(self,vars,par,theta=0, npass=0):
"""
Calculates the model SIR, and return its values.
* vars The state variables of the models
* par The parameters (Beta, alpha, E,r,delta,B, w, p) see docs.
* theta = infectious individuals from neighbor sites
* npass = Total number of people arriving at this node
"""
# Get state variables' current values
if self.parentSite.parentGraph.simstep == 1: # if first step
# Define variable names to appear in the output
self.parentSite.vnames = ('Exposed','Infectious','Susceptible')
# And get state variables's initial values (stored in dict self.bi)
E,I,S = (self.bi['e'],self.bi['i'],self.bi['s'])
else: # if nor first step
E,I,S = vars
# Get parameter values
N = self.parentSite.totpop
beta,alpha,e,r,delta,B,w,p = (self.bp['beta'],self.bp['alpha'],
self.bp['e'],self.bp['r'],self.bp['delta'],self.bp['b'],
self.bp['w'],self.bp['p'])
#Vacination event (optional)
if self.parentSite.vaccineNow:
S -= self.parentSite.vaccov*S
# Model
Lpos = beta*S*((I+theta)/(N+npass))**alpha #Number of new cases
Ipos = (1-r)*I + Lpos
Spos = S + B - Lpos
Rpos = N-(Spos+Ipos)
# Update stats
self.parentSite.totalcases += Lpos #update number of cases
self.parentSite.incidence.append(Lpos)
# Raise site infected flag and add parent site to the epidemic history list.
if not self.parentSite.infected:
if Lpos > 0:
self.parentSite.infected = self.parentSite.parentGraph.simstep
self.parentSite.parentGraph.epipath.append(
(self.parentSite.parentGraph.simstep,
self.parentSite,self.parentSite.infector))
self.parentSite.migInf.append(Ipos)
return [0,Ipos,Spos]
Let’s analyze the above code. The first thing to notice is that an Epigrass custom model is a Python program. So anything you can do with Python in your system, you can do in your custom model. Naturally, your knowledge of the Python programming language will define how far you can go in this customization. There are a few requirements on this Python program in order to make it a valid custom model from Epigrass’s perspective.
In the beginning of the function you define a list of strings (self.parentSite.vnames) which will be the names used when storing the resulting time-series in the database. Choose strings that are not very long and are meaningful. You only need to do this once, ate the beginning of the simulation so put it inside an if statement, which will be executed only at time-step 1 (see code above).
After defining variable names, set their initial values in the same if clause. An else clause linked to this one will set variables values for the rest of the simulation.
Define local names for the total population N and fixed parameters.
Proceed to implement your model anyway you see fit.
Finally, this function must return a list/tuple with the values of the state variables in the same order as received in vars.
Warning
The strings in self.parentSite.vnames must be valid SQL variable names, or else you will have a insert error at the end of the simulation.
After defining this function with all its required features, you can continue to develop you custom model, writing other functions classes, etc. Note however, that only the Model function will be called by Epigrass, so any other code you add to your program must be called from within that function.
Note
Since CustomModel is imported from within Epigrass, any global code (unindented) in it is also executed. So you may add imports and other initialization code.
Warning
The name CustomModel.py is case-sensitive and cannot be changed. The same is true for the Model function.
From quickly going through the example Custom model above it probably became clear, to the Python-initiated, that Yous can access variables at the node and graph levels. This is possible because Model becomes a method in a node object which in is turn is contained into a graph object (see figure).
Besides being nested within the graph object, node and edge contain references to their containers. This means that using the introspective abilities of Python the user can access any information at any level of the full graph model and use it in the custom model. In order to help you do this, Let’s establish an API for developing custom models.
All attributes and methods (functions) from all around the simulation must be references from the model’s perspective, denoted by self. The parent objects can be accessed through the following notation:
Is the Site (node) containing the model.
Is the Graph containing the parent site of the model.
The following attributes and methods can be accessed by appending them to one one the objects above. For example:
self.parentSite.parentGraph.simstep
Not all attributes and methods are listed, only the most useful. For a complete reference, look at the source code documentation.
self.parentSite. Actually named siteobj in the source code.
Dictionary with initial values for all of the model’s state variables. Keys are the variable names.
Dictionary with initial values for all of the model’s parameters. Keys are the parameter names.
Initial total population
List containing the model output time series (variables in the same order of the model)
Incidence time series
Has the site been already infected? (logical variable)
Site’s name (provided in the .csv)
Tuple containing extra-variables provided by .csv file
Graph to which Site belongs (see class Graph)
List containing all edge objects connected to Site
List containing all inbound edges
List containing all outbound edges
Site’s geocode
Type of dynamic model running in Site
Time and coverage of vaccination event. Format as in .epg
Flag indicating that it is vaccine day (0 or 1)
Current vaccination coverage
At time t, the population is vaccinated with coverage cov
Returns list of outbound edges
Returns list of inbound edges
Returns a dictionary of neighbor sites as keys and distances as values
Returns the distance in km from a given neighbor
Returns degree of this site, that is, the number of sites connected to it
Not all attributes and methods are listed, only the most useful. For a complete reference, look at the source code documentation.
self.parentSite.parentGraph
Time-step of the simulation. Use it to keep track of the simulation progress.
The speed of the transportation system
Final time-step of the simulation
Current size of the epidemic, graph-wise.
Full list of nodes in the graph. Each element in this list is a real node object.
Full list of nodes in the graph. Each element in this list is a real node object.
Returns an site object named name