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.
%jsroot on
gROOT->LoadMacro("${BDMSYS}/etc/rootlogon.C");
INFO: Created simulation object 'simulation' with UniqueName='simulation'.
using namespace bdm::experimental;
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 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.
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.
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
simulation.GetResourceManager()->ClearAgents();
ModelInitializer::CreateAgentsRandom(0, 200, 100, create_cell);
simulation.GetScheduler()->FinalizeInitialization();
VisualizeInNotebook();
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 ...
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 .
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.
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
simulation.GetScheduler()->Simulate(40);
Now we can plot how the number of agents (in this case cells) and the number of agents with evolved over time.
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();
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
auto fitresult = g.GetTGraphs("num-agents")[0]->Fit("expo", "S");
g.Draw();
**************************************** 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 with constant = 4.6 and slope = 0.049 This corresponds to the division probability of
This is how to change the color after the creation of g
.
Also the position of the legend can be optimized.
g.GetTGraphs("num-agents")[0]->SetLineColor(kBlack);
g.SetLegendPos(1, 500, 20, 700);
g.Draw();
Let's save these results in multiple formats
g.SaveAs(Concat(simulation.GetOutputDir(), "/line-graph"),
{".root", ".svg", ".png", ".C"});