Skip to content

Commit

Permalink
Fix default electrical series naming for SpikeGLXConverter (#957)
Browse files Browse the repository at this point in the history
Co-authored-by: CodyCBakerPhD <[email protected]>
  • Loading branch information
CodyCBakerPhD and CodyCBakerPhD authored Aug 5, 2024
1 parent 8912133 commit fa63645
Show file tree
Hide file tree
Showing 12 changed files with 249 additions and 41 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Upcoming

### Improvements
### Bug fixes
* Fixed the default naming of multiple electrical series in the `SpikeGLXConverterPipe`. [PR #957](https://github.com/catalystneuro/neuroconv/pull/957)

### Improvements
* The `OpenEphysBinaryRecordingInterface` now uses `lxml` for extracting the session start time from the settings.xml file and does not depend on `pyopenephys` anymore. [PR #971](https://github.com/catalystneuro/neuroconv/pull/971)
* Swap the majority of package setup and build steps to `pyproject.toml` instead of `setup.py`. [PR #955](https://github.com/catalystneuro/neuroconv/pull/955)
* The `DeeplabcutInterface` now skips inferring timestamps from movie when timestamps are specified, running faster. [PR #967](https://github.com/catalystneuro/neuroconv/pull/967)
Expand Down
11 changes: 7 additions & 4 deletions src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglx_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ def add_recording_extractor_properties(recording_extractor) -> None:
probe = recording_extractor.get_probe()
channel_ids = recording_extractor.get_channel_ids()

# Should follow pattern 'Imec0', 'Imec1', etc.
probe_name = recording_extractor.stream_id[:5].capitalize()

if probe.get_shank_count() > 1:
group_name = [contact_id.split("e")[0] for contact_id in probe.contact_ids]
shank_electrode_number = [int(contact_id.split("e")[1]) for contact_id in probe.contact_ids]
group_name = [f"{probe_name}{contact_id.split('e')[0]}" for contact_id in probe.contact_ids]
else:
shank_electrode_number = recording_extractor.ids_to_indices(channel_ids)
group_name = ["s0"] * len(channel_ids)
group_name = [f"{probe_name}Shank0"] * len(channel_ids)

recording_extractor.set_property(key="shank_electrode_number", ids=channel_ids, values=shank_electrode_number)
recording_extractor.set_property(key="group_name", ids=channel_ids, values=group_name)
Expand Down Expand Up @@ -113,9 +116,9 @@ def get_device_metadata(meta) -> dict:
if "imDatBsc_pn" in meta:
metadata_dict.update(connected_base_station_part_number=meta["imDatBsc_pn"])

description_string = "no description"
description_string = "A Neuropixel probe of unknown subtype."
if metadata_dict:
description_string = json.dumps(metadata_dict)
device_metadata = dict(name="Neuropixel-Imec", description=description_string, manufacturer="Imec")
device_metadata = dict(name="NeuropixelImec", description=description_string, manufacturer="Imec")

return device_metadata
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,16 @@ def __init__(
file_path = (
folder_path / f"{folder_path.stem}_{probe_name}" / f"{folder_path.stem}_t0.{probe_name}.ap.bin"
)
interface = SpikeGLXRecordingInterface(file_path=file_path)
if "lf" in stream:
es_key = f"ElectricalSeriesAP{probe_name.capitalize()}"
interface = SpikeGLXRecordingInterface(file_path=file_path, es_key=es_key)
elif "lf" in stream:
probe_name = stream[:5]
file_path = (
folder_path / f"{folder_path.stem}_{probe_name}" / f"{folder_path.stem}_t0.{probe_name}.lf.bin"
)
interface = SpikeGLXRecordingInterface(file_path=file_path)
if "nidq" in stream:
es_key = f"ElectricalSeriesLF{probe_name.capitalize()}"
interface = SpikeGLXRecordingInterface(file_path=file_path, es_key=es_key)
elif "nidq" in stream:
file_path = folder_path / f"{folder_path.stem}_t0.nidq.bin"
interface = SpikeGLXNIDQInterface(file_path=file_path)
data_interfaces.update({str(stream): interface}) # Without str() casting, is a numpy string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,16 @@ def get_metadata(self) -> dict:
# Device metadata
device = get_device_metadata(self.meta)

# Should follow pattern 'Imec0', 'Imec1', etc.
probe_name = self.stream_id[:5].capitalize()
device["name"] = f"Neuropixel{probe_name}"

# Add groups metadata
metadata["Ecephys"]["Device"] = [device]
electrode_groups = [
dict(
name=group_name,
description=f"a group representing shank {group_name}",
description=f"A group representing probe/shank '{group_name}'.",
location="unknown",
device=device["name"],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import numpy as np

from .spikeglx_utils import get_device_metadata, get_session_start_time
from .spikeglx_utils import get_session_start_time
from ..baserecordingextractorinterface import BaseRecordingExtractorInterface
from ....tools.signal_processing import get_rising_frames_from_ttl
from ....utils import FilePathType, get_schema_from_method_signature
Expand Down Expand Up @@ -72,7 +72,11 @@ def get_metadata(self) -> dict:
metadata["NWBFile"]["session_start_time"] = session_start_time

# Device metadata
device = get_device_metadata(self.meta)
device = dict(
name="NIDQBoard",
description="A NIDQ board used in conjunction with SpikeGLX.",
manufacturer="National Instruments",
)

# Add groups metadata
metadata["Ecephys"]["Device"] = [device]
Expand Down
18 changes: 14 additions & 4 deletions tests/test_ecephys/test_mock_nidq_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,17 @@ def test_mock_metadata():
expected_ecephys_metadata = {
"Ecephys": {
"Device": [
{"description": "no description", "manufacturer": "Imec", "name": "Neuropixel-Imec"},
{
"name": "NIDQBoard",
"description": "A NIDQ board used in conjunction with SpikeGLX.",
"manufacturer": "National Instruments",
},
],
"ElectrodeGroup": [
{
"name": "NIDQChannelGroup",
"description": "A group representing the NIDQ channels.",
"device": "Neuropixel-Imec",
"device": "NIDQBoard",
"location": "unknown",
},
],
Expand All @@ -68,7 +72,7 @@ def test_mock_metadata():
},
}
}
print(metadata["Ecephys"])

assert metadata["Ecephys"] == expected_ecephys_metadata["Ecephys"]

expected_start_time = datetime(2020, 11, 3, 10, 35, 10)
Expand All @@ -88,7 +92,13 @@ def test_mock_run_conversion(tmpdir: pathlib.Path):
with NWBHDF5IO(path=nwbfile_path, mode="r") as io:
nwbfile = io.read()

assert "Neuropixel-Imec" in nwbfile.devices
assert "NIDQBoard" in nwbfile.devices
assert len(nwbfile.devices) == 1

assert "NIDQChannelGroup" in nwbfile.electrode_groups
assert len(nwbfile.electrode_groups) == 1

assert list(nwbfile.electrodes.id[:]) == [0, 1, 2, 3, 4, 5, 6, 7]

assert "ElectricalSeriesNIDQ" in nwbfile.acquisition
assert len(nwbfile.acquisition) == 1

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"NWBFile": {
"session_description": ""
},
"Ecephys": {
"Device": [
{
"name": "NeuropixelImec0",
"description": "{\"probe_type\": \"0\", \"probe_type_description\": \"NP1.0\", \"flex_part_number\": \"NP2_FLEX_0\", \"connected_base_station_part_number\": \"NP2_QBSC_00\"}",
"manufacturer": "Imec"
},
{
"name": "NeuropixelImec1",
"description": "{\"probe_type\": \"0\", \"probe_type_description\": \"NP1.0\", \"flex_part_number\": \"NP2_FLEX_0\", \"connected_base_station_part_number\": \"NP2_QBSC_00\"}",
"manufacturer": "Imec"
}
],
"ElectrodeGroup": [
{
"name": "Imec0Shank0",
"description": "A group representing probe/shank 'Imec0Shank0'.",
"location": "unknown",
"device": "NeuropixelImec0"
},
{
"name": "Imec1Shank0",
"description": "A group representing probe/shank 'Imec1Shank0'.",
"location": "unknown",
"device": "NeuropixelImec1"
}
],
"ElectricalSeriesAPImec0": {
"name": "ElectricalSeriesAPImec0",
"description": "Acquisition traces for the ElectricalSeriesAPImec0."
},
"Electrodes": [
{
"name": "shank_electrode_number",
"description": "0-indexed channel within a shank."
},
{
"name": "group_name",
"description": "Name of the ElectrodeGroup this electrode is a part of."
},
{
"name": "contact_shapes",
"description": "The shape of the electrode"
}
],
"ElectricalSeriesAPImec1": {
"name": "ElectricalSeriesAPImec1",
"description": "Acquisition traces for the ElectricalSeriesAPImec1."
},
"ElectricalSeriesLFImec0": {
"name": "ElectricalSeriesLFImec0",
"description": "Acquisition traces for the ElectricalSeriesLFImec0."
},
"ElectricalSeriesLFImec1": {
"name": "ElectricalSeriesLFImec1",
"description": "Acquisition traces for the ElectricalSeriesLFImec1."
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"NWBFile": {
"session_description": ""
},
"Ecephys": {
"Device": [
{
"name": "NeuropixelImec0",
"description": "{\"probe_type\": \"0\", \"probe_type_description\": \"NP1.0\", \"flex_part_number\": \"NP2_FLEX_0\", \"connected_base_station_part_number\": \"NP2_QBSC_00\"}",
"manufacturer": "Imec"
},
{
"name": "NIDQBoard",
"description": "A NIDQ board used in conjunction with SpikeGLX.",
"manufacturer": "National Instruments"
}
],
"ElectrodeGroup": [
{
"name": "Imec0Shank0",
"description": "A group representing probe/shank 'Imec0Shank0'.",
"location": "unknown",
"device": "NeuropixelImec0"
},
{
"name": "NIDQChannelGroup",
"description": "A group representing the NIDQ channels.",
"location": "unknown",
"device": "NIDQBoard"
}
],
"ElectricalSeriesAPImec0": {
"name": "ElectricalSeriesAPImec0",
"description": "Acquisition traces for the ElectricalSeriesAPImec0."
},
"Electrodes": [
{
"name": "shank_electrode_number",
"description": "0-indexed channel within a shank."
},
{
"name": "group_name",
"description": "Name of the ElectrodeGroup this electrode is a part of."
},
{
"name": "contact_shapes",
"description": "The shape of the electrode"
}
],
"ElectricalSeriesNIDQ": {
"name": "ElectricalSeriesNIDQ",
"description": "Raw acquisition traces from the NIDQ (.nidq.bin) channels."
},
"ElectricalSeriesLFImec0": {
"name": "ElectricalSeriesLFImec0",
"description": "Acquisition traces for the ElectricalSeriesLFImec0."
}
}
}
Loading

0 comments on commit fa63645

Please sign in to comment.