Simulator
Simulator is the top-level C++ object that coordinates a CompuCell3D
simulation. Its implementation is in CompuCell3D/Simulator.h and
CompuCell3D/Simulator.cpp.
The simulator does not implement the Potts algorithm directly. Instead,
it owns a Potts3D object and calls it once per Monte Carlo Step
(MCS). It also loads plugins and steppables, owns the class registry,
keeps simulation-wide parse data, manages concentration-field lookup,
and performs cleanup when a run finishes.
The shortest accurate summary is:
Simulatorreads the parsed CC3DML configuration, initializesPotts3Dand dynamically loaded modules, then repeatedly callsPotts3D::metropolisfollowed by simulation-level steppables.
Important Members
The core members in Simulator show its role as the owner of the run:
ClassRegistry *classRegistry;
Potts3D potts;
int currstep;
std::map<std::string, Field3D<float>*> concentrationFieldNameMap;
std::map<std::string, SteerableObject *> steerableObjectMap;
PottsParseData *ppdCC3DPtr;
ParallelUtilsOpenMP *pUtils;
ParallelUtilsOpenMP *pUtilsSingle;
A few relationships are worth remembering:
SimulatorcontainsPotts3Ddirectly, not through a pointer.ClassRegistrystores active simulation-level steppables.PottsParseDatastores parsed<Potts>settings such as lattice dimensions, number of steps, temperature/fluctuation amplitude,Flip2DimRatio, annealing steps, and boundary settings.concentrationFieldNameMapmaps field names to concentration-field pointers so plugins and steppables can retrieve fields by name.steerableObjectMapstores objects that can be updated during steering.
Namespace and Export Macro
Most core CC3D classes are in the CompuCell3D namespace:
namespace CompuCell3D {
class COMPUCELLLIB_EXPORT Simulator : public Steppable {
...
};
}
The COMPUCELLLIB_EXPORT macro handles platform-specific symbol export
and import rules. On Windows it expands to the appropriate
__declspec decoration. On other platforms it is typically empty.
This is why many CC3D classes are declared with export macros even when
the macro does not affect Linux or macOS builds.
Simulation Lifecycle
The simulator lifecycle is organized around these methods:
void initializeCC3D();
void initializePottsCC3D(CC3DXMLElement *_xmlData);
void processMetadataCC3D(CC3DXMLElement *_xmlData);
virtual void extraInit();
virtual void start();
virtual void step(const unsigned int currentStep);
virtual void finish();
void cleanAfterSimulation();
void unloadModules();
The typical order is:
initializeCC3Dinitializes the Potts object, parallel utilities, metadata, plugins, and steppables.extraInitgives plugins and steppables a second initialization pass after all modules have been constructed and registered.startcallsstarton active steppables and marks the simulator as stepping.stepruns one MCS: first Potts copy attempts, then steppables.finishruns optional annealing steps, calls steppablefinish, and unloads modules.cleanAfterSimulationclears simulation state that must be removed before modules are unloaded or another simulation is started.
Initialization
Simulator::initializeCC3D is the main setup function. It performs the
following high-level sequence:
initializePottsCC3D(ps.pottsCC3DXMLElement);
pUtils->init(potts.getCellFieldG()->getDim());
potts.setParallelUtils(pUtils);
pUtilsSingle->init(potts.getCellFieldG()->getDim());
processMetadataCC3D(ps.metadataCC3DXMLElement);
// Load and initialize plugins.
// Load and initialize steppables.
The order matters. Potts3D must create the cell field before parallel
utilities can be initialized from the lattice dimensions. Metadata is
processed after the main parallel utility object exists because metadata
can affect parallel behavior, including modules that should run in a
single-threaded mode.
After Potts and parallel utilities are ready, the simulator loads plugins
from the parsed <Plugin> elements and steppables from the parsed
<Steppable> elements. Plugins are initialized through
pluginManager. Steppables are initialized through steppableManager
and then added to ClassRegistry.
Initializing Potts3D
initializePottsCC3D reads the <Potts> section and configures the
Potts3D object. The first step is to register Potts as a steerable
object:
registerSteerableObject(&potts);
Then it reads settings such as:
DimensionsStepsAnnealTemperatureorFluctuationAmplitudeCellMotilityor cell-type fluctuation amplitudesFlip2DimRatioMetropolisAlgorithmRandomNumberGeneratorandRandomSeedboundary conditions
lattice and dimension type
neighbor order or maximum flip-neighbor distance
The dimensions are mandatory. Once dimensions have been parsed, the simulator creates the Potts cell field:
if (!(ppdCC3DPtr->dim.x != 0 || ppdCC3DPtr->dim.y != 0 || ppdCC3DPtr->dim.z != 0))
throw CC3DException("You must set Dimensions!");
potts.createCellField(ppdCC3DPtr->dim);
The simulator also instantiates BoundaryStrategy while processing the
Potts section. This is the object that later answers neighbor queries for
Potts3D during copy attempts.
Plugins and Steppables
CC3D separates plugins from steppables.
Plugins usually provide low-level services or Potts extensions. A plugin
may register an energy function, a lattice watcher, cell attributes, or
other callbacks with Potts3D. Plugins are initialized during
initializeCC3D:
Plugin *plugin = pluginManager.get(pluginName, &pluginAlreadyRegisteredFlag);
if (!pluginAlreadyRegisteredFlag) {
plugin->init(this, ps.pluginCC3DXMLElementVector[i]);
}
Steppables are simulation-level modules that run on an MCS schedule.
They are also initialized during initializeCC3D, then stored in
ClassRegistry:
Steppable *steppable = steppableManager.get(
steppableName,
&steppableAlreadyRegisteredFlag
);
if (!steppableAlreadyRegisteredFlag) {
if (ps.steppableCC3DXMLElementVector[i]->findAttribute("Frequency"))
steppable->frequency =
ps.steppableCC3DXMLElementVector[i]->getAttributeAsUInt("Frequency");
steppable->init(this, ps.steppableCC3DXMLElementVector[i]);
classRegistry->addStepper(steppableName, steppable);
}
This is the main difference between Potts-level callbacks and simulation-level steppables:
Potts-level
Stepperobjects are called inside the Potts copy-attempt machinery.Simulation-level
Steppableobjects are called byClassRegistryafter Potts finishes an MCS.
The Per-MCS Step
Simulator::step implements the per-MCS execution order:
Dim3D dim = potts.getCellFieldG()->getDim();
int flipAttempts = (int)(dim.x * dim.y * dim.z * ppdCC3DPtr->flip2DimRatio);
int flips = potts.metropolis(flipAttempts, ppdCC3DPtr->temperature);
currstep = currentStep;
classRegistry->step(currentStep);
This is the most important runtime sequence in the C++ core:
Compute the number of Potts copy attempts from lattice volume and
Flip2DimRatio.Run
potts.metropoliswith the configured fluctuation amplitude.Update the simulator’s current step.
Run active steppables whose frequency divides the current MCS.
ClassRegistry::step handles the steppable frequency check:
void ClassRegistry::step(const unsigned int currentStep) {
for (it = activeSteppers.begin(); it != activeSteppers.end(); it++) {
if ((*it)->frequency && (currentStep % (*it)->frequency) == 0)
(*it)->step(currentStep);
}
}
This means a steppable with Frequency="10" runs at MCS values that
are multiples of 10.
Extra Initialization
Simulator::extraInit gives plugins and steppables a second chance to
initialize after all requested modules exist:
for (it = infos->begin(); it != infos->end(); it++)
if (pluginManager.isLoaded((*it)->getName())) {
pluginManager.get((*it)->getName())->extraInit(this);
}
classRegistry->extraInit(this);
This second pass is useful when one module depends on another module that
may not have existed during the first init call. For plugin authors,
extraInit is the right place for setup that requires other plugins,
fields, or steppables to be available.
Start and Finish
Simulator::start calls start on every active steppable through
ClassRegistry and initializes runtime bookkeeping:
classRegistry->start();
currstep = 0;
simulatorIsStepping = true;
Simulator::finish first sets the Potts temperature to zero and runs
any configured annealing steps. It then calls finish on active
steppables and unloads dynamically loaded modules:
ppdCC3DPtr->temperature = 0.0;
for (unsigned int i = 1; i <= ppdCC3DPtr->anneal; i++)
step(ppdCC3DPtr->numSteps + i);
classRegistry->finish();
unloadModules();
Annealing lets the model perform additional zero-temperature relaxation steps after the normal simulation steps have completed.
Concentration Fields
The simulator keeps a map from concentration-field names to field pointers:
std::map<std::string, Field3D<float>*> concentrationFieldNameMap;
Modules can register fields and retrieve them by name:
void registerConcentrationField(std::string _name, Field3D<float>* _fieldPtr);
Field3D<float>* getConcentrationFieldByName(std::string _fieldName);
std::vector<std::string> getConcentrationFieldNameVector();
This is how plugins and steppables share concentration fields without hard-coding ownership relationships. A PDE solver can create or register a field, and another module can later retrieve that field by name.
Steering
Simulator stores steerable objects in steerableObjectMap:
std::map<std::string, SteerableObject *> steerableObjectMap;
Objects such as Potts3D can be registered as steerable objects. This
allows CC3D to update selected module parameters while the simulation is
running. Simulator::steer dispatches updates to registered steerable
objects based on the steering data available to the simulator.
Parallel Utilities and Metadata
The simulator owns two ParallelUtilsOpenMP objects:
pUtilsfor the normal simulation parallel configuration.pUtilsSinglefor modules that must run using a single worker.
During initialization, pUtils is attached to Potts3D by default.
Metadata can later request single-thread behavior for selected modules.
This matters for modules that are not safe or efficient to run in
parallel.
The important point for C++ developers is that parallel behavior is not
controlled only inside Potts3D. The simulator initializes the
parallel utilities, processes metadata, and then Potts and other modules
use the configured utility objects.
Cleanup
Cleanup is part of the simulator’s responsibility because dynamically loaded modules can own resources, register callbacks, and attach dynamic cell attributes. The simulator provides two relevant methods:
cleanAfterSimulationclears simulation state that must be removed before a new run.unloadModulesunloads dynamically loaded plugins and steppables.
This order is important. Cell inventory and dynamic attributes need to be cleaned while the relevant plugin code is still available. Unloading modules too early would leave the simulator with callbacks or attributes whose implementation code is gone.
How to Read the Core Control Flow
When reading CC3D C++ code, follow this path first:
Simulator::initializeCC3Dto see how the simulation is assembled.Simulator::initializePottsCC3Dto see how the<Potts>section becomesPotts3Dstate.Plugin
initmethods to see what they register withPotts3D.Simulator::stepto see the MCS-level schedule.Potts3D::metropolisFastto see individual copy attempts.ClassRegistry::stepto see when simulation-level steppables run.
This path gives the most useful mental model: Simulator owns the run,
Potts3D owns pixel-copy dynamics, plugins extend Potts behavior, and
ClassRegistry schedules steppables after each MCS.