Documentation for Release 2024.1
Cell-based Chaste Code Structure
This page is intended for new cell-based Chaste users/developers. For more information, see the technical Chaste papers.
Language and external libraries
The main Chaste code has been written in C++ because it is a fast, object-oriented language. Object-oriented languages lead naturally to more modular code – software which is easier to abstract, to modify, and to document.
All functional code developed for Chaste has a corresponding test suite. Since we use test-driven development, the test suites are written at the same time as the functionality and are of comparable length. The test suites are written using the C++ library CxxTest, which is a light-weight portable testing framework. A typical test suite file for a Chaste C++ class will include approximately 10 individual test methods. Each method will instantiate and set up relevant classes, check that no exceptions are thrown and then check that any numerical output is correct within some given tolerance.
In order to make the best use of mature linear algebra routines and to ease the route to rapid parallelisation of parts of the code, we use PETSc for sparse large-scale linear algebra and MPI for parallelisation. Since the core linear algebra used within Chaste is a small subset of PETSc, we have provide light-weight wrappers to common routines such as the creation of sparse matrices, choice of pre-conditioners and iterative solution of linear systems.
Also used throughout the code are: the Standard Template Library (for extensible vector structures and iterators); Boost uBLAS (for small-scale sequential vectors and matrices); Boost Serialization (for check-pointing and parameter definitions in the cell-based code); and CodeSynthesis XSD (for XML parameter input in cardiac-related code). In the area of ODE solvers, we provide an option to use light-weight interfaces to link against the CVODE solvers provided by SUNDIALS. In the area of fault-tolerance and check-pointing there is, at present, only limited functionality in Chaste. We currently use the Boost Serialization library to provide portable check-point files for crypt simulations.
Chaste libraries
Computational modelling of both the heart
and cell_based
domains relies
heavily on
- accurate representation of a complex, realistic geometry provided primarily through unstructured finite-element meshes;
- conversion, using the finite element method (FEM), of PDEs from a given domain into a sparse system of linear (or non-linear) equations, known as assembly;
- the iterative solution of large sparse systems via PETSc; and
- the numerical solution of ODE systems.
These four common features of the code become four component libraries: mesh
,
pde
, linalg
and ode
respectively. They are supported by two lower-level
libraries, global
(which is responsible for initialisation and book-keeping
calls to PETSc as well as taking care of exceptions) and io
(which handles
input and output, mainly via wrapped calls to
HDF5 functionality). Built upon
these are the two modelling components of Chaste, heart
and cell_based
. Each
of the six core components and the two main modelling components comprise a
number of interacting C++ classes, each in their own source file, together with
a set of CxxTest test suites. Coverage of the functionality of each component by
its test suites is verified on a daily basis using gcov, which is part of the
GNU GCC project.
The build system used is CMake. When a developer compiles the code of Chaste, they typically build and test either a single test-suite, a library component or an entire raft of test-suites – known as the Continuous test pack. In every case, code is compiled as necessary and executables are constructed corresponding to each test-suite. These test-suite executables are run as they become available and summary pass-fail web-pages are produced for the entire set of tests. This system makes it easy for a developer to understand the impact that a change in source code has across the entire archive of test-suites.
Frequently used classes
A few classes are frequently used throughout the code-base:
Singleton classes: There are a number of C++ objects used in physiological simulations for which there ought to be at most one instantiation per simulation. Notably, if simulation parameters are needed it is important that they are recorded and used in a consistent way. For this reason these classes use a singleton design pattern. Such classes are accessed not by a constructor as usual, but by an
Instance()
method. When theInstance()
method is first called, then either the singleton object is taken and modified or, if no object exists, a new one is created using a set of default configuration parameters. At the end of each test, aDestroy()
method must be called on the singleton object. Singleton classes used by thecell_based
code include:SimulationTime
, which provides a consistent current time to the multiple time-dependent components of cell-based simulations;RandomNumberGenerator
, which records how pseudo-random numbers are seeded and logs every use, so can produce repeatable streams of random numbers even if the generator is stored in a check-point and restarted; andCellwiseData
, which stores higher-level information that is accessible by each cell (such as the local oxygen concentration).Time-stepping: The use of time-stepping is common within all physiological models. The idea behind the time-stepper is to provide a robust way to deal with time loops. A naive programmer might explicitly state the number of steps in a simulation and iterate using fixed point numbers or they might iterate using a floating point loop and trust that an inequality test will always give the expected answer. Since neither of these alternatives is ideal, the timestepper uses an incremented integer counter to store its state (and to calculate the current time) and will always finish at the correct end time. This may be achieved by making the final time-step smaller than others.
Meshes: All mesh geometry within Chaste is based on simplices (triangles in 2D, tetrahedra in 3D). There is a hierarchy of meshes inheriting from an
AbstractMesh
class which are templated over the dimensionality of their elements (1 for beams, 2 for triangles or 3 for tetrahedra) and over the space in which they are embedded. Normally the dimensions of the elements and of the embedding space will be the same, but it is possible to introduce a 1D mesh in 3D or a 2D mesh in 3D (say, to model a monolayer of cells in a colonic crypt). There are mesh readers and writers available for a variety of file formats. The default concrete mesh class isTetrahedralMesh
but there are three notable others:MutableMesh
, which supports changes in mesh geometry and topology, as well as re-meshing via external library calls (Triangle and Tetgen provide re-meshing functionality in 2D and 3D respectively);ParallelTetrahedralMesh
which partitions the geometry using METIS and then loads only a required part of the mesh (and corresponding halo nodes) into each process; andQuadraticMesh
, which has more nodes per element than the meshes made from linear simplices.Finite element solvers: A hierarchy of finite element assemblers and solvers exist within the Chaste
pde
library. The role of the assemblers and solvers, between them, is to take a mathematical description of a set of PDEs together with their boundary conditions on a relevant mesh, to form a corresponding linear system and solve it. A straightforward assembler-solver would take a tetrahedral mesh and the weak form of a static (time-independent) PDE together with appropriate boundary conditions, form a linear system and solve it via a wrapped call to a PETSc linear system solver.
Classes specific to cell_based
We now provide an overview of the structure of the cell_based
codebase:
Cell-cycle models and cells: At the lowest level in the code are the cell-cycle models. Examples of cell-cycle models include those where division occurs after a fixed time, or after a normally-distributed time, and those where cell-division is dependent on the local oxygen concentration. A
Cell
object then takes in a cell-cycle model, and provides all the functionality required of a single cell, in particular: functionality relating to age, type, generation, and mutational status; functionality to undergo mitosis; and functionality to undergo apoptosis (programmed cell death).Cylindrical mesh and cell population classes: Crypt simulations on cylindrical geometries make use of the
Cylindrical2dMesh
class, which inherits fromMutableMesh
but implements the extra functionality required for enforcing periodic boundaries, including the ability to remesh on a cylindrical domain. This means that no other component has to deal with issues relating to the imposition of periodic boundary conditions. TheMeshBasedCellPopulation
class then takes in a set of cells and a mesh, which may be cylindrical or not, and maintains coherence between the two as cells are added or deleted as part of cell-birth or cell-death. Note that theMeshBasedCellPopulation
class is for off-lattice models where cell-connectivity is determined through triangulation – a number of other cell population classes exist that consider alternative ways to define cell-based models. These includeNodeBasedCellPopulation
, which defines ‘overlapping spheres’ models, where connectivity is based on relative distance, andVertexBasedCellPopulation
, in which each cell is represented by a finite set of vertices that define its boundary.Force and update-rule objects: Several force objects have been defined, the simplest being
GeneralisedLinearSpringForce
. This inherits from theAbstractForce
class, which defines an interface for all force objects. Further force classes have been implemented, each inheriting fromAbstractForce
, and each only implementing the relevant contribution to the force, not the total force. Since cell-based simulation objects (defined below) can take in any number ofAbstractForce
classes, simulations using a wide range of (total) force laws can be put together in a straightforward manner by picking the required contributory force classes. Just as off-lattice simulations incorporate force laws, on-lattice simulations use aUpdateRule
class hierarchy.Cell-killer objects: Cell-killer objects contain rules used to determine which cells should be removed from the simulation and when. For example, a
SloughingCellKiller
has been introduced to identify cells for sloughing in the crypt by virtue of their position - cells above a certain height are removed to simulate sloughing into the lumen. Additionally, anOxygenBasedCellKiller
is used in tumour spheroid simulations to eliminate cells whose local nutrient concentration is too low for them to remain alive.Cell-based simulation classes: At the highest level are the cell-based simulation classes, such as
CryptSimulation2d
. These inherit common functionality from aAbstractCellBasedSimulation
class, which deals with cell-birth and death, and is written to run in all spatial dimensions, so that the same code is used to simulate a 2D crypt or a 3D tumour spheroid. Derived classes, such asCryptSimulation2d
, deal with application-specific concerns only (such as boundary conditions or integrating the PDE governing nutrient concentration, respectively). These objects take in a cell population, any number of force classes, and any number of cell killers. Tumour spheroid simulations are set up in a similar manner.
The object-based structure, in addition to being amenable to component-wise testing as described above, is especially advantageous in such discrete cell-based models, since it allows several different simulations to be created in a straightforward manner, by using whichever components are desired, and prevent unnecessary repetition of code shared by the crypt and tumour spheroid models. More examples are available as tutorials.