diff --git a/spec/ndx-extracellular-channels.extensions.yaml b/spec/ndx-extracellular-channels.extensions.yaml index 4af2e46..cdf0cfc 100644 --- a/spec/ndx-extracellular-channels.extensions.yaml +++ b/spec/ndx-extracellular-channels.extensions.yaml @@ -23,7 +23,9 @@ groups: - name: reference dtype: text doc: Reference point for the relative position coordinates and information about - the coordinate system used. + the coordinate system used, e.g., which direction is positive in the x direction + (first element), which direction is positive in the y direction (second element), + etc. required: false - name: shape neurodata_type_inc: VectorData @@ -87,7 +89,7 @@ groups: - neurodata_type_def: ProbeModel neurodata_type_inc: Device doc: Neural probe object, compatible with the ProbeInterface specification. The - name of the object should be the model name of the probe, e.g., 'Neuropixels 1.0'. + name of the object should be the model name of the probe, e.g., "Neuropixels 1.0". attributes: - name: ndim dtype: int @@ -96,7 +98,7 @@ groups: required: false - name: model dtype: text - doc: Name of the model of the probe, e.g., 'Neuropixels 1.0'. + doc: Name of the model of the probe, e.g., "Neuropixels 1.0". - name: planar_contour_in_um dtype: float dims: @@ -114,6 +116,7 @@ groups: the polygon. e.g., [(-20., -30.), (20., -110.), (60., -30.), (60., 190.), (-20., 190.)].See 'probe_planar_contour' in https://probeinterface.readthedocs.io/en/main/format_spec.html for more details. + required: false groups: - name: contacts_table neurodata_type_inc: ContactsTable @@ -136,10 +139,28 @@ groups: doc: Metadata about the insertion of a probe into the brain, which can be used to determine the location of the probe in the brain. attributes: - - name: reference + - name: insertion_position_ap_in_mm + dtype: float + doc: Anteroposterior (AP) stereotactic coordinate of where the probe was inserted, + in millimeters. + is anterior. Coordinate is relative to the zero-point described + in `position_reference`. + required: false + - name: insertion_position_ml_in_mm + dtype: float + doc: Mediolateral (ML) stereotactic coordinate of where the probe was inserted, + in millimeters. + is right. Coordinate is relative to the zero-point described + in `position_reference`. + required: false + - name: insertion_position_dv_in_mm + dtype: float + doc: Dorsoventral (DV) stereotactic coordinate of where the probe was inserted, + in millimeters. + is up. Coordinate is relative to the zero-point described + in `position_reference`. + required: false + - name: position_reference dtype: text - doc: Reference point for `insertion_position_in_mm` coordinates, e.g., "bregma - at the cortical surface". + doc: Location of the origin (0, 0, 0) for `insertion_position_{X}_in_mm` coordinates, + e.g., "(AP, ML, DV) = (0, 0, 0) corresponds to bregma at the cortical surface". required: false - name: hemisphere dtype: text @@ -147,10 +168,35 @@ groups: stimulus site. Should be consistent with `insertion_position_in_mm.ml` coordinate (left = ml < 0, right = ml > 0). required: false + - name: insertion_angle_pitch_in_deg + dtype: float + doc: The pitch angle of the probe at the time of insertion, in degrees. Pitch + = rotation around left-right axis, like nodding (+ is rotating the nose upward). + Zero is defined as the probe being parallel to an axial slice of the brain.Yaw + = rotation around dorsal-ventral axis, like shaking (+ is rotating the nose + rightward). Roll = rotation around anterior-posterior axis, like tilting (+ + is rotating the right side downward). + required: false + - name: insertion_angle_yaw_in_deg + dtype: float + doc: The yaw angle of the probe at the time of insertion, in degrees. Yaw = rotation + around dorsal-ventral axis, like shaking (+ is rotating the nose rightward). + Zero is defined as the probe being parallel to an sagittal slice of the brain. + required: false + - name: insertion_angle_roll_in_deg + dtype: float + doc: 'The roll angle of the probe at the time of insertion, in degrees. Roll = + rotation around anterior-posterior axis, like tilting (+ is rotating the right + side downward). Zero is defined as the probe being parallel to a coronal slice + of the brain. ' + required: false - name: depth_in_mm dtype: float - doc: Depth that the probe was driven along `insertion_angle` starting from `insertion_position_in_mm`, - in millimeters. + doc: Depth that the probe was driven along `insertion_angle` starting from `insertion_position_ap_in_mm` + and `insertion_position_ml_in_mm`, in millimeters. This is an alternate method + of providing the dorsal-ventral coordinate of the probe insertion site. If both + `insertion_position_dv_in_mm` and `depth_in_mm` are provided, the values should + be consistent. required: false - neurodata_type_def: ChannelsTable neurodata_type_inc: DynamicTable @@ -158,10 +204,20 @@ groups: doc: Metadata about the channels used in an extracellular recording from a single probe. attributes: + - name: position_reference + dtype: text + doc: Location of the origin (0, 0, 0) for `{X}_position_{Y}_in_mm` coordinates, + e.g., "(AP, ML, DV) = (0, 0, 0) corresponds to bregma at the cortical surface". + required: false - name: reference_mode dtype: text - doc: The reference mode used for the recording; e.g. 'external wire', 'common - reference'. + doc: The reference mode used for the recording; e.g., "external wire in CSF", + common reference", "skull screw over frontal cortex". + required: false + - name: position_confirmation_method + dtype: text + doc: Description of the method used to confirm the position of the contacts or + brain area, e.g., "histology", "MRI". required: false datasets: - name: contact @@ -176,63 +232,59 @@ groups: neurodata_type_inc: VectorData dtype: text doc: The filter used on the raw (wideband) voltage data from this contact, including - the filter name and frequency cutoffs, e.g., 'High-pass filter at 300 Hz.' + the filter name and frequency cutoffs, e.g., "High-pass filter at 300 Hz." quantity: '?' - - name: estimated_position_in_mm + - name: estimated_position_ap_in_mm neurodata_type_inc: VectorData - dtype: - - name: ap - dtype: float - doc: Anteroposterior coordinate in mm, relative to `reference` (+ is anterior). - - name: ml - dtype: float - doc: Mediolateral coordinate in mm, relative to `reference` (+ is right). - - name: dv - dtype: float - doc: Dorsoventral coordinate in mm, relative to `reference` (+ is up). - doc: Stereotactic coordinates (AP, ML, DV) of the estimated contact position, - in millimeters. AP = anteroposterior coordinate in mm (+ is anterior). ML = - mediolateral coordinate in mm (+ is right). DV = dorsoventral coordinate in - mm (+ is up).Coordinates are relative to `reference` + dtype: float + doc: Anteroposterior (AP) stereotactic coordinate of the estimated contact position, + in millimeters. + is anterior. Coordinate is relative to the zero-point described + in `position_reference`. + quantity: '?' + - name: estimated_position_ml_in_mm + neurodata_type_inc: VectorData + dtype: float + doc: Mediolateral (ML) stereotactic coordinate of the estimated contact position, + in millimeters. + is right. Coordinate is relative to the zero-point described + in `position_reference`. + quantity: '?' + - name: estimated_position_dv_in_mm + neurodata_type_inc: VectorData + dtype: float + doc: Dorsoventral (DV) stereotactic coordinate of the estimated contact position, + in millimeters. + is up. Coordinate is relative to the zero-point described + in `position_reference`. quantity: '?' - attributes: - - name: reference - dtype: text - doc: Reference point for the position coordinates. e.g., "bregma at the cortical - surface". - required: false - name: estimated_brain_area neurodata_type_inc: VectorData dtype: text - doc: The brain area of the estimated contact position, e.g., 'CA1'. + doc: The brain area of the estimated contact position, e.g., "CA1". quantity: '?' - - name: confirmed_position_in_mm + - name: confirmed_position_ap_in_mm neurodata_type_inc: VectorData - dtype: - - name: ap - dtype: float - doc: Anteroposterior coordinate in mm, relative to `reference` (+ is anterior). - - name: ml - dtype: float - doc: Mediolateral coordinate in mm, relative to `reference` (+ is right). - - name: dv - dtype: float - doc: Dorsoventral coordinate in mm, relative to `reference` (+ is up). - doc: Stereotactic coordinates (AP, ML, DV) of the the verified actual contact - position, such as from histology, in millimeters. AP = anteroposterior coordinate - in mm (+ is anterior). ML = mediolateral coordinate in mm (+ is right). DV = - dorsoventral coordinate in mm (+ is up).Coordinates are relative to `reference` + dtype: float + doc: Anteroposterior (AP) stereotactic coordinate of the confirmed contact position, + in millimeters. + is anterior. Coordinate is relative to the zero-point described + in `position_reference`. + quantity: '?' + - name: confirmed_position_ml_in_mm + neurodata_type_inc: VectorData + dtype: float + doc: Mediolateral (ML) stereotactic coordinate of the confirmed contact position, + in millimeters. + is right. Coordinate is relative to the zero-point described + in `position_reference`. + quantity: '?' + - name: confirmed_position_dv_in_mm + neurodata_type_inc: VectorData + dtype: float + doc: Dorsoventral (DV) stereotactic coordinate of the confirmed contact position, + in millimeters. + is up. Coordinate is relative to the zero-point described + in `position_reference`. quantity: '?' - attributes: - - name: reference - dtype: text - doc: Reference point for the position coordinates. e.g., "bregma at the cortical - surface". - required: false - name: confirmed_brain_area neurodata_type_inc: VectorData dtype: text - doc: The brain area of the actual contact position, e.g., 'CA1'. + doc: The brain area of the actual contact position, e.g., "CA1". quantity: '?' groups: - name: probe_insertion diff --git a/src/pynwb/ndx_extracellular_channels/utils.py b/src/pynwb/ndx_extracellular_channels/utils.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/pynwb/tests/test_classes.py b/src/pynwb/tests/test_classes.py index 18474e4..19212ac 100644 --- a/src/pynwb/tests/test_classes.py +++ b/src/pynwb/tests/test_classes.py @@ -139,7 +139,6 @@ def test_constructor(self): description="A neuropixels probe", model="Neuropixels 1.0", manufacturer="IMEC", - # TODO make planar_contour_in_um optional planar_contour_in_um=[[-10.0, -10.0], [10.0, -10.0], [10.0, 10.0], [-10.0, 10.0]], contacts_table=ct, ) @@ -322,31 +321,65 @@ class TestProbeInsertion(TestCase): def test_constructor_minimal(self): pi = ProbeInsertion() assert pi.name == "probe_insertion" - assert pi.reference is None + assert pi.position_reference is None assert pi.hemisphere is None assert pi.depth_in_mm is None - # assert pi.insertion_position_in_mm is None - # assert pi.insertion_angle_in_deg is None - - def test_constructor(self): + assert pi.insertion_position_ap_in_mm is None + assert pi.insertion_position_ml_in_mm is None + assert pi.insertion_position_dv_in_mm is None + assert pi.insertion_angle_roll_in_deg is None + assert pi.insertion_angle_pitch_in_deg is None + assert pi.insertion_angle_yaw_in_deg is None + + def test_constructor_with_depth(self): pi = ProbeInsertion( name="ProbeInsertion", # test custom name - reference="Bregma at the cortical surface.", + position_reference="Bregma at the cortical surface.", hemisphere="left", depth_in_mm=10.0, - # insertion_position_in_mm=[2.0, -4.0, 0.0], # TODO waiting on schema - # insertion_angle_in_deg=[0.0, 0.0, -10.0], + insertion_position_ap_in_mm=2.0, + insertion_position_ml_in_mm=-4.0, + insertion_angle_roll_in_deg=-10.0, + insertion_angle_pitch_in_deg=0.0, + insertion_angle_yaw_in_deg=0.0, ) assert pi.name == "ProbeInsertion" - assert pi.reference == "Bregma at the cortical surface." + assert pi.position_reference == "Bregma at the cortical surface." assert pi.hemisphere == "left" assert pi.depth_in_mm == 10.0 - # assert pi.insertion_position_in_mm == [2.0, -4.0, 0.0] - # assert pi.insertion_angle_in_deg == [0.0, 0.0, -10.0] + assert pi.insertion_position_ap_in_mm == 2.0 + assert pi.insertion_position_ml_in_mm == -4.0 + assert pi.insertion_position_dv_in_mm is None + assert pi.insertion_angle_roll_in_deg == -10.0 + assert pi.insertion_angle_pitch_in_deg == 0.0 + assert pi.insertion_angle_yaw_in_deg == 0.0 + + def test_constructor_with_dv(self): + pi = ProbeInsertion( + name="ProbeInsertion", # test custom name + position_reference="Bregma at the cortical surface.", + hemisphere="left", + insertion_position_ap_in_mm=2.0, + insertion_position_ml_in_mm=-4.0, + insertion_position_dv_in_mm=-10.0, + insertion_angle_roll_in_deg=-10.0, + insertion_angle_pitch_in_deg=0.0, + insertion_angle_yaw_in_deg=0.0, + ) + assert pi.name == "ProbeInsertion" + assert pi.position_reference == "Bregma at the cortical surface." + assert pi.hemisphere == "left" + assert pi.insertion_position_ap_in_mm == 2.0 + assert pi.insertion_position_ml_in_mm == -4.0 + assert pi.insertion_position_dv_in_mm == -10.0 + assert pi.insertion_angle_roll_in_deg == -10.0 + assert pi.insertion_angle_pitch_in_deg == 0.0 + assert pi.insertion_angle_yaw_in_deg == 0.0 -class TestProbeInsertionRoundTrip(NWBH5IOFlexMixin, TestCase): + +class TestProbeInsertionDepthRoundTrip(NWBH5IOFlexMixin, TestCase): """Simple roundtrip test for a ProbeInsertion.""" def getContainerType(self): @@ -355,11 +388,41 @@ def getContainerType(self): def addContainer(self): pi = ProbeInsertion( name="ProbeInsertion", # test custom name - reference="Bregma at the cortical surface.", + position_reference="Bregma at the cortical surface.", hemisphere="left", depth_in_mm=10.0, - # insertion_position_in_mm=[2.0, -4.0, 0.0], # TODO waiting on schema - # insertion_angle_in_deg=[0.0, 0.0, -10.0], + insertion_position_ap_in_mm=2.0, + insertion_position_ml_in_mm=-4.0, + insertion_angle_roll_in_deg=-10.0, + insertion_angle_pitch_in_deg=0.0, + insertion_angle_yaw_in_deg=0.0, + ) + + # put this in nwbfile.scratch for testing + self.nwbfile.add_scratch(pi) + + def getContainer(self, nwbfile: NWBFile): + return nwbfile.scratch["ProbeInsertion"] + + +class TestProbeInsertionDVRoundTrip(NWBH5IOFlexMixin, TestCase): + """Simple roundtrip test for a ProbeInsertion.""" + + def getContainerType(self): + return "ProbeInsertion" + + def addContainer(self): + pi = ProbeInsertion( + name="ProbeInsertion", # test custom name + position_reference="Bregma at the cortical surface.", + hemisphere="left", + depth_in_mm=10.0, + insertion_position_ap_in_mm=2.0, + insertion_position_ml_in_mm=-4.0, + insertion_position_dv_in_mm=-10.0, + insertion_angle_roll_in_deg=-10.0, + insertion_angle_pitch_in_deg=0.0, + insertion_angle_yaw_in_deg=0.0, ) # put this in nwbfile.scratch for testing @@ -410,7 +473,9 @@ def test_constructor_add_row(self): ct = ChannelsTable( name="Neuropixels1ChannelsTable", # test custom name description="Test channels table", - reference_mode="Reference to channel 2", + reference_mode="Referenced to channel 2.", + position_reference = "(AP, ML, DV) = (0, 0, 0) corresponds to bregma at the cortical surface.", + position_confirmation_method = "Histology", probe=probe, probe_insertion=pi, target_tables={ @@ -424,9 +489,13 @@ def test_constructor_add_row(self): contact=0, reference_contact=2, filter="High-pass at 300 Hz", - estimated_position_in_mm=[-1.5, 2.5, -2.5], + estimated_position_ap_in_mm=2.0, + estimated_position_ml_in_mm=-5.0, + estimated_position_dv_in_mm=-9.5, estimated_brain_area="CA3", - confirmed_position_in_mm=[-1.5, 2.4, -2.4], + confirmed_position_ap_in_mm=2.0, + confirmed_position_ml_in_mm=-4.9, + confirmed_position_dv_in_mm=-9.5, confirmed_brain_area="CA3", ) @@ -434,20 +503,37 @@ def test_constructor_add_row(self): contact=1, reference_contact=2, filter="High-pass at 300 Hz", - estimated_position_in_mm=[-1.5, 2.5, -2.4], + estimated_position_ap_in_mm=2.0, + estimated_position_ml_in_mm=-4.9, + estimated_position_dv_in_mm=-9.3, estimated_brain_area="CA3", - confirmed_position_in_mm=[-1.5, 2.4, -2.3], + confirmed_position_ap_in_mm=2.0, + confirmed_position_ml_in_mm=-4.8, + confirmed_position_dv_in_mm=-9.3, confirmed_brain_area="CA3", ) - # TODO might be nice to put this on the constructor of ContactsTable as position__reference - # without using a custom mapper - ct["estimated_position_in_mm"].reference = "Bregma at the cortical surface" - ct["confirmed_position_in_mm"].reference = "Bregma at the cortical surface" - - # TODO assert ct.name == "Neuropixels1ChannelsTable" - # assert ... + assert ct.description == "Test channels table" + assert ct.reference_mode == "Referenced to channel 2." + assert ct.position_reference == "(AP, ML, DV) = (0, 0, 0) corresponds to bregma at the cortical surface." + assert ct.position_confirmation_method == "Histology" + assert ct.probe is probe + assert ct.probe_insertion is pi + assert len(ct) == 2 + assert ct["contact"].data == [0, 1] + assert ct["contact"].table is probe.probe_model.contacts_table + assert ct["reference_contact"].data == [2, 2] + assert ct["reference_contact"].table is probe.probe_model.contacts_table + assert ct["filter"].data == ["High-pass at 300 Hz", "High-pass at 300 Hz"] + assert ct["estimated_position_ap_in_mm"].data == [2.0, 2.0] + assert ct["estimated_position_ml_in_mm"].data == [-5.0, -4.9] + assert ct["estimated_position_dv_in_mm"].data == [-9.5, -9.3] + assert ct["estimated_brain_area"].data == ["CA3", "CA3"] + assert ct["confirmed_position_ap_in_mm"].data == [2.0, 2.0] + assert ct["confirmed_position_ml_in_mm"].data == [-4.9, -4.8] + assert ct["confirmed_position_dv_in_mm"].data == [-9.5, -9.3] + assert ct["confirmed_brain_area"].data == ["CA3", "CA3"] class TestChannelsTableRoundTrip(NWBH5IOFlexMixin, TestCase): @@ -468,7 +554,9 @@ def addContainer(self): ct = ChannelsTable( name="Neuropixels1ChannelsTable", # test custom name description="Test channels table", - reference_mode="Reference to channel 2", + reference_mode="Referenced to channel 2.", + position_reference = "(AP, ML, DV) = (0, 0, 0) corresponds to bregma at the cortical surface.", + position_confirmation_method = "Histology", probe=probe, probe_insertion=pi, target_tables={ @@ -482,9 +570,13 @@ def addContainer(self): contact=0, reference_contact=2, filter="High-pass at 300 Hz", - estimated_position_in_mm=[-1.5, 2.5, -2.5], + estimated_position_ap_in_mm=2.0, + estimated_position_ml_in_mm=-5.0, + estimated_position_dv_in_mm=-9.5, estimated_brain_area="CA3", - confirmed_position_in_mm=[-1.5, 2.4, -2.4], + confirmed_position_ap_in_mm=2.0, + confirmed_position_ml_in_mm=-4.9, + confirmed_position_dv_in_mm=-9.5, confirmed_brain_area="CA3", ) @@ -492,18 +584,16 @@ def addContainer(self): contact=1, reference_contact=2, filter="High-pass at 300 Hz", - estimated_position_in_mm=[-1.5, 2.5, -2.4], + estimated_position_ap_in_mm=2.0, + estimated_position_ml_in_mm=-4.9, + estimated_position_dv_in_mm=-9.3, estimated_brain_area="CA3", - confirmed_position_in_mm=[-1.5, 2.4, -2.3], + confirmed_position_ap_in_mm=2.0, + confirmed_position_ml_in_mm=-4.8, + confirmed_position_dv_in_mm=-9.3, confirmed_brain_area="CA3", ) - # TODO might be nice to put this on the constructor of ContactsTable as position__reference - # without using a custom mapper - # TODO does matching this happen in the container equals roundtrip test? - ct["estimated_position_in_mm"].reference = "Bregma at the cortical surface" - ct["confirmed_position_in_mm"].reference = "Bregma at the cortical surface" - # put this in nwbfile.acquisition for testing self.nwbfile.add_acquisition(ct) @@ -536,21 +626,25 @@ def test_constructor(self): es = ExtracellularSeries( name="ExtracellularSeries", data=[0.0, 1.0, 2.0], - timestamps=[0.0, 0.001, 0.0002], + timestamps=[0.0, 0.001, 0.002], channels=channels, - channel_conversion=[1.0, 1.1, 1.2], + channel_conversion=[1.1], conversion=1e5, offset=0.001, unit="volts", # TODO should not have to specify this in init ) + assert es.name == "ExtracellularSeries" + assert es.data == [0.0, 1.0, 2.0] + assert es.timestamps == [0.0, 0.001, 0.002] + assert es.channels is channels + assert es.channel_conversion == [1.1] + assert es.conversion == 1e5 + assert es.offset == 0.001 # NOTE: the TimeSeries mapper maps spec "ExtracellularSeries/data/unit" to "ExtracellularSeries.unit" assert es.unit == "volts" assert es.timestamps_unit == "seconds" - # TODO - # assert ... - class TestExtracellularSeriesRoundTrip(NWBH5IOFlexMixin, TestCase): """Simple roundtrip test for a ExtracellularSeries.""" diff --git a/src/pynwb/tests/test_example_usage_all.py b/src/pynwb/tests/test_example_usage_all.py index 45d04e9..c34ffdc 100644 --- a/src/pynwb/tests/test_example_usage_all.py +++ b/src/pynwb/tests/test_example_usage_all.py @@ -71,17 +71,22 @@ nwbfile.add_device(probe) pi = ProbeInsertion( - reference="Bregma at the cortical surface.", + position_reference="(AP, ML, DV) = (0, 0, 0) corresponds to bregma at the cortical surface.", hemisphere="left", - depth_in_mm=10.0, - # insertion_position_in_mm=[2.0, -4.0, 0.0], # TODO waiting on schema - # insertion_angle_in_deg=[0.0, 0.0, -10.0], + depth_in_mm=10.0, # recommended to provide either `depth_in_mm` or `insertion_position_dv_in_mm` + insertion_position_ap_in_mm=2.0, + insertion_position_ml_in_mm=-4.0, + insertion_angle_roll_in_deg=-10.0, + insertion_angle_pitch_in_deg=0.0, + insertion_angle_yaw_in_deg=0.0, ) channels_table = ChannelsTable( name="Neuropixels1ChannelsTable", # test custom name description="Test channels table", - reference_mode="Reference to channel 2", + reference_mode="Referenced to channel 2.", + position_reference = "(AP, ML, DV) = (0, 0, 0) corresponds to bregma at the cortical surface.", + position_confirmation_method = "Histology", probe=probe, probe_insertion=pi, target_tables={ @@ -96,22 +101,28 @@ contact=0, reference_contact=2, filter="High-pass at 300 Hz", - estimated_position_in_mm=[-1.5, 2.5, -2.5], + estimated_position_ap_in_mm=2.0, + estimated_position_ml_in_mm=-5.0, + estimated_position_dv_in_mm=-9.5, estimated_brain_area="CA3", - confirmed_position_in_mm=[-1.5, 2.4, -2.4], + confirmed_position_ap_in_mm=2.0, + confirmed_position_ml_in_mm=-4.9, + confirmed_position_dv_in_mm=-9.5, confirmed_brain_area="CA3", ) channels_table.add_row( contact=1, reference_contact=2, filter="High-pass at 300 Hz", - estimated_position_in_mm=[-1.5, 2.5, -2.4], + estimated_position_ap_in_mm=2.0, + estimated_position_ml_in_mm=-4.9, + estimated_position_dv_in_mm=-9.3, estimated_brain_area="CA3", - confirmed_position_in_mm=[-1.5, 2.4, -2.3], + confirmed_position_ap_in_mm=2.0, + confirmed_position_ml_in_mm=-4.8, + confirmed_position_dv_in_mm=-9.3, confirmed_brain_area="CA3", ) -channels_table["estimated_position_in_mm"].reference = "Bregma at the cortical surface" -channels_table["confirmed_position_in_mm"].reference = "Bregma at the cortical surface" # put this in nwbfile.acquisition for testing nwbfile.add_acquisition(channels_table) diff --git a/src/spec/create_extension_spec.py b/src/spec/create_extension_spec.py index 36b4124..5f62336 100644 --- a/src/spec/create_extension_spec.py +++ b/src/spec/create_extension_spec.py @@ -8,7 +8,6 @@ NWBAttributeSpec, NWBDatasetSpec, NWBLinkSpec, - NWBDtypeSpec, ) @@ -60,7 +59,8 @@ def main(): name="reference", doc=( "Reference point for the relative position coordinates and information about the " - "coordinate system used." + "coordinate system used, e.g., which direction is positive in the x direction " + "(first element), which direction is positive in the y direction (second element), etc." ), dtype="text", required=False, # TODO should this be required? @@ -163,7 +163,7 @@ def main(): neurodata_type_def="ProbeModel", neurodata_type_inc="Device", doc=("Neural probe object, compatible with the ProbeInterface specification. The name of the object should " - "be the model name of the probe, e.g., 'Neuropixels 1.0'."), + 'be the model name of the probe, e.g., "Neuropixels 1.0".'), groups=[ NWBGroupSpec( name="contacts_table", @@ -179,7 +179,7 @@ def main(): # devices in the NWB file, and users may decide to use a more descriptive name than just # the model name name="model", - doc="Name of the model of the probe, e.g., 'Neuropixels 1.0'.", + doc='Name of the model of the probe, e.g., "Neuropixels 1.0".', dtype="text", ), NWBAttributeSpec( @@ -192,6 +192,7 @@ def main(): dtype="float", dims=[["num_points", "x, y"], ["num_points", "x, y, z"]], shape=[[None, 2], [None, 3]], + required=False, ), ], ) @@ -206,42 +207,41 @@ def main(): default_name="probe_insertion", attributes=[ # TODO waiting on https://github.com/hdmf-dev/hdmf/issues/1099 to add these attributes - # NWBAttributeSpec( - # name="insertion_position_in_mm", - # doc=( - # "Stereotactic coordinates (AP, ML, DV) of where the probe was inserted, in millimeters. " - # "AP = anteroposterior coordinate in mm (+ is anterior). " - # "ML = mediolateral coordinate in mm (+ is right). " - # "DV = dorsoventral coordinate in mm (+ is up)." - # "Coordinates are relative to `reference`" - # ), - # dtype=[ - # NWBDtypeSpec( - # name="ap", - # dtype="float", - # doc="Anteroposterior coordinate in mm, relative to `reference` (+ is anterior).", - # ), - # NWBDtypeSpec( - # name="ml", - # dtype="float", - # doc="Mediolateral coordinate in mm, relative to `reference` (+ is right).", - # ), - # NWBDtypeSpec( - # name="dv", - # dtype="float", - # doc="Dorsoventral coordinate in mm, relative to `reference` (+ is up).", - # ), - # ], - # required=False, - # ), NWBAttributeSpec( - name="reference", + name="insertion_position_ap_in_mm", + doc=( + "Anteroposterior (AP) stereotactic coordinate of where the probe was inserted, in millimeters. " + "+ is anterior. Coordinate is relative to the zero-point described in `position_reference`." + ), + dtype="float", + required=False, + ), + NWBAttributeSpec( + name="insertion_position_ml_in_mm", doc=( - "Reference point for `insertion_position_in_mm` coordinates, e.g., " - '"bregma at the cortical surface".' + "Mediolateral (ML) stereotactic coordinate of where the probe was inserted, in millimeters. " + "+ is right. Coordinate is relative to the zero-point described in `position_reference`." + ), + dtype="float", + required=False, + ), + NWBAttributeSpec( + name="insertion_position_dv_in_mm", + doc=( + "Dorsoventral (DV) stereotactic coordinate of where the probe was inserted, in millimeters. " + "+ is up. Coordinate is relative to the zero-point described in `position_reference`." + ), + dtype="float", + required=False, + ), + NWBAttributeSpec( + name="position_reference", + doc=( + "Location of the origin (0, 0, 0) for `insertion_position_{X}_in_mm` coordinates, e.g., " + '"(AP, ML, DV) = (0, 0, 0) corresponds to bregma at the cortical surface".' ), dtype="text", - required=False, # TODO should this be required? + required=False, ), NWBAttributeSpec( name="hemisphere", # TODO this is useful to cache but could be done at the API level @@ -253,42 +253,47 @@ def main(): dtype="text", required=False, ), - # NWBAttributeSpec( - # name="insertion_angle_in_deg", - # doc=( - # "The angles (pitch, yaw, roll) of the probe at the time of insertion, in degrees. " - # "Pitch = rotation around left-right axis, like nodding (+ is rotating the nose upward). " - # "Yaw = rotation around dorsal-ventral axis, like shaking (+ is rotating the nose rightward). " - # "Roll = rotation around anterior-posterior axis, like tilting (+ is rotating the right side " - # "downward). " - # ), - # dtype=[ - # NWBDtypeSpec( - # name="pitch", - # dtype="float", - # doc="Rotation around left-right axis, like nodding (+ is rotating the nose upward).", - # ), - # NWBDtypeSpec( - # name="yaw", - # dtype="float", - # doc="Rotation around dorsal-ventral axis, like shaking (+ is rotating the nose rightward).", - # ), - # NWBDtypeSpec( - # name="roll", - # dtype="float", - # doc=( - # "Rotation around anterior-posterior axis, like tilting (+ is rotating the right side " - # "downward)." - # ), - # ), - # ], - # required=False, - # ), + NWBAttributeSpec( + name="insertion_angle_pitch_in_deg", + doc=( + "The pitch angle of the probe at the time of insertion, in degrees. " + "Pitch = rotation around left-right axis, like nodding (+ is rotating the nose upward). " + "Zero is defined as the probe being parallel to an axial slice of the brain." + "Yaw = rotation around dorsal-ventral axis, like shaking (+ is rotating the nose rightward). " + "Roll = rotation around anterior-posterior axis, like tilting (+ is rotating the right side " + "downward)." + ), + dtype="float", + required=False, + ), + NWBAttributeSpec( + name="insertion_angle_yaw_in_deg", + doc=( + "The yaw angle of the probe at the time of insertion, in degrees. " + "Yaw = rotation around dorsal-ventral axis, like shaking (+ is rotating the nose rightward). " + "Zero is defined as the probe being parallel to an sagittal slice of the brain." + ), + dtype="float", + required=False, + ), + NWBAttributeSpec( + name="insertion_angle_roll_in_deg", + doc=( + "The roll angle of the probe at the time of insertion, in degrees. " + "Roll = rotation around anterior-posterior axis, like tilting (+ is rotating the right side " + "downward). Zero is defined as the probe being parallel to a coronal slice of the brain. " + ), + dtype="float", + required=False, + ), NWBAttributeSpec( name="depth_in_mm", doc=( "Depth that the probe was driven along `insertion_angle` starting from " - "`insertion_position_in_mm`, in millimeters." + "`insertion_position_ap_in_mm` and `insertion_position_ml_in_mm`, in millimeters. This is an " + "alternate method of providing the dorsal-ventral coordinate of the probe insertion site. If " + "both `insertion_position_dv_in_mm` and `depth_in_mm` are provided, the values should be " + "consistent." ), dtype="float", required=False, @@ -328,97 +333,82 @@ def main(): dtype="text", doc=( "The filter used on the raw (wideband) voltage data from this contact, including the filter " - "name and frequency cutoffs, e.g., 'High-pass filter at 300 Hz.'" + 'name and frequency cutoffs, e.g., "High-pass filter at 300 Hz."' ), quantity="?", ), NWBDatasetSpec( - name="estimated_position_in_mm", + name="estimated_position_ap_in_mm", neurodata_type_inc="VectorData", doc=( - "Stereotactic coordinates (AP, ML, DV) of the estimated contact position, in millimeters. " - "AP = anteroposterior coordinate in mm (+ is anterior). " - "ML = mediolateral coordinate in mm (+ is right). " - "DV = dorsoventral coordinate in mm (+ is up)." - "Coordinates are relative to `reference`" + "Anteroposterior (AP) stereotactic coordinate of the estimated contact position, in millimeters. " + "+ is anterior. Coordinate is relative to the zero-point described in `position_reference`." ), - dtype=[ - NWBDtypeSpec( - name="ap", - dtype="float", - doc="Anteroposterior coordinate in mm, relative to `reference` (+ is anterior).", - ), - NWBDtypeSpec( - name="ml", - dtype="float", - doc="Mediolateral coordinate in mm, relative to `reference` (+ is right).", - ), - NWBDtypeSpec( - name="dv", - dtype="float", - doc="Dorsoventral coordinate in mm, relative to `reference` (+ is up).", - ), - ], + dtype="float", + quantity="?", + ), + NWBDatasetSpec( + name="estimated_position_ml_in_mm", + neurodata_type_inc="VectorData", + doc=( + "Mediolateral (ML) stereotactic coordinate of the estimated contact position, in millimeters. " + "+ is right. Coordinate is relative to the zero-point described in `position_reference`." + ), + dtype="float", + quantity="?", + ), + NWBDatasetSpec( + name="estimated_position_dv_in_mm", + neurodata_type_inc="VectorData", + doc=( + "Dorsoventral (DV) stereotactic coordinate of the estimated contact position, in millimeters. " + "+ is up. Coordinate is relative to the zero-point described in `position_reference`." + ), + dtype="float", quantity="?", - attributes=[ - NWBAttributeSpec( - name="reference", - doc=('Reference point for the position coordinates. e.g., "bregma at the cortical surface".'), - dtype="text", - required=False, # TODO should this be required? - ) - ], ), NWBDatasetSpec( name="estimated_brain_area", neurodata_type_inc="VectorData", dtype="text", - doc=("The brain area of the estimated contact position, e.g., 'CA1'."), + doc=('The brain area of the estimated contact position, e.g., "CA1".'), quantity="?", ), NWBDatasetSpec( - name="confirmed_position_in_mm", + name="confirmed_position_ap_in_mm", neurodata_type_inc="VectorData", doc=( - "Stereotactic coordinates (AP, ML, DV) of the the verified actual contact position, such as from " - "histology, in millimeters. " - "AP = anteroposterior coordinate in mm (+ is anterior). " - "ML = mediolateral coordinate in mm (+ is right). " - "DV = dorsoventral coordinate in mm (+ is up)." - "Coordinates are relative to `reference`" + "Anteroposterior (AP) stereotactic coordinate of the confirmed contact position, in millimeters. " + "+ is anterior. Coordinate is relative to the zero-point described in `position_reference`." ), - dtype=[ - NWBDtypeSpec( - name="ap", - dtype="float", - doc="Anteroposterior coordinate in mm, relative to `reference` (+ is anterior).", - ), - NWBDtypeSpec( - name="ml", - dtype="float", - doc="Mediolateral coordinate in mm, relative to `reference` (+ is right).", - ), - NWBDtypeSpec( - name="dv", - dtype="float", - doc="Dorsoventral coordinate in mm, relative to `reference` (+ is up).", - ), - ], + dtype="float", + quantity="?", + ), + NWBDatasetSpec( + name="confirmed_position_ml_in_mm", + neurodata_type_inc="VectorData", + doc=( + "Mediolateral (ML) stereotactic coordinate of the confirmed contact position, in millimeters. " + "+ is right. Coordinate is relative to the zero-point described in `position_reference`." + ), + dtype="float", + quantity="?", + ), + NWBDatasetSpec( + name="confirmed_position_dv_in_mm", + neurodata_type_inc="VectorData", + doc=( + "Dorsoventral (DV) stereotactic coordinate of the confirmed contact position, in millimeters. " + "+ is up. Coordinate is relative to the zero-point described in `position_reference`." + ), + dtype="float", quantity="?", - attributes=[ - NWBAttributeSpec( - name="reference", - doc=('Reference point for the position coordinates. e.g., "bregma at the cortical surface".'), - dtype="text", - required=False, # TODO should this be required? - ) - ], ), NWBDatasetSpec( name="confirmed_brain_area", neurodata_type_inc="VectorData", dtype="text", - doc=("The brain area of the actual contact position, e.g., 'CA1'."), + doc=('The brain area of the actual contact position, e.g., "CA1".'), quantity="?", ), ], @@ -430,9 +420,24 @@ def main(): ), ], attributes=[ + NWBAttributeSpec( + name="position_reference", + doc=("Location of the origin (0, 0, 0) for `{X}_position_{Y}_in_mm` coordinates, e.g., " + '"(AP, ML, DV) = (0, 0, 0) corresponds to bregma at the cortical surface".'), + dtype="text", + required=False, + ), NWBAttributeSpec( name="reference_mode", - doc="The reference mode used for the recording; e.g. 'external wire', 'common reference'.", + doc=('The reference mode used for the recording; e.g., "external wire in CSF", ' + 'common reference", "skull screw over frontal cortex".'), + dtype="text", + required=False, + ), + NWBAttributeSpec( + name="position_confirmation_method", + doc=("Description of the method used to confirm the position of the contacts or brain area, " + 'e.g., "histology", "MRI".'), dtype="text", required=False, ),