Building Steppable ================== It is probably best to start discussing extension of CC3D by showing a relatively simple example of a steppable written in C++. In typical scenario steppables are written in Python. There are three main reasons for that **1)** No compilation is required. **2)** The code is more compact and easier to write than C++. **3)** Python has a rich set of libraries that make scientific computation easily accessible. However, writing a steppable in C++ is not that much more difficult, as you will see shortly, and you are almost guaranteed that your code will run orders of magnitude faster. Let me rephrase this last sentence - a **typical** code written in C++ is orders of magnitude faster than equivalent code written in pure Python. Since most of the steppable code consists of iterating over all cells and adjusting their attributes, C++ will perform this task much faster. Getting started --------------- Before you start developing CC3D C++ extension modules, you need to clone CC3D repository. .. code-block:: console mkdir CC3D_DEVELOP cd CC3D_DEVELOP git clone https://github.com/CompuCell3D/CompuCell3D.git . It is optional to checkout a particular branch of CC3D, but most often you will work with ``master`` branch. If , however, you want to checkout a branch - you would type something like this: .. code-block:: console git checkout 4.0.0 |git_setup| At this point you have complete code in ``CC3D_DEVELOP`` directory. Now we open Twedit++ - you need to have "standard" installation of CC3D on your machine available - and go to ``CC3D C++`` menu and choose ``Generate New Module...`` entry and fill out the dialog box: |twedit_steppable_wizard| .. note:: In this example we show how to generate steppable code template in the "main" CC3D code. However, a more frequently used scenario is to Generate steppable code in "DeveloperZone" folder. We will show it in the subsequent chapter This is exactly what we did: #. We specify a name of a steppable as ``GrowthSteppable`` #. We specify the location of when steppable code is stored ``/Users/m/CC3D_DEVELOP/CompuCell3D/core/CompuCell3D/steppables``. Note, that we point to the cloned CC3D repository that we originally stored in ``CC3D_DEVELOP`` subdirectory. It happens that the path to this repository is ``/Users/m/CC3D_DEVELOP``. #. We select **Steppable** radio in the **C++ Module Type** panel. We also check ``Python Wrap`` checkbox to allow generation of Python bindings of this steppable. When we press ``OK`` button Twedit++ will generate a complete set of template files that could be compiled as-is and the steppable will run. Obviously our goal is to modify template file to generate steppable w want. In current implementation of CC3D Twedit++ generates or modifies approximately 10 files. |twedit_generated_steppable| As you can see in the ``CMakeLists.txt`` file Twedit++ modified this file and added line ``ADD_SUBDIRECTORY(GrowthSteppable)`` Now, let us focus on modifying template files and creating a steppable (``GrowthSteppable``) we specify growth rate in the XMl and allow modification of this rate from Python. Let's first examine the header of the ``GrowthSteppable`` class: .. code-block:: cpp #ifndef GROWTHSTEPPABLESTEPPABLE_H #define GROWTHSTEPPABLESTEPPABLE_H #include #include "GrowthSteppableDLLSpecifier.h" namespace CompuCell3D { template class Field3D; template class WatchableField3D; class Potts3D; class Automaton; class BoundaryStrategy; class CellInventory; class CellG; class GROWTHSTEPPABLE_EXPORT GrowthSteppable : public Steppable { WatchableField3D *cellFieldG; Simulator * sim; Potts3D *potts; CC3DXMLElement *xmlData; Automaton *automaton; BoundaryStrategy *boundaryStrategy; CellInventory * cellInventoryPtr; Dim3D fieldDim; public: GrowthSteppable (); virtual ~GrowthSteppable (); // SimObject interface virtual void init(Simulator *simulator, CC3DXMLElement *_xmlData=0); virtual void extraInit(Simulator *simulator); //steppable interface virtual void start(); virtual void step(const unsigned int currentStep); virtual void finish() {} //SteerableObject interface virtual void update(CC3DXMLElement *_xmlData, bool _fullInitFlag=false); virtual std::string steerableName(); virtual std::string toString(); }; }; #endif Each steppable defines ``virtual void start()``, ``virtual void step(const unsigned int currentStep)`` and ``virtual void finish()`` functions. They have exactly the same role as analogous functions in Python scripting. The oly differentce is that C++ steppables will be called **before** Python steppables Let us check the generated implementation file of the Steppable (the ``.cpp`` file): .. code-block:: cpp #include using namespace CompuCell3D; using namespace std; #include "GrowthSteppable.h" GrowthSteppable::GrowthSteppable() : cellFieldG(0),sim(0),potts(0),xmlData(0),boundaryStrategy(0),automaton(0),cellInventoryPtr(0){} GrowthSteppable::~GrowthSteppable() { } void GrowthSteppable::init(Simulator *simulator, CC3DXMLElement *_xmlData) { xmlData=_xmlData; potts = simulator->getPotts(); cellInventoryPtr=& potts->getCellInventory(); sim=simulator; cellFieldG = (WatchableField3D *)potts->getCellFieldG(); fieldDim=cellFieldG->getDim(); simulator->registerSteerableObject(this); update(_xmlData,true); } void GrowthSteppable::extraInit(Simulator *simulator){ //PUT YOUR CODE HERE } void GrowthSteppable::start(){ //PUT YOUR CODE HERE } void GrowthSteppable::step(const unsigned int currentStep){ //REPLACE SAMPLE CODE BELOW WITH YOUR OWN CellInventory::cellInventoryIterator cInvItr; CellG * cell=0; cerr<<"currentStep="<cellInventoryBegin() ; cInvItr !=cellInventoryPtr->cellInventoryEnd() ;++cInvItr ) { cell=cellInventoryPtr->getCell(cInvItr); cerr<<"cell.id="<id<<" vol="<volume<getAutomaton(); ASSERT_OR_THROW("CELL TYPE PLUGIN WAS NOT PROPERLY INITIALIZED YET. MAKE SURE THIS IS THE FIRST PLUGIN THAT YOU SET", automaton) set cellTypesSet; CC3DXMLElement * exampleXMLElem=_xmlData->getFirstElement("Example"); if (exampleXMLElem){ double param=exampleXMLElem->getDouble(); cerr<<"param="<findAttribute("Type")){ std::string attrib=exampleXMLElem->getAttribute("Type"); // double attrib=exampleXMLElem->getAttributeAsDouble("Type"); //in case attribute is of type double cerr<<"attrib="< 1.0 We would parse this XML in C++ using the following code: .. code-block:: cpp void GrowthSteppable::update(CC3DXMLElement *_xmlData, bool _fullInitFlag){ automaton = potts->getAutomaton(); ASSERT_OR_THROW("CELL TYPE PLUGIN WAS NOT PROPERLY INITIALIZED YET. MAKE SURE THIS IS THE FIRST PLUGIN THAT YOU SET", automaton) set cellTypesSet; CC3DXMLElement * growthElem = _xmlData->getFirstElement("GrowthRate"); if (growthElem){ this->growthRate = growthElem->getDouble(); } //boundaryStrategy has information about pixel neighbors boundaryStrategy=BoundaryStrategy::getInstance(); } As we mentioned before ``_xmlData`` points to ````. We need to get the child of this element *i.e.* ``1.0``. Since we know that there is only one child element (let's say we make such constraint for now - we will relax it later) we use the following code: .. code-block:: cpp CC3DXMLElement * growthElem = _xmlData->getFirstElement("GrowthRate"); The ``getFirstElement`` method returns a pointer to a child element that is of the form .. code-block:: xml ... The returned pointer can be ``NULL`` if suitable child element cannot be found. This is why we add ``if (growthElem)`` check. Assuming that the ```` child exist we read its ``cdata`` part. For any XML element , cdata part (cdata stands for character data) is the part that sits between closing ``>`` and opening ``<`` brackets of XML element. For example in .. code-block:: xml 1.0 the ``cdata`` part is 1.0. The ``CC3DXMLElement`` has several methods that read and convert cdata to appropriate C++ type. Here we are using ``getDouble()`` .. code-block:: cpp this->growthRate = growthElem->getDouble(); Obviously, ``CC3DXMLElement`` defines more methods to convert character data to required type (``getInt``, ``getBool`` , *etc...*) They are defined in ``XMLUtils/CC3DXMLElement.h`` In order for this code to work we need to define growthRate inside ``GrowthSteppable`` class header - we can do it as follows: .. code-block:: cpp class GROWTHSTEPPABLE_EXPORT GrowthSteppable : public Steppable { WatchableField3D *cellFieldG; Simulator * sim; Potts3D *potts; CC3DXMLElement *xmlData; Automaton *automaton; BoundaryStrategy *boundaryStrategy; CellInventory * cellInventoryPtr; Dim3D fieldDim; public: GrowthSteppable (); virtual ~GrowthSteppable (); double growthRate; ... } With those changes we can rewrite our ``step`` function as: .. code-block:: void GrowthSteppable::step(const unsigned int currentStep){ CellInventory::cellInventoryIterator cInvItr; CellG * cell=0; if (currentStep > 100) return; for(cInvItr=cellInventoryPtr->cellInventoryBegin() ; cInvItr !=cellInventoryPtr->cellInventoryEnd() ;++cInvItr ) { cell=cellInventoryPtr->getCell(cInvItr); if (cell->type == 1){ cell->targetVolume += this->growthRate; } } } It is almost the same implementation as before except we use ``cell->targetVolume += this->growthRate;`` instead of ``cell->targetVolume += growthRate;`` The ``this->growthRate`` gets initialized based on the input provided in .. code-block:: xml 1.0 If we change it to .. code-block:: xml 2.0 and rerun the simulation the rate of increase of target volume will be 2.0. All the changes we make to the growth rate now do not require recompilation but only chenges int he XML file, exactly how CC3D is designed to work. Next we will learn how to parse attributes of the XML elements. As a motivating example we will specify different growth rates for different cell types. Parsing XMl Attributes ~~~~~~~~~~~~~~~~~~~~~~ If we want our simulation to have different growth rates for different cell types we need to store them in *e.g.* STL map and we need to modify header of the ``GrowthSteppable`` to look as follows: .. code-block:: cpp class GROWTHSTEPPABLE_EXPORT GrowthSteppable : public Steppable { WatchableField3D *cellFieldG; Simulator * sim; Potts3D *potts; CC3DXMLElement *xmlData; Automaton *automaton; BoundaryStrategy *boundaryStrategy; CellInventory * cellInventoryPtr; Dim3D fieldDim; public: GrowthSteppable (); virtual ~GrowthSteppable (); std::map growthRateMap; ... } We replaced ``double growthRate`` with ``std::map growthRateMap;`` The key of the map is cell type and the value is growth rate. Now we need to design and parse XML that will allow users to specify required data. Let us try the following syntax: .. code-block:: xml 1.3 1.7 I case you wonder what I mean by "trying out syntax" it means that it is up to you to design XML syntax in such a way that it allows you to specify model in the way you want. The above example fulfills this requirement because we specify different growth rates for different cell types. However, we could also come up with a different way of specifying the same information: .. code-block:: xml Both approaches are OK. Let us write the ``update`` function that will parse first of the above XMLs: .. code-block:: cpp void GrowthSteppable::update(CC3DXMLElement *_xmlData, bool _fullInitFlag){ automaton = potts->getAutomaton(); ASSERT_OR_THROW("CELL TYPE PLUGIN WAS NOT PROPERLY INITIALIZED YET. MAKE SURE THIS IS THE FIRST PLUGIN THAT YOU SET", automaton) set cellTypesSet; CC3DXMLElementList growthVec = _xmlData->getElements("GrowthRate"); for (int i = 0; i < growthVec.size(); ++i) { unsigned int cellType = growthVec[i]->getAttributeAsUInt("CellType"); double growthRateTmp = growthVec[i]->getDouble(); this->growthRateMap[cellType] = growthRateTmp; } //boundaryStrategy has information about pixel neighbors boundaryStrategy=BoundaryStrategy::getInstance(); } The code is slightly different this time because we expect multiple entries of the type ````. Therefore, by writing the code: .. code-block:: cpp CC3DXMLElementList growthVec = _xmlData->getElements("Rate"); we ensure that CC3D will return a list (actually it is implemented as an STL vector) of XML element pointers that start with ```` . Next, we iterate over the vector of XML element pointers and notice that ``growthVec[i]`` returns a pointer to XML element pointer and we query this element. First, we read and convert to ``unsigned int`` value of ``CellType`` attribute: .. code-block:: cpp unsigned int cellType = growthVec[i]->getAttributeAsUInt("CellType"); The next line: .. code-block:: cpp double growthRateTmp = growthVec[i]->getDouble(); should be familiar already because it reads the value of ``cdata`` of ``1.3`` Once we extracted cell type and actual growth rate from a single element we store those values in ``this->growthRateMap`` map: .. code-block:: cpp this->growthRateMap[cellType] = growthRateTmp; .. note:: We are not performing any error checks in the above code and assume that users enter reasonable values. In the production code we would monitor for possible errors but this extra code would make this introductory manual a bit too confusing If we wanted to parse second syntax where we specify growth rate as and attribute rather than ``cdata`` : .. code-block:: xml we would need to make only small modification: .. code-block:: cpp void GrowthSteppable::update(CC3DXMLElement *_xmlData, bool _fullInitFlag){ automaton = potts->getAutomaton(); ASSERT_OR_THROW("CELL TYPE PLUGIN WAS NOT PROPERLY INITIALIZED YET. MAKE SURE THIS IS THE FIRST PLUGIN THAT YOU SET", automaton) set cellTypesSet; CC3DXMLElementList growthVec = _xmlData->getElements("GrowthRate"); for (int i = 0; i < growthVec.size(); ++i) { unsigned int cellType = growthVec[i]->getAttributeAsUInt("CellType"); double growthRateTmp = growthVec[i]->getAttributeAsDouble("GrowthRate"); this->growthRateMap[cellType] = growthRateTmp; } //boundaryStrategy has information about pixel neighbors boundaryStrategy=BoundaryStrategy::getInstance(); } The code differs from previous parsing code by only one line: .. code-block:: cpp double growthRateTmp = growthVec[i]->getAttributeAsDouble("GrowthRate"); As usual for a complete list of functions that read and convert XML attributes to concrete C++ types , check ``XMLUtils/CC3DXMLElement.h`` In order to take advantage of the specification of growth rate on a per-cell-type basis we modify step function as follows: .. code-block:: cpp void GrowthSteppable::step(const unsigned int currentStep){ CellInventory::cellInventoryIterator cInvItr; CellG * cell=0; if (currentStep > 100) return; std::map::iterator mitr; for(cInvItr=cellInventoryPtr->cellInventoryBegin() ; cInvItr !=cellInventoryPtr->cellInventoryEnd() ;++cInvItr ) { cell=cellInventoryPtr->getCell(cInvItr); mitr = this->growthRateMap.find((unsigned int)cell->type); if (mitr != this->growthRateMap.end()){ cell->targetVolume += mitr->second; } } } We declare an iterator to the ``std::map``. HInt: iterator is like a pointer and in the case of map iterator will have two components ``mitr->first`` which will be a key of ``this->growthRateMap`` map (in our case a key is a cell type) and ``mitr->second`` which will point to a value of the ``this->growthRateMap`` which in our case is a growth rate. When we get a new cell first thing we do is to check if iterator pointing to a pair of (cell type, growth rate) exist: ``mitr = this->growthRateMap.find((unsigned int)cell->type);`` If such entry exists in the ``this->growthRateMap`` then this iterator will point to a value different than ``this->growthRateMap.end()`` and in such a case we know that ``mitr->second`` points to a growth rate for a cell type given by ``cell->type``. We simply increase target volume of such cell by the growth rate. This logic is code up in the following if statement": .. code-block:: cpp if (mitr != this->growthRateMap.end()){ cell->targetVolume += mitr->second; } The presented example went over a theory of how to build a basic steppable and integrate it with main CC3D code. In the next tutorial we will present the same steppable but we will build it in the ``DevelopeZone`` folder of CC3D. The idea here is that this new steppable can live outside main CC3D code and still be accessible by the installed binaries. .. |git_setup| image:: images/git_setup.png :width: 6.0in :height: 2.2in .. |twedit_steppable_wizard| image:: images/twedit_steppable_wizard.png :width: 7.8in :height: 4.4in .. |twedit_generated_steppable| image:: images/twedit_generated_steppable.png :width: 7.8in :height: 4.4in