Simulation time series plotting and analysis¶

Author: Lukas Breitwieser
In this tutorial we show how to collect data during the simulation, and plot and analyse it at the end.
To this extent, we create a simulation where cells divide rapidly leading to exponential growth.

Let's start by setting up BioDynaMo notebooks.

In [1]:
%jsroot on
gROOT->LoadMacro("${BDMSYS}/etc/rootlogon.C");
INFO: Created simulation object 'simulation' with UniqueName='simulation'.
In [2]:
using namespace bdm::experimental;
In [3]:
auto set_param = [](Param* param) {
    param->simulation_time_step = 1.0;
};
Simulation simulation("MySimulation", set_param);

Let's create a behavior which divides cells with 5%5% probability in each time step.
New cells should also get this behavior.
Therefore, we have to call AlwaysCopyToNew().
Otherwise, we would only see linear growth.

In [4]:
StatelessBehavior rapid_division([](Agent* agent) {
  if (Simulation::GetActive()->GetRandom()->Uniform() < 0.05) {
    bdm_static_cast<Cell*>(agent)->Divide();
  }
});
rapid_division.AlwaysCopyToNew();

Let's create a function that creates a cell at a specific position, with diameter = 10, and the rapid_division behavior.

In [5]:
auto create_cell = [](const Real3& position) {
  Cell* cell = new Cell(position);
  cell->SetDiameter(10);
  cell->AddBehavior(rapid_division.NewCopy());
  return cell;
};

As starting condition we want to create 100 cells randomly distributed in a cube with min=0,max=200min=0,max=200

In [6]:
simulation.GetResourceManager()->ClearAgents();
ModelInitializer::CreateAgentsRandom(0, 200, 100, create_cell);
simulation.GetScheduler()->FinalizeInitialization();
VisualizeInNotebook();
ROOT canvasToggle tool buttonsCreate PNG (keyshortcut Ctrl PrintScreen)Access context menusEnlarge canvasToggle GedToggle Status

Before we start the simulation, we have to tell BioDynaMo which data to collect.
We can do this with the TimeSeries::AddCollector function.
In this example we are interested in the number of agents ...

In [7]:
auto* ts = simulation.GetTimeSeries();
auto get_num_agents = [](Simulation* sim) {
  return static_cast<real_t>(sim->GetResourceManager()->GetNumAgents());
};
ts->AddCollector("num-agents", get_num_agents);

... and the number agents with diameter<5diameter<5.
We create a condition cond and pass it to an instance of Counter which calculates the number of agents for which cond(agent) evaluates to true.

In [8]:
auto cond = [](Agent* a) { return a->GetDiameter() < 5; };
ts->AddCollector("agents_lt_5", new bdm::experimental::Counter<real_t>(cond));

Now let's simulate 40 iterations

In [9]:
simulation.GetScheduler()->Simulate(40);

Now we can plot how the number of agents (in this case cells) and the number of agents with diameter<5diameter<5 evolved over time.

In [10]:
LineGraph g(ts, "my result", "Time", "Number of agents", 
                               true, nullptr, 500, 300);
g.Add("num-agents", "Number of Agents", "L", kBlue);
g.Add("agents_lt_5", "Number of Agents diam < 5", "L", kGreen);
g.Draw();
ROOT canvas0510152025303540Time0100200300400500600700Number of agentsNumber of AgentsNumber of Agents diam < 5my resultToggle tool buttonsCreate PNG (keyshortcut Ctrl PrintScreen)Access context menusEnlarge canvasToggle GedToggle StatusToggle between unzoom and autozoom-in (keyshortcut Ctrl *)Toggle log x (keyshortcut PageDown)Toggle log y (keyshortcut PageUp)

Let's try to fit an exponential function to verify our assumption that the cells grew exponentially.
Please visit the ROOT user-guide for more information regarding fitting

In [11]:
auto fitresult = g.GetTGraphs("num-agents")[0]->Fit("expo", "S");
g.Draw();
ROOT canvas0510152025303540Time0100200300400500600700Number of agentsχ^2 / ndf 377.1 / 38Constant 4.648 ± 0.005Slope 0.04977 ± 0.00016Number of AgentsNumber of Agents diam < 5my resultToggle tool buttonsCreate PNG (keyshortcut Ctrl PrintScreen)Access context menusEnlarge canvasToggle GedToggle StatusToggle between unzoom and autozoom-in (keyshortcut Ctrl *)Toggle log x (keyshortcut PageDown)Toggle log y (keyshortcut PageUp)
****************************************
Minimizer is Minuit2 / Migrad
Chi2                      =      377.117
NDf                       =           38
Edm                       =  2.80271e-11
NCalls                    =           49
Constant                  =      4.64791   +/-   0.00505256  
Slope                     =    0.0497729   +/-   0.000161219 

Indeed, the number of agents follow an exponential function y=exp(slope∗x+constant)y=exp⁡(slope∗x+constant) with constant = 4.6 and slope = 0.049 This corresponds to the division probability of 0.050.05

This is how to change the color after the creation of g.
Also the position of the legend can be optimized.

In [12]:
g.GetTGraphs("num-agents")[0]->SetLineColor(kBlack);
g.SetLegendPos(1, 500, 20, 700);
g.Draw();
ROOT canvas0510152025303540Time0100200300400500600700Number of agentsχ^2 / ndf 377.1 / 38Constant 4.648 ± 0.005Slope 0.04977 ± 0.00016Number of AgentsNumber of Agents diam < 5my resultToggle tool buttonsCreate PNG (keyshortcut Ctrl PrintScreen)Access context menusEnlarge canvasToggle GedToggle StatusToggle between unzoom and autozoom-in (keyshortcut Ctrl *)Toggle log x (keyshortcut PageDown)Toggle log y (keyshortcut PageUp)

Let's save these results in multiple formats

In [13]:
g.SaveAs(Concat(simulation.GetOutputDir(), "/line-graph"), 
         {".root", ".svg", ".png", ".C"});
ROOT canvas0510152025303540Time0100200300400500600700Number of agentsχ^2 / ndf 377.1 / 38Constant 4.648 ± 0.005Slope 0.04977 ± 0.00016Number of AgentsNumber of Agents diam < 5my resultToggle tool buttonsCreate PNG (keyshortcut Ctrl PrintScreen)Access context menusEnlarge canvasToggle GedToggle StatusToggle between unzoom and autozoom-in (keyshortcut Ctrl *)Toggle log x (keyshortcut PageDown)Toggle log y (keyshortcut PageUp)