.. currentmodule:: brian

Units
=====

Basics
------
Brian has a system for physical quantities with units built in, and most of the
library functions require that variables have the right units. This restriction
is useful in catching hard to find errors based on using incorrect units, and
ensures that simulated models are physically meaningful. For example, running
the following code causes an error::

	>>> from brian import *
	>>> c = Clock(t=0)
	
	Traceback (most recent call last):
	  File "<pyshell#1>", line 1, in <module>
	    c = Clock(t=0)
	  File "C:\Documents and Settings\goodman\Mes documents\Programming\Python simulator\Brian\units.py", line 1126, in new_f
	    raise DimensionMismatchError("Function " + f.__name__ + " variable " + k + " should have dimensions of " + str(au[k]), getDimensions(newkeyset[k]))
	DimensionMismatchError: Function __init__ variable t should have dimensions of s, dimensions were (1) 

You can see that Brian raises a ``DimensionMismatchError`` exception, because the
:class:`Clock` object expects ``t`` to have units of time. The correct thing to write is::

	>>> from brian import *
	>>> c = Clock(t=0*second)

Similarly, attempting to do numerical operations with inconsistent units will
raise an error::

	>>> from brian import *
	>>> 3*second+2*metre
	
	Traceback (most recent call last):
	  File "<pyshell#38>", line 1, in <module>
	    3*second+2*metre
	  File "C:\Documents and Settings\goodman\Mes documents\Programming\Python simulator\Brian\units.py", line 600, in __add__
	    if dim==self.dim:
	DimensionMismatchError: Addition, dimensions were (s) (m) 

Units defined in Brian
----------------------
The following fundamental SI unit names are defined:

	``metre``, ``meter`` (US spelling), ``kilogram``, ``second``, ``amp``, ``kelvin``, ``mole``, ``candle``
	
These derived SI unit names are also defined:

	``radian``, ``steradian``, ``hertz``, ``newton``, ``pascal``, ``joule``, ``watt``,
	``coulomb``, ``volt``, ``farad``, ``ohm``, ``siemens``, ``weber``, ``tesla``,
	``henry``, ``celsius``, ``lumen``, ``lux``, ``becquerel``, ``gray``, ``sievert``, ``katal``

In addition, you can form scaled versions of these units with any of the standard SI prefixes:

========== ======= ================ ========== ======= ================
Factor      Name    Symbol          Factor      Name    Symbol         
========== ======= ================ ========== ======= ================
 10^24      yotta   Y                10^-24     yocto   y
 10^21      zetta   Z                10^-21     zepto   z
 10^18      exa     E                10^-21     zepto   z
 10^15      peta    P                10^-15     femto   f
 10^12      tera    T                10^-12     pico    p
 10^9       giga    G                10^-9      nano    n
 10^6       mega    M                10^-6      micro   u (mu in SI)
 10^3       kilo    k                10^-3      milli   m
 10^2       hecto   h                10^-2      centi   c
 10^1       deka    da               10^-1      deci    d
========== ======= ================ ========== ======= ================

So for example, you could write ``fnewton`` for femto-newtons, ``Mwatt`` for megawatt, etc.

There are also units for 2nd and 3rd powers of each of the above units, for example
``metre3 = metre**3``, ``watt2 = watt*watt``, etc.

You can optionally use short names for some units derived from volts, amps,
farads, siemens, seconds, hertz and metres:
``mV, mA, uA, nA, pA, mF, uF, nF, mS, uS, ms, Hz, kHz, MHz, cm, cm2, cm3,``
``mm, mm2, mm3, um, um2, um3``.
Since these names are so short, there is a danger that they might clash with your
own variables names, so watch out for that.

.. removed in Brian 1.0

	Arrays with units
	-----------------
	In Brian, arrays have units by default. In fact, a new class named
	:class:`qarray` replaces the standard ``array`` class. For example, one can define a
	vector with each element being ``1*mV`` as follows::
	
	  x=ones(5)*mV
	  
	Attempts to assign one element of x with a different units (e.g. ``x[2]=1*nA``)
	raises an error.

Arrays and units
----------------

Versions of Brian before 1.0 had a system for allowing arrays to have units, this has
been removed for the 1.0 release because of stability problems - as new releases of
NumPy, SciPy and PyLab came out it required changes to the units code. Now all arrays
used by Brian are standard NumPy arrays and have no units.

Checking units
--------------

Units are automatically checked when arithmetic operations are performed, and when
a neuron group is initialised (the consistency of the differential equations is checked).
They can also be checked explictly when a user-defined function is called by using the
decorator ``@check_units``, which can be used as follows::

  @check_units(I=amp,R=ohm,wibble=metre,result=volt)
  def getvoltage(I,R,**k):
      return I*R

Remarks:

* not all arguments need to be checked
* keyword arguments may be checked
* the result can optionnally be checked
* no error is raised if the values are strings.

Disabling units
---------------

Unit checking can slow down the simulations. The units system can be disabled by inserting
``import brian_no_units`` as the *first line* of the script, e.g.::
 
  import brian_no_units
  from brian import *
  # etc
  
Internally, physical quantities are floats with an additional units information. The float
value is the value in the SI system. For example, ``float(mV)`` returns ``0.001``. 
After importing ``brian_no_units``, all units are converted to their float values. For example,
``mV`` is simply the number ``0.001``.
This may also be a solution when using external libraries which are not compatible with units
(but see next section).

Unit checking can also be turned down locally when initializing a neuron group by
passing the argument ``check_units=False``. In that case, no error is raised if the
differential equations are not homogeneous.

A good practice is to develop the script with units on, then switch them off once the script
runs correctly.

Converting quantities
---------------------

In many situations, physical quantities need to be expressed with given units. For example,
one might want to plot a graph of the membrane potential in mV as a function of time in ms.
The following code::

  plot(t,V)
  
displays the trace with time in seconds and potential in volts. The simplest solution to have
time in ms and potential in mV is to use units operations::

  plot(t/ms,V/mV)

Here, t/ms is a unitless array containing the values of t in ms.
The same trick may be applied to use external functions which do not work with units
(convert the arguments to unitless quantities as above).
