Networks and clocks (Brian 1 --> 2 conversion)
==============================================
.. sidebar:: Brian 2 documentation

    For the main documentation about running simulations, controling the
    simulation timestep, etc., see the document :doc:`../../user/running`.

.. contents::
    :local:
    :depth: 1

Clocks and timesteps
--------------------
Brian's system of handling clocks has substantially changed. For details about
the new system in place see :ref:`time_steps`. The main differences to Brian 1
are:

* There is no more "clock guessing" -- objects either use the `defaultclock` or
  a ``dt``/``clock`` value that was explicitly specified during their
  construction.
* In Brian 2, the time step is allowed to change after the creation of an object
  and between runs -- the relevant value is the value in place at the point of
  the `run` call.
* It is rarely necessary to create an explicit `Clock` object, most of the time
  you should use the `defaultclock` or provide a ``dt`` argument during the
  construction of the object.
* There's only one `Clock` class, the (deprecated) ``FloatClock``,
  ``RegularClock``, etc. classes that Brian 1 provided no longer exist.
* It is no longer possible to (re-)set the time of a clock explicitly, there is
  no direct equivalent of ``Clock.reinit`` and ``reinit_default_clock``. To
  start a completely new simulation after you have finished a previous one,
  either create a new `Network` or use the `start_scope` mechanism. To "rewind"
  a simulation to a previous point, use the new `store`/`restore` mechanism. For
  more details, see below and :doc:`../../user/running`.

Networks
--------
Both Brian 1 and Brian 2 offer two ways to run a simulation: either by
explicitly creating a `Network` object, or by using a `MagicNetwork`, i.e. a
simple `run` statement.

Explicit network
~~~~~~~~~~~~~~~~
The mechanism to create explicit `Network` objects has not changed significantly
from Brian 1 to Brian 2. However, creating a new `Network` will now also
automatically reset the clock back to 0s, and stricter checks no longer allow
the inclusion of the same object in multiple networks.

+------------------------------+------------------------------+
+ Brian 1                      | Brian 2                      |
+==============================+==============================+
| .. code::                    | .. code::                    |
|                              |                              |
|    group = ...               |    group = ...               |
|    mon = ...                 |    mon = ...                 |
|    net = Network(group, mon) |    net = Network(group, mon) |
|    net.run(1*ms)             |    net.run(1*ms)             |
|                              |                              |
|    reinit()                  |    # new network starts at 0s|
|    group = ...               |    group = ...               |
|    mon = ...                 |    mon = ...                 |
|    net = Network(group, mon) |    net = Network(group, mon) |
|    net.run(1*ms)             |    net.run(1*ms)             |
|                              |                              |
+------------------------------+------------------------------+

"Magic" network
~~~~~~~~~~~~~~~
For most simple, "flat", scripts (see e.g. the :doc:`../../examples/index`),
the `run` statement in Brian 2 automatically collects all the Brian objects
(`NeuronGroup`, etc.) into a "magic" network in the same way as Brian 1 did.
The logic behind this collection has changed, though, with important
consequences for more complex simulation scripts: in Brian 1, the magic network
includes all Brian objects that have been *created* in the same execution frame
as the `run` call. Objects that are created in other functions could be added
using ``magic_return`` and ``magic_register``. In Brian 2, the magic network
contains all Brian objects that are *visible* in the same execution frame as the
`run` call. The advantage of the new system is that it is clearer what will be
included in the network and there is no danger of including previously created,
but no longer needed, objects in a simulation. E.g. in the following example,
a common mistake in Brian 1 was to not include the `clear()`, which meant that
each run not only simulated the current objects, but also all objects from
previous loop iterations. Also, without the ``reinit_default_clock()``,
each run would start at the end time of the previous run. In Brian 2, this loop
does not need any explicit clearing up, each `run` will only simulate the
object that it "sees" (``group1``, ``group2``, ``syn``, and ``mon``) and start
each simulation at 0s:

+--------------------------------------------+--------------------------------------------+
| Brian 1                                    | Brian 2                                    |
+============================================+============================================+
| .. code::                                  | .. code::                                  |
|                                            |                                            |
|     for r in range(100):                   |     for r in range(100):                   |
|         reinit_default_clock()             |                                            |
|         clear()                            |                                            |
|         group1 = NeuronGroup(...)          |         group1 = NeuronGroup(...)          |
|         group2 = NeuronGroup(...)          |         group2 = NeuronGroup(...)          |
|         syn = Synapses(group1, group2, ...)|         syn = Synapses(group1, group2, ...)|
|         mon = SpikeMonitor(group2)         |         mon = SpikeMonitor(group2)         |
|         run(1*second)                      |         run(1*second)                      |
|                                            |                                            |
+--------------------------------------------+--------------------------------------------+

There is no replacement for the ``magic_return`` and ``magic_register``
functions. If the returned object is stored in a variable at the level of
the `run` call, then it is no longer necessary to use ``magic_return``, as the
returned object is "visible" at the level of the `run` call:

+-----------------------------------------------+-------------------------------------------------+
| Brian 1                                       | Brian 2                                         |
+===============================================+=================================================+
| .. code::                                     | .. code::                                       |
|                                               |                                                 |
|     @magic_return                             |                                                 |
|     def f():                                  |     def f():                                    |
|         return PoissonGroup(100, rates=100*Hz)|         return PoissonGroup(100, rates=100*Hz)  |
|                                               |                                                 |
|     pg = f() # needs magic_return             |     pg = f() # is "visible" and will be included|
|     mon = SpikeMonitor(pg)                    |     mon = SpikeMonitor(pg)                      |
|     run(100*ms)                               |     run(100*ms)                                 |
|                                               |                                                 |
+-----------------------------------------------+-------------------------------------------------+

The general recommendation is however: if your script is complex (multiple
functions/files/classes) and you are not sure whether some objects will be
included in the magic network, use an explicit `Network` object.

Note that one consequence of the "is visible" approach is that objects stored
in containers (lists, dictionaries, ...) will not be automatically included in
Brian 2. Use an explicit `Network` object to get around this restriction:

+----------------------------------------+----------------------------------------+
| Brian 1                                | Brian 2                                |
+========================================+========================================+
| .. code::                              | .. code::                              |
|                                        |                                        |
|     groups = {'exc': NeuronGroup(...), |     groups = {'exc': NeuronGroup(...), |
|               'inh': NeuronGroup(...)} |               'inh': NeuronGroup(...)} |
|     ...                                |     ...                                |
|                                        |     net = Network(groups)              |
|     run(5*ms)                          |     net.run(5*ms)                      |
|                                        |                                        |
+----------------------------------------+----------------------------------------+

External constants
~~~~~~~~~~~~~~~~~~
In Brian 2, external constants are taken from the surrounding namespace at
the point of the `run` call and not when the object is defined (for other ways
to define the namespace, see :ref:`external-variables`). This allows to easily
change external constants between runs, in contrast to Brian 1 where the whether
this worked or not depended on details of the model (e.g. whether linear
integration was used):

+----------------------------------------------------------+-----------------------------------------------------------+
| Brian 1                                                  | Brian 2                                                   |
+==========================================================+===========================================================+
| .. code::                                                | .. code::                                                 |
|                                                          |                                                           |
|    tau = 10*ms                                           |     tau = 10*ms                                           |
|    # to be sure that changes between runs are taken into |                                                           |
|    # account, define "I" as a neuronal parameter         |     # The value for I will be updated at each run         |
|    group = NeuronGroup(10, '''dv/dt = (-v + I) / tau : 1 |     group = NeuronGroup(10, 'dv/dt = (-v + I) / tau : 1') |
|                               I : 1''')                  |                                                           |
|    group.v = linspace(0, 1, 10)                          |     group.v = linspace(0, 1, 10)                          |
|    group.I = 0.0                                         |     I = 0.0                                               |
|    mon = StateMonitor(group, 'v', record=True)           |     mon = StateMonitor(group, 'v', record=True)           |
|    run(5*ms)                                             |     run(5*ms)                                             |
|    group.I = 0.5                                         |     I = 0.5                                               |
|    run(5*ms)                                             |     run(5*ms)                                             |
|    group.I = 0.0                                         |     I = 0.0                                               |
|    run(5*ms)                                             |     run(5*ms)                                             |
|                                                          |                                                           |
+----------------------------------------------------------+-----------------------------------------------------------+

