From 1880684f33c220c502283dba88a458739df9174e Mon Sep 17 00:00:00 2001 From: Alessandra Trapani <55453048+alessandratrapani@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:14:06 +0100 Subject: [PATCH] Change quantitative properties to add units to name (#6) * add new spec (not working for the stimulated_rois) * tests not working * fix testing * define what is required * improve tutorial * add explaination for stimulus table * add testing for power,frequency and pulse_width as 1D arrays * set defaults for power,frequency and pulse_width * add schema diagram * remove unnecessary imports * add targeted_rois (required) and segmented_rois (optional) * remove unnecessary imports * remove old schema * update schema * minor fixes * add elements to __all__ to count as used * add exception to ruff * remove space * adjust to proper sentences * Update spec/ndx-patterned-ogen.extensions.yaml Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> * Update spec/ndx-patterned-ogen.extensions.yaml Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> * Update spec/ndx-patterned-ogen.extensions.yaml Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> * Update spec/ndx-patterned-ogen.extensions.yaml Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> * Update spec/ndx-patterned-ogen.extensions.yaml Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> * Update spec/ndx-patterned-ogen.extensions.yaml Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> * Update spec/ndx-patterned-ogen.extensions.yaml Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> * Update spec/ndx-patterned-ogen.extensions.yaml Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> * Improve readability of doc strings * targeted_rois defined as table region * add example for slm model and filter_description * split SLM in 2 device SLM2D and SLM3D * split OgenPattern in 2D and 3D * update tutorial and schema * minor fixes * update schema * set sweeep_size and sweep_mask as datasets * set SpiralScanning and TemporalFocusing properties as datasets * set effector as dataset * set spatial_resolution as dataset * set all LightSource properties as datasets except "model" * update tutorial * set segmented_rois default * change required:false to quantity:'?' for dataset * remove unnecessary class definition * add unit, shape and dims * update tutorial and mock_fun accordingly * minor fixes * Apply suggestions from code review * add unit measure in the name of the attribute --------- Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> --- spec/ndx-patterned-ogen.extensions.yaml | 134 ++++-- src/pynwb/ndx_patterned_ogen/__init__.py | 20 +- .../ndx_patterned_ogen/patterned_ogen.py | 347 +------------- src/pynwb/tests/mock/patternedogen.py | 62 +-- src/pynwb/tests/test_patternedogen.py | 3 +- tutorial_patterned_ogen.ipynb | 429 ++++++++++++++++-- 6 files changed, 538 insertions(+), 457 deletions(-) diff --git a/spec/ndx-patterned-ogen.extensions.yaml b/spec/ndx-patterned-ogen.extensions.yaml index c604e88..6138530 100644 --- a/spec/ndx-patterned-ogen.extensions.yaml +++ b/spec/ndx-patterned-ogen.extensions.yaml @@ -6,22 +6,34 @@ groups: - name: description dtype: text doc: Description of the scanning or scanless method for shaping optogenetic light. Examples include diffraction limited points, 3D shot, disks, etc. - required: false - - name: sweep_size + - name: sweep_size_in_um dtype: numeric doc: - Size of the scanning sweep pattern in micrometers. If a scalar is provided, the sweep pattern is + Size of the scanning sweep pattern (default in micrometers). If a scalar is provided, the sweep pattern is assumed to be a circle (for 2D patterns) with diameter 'sweep_size'. If 'sweep_size' is a two dimensional array, the the sweep pattern is assumed to be a rectangle, with dimensions [width, height]. required: false + dims: + - diameter + - width, height + shape: + - - 1 + - - 2 + datasets: - name: sweep_mask dtype: numeric doc: Scanning sweep pattern designated using a mask of size [width, height] for 2D stimulation, where for a given pixel a value of 1 indicates stimulation, and a value of 0 indicates no stimulation. - required: false + quantity: "?" + dims: + - num_rows + - num_cols + shape: + - + - - neurodata_type_def: OptogeneticStimulus3DPattern neurodata_type_inc: LabMetaData doc: Container to store the information about a generic 3D stimulus pattern (spatial information). @@ -29,58 +41,68 @@ groups: - name: description dtype: text doc: Description of the scanning or scanless method for shaping optogenetic light. Examples include diffraction limited points, 3D shot, disks, etc. - required: false - - name: sweep_size + - name: sweep_size_in_um dtype: numeric doc: - Size of the scanning sweep pattern in micrometers. If a scalar is provided, the sweep pattern is + Size of the scanning sweep pattern (default in micrometers). If a scalar is provided, the sweep pattern is assumed to be a cylinder (for 3D patterns), with diameter 'sweep_size'. If 'sweep_size' is a three dimensional array, the the sweep pattern is assumed to be a cuboid, with dimensions [width, height, depth]. required: false + dims: + - diameter + - width, height, depth + shape: + - - 1 + - - 3 + datasets: - name: sweep_mask dtype: numeric doc: Scanning sweep pattern designated using a mask of size [width,height, depth] for 3D stimulation, where for a given pixel a value of 1 indicates stimulation, and a value of 0 indicates no stimulation. - required: false + quantity: "?" + dims: + - num_rows + - num_cols + - num_planes + shape: + - + - + - - neurodata_type_def: SpiralScanning neurodata_type_inc: LabMetaData doc: Container to store the parameters defining a spiral scanning pattern. attributes: - - name: diameter + - name: diameter_in_um dtype: numeric doc: Spiral diameter (in micrometers). - required: true - name: number_of_revolutions dtype: numeric doc: Number of turns within a spiral. - required: true + - name: height_in_um + dtype: numeric + doc: Spiral height of each sweep (in micrometers). + required: false - name: description dtype: text doc: Describe any additional details about the pattern. required: false - - name: height - dtype: numeric - doc: Spiral height of each sweep (in micrometers). - required: false - neurodata_type_def: TemporalFocusing neurodata_type_inc: LabMetaData doc: Container to store the parameters defining a temporal focusing beam-shaping. attributes: - - name: lateral_point_spread_function + - name: lateral_point_spread_function_in_um dtype: text doc: Estimated lateral spatial profile or point spread function, expressed as mean [um] ± s.d [um]. - required: true - - name: axial_point_spread_function + - name: axial_point_spread_function_in_um dtype: text doc: Estimated axial spatial profile or point spread function, expressed as mean [um] ± s.d [um]. - required: true - name: description dtype: text doc: Describe any additional details about the pattern. @@ -92,12 +114,10 @@ groups: - name: effector dtype: text doc: Light-activated effector protein expressed by the targeted cell (e.g., ChR2). - required: true links: - name: light_source - target_type: LightSource + target_type: Device doc: Light source used to apply photostimulation. - required: true - name: spatial_light_modulator target_type: Device doc: Spatial light modulator used to generate photostimulation pattern. @@ -110,10 +130,14 @@ groups: dtype: text doc: The model specification of the spatial light modulator (e.g. 'X15213 series', from Hamamatsu). required: false - - name: spatial_resolution + - name: spatial_resolution_in_px dtype: numeric doc: Resolution of spatial light modulator (in pixels), formatted as [width, height]. required: false + dims: + - width, height + shape: + - 2 - neurodata_type_def: SpatialLightModulator3D neurodata_type_inc: Device doc: 3D spatial light modulator used in the experiment. @@ -122,46 +146,49 @@ groups: dtype: text doc: The model specification of the spatial light modulator (e.g. 'NeuraLight 3D Ultra', from Bruker). required: false - - name: spatial_resolution + - name: spatial_resolution_in_px dtype: numeric doc: Resolution of spatial light modulator (in pixels), formatted as [width, height, depth]. required: false + dims: + - width, height, depth + shape: + - 3 - neurodata_type_def: LightSource neurodata_type_inc: Device doc: Light source used in the experiment. attributes: - - name: stimulation_wavelength + - name: stimulation_wavelength_in_nm dtype: numeric doc: Excitation wavelength of stimulation light (nanometers). - required: true - - name: model - dtype: text - doc: Model of light source device. - required: false - name: filter_description dtype: text doc: Filter used to obtain the excitation wavelength of stimulation light, e.g. 'Short pass at 1040 nm'. required: false - - name: peak_power + - name: peak_power_in_W dtype: numeric doc: Incident power of stimulation device (in Watts). required: false - - name: peak_pulse_energy + - name: peak_pulse_energy_in_J dtype: numeric doc: If device is pulsed light source, pulse energy (in Joules). required: false - - name: intensity + - name: intensity_in_W_per_m2 dtype: numeric doc: Intensity of the excitation in W/m^2, if known. required: false - - name: exposure_time + - name: exposure_time_in_s dtype: numeric doc: Exposure time of the sample (in sec). required: false - - name: pulse_rate + - name: pulse_rate_in_Hz dtype: numeric doc: If device is pulsed light source, pulse rate (in Hz) used for stimulation. required: false + - name: model + dtype: text + doc: Model of light source device. + required: false - neurodata_type_def: OptogeneticStimulusTarget neurodata_type_inc: LabMetaData doc: Container to store the targated rois in a photostimulation experiment. @@ -169,58 +196,83 @@ groups: - name: targeted_rois neurodata_type_inc: DynamicTableRegion doc: A table region referencing a PlaneSegmentation object storing targeted ROIs. - required: true - name: segmented_rois neurodata_type_inc: DynamicTableRegion doc: A table region referencing a PlaneSegmentation object storing segmented ROIs that receive photostimulation. - required: false + quantity: "?" - neurodata_type_def: PatternedOptogeneticStimulusTable neurodata_type_inc: TimeIntervals doc: Table to hold all patterned optogenetic stimulus onsets. - quantity: "?" datasets: - name: power neurodata_type_inc: VectorData doc: Power (in Watts) defined as a scalar. All rois in target receive the same photostimulation power. quantity: "?" + attributes: + - name: unit + value: Watts + doc: Unit of measure of power, fixed to Watts. + dtype: text - name: power_per_roi neurodata_type_inc: VectorData doc: Power (in Watts) defined as an array. Each power value refers to each roi in target. quantity: "?" + attributes: + - name: unit + value: Watts + doc: Unit of measure of power, fixed to Watts. + dtype: text - name: targets neurodata_type_inc: VectorData dtype: target_type: OptogeneticStimulusTarget reftype: object doc: Targeted rois for the stimulus onset. - required: true - name: stimulus_pattern neurodata_type_inc: VectorData dtype: target_type: LabMetaData reftype: object doc: Link to the stimulus pattern. - required: true - name: stimulus_site neurodata_type_inc: VectorData dtype: target_type: PatternedOptogeneticStimulusSite reftype: object doc: Link to the stimulus site. - required: true - name: frequency neurodata_type_inc: VectorData doc: Frequency (in Hz) defined as a scalar. All rois in target receive the photostimulation at the same frequency. quantity: "?" + attributes: + - name: unit + value: Hertz + doc: Unit of measure of frequency, fixed to Hertz. + dtype: text - name: frequency_per_roi neurodata_type_inc: VectorData doc: Frequency (in Hz) defined as an array. Each frequency value refers to each roi in target. quantity: "?" + attributes: + - name: unit + value: Hertz + doc: Unit of measure of frequency, fixed to Hertz. + dtype: text - name: pulse_width neurodata_type_inc: VectorData doc: Pulse Width (in sec/phase) defined as a scalar. All rois in target receive the photostimulation with the same pulse width. quantity: "?" + attributes: + - name: unit + value: seconds/phase + doc: Unit of measure of power, fixed to seconds per phase. + dtype: text - name: pulse_width_per_roi neurodata_type_inc: VectorData doc: Pulse Width (in sec/phase) defined as an array. Each pulse width value refers to each roi in target. quantity: "?" + attributes: + - name: unit + value: seconds/phase + doc: Unit of measure of power, fixed to seconds per phase. + dtype: text diff --git a/src/pynwb/ndx_patterned_ogen/__init__.py b/src/pynwb/ndx_patterned_ogen/__init__.py index 44fa0ef..b9eee51 100644 --- a/src/pynwb/ndx_patterned_ogen/__init__.py +++ b/src/pynwb/ndx_patterned_ogen/__init__.py @@ -1,5 +1,5 @@ import os -from pynwb import load_namespaces +from pynwb import load_namespaces, get_class try: from importlib.resources import files @@ -14,22 +14,24 @@ # If that path does not exist, we are likely running in editable mode. Use the local path instead if not os.path.exists(__spec_path): __spec_path = __location_of_this_file.parent.parent.parent / "spec" / "ndx-patterned-ogen.namespace.yaml" + +load_namespaces(str(__spec_path)) + +SpatialLightModulator2D = get_class('SpatialLightModulator2D', 'ndx-patterned-ogen') +SpatialLightModulator3D = get_class('SpatialLightModulator3D', 'ndx-patterned-ogen') +LightSource = get_class('LightSource', 'ndx-patterned-ogen') +OptogeneticStimulus2DPattern = get_class('OptogeneticStimulus2DPattern', 'ndx-patterned-ogen') +OptogeneticStimulus3DPattern = get_class('OptogeneticStimulus3DPattern', 'ndx-patterned-ogen') +SpiralScanning = get_class('SpiralScanning', 'ndx-patterned-ogen') +TemporalFocusing = get_class('TemporalFocusing', 'ndx-patterned-ogen') # Load the namespace -load_namespaces(str(__spec_path)) from .patterned_ogen import ( - SpatialLightModulator3D, - SpatialLightModulator2D, - LightSource, PatternedOptogeneticStimulusSite, PatternedOptogeneticStimulusTable, - OptogeneticStimulus2DPattern, - OptogeneticStimulus3DPattern, OptogeneticStimulusSite, OptogeneticStimulusTarget, - SpiralScanning, - TemporalFocusing, ) __all__ = [ diff --git a/src/pynwb/ndx_patterned_ogen/patterned_ogen.py b/src/pynwb/ndx_patterned_ogen/patterned_ogen.py index 74195c8..946967a 100644 --- a/src/pynwb/ndx_patterned_ogen/patterned_ogen.py +++ b/src/pynwb/ndx_patterned_ogen/patterned_ogen.py @@ -10,147 +10,6 @@ namespace = "ndx-patterned-ogen" -@register_class("SpatialLightModulator3D", namespace) -class SpatialLightModulator3D(Device): - """ - Spatial light modulator used in the experiment. - """ - - __nwbfields__ = ("model", "spatial_resolution") - - @docval( - {"name": "name", "type": str, "doc": "Name of SpatialLightModulator3D object."}, - *get_docval(Device.__init__, "description", "manufacturer"), - { - "name": "model", - "type": str, - "doc": "The model specification of the spatial light modulator (e.g. 'NeuraLight 3D Ultra', from Bruker).", - }, - { - "name": "spatial_resolution", - "type": Iterable, - "doc": "Resolution of spatial light modulator (in pixels), formatted as [width, height, depth].", - "default": None, - "shape": (3,), - }, - ) - def __init__(self, **kwargs): - keys_to_set = ("model", "spatial_resolution") - args_to_set = popargs_to_dict(keys_to_set, kwargs) - super().__init__(**kwargs) - for key, val in args_to_set.items(): - setattr(self, key, val) - - -@register_class("SpatialLightModulator2D", namespace) -class SpatialLightModulator2D(Device): - """ - Spatial light modulator used in the experiment. - """ - - __nwbfields__ = ("model", "spatial_resolution") - - @docval( - {"name": "name", "type": str, "doc": "Name of SpatialLightModulator3D object. "}, - *get_docval(Device.__init__, "description", "manufacturer"), - { - "name": "model", - "type": str, - "doc": "The model specification of the spatial light modulator (e.g. 'X15213 series', from Hamamatsu).", - }, - { - "name": "spatial_resolution", - "type": Iterable, - "doc": "Resolution of spatial light modulator (in pixels), formatted as [width, height].", - "default": None, - "shape": (2,), - }, - ) - def __init__(self, **kwargs): - keys_to_set = ("model", "spatial_resolution") - args_to_set = popargs_to_dict(keys_to_set, kwargs) - super().__init__(**kwargs) - for key, val in args_to_set.items(): - setattr(self, key, val) - - -@register_class("LightSource", namespace) -class LightSource(Device): - """ - Light source used in the experiment. - """ - - __nwbfields__ = ( - "model", - "stimulation_wavelength", - "peak_power", - "filter_descriptionpeak_pulse_energy", - "intensity", - "pulse_rate", - "exposure_time", - ) - - @docval( - {"name": "name", "type": str, "doc": "Name of LightSource object."}, - *get_docval(Device.__init__, "description", "manufacturer"), - {"name": "model", "type": str, "doc": "Model of light source device."}, - { - "name": "stimulation_wavelength", - "type": (int, float), - "doc": "Excitation wavelength of stimulation light (nanometers).", - "default": None, - }, - { - "name": "peak_power", - "type": (int, float), - "doc": "Incident power of stimulation device (in Watts).", - "default": None, - }, - { - "name": "filter_description", - "type": str, - "doc": ( - "Filter used to obtain the excitation wavelength of stimulation light, e.g. 'Short pass at 1040 nm'." - ), - "default": None, - }, - { - "name": "peak_pulse_energy", - "type": (int, float), - "doc": "If device is pulsed light source, pulse energy (in Joules).", - "default": None, - }, - { - "name": "intensity", - "type": (int, float), - "doc": "Intensity of the excitation in W/m^2, if known.", - "default": None, - }, - { - "name": "pulse_rate", - "type": (int, float), - "doc": "If device is pulsed light source, pulse rate (in Hz) used for stimulation.", - "default": None, - }, - {"name": "exposure_time", "type": (int, float), "doc": "Exposure time of the sample (in sec)", "default": None}, - ) - def __init__(self, **kwargs): - keys_to_set = ( - "model", - "stimulation_wavelength", - "peak_power", - "filter_description", - "peak_pulse_energy", - "intensity", - "pulse_rate", - "exposure_time", - ) - args_to_set = popargs_to_dict(keys_to_set, kwargs) - super().__init__(**kwargs) - for key, val in args_to_set.items(): - setattr(self, key, val) - - @register_class("PatternedOptogeneticStimulusSite", namespace) class PatternedOptogeneticStimulusSite(OptogeneticStimulusSite): """ @@ -169,10 +28,10 @@ class PatternedOptogeneticStimulusSite(OptogeneticStimulusSite): }, { "name": "spatial_light_modulator", - "type": (SpatialLightModulator3D, SpatialLightModulator2D), + "type": Device, "doc": "Spatial light modulator used to generate photostimulation pattern.", }, - {"name": "light_source", "type": LightSource, "doc": "Light source used to apply photostimulation."}, + {"name": "light_source", "type": Device, "doc": "Light source used to apply photostimulation."}, ) def __init__(self, **kwargs): keys_to_set = ("effector", "spatial_light_modulator", "light_source") @@ -181,13 +40,11 @@ def __init__(self, **kwargs): for key, val in args_to_set.items(): setattr(self, key, val) - @docval( - { - "name": "spatial_light_modulator", - "type": (SpatialLightModulator3D, SpatialLightModulator2D), - "doc": "Spatial light modulator used to generate photostimulation pattern. ", - } - ) + @docval({ + "name": "spatial_light_modulator", + "type": Device, + "doc": "Spatial light modulator used to generate photostimulation pattern. ", + }) def add_spatial_light_modulator(self, spatial_light_modulator): """ Add a spatial light modulator to the photostimulation method. @@ -197,7 +54,7 @@ def add_spatial_light_modulator(self, spatial_light_modulator): else: self.spatial_light_modulator = spatial_light_modulator - @docval({"name": "light_source", "type": LightSource, "doc": "Light source used to apply photostimulation."}) + @docval({"name": "light_source", "type": Device, "doc": "Light source used to apply photostimulation."}) def add_light_source(self, light_source): """ Add a light source to the photostimulation method. @@ -245,182 +102,6 @@ def __init__(self, **kwargs): setattr(self, key, val) -@register_class("OptogeneticStimulus2DPattern", namespace) -class OptogeneticStimulus2DPattern(LabMetaData): - """ - Container to store the information about a generic 2D stimulus pattern (spatial information). - """ - - __nwbfields__ = ("description", "sweep_size", "sweep_mask") - - @docval( - *get_docval(LabMetaData.__init__, "name"), - { - "name": "description", - "type": str, - "doc": ( - "Description of the scanning or scanless method for shaping optogenetic light. Examples include" - " diffraction limited points, 3D shot, disks, etc." - ), - }, - { - "name": "sweep_size", - "type": (int, float, Iterable), - "doc": ( - "Size of the scanning sweep pattern in micrometers. If a scalar is provided, the sweep pattern is" - " assumed to be a circle (for 2D patterns) with diameter 'sweep_size'." - " If 'sweep_size' is a two dimensional array, the the sweep pattern is assumed to be a" - " rectangle, with dimensions [width, height]." - ), - "default": None, - }, - { - "name": "sweep_mask", - "type": Iterable, - "doc": ( - "Scanning sweep pattern designated using a mask of size [width, height] for 2D stimulation," - " where for a given pixel a value of 1 indicates stimulation, and a" - " value of 0 indicates no stimulation." - ), - "default": None, - }, - ) - def __init__(self, **kwargs): - keys_to_set = ("description", "sweep_size", "sweep_mask") - args_to_set = popargs_to_dict(keys_to_set, kwargs) - super().__init__(**kwargs) - for key, val in args_to_set.items(): - setattr(self, key, val) - - -@register_class("OptogeneticStimulus3DPattern", namespace) -class OptogeneticStimulus3DPattern(LabMetaData): - """ - Container to store the information about a generic 3D stimulus pattern (spatial information). - """ - - __nwbfields__ = ("description", "sweep_size", "sweep_mask") - - @docval( - *get_docval(LabMetaData.__init__, "name"), - { - "name": "description", - "type": str, - "doc": ( - "Description of the scanning or scanless method for shaping optogenetic light. Examples include" - " diffraction limited points, 3D shot, disks, etc." - ), - }, - { - "name": "sweep_size", - "type": (int, float, Iterable), - "doc": ( - "Size of the scanning sweep pattern in micrometers. If a scalar is provided, the sweep pattern is" - " assumed to be a cylinder (for 3D patterns), with diameter 'sweep_size'." - " If 'sweep_size' is a three dimensional array, the the sweep pattern is assumed to be a" - " cuboid, with dimensions [width, height, depth]." - ), - "default": None, - }, - { - "name": "sweep_mask", - "type": Iterable, - "doc": ( - "Scanning sweep pattern designated using a mask of size [width, height, depth] for 3D stimulation," - " where for a given pixel a value of 1 indicates stimulation, and a" - " value of 0 indicates no stimulation." - ), - "default": None, - }, - ) - def __init__(self, **kwargs): - keys_to_set = ("description", "sweep_size", "sweep_mask") - args_to_set = popargs_to_dict(keys_to_set, kwargs) - super().__init__(**kwargs) - for key, val in args_to_set.items(): - setattr(self, key, val) - - -@register_class("TemporalFocusing", namespace) -class TemporalFocusing(LabMetaData): - """ - Container to store the parameters defining a temporal focusing beam-shaping - """ - - __nwbfields__ = ("description", "lateral_point_spread_function", "axial_point_spread_function") - - @docval( - *get_docval(LabMetaData.__init__, "name"), - { - "name": "description", - "type": str, - "doc": "Describe any additional details about the pattern.", - "default": None, - }, - { - "name": "lateral_point_spread_function", - "type": str, - "doc": "Estimated lateral spatial profile or point spread function, expressed as mean [um] ± s.d [um].", - "default": None, - }, - { - "name": "axial_point_spread_function", - "type": str, - "doc": "Estimated axial spatial profile or point spread function, expressed as mean [um] ± s.d [um]", - "default": None, - }, - ) - def __init__(self, **kwargs): - keys_to_set = ("description", "lateral_point_spread_function", "axial_point_spread_function") - args_to_set = popargs_to_dict(keys_to_set, kwargs) - super().__init__(**kwargs) - for key, val in args_to_set.items(): - setattr(self, key, val) - - -@register_class("SpiralScanning", namespace) -class SpiralScanning(LabMetaData): - """ - Container to store the parameters defining a spiral scanning pattern. - """ - - __nwbfields__ = ("description", "diameter", "height", "number_of_revolutions") - - @docval( - *get_docval(LabMetaData.__init__, "name"), - { - "name": "description", - "type": str, - "doc": "Describe any additional details about the pattern.", - "default": None, - }, - { - "name": "diameter", - "type": (int, float), - "doc": "Spiral diameter (in micrometers).", - "default": None, - }, - { - "name": "height", - "type": (int, float), - "doc": "Spiral height of each sweep (in micrometers).", - "default": None, - }, - { - "name": "number_of_revolutions", - "type": int, - "doc": "Number of turns within a spiral.", - "default": None, - }, - ) - def __init__(self, **kwargs): - keys_to_set = ("description", "diameter", "height", "number_of_revolutions") - args_to_set = popargs_to_dict(keys_to_set, kwargs) - super().__init__(**kwargs) - for key, val in args_to_set.items(): - setattr(self, key, val) - - @register_class("PatternedOptogeneticStimulusTable", namespace) class PatternedOptogeneticStimulusTable(TimeIntervals): """ @@ -429,8 +110,8 @@ class PatternedOptogeneticStimulusTable(TimeIntervals): __fields__ = () __columns__ = ( - {"name": "start_time", "description": "Start time of stimulation, in seconds", "required": True}, - {"name": "stop_time", "description": "Stop time of stimulation, in seconds", "required": True}, + {"name": "start_time", "description": "Start time of stimulation, in seconds"}, + {"name": "stop_time", "description": "Stop time of stimulation, in seconds"}, { "name": "power", "description": ( @@ -471,16 +152,14 @@ class PatternedOptogeneticStimulusTable(TimeIntervals): ), "required": False, }, - {"name": "targets", "description": "Targeted rois for the stimulus onset.", "required": True}, + {"name": "targets", "description": "Targeted rois for the stimulus onset."}, { "name": "stimulus_pattern", "description": "Link to the stimulus pattern.", - "required": True, }, { "name": "stimulus_site", "description": "Link to the stimulus site.", - "required": True, }, ) @@ -492,7 +171,7 @@ def check_if_argument_is_not_scalar(cls, colset, field_name): f"{field_name} should be defined as scalar. Use '{field_name}_per_roi' to store photostimulation" f" at different {field_name}, for each rois in target." ) - + @classmethod def check_length_rois_properties(cls, colset, field_name): for row in range(len(colset[field_name])): @@ -602,7 +281,7 @@ def __init__(self, **kwargs): { "name": "stimulus_pattern", "doc": "Link to the stimulus pattern.", - "type": (OptogeneticStimulus3DPattern, OptogeneticStimulus2DPattern, TemporalFocusing, SpiralScanning), + "type": LabMetaData, }, { "name": "stimulus_site", diff --git a/src/pynwb/tests/mock/patternedogen.py b/src/pynwb/tests/mock/patternedogen.py index 1281360..04a8784 100644 --- a/src/pynwb/tests/mock/patternedogen.py +++ b/src/pynwb/tests/mock/patternedogen.py @@ -26,7 +26,7 @@ def mock_OptogeneticStimulus2DPattern( name: Optional[str] = None, description: str = "Generic description for optogenetic stimulus 2D pattern", - sweep_size: float = 5, # um + sweep_size_in_um: list = [5], # um sweep_mask=np.zeros((10, 10)), nwbfile: Optional[NWBFile] = None, ) -> OptogeneticStimulus2DPattern: @@ -34,7 +34,7 @@ def mock_OptogeneticStimulus2DPattern( name=name or name_generator("OptogeneticStimulus2DPattern"), description=description, sweep_mask=sweep_mask, - sweep_size=sweep_size, + sweep_size_in_um=sweep_size_in_um, ) nwbfile.add_lab_meta_data(stimulus_pattern) return stimulus_pattern @@ -43,7 +43,7 @@ def mock_OptogeneticStimulus2DPattern( def mock_OptogeneticStimulus3DPattern( name: Optional[str] = None, description: str = "Generic description for optogenetic stimulus 3D pattern", - sweep_size: float = 5, # um + sweep_size_in_um: list = [5], # um sweep_mask=np.zeros((10, 10, 2)), nwbfile: Optional[NWBFile] = None, ) -> OptogeneticStimulus3DPattern: @@ -51,7 +51,7 @@ def mock_OptogeneticStimulus3DPattern( name=name or name_generator("OptogeneticStimulus3DPattern"), description=description, sweep_mask=sweep_mask, - sweep_size=sweep_size, + sweep_size_in_um=sweep_size_in_um, ) nwbfile.add_lab_meta_data(stimulus_pattern) return stimulus_pattern @@ -60,15 +60,15 @@ def mock_OptogeneticStimulus3DPattern( def mock_TemporalFocusing( name: Optional[str] = None, description: str = "Generic description for optogenetic stimulus pattern", - lateral_point_spread_function: str = "9e-6 m ± 0.7e-6 m", - axial_point_spread_function: str = "32e-6 m ± 1.6e-6 m", + lateral_point_spread_function_in_um: str = "9e-6 m ± 0.7e-6 m", + axial_point_spread_function_in_um: str = "32e-6 m ± 1.6e-6 m", nwbfile: Optional[NWBFile] = None, ) -> TemporalFocusing: stimulus_pattern_temporal_focusing = TemporalFocusing( name=name or name_generator("TemporalFocusing"), description=description, - lateral_point_spread_function=lateral_point_spread_function, - axial_point_spread_function=axial_point_spread_function, + lateral_point_spread_function_in_um=lateral_point_spread_function_in_um, + axial_point_spread_function_in_um=axial_point_spread_function_in_um, ) nwbfile.add_lab_meta_data(stimulus_pattern_temporal_focusing) return stimulus_pattern_temporal_focusing @@ -77,16 +77,16 @@ def mock_TemporalFocusing( def mock_SpiralScanning( name: Optional[str] = None, description: str = "Generic description for optogenetic stimulus pattern", - diameter: float = 15e-6, # diameter of a single spiral - height: float = 10e-6, # height of a single spira (if 3D pattern) + diameter_in_um: float = 15e-6, # diameter_in_um of a single spiral + height_in_um: float = 10e-6, # height_in_um of a single spira (if 3D pattern) number_of_revolutions: float = 5, # number of revolution of a single spira nwbfile: Optional[NWBFile] = None, ) -> SpiralScanning: stimulus_pattern_spiral_scanning = SpiralScanning( name=name or name_generator("SpiralScanning"), description=description, - diameter=diameter, - height=height, + diameter_in_um=diameter_in_um, + height_in_um=height_in_um, number_of_revolutions=number_of_revolutions, ) nwbfile.add_lab_meta_data(stimulus_pattern_spiral_scanning) @@ -97,28 +97,28 @@ def mock_LightSource( name: Optional[str] = None, manufacturer: Optional[str] = None, model: Optional[str] = "laser model", - stimulation_wavelength: float = 1035.0, # nm + stimulation_wavelength_in_nm: float = 1035.0, # nm description: str = "Generic description for the laser", - peak_power: float = 0.70, # the peak power of stimulation in Watts + peak_power_in_W: float = 0.70, # the peak power of stimulation in Watts filter_description: str = "Short pass at 1040 nm", - peak_pulse_energy: float = 0.70, - intensity: float = 0.005, # the intensity of excitation in W/mm^2 - exposure_time: float = 2.51e-13, # the exposure time of the sample in seconds - pulse_rate: float = 2.0e6, # the pulse rate of the laser is in Hz + peak_pulse_energy_in_J: float = 0.70, + intensity_in_W_per_m2: float = 0.005, # the intensity of excitation in W/mm^2 + exposure_time_in_s: float = 2.51e-13, # the exposure time of the sample in seconds + pulse_rate_in_Hz: float = 2.0e6, # the pulse rate of the laser is in Hz nwbfile: Optional[NWBFile] = None, ) -> LightSource: light_source = LightSource( name=name or name_generator("LightSource"), manufacturer=manufacturer, model=model, - stimulation_wavelength=stimulation_wavelength, + stimulation_wavelength_in_nm=stimulation_wavelength_in_nm, filter_description=filter_description, description=description, - peak_pulse_energy=peak_pulse_energy, - peak_power=peak_power, - intensity=intensity, - exposure_time=exposure_time, - pulse_rate=pulse_rate, + peak_pulse_energy_in_J=peak_pulse_energy_in_J, + peak_power_in_W=peak_power_in_W, + intensity_in_W_per_m2=intensity_in_W_per_m2, + exposure_time_in_s=exposure_time_in_s, + pulse_rate_in_Hz=pulse_rate_in_Hz, ) nwbfile.add_device(light_source) return light_source @@ -129,7 +129,7 @@ def mock_SpatialLightModulator2D( description: str = "Generic description for the spatial light modulator device", model: str = "Generic model for the spatial light modulator device", manufacturer: Optional[str] = None, - spatial_resolution: list = [100, 100], + spatial_resolution_in_px: list = [100, 100], nwbfile: Optional[NWBFile] = None, ) -> SpatialLightModulator2D: spatial_light_modulator = SpatialLightModulator2D( @@ -137,7 +137,7 @@ def mock_SpatialLightModulator2D( description=description, model=model, manufacturer=manufacturer, - spatial_resolution=spatial_resolution, + spatial_resolution_in_px=spatial_resolution_in_px, ) nwbfile.add_device(spatial_light_modulator) return spatial_light_modulator @@ -148,7 +148,7 @@ def mock_SpatialLightModulator3D( description: str = "Generic description for the spatial light modulator device", model: str = "Generic model for the spatial light modulator device", manufacturer: Optional[str] = None, - spatial_resolution: list = [100, 100, 100], + spatial_resolution_in_px: list = [100, 100, 100], nwbfile: Optional[NWBFile] = None, ) -> SpatialLightModulator3D: spatial_light_modulator = SpatialLightModulator3D( @@ -156,7 +156,7 @@ def mock_SpatialLightModulator3D( description=description, model=model, manufacturer=manufacturer, - spatial_resolution=spatial_resolution, + spatial_resolution_in_px=spatial_resolution_in_px, ) nwbfile.add_device(spatial_light_modulator) return spatial_light_modulator @@ -225,9 +225,9 @@ def mock_PatternedOptogeneticStimulusTable( description: str = "no description", start_time: list = [0.0, 0.1, 0.2], stop_time: list = [0.7, 0.8, 0.9], - power: list = [700.0, 800.0, 900.0], - frequency: list = [7.0, 8.0, 9.0], - pulse_width: list = [0.1, 0.1, 0.1], + power: list = [0.7, 0.8, 0.9], + frequency: list = [20.0, 10.0, 5.0], + pulse_width: list = [0.01, 0.02, 0.05], stimulus_pattern: list = [None, None, None], targets: list = [None, None, None], stimulus_site: list = [None, None, None], diff --git a/src/pynwb/tests/test_patternedogen.py b/src/pynwb/tests/test_patternedogen.py index ec0b7b5..a8853dc 100644 --- a/src/pynwb/tests/test_patternedogen.py +++ b/src/pynwb/tests/test_patternedogen.py @@ -1,5 +1,4 @@ -"""Unit and integration tests for the PatternedOptogeneticStimulusTable extension neurodata type. -""" +"""Unit and integration tests for the PatternedOptogeneticStimulusTable extension neurodata type.""" import numpy as np from hdmf.common.table import VectorData diff --git a/tutorial_patterned_ogen.ipynb b/tutorial_patterned_ogen.ipynb index afed8a9..72689af 100644 --- a/tutorial_patterned_ogen.ipynb +++ b/tutorial_patterned_ogen.ipynb @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -61,7 +61,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -70,7 +70,7 @@ " name=\"SpatialLightModulator2D\",\n", " description=\"Generic description for the slm\",\n", " manufacturer=\"slm manufacturer\",\n", - " spatial_resolution=[512, 512],\n", + " spatial_resolution_in_px=[512, 512],\n", ")\n", "nwbfile.add_device(spatial_light_modulator)\n", "\n", @@ -79,13 +79,13 @@ " name=\"Laser\",\n", " model=\"laser model\",\n", " manufacturer=\"laser manufacturer\",\n", - " stimulation_wavelength=1035.0, # nm\n", + " stimulation_wavelength_in_nm=1035.0, # nm\n", " filter_description=\"Short pass at 1040 nm\",\n", " description=\"Generic description for the laser\",\n", - " peak_power=70e-3, # the peak power of stimulation in Watts\n", - " intensity=0.005, # the intensity of excitation in W/mm^2\n", - " exposure_time=2.51e-13, # the exposure time of the sample in seconds\n", - " pulse_rate=1 / 2.51e-13, # the pulse rate of the laser is in Hz\n", + " peak_power_in_W=70e-3, # the peak power of stimulation in Watts\n", + " intensity_in_W_per_m2=0.005, # the intensity of excitation in W/mm^2\n", + " exposure_time_in_s=2.51e-13, # the exposure time of the sample in seconds\n", + " pulse_rate_in_Hz=1 / 2.51e-13, # the pulse rate of the laser is in Hz\n", ")\n", "nwbfile.add_device(light_source)\n", "\n", @@ -108,9 +108,86 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + "
\n", + " | pixel_mask | \n", + "
---|---|
id | \n", + "\n", + " |
0 | \n", + "[[0, 0, 0]] | \n", + "
1 | \n", + "[[1, 1, 0]] | \n", + "
2 | \n", + "[[2, 2, 0]] | \n", + "
3 | \n", + "[[3, 3, 0]] | \n", + "
... and 41 more rows.
\n", + " | start_time | \n", + "stop_time | \n", + "power | \n", + "frequency | \n", + "pulse_width | \n", + "targets | \n", + "stimulus_pattern | \n", + "stimulus_site | \n", + "
---|---|---|---|---|---|---|---|---|
id | \n", + "\n", + " | \n", + " | \n", + " | \n", + " | \n", + " | \n", + " | \n", + " | \n", + " |
0 | \n", + "0.0 | \n", + "1.0 | \n", + "0.07 | \n", + "20.0 | \n", + "0.1 | \n", + "Hologram1 ndx_patterned_ogen.patterned_ogen.OptogeneticStimulusTarget at 0x140319653226032\\nFields:\\n segmented_rois: segmented_rois <class 'hdmf.common.table.DynamicTableRegion'>\\n targeted_rois: targeted_rois <class 'hdmf.common.table.DynamicTableRegion'>\\n | \n", + "TemporalFocusing abc.TemporalFocusing at 0x140319659264384\\nFields:\\n axial_point_spread_function_in_um: 32 um ± 1.6 um\\n description: scanless beam pattern\\n lateral_point_spread_function_in_um: 9 um ± 0.7 um\\n | \n", + "PatternedOptogeneticStimulusSite ndx_patterned_ogen.patterned_ogen.PatternedOptogeneticStimulusSite at 0x140319671366000\\nFields:\\n description: Scanning\\n device: 2P_microscope pynwb.device.Device at 0x140321045509376\\nFields:\\n description: My two-photon microscope\\n manufacturer: The best microscope manufacturer\\n\\n effector: ChR2\\n excitation_lambda: 600.0\\n light_source: Laser abc.LightSource at 0x140319685390176\\nFields:\\n description: Generic description for the laser\\n exposure_time_in_s: 2.51e-13\\n filter_description: Short pass at 1040 nm\\n intensity_in_W_per_m2: 0.005\\n manufacturer: laser manufacturer\\n model: laser model\\n peak_power_in_W: 0.07\\n pulse_rate_in_Hz: 3984063745019.9204\\n stimulation_wavelength_in_nm: 1035.0\\n\\n location: VISrl\\n spatial_light_modulator: SpatialLightModulator2D abc.SpatialLightModulator2D at 0x140321045509664\\nFields:\\n description: Generic description for the slm\\n manufacturer: slm manufacturer\\n spatial_resolution_in_px: [512 512]\\n\\n | \n", + "
1 | \n", + "0.5 | \n", + "1.0 | \n", + "0.05 | \n", + "20.0 | \n", + "0.1 | \n", + "Hologram2 ndx_patterned_ogen.patterned_ogen.OptogeneticStimulusTarget at 0x140321045763456\\nFields:\\n segmented_rois: segmented_rois <class 'hdmf.common.table.DynamicTableRegion'>\\n targeted_rois: targeted_rois <class 'hdmf.common.table.DynamicTableRegion'>\\n | \n", + "SpiralScanning abc.SpiralScanning at 0x140319654796544\\nFields:\\n description: scanning beam pattern\\n diameter_in_um: 15\\n height_in_um: 10\\n number_of_revolutions: 5\\n | \n", + "PatternedOptogeneticStimulusSite ndx_patterned_ogen.patterned_ogen.PatternedOptogeneticStimulusSite at 0x140319671366000\\nFields:\\n description: Scanning\\n device: 2P_microscope pynwb.device.Device at 0x140321045509376\\nFields:\\n description: My two-photon microscope\\n manufacturer: The best microscope manufacturer\\n\\n effector: ChR2\\n excitation_lambda: 600.0\\n light_source: Laser abc.LightSource at 0x140319685390176\\nFields:\\n description: Generic description for the laser\\n exposure_time_in_s: 2.51e-13\\n filter_description: Short pass at 1040 nm\\n intensity_in_W_per_m2: 0.005\\n manufacturer: laser manufacturer\\n model: laser model\\n peak_power_in_W: 0.07\\n pulse_rate_in_Hz: 3984063745019.9204\\n stimulation_wavelength_in_nm: 1035.0\\n\\n location: VISrl\\n spatial_light_modulator: SpatialLightModulator2D abc.SpatialLightModulator2D at 0x140321045509664\\nFields:\\n description: Generic description for the slm\\n manufacturer: slm manufacturer\\n spatial_resolution_in_px: [512 512]\\n\\n | \n", + "
2 | \n", + "0.8 | \n", + "1.7 | \n", + "0.04 | \n", + "20.0 | \n", + "0.1 | \n", + "Hologram3 ndx_patterned_ogen.patterned_ogen.OptogeneticStimulusTarget at 0x140321045763696\\nFields:\\n targeted_rois: targeted_rois <class 'hdmf.common.table.DynamicTableRegion'>\\n | \n", + "CircularOptogeneticStimulusPattern abc.OptogeneticStimulus2DPattern at 0x140319671364608\\nFields:\\n description: circular pattern\\n sweep_size_in_um: [8]\\n | \n", + "PatternedOptogeneticStimulusSite ndx_patterned_ogen.patterned_ogen.PatternedOptogeneticStimulusSite at 0x140319671366000\\nFields:\\n description: Scanning\\n device: 2P_microscope pynwb.device.Device at 0x140321045509376\\nFields:\\n description: My two-photon microscope\\n manufacturer: The best microscope manufacturer\\n\\n effector: ChR2\\n excitation_lambda: 600.0\\n light_source: Laser abc.LightSource at 0x140319685390176\\nFields:\\n description: Generic description for the laser\\n exposure_time_in_s: 2.51e-13\\n filter_description: Short pass at 1040 nm\\n intensity_in_W_per_m2: 0.005\\n manufacturer: laser manufacturer\\n model: laser model\\n peak_power_in_W: 0.07\\n pulse_rate_in_Hz: 3984063745019.9204\\n stimulation_wavelength_in_nm: 1035.0\\n\\n location: VISrl\\n spatial_light_modulator: SpatialLightModulator2D abc.SpatialLightModulator2D at 0x140321045509664\\nFields:\\n description: Generic description for the slm\\n manufacturer: slm manufacturer\\n spatial_resolution_in_px: [512 512]\\n\\n | \n", + "