From d0d3eccb673c4a92b1bf0796e431324b8858a9b3 Mon Sep 17 00:00:00 2001 From: rly Date: Sat, 29 Jun 2024 23:49:33 -0700 Subject: [PATCH] Rearrange spec, update readme --- README.md | 93 +++++++++++-------- ...ndx-extracellular-channels.extensions.yaml | 44 ++++----- src/spec/create_extension_spec.py | 62 ++++++------- 3 files changed, 107 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index 65399b3..23b95fe 100644 --- a/README.md +++ b/README.md @@ -79,10 +79,14 @@ classDiagram direction LR class ExtracellularSeries { - <> + <> + data : numeric + --> unit : str = "microvolts" channels : DynamicTableRegion --> target : ChannelsTable + channel_conversion : List[float], optional + --> axis : int = 1 } class ChannelsTable{ @@ -94,38 +98,46 @@ classDiagram description : str probe : ProbeModel probe_insertion : ProbeInsertion, optional - contacts : DynamicTableRegion, optional? - --> target : ContactsTable - reference_contact : DynamicTableRegion, optional - --> target : ContactsTable - reference_mode : Literal["external wire", ...], optional + position_reference : str, optional + reference_mode : str, optional + position_confirmation_method : str, optional -------------------------------------- columns -------------------------------------- - id : int - filter : VectorData, optional - ---> Values strings such as "Bandpass 0-300 Hz". - contact_position [x, y, z] : VectorData, optional - ---> Each value is length 3 tuple of floats. - brain_area : VectorData, optional - --> data : str - ----> Plays the role of the old 'location'. - ... Any other custom columns, such analong frontend e.g. ADC information + id : VectorData[int] + contact : DynamicTableRegion, optional + --> target : ContactsTable + reference_contact : DynamicTableRegion, optional + --> target : ContactsTable + filter : VectorData[str], optional + ---> Strings such as "Bandpass 0-300 Hz". + estimated_position_ap_in_mm : VectorData[float], optional + estimated_position_ml_in_mm : VectorData[float], optional + estimated_position_dv_in_mm : VectorData[float], optional + estimated_brain_area : VectorData[str], optional + confirmed_position_ap_in_mm : VectorData[float], optional + confirmed_position_ml_in_mm : VectorData[float], optional + confirmed_position_dv_in_mm : VectorData[float], optional + confirmed_brain_area : VectorData[str], optional + ... Any other custom columns, e.g., ADC information } class ProbeInsertion { <> - - insertion_position : Tuple[float, float, float], optional - ----> Stereotactic coordinates on surface. + insertion_position_ap_in_mm : float, optional + insertion_position_ml_in_mm : float, optional + insertion_position_dv_in_mm : float, optional + position_reference : str, optional + hemisphere : Literal["left", "right"], optional + insertion_angle_pitch_in_deg : float, optional + insertion_angle_roll_in_deg : float, optional + insertion_angle_yaw_in_deg : float, optional depth_in_um : float, optional - insertion_angle : Tuple[float, float, float], optional - ----> The pitch/roll/yaw relative to the position on the surface. } - namespace ProbeInterface{ + namespace ProbeInterface { class Probe { <> @@ -135,16 +147,17 @@ classDiagram } class ProbeModel { - <> + <> name : str manufacturer : str model : str - contour : List[Tuple[float, float], Tuple[float, float, float]] - contact_table : ContactsTable + ndim : int, optional + planar_contour_in_um : List[Tuple[float, float], Tuple[float, float, float]], optional + contacts_table : ContactsTable } - class ContactTable { + class ContactsTable { <> -------------------------------------- @@ -156,23 +169,25 @@ classDiagram -------------------------------------- columns -------------------------------------- - id : int - shape : str, optional - size : str, optional - shank_id : str, optional - relative_position : List[Tuple[float, float], Tuple[float, float, float]], optional + id : VectorData[int] + relative_position_in_mm : List[Tuple[float, float], Tuple[float, float, float]] + --> reference : str, optional + contact_id : VectorData[str], optional + device_channel : VectorData[int], optional + shank_id : VectorData[str], optional + plane_axes : List[Tuple[int, int], Tuple[int, int, int]], optional + shape : VectorData[str], optional + radius_in_um : VectorData[float], optional + width_in_um : VectorData[float], optional + height_in_um : VectorData[float], optional } } - - - ExtracellularSeries ..> ChannelsTable : links with channels - ProbeModel *--> ContactTable : contains - Probe *..> ProbeModel : links with probe_model - ChannelsTable *..> Probe : links with probe - - ChannelsTable ..> ContactTable : links with contacts - + Probe *..> ProbeModel : links to probe_model + ProbeModel *--> ContactsTable : contains + ExtracellularSeries ..> ChannelsTable : links to channels + ChannelsTable *..> Probe : links to probe + ChannelsTable ..> ContactTable : links to contacts ChannelsTable *--> ProbeInsertion: might contain ProbeInsertion note for ChannelsTable "ChannelsTable is no longer global" ``` diff --git a/spec/ndx-extracellular-channels.extensions.yaml b/spec/ndx-extracellular-channels.extensions.yaml index cdf0cfc..840d079 100644 --- a/spec/ndx-extracellular-channels.extensions.yaml +++ b/spec/ndx-extracellular-channels.extensions.yaml @@ -27,15 +27,21 @@ groups: (first element), which direction is positive in the y direction (second element), etc. required: false - - name: shape - neurodata_type_inc: VectorData - dtype: text - doc: Shape of the contact; e.g. 'circle' - name: contact_id neurodata_type_inc: VectorData dtype: text doc: Unique ID of the contact quantity: '?' + - name: device_channel + neurodata_type_inc: VectorData + dtype: int + doc: 'Index of the channel connected to the contact on the device. Probes can + have a complex contact indexing system due to the probe layout. When they are + plugged into a recording device like an Open Ephys with an Intan headstage, + the channel order can be mixed again. So the physical contact channel index + is rarely the channel index on the device. See the probeinterface tutorial on + automatic wiring for an example: https://probeinterface.readthedocs.io/en/main/examples/ex_11_automatic_wiring.html#sphx-glr-examples-ex-11-automatic-wiring-py' + quantity: '?' - name: shank_id neurodata_type_inc: VectorData dtype: text @@ -61,6 +67,10 @@ groups: doc: The axes defining the contact plane. See 'contact_plane_axes' in https://probeinterface.readthedocs.io/en/main/format_spec.html for more details. quantity: '?' + - name: shape + neurodata_type_inc: VectorData + dtype: text + doc: Shape of the contact; e.g. 'circle' - name: radius_in_um neurodata_type_inc: VectorData dtype: float @@ -76,16 +86,6 @@ groups: dtype: float doc: Height of a rectangular contact, in micrometers. quantity: '?' - - name: device_channel - neurodata_type_inc: VectorData - dtype: int - doc: 'Index of the channel connected to the contact on the device. Probes can - have a complex contact indexing system due to the probe layout. When they are - plugged into a recording device like an Open Ephys with an Intan headstage, - the channel order can be mixed again. So the physical contact channel index - is rarely the channel index on the device. See the probeinterface tutorial on - automatic wiring for an example: https://probeinterface.readthedocs.io/en/main/examples/ex_11_automatic_wiring.html#sphx-glr-examples-ex-11-automatic-wiring-py' - quantity: '?' - neurodata_type_def: ProbeModel neurodata_type_inc: Device doc: Neural probe object, compatible with the ProbeInterface specification. The @@ -157,6 +157,14 @@ groups: in millimeters. + is up. Coordinate is relative to the zero-point described in `position_reference`. required: false + - name: depth_in_mm + dtype: float + 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 - name: position_reference dtype: text doc: Location of the origin (0, 0, 0) for `insertion_position_{X}_in_mm` coordinates, @@ -190,14 +198,6 @@ groups: 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_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 default_name: ChannelsTable diff --git a/src/spec/create_extension_spec.py b/src/spec/create_extension_spec.py index 5f62336..e482ce0 100644 --- a/src/spec/create_extension_spec.py +++ b/src/spec/create_extension_spec.py @@ -68,16 +68,24 @@ def main(): ], ), NWBDatasetSpec( - name="shape", + name="contact_id", # id is already used by DynamicTable neurodata_type_inc="VectorData", - doc="Shape of the contact; e.g. 'circle'", + doc="Unique ID of the contact", dtype="text", + quantity="?", ), NWBDatasetSpec( - name="contact_id", # id is already used by DynamicTable + # NOTE: cannot end this name with "_index" because it conflicts with ragged arrays + name="device_channel", neurodata_type_inc="VectorData", - doc="Unique ID of the contact", - dtype="text", + doc=("Index of the channel connected to the contact on the device. " + "Probes can have a complex contact indexing system due to the probe layout. " + "When they are plugged into a recording device like an Open Ephys with an Intan headstage, " + "the channel order can be mixed again. So the physical contact channel index " + "is rarely the channel index on the device. See the probeinterface tutorial on automatic " + "wiring for an example: " + "https://probeinterface.readthedocs.io/en/main/examples/ex_11_automatic_wiring.html#sphx-glr-examples-ex-11-automatic-wiring-py"), + dtype="int", quantity="?", ), NWBDatasetSpec( @@ -100,6 +108,12 @@ def main(): shape=[[None, 2, 2], [None, 2, 3]], quantity="?", ), + NWBDatasetSpec( + name="shape", + neurodata_type_inc="VectorData", + doc="Shape of the contact; e.g. 'circle'", + dtype="text", + ), NWBDatasetSpec( name="radius_in_um", neurodata_type_inc="VectorData", @@ -121,20 +135,6 @@ def main(): dtype="float", quantity="?", ), - NWBDatasetSpec( - # NOTE: cannot end this name with "_index" because it conflicts with ragged arrays - name="device_channel", - neurodata_type_inc="VectorData", - doc=("Index of the channel connected to the contact on the device. " - "Probes can have a complex contact indexing system due to the probe layout. " - "When they are plugged into a recording device like an Open Ephys with an Intan headstage, " - "the channel order can be mixed again. So the physical contact channel index " - "is rarely the channel index on the device. See the probeinterface tutorial on automatic " - "wiring for an example: " - "https://probeinterface.readthedocs.io/en/main/examples/ex_11_automatic_wiring.html#sphx-glr-examples-ex-11-automatic-wiring-py"), - dtype="int", - quantity="?", - ), ], ) @@ -234,6 +234,18 @@ def main(): dtype="float", required=False, ), + NWBAttributeSpec( + name="depth_in_mm", + 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." + ), + dtype="float", + required=False, + ), NWBAttributeSpec( name="position_reference", doc=( @@ -286,18 +298,6 @@ def main(): dtype="float", required=False, ), - NWBAttributeSpec( - name="depth_in_mm", - 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." - ), - dtype="float", - required=False, - ), ], )