Skip to content

Commit 71d9a42

Browse files
authored
Merge pull request #199 from ttngu207/main
fix(probe): better handling of different Neuropixels probe types and SpikeGLX meta loader
2 parents 27c56ea + f754392 commit 71d9a42

File tree

6 files changed

+77
-57
lines changed

6 files changed

+77
-57
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and
44
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention.
55

6+
## [0.3.5] - 2024-08-16
7+
8+
+ Fix - Improve `spikeglx` loader in extracting neuropixels probe type from the meta file
9+
+ Update - Explicit call to `probe.create_neuropixels_probe_types()` to create entries in ProbeType
10+
11+
612
## [0.3.4] - 2024-03-22
713

814
+ Add - pytest

element_array_ephys/probe.py

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
"""
2-
Neuropixels Probes
3-
"""
4-
51
import datajoint as dj
62

73
from .readers import probe_geometry
84
from .readers.probe_geometry import build_electrode_layouts
95

6+
log = dj.logger
7+
108
schema = dj.schema()
119

1210

@@ -33,20 +31,6 @@ def activate(
3331
schema_name, create_schema=create_schema, create_tables=create_tables
3432
)
3533

36-
# Add neuropixels probes
37-
for probe_type in (
38-
"neuropixels 1.0 - 3A",
39-
"neuropixels 1.0 - 3B",
40-
"neuropixels UHD",
41-
"neuropixels 2.0 - SS",
42-
"neuropixels 2.0 - MS",
43-
):
44-
if not (ProbeType & {"probe_type": probe_type}):
45-
try:
46-
ProbeType.create_neuropixels_probe(probe_type)
47-
except dj.errors.DataJointError as e:
48-
print(f"Unable to create probe-type: {probe_type}\n{str(e)}")
49-
5034

5135
@schema
5236
class ProbeType(dj.Lookup):
@@ -87,39 +71,10 @@ class Electrode(dj.Part):
8771

