diff --git a/brian2/core/clocks.py b/brian2/core/clocks.py index d2f051dee..d6ecdf73c 100644 --- a/brian2/core/clocks.py +++ b/brian2/core/clocks.py @@ -36,8 +36,7 @@ def check_dt(new_dt, old_dt, target_t): ------ ValueError If using the new dt value would lead to a difference in the target - time of more than `Clock.epsilon_dt` times ``new_dt`` (by default, - 0.01% of the new dt). + time of more than `Clock.epsilon_dt` (by default, 10ns). Examples -------- @@ -52,7 +51,7 @@ def check_dt(new_dt, old_dt, target_t): old_t = np.int64(np.round(target_t / old_dt)) * old_dt new_t = np.int64(np.round(target_t / new_dt)) * new_dt error_t = target_t - if abs(new_t - old_t)/new_dt > Clock.epsilon_dt: + if abs(new_t - old_t) > Clock.epsilon_dt: raise ValueError(('Cannot set dt from {old} to {new}, the ' 'time {t} is not a multiple of ' '{new}').format(old=str(old_dt * second), @@ -74,10 +73,8 @@ class Clock(VariableOwner): 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) {{ openmp_pragma('include') }} -#define Clock_epsilon 1e-14 - double Network::_last_run_time = 0.0; double Network::_last_run_completed_fraction = 0.0; @@ -158,7 +156,7 @@ Clock* Network::next_clocks() { Clock *clock = *i; double s = clock->t[0]; - if(s==t || fabs(s-t)<=Clock_epsilon) + if(s==t || fabs(s-t) <= clock->epsilon) curclocks.insert(clock); } return minclock; diff --git a/brian2/devices/cpp_standalone/templates/objects.cpp b/brian2/devices/cpp_standalone/templates/objects.cpp index a6fc2d5a0..c9d9f74a3 100644 --- a/brian2/devices/cpp_standalone/templates/objects.cpp +++ b/brian2/devices/cpp_standalone/templates/objects.cpp @@ -59,7 +59,7 @@ SynapticPathway {{path.name}}( //////////////// clocks /////////////////// {% for clock in clocks | sort(attribute='name') %} -Clock {{clock.name}}; // attributes will be set in run.cpp +Clock {{clock.name}}({{clock.epsilon_dt}}); // attributes will be set in run.cpp {% endfor %} {% if profiled_codeobjects is defined %} diff --git a/brian2/tests/test_clocks.py b/brian2/tests/test_clocks.py index eba9c96b8..1607123e7 100644 --- a/brian2/tests/test_clocks.py +++ b/brian2/tests/test_clocks.py @@ -59,6 +59,31 @@ def test_set_interval_warning(): assert logs[0][1].endswith('many_timesteps') +@attr('codegen-independent') +def test_very_long_dt(): + # See github issue #1054 + clock1 = Clock(dt=0.1*ms) + clock2 = Clock(dt=100000*second) + clock1.set_interval(0*ms, 1*ms) + clock2.set_interval(0*ms, 1*ms) # The clock should advance + assert clock1.timestep[:] == 0 + assert clock2.timestep[:] == 0 + assert clock1._i_end == 10 + assert clock2._i_end == 1 + # Simulate advancing the clock + clock1.variables['timestep'].set_value(clock1._i_end) + clock1.variables['t'].set_value(clock1._i_end * clock1.dt) + clock2.variables['timestep'].set_value(clock2._i_end) + clock2.variables['t'].set_value(clock2._i_end * clock2.dt) + + clock1.set_interval(1*ms, 2*ms) + clock2.set_interval(1*ms, 2*ms) # The clock should not advance + assert clock1.timestep[:] == 10 + assert clock2.timestep[:] == 1 + assert clock1._i_end == 20 + assert clock2._i_end == 1 + + if __name__ == '__main__': test_clock_attributes() restore_initial_state() @@ -66,3 +91,4 @@ def test_set_interval_warning(): restore_initial_state() test_defaultclock() test_set_interval_warning() + test_very_long_dt() diff --git a/brian2/tests/test_network.py b/brian2/tests/test_network.py index 175440fe3..bed2b3679 100644 --- a/brian2/tests/test_network.py +++ b/brian2/tests/test_network.py @@ -1372,6 +1372,24 @@ def test_small_runs(): assert_allclose(mon_1.t_[:], mon_2.t_[:]) assert_allclose(mon_1.v_[:], mon_2.v_[:]) +@attr('standalone-compatible') +@with_setup(teardown=reinit_devices) +def test_long_run(): + defaultclock.dt = 0.1 * ms + group = NeuronGroup(1, 'x : 1') + group.run_regularly('x += 1') + # Timesteps are internally stored as 64bit integers, but previous versions + # converted them into 32bit integers along the way. We'll make sure that + # this is not the case and everything runs fine. To not actually run such a + # long simulation we manually advance the clock. + net = Network(group, name='network') + start_step = 2**31-5 + start_time = start_step*defaultclock.dt_ + with catch_logs() as l: + net.t_ = start_time # for runtime + device.insert_code('main', 'network.t = {};'.format(start_time)) # for standalone + net.run(6 * defaultclock.dt) + assert group.x == 6 @attr('codegen-independent') @with_setup(teardown=reinit_devices) @@ -1409,6 +1427,7 @@ def test_multiple_runs_function_change(): device.build(direct_call=False, **device.build_options) assert_equal(mon.v[0], [1, 2, 3, 4]) + if __name__ == '__main__': BrianLogger.log_level_warn() for t in [ @@ -1470,6 +1489,7 @@ def test_multiple_runs_function_change(): test_magic_scope, test_runtime_rounding, test_small_runs, + test_long_run, test_long_run_dt_change, test_multiple_runs_constant_change, test_multiple_runs_function_change