/
clocks.py
157 lines (127 loc) · 5.07 KB
/
clocks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
"""
Clocks for the simulator.
"""
__docformat__ = "restructuredtext en"
from numpy import ceil
from brian2.utils.logger import get_logger
from brian2.core.names import Nameable
from brian2.units.fundamentalunits import check_units
from brian2.units.allunits import second, msecond
__all__ = ['Clock', 'defaultclock']
logger = get_logger(__name__)
class Clock(Nameable):
'''
Clock(dt=0.1*ms, name=None)
An object that holds the simulation time and the time step.
Parameters
----------
dt : `Quantity`, optional
The time step of the simulation, will be set to ``0.1*ms`` if
unspecified.
name : (str, None), optional
An explicit name, if not specified gives an automatically generated name
Notes
-----
Clocks are run in the same `Network.run` iteration if `~Clock.t` is the
same. The condition for two
clocks to be considered as having the same time is
``abs(t1-t2)<epsilon*abs(t1)``, a standard test for equality of floating
point values. The value of ``epsilon`` is ``1e-14``.
'''
#: The stem for the automatically generated `name` attribute
basename = 'clock'
name = Nameable.name
@check_units(dt=second)
def __init__(self, dt=None, name=None):
self._dt_spec = dt
self.i = 0 #: The time step of the simulation as an integer.
self.i_end = 0 #: The time step the simulation will end as an integer
Nameable.__init__(self, name)
logger.debug("Created clock {self.name} with dt={self._dt_spec}".format(self=self))
def reinit(self):
'''
Reinitialises the clock time to zero.
'''
self.i = 0
def __str__(self):
return 'Clock ' + self.name + ': t = ' + str(self.t) + ', dt = ' + str(self.dt)
def __repr__(self):
return 'Clock(dt=%r, name=%r)' % (self.dt, self.name)
def tick(self):
'''
Advances the clock by one time step.
'''
self.i += 1
@check_units(t=second)
def _set_t(self, t):
self.i = int(float(t) / self.dt_)
def _set_t_(self, t):
self.i = int(t/self.dt_)
@check_units(end=second)
def _set_t_end(self, end):
self.i_end = int(float(end) / self.dt_)
def _get_dt_(self):
if hasattr(self, '_dt'):
return self._dt
else:
dtspec = self._dt_spec
if dtspec is None:
dtspec = 0.1*msecond
self._dt = float(dtspec)
return self._dt
def _set_dt_(self, dt_):
if hasattr(self, '_dt'):
raise RuntimeError("Cannot change dt, it has already been set to "+str(self.dt))
self._dt = dt_
logger.debug("Set dt for clock {self.name} to {self.dt}".format(self=self))
@check_units(dt=second)
def _set_dt(self, dt):
self.dt_ = float(dt)
dt = property(fget=lambda self: self.dt_*second,
fset=_set_dt,
doc='''The time step of the simulation in seconds
Returns a `Quantity`, and can only
be set once. Defaults to ``0.1*ms``.''',
)
dt_ = property(fget=_get_dt_, fset=_set_dt_,
doc='''The time step of the simulation as a float (in seconds)''')
t = property(fget=lambda self: self.i*self.dt_*second,
fset=_set_t,
doc='The simulation time in seconds')
t_ = property(fget=lambda self: self.i*self.dt_,
fset=_set_t_,
doc='The simulation time as a float (in seconds)')
t_end = property(fget=lambda self: self.i_end*self.dt_*second,
fset=_set_t_end,
doc='The time the simulation will end (in seconds)')
@check_units(start=second, end=second)
def set_interval(self, start, end):
'''
set_interval(self, start, end)
Set the start and end time of the simulation.
Sets the start and end value of the clock precisely if
possible (using epsilon) or rounding up if not. This assures that
multiple calls to `Network.run` will not re-run the same time step.
'''
start = float(start)
end = float(end)
i_start = int(round(start/self.dt_))
t_start = i_start*self.dt_
if t_start==start or abs(t_start-start)<=self.epsilon*abs(t_start):
self.i = i_start
else:
self.i = int(ceil(start/self.dt_))
i_end = int(round(end/self.dt_))
t_end = i_end*self.dt_
if t_end==end or abs(t_end-end)<=self.epsilon*abs(t_end):
self.i_end = i_end
else:
self.i_end = int(ceil(end/self.dt_))
@property
def running(self):
'''
A ``bool`` to indicate whether the current simulation is running.
'''
return self.i<self.i_end
epsilon = 1e-14
defaultclock = Clock(name='defaultclock')