diff --git a/pyproject.toml b/pyproject.toml index d9e1d7f..388eb56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ include = ["*"] [tool.black] line-length = 120 -target-version = ['py38', 'py39', 'py310', 'py311', 'py312'] +target-version = ['py38', 'py39', 'py310', 'py311'] include = '\.pyi?$' extend-exclude = ''' /( diff --git a/src/schneider_lab_to_nwb/schneider_2024/__init__.py b/src/schneider_lab_to_nwb/schneider_2024/__init__.py index 11d0496..eafd574 100644 --- a/src/schneider_lab_to_nwb/schneider_2024/__init__.py +++ b/src/schneider_lab_to_nwb/schneider_2024/__init__.py @@ -1,2 +1,2 @@ -from .schneider_2024behaviorinterface import Schneider2024BehaviorInterface -from .schneider_2024nwbconverter import Schneider2024NWBConverter +from .schneider_2024_behaviorinterface import Schneider2024BehaviorInterface +from .schneider_2024_nwbconverter import Schneider2024NWBConverter diff --git a/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_convert_session.py b/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_convert_session.py index 4a3313e..b8686c4 100644 --- a/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_convert_session.py +++ b/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_convert_session.py @@ -2,10 +2,13 @@ from pathlib import Path from typing import Union import datetime +import pytz from zoneinfo import ZoneInfo +import shutil +from pprint import pprint +import numpy as np from neuroconv.utils import load_dict_from_file, dict_deep_update - from schneider_lab_to_nwb.schneider_2024 import Schneider2024NWBConverter @@ -13,37 +16,38 @@ def session_to_nwb(data_dir_path: Union[str, Path], output_dir_path: Union[str, data_dir_path = Path(data_dir_path) output_dir_path = Path(output_dir_path) + recording_folder_path = data_dir_path / "Raw Ephys" / "m69_2023-10-31_17-24-15_Day1_A1" + sorting_folder_path = data_dir_path / "Processed Ephys" / "m69_2023-10-31_17-24-15_Day1_A1" if stub_test: output_dir_path = output_dir_path / "nwb_stub" + recording_folder_path = recording_folder_path.with_name(recording_folder_path.name + "_stubbed") output_dir_path.mkdir(parents=True, exist_ok=True) - session_id = "subject_identifier_usually" + session_id = "sample_session" nwbfile_path = output_dir_path / f"{session_id}.nwb" source_data = dict() conversion_options = dict() # Add Recording - source_data.update(dict(Recording=dict())) + stream_name = "Signals CH" # stream_names = ["Signals CH", "Signals AUX"] + source_data.update(dict(Recording=dict(folder_path=recording_folder_path, stream_name=stream_name))) conversion_options.update(dict(Recording=dict(stub_test=stub_test))) # Add Sorting - source_data.update(dict(Sorting=dict())) + source_data.update(dict(Sorting=dict(folder_path=sorting_folder_path))) conversion_options.update(dict(Sorting=dict())) - # Add Behavior - source_data.update(dict(Behavior=dict())) - conversion_options.update(dict(Behavior=dict())) + # # Add Behavior + # source_data.update(dict(Behavior=dict())) + # conversion_options.update(dict(Behavior=dict())) converter = Schneider2024NWBConverter(source_data=source_data) # Add datetime to conversion metadata = converter.get_metadata() - datetime.datetime( - year=2020, month=1, day=1, tzinfo=ZoneInfo("US/Eastern") - ) - date = datetime.datetime.today() # TO-DO: Get this from author - metadata["NWBFile"]["session_start_time"] = date + EST = ZoneInfo("US/Eastern") + metadata["NWBFile"]["session_start_time"] = metadata["NWBFile"]["session_start_time"].replace(tzinfo=EST) # Update default metadata with the editable in the corresponding yaml file editable_metadata_path = Path(__file__).parent / "schneider_2024_metadata.yaml" @@ -51,19 +55,42 @@ def session_to_nwb(data_dir_path: Union[str, Path], output_dir_path: Union[str, metadata = dict_deep_update(metadata, editable_metadata) metadata["Subject"]["subject_id"] = "a_subject_id" # Modify here or in the yaml file + conversion_options["Sorting"]["units_description"] = metadata["Sorting"]["units_description"] + + # Add electrode metadata + channel_positions = np.load(sorting_folder_path / "channel_positions.npy") + if stub_test: + channel_positions = channel_positions[:1, :] + location = metadata["Ecephys"]["ElectrodeGroup"][0]["location"] + channel_ids = converter.data_interface_objects["Recording"].recording_extractor.get_channel_ids() + converter.data_interface_objects["Recording"].recording_extractor.set_channel_locations( + channel_ids=channel_ids, locations=channel_positions + ) + converter.data_interface_objects["Recording"].recording_extractor.set_property( + key="brain_area", + ids=channel_ids, + values=[location] * len(channel_ids), + ) + metadata["Ecephys"]["Device"] = editable_metadata["Ecephys"]["Device"] # Run conversion converter.run_conversion(metadata=metadata, nwbfile_path=nwbfile_path, conversion_options=conversion_options) -if __name__ == "__main__": - +def main(): # Parameters for conversion - data_dir_path = Path("/Directory/With/Raw/Formats/") - output_dir_path = Path("~/conversion_nwb/") - stub_test = False - - session_to_nwb(data_dir_path=data_dir_path, - output_dir_path=output_dir_path, - stub_test=stub_test, - ) + data_dir_path = Path("/Volumes/T7/CatalystNeuro/Schneider/Schneider sample Data") + output_dir_path = Path("/Volumes/T7/CatalystNeuro/Schneider/conversion_nwb") + stub_test = True + + if output_dir_path.exists(): + shutil.rmtree(output_dir_path, ignore_errors=True) + session_to_nwb( + data_dir_path=data_dir_path, + output_dir_path=output_dir_path, + stub_test=stub_test, + ) + + +if __name__ == "__main__": + main() diff --git a/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_metadata.yaml b/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_metadata.yaml index 71992a5..a6d749c 100644 --- a/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_metadata.yaml +++ b/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_metadata.yaml @@ -1,14 +1,56 @@ NWBFile: - related_publications: - https://doi.org/### or link to APA or MLA citation of the publication - session_description: - A rich text description of the experiment. Can also just be the abstract of the publication. + keywords: + - auditory cortex + - predictive coding + - optogenetics + experiment_description: + Identifying mistakes is important for improving performance during acoustic behaviors like speech and musicianship. Although hearing is instrumental for monitoring and adapting these behaviors, the neural circuits that integrate motor, acoustic, and goal-related signals to detect errors and guide ongoing sensorimotor adaptation in mammals remain unidentified. Here, we develop a novel closed-loop, sound-guided behavior that requires mice to use real-time acoustic feedback to guide skilled ongoing forelimb movements. Large scale electrophysiology recordings reveal that the mouse auditory cortex integrates information about sound and movement, as well as encodes error- and learning-related signals during this sound-generating behavior. Distinct groups of auditory cortex neurons signal different error types, and the activity of these neurons predicts both within-trial and across-trial behavioral adaptations. Brief, behavior-triggered optogenetic suppression of auditory cortex during error signaling hinders behavioral corrections on both rapid and long time scales, indicating that cortical error signals are necessary for skilled acoustic behaviors. Together, these experiments identify a cortical role for detecting errors and learning from mistakes and suggest that the auditory cortex plays a critical role in skilled, sound-generating behavior in mammals. institution: Institution where the lab is located lab: Schneider experimenter: - - Last, First Middle - - Last, First Middle + - Zempolich, Grant W. + - Schneider, David M. Subject: - species: Rattus norvegicus - age: TBD # in ISO 8601, such as "P1W2D" - sex: TBD # One of M, F, U, or O + species: Mus musculus + age: P12W/ # in ISO 8601, such as "P1W2D" + sex: U # One of M, F, U, or O + description: 12-week-old C57BL/6 or VGATChR2-EYFP mice (see Aims approaches below for details) will be used for all behavioral, electrophysiology, and optogenetic experiments. In the VGAT-ChR2-EYFP mouse line, channelrhodopsin (ChR2) is coupled to the vesicular GABA transporter, inducing expression in GABAergic inhibitory neurons ubiquitously across cortex and allowing for real time optogenetic inhibition of brain regions of interest. + strain: C57BL/6 + +Ecephys: + Device: + - name: MasmanidisSiliconMicroprobe128AxN + description: Masmanidis Lab dense 128-channel recording probes (https://masmanidislab.neurobio.ucla.edu/technology.html). + manufacturer: Masmanidis Lab + ElectrodeGroup: + - name: ElectrodeGroup + description: ElectrodeGroup for all channels in the recording probe. + location: Auditory Cortex + device: MasmanidisSiliconMicroprobe128AxN + ElectricalSeries: + - name: ElectricalSeries + description: Recording of AC neural responses in mice performing this behavioral task will utilize dense 128-channel recording probes (Masmanidis Lab). These recording probes span a depth ~1mm, allowing for sampling of all layers of cortex. Electrophysiology data will be recorded using OpenEphys Acquisition Board v2.4 and associated OpenEphys GUI software. + # UnitProperties: + # - name: n_spikes + # description: Number of spikes recorded from each unit. + # - name: fr + # description: Average firing rate of each unit. + # - name: depth + # description: Estimated depth of each unit in micrometers. + # - name: Amplitude + # description: Per-template amplitudes, computed as the L2 norm of the template. + # - name: ContamPct + # description: Contamination rate for each template, computed as fraction of refractory period violations relative to expectation based on a Poisson process. + # - name: KSLabel + # description: Label indicating whether each template is 'mua' (multi-unit activity) or 'good' (refractory). + # - name: original_cluster_id + # description: Original cluster ID assigned by Kilosort. + # - name: amp + # description: For every template, the maximum amplitude of the template waveforms across all channels. + # - name: ch + # description: The channel label of the best channel, as defined by the user. + # - name: sh + # description: The shank label of the best channel. + +Sorting: + units_description: Neural spikes will be sorted offline using Kilosort 2.5 and Phy2 software and manually curated to ensure precise spike time acquisition. diff --git a/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_notes.md b/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_notes.md index a75560d..5ae3cf7 100644 --- a/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_notes.md +++ b/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_notes.md @@ -1 +1,9 @@ # Notes concerning the schneider_2024 conversion + +## Data Requests +- Mice sexes +- Remaining data for Grant's project +- More detailed position info for recording probe + - Subfield of auditory cortex: A1? A2? AAF? etc. + - stereotactic coordinates of the whole probe +- \ No newline at end of file diff --git a/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_nwbconverter.py b/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_nwbconverter.py index 5de50c9..5806bc5 100644 --- a/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_nwbconverter.py +++ b/src/schneider_lab_to_nwb/schneider_2024/schneider_2024_nwbconverter.py @@ -1,7 +1,7 @@ """Primary NWBConverter class for this dataset.""" from neuroconv import NWBConverter from neuroconv.datainterfaces import ( - SpikeGLXRecordingInterface, + OpenEphysRecordingInterface, PhySortingInterface, ) @@ -12,7 +12,6 @@ class Schneider2024NWBConverter(NWBConverter): """Primary conversion class for my extracellular electrophysiology dataset.""" data_interface_classes = dict( - Recording=SpikeGLXRecordingInterface, + Recording=OpenEphysRecordingInterface, Sorting=PhySortingInterface, - Behavior=Schneider2024BehaviorInterface, )