diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e45e427..d2e48eaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/element_array_ephys/probe.py b/element_array_ephys/probe.py index d1fa59c6..97f8aa19 100644 --- a/element_array_ephys/probe.py +++ b/element_array_ephys/probe.py @@ -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() @@ -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): @@ -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 @@ -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) diff --git a/element_array_ephys/readers/probe_geometry.py b/element_array_ephys/readers/probe_geometry.py index b0f27765..b6fbc09e 100644 --- a/element_array_ephys/readers/probe_geometry.py +++ b/element_array_ephys/readers/probe_geometry.py @@ -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, diff --git a/element_array_ephys/readers/spikeglx.py b/element_array_ephys/readers/spikeglx.py index 6d9ba4f6..3e5f7a36 100644 --- a/element_array_ephys/readers/spikeglx.py +++ b/element_array_ephys/readers/spikeglx.py @@ -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: @@ -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 = ( diff --git a/element_array_ephys/version.py b/element_array_ephys/version.py index 148bac24..6b5406e8 100644 --- a/element_array_ephys/version.py +++ b/element_array_ephys/version.py @@ -1,3 +1,3 @@ """Package metadata.""" -__version__ = "0.3.4" +__version__ = "0.3.5" diff --git a/tests/tutorial_pipeline.py b/tests/tutorial_pipeline.py index fc92280b..74b27ddc 100644 --- a/tests/tutorial_pipeline.py +++ b/tests/tutorial_pipeline.py @@ -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__ = [""]