Tutorial 4 (Simulator)

This is a tutorial for E-Cell4. Here, we explain how to handle Simulators.

Each World has its corresponding Simulator.


In [1]:
from ecell4.core import *
from ecell4.gillespie import GillespieWorld as world_type, GillespieSimulator as simulator_type
# from ecell4.ode import ODEWorld as world_type, ODESimulator as simulator_type
# from ecell4.lattice import LatticeWorld as world_type, LatticeSimulator as simulator_type
# from ecell4.meso import MesoscopicWorld as world_type, MesoscopicSimulator as simulator_type
# from ecell4.bd import BDWorld as world_type, BDSimulator as simulator_type
# from ecell4.egfrd import EGFRDWorld as world_type, EGFRDSimulator as simulator_type

Simulator needs a Model and World at the instantiation.


In [2]:
m = NetworkModel()
m.add_species_attribute(Species("A", "0.0025", "1"))
m.add_reaction_rule(create_degradation_reaction_rule(Species("A"), 0.693 / 1))

w = world_type(Real3(1, 1, 1))
w.bind_to(m)
w.add_molecules(Species("A"), 60)

sim = simulator_type(m, w)
sim.set_dt(0.01) #XXX: Optional

A Simulator has getters for a simulation time, a step interval, and the next-event time. In principle, a Simulator returns the World's time as its simulation time, and does a sum of the current time and a step interval as the next-event time.


In [3]:
print(sim.num_steps())
print(sim.t(), w.t())
print(sim.next_time(), sim.t() + sim.dt())


0
(0.0, 0.0)
(0.05844111911215542, 0.05844111911215542)

A Simulator can return the connected model and world. They are not copies, but the shared objects.


In [4]:
print(sim.model(), sim.world())


(<ecell4.core.Model object at 0x7fdfe76e8990>, <ecell4.gillespie.GillespieWorld object at 0x7fdfe76e89c0>)

If you change a World after connecting it to a Simulator, you have to call initialize() manually before step(). The call will update the internal state of the Simulator.


In [5]:
sim.world().add_molecules(Species("A"), 60) # w.add_molecules(Species("A"), 60)
sim.initialize()

In [6]:
# w.save('test.h5')

Simulator has two types of step functions. First, with no argument, step() increments the time until next_time().


In [7]:
print("%.3e %.3e" % (sim.t(), sim.next_time()))
sim.step()
print("%.3e %.3e" % (sim.t(), sim.next_time()))


0.000e+00 1.240e-03
1.240e-03 6.506e-03

With an argument upto, if upto is later than next_time(), step(upto) increments the time upto the next_time() and returns True. Otherwise, it increments the time for upto and returns False. (If the current time t() is less than upto, it does nothing and returns False.)


In [8]:
print("%.3e %.3e" % (sim.t(), sim.next_time()))
print(sim.step(0.1))
print("%.3e %.3e" % (sim.t(), sim.next_time()))


1.240e-03 6.506e-03
True
6.506e-03 2.118e-02

For a discrete-step simulation, the main loop should be written like:


In [9]:
# w.load('test.h5')
sim.initialize()

In [10]:
next_time, dt = 0.0, 1e-2
for _ in range(5):
    while sim.step(next_time): pass
    next_time += dt
    print("%.3e %.3e %d %g" % (sim.t(), sim.dt(), sim.num_steps(), w.num_molecules(Species("A"))))


6.506e-03 2.734e-02 2 118
1.000e-02 1.988e-02 2 118
2.000e-02 9.746e-03 2 118
3.000e-02 2.122e-03 3 117
4.000e-02 1.734e-02 6 114

In [ ]: