developing compucell3d modules in c++

51
Developing CompuCell3D modules in C++ Outline 1. Why you might want to consider developing CompuCell3D modules in C++? 2. C++ CompCell3D programming essentials 3. Step-by-step examples of CompuCell3D modules development

Upload: airlia

Post on 12-Jan-2016

52 views

Category:

Documents


0 download

DESCRIPTION

Developing CompuCell3D modules in C++. Outline Why you might want to consider developing CompuCell3D modules in C++? C++ CompCell3D programming essentials Step-by-step examples of CompuCell3D modules development. When to use C++ for CompuCell3D modules development. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Developing CompuCell3D modules in C++

Developing CompuCell3D modules in C++

Outline

1. Why you might want to consider developing CompuCell3D modules in C++?

2. C++ CompCell3D programming essentials

3. Step-by-step examples of CompuCell3D modules development

Page 2: Developing CompuCell3D modules in C++

When to use C++ for CompuCell3D modules development

1. When you need speed and flexibility. CompuCell3D core modules are all written in C++ so if you want to have access to all the CompuCell3D components C++ is pretty much the only option.

2. When you want to contribute new modules to CompuCell3D repository.

3. When you want to work on CompuCell3D kernel.

IMPORTANT: Since BioLogo generates fast C++ code that can be edited by the user you should consider this option before starting straght C++ development

Based on our experience modules that are best candidates for being implemented in C++ are energy functions. These modules are called every spin flip attempt. They are clearly the most frequently called modules in CompuCell3D. Because function calls are expensive in Python, developing even simple energy function in this scripting language results in “visible” simulation slowdown.

In most cases all other modules can be safely developed using either Python or Biologo.

Developer’s documentation and source code documentation can be found on www.compucell3d.org

Page 3: Developing CompuCell3D modules in C++

Developer’s documentation and source code documentation online

Click “Documentation” link to get access to source code documentation

Page 4: Developing CompuCell3D modules in C++
Page 5: Developing CompuCell3D modules in C++

Prerequisites for building entire CompuCell3D from source

1. CMake - www.cmake.org

2. Qt4 – www.trolltech.com , get Open Source version for your operating system. Note (it is included with most Linux distributions)

3. Swig 1.3.31 or higher - www.swig.org (included with most Linux distributions, available on OSX through Darwin Ports, downloadable Windows binary)

4. Xercesc 2.4 or higher http://xml.apache.org/xerces-c/ (included with most Linux distributions, available on OSX through Darwin Ports)

5. Python-dev(eloper files) 2.5 or higher - www.python.org – standard on most linux distributions and available on OSX through Darwin Ports

6. VTK - www.vtk.org – standard on some linux distributions (e.g. Debian, Ubuntu)

7. Compiler – either gcc/g++ or MS Visual Studio 2005

Page 6: Developing CompuCell3D modules in C++

If you are interested only in plugin and steppable development all you need is:

1. CMake - www.cmake.org

2. Swig 1.3.31 or higher - www.swig.org (included with most Linux distributions, available on OSX through Darwin Ports, downloadable Windows binary)

3. Xercesc 2.4 or higher http://xml.apache.org/xerces-c/ (included with most Linux distributions, available on OSX through Darwin Ports)

4. Python-dev(eloper files) 2.5 or higher - www.python.org – standard on most linux distributions and available on OSX through Darwin Ports

5. Compiler – either gcc/g++ or MS Visual Studio 2005

Page 7: Developing CompuCell3D modules in C++

Repositories

Current (June 2008 ) CompuCell3D source code (version 3.2.1) is stored in the following svn repository:

http://trac.compucell3d.net/svn/cc3d_svn/trunk/src-3.2.1-all/

You need to use SVN client to download the source. On Windows use TortoiseSVN:

http://tortoisesvn.tigris.org/

You may also download source code directly from our website skipping svn client.

Page 8: Developing CompuCell3D modules in C++

CompuCell3D terminology

1. Spin-copy attempt is an event where program randomly picks a lattice site in an attempt to copy its spin to a neighboring lattice site.

2. Monte Carlo Step (MCS) consists of series spin-copy attempts. Usually the number of spin copy-attempts in single MCS is equal to the number of lattice sites, but this is can be customized

3. CompuCell3D Plugin is a software module that either calculates an energy term in a Hamiltonian or implements action in response to spin copy (lattice monitors). Note that not all spin-copy attempts will trigger lattice monitors to run.

4. Steppables are CompuCell3D modules that are run every MCS after all spin-copy attempts for a given MCS have been exhausted. Most of Steppables are implemented in Python. Most customizations of CompuCell3D simulations is done through Steppables

5. Steppers are modules that are run for those spin-copy attempts that actually resulted in energy calculation. They are run regardless whether actual spin-copy occurred or not. For example cell mitosis is implemented in the form of stepper.

6. Fixed Steppers are modules that are run every spin-copy attempt.

Page 9: Developing CompuCell3D modules in C++

Spin copy “blue” pixel (newCell) replaces “yellow” pixel (oldCell)

