Steppable Section

Steppables are CompuCell modules that are called every Monte Carlo Step (MCS). More precisely, they are called after all the pixel copy attempts in a given MCS have been carried out. Steppables may have various functions - for example solving PDEs, checking if critical concentration threshold have been reached, updating target volume or target surface given the concentration of come growth factor, initializing cell field, writing numerical results to a file, etc… In general, steppables perform all functions that need to be done every MCS. In the reminder of this section we will present steppables currently available in the CompuCell3D and describe their usage.

Tip

It is most convenient to implement Steppables in Python. However, in certain situations where code performance is an issue users can implement steppables in C++

This section “off-the-shelf” steppables that are available in CC3D and were implemented using C++

BoxWatcher Steppable

Warning

Functionality of this module has been reduced in CC3D versions that support parallel computations (3.6.0 and up). Main motivation for this module was to speed up computations but with parallel version the need for this module is somewhat smaller.

This steppable can potentially speed-up your simulation. Every MCS (or every Frequency MCS) it determines maximum and minimum coordinates of cells and then imposes slightly bigger box around cells and ensures that in the subsequent MCS pixel copy attempts take place only inside this box containing cells (plus some amount of medium on the sides). Thus, instead of sweeping entire lattice and attempting random pixel copies CompuCell3D will only spend time trying flips inside the box. Depending on the simulation the performance gains are up to approx. 30%. The steppable will work best if you have simulation with cells localized in one region of the lattice with lots of empty space. The steppable will adjust box every MCS (or every Frequency MCS) according to evolving cellular pattern.

The syntax is as follows:

<Steppable Type="BoxWatcher">
    <XMargin>5</XMargin>
    <YMargin>5</YMargin>
    <ZMargin>5</ZMargin>
</Steppable>

All that is required is to specify amount of extra space (expressed in units of pixels) that needs to be added to a tight box i.e. the box whose sides just touch most peripheral cells’ pixels.

BlobInitializer Steppable

BlobInitializer steppable is used to lay out circular blob of cells on the lattice.

An example syntax where we create one circular region of cells in the lattice is presented below:

<Steppable Type="BlobInitializer">
   <Region>
     <Gap>0</Gap>
     <Width>5</Width>
     <Radius>40</Radius>
     <Center x="100" y="100" z="0"/>
     <Types>Condensing,NonCondensing</Types>
   </Region>
</Steppable>

Similarly as for the UniformFieldInitializer users can define many regions each of which is a blob of a particular center point, radius and list of cell types that will be assigned to cells forming the blob. Listing types in the <Types> tag follows same rules as in the UniformInitializer.

Note

Original (and deprecated) syntax of this plugin looks as follows:

<Steppable Type="BlobInitializer">
    <Gap>0</Gap>
    <Width>5</Width>
    <CellSortInit>yes</CellSortInit>
    <Radius>40</Radius>
</Steppable>

The blob is centered in the middle of th lattice and has radius given by <Radius> parameter. All cells are initially squares (or cubes in 3D) where <Width> determines the length of the cube or square side and <Gap> determines space between squares or cubes. <CellSortInit> tag and value yes is used to initialize cells randomly with type id being either 1 or 2 . Otherwise all cells will have type id 1. This can be easily modified in Python .

PIF Initializer

To initialize the configuration of the simulation lattice we can write custom lattice initialization file. Our experience suggests that you will probably have to write your own initialization files rather than relying on built-in initializers. The reason is simple: the built-in initializers implement very simple cell layouts, and if you want to study more complicated cell arrangements, the built-in initializers will not be very helpful. Therefore, we encourage you to learn how to prepare lattice initialization files. We have developed CellDraw tool which is a part of CC3D suite and it allows users to draw initial cell layout in a very intuitive way. We encourage you to read “Introduction to CellDraw” to familiarize yourself with this tool.

To import custom cell layouts, CompuCell3D uses very simple Potts Initial File PIF file format. It tells CompuCell3D how to lay out assign the simulation lattice pixels to cells.

The PIF consists of multiple lines of the following format:

cell# celltype x1 x2 y1 y2 z1 z2

Where cell# is the unique integer index of a cell, celltype is a string representing the cell’s initial type, and x1 and x2 specify a range of x-coordinates contained in the cell (similarly y1 and y2 specify a range of y-coordinates and z1 and z2 specify a range of z-coordinates). Thus each line assigns a rectangular volume to a cell. If a cell is not perfectly rectangular, multiple lines can be used to build up the cell out of rectangular sub-volumes (just by reusing the cell# and celltype).

A PIF can be provided to CompuCell3D by including the steppable object PIFInitializer

Let’s look at a PIF example for foams:

0 Medium 0 101 0 101 0 0
1 Foam 13 25 0 5 0 0
2 Foam 25 39 0 5 0 0
3 Foam 39 46 0 5 0 0
4 Foam 46 57 0 5 0 0
5 Foam 57 65 0 5 0 0
6 Foam 65 76 0 5 0 0
7 Foam 76 89 0 5 0 0

These lines define a background of Medium which fills the whole lattice and is then overwritten by seven rectangular cells of type Foam numbered 1 through 7 . Notice that these cells lie in the xy plane (z1=0 z2=0 implies that cells have thickness =1) so this example is a two-dimensional initialization.

You can write the PIF file manually, but using a script or program that will write PIF file for you in the language of your choice (Perl, Python, Matlab, Mathematica, C, C++, Java or any other programming language) will save a great deal of typing.

Notice, that for compartmental cell model the format of the PIF file is different:

Include Clusters
cluster # cell# celltype x1 x2 y1 y2 z1 z2

For example:

Include Clusters
1 1 Side1 23 25 47 56 10 14
1 2 Center 26 30 50 54 10 14
1 3 Side2 31 33 47 56 10 14
1 4 Top 26 30 55 59 10 14
1 5 Bottom 26 30 45 49 10 14
2 6 Side1 35 37 47 56 10 14
2 7 Center 38 42 50 54 10 14
2 8 Side2 43 45 47 56 10 14
2 9 Top 38 42 55 59 10 14
2 10 Bottom 38 42 45 49 10 14

Tip

An easy way to generate PIF file from the current simulation snapshot is to use Player Tools->Generate PIF file from current snapshot… menu option. Alternatively we can use PIFDumper steppable discussed next.

PIFDumper Steppable

This steppable does the opposite to PIFIitialize``r it writes PIF file of current lattice configuration. The syntax similar to the syntax of ``PIFInitializer:

<Steppable Type="PIFDumper" Frequency=”100”>
    <PIFName>line</PIFName>
</Steppable>

Notice that we used Frequency attribute of steppable to ensure that PIF files are written every 100 MCS. Without it they would be written every MCS. The file names will have the following format: PIFName.MCS.pif

In our case they would be line.0.pif, line.100.pif, line.200.pif, etc…

This module is actually quite useful. For example, if we want to start simulation from a more configuration of cells (not rectangular cells as this is the case when we use Uniform, Blob, or Polygon initializers). In such a case we would run a simulation with a PIFDumper included and once the cell configuration reaches desired shape we would stop and use PIF file corresponding to this state. Once we have PIF initial configuration we may run many simulation starting from the same, realistic initial condition.

Tip

Restarting simulation from a given configuration is actually even easier in the recent versions of CC3D. All you have to do is to create .``cc3d`` project where you add serialization optyion CC3D will be savbing complete snapshots of the simulation (including PIF files) and you can easily restart the simulation from a given end-point of the previous run. For more details see “Python Scripting Manual” https://pythonscriptingmanual.readthedocs.io/en/latest/restarting_simulations.html?highlight=restart

Tip

You can also generate PIF file from the current simulation snapshot by using Player tool: Tools->Generate PIF file from current snapshot…

Mitosis Steppable.

Mitosis steppable is described in great detail in “Python Scripting Manual” - see for example

https://pythonscriptingmanual.readthedocs.io/en/latest/mitosis.html?highlight=mitosis

but because of its importance we are including a copy of that description here.

In developmental simulations we often need to simulate cells which grow and divide. In earlier versions of CompuCell3D we had to write quite complicated plugin to do that which was quite cumbersome and unintuitive. The only advantage of the plugin was that mitosis was taking place immediately after the pixel copy which had triggered mitosis condition. This guaranteed that any cell which was supposed divide at any instance in the simulation, actually did. However, because state of the simulation is normally observed after completion of full a Monte Carlo Step, and not in the middle of MCS it makes actually more sense to implement Mitosis as a steppable. Let us examine the simplest simulation which involves mitosis. We start with a single cell and grow it. When cell reaches critical (doubling) volume it divides. We check if the cell has reached doubling volume at the end of each MCS. The folder containing this simulation is

Demos/CompuCellPythonTutorial/steppableBasedMitosis. The mitosis algorithm is implemented in Demos/CompuCellPythonTutorial/steppableBasedMitosis/Simulation/steppableBasedMitosisSteppables.py

File:

Demos/CompuCellPythonTutorial/steppableBasedMitosis/Simulation/steppableBasedMitosisSteppables.py

from PySteppables import *
from PySteppablesExamples import MitosisSteppableBase
import CompuCell

class VolumeParamSteppable(SteppableBasePy):
    def __init__(self, _simulator, _frequency=1):
        SteppableBasePy.__init__(self, _simulator, _frequency)

    def start(self):
        for cell in self.cellList:
            cell.targetVolume = 25
            cell.lambdaVolume = 2.0

    def step(self, mcs):
        for cell in self.cellList:
            cell.targetVolume += 1

class MitosisSteppable(MitosisSteppableBase):
    def __init__(self, _simulator, _frequency=1):
        MitosisSteppableBase.__init__(self, _simulator, _frequency)

        # 0 - parent child position will be randomized between mitosis event
        # negative integer - parent appears on the 'left' of the child
        # positive integer - parent appears on the 'right' of the child
        self.setParentChildPositionFlag(-1)

    def step(self, mcs):
        cells_to_divide = []
        for cell in self.cellList:
            if cell.volume > 50:
                cells_to_divide.append(cell)

        for cell in cells_to_divide:
            # to change mitosis mode leave one of the below lines uncommented
            self.divideCellRandomOrientation(cell)

    def updateAttributes(self):
        self.parentCell.targetVolume /= 2.0  # reducing parent target volume
        self.cloneParent2Child()

        if self.parentCell.type == self.CONDENSING:
            self.childCell.type = self.NONCONDENSING
        else:
            self.childCell.type = self.CONDENSING

Two steppables:`` VolumeParamSteppable`` and MitosisSteppable are the essence of the above simulation. The first steppable initializes volume constraint for all the cells present at T=0 MCS (only one cell) and then every 10 MCS (see the frequency with which VolumeParamSteppable in initialized to run - Demos/CompuCellPythonTutorial/steppableBasedMitosis/Simulation/steppableBasedMitosis.py) it increases target volume of cells, effectively causing cells to grow.

from steppableBasedMitosisSteppables import VolumeParamSteppable
volumeParamSteppable=VolumeParamSteppable(sim ,10)
steppableRegistry.registerSteppable(volumeParamSteppable)

from steppableBasedMitosisSteppables import MitosisSteppable
mitosisSteppable=MitosisSteppable(sim, 10)
steppableRegistry.registerSteppable(mitosisSteppable)

The second steppable checks every 10 MCS (we can, of course, run it every MCS) if cell has reached doubling volume of 50. If it did such cell is added to the list cells_to_divide. After construction of cells_to_divide is complete we iterate over this list and divide all the cells in it.

Warning

It is important to divide cells outside the loop where we iterate over entire cell inventory. If we keep dividing cells in this loop we are adding elements to the list over which we iterate over and this might have unwanted side effects. The solution is to use use list of cells to divide as we did in the example.

Notice that we call self.divideCellRandomOrientation(cell) function to divide cells. Other modes of division are available as well and they are as follows:

self.divideCellOrientationVectorBased(cell,1,0,0)
self.divideCellAlongMajorAxis(cell)
self.divideCellAlongMinorAxis(cell)

Notice that MitosisSteppable inherits MitosisSteppableBase class (defined in PySteppablesExamples.py).It is the base class which ensures that after we call any of the cell dividing function (e.g. divideCellRandomOrientation) CompuCell3D will automatically call updateAttributes function as well. updateAttributes function is very important and we must call it in order to ensure integrity and sanity of the simulation. During mitosis a new cell is created (accessed in Python as childCell – defined in MitosisSteppableBase - self.mitosisSteppable.childCell) and as such this cell is uninitialized. It does have default attributes (read-only) of a cell such as volume, surface (if we decide to use surface constraint or SurfaceTracker plugin) but all other parameters of such cell are set to default values. In our simulation we have been setting targetVolume and lambdaVolume individually for each cell. After mitosis childCell will need those parameters to be set as well. To make things more interesting, in our simulation we decided to change type of cell to be different than type of parent cell. In more complex simulations where cells have more attributes which are used in the simulation, we have to make sure that in the updateAttributes function childCell and its attributes get properly initialized. It is also very common practice to change attributes of parentCell after mitosis as well to account for the fact that parentCell is not the original parentCell from before the mitosis.

Note

If you specify orientation vector for the mitosis the actual division will take place along the line/plane perpendicular to this vector.

Note

The name of the function where we update attributes after mitosis has to be exactly updateAtttributes. If it is called differently CC3D will not call it automatically. We can of course call such function by hand, immediately we do the mitosis but this is not very elegant solution.

muParser

CC3D uses muParser to allow users specify simple mathematical expressions in the XML (or XML-equivalent Python scripts). The following link points to full specification of the muParser: http://muparser.sourceforge.net/mup_features.html#idDef2. The general guideline to using muParser syntax inside XML is to enclose muParser expression between <![CDATA[ and ]]> :

<XML_ELEMENT_WITH_MUPARSER_EXPRESSION>
    <![CDATA[
        MUPARSER EXPRESSION
    ]]>
</XML_ELEMENT_WITH_MUPARSER_EXPRESSION>

For example:

<AdditionalTerm>
    <![CDATA[
        CellType<1 ? 0.01*F : 0.15*F
    ]]>
</AdditionalTerm>

The reason for enclosing muParser expression between <![CDATA[ and ]]> is to prevent XML parser from interpreting < or > as beginning or end of the XML elements

Alternatively you may replace XML with equivalent Python syntax in which case things will look a bit simpler:

DiffusionDataElmnt_2.ElementCC3D("AdditionalTerm",{}," CellType<1 ? 0.01*F : 0.15*F ")