1 / 51

Developing CompuCell3D modules in C++

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.

airlia
Download Presentation

Developing CompuCell3D modules in C++

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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

  2. 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

  3. Developer’s documentation and source code documentation online Click “Documentation” link to get access to source code documentation

  4. Prerequisites for building entire CompuCell3D from source • CMake - www.cmake.org • Qt4 – www.trolltech.com , get Open Source version for your operating system. Note (it is included with most Linux distributions) • Swig 1.3.31 or higher - www.swig.org (included with most Linux distributions, available on OSX through Darwin Ports, downloadable Windows binary) • Xercesc 2.4 or higher http://xml.apache.org/xerces-c/ (included with most Linux distributions, available on OSX through Darwin Ports) • Python-dev(eloper files) 2.5 or higher - www.python.org – standard on most linux distributions and available on OSX through Darwin Ports • VTK - www.vtk.org – standard on some linux distributions (e.g. Debian, Ubuntu) • Compiler – either gcc/g++ or MS Visual Studio 2005

  5. If you are interested only in plugin and steppable development all you need is: • CMake - www.cmake.org • Swig 1.3.31 or higher - www.swig.org (included with most Linux distributions, available on OSX through Darwin Ports, downloadable Windows binary) • Xercesc 2.4 or higher http://xml.apache.org/xerces-c/ (included with most Linux distributions, available on OSX through Darwin Ports) • Python-dev(eloper files) 2.5 or higher - www.python.org – standard on most linux distributions and available on OSX through Darwin Ports • Compiler – either gcc/g++ or MS Visual Studio 2005

  6. 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.

  7. CompuCell3D terminology • 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. • 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 • 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. • 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 • 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. • Fixed Steppers are modules that are run every spin-copy attempt.

  8. CompuCell3D terminology – visual guide Change pixel Spin copy “blue” pixel (newCell) replaces “yellow” pixel (oldCell) 100x100x1 square lattice = 10000 lattice sites (pixels) MCS 21 MCS 22 MCS 23 MCS 24 10000 spin-copy attempts 10000 spin-copy attempts 10000 spin-copy attempts 10000 spin-copy attempts Run Steppables Run Steppables Run Steppables

  9. Developing CompuCell3D energy function plugin in C++ General structure of the CompuCell3D energy function plugin General structure of the CompuCell3D energy function plugin Plugin Plugin Energy Function Energy Function Pointer Plugin class is used as a place holder to store object of Energy Function class Energy Function Plugin class can also store pointer to energy function

  10. 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(); }; };

  11. 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. 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); }

  12. void VolumePlugin::update(ParseData *_pd, bool _fullInitFlag){ volumeEnergy.update((VolumeParseData *)_pd); } std::string VolumePlugin::steerableName(){ return volumeEnergy.vpd.ModuleName(); } • Checklist: • Define new plugin class (inherit from Plugin base class) • Implement init, update and steerableName functions • Use either pointer to energy function of simply store Energy function inside Plugin class • Implement energy function .Steps 1-3 are used to properly setup plugin. Actual calculations take place inside energy function.

  13. Declaring Energy Function 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

  14. 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 …

  15. 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; }

  16. 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

  17. 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. 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); } } <Plugin Name="Volume"> <TargetVolume>25</TargetVolume> <LambdaVolume>2.0</LambdaVolume> </Plugin>

  18. 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)

  19. 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

  20. 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(); };

  21. 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) { }

  22. 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

  23. 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 calculations void VolumeTrackerPlugin::step() { if (deadCellG) { potts->destroyCellG(deadCellG); //destroying cell (deadCellG) whose volume reached zero deadCellG = 0;//reseting pointer deadCellG } }

  24. 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(); };

  25. Calculating change in cell surface due to spin copy Visited pixels Change pixel Flip pixel 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

  26. 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

  27. 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); }

  28. 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; }

  29. 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; } } }; };

  30. 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(); }

  31. Calculating surface energy change due to spin flip is done in an analogous way to the way we did it for the Volume plugin: if (newCell){ energy += diffEnergy(newCell->surface*spdPtr->scaleSurface, newDiff*spdPtr->scaleSurface); } if (oldCell){ energy += diffEnergy(oldCell->surface*spdPtr->scaleSurface, oldDiff*spdPtr->scaleSurface); } return energy; } 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; }

  32. 4 4 4 4 3 2 2 4 3 4 3 3 1 1 1 4 4 2 4 1 4 2 2 2 1 1 4 4 1 3 3 1 3 1 3 2 2 2 2 4 4 1 4 4 3 4 4 4 3 4 Nearest neighbors in 2D and their Euclidian distances from the central pixel

  33. 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; }

  34. Calculating change contact energy due to spin copy Visited pixels Change pixel Flip pixel 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.

  35. 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

  36. 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 100x100x1 square lattice = 10000 lattice sites (pixels) MCS 21 MCS 22 MCS 23 MCS 24 10000 spin-copy attempts 10000 spin-copy attempts 10000 spin-copy attempts 10000 spin-copy attempts Run Steppables Run Steppables Run Steppables

  37. BoxWatcher Steppable 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(); }; Every SimObject (plugin, steppable) has init and extraInit virtual functions Every steppable defines those virtual functions start(), step(), finish()

  38. Defining initialization functions 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); } 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

  39. Reading XML for BoxWatcher Stappeble 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> Convenience function that checks if a given cell type is frozen (i.e. does not participate in spin copy)

  40. 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.

  41. All the work is done in adjustBox() function: 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); }

  42. adjustCoordinates is essentially a function that finds min and max of several pairs of numbers. 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; }

  43. 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.

  44. 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…

  45. 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)

  46. 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)

  47. Next to integrate the plugin with Python you need to: • Go to pyinterface/CompuCellExtraModules directory • Edit CMakeLists.txt: • … • SET(LIBS_AUX • FancyVolumeShared • VolumeMeanShared • YourNewPluginShared • PyPlugin • ) • …

  48. 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 …

  49. // ********************************************* 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

More Related