Skip to content

Commit

Permalink
Merge pull request #199 from ttngu207/main
Browse files Browse the repository at this point in the history
fix(probe): better handling of different Neuropixels probe types and SpikeGLX meta loader
  • Loading branch information
kushalbakshi authored Aug 19, 2024
2 parents 27c56ea + f754392 commit 71d9a42
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 57 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention.

## [0.3.5] - 2024-08-16

+ Fix - Improve `spikeglx` loader in extracting neuropixels probe type from the meta file
+ Update - Explicit call to `probe.create_neuropixels_probe_types()` to create entries in ProbeType


## [0.3.4] - 2024-03-22

+ Add - pytest
Expand Down
101 changes: 51 additions & 50 deletions element_array_ephys/probe.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
"""
Neuropixels Probes
"""

import datajoint as dj

from .readers import probe_geometry
from .readers.probe_geometry import build_electrode_layouts

log = dj.logger

schema = dj.schema()


Expand All @@ -33,20 +31,6 @@ def activate(
schema_name, create_schema=create_schema, create_tables=create_tables
)

# Add neuropixels probes
for probe_type in (
"neuropixels 1.0 - 3A",
"neuropixels 1.0 - 3B",
"neuropixels UHD",
"neuropixels 2.0 - SS",
"neuropixels 2.0 - MS",
):
if not (ProbeType & {"probe_type": probe_type}):
try:
ProbeType.create_neuropixels_probe(probe_type)
except dj.errors.DataJointError as e:
print(f"Unable to create probe-type: {probe_type}\n{str(e)}")


@schema
class ProbeType(dj.Lookup):
Expand Down Expand Up @@ -87,39 +71,10 @@ class Electrode(dj.Part):

@staticmethod
def create_neuropixels_probe(probe_type: str = "neuropixels 1.0 - 3A"):
"""
Create `ProbeType` and `Electrode` for neuropixels probes:
+ neuropixels 1.0 - 3A
+ neuropixels 1.0 - 3B
+ neuropixels UHD
+ neuropixels 2.0 - SS
+ neuropixels 2.0 - MS
For electrode location, the (0, 0) is the
bottom left corner of the probe (ignore the tip portion)
Electrode numbering is 0-indexing
"""

npx_probes_config = probe_geometry.M
npx_probes_config["neuropixels 1.0 - 3A"] = npx_probes_config["3A"]
npx_probes_config["neuropixels 1.0 - 3B"] = npx_probes_config["NP1010"]
npx_probes_config["neuropixels UHD"] = npx_probes_config["NP1100"]
npx_probes_config["neuropixels 2.0 - SS"] = npx_probes_config["NP2000"]
npx_probes_config["neuropixels 2.0 - MS"] = npx_probes_config["NP2010"]

probe_type = {"probe_type": probe_type}
probe_params = dict(
zip(
probe_geometry.geom_param_names,
npx_probes_config[probe_type["probe_type"]],
)
)
electrode_layouts = probe_geometry.build_npx_probe(
**{**probe_params, **probe_type}
log.warning(
"Class method `ProbeType.create_neuropixels_probe` is deprecated. Use `create_neuropixels_probe` instead.",
)
with ProbeType.connection.transaction:
ProbeType.insert1(probe_type, skip_duplicates=True)
ProbeType.Electrode.insert(electrode_layouts, skip_duplicates=True)
return create_neuropixels_probe(probe_type)


@schema
Expand Down Expand Up @@ -171,3 +126,49 @@ class Electrode(dj.Part):
-> master
-> ProbeType.Electrode
"""


def create_neuropixels_probe_types():
# Add neuropixels probes
for probe_type in (
"neuropixels 1.0 - 3A",
"neuropixels 1.0 - 3B",
"neuropixels UHD",
"neuropixels 2.0 - SS",
"neuropixels 2.0 - MS",
):
if not (ProbeType & {"probe_type": probe_type}):
create_neuropixels_probe(probe_type)


def create_neuropixels_probe(probe_type: str = "neuropixels 1.0 - 3A"):
"""
Create `ProbeType` and `Electrode` for neuropixels probes:
+ neuropixels 1.0 - 3A
+ neuropixels 1.0 - 3B
+ neuropixels UHD
+ neuropixels 2.0 - SS
+ neuropixels 2.0 - MS
For electrode location, the (0, 0) is the
bottom left corner of the probe (ignore the tip portion)
Electrode numbering is 0-indexing
"""
npx_probes_config = probe_geometry.M
if probe_type not in npx_probes_config:
raise ValueError(
f"Probe type {probe_type} not found in probe_geometry configuration. Not a Neuropixels probe?"
)

probe_params = dict(
zip(
probe_geometry.geom_param_names,
npx_probes_config[probe_type],
)
)
electrode_layouts = probe_geometry.build_npx_probe(
**{**probe_params, "probe_type": probe_type}
)
with ProbeType.connection.transaction:
ProbeType.insert1({"probe_type": probe_type})
ProbeType.Electrode.insert(electrode_layouts)
8 changes: 8 additions & 0 deletions element_array_ephys/readers/probe_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@
]
)

# additional alias to maintain compatibility with previous naming in the pipeline
M["neuropixels 1.0 - 3A"] = M["3A"]
M["neuropixels 1.0 - 3B"] = M["NP1010"]
M["neuropixels 1.0"] = M["NP1010"]
M["neuropixels UHD"] = M["NP1100"]
M["neuropixels 2.0 - SS"] = M["NP2000"]
M["neuropixels 2.0 - MS"] = M["NP2010"]


def build_npx_probe(
nShank: int,
Expand Down
15 changes: 9 additions & 6 deletions element_array_ephys/readers/spikeglx.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,18 @@ def __init__(self, meta_filepath):
self.fname = meta_filepath
self.meta = _read_meta(meta_filepath)

# Get probe part number
self.probe_PN = self.meta.get("imDatPrb_pn", "3A")

# Infer npx probe model (e.g. 1.0 (3A, 3B) or 2.0)
probe_model = self.meta.get("imDatPrb_type", 1)
if probe_model <= 1:
if "typeEnabled" in self.meta:
if probe_model < 1:
if "typeEnabled" in self.meta and self.probe_PN == "3A":
self.probe_model = "neuropixels 1.0 - 3A"
elif "typeImEnabled" in self.meta:
self.probe_model = "neuropixels 1.0 - 3B"
elif "typeImEnabled" in self.meta and self.probe_PN == "NP1010":
self.probe_model = "neuropixels 1.0"
else:
self.probe_model = self.probe_PN
elif probe_model == 1100:
self.probe_model = "neuropixels UHD"
elif probe_model == 21:
Expand All @@ -293,8 +298,6 @@ def __init__(self, meta_filepath):
"Probe Serial Number not found in"
' either "imProbeSN" or "imDatPrb_sn"'
)
# Get probe part number
self.probe_PN = self.meta.get("imDatPrb_pn", "3A")

# Parse channel info
self.chanmap = (
Expand Down
2 changes: 1 addition & 1 deletion element_array_ephys/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Package metadata."""

__version__ = "0.3.4"
__version__ = "0.3.5"
2 changes: 2 additions & 0 deletions tests/tutorial_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,7 @@ def get_session_directory(session_key):

ephys.activate(db_prefix + "ephys", db_prefix + "probe", linking_module=__name__)

probe.create_neuropixels_probe_types()


__all__ = [""]

0 comments on commit 71d9a42

Please sign in to comment.