NeurodataWithoutBorders / pynwb

A Python API for working with Neurodata stored in the NWB Format
https://pynwb.readthedocs.io
Other
174 stars 85 forks source link

[Bug]: NWB file created with pynwb cannot be read by OpenEphys #1912

Closed florian6973 closed 1 month ago

florian6973 commented 1 month ago

What happened?

I wrote an ephys recording to file with pynwb. When trying to load it in the file reader of OpenEphys (after installing the NWB plugin), OpenEphys crashes.

Steps to Reproduce

- Generate a recording or read a current recording file (for instance with spikeinterface)
- Write it to file with PyNWB (Electrode Table + ElectricalSeries)
- Try to open it in OpenEphys

Traceback

Missing rate, sync and other data/attributes in ElectricalSeries in the OpenEphys logs.
https://github.com/open-ephys-plugins/nwb-format/blob/main/Source/FileSource/NWBFileSource.cpp

Operating System

Windows

Python Executable

Conda

Python Version

3.11

Package Versions

No response

Code of Conduct

stephprince commented 1 month ago

Thanks for submitting this issue @florian6973, would you be able to share the file or the code you use to generate the file?

florian6973 commented 1 month ago

Sure, here it is:

import spikeinterface.full as si
from datetime import datetime
from uuid import uuid4
import numpy as np
from dateutil.tz import tzlocal
from pynwb import NWBHDF5IO, NWBFile
from pynwb.ecephys import LFP, ElectricalSeries

rec, sort = si.read_mearec("recording-demo.h5")

nwbfile = NWBFile(
    session_description="my first synthetic recording",
    identifier=str(uuid4()),
    session_start_time=datetime.now(tzlocal()),
    experimenter=[
        "Me",
    ],
    lab="Lab",
    institution="University",
    experiment_description="Test.",
    session_id="S1",
)

device = nwbfile.create_device(
    name="array", description="the best array", manufacturer="Probe Company 9000"
)

nwbfile.add_electrode_column(name="label", description="label of electrode")

nshanks = 4
nchannels_per_shank = 32
electrode_counter = 0

for ishank in range(nshanks):
    # create an electrode group for this shank
    electrode_group = nwbfile.create_electrode_group(
        name="shank{}".format(ishank),
        description="electrode group for shank {}".format(ishank),
        device=device,
        location="brain area",
    )
    # add electrodes to the electrode table
    for ielec in range(nchannels_per_shank):
        nwbfile.add_electrode(
            group=electrode_group,
            label="shank{}elec{}".format(ishank, ielec),
            location="brain area",
        )
        electrode_counter += 1

all_table_region = nwbfile.create_electrode_table_region(
    region=list(range(electrode_counter)),  # reference row indices 0 to N-1
    description="all electrodes",
)

raw_data = rec.get_traces(return_scaled=True)
raw_electrical_series = ElectricalSeries(
    name="ElectricalSeries",
    data=raw_data.astype(np.int16),
    electrodes=all_table_region,
    channel_conversion=np.ones(raw_data.shape[1]),
    timestamps=np.arange(raw_data.shape[0]) / rec.get_sampling_frequency(),
)

nwbfile.add_acquisition(raw_electrical_series)
with NWBHDF5IO("test_oe.nwb", "w") as io:
    io.write(nwbfile)

What I am not sure though is whether it is a pynwb or an OpenEphys bug.

stephprince commented 1 month ago

@florian6973 thanks for sharing that code. After looking into it a bit, I think this is an OpenEphys issue.

The OpenEphys file reader expects the ElectricalSeries in the NWB file to have a sync group for aligning time information. Once timestamp data is calculated and stored, the contents of 'sync' should be mostly for archival purposes. There are many NWB files (like the one you created above) that will not have this sync group since the timestamps are already in a common timebase.

You can open up an issue here: https://github.com/open-ephys-plugins/nwb-format

florian6973 commented 1 month ago

Thank you for your explanation! Just did :)