Creating a grid of electrodes

This example shows how to use ElectrodeGrid.

Most current electrode arrays arrange their electrodes in a 2D grid.

Creating a rectangular grid

To create a rectangular grid, we need to specify:

  • The shape of the grid, passed as a tuple containing the desired number of rows and columns
  • The electrode-to-electrode spacing in microns
  • The (x, y) location of the center of the array
  • The rotation angle rot of the grid in degrees, where positive angles rotate all electrodes in the array in a counter-clockwise fashion on the retinal surface.

We can also specify:

  • A naming convention names for the rows and columns in the grid. For example, to label rows alphabetically and columns numerically, we would pass a tuple ('A', '1'). To label both alphabetically, we would pass ('A', 'A').
  • An electrode type etype, which must be a subclass of Electrode. By default, PointSource is chosen.
  • Any additional parameters that should be passed to the Electrode constructor, such as a radius r for DiskElectrode.

Let’s say we want to create a 2x3 rectangular grid of PointSource objects, each electrode spaced 500 microns apart, and the whole grid should be centered over the fovea:

from pulse2percept.implants import ElectrodeGrid

grid = ElectrodeGrid((2, 3), 500)

We can access individual electrodes by indexing into grid:

The first electrode:

grid[0]

Out:

PointSource(x=-500.0, y=-250.0, z=0.0)

The first electrode by name:

grid['A1']

Out:

PointSource(x=-500.0, y=-250.0, z=0.0)

Accessing the x-coordinate of the first electrode:

grid[0].x

Out:

-500.0

Showing all electrodes:

grid[:]

Out:

[PointSource(x=-500.0, y=-250.0, z=0.0), PointSource(x=0.0, y=-250.0, z=0.0), PointSource(x=500.0, y=-250.0, z=0.0), PointSource(x=-500.0, y=250.0, z=0.0), PointSource(x=0.0, y=250.0, z=0.0), PointSource(x=500.0, y=250.0, z=0.0)]

We can iterate over all electrodes as if we were dealing with a dictionary:

for name, electrode in grid.items():
    print(name, electrode)

Out:

A1 PointSource(x=-500.0, y=-250.0, z=0.0)
A2 PointSource(x=0.0, y=-250.0, z=0.0)
A3 PointSource(x=500.0, y=-250.0, z=0.0)
B1 PointSource(x=-500.0, y=250.0, z=0.0)
B2 PointSource(x=0.0, y=250.0, z=0.0)
B3 PointSource(x=500.0, y=250.0, z=0.0)

To make a grid of DiskElectrode objects, we need to explicitly specify the electrode type (etype) and the radius to use (r):

from pulse2percept.implants import DiskElectrode

# 11x13 grid, 100-um disk electrodes spaced 500um apart:
disk_grid = ElectrodeGrid((11, 13), 500, etype=DiskElectrode, r=100)

disk_grid[:]

Out:

[DiskElectrode(r=100.0, x=-3000.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=-2500.0, z=0.0), DiskElectrode(r=100.0, x=-3000.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=-2000.0, z=0.0), DiskElectrode(r=100.0, x=-3000.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=-1500.0, z=0.0), DiskElectrode(r=100.0, x=-3000.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=-1000.0, z=0.0), DiskElectrode(r=100.0, x=-3000.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=-500.0, z=0.0), DiskElectrode(r=100.0, x=-3000.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=0.0, z=0.0), DiskElectrode(r=100.0, x=-3000.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=500.0, z=0.0), DiskElectrode(r=100.0, x=-3000.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=1000.0, z=0.0), DiskElectrode(r=100.0, x=-3000.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=1500.0, z=0.0), DiskElectrode(r=100.0, x=-3000.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=2000.0, z=0.0), DiskElectrode(r=100.0, x=-3000.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=-2500.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=-2000.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=-1500.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=-1000.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=-500.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=0.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=500.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=1000.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=1500.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=2000.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=2500.0, y=2500.0, z=0.0), DiskElectrode(r=100.0, x=3000.0, y=2500.0, z=0.0)]

Note

You can also specify a list of radii, one value for each electrode in the grid.

We can visualize the grid by using its plot method:

plot electrode grid

Out:

<AxesSubplot:xlabel='x (microns)', ylabel='y (microns)'>

Creating a hexagonal grid

To create a hexagonal grid instead, all we need to do is change the grid type from ‘rect’ (default) to ‘hex’:

hex_grid = ElectrodeGrid((11, 13), 500, type='hex', etype=DiskElectrode, r=100)

hex_grid.plot()
plot electrode grid

Out:

<AxesSubplot:xlabel='x (microns)', ylabel='y (microns)'>

The following example centers the grid on (x,y) = (-600um, 200 um), z=150um away from the retinal surface, and rotates it clockwise by 45 degrees (note the minus sign):

from numpy import pi
offset_grid = ElectrodeGrid((11, 13), 500, type='hex', x=-600, y=200, z=150,
                            rot=-45, etype=DiskElectrode, r=100)

Note

Clockwise/counter-clockwise rotations refer to rotations on the retinal surface (that is, as if seen on a fundus photograph).

We can also plot the grid on top of a map of retinal nerve fiber bundles:

from pulse2percept.models import AxonMapModel

AxonMapModel().plot()
offset_grid.plot()
plot electrode grid

Out:

<AxesSubplot:xlabel='x (microns)', ylabel='y (microns)'>

Total running time of the script: ( 0 minutes 1.120 seconds)

Gallery generated by Sphinx-Gallery