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
- BlobInitializer Steppable
- PIF Initializer
- PIFDumper Steppable
- Mitosis Steppable.
- box_watcher
- mu-parser
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:
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
or Blob
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 ")