In the previous cell-based Chaste tutorials, we used existing cell-cycle models to define how cells
proliferate. In this tutorial, we show how to create a new cell-cycle model class, and how this
can be used in a cell-based simulation.
The next header includes the Boost shared_ptr smart pointer, and defines some useful
macros to save typing when using it.
The next header includes the NEVER_REACHED macro, used in one of the methods below.
The next header defines a base class for simple generation-based cell-cycle models.
A cell-cycle model is defined as simple if the duration of each phase of the cell
cycle is determined when the cell-cycle model is created, rather than
evaluated on the fly (e.g. by solving a system of ordinary differential
equations for the concentrations of key cell cycle proteins), and may
depend on the cell type. A simple cell-cycle model is defined as generation-based if it keeps track of the
generation of the corresponding cell, and sets the cell type according
to this. Our new cell-cycle model will inherit from this abstract class.
The remaining header files define classes that will be used in the cell-based
simulation test. We have encountered each of these header files in previous cell-based Chaste
tutorials, except for CheckReadyToDivideAndPhaseIsUpdated(), which defines a helper
class for testing a cell-cycle model.
As an example, let us consider a cell-cycle model in which the durations
of S, G2 and M phases are fixed, but the duration of G1 phase is an exponential
random variable with rate parameter λ. This rate parameter is a constant, dependent on cell type, whose value is
chosen such that the mean of the distribution, 1/λ, equals the mean
G1 duration as defined in the AbstractCellCycleModel class. We will also assume that
cells divide a certain number of generations before becoming differentiated. To implement this model we define a new cell-cycle model, MyCellCycleModel,
which inherits from AbstractSimpleGenerationalCellCycleModel and
overrides the SetG1Duration() method.
Note that usually this code would be separated out into a separate declaration in
a .hpp file and definition in a .cpp file.
We only need to include the next block of code if we wish to be able
to archive (save or load) the cell-cycle model object in a cell-based simulation.
The code consists of a serialize method, in which we first archive the cell
cycle model using the serialization code defined in the base class
AbstractSimpleGenerationalCellCycleModel. We then archive an instance
of the RandomNumberGenerator singleton class, which is used in the
SetG1Duration() method. Note that serialization of singleton objects
must be done with care. Before the object is serialized via a pointer, it must
be serialized directly, or an assertion will trip when a second instance of the
class is created on de-serialization.
We override the SetG1Duration() method as follows.
As we will access the cell type of the cell associated with this cell
cycle model, we should assert that this cell exists.
We now set the G1 duration based on cell type. For stem and transit cells, we use the RandomNumberGenerator
singleton class to generate a random number U drawn from U[0,1], and
transform this into a random number T drawn from Exp(λ) using
the transformation T = -log(U)/λ. For differentiated cells, which do not progress through the
cell cycle, we set the G1 duration to DBL_MAX.
The first public method is a default constructor, which just calls the base
constructor.
The second public method overrides CreateCellCycleModel(). This is a
builder method to create new copies of the cell-cycle model. We first create
a new cell-cycle model, then set each member variable of the new cell-cycle
model that inherits its value from the parent.
There are a number of things to mention regarding the CreateCellCycleModel()
method: these are quite technical, but are worth stating here for the sake of
completeness. If we look at which member variables
MyCellCycleModel inherits from its base class, we will find that some of
these member variables are not set here. This is for two main reasons. First, some
of the new cell-cycle model’s member variables (namely mBirthTime,
mCurrentCellCyclePhase, mReadyToDivide) will already have been
correctly initialized in the new cell-cycle model’s constructor. Second, the
member variable mDimension remains unset, since this cell-cycle
model does not need to know the spatial dimension, so if we were to call
SetDimension() on the new cell-cycle model an exception would be triggered;
hence we do not set this member variable. It is also worth noting that in a simulation,
one or more of the new cell-cycle model’s member variables
may be set/overwritten as soon as InitialiseDaughterCell() is called on
the new cell-cycle model; this occurs when the associated cell has called its
Divide() method.
We need to include the next block of code if you want to be able to archive (save or load)
the cell-cycle model object in a cell-based simulation. It is also required for writing out
the parameters file describing the settings for a simulation - it provides the unique
identifier for our new cell-cycle model. Thus every cell-cycle model class must provide this,
or you’ll get errors when running simulations.
Since we’re defining the new cell-cycle model within the test file, we need to include the
following stanza as well, to make the code work with newer versions of the Boost libraries.
Normally the above export declaration would occur in the cell-cycle model’s .hpp file, and
the following lines would appear in the .cpp file. See Boost Serialization Guide for
more information.
This completes the code for MyCellCycleModel. Note that usually this code would
be separated out into a separate declaration in a .hpp file and definition in a .cpp file.
We begin by testing that our new cell-cycle model is implemented correctly.
Test that we can construct a MyCellCycleModel object:
Now we construct and initialise a large number of MyCellCycleModels and
associated cells:
To check the CCM has been set up correctly we get a pointer to the one stored on the first cell.
We use a static_cast so we can access all the member variables in the concrete class MyCellCycleModel.
Find the mean G1 duration and test that it is within some tolerance of
the expected value:
Now construct another MyCellCycleModel and associated cell. To check it works for transit cells.
Use the helper method CheckReadyToDivideAndPhaseIsUpdated() to
test that this cell progresses correctly through the cell cycle.
The numbers for the G1 duration below is taken from the first
random number generated:
Lastly, we briefly test that archiving of MyCellCycleModel has
been implemented correctly. Create an OutputFileHandler and use
this to define a filename for the archive.
Create an output archive.
Destroy the current instance of SimulationTime and create another instance.
Set the start time, end time and number of time steps.
Create a cell with associated cell-cycle model.
Move forward two time steps.
Set the birth time of the cell and update the cell cycle phase.
Now archive the cell-cycle model through its cell.
Now create an input archive. Begin by again destroying the current
instance of SimulationTime and creating another instance. Set
the start time, end time and number of time steps.
Create a pointer to a cell.
Create an input archive and restore the cell from the archive.
Test that the private data has been restored correctly. Note we cast it to the correct type
so we can acess all the member variables
Using the cell-cycle model in a cell-based simulation#
We conclude with a brief test demonstrating how MyCellCycleModel can be used
in a cell-based simulation.
We use the honeycomb mesh generator to create a honeycomb mesh covering a
circular domain of given radius.
Get the mesh using the GetCircularMesh() method.
Next, we create some cells. First, define the cells vector.
We must create a shared_ptr to a CellMutationState with which to bestow the cells.
We make use of the macro MAKE_PTR to do this: the first argument is the class and
the second argument is the name of the shared_ptr.
Then we loop over the nodes.
For each node we create a cell with our cell-cycle model.
Now, we define a random birth time, chosen from [-T,0], where
T = t,,1,, + t,,2,,, where t,,1,, is a parameter representing the G,,1,, duration
of a stem cell, and t,,2,, is the basic S+G,,2,,+M phases duration.
We then set the birth time and push the cell back into the vector of cells.
Now that we have defined the mesh and cells, we can define the cell population. The constructor
takes in the mesh and the cells vector.
We then pass in the cell population into an OffLatticeSimulation,
and set the output directory and end time.
We create a force law and pass it to the OffLatticeSimulation.