From 7f52f594ac4d24b2210cc3e2bee4adf0f4c3c913 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Wed, 8 May 2024 10:48:05 -0500 Subject: [PATCH 1/6] Merge branch 'dev_separated_create_probe' into nei_nienborg --- element_array_ephys/probe.py | 103 ++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/element_array_ephys/probe.py b/element_array_ephys/probe.py index d1fa59c6..0a7016a2 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"]], - ) + log.warning( + "Class method `ProbeType.create_neuropixels_probe` is deprecated. Use `create_neuropixels_probe` instead.", ) - electrode_layouts = probe_geometry.build_npx_probe( - **{**probe_params, **probe_type} - ) - 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,51 @@ 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 + 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} + ) + with ProbeType.connection.transaction: + ProbeType.insert1(probe_type) + ProbeType.Electrode.insert(electrode_layouts) From 46679e605e116e13a2cc373148ea24127a2fc447 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 15 Aug 2024 17:22:28 -0500 Subject: [PATCH 2/6] rearrange: explicitly call `probe.create_neuropixels_probe_types()` to create entries in ProbeType --- tests/tutorial_pipeline.py | 2 ++ 1 file changed, 2 insertions(+) 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__ = [""] From aaec76339954b17a2dbef8aeaa84e92e64bdad35 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 15 Aug 2024 18:03:01 -0500 Subject: [PATCH 3/6] fix(probe): better handling of different Neuropixels probe types --- element_array_ephys/probe.py | 14 ++++++-------- element_array_ephys/readers/probe_geometry.py | 8 ++++++++ element_array_ephys/readers/spikeglx.py | 19 +++++++++++-------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/element_array_ephys/probe.py b/element_array_ephys/probe.py index 0a7016a2..5494b274 100644 --- a/element_array_ephys/probe.py +++ b/element_array_ephys/probe.py @@ -155,21 +155,19 @@ def create_neuropixels_probe(probe_type: str = "neuropixels 1.0 - 3A"): 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"] + 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_type = {"probe_type": probe_type} probe_params = dict( zip( probe_geometry.geom_param_names, - npx_probes_config[probe_type["probe_type"]], + npx_probes_config[probe_type], ) ) electrode_layouts = probe_geometry.build_npx_probe( - **{**probe_params, **probe_type} + **{**probe_params, "probe_type": probe_type} ) with ProbeType.connection.transaction: ProbeType.insert1(probe_type) 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..01f1046d 100644 --- a/element_array_ephys/readers/spikeglx.py +++ b/element_array_ephys/readers/spikeglx.py @@ -262,14 +262,19 @@ 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: + probe_model = self.meta.get("imDatPrb_type") + if probe_model is None: + 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 probe_model == 1100: + elif "typeImEnabled" in self.meta and self.probe_PN == "NP1010": + self.probe_model = "neuropixels 1.0" + else: + self.probe_model = self.probe_PN + if probe_model == 1100: self.probe_model = "neuropixels UHD" elif probe_model == 21: self.probe_model = "neuropixels 2.0 - SS" @@ -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 = ( From e8870b94cf6dc09b251e268c4102fb4b82149da2 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 15 Aug 2024 18:05:59 -0500 Subject: [PATCH 4/6] fix: minor bugfix --- element_array_ephys/probe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/element_array_ephys/probe.py b/element_array_ephys/probe.py index 5494b274..97f8aa19 100644 --- a/element_array_ephys/probe.py +++ b/element_array_ephys/probe.py @@ -170,5 +170,5 @@ def create_neuropixels_probe(probe_type: str = "neuropixels 1.0 - 3A"): **{**probe_params, "probe_type": probe_type} ) with ProbeType.connection.transaction: - ProbeType.insert1(probe_type) + ProbeType.insert1({"probe_type": probe_type}) ProbeType.Electrode.insert(electrode_layouts) From 6764f8c1adb9a80569f75233028e551cf58d8917 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Thu, 15 Aug 2024 18:24:10 -0500 Subject: [PATCH 5/6] fix(spikeglx): minor bugfix --- element_array_ephys/readers/spikeglx.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/element_array_ephys/readers/spikeglx.py b/element_array_ephys/readers/spikeglx.py index 01f1046d..3e5f7a36 100644 --- a/element_array_ephys/readers/spikeglx.py +++ b/element_array_ephys/readers/spikeglx.py @@ -266,15 +266,15 @@ def __init__(self, meta_filepath): 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") - if probe_model is None: + probe_model = self.meta.get("imDatPrb_type", 1) + 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 and self.probe_PN == "NP1010": self.probe_model = "neuropixels 1.0" else: self.probe_model = self.probe_PN - if probe_model == 1100: + elif probe_model == 1100: self.probe_model = "neuropixels UHD" elif probe_model == 21: self.probe_model = "neuropixels 2.0 - SS" From f75439241693f82e75927d23637a4ae471dd6377 Mon Sep 17 00:00:00 2001 From: Thinh Nguyen Date: Fri, 16 Aug 2024 10:36:47 -0500 Subject: [PATCH 6/6] update: version and changelog --- CHANGELOG.md | 6 ++++++ element_array_ephys/version.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) 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/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"