Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some metadata #20

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 31 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,37 @@ conversion scripts and datainterfaces. As a placeholder, here we have `src/schn

## Running a Conversion

To convert the 4 example sessions, simply run
```bash
python src/schneider_lab_to_nwb/zempolich_2024/zempolich_2024_convert_session.py
```

To convert the whole dataset, simply run
```bash
python src/schneider_lab_to_nwb/zempolich_2024/zempolich_2024_convert_dataset.py
```
To convert the 5 example sessions,
1. In `src/schneider_lab_to_nwb/zempolich_2024/zempolich_2024_convert_session.py`, update the `data_dir_path` and
`output_dir_path` to appropriate local paths. `data_dir_path` should be the high-level directory where the data is
stored, corresponding to `Grant Zempolich Project Data` in the GDrive. `output_dir_path` can be any valid path on
your system where the output NWB files will be stored.
2. simply run
```bash
python src/schneider_lab_to_nwb/zempolich_2024/zempolich_2024_convert_session.py
```

To convert the whole dataset,
1. Update `data_dir_path` and `output_dir_path` in `src/schneider_lab_to_nwb/zempolich_2024/zempolich_2024_convert_all_sessions.py`
as with the example sessions.
2. simply run
```bash
python src/schneider_lab_to_nwb/zempolich_2024/zempolich_2024_convert_all_sessions.py
```

Note that the dataset conversion uses multiprocessing, currently set to 4 workers. To use more or fewer workers, simply
change the `max_workers` argument to `dataset_to_nwb()`.

### TODOs

There are some placeholders in the current version of the conversion that will need to be filled in by the Schneider Lab
before the conversion can be completed with the full data/metadata. These placeholders are marked with TODOs in the code
to make them easier to spot, and a list is provided below for convenience:

* In `src/schneider_lab_to_nwb/zempolich_2024/zempolich_2024_open_ephys_recording_interface.py` Line 36,
channel_positions are truncated to account for the 1-channel ephys data provided in the google drive. Lines 36-37
will need to be removed to enable running the conversion on the full ephys data.
* In `src/schneider_lab_to_nwb/zempolich_2024/metadata.yaml` Line 29, the mapping between subject_id and genotype is a
placeholder. Please specify the genotype for each subject, and it will automatically propagate to the NWB file.
* In `src/schneider_lab_to_nwb/zempolich_2024/metadata.yaml` Line 51, the mapping between subject_id and sex is a
placeholder. Please specify the sex for each subject, and it will automatically propagate to the NWB file.
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ def session_to_nwb(
editable_metadata = load_dict_from_file(editable_metadata_path)
metadata = dict_deep_update(metadata, editable_metadata)

# Add session description to metadata
behavior_folder_name = behavior_file_path.parent.name
session_metadata = next(meta for meta in metadata["Session"] if meta["name"] == behavior_folder_name)
session_description = session_metadata["description"]
metadata["NWBFile"]["session_description"] = session_description

add_session_start_time_to_metadata(
behavior_file_path=behavior_file_path, ephys_folder_path=ephys_folder_path, metadata=metadata
)
Expand All @@ -116,13 +122,22 @@ def session_to_nwb(
for i, video_file_path in enumerate(video_file_paths):
metadata_key_name = f"VideoCamera{i+1}"
metadata["Behavior"][metadata_key_name] = editable_metadata["Behavior"][metadata_key_name]
# remove extra videos from metadata
i += 1
while f"VideoCamera{i+1}" in metadata["Behavior"]:
metadata["Behavior"].pop(f"VideoCamera{i+1}")
i += 1

subject_id = behavior_file_path.name.split("_")[1]
session_id = behavior_file_path.name.split("_")[2]
nwbfile_path = output_dir_path / f"sub-{subject_id}_ses-{session_id}.nwb"
metadata["NWBFile"]["session_id"] = session_id
metadata["Subject"]["subject_id"] = subject_id

# Add subject info to metadata
metadata["Subject"]["sex"] = metadata["SubjectMaps"]["subject_id_to_sex"][subject_id]
metadata["Subject"]["genotype"] = metadata["SubjectMaps"]["subject_id_to_genotype"][subject_id]

# Run conversion
converter.run_conversion(metadata=metadata, nwbfile_path=nwbfile_path, conversion_options=conversion_options)

Expand Down Expand Up @@ -227,6 +242,20 @@ def main():
verbose=verbose,
)

# Example Session missing Camera 2
behavior_file_path = data_dir_path / "M2_EphysBehavioralFiles" / "raw_m80_240819_001.mat"
video_folder_path = data_dir_path / "Videos" / "M2EphysVideos" / "m80" / "240819"
intrinsic_signal_optical_imaging_folder_path = data_dir_path / "Intrinsic Imaging Data" / "m80"
session_to_nwb(
behavior_file_path=behavior_file_path,
video_folder_path=video_folder_path,
intrinsic_signal_optical_imaging_folder_path=intrinsic_signal_optical_imaging_folder_path,
brain_region="M2",
output_dir_path=output_dir_path,
stub_test=stub_test,
verbose=verbose,
)


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ NWBFile:
experimenter:
- Zempolich, Grant W.
- Schneider, David M.

Subject:
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 were used for all behavioral, electrophysiology, and optogenetic experiments. In the VGAT-ChR2-EYFP mouse line, channelrhodopsin (ChR2) was 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

Expand All @@ -25,6 +25,62 @@ BrainRegion:
electrode_group_location: Secondary Motor Cortex (M2)
optogenetic_stimulus_site_location: Secondary Motor Cortex (1.0-1.5 AP, 0.5-0.7 ML)

SubjectMaps:
subject_id_to_genotype: # TODO: Replace with actual genotype
m53: C57BL/6 or VGATChR2-EYFP
m54: C57BL/6 or VGATChR2-EYFP
m55: C57BL/6 or VGATChR2-EYFP
m63: C57BL/6 or VGATChR2-EYFP
m64: C57BL/6 or VGATChR2-EYFP
m65: C57BL/6 or VGATChR2-EYFP
m66: C57BL/6 or VGATChR2-EYFP
m67: C57BL/6 or VGATChR2-EYFP
m69: C57BL/6 or VGATChR2-EYFP
m70: C57BL/6 or VGATChR2-EYFP
m71: C57BL/6 or VGATChR2-EYFP
m72: C57BL/6 or VGATChR2-EYFP
m73: C57BL/6 or VGATChR2-EYFP
m74: C57BL/6 or VGATChR2-EYFP
m75: C57BL/6 or VGATChR2-EYFP
m76: C57BL/6 or VGATChR2-EYFP
m77: C57BL/6 or VGATChR2-EYFP
m78: C57BL/6 or VGATChR2-EYFP
m79: C57BL/6 or VGATChR2-EYFP
m80: C57BL/6 or VGATChR2-EYFP
m81: C57BL/6 or VGATChR2-EYFP
subject_id_to_sex: # TODO: Replace with actual sex (M, F, U, or O)
m53: U
m54: U
m55: U
m63: U
m64: U
m65: U
m66: U
m67: U
m69: U
m70: U
m71: U
m72: U
m73: U
m74: U
m75: U
m76: U
m77: U
m78: U
m79: U
m80: U
m81: U

Session:
- name: A1_EphysBehavioralFiles
description: Mice performed the auditory guided task while electricophysiological neural activity was recorded in the primary auditory cortex (A1).
- name: A1_OptoBehavioralFiles
description: Mice performed the auditory guided task while optogenetic stimulation was delivered to the primary auditory cortex (A1).
- name: M2_EphysBehavioralFiles
description: Mice performed the auditory guided task while electricophysiological neural activity was recorded in the secondary motor cortex (M2).
- name: M2_OptoBehavioralFiles
description: Mice performed the auditory guided task while optogenetic stimulation was delivered to the secondary motor cortex (M2).

Ecephys:
Device:
- name: MasmanidisSiliconMicroprobe128AxN
Expand All @@ -37,7 +93,7 @@ Ecephys:
ElectricalSeries:
- name: ElectricalSeries
description: Recording of AC neural responses in mice performing this behavioral task utilized dense 128-channel recording probes (Masmanidis Lab). These recording probes span a depth ~1mm, allowing for sampling of all layers of cortex. Electrophysiology data was recorded using OpenEphys Acquisition Board v2.4 and associated OpenEphys GUI software.
folder_name_to_start_datetime:
folder_name_to_start_datetime: # This mapping is used for ambiguous start times from OpenEphys ex. '18413' could be 18:41:03 or 18:04:13
m53/Day1_A1: 2023-10-29T16:56:01
m54/Day1_A1: 2023-10-29T18:18:03
m65/Day2_A1: 2023-10-26T18:49:04
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@

## Active Requests
- Mice sexes
- target image for m73
- Cam2 Video for M2EphysVideos/m80/240819

## Questions for Midway Meeting
- injection vs stimulation location(s) for A1 vs M2???
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ def temporally_align_data_interfaces(self) -> None:
starting_timestamp = get_starting_timestamp(mat_file=file)
cam1_timestamps -= starting_timestamp
cam2_timestamps -= starting_timestamp
self.data_interface_objects["VideoCamera1"].set_aligned_timestamps([cam1_timestamps])
self.data_interface_objects["VideoCamera2"].set_aligned_timestamps([cam2_timestamps])
if "VideoCamera1" in self.data_interface_objects:
self.data_interface_objects["VideoCamera1"].set_aligned_timestamps([cam1_timestamps])
if "VideoCamera2" in self.data_interface_objects:
self.data_interface_objects["VideoCamera2"].set_aligned_timestamps([cam2_timestamps])

# NOTE: passing in conversion_options as an attribute is a temporary solution until the neuroconv library is updated
# to allow for easier customization of the conversion process
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def add_to_nwbfile(
"""
folder_path = self.source_data["folder_path"]
channel_positions = np.load(folder_path / "channel_positions.npy")
if True: # TODO: Replace with `if stub_test:` once all channels are present in the data
if True: # TODO: Remove this if block once all channels are present in the data
channel_positions = channel_positions[:1, :]
location = metadata["BrainRegion"][brain_region]["electrode_group_location"]
for electrode_group in metadata["Ecephys"]["ElectrodeGroup"]:
Expand Down