Change pixel

MCS 21

10000 spin-copy attempts

MCS 22 MCS 23 MCS 2410000 spin-

copy attempts10000 spin-

copy attempts10000 spin-

copy attempts

Run

Steppables

Run

Steppables

Run

Steppables

100x100x1 square lattice = 10000 lattice sites (pixels)

CompuCell3D terminology – visual guide

Page 10: Developing CompuCell3D modules in C++

Developing CompuCell3D energy function plugin in C++

Plugin

Energy Function

General structure of the CompuCell3D energy function plugin

Plugin class is used as a place holder to store object of Energy Function class

Plugin

Energy Function

General structure of the CompuCell3D energy function plugin

Energy Function Pointer

Plugin class can also store pointer to energy function

Page 11: Developing CompuCell3D modules in C++

Plugin Declaration

namespace CompuCell3D { class Potts3D; class CellG;

class DECLSPECIFIER VolumePlugin : public Plugin { VolumeEnergy volumeEnergy; Potts3D *potts; public: VolumePlugin():potts(0){}; virtual ~VolumePlugin(); // SimObject interface virtual void init(Simulator *simulator, ParseData *_pd=0); virtual void update(ParseData *_pd, bool _fullInitFlag=false);

virtual void readXML(XMLPullParser &in); virtual void writeXML(XMLSerializer &out); VolumeParseData * getParseDataCast(); virtual std::string steerableName(); };};

Page 12: Developing CompuCell3D modules in C++

void VolumePlugin::init(Simulator *simulator, ParseData * _pd) { potts = simulator->getPotts(); bool pluginAlreadyRegisteredFlag; Plugin *plugin=Simulator::pluginManager.get("VolumeTracker",&pluginAlreadyRegisteredFlag);

if(!pluginAlreadyRegisteredFlag) plugin->init(simulator);

potts->registerEnergyFunctionWithName(&volumeEnergy,volumeEnergy.toString()); volumeEnergy.vpdPtr=(VolumeParseData*)_pd; volumeEnergy.update(volumeEnergy.vpdPtr); // copy ParseData to plugin parameters simulator->registerSteerableObject(this); //get steering }void VolumePlugin::readXML(XMLPullParser &in) { pd=&volumeEnergy.vpd; volumeEnergy.readXML(in);}

void VolumePlugin::writeXML(XMLSerializer &out) { volumeEnergy.writeXML(out);}

The implementation of the Plugin class is usually very simple and schematic. The most important task here is to register energy function with “potts” object. Before doing so you may also want to load dependent plugins – in this case we load VolumeTracker plugin that makes sure that volume of the cell is properly updated every spin-flip.

Notice, parsing of the plugin’s XML is done by volumeEnergy class.

Page 13: Developing CompuCell3D modules in C++

void VolumePlugin::update(ParseData *_pd, bool _fullInitFlag){ volumeEnergy.update((VolumeParseData *)_pd);}

std::string VolumePlugin::steerableName(){ return volumeEnergy.vpd.ModuleName();}

Checklist:

1. Define new plugin class (inherit from Plugin base class)

2. Implement init, update and steerableName functions

3. Use either pointer to energy function of simply store Energy function inside Plugin class

4. Implement energy function .Steps 1-3 are used to properly setup plugin. Actual calculations take place inside energy function.

Page 14: Developing CompuCell3D modules in C++

namespace CompuCell3D {

class DECLSPECIFIER VolumeEnergy: public EnergyFunction, public virtual XMLSerializable { double targetVolume; double lambdaVolume; public: VolumeParseData vpd; VolumeParseData *vpdPtr;

VolumeEnergy():vpdPtr(0) {} virtual double localEnergy(const Point3D &pt); virtual void update(ParseData *pd, bool _fullInitFlag=false);//used for steering virtual double changeEnergy(const Point3D &pt, const CellG *newCell,const CellG *oldCell); VolumeParseData * getVolumeParseDataPtr(){return &vpd;} // Begin XMLSerializable interface virtual void readXML(XMLPullParser &in); virtual void writeXML(XMLSerializer &out); virtual std::string toString(); virtual std::string steerableName(); // End XMLSerializable interface };};#endif

Declaring Energy Function

Page 15: Developing CompuCell3D modules in C++

Including header files – order is important on Windows platform

#include <CompuCell3D/Potts3D/Potts3D.h>using namespace CompuCell3D;

#include <XMLCereal/XMLPullParser.h>#include <XMLCereal/XMLSerializer.h>

#include <BasicUtils/BasicString.h>#include <BasicUtils/BasicException.h>

#include <string>using namespace std;

#define EXP_STL#include "VolumeEnergy.h"

On MS operating systems to build dll one needs to include special macros in the definition of the class when you import and export symbols to/from dll. We have used DECLSPECIFIER macro to hide all the details. For this reason we import header file for a given plugin as the last header file on the list of imported headers. Notice we also have to use #define EXP_STL macro to make sure everything works properly. MS always makes sure programmers don’t fall asleep behind keybords …

Page 16: Developing CompuCell3D modules in C++

Most important function in EnergyFunction class:

double VolumeEnergy::changeEnergy(const Point3D &pt, const CellG *newCell, const CellG *oldCell) {

double energy = 0;

if (oldCell == newCell) return 0;

if (newCell){ energy += lambdaVolume * (1 + 2 * (newCell->volume - targetVolume)); } if (oldCell){ energy += lambdaVolume * (1 - 2 * (oldCell->volume - targetVolume)); } return energy;}

2tVV=E

tttbeforeafternew VV+=VVV+V=EE=ΔE 211 22

tttbeforeafterold VV=VVVV=EE=ΔE 211 22

newold ΔE+ΔE=ΔE

Page 17: Developing CompuCell3D modules in C++

Making Steering Possible:

void VolumeEnergy::update(ParseData *_pd, bool _fullInitFlag){ vpdPtr=(VolumeParseData *)_pd; targetVolume=vpdPtr->targetVolume; lambdaVolume=vpdPtr->lambdaVolume; }

update function will be called from Python every time you change VolumeParseData parameters and call CompuCellSetup.steer function from Python

Page 18: Developing CompuCell3D modules in C++

void VolumeEnergy::readXML(XMLPullParser &in) { in.skip(TEXT);

while (in.check(START_ELEMENT)) { if (in.getName() == "TargetVolume") { vpd.targetVolume = BasicString::parseDouble(in.matchSimple());

} else if (in.getName() == "LambdaVolume") { vpd.lambdaVolume = BasicString::parseDouble(in.matchSimple());

} else { throw BasicException(string("Unexpected element '") +

in.getName() + "'!", in.getLocation());

}

in.skip(TEXT); } }

Reading XML

Coding this function is the first thing you should do when developing new plugin. Yes, it is absolutely no fun and is extremely boring but it is essential to ensure that your plugin is configurable from XML level. In certain cases when you want your plugin to be configurable from Python level leave readXML function empty.

<Plugin Name="Volume">

<TargetVolume>25</TargetVolume>

<LambdaVolume>2.0</LambdaVolume>

</Plugin>

Page 19: Developing CompuCell3D modules in C++

Implementing “less important” methods of Energy Function Plugin:

std::string VolumeEnergy::steerableName(){ return vpd.ModuleName();}

std::string VolumeEnergy::steerableName(){ return vpd.ModuleName();}

std::string VolumeEnergy::toString(){ return vpd.ModuleName();}

void VolumeEnergy::writeXML(XMLSerializer &out) {}

Notice, writeXML function is empty (we do not write XML from CompuCell3D at this time), but has to be defined as it is declared as virtual function. Rule of thumb, to avoid errors with virtual tables make sure you provide implementation of virtual function (it can be an empty function, as above)

Page 20: Developing CompuCell3D modules in C++

One more file to include

To ensure that newly developed plugin is properly integrated with CompUCell3D you need to include PluginProxy object that ensures proper initialization of the plugin when it is loaded from the disc. Every plugin you develop has to have accompanying proxy object. The implementation of the proxy object is really simple:

#include "VolumePlugin.h"#include <CompuCell3D/Simulator.h>using namespace CompuCell3D;

#include <BasicUtils/BasicPluginProxy.h>

BasicPluginProxy<Plugin, VolumePlugin> volumeProxy("Volume", "Tracks cell volumes and adds volume energy function.",

&Simulator::pluginManager);

“XML name” of the plugin Information that is displayed when plugin is loaded

Page 21: Developing CompuCell3D modules in C++

Developing lattice monitor in C++

Lattice monitors are modules that react to spin copy event. Steppers are modules that are run for those spin-copy attempts that actually resulted in energy calculation. They are run regardless whether actual spin-copy occurred or not.

class DECLSPECIFIER VolumeTrackerPlugin : public Plugin, public CellGChangeWatcher, public Stepper { Potts3D *potts; CellG *deadCellG;

public: VolumeTrackerParseData vtpd; VolumeTrackerParseData * vtpdPtr; VolumeTrackerPlugin(); virtual ~VolumeTrackerPlugin(); // SimObject interface virtual void init(Simulator *simulator, ParseData *_pd=0); ///CellChangeWatcher interface virtual void field3DChange(const Point3D &pt, CellG *newCell, CellG *oldCell);

// Stepper interface virtual void step();

// Begin XMLSerializable interface virtual void readXML(XMLPullParser &in); virtual void writeXML(XMLSerializer &out); // End XMLSerializable interface

virtual std::string toString(); };

Page 22: Developing CompuCell3D modules in C++

Implementing “less important” methods of VolumeTrackerPlugin:

VolumeTrackerPlugin::VolumeTrackerPlugin() : vtpdPtr(0),potts(0), deadCellG(0) {}

VolumeTrackerPlugin::~VolumeTrackerPlugin() {}

void VolumeTrackerPlugin::init(Simulator *simulator,ParseData *_pd) {

potts = simulator->getPotts(); potts->registerCellGChangeWatcher(this); potts->registerStepper(this); }

std::string VolumeTrackerPlugin::toString(){return vtpd.ModuleName();}

void VolumeTrackerPlugin::readXML(XMLPullParser &in) { pd= &vtpd; in.skip(TEXT);}

void VolumeTrackerPlugin::writeXML(XMLSerializer &out) {}

Page 23: Developing CompuCell3D modules in C++

ParseData class is really simple:

#ifndef VOLUMETRACKERPARSEDATA_H#define VOLUMETRACKERPARSEDATA_H

#include <CompuCell3D/ParseData.h>#include <CompuCell3D/dllDeclarationSpecifier.h>

namespace CompuCell3D {

class DECLSPECIFIER VolumeTrackerParseData:public ParseData{ public: VolumeTrackerParseData():ParseData("VolumeTracker") {} };};#endif

Page 24: Developing CompuCell3D modules in C++

Implementing lattice monitor function and stepper function

void VolumeTrackerPlugin::field3DChange(const Point3D &pt, CellG *newCell, CellG *oldCell) {

if (newCell) newCell->volume++;//newCell is a cell that gains one pixel if (oldCell){ if((--oldCell->volume) == 0) //old cell is a cell that loses one pixel deadCellG = oldCell; //flagging cell for destruction if oldCell volume reaches 0} }//This function will run after spin flip (if any) and after energy calculationsvoid VolumeTrackerPlugin::step() { if (deadCellG) { potts->destroyCellG(deadCellG); //destroying cell (deadCellG) whose volume reached zero deadCellG = 0;//reseting pointer deadCellG }}

Page 25: Developing CompuCell3D modules in C++

Surface Tracker – a more complex example of lattice monitor

class DECLSPECIFIER SurfaceTrackerPlugin : public Plugin, public CellGChangeWatcher {

WatchableField3D<CellG *> *cellFieldG; unsigned int maxNeighborIndex; BoundaryStrategy *boundaryStrategy; bool deeperNeighborsRequested; float maxNeighborDepth; LatticeMultiplicativeFactors lmf; Potts3D *potts;

public: SurfaceTrackerParseData stpd; SurfaceTrackerParseData *stpdPtr; SurfaceTrackerPlugin(); virtual ~SurfaceTrackerPlugin();

virtual void init(Simulator *simulator , ParseData *_pd=0); const LatticeMultiplicativeFactors & getLatticeMultiplicativeFactors() const {return lmf;} unsigned int getMaxNeighborIndex(){return maxNeighborIndex;}

virtual void field3DChange(const Point3D &pt, CellG *newCell, CellG *oldCell);

virtual void readXML(XMLPullParser &in); virtual void writeXML(XMLSerializer &out); virtual void update(ParseData *pd, bool _fullInitFlag=false); virtual std::string steerableName(); };

Page 26: Developing CompuCell3D modules in C++

Spin copy “blue” pixel (newCell) replaces “yellow” pixel (oldCell)

To calculate surface change due to spin copy for yellow cell “walk around” pixel where the copy happens (yellow pixel) and for each cell belonging to oldCell (yellow) increase oldSurfaceDifference. For each pixel belonging to a different cell decrease the oldSurfaceDifference. Think of it as a situation when oldCell is losing surface when change pixel ( ) touches other cell and gaining surface when it touches oldCell (yellow)

To calculate surface change due to spin copy for “blue” cell “walk around” pixel where the copy happens (yellow pixel) and for each cell belonging to newCell (blue) decrease newSurfaceDifference. For each pixel belonging to a different cell increase the newSurfaceDifference.

Note, here we have considered only nearest neighbors

Change pixel

Calculating change in cell surface due to spin copy

Flip pixel

Visited pixels

Page 27: Developing CompuCell3D modules in C++

What will happen after spin copy?

These interfaces will be lost

These interfaces will be created

Yellow cell will lose 3 interfaces and gain 1 , thus net change in surface is -2

Blue cell will lose 3 interfaces and gain 1, thus net change in surface is -2

Page 28: Developing CompuCell3D modules in C++

Implementing SurfaceTracker Plugin

SurfaceTrackerPlugin::SurfaceTrackerPlugin() : stpdPtr(0),cellFieldG(0),boundaryStrategy(0),maxNeighborIndex(0),maxNeighborDepth(1.1),deeperNeighborsRequested(false) {}

SurfaceTrackerPlugin::~SurfaceTrackerPlugin() {}

std::string SurfaceTrackerPlugin::steerableName(){ return stpd.moduleName;}void SurfaceTrackerPlugin::writeXML(XMLSerializer &out) {}void SurfaceTrackerPlugin::init(Simulator *simulator , ParseData *_pd) {

if(!_pd){ stpdPtr=&stpd; pd=&stpd; }else{ stpdPtr=(SurfaceTrackerParseData *)_pd; pd=_pd; } potts = simulator->getPotts(); cellFieldG = (WatchableField3D<CellG *> *)potts->getCellFieldG();

potts->registerCellGChangeWatcher(this); boundaryStrategy=BoundaryStrategy::getInstance(); update(stpdPtr); simulator->registerSteerableObject(this);}

Page 29: Developing CompuCell3D modules in C++

Implementing SurfaceTracker Plugin –calculating differences in cell surfaces before and after spin copy

void SurfaceTrackerPlugin::field3DChange(const Point3D &pt, CellG *newCell, CellG *oldCell) {

double oldDiff = 0.; double newDiff = 0.; CellG *nCell=0; Neighbor neighbor;

for(unsigned int nIdx=0 ; nIdx <= maxNeighborIndex ; ++nIdx ){ neighbor=boundaryStrategy->getNeighborDirect(const_cast<Point3D&>(pt),nIdx); if(!neighbor.distance){ //if distance is 0 then the neighbor returned is invalid continue; } nCell = cellFieldG->get(neighbor.pt); if (newCell == nCell) newDiff-=lmf.surfaceMF; else newDiff+=lmf.surfaceMF; if (oldCell == nCell) oldDiff+=lmf.surfaceMF; else oldDiff-=lmf.surfaceMF;

} if (newCell) newCell->surface += newDiff; if (oldCell) oldCell->surface += oldDiff;}

Page 30: Developing CompuCell3D modules in C++

ParseData class for SurfaceTracker:

class DECLSPECIFIER SurfaceTrackerParseData:public ParseData{ public: SurfaceTrackerParseData():ParseData("SurfaceTracker"),deeperNeighborsRequested(false),higherOrderNeighborsRequested(false),maxNeighborDepth(1.05),maxNeighborOrder(1)

{} bool deeperNeighborsRequested; bool higherOrderNeighborsRequested; double maxNeighborDepth; unsigned int maxNeighborOrder; void MaxNeighborDepth(double _maxNeighborDepth){ if(_maxNeighborDepth>maxNeighborDepth){ maxNeighborDepth=_maxNeighborDepth; deeperNeighborsRequested=true; } } void MaxNeighborOrder(unsigned int _maxNeighborOrder){ if(maxNeighborOrder>1){ maxNeighborOrder=_maxNeighborOrder; higherOrderNeighborsRequested=true; } }

};};

Page 31: Developing CompuCell3D modules in C++

Update function – mapping XML parameters into plugin parameters - steering

void SurfaceTrackerPlugin::update(ParseData *_pd, bool _fullInitFlag){

stpdPtr=(SurfaceTrackerParseData *)_pd;

if(stpdPtr->higherOrderNeighborsRequested) maxNeighborIndex=boundaryStrategy ->getMaxNeighborIndexFromNeighborOrder(stpdPtr->maxNeighborOrder); else if(stpdPtr->deeperNeighborsRequested){ maxNeighborIndex=boundaryStrategy->getMaxNeighborIndexFromDepth(stpdPtr ->maxNeighborDepth);//depth=1.1 - means 1st nearest neighbor }else{ maxNeighborIndex=boundaryStrategy ->getMaxNeighborIndexFromNeighborOrder(stpdPtr->maxNeighborOrder); } lmf=boundaryStrategy->getLatticeMultiplicativeFactors();

}

Page 32: Developing CompuCell3D modules in C++

Calculating surface energy change due to spin flip is done in an analogous way to the way we did it for the Volume plugin:

2tSS=E

tnewtnewtnewnewbeforeafternew SS+=SSSS+S=EE=ΔE new2

new22 S2S

toldtoldtoldoldbeforeafterold SS+=SSSS+S=EE=ΔE old2

old22 S2S

double SurfaceEnergy::changeEnergy(const Point3D &pt, const CellG *newCell, const CellG *oldCell) {

CellG *nCell;

if (oldCell == newCell) return 0;

double oldDiff = 0.; double newDiff = 0.; Point3D n; double energy = 0;

Neighbor neighbor; for(unsigned int nIdx=0 ; nIdx <= maxNeighborIndex ; ++nIdx ){ neighbor=boundaryStrategy->getNeighborDirect(const_cast<Point3D&>(pt),nIdx); if(!neighbor.distance){ //if distance is 0 then the neighbor returned is invalid continue; } nCell = cellFieldG->get(neighbor.pt); if (newCell == nCell) newDiff-=lmf.surfaceMF; else newDiff+=lmf.surfaceMF;

if (oldCell == nCell) oldDiff+=lmf.surfaceMF; else oldDiff-=lmf.surfaceMF;

}

if (newCell){ energy += diffEnergy(newCell->surface*spdPtr->scaleSurface, newDiff*spdPtr->scaleSurface);

} if (oldCell){ energy += diffEnergy(oldCell->surface*spdPtr->scaleSurface, oldDiff*spdPtr->scaleSurface); } return energy;}

Page 33: Developing CompuCell3D modules in C++

Nearest neighbors in 2D and their Euclidian distances from the central pixel

1

1

1

1

2

22

2

3

3

3

3

4

4

4

4 4

4

4

41

1

11

1

1

2 2

2

22

2

3

3

3

3

3

3

4

4

444

4

4

4

44 4

4

2D Square Lattice 2D Hexagonal Lattice

Neighbor Order

Number of Neighbors

Euclidian Distance

Number of Neighbors

Euclidian Distance

1 4 1 6

2 4 6

3 4 2 6

4 8 12

3/2

2 3/6

3/8

14 / 35

Page 34: Developing CompuCell3D modules in C++

Contact Energy Plugin

double ContactEnergy::changeEnergy(const Point3D &pt, const CellG *newCell, const CellG *oldCell) {

double energy = 0; Point3D n;

CellG *nCell=0; WatchableField3D<CellG *> *fieldG =(WatchableField3D<CellG *> *) potts->getCellFieldG(); Neighbor neighbor;

for(unsigned int nIdx=0 ; nIdx <= maxNeighborIndex ; ++nIdx ){ neighbor=boundaryStrategy->getNeighborDirect(const_cast<Point3D&>(pt),nIdx); if(!neighbor.distance){ //if distance is 0 then the neighbor returned is invalid continue; }

nCell = fieldG->get(neighbor.pt); if(nCell!=oldCell){ energy -= contactEnergy(oldCell, nCell); } if(nCell!=newCell){ energy += contactEnergy(newCell, nCell); } } return energy;}

Page 35: Developing CompuCell3D modules in C++

Spin copy “blue” pixel (newCell) replaces “yellow” pixel (oldCell)

“Walk around” pixel where the copy happens (yellow pixel) and for each cell belonging cell differrent than oldCell (yellow) decrease energy change by the contact energy between oldCell and this other cell. This is because after spin flip those interfaces will be lost thus will contact energy.

“Walk around” pixel where the copy happens (yellow pixel) and for each cell belonging cell differrent than newCell (yellow) increase energy change by the contact energy between oldCell and this other cell. This is because after spin flip those interfaces will show up , so will the energy.

Although shown algorithm was presented for 1st nearest neighbor it will work for 2nd , 3rd, 4th etc… nearest neighbors as well.

Change pixel

Calculating change contact energy due to spin copy

Flip pixel

Visited pixels

Page 36: Developing CompuCell3D modules in C++

What will happen after spin copy?

These interfaces will be lost

These interfaces will be created

Yellow cell will lose 3 interfaces thus overall system will lose “3 contact energies”

There will be one interface created , thus overall system will gain “1 contact energy”

The actual value of the contact energy will depend on types of cells that touch each other

Page 37: Developing CompuCell3D modules in C++

Developing Steppables

Developing Steppables in C++ usually is not the best option because, you can do it far more efficiently and elegantly in Python. However for completeness purposes we will show you how to deal with this task as well.

The basic principles are analogous to those used for plugin development so we will not cover all the details in the way we did when we discussed plugins. However, we will teach you

MCS 21

10000 spin-copy attempts

MCS 22 MCS 23 MCS 2410000 spin-

copy attempts10000 spin-

copy attempts10000 spin-

copy attempts

Run

Steppables

Run

Steppables

Run

Steppables

100x100x1 square lattice = 10000 lattice sites (pixels)

Page 38: Developing CompuCell3D modules in C++

class DECLSPECIFIER BoxWatcher : public Steppable {

WatchableField3D<CellG *> *cellFieldG; Simulator * sim; Potts3D *potts; Dim3D fieldDim;

Point3D minCoordinates; Point3D maxCoordinates; std::vector<unsigned char> frozenTypeVector; void adjustBox(); void adjustCoordinates(Point3D pt); bool checkIfFrozen(unsigned char _type);

public: BoxWatcherParseData bwpd; BoxWatcherParseData * bwpdPtr; BoxWatcher(); virtual ~BoxWatcher();

virtual void init(Simulator *simulator , ParseData * _pd=0); virtual void extraInit(Simulator *simulator);

virtual void start(); virtual void step(const unsigned int currentStep); virtual void finish() {}

Point3D getMinCoordinates(); Point3D getMaxCoordinates(); Point3D *getMinCoordinatesPtr(){return &minCoordinates;} Point3D *getMaxCoordinatesPtr(){return &minCoordinates;}

Point3D getMargins();

virtual void readXML(XMLPullParser &in); virtual void writeXML(XMLSerializer &out);

virtual void update(ParseData *pd, bool _fullInitFlag=false); virtual std::string steerableName();

};

BoxWatcher Steppable

Every steppable defines those virtual functions start(), step(), finish()

Every SimObject (plugin, steppable) has init and extraInit virtual functions

Page 39: Developing CompuCell3D modules in C++

BoxWatcher::BoxWatcher() : bwpdPtr(0),cellFieldG(0),sim(0),potts(0) {}

BoxWatcher::~BoxWatcher() {}void BoxWatcher::extraInit(Simulator *simulator){ frozenTypeVector=potts->getFrozenTypeVector();}void BoxWatcher::init(Simulator *simulator , ParseData *_pd) {

if(_pd){ pd=_pd; bwpdPtr=(BoxWatcherParseData *)pd; }else{ pd=&bwpd; bwpdPtr=&bwpd; } potts = simulator->getPotts(); sim=simulator; cellFieldG = (WatchableField3D<CellG *> *)potts->getCellFieldG(); fieldDim=cellFieldG->getDim();

minCoordinates=Point3D(fieldDim.x/2,fieldDim.y/2,fieldDim.z/2); maxCoordinates=Point3D(fieldDim.x/2,fieldDim.y/2,fieldDim.z/2);

simulator->registerSteerableObject(this);}

Defining initialization functions

IMPORTANT: init() is run after XML parsing is done. extraInit is run after init for all the modules has been called. Some of the plugin initialization has to be done after “initial initialization” took place. That’s why we use extraInit

Page 40: Developing CompuCell3D modules in C++

void BoxWatcher::readXML(XMLPullParser &in) { in.skip(TEXT);

pd=&bwpd; bwpdPtr=&bwpd;

while (in.check(START_ELEMENT)) { if (in.getName() == "XMargin") { bwpdPtr->xMargin = BasicString::parseUInteger(in.matchSimple());

} else if (in.getName() == "YMargin") { bwpdPtr->yMargin = BasicString::parseUInteger(in.matchSimple());

} else if (in.getName() == "ZMargin") { bwpdPtr->zMargin = BasicString::parseUInteger(in.matchSimple());

} else { throw BasicException(string("Unexpected element '") + in.getName() +

"'!", in.getLocation()); } in.skip(TEXT); }}

void BoxWatcher::writeXML(XMLSerializer &out) {}

bool BoxWatcher::checkIfFrozen(unsigned char _type){

for (unsigned int i = 0 ; i< frozenTypeVector.size(); ++i ){ if(frozenTypeVector[i]==_type) return true; } return false;}

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

<YMargin>5</YMargin>

<ZMargin>5</ZMargin> </Steppable>

Reading XML for BoxWatcher Stappeble

Convenience function that checks if a given cell type is frozen (i.e. does not participate in spin copy)

Page 41: Developing CompuCell3D modules in C++

Two most important functions of each steppable

void BoxWatcher::start(){

minCoordinates=Point3D(fieldDim.x,fieldDim.y,fieldDim.z); maxCoordinates=Point3D(0,0,0); adjustBox();

}

void BoxWatcher::step(const unsigned int currentStep){

minCoordinates=Point3D(fieldDim.x,fieldDim.y,fieldDim.z); maxCoordinates=Point3D(0,0,0); adjustBox();}

start() and step() functions are the two functions that make up a steppable. There is a third one finish(), but this one is very rarely used. start() runs before simulation begins, start() runs every MCS and finish() runs once the simulation is done.

Page 42: Developing CompuCell3D modules in C++

void BoxWatcher::adjustBox(){ Point3D pt; CellG * cell;//loop over each pixel to figure out extreme most pixels for (int x = 0 ; x < fieldDim.x ; ++x) for (int y = 0 ; y < fieldDim.y ; ++y) for (int z = 0 ; z < fieldDim.z ; ++z){ pt=Point3D(x,y,z); cell=cellFieldG->get(pt); if(!cell) continue; if(checkIfFrozen(cell->type)) continue; //ignore frozen cells, they do not participate in spin copying adjustCoordinates(pt); //adjusting extreme coordinates of pixels visited until now }

//expanding box to account for margin minCoordinates.x = ((int)minCoordinates.x-(int)bwpdPtr->xMargin<=0 ? 0 :minCoordinates.x-bwpdPtr->xMargin); minCoordinates.y = ((int)minCoordinates.y-(int)bwpdPtr->yMargin<=0 ? 0 :minCoordinates.y-bwpdPtr->yMargin); minCoordinates.z = ((int)minCoordinates.z-(int)bwpdPtr->zMargin<=0 ? 0 :minCoordinates.z-bwpdPtr->zMargin);

//note fieldDim.x-1 is max x cocordinate for lattice pixel, similarly for y, and z maxCoordinates.x = (maxCoordinates.x+bwpdPtr->xMargin>=fieldDim.x-1 ? fieldDim.x :maxCoordinates.x+bwpdPtr->xMargin+1); maxCoordinates.y = (maxCoordinates.y+bwpdPtr->yMargin>=fieldDim.y-1 ? fieldDim.y :maxCoordinates.y+bwpdPtr->yMargin+1); maxCoordinates.z = (maxCoordinates.z+bwpdPtr->zMargin>=fieldDim.z-1 ? fieldDim.z :maxCoordinates.z+bwpdPtr->zMargin+1);

//setting min and max box coordinates in the potts object. Spin flips will take place only within the box – faster simulation potts->setMinCoordinates(minCoordinates); potts->setMaxCoordinates(maxCoordinates);

}

All the work is done in adjustBox() function:

Page 43: Developing CompuCell3D modules in C++

void BoxWatcher::adjustCoordinates(Point3D _pt){

if(_pt.x>maxCoordinates.x) maxCoordinates.x=_pt.x; if(_pt.y>maxCoordinates.y) maxCoordinates.y=_pt.y; if(_pt.z>maxCoordinates.z) maxCoordinates.z=_pt.z;

if(_pt.x<minCoordinates.x) minCoordinates.x=_pt.x; if(_pt.y<minCoordinates.y) minCoordinates.y=_pt.y; if(_pt.z<minCoordinates.z) minCoordinates.z=_pt.z;

}

adjustCoordinates is essentially a function that finds min and max of several pairs of numbers.

Page 44: Developing CompuCell3D modules in C++

Do not forget about proxy…

#include "BoxWatcher.h"

#include <CompuCell3D/Simulator.h>using namespace CompuCell3D;

#include <BasicUtils/BasicPluginProxy.h>

BasicPluginProxy<Steppable, BoxWatcher> boxWatcherProxy(

"BoxWatcher", "Monitors and updates dimension of the rectangular box in which non-frozen cells are contained", &Simulator::steppableManager);

Syntax of proxy is essentially the same as in the case of plugins. The only difference is BasicPluginProxy template parameter steppable.

Page 45: Developing CompuCell3D modules in C++

Looping over cell inventory – one of the most frequently used actions#include <CompuCell3D/Potts3D/CellInventory.h>#include <fstream>#include <sstream>

void FoamDataOutput::step(const unsigned int currentStep) {

ostringstream str; str<<fileName<<"."<<currentStep; ofstream out(str.str().c_str()); CellInventory *cellInventoryPtr=potts->getCellInventory(); CellInventory::cellInventoryIterator cInvItr; CellG * cell;

for(cInvItr=cellInventoryPtr->cellInventoryBegin() ; cInvItr !=cellInventoryPtr->cellInventoryEnd() ;++cInvItr ) {

cell=*cInvItr; if(cellIDFlag) out<<cell->id<<"\t"; if(volFlag) out<<cell->volume<<"\t";

if(surFlag) out<<cell->surface<<"\t"; out<<endl; }}

Python solution is much more elegant however…

Page 46: Developing CompuCell3D modules in C++

Integrating your new pluigin with CompuCell3D build system

1. Check DeveloperZone directory for examples. There you can see how to write CMakeLists.txt files for new plugins and also how to integrate new plugins with Python

2. Add directory for your new plugin

… SET_TARGET_PROPERTIES(${LIBRARY_NAME}Shared PROPERTIES OUTPUT_NAME CC3D${LIBRARY_NAME}${LIBRARY_SUFFIX}) INSTALL_TARGETS(/lib/CompuCell3DSteppables RUNTIME_DIRECTORY /lib/CompuCell3DSteppables ${LIBRARY_NAME}Shared)

ENDMACRO(ADD_COMPUCELL3D_STEPPABLE)

add_subdirectory(FancyVolume)add_subdirectory(VolumeMean)add_subdirectory(YourNewPlugin) # I assume this is a subdirectory of DeveloperZone

add_subdirectory(pyinterface)

Page 47: Developing CompuCell3D modules in C++

Next, write CMakeLists.txt for your plugin:

ADD_COMPUCELL3D_PLUGIN(YourNewPlugin

YourNewEnergy.cpp YourNewPlugin.cpp YourNewPluginProxy.cpp

LINK_LIBRARIES

${CC3DLibraries})

ADD_COMPUCELL3D_PLUGIN_HEADERS(FancyVolume YourNewEnergy.h YourNewPlugin.h)

Page 48: Developing CompuCell3D modules in C++

Next to integrate the plugin with Python you need to:

1. Go to pyinterface/CompuCellExtraModules directory

2. Edit CMakeLists.txt:

SET(LIBS_AUX FancyVolumeShared VolumeMeanShared YourNewPluginShared

PyPlugin

)

Page 49: Developing CompuCell3D modules in C++

Now edit CompuCellExtraModules.i swig file:

…// ********************************************* PUT YOUR PLUGIN PARSE DATA AND PLUGIN FILES HERE

#include <FancyVolume/FancyVolumePlugin.h>#include <FancyVolume/FancyVolumeParseData.h>

#include <YourNewPlugin/YourNewPlugin.h>#include <YourNewPlugin/YourNewPluginParseData.h>

// ********************************************* END OF SECTION

Page 50: Developing CompuCell3D modules in C++

// ********************************************* PUT YOUR PLUGIN PARSE DATA AND PLUGIN FILES HERE%include <FancyVolume/FancyVolumePlugin.h>%include <FancyVolume/FancyVolumeParseData.h>

using namespace CompuCell3D;

%inline %{ FancyVolumePlugin * reinterpretFancyVolumePlugin(Plugin * _plugin){ return (FancyVolumePlugin *)_plugin; }

%}

%include <YourNewPlugin/YourNewPlugin.h>%include <YourNewPlugin/YourNewPluginParseData.h>

%inline %{ YourNewPlugin * reinterpretYourNewPlugin (Plugin * _plugin){ return (YourNewPlugin *)_plugin; }

%}

// ********************************************* END OF SECTION

Page 51: Developing CompuCell3D modules in C++

Now, compile and run your simulation with new plugin.