Contact graph - cellmodeller/CellModeller GitHub Wiki
One of the new features developed in the 4.2.1 CellModeller release is the ability to build store contact information in the cellstate and build contact networks. This feature allows cells to know which other cells they are in physical contact with, allowing us model contact-dependent behaviour, as well as to represent cellular arrangements by way of a network.
This feature was motivated to model contact dependent behaviour, in particular plasmid conjugation, where mobile DNA plasmids can be transferred from one bacterium to another. This feature can also have useful applications in several other fields, such as studying dense bacterial populations as granular materials, where the contact network could be important. There are several ways of doing this, depending on the application:
NB. This script requires the use of the great python package networkx (version 1). To install this please use conda to do this (ie. run conda install networkx=1. You may have to do this when inside your miniconda/bin folder).
The simplest way to extract the contact information from a saved instance of the simulation is by running a script that generates a graph object from a ‘.pickle’ file, and subsequently draws the graph network. This script requires no changes to the model, and can be used on any pickle. In ‘vanilla’ CellModeller, each cells contacts are NOT stored in the cell state, and this script calculates the contacts in a saved instance of a simulation when run, thus not requiring any modifications to a model.
To do this, use the script contactGraph.py on a pickle file, eg. $ cmpython /Scripts/contactGraph.py $DATA/step-#####.pickle on a file within your data folder (please amend the path as required). This script works very much like the Draw2PDF.py script (detailed here), and generates a .pdf file from the pickle, revealing the network of contacts:

The script draws each cell as a node of a network (coloured by the cellstate color variable), and draws an edge between each cell in contact. The script does this by turning the colony into a networkx network object, and the networkx package has a range of resources to analyze the network. In order to do this, you could alter the script after line 123: get_current_contacts(G, data). At this point, variable G is a network object, and can be analyzed with the tools within the networkx package. For example, lines 126 and 127 show how to to obtain the mean degree of the network:
degrees = G.degree().values()
print "mean degree = " + str(np.mean(degrees))In order to draw videos in this network representation, the script contactVideo.sh is provided, and can be used in exactly the same manner as the video.sh script (instructions here).
In order to model contact dependent behaviours and allow cells to store their neighbours in the cellstate, the model needs to be altered very slightly. The contact monitoring slows the simulation a little, so we decided to make it an optional feature that can be turned on when necessary.
Let’s look at the example model Conjugation.py found here. To turn on the feature, the biophysics module needs to be initialized with the argument compNeighbours=True. This now means that the ids of all the neighbouring cells is saved in the cellstate in a list called neighbours.
To demonstrate modelling cell dependent behaviour, this model provides a simple model of conjugation, whereby a mobile plasmid can transfer from one cell to another. In this case, there are two initial cell types, an acceptor (cellType=0), and a donor (cellType=1):
sim.addCell(cellType=0, pos=(-5,0,0), dir=(1,0,0), length=delta,rad=0.4) #acceptor
sim.addCell(cellType=1, pos=(5,0,0), dir=(1,0,0), length=delta,rad=0.4) #donorWhen a donor cell comes into contact with an acceptor, it creates a transconjugant, meaning that the plasmid from the donor has infected the acceptor. Since the infectious plasmid DNA codes for the machinery to infect others, the transconjugant can pass infect other acceptors.
This is modeled in the update step (lines 47 – 53):
infect_chances = 0
if cell.cellType == 0: #if a cell is an acceptor,
for index in cell.neighbours:#loop through all contacts
if cells[index].cellType != 0: #if donor or transconjugant is in contact
if random.random() < cells[index].effGrowth/10.0: # probability of infection is proportional to donor growth rate
cell.cellType = 2 #become transconjugantDuring this step, if the cell is an acceptor, it will loop through all of its contacts. If there is a donor or transconjugant present in the list of neighbours, there is some probability of infection. In this case, the probability of infection is proportional to the growth rate of the donor cell. If the infection happens, the acceptor becomes a transconjugant and can subsequently infect others. The precise model of infection on a single cell level is an open question, and we believe models such as this will go a long way to understanding the process.
Here is a video of this simulation in both the normal and network representations: Donors are in green, acceptors in red, and transconjugants in blue.
