==================
Simulation control
==================

Initialising the simulator
==========================

.. testsetup::

    from pyNN.mock import *


Before using any other functions or classes from PyNN, the user must call the
:func:`setup()` function::

    >>> setup()

:func:`setup()` takes various optional arguments: setting the simulation
timestep (there is currently no support in the API for variable timestep methods
although native simulator code can be used to select this option where the
simulator supports it) and setting the minimum and maximum synaptic delays, e.g.::

    >>> setup(timestep=0.1, min_delay=0.1, max_delay=10.0)

Calling :func:`setup()` a second time resets the simulator entirely, destroying
any network that may have been created in the meantime.

.. todo:: add links to documentation on simulator-specific options to setup()

Getting information about the simulation state
==============================================

Several functions are available for obtaining information about the simulation
state:

* :func:`get_current_time` - the time within the simulation
* :func:`get_time_step` - the integration time step
* :func:`get_min_delay` - the minimum allowed synaptic delay
* :func:`get_max_delay` - the maximum allowed synaptic delay
* :func:`num_processes` - the number of MPI processes
* :func:`rank` - the MPI rank of the current node

Running a simulation
====================

The :func:`run()` function advances the simulation for a given number of milliseconds, e.g.::

    >>> run(1000.0)

You can also use :func:`run_for()`, which is an alias for :func:`run()`.
The :func:`run_until()` function advances the simulation until a given future
time point, e.g.::

    >>> run_until(1001.0)
    >>> get_current_time()
    1001.0


Performing operations during a run
----------------------------------

You may wish to perform some calculation, or show some information, during a
run. One way to do this is to break the simulation into steps, and perform
the operation at the end of each step, e.g.::

    >>> for i in range(4):
    ...    run_until(100.0*i)
    ...    print("The time is %g" % (100*i,))
    The time is 0
    The time is 100
    The time is 200
    The time is 300

Alternatively, PyNN can take care of breaking the simulation into steps for you.
:func:`run()` and :func:`run_until()` each accept an optional list of callbacks
functions. Each callback should accept the current time as an argument, and
return the next time it wishes to be called.

    >>> def report_time(t):
    ...     print("The time is %g" % t)
    ...     return t + 100.0
    >>> run_until(300.0, callbacks=[report_time])
    The time is 0
    The time is 100
    The time is 200
    The time is 300
    300.0

For simple cases, this requires a bit more code, but it is potentially much more
powerful, especially if you have complex or multiple callbacks.


Repeating a simulation
======================

If you wish to reset network time to zero to run a new simulation with the same
network (with different parameter values, perhaps), use the :func:`reset()` function.
Note that this does not change the network structure, nor the choice of which
neurons to record (from previous :meth:`record()` calls).

Finishing up
============

Just as a simulation must be begun with a call to ``setup()``, it should be
ended with a call to ``end()``. This is not always necessary, but it is safest
to always use it.