8872
@staticmethod
8973
def create_neuropixels_probe(probe_type: str = "neuropixels 1.0 - 3A"):
90-
"""
91-
Create `ProbeType` and `Electrode` for neuropixels probes:
92-
+ neuropixels 1.0 - 3A
93-
+ neuropixels 1.0 - 3B
94-
+ neuropixels UHD
95-
+ neuropixels 2.0 - SS
96-
+ neuropixels 2.0 - MS
97-
98-
For electrode location, the (0, 0) is the
99-
bottom left corner of the probe (ignore the tip portion)
100-
Electrode numbering is 0-indexing
101-
"""
102-
103-
npx_probes_config = probe_geometry.M
104-
npx_probes_config["neuropixels 1.0 - 3A"] = npx_probes_config["3A"]
105-
npx_probes_config["neuropixels 1.0 - 3B"] = npx_probes_config["NP1010"]
106-
npx_probes_config["neuropixels UHD"] = npx_probes_config["NP1100"]
107-
npx_probes_config["neuropixels 2.0 - SS"] = npx_probes_config["NP2000"]
108-
npx_probes_config["neuropixels 2.0 - MS"] = npx_probes_config["NP2010"]
109-
110-
probe_type = {"probe_type": probe_type}
111-
probe_params = dict(
112-
zip(
113-
probe_geometry.geom_param_names,
114-
npx_probes_config[probe_type["probe_type"]],
115-
)
116-
)
117-
electrode_layouts = probe_geometry.build_npx_probe(
118-
**{**probe_params, **probe_type}
74+
log.warning(
75+
"Class method `ProbeType.create_neuropixels_probe` is deprecated. Use `create_neuropixels_probe` instead.",
11976
)
120-
with ProbeType.connection.transaction:
121-
ProbeType.insert1(probe_type, skip_duplicates=True)
122-
ProbeType.Electrode.insert(electrode_layouts, skip_duplicates=True)
77+
return create_neuropixels_probe(probe_type)
12378

12479

12580
@schema
@@ -171,3 +126,49 @@ class Electrode(dj.Part):
171126
-> master
172127
-> ProbeType.Electrode
173128
"""
129+
130+
131+
def create_neuropixels_probe_types():
132+
# Add neuropixels probes
133+
for probe_type in (
134+
"neuropixels 1.0 - 3A",
135+
"neuropixels 1.0 - 3B",
136+
"neuropixels UHD",
137+
"neuropixels 2.0 - SS",
138+
"neuropixels 2.0 - MS",
139+
):
140+
if not (ProbeType & {"probe_type": probe_type}):
141+
create_neuropixels_probe(probe_type)
142+
143+
144+
def create_neuropixels_probe(probe_type: str = "neuropixels 1.0 - 3A"):
145+
"""
146+
Create `ProbeType` and `Electrode` for neuropixels probes:
147+
+ neuropixels 1.0 - 3A
148+
+ neuropixels 1.0 - 3B
149+
+ neuropixels UHD
150+
+ neuropixels 2.0 - SS
151+
+ neuropixels 2.0 - MS
152+
153+
For electrode location, the (0, 0) is the
154+
bottom left corner of the probe (ignore the tip portion)
155+
Electrode numbering is 0-indexing
156+
"""
157+
npx_probes_config = probe_geometry.M
158+
if probe_type not in npx_probes_config:
159+
raise ValueError(
160+
f"Probe type {probe_type} not found in probe_geometry configuration. Not a Neuropixels probe?"
161+
)
162+
163+
probe_params = dict(
164+
zip(
165+
probe_geometry.geom_param_names,
166+
npx_probes_config[probe_type],
167+
)
168+
)
169+
electrode_layouts = probe_geometry.build_npx_probe(
170+
**{**probe_params, "probe_type": probe_type}
171+
)
172+
with ProbeType.connection.transaction:
173+
ProbeType.insert1({"probe_type": probe_type})
174+
ProbeType.Electrode.insert(electrode_layouts)

element_array_ephys/readers/probe_geometry.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@
101101
]
102102
)
103103

104+
# additional alias to maintain compatibility with previous naming in the pipeline
105+
M["neuropixels 1.0 - 3A"] = M["3A"]
106+
M["neuropixels 1.0 - 3B"] = M["NP1010"]
107+
M["neuropixels 1.0"] = M["NP1010"]
108+
M["neuropixels UHD"] = M["NP1100"]
109+
M["neuropixels 2.0 - SS"] = M["NP2000"]
110+
M["neuropixels 2.0 - MS"] = M["NP2010"]
111+
104112

105113
def build_npx_probe(
106114
nShank: int,

element_array_ephys/readers/spikeglx.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -262,13 +262,18 @@ def __init__(self, meta_filepath):
262262
self.fname = meta_filepath
263263
self.meta = _read_meta(meta_filepath)
264264

265+
# Get probe part number
266+
self.probe_PN = self.meta.get("imDatPrb_pn", "3A")
267+
265268
# Infer npx probe model (e.g. 1.0 (3A, 3B) or 2.0)
266269
probe_model = self.meta.get("imDatPrb_type", 1)
267-
if probe_model <= 1:
268-
if "typeEnabled" in self.meta:
270+
if probe_model < 1:
271+
if "typeEnabled" in self.meta and self.probe_PN == "3A":
269272
self.probe_model = "neuropixels 1.0 - 3A"
270-
elif "typeImEnabled" in self.meta:
271-
self.probe_model = "neuropixels 1.0 - 3B"
273+
elif "typeImEnabled" in self.meta and self.probe_PN == "NP1010":
274+
self.probe_model = "neuropixels 1.0"
275+
else:
276+
self.probe_model = self.probe_PN
272277
elif probe_model == 1100:
273278
self.probe_model = "neuropixels UHD"
274279
elif probe_model == 21:
@@ -293,8 +298,6 @@ def __init__(self, meta_filepath):
293298
"Probe Serial Number not found in"
294299
' either "imProbeSN" or "imDatPrb_sn"'
295300
)
296-
# Get probe part number
297-
self.probe_PN = self.meta.get("imDatPrb_pn", "3A")
298301

299302
# Parse channel info
300303
self.chanmap = (

element_array_ephys/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Package metadata."""
22

3-
__version__ = "0.3.4"
3+
__version__ = "0.3.5"

tests/tutorial_pipeline.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,7 @@ def get_session_directory(session_key):
6464

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

67+
probe.create_neuropixels_probe_types()
68+
6769

6870
__all__ = [""]

0 commit comments

Comments
 (0)