# Representing spatial structure and calculating distancesΒΆ

The `space` module contains classes for specifying the locations of neurons in space and for calculating the distances between them.

Neuron positions can be defined either manually, using the `positions` attribute of a `Population` or using a `Structure` instance which is passed to the `Population` constructor.

A number of different structures are available in `space`. It is simple to define your own `Structure` sub-class if you need something that is not already provided.

The simplest structure is a grid, whether 1D, 2D or 3D, e.g.:

``` >>> from pyNN.space import *
>>> line = Line(dx=100.0, x0=0.0, y=200.0, z=500.0)
>>> line.generate_positions(7)
array([[   0.,  100.,  200.,  300.,  400.,  500.,  600.],
[ 200.,  200.,  200.,  200.,  200.,  200.,  200.],
[ 500.,  500.,  500.,  500.,  500.,  500.,  500.]])
>>> grid = Grid2D(aspect_ratio=3, dx=10.0, dy=25.0, z=-3.0)
>>> grid.generate_positions(3)
array([[  0.,  10.,  20.],
[  0.,   0.,   0.],
[ -3.,  -3.,  -3.]])
>>> grid.generate_positions(12)
array([[  0.,   0.,  10.,  10.,  20.,  20.,  30.,  30.,  40.,  40.,  50.,  50.],
[  0.,  25.,   0.,  25.,   0.,  25.,   0.,  25.,   0.,  25.,   0.,  25.],
[ -3.,  -3.,  -3.,  -3.,  -3.,  -3.,  -3.,  -3.,  -3.,  -3.,  -3.,  -3.]])
```

Here we have specified an x:y ratio of 3, so if we ask the grid to generate positions for 3 neurons, we get a 3x1 grid, 12 neurons a 6x2 grid, 27 neurons 9x3, etc.

BY default, grid positions are filled sequentially, iterating first over the z dimension, then y, then x, but we can also fill the grid randomly:

```>>> rgrid = Grid2D(aspect_ratio=1, dx=10.0, dy=10.0, fill_order='random', rng=NumpyRNG(seed=13886))
>>> rgrid.generate_positions(9)
array([[ 10.,  10.,  10.,   0.,   0.,   0.,  20.,  20.,  20.],
[  0.,  10.,  20.,  10.,  20.,   0.,   0.,  10.,  20.],
[  0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.]])
```

The `space` module also provides the `RandomStructure` class, which distributes neurons randomly and uniformly within a given volume:

``` >>> glomerulus = RandomStructure(boundary=Sphere(radius=200.0), rng=NumpyRNG(seed=34534))
>>> glomerulus.generate_positions(5)
array([[ -19.78455022,   33.21412264,  -79.4314059 ,  143.39033263,  -63.18242977],
[  56.17281502,  -23.15159309,  131.89071845,  -73.73583484,  -8.86422999],
[ -78.88348228,   -3.97408513,  -95.03056844,   45.13969087,  -111.67070498]])
```

The volume classes currently available are `Sphere` and `Cuboid`.

Defining your own `Structure` classes is straightforward, just inherit from `BaseStructure` and implement a `generate_positions()` method:

```class MyStructure(BaseStructure):
parameter_names = ("spam", "eggs")

def __init__(self, spam=3, eggs=1):
...

def generate_positions(self, n):
...
# must return a 3xn numpy array
```

To define your own `Shape` class for use with `RandomStructure`, subclass `Shape` and implement a `sample()` method:

```class Tetrahedron(Shape):

def __init__(self, side_length):
...

def sample(self, n, rng):
...
# return a nx3 numpy array.
```

Note

rotation of structures is currently missing, but is planned for a future release.