From 5bceb2c32f4172aa831aaaa7b364f95160ecdbad Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Tue, 14 Jan 2025 14:04:52 -0800 Subject: [PATCH 01/34] breaking-change: merge instrument/rig and convert rig to List[Optional[]] --- examples/aibs_smartspim_instrument.json | 510 ------------------ ...im_instrument.py => aibs_smartspim_rig.py} | 168 +++--- examples/test.py | 30 ++ src/aind_data_schema/base.py | 4 +- src/aind_data_schema/components/devices.py | 5 +- src/aind_data_schema/core/instrument.py | 110 ---- src/aind_data_schema/core/rig.py | 301 +++++------ tests/test_rig.py | 20 +- 8 files changed, 264 insertions(+), 884 deletions(-) delete mode 100644 examples/aibs_smartspim_instrument.json rename examples/{aibs_smartspim_instrument.py => aibs_smartspim_rig.py} (76%) create mode 100644 examples/test.py delete mode 100644 src/aind_data_schema/core/instrument.py diff --git a/examples/aibs_smartspim_instrument.json b/examples/aibs_smartspim_instrument.json deleted file mode 100644 index a799ec7f6..000000000 --- a/examples/aibs_smartspim_instrument.json +++ /dev/null @@ -1,510 +0,0 @@ -{ - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/instrument.py", - "schema_version": "1.0.5", - "instrument_id": "440_SmartSPIM2_20231004", - "modification_date": "2023-10-04", - "instrument_type": "SmartSPIM", - "manufacturer": { - "name": "LifeCanvas", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "temperature_control": false, - "optical_tables": [ - { - "device_type": "Optical table", - "name": "Table", - "serial_number": null, - "manufacturer": { - "name": "Technical Manufacturing Corporation", - "abbreviation": "TMC", - "registry": null, - "registry_identifier": null - }, - "model": "CleanTop", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "length": "35", - "width": "29", - "table_size_unit": "inch", - "vibration_control": true - } - ], - "enclosure": null, - "objectives": [ - { - "device_type": "Objective", - "name": "TLX Objective", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "TL4X-SAP", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": "Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", - "numerical_aperture": "0.2", - "magnification": "3.6", - "immersion": "multi", - "objective_type": null - } - ], - "detectors": [ - { - "device_type": "Detector", - "name": "Camera 1", - "serial_number": "001107", - "manufacturer": { - "name": "Hamamatsu", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "03natb733" - }, - "model": "C14440-20UP", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": null, - "frame_rate": null, - "frame_rate_unit": null, - "immersion": null, - "chroma": null, - "sensor_width": null, - "sensor_height": null, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": null, - "bin_mode": "None", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - } - ], - "light_sources": [ - { - "device_type": "Laser", - "name": "Ex_488", - "serial_number": "VL01222A11", - "manufacturer": { - "name": "Vortran", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "Stradus", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": "All lasers controlled via Vortran VersaLase System", - "wavelength": 488, - "wavelength_unit": "nanometer", - "maximum_power": "150", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Ex_561", - "serial_number": "417927", - "manufacturer": { - "name": "Coherent Scientific", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "031tysd23" - }, - "model": "Obis", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": "All lasers controlled via Vortran VersaLase System", - "wavelength": 561, - "wavelength_unit": "nanometer", - "maximum_power": "150", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Ex_647", - "serial_number": "VL01222A10", - "manufacturer": { - "name": "Vortran", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "Stradus", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": "All lasers controlled via Vortran VersaLase System", - "wavelength": 647, - "wavelength_unit": "nanometer", - "maximum_power": "160", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - } - ], - "lenses": [], - "fluorescence_filters": [ - { - "device_type": "Filter", - "name": "Em_525", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF03-525/50-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 0, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Em_600", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF01-600/52-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 1, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Em_690", - "serial_number": null, - "manufacturer": { - "name": "Chroma", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "ET690/50m", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 2, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - } - ], - "motorized_stages": [ - { - "device_type": "Motorized stage", - "name": "Focus stage", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-100", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "travel": "100", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #1", - "serial_number": null, - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #2", - "serial_number": null, - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #3", - "serial_number": null, - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #4", - "serial_number": null, - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - } - ], - "scanning_stages": [ - { - "device_type": "Motorized stage", - "name": "Sample stage Z", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-50", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "travel": "50", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Detection axis", - "stage_axis_name": "Z" - }, - { - "device_type": "Motorized stage", - "name": "Sample stage X", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-50", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "travel": "50", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Illumination axis", - "stage_axis_name": "X" - }, - { - "device_type": "Motorized stage", - "name": "Sample stage Y", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-50", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "travel": "50", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Perpendicular axis", - "stage_axis_name": "Y" - } - ], - "additional_devices": [ - { - "device_type": "Additional imaging device", - "name": "Lens 1", - "serial_number": null, - "manufacturer": { - "name": "Optotune", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "EL-16-40-TC", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "imaging_device_type": "Tunable lens" - }, - { - "device_type": "Additional imaging device", - "name": "Lens 2", - "serial_number": null, - "manufacturer": { - "name": "Optotune", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "EL-16-40-TC", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "imaging_device_type": "Tunable lens" - }, - { - "device_type": "Additional imaging device", - "name": "Sample chamber", - "serial_number": null, - "manufacturer": { - "name": "LifeCanvas", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "Large-uncoated-glass", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "imaging_device_type": "Sample Chamber" - } - ], - "calibration_date": null, - "calibration_data": null, - "com_ports": [ - { - "hardware_name": "Laser Launch", - "com_port": "COM3" - }, - { - "hardware_name": "ASI Tiger", - "com_port": "COM5" - }, - { - "hardware_name": "MightyZap", - "com_port": "COM4" - } - ], - "daqs": [], - "notes": null -} \ No newline at end of file diff --git a/examples/aibs_smartspim_instrument.py b/examples/aibs_smartspim_rig.py similarity index 76% rename from examples/aibs_smartspim_instrument.py rename to examples/aibs_smartspim_rig.py index 8fb42aca9..3e745e59a 100644 --- a/examples/aibs_smartspim_instrument.py +++ b/examples/aibs_smartspim_rig.py @@ -9,32 +9,26 @@ AdditionalImagingDevice, Detector, Filter, + ImagingInstrumentType, Laser, MotorizedStage, Objective, OpticalTable, ScanningStage, ) -from aind_data_schema.core.instrument import Com, Instrument +from aind_data_schema_models.modalities import Modality +from aind_data_schema.core.rig import Com, Rig -inst = Instrument( - instrument_id="440_SmartSPIM2_20231004", - modification_date=datetime.date(2023, 10, 4), - instrument_type="SmartSPIM", - manufacturer=Organization.LIFECANVAS, - objectives=[ - Objective( - name="TLX Objective", - numerical_aperture=0.2, - magnification=3.6, - immersion="multi", - manufacturer=Organization.THORLABS, - model="TL4X-SAP", - notes="Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", - ), - ], - detectors=[ - Detector( +objective = Objective( + name="TLX Objective", + numerical_aperture=0.2, + magnification=3.6, + immersion="multi", + manufacturer=Organization.THORLABS, + model="TL4X-SAP", + notes="Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", +) +camera1 = Detector( name="Camera 1", detector_type="Camera", data_interface="USB", @@ -42,10 +36,8 @@ manufacturer=Organization.HAMAMATSU, model="C14440-20UP", serial_number="001107", - ), - ], - light_sources=[ - Laser( + ) +laser1 = Laser( name="Ex_488", device_type="Laser", coupling="Single-mode fiber", @@ -55,8 +47,8 @@ manufacturer=Organization.VORTRAN, model="Stradus", notes="All lasers controlled via Vortran VersaLase System", - ), - Laser( + ) +laser2 = Laser( name="Ex_561", device_type="Laser", coupling="Single-mode fiber", @@ -66,8 +58,8 @@ manufacturer=Organization.COHERENT_SCIENTIFIC, model="Obis", notes="All lasers controlled via Vortran VersaLase System", - ), - Laser( + ) +laser3 = Laser( name="Ex_647", device_type="Laser", coupling="Single-mode fiber", @@ -77,68 +69,62 @@ manufacturer=Organization.VORTRAN, model="Stradus", notes="All lasers controlled via Vortran VersaLase System", - ), - ], - motorized_stages=[ - MotorizedStage( + ) +stage0 = MotorizedStage( name="Focus stage", model="LS-100", manufacturer=Organization.ASI, travel=100, - ), - MotorizedStage( + ) +stage1 = MotorizedStage( name="Cylindrical lens #1", model="L12-20F-4", manufacturer=Organization.IR_ROBOT_CO, travel=41, - ), - MotorizedStage( + ) +stage2 = MotorizedStage( name="Cylindrical lens #2", model="L12-20F-4", manufacturer=Organization.IR_ROBOT_CO, travel=41, - ), - MotorizedStage( + ) +stage3 = MotorizedStage( name="Cylindrical lens #3", model="L12-20F-4", manufacturer=Organization.IR_ROBOT_CO, travel=41, - ), - MotorizedStage( + ) +stage4 = MotorizedStage( name="Cylindrical lens #4", model="L12-20F-4", manufacturer=Organization.IR_ROBOT_CO, travel=41, - ), - ], - scanning_stages=[ - ScanningStage( + ) +scan_stage1 = ScanningStage( name="Sample stage Z", model="LS-50", manufacturer=Organization.ASI, stage_axis_direction="Detection axis", stage_axis_name="Z", travel=50, - ), - ScanningStage( + ) +scan_stage2 = ScanningStage( name="Sample stage X", model="LS-50", manufacturer=Organization.ASI, stage_axis_direction="Illumination axis", stage_axis_name="X", travel=50, - ), - ScanningStage( + ) +scan_stage3 = ScanningStage( name="Sample stage Y", model="LS-50", manufacturer=Organization.ASI, stage_axis_direction="Perpendicular axis", stage_axis_name="Y", travel=50, - ), - ], - optical_tables=[ - OpticalTable( + ) +table = OpticalTable( name="Table", model="CleanTop", # model="VIS2424-IG2-125A", # ~3 months length=35, # length=24, @@ -146,21 +132,7 @@ vibration_control=True, manufacturer=Organization.TMC, ) - ], - temperature_control=False, - com_ports=[ - Com( - hardware_name="Laser Launch", - com_port="COM3", - ), - Com( - hardware_name="ASI Tiger", - com_port="COM5", - ), - Com(hardware_name="MightyZap", com_port="COM4"), - ], - fluorescence_filters=[ - Filter( +filter0 = Filter( name="Em_525", filter_type="Band pass", manufacturer=Organization.SEMROCK, @@ -169,8 +141,8 @@ thickness_unit=SizeUnit.MM, model="FF03-525/50-25", filter_wheel_index=0, - ), - Filter( + ) +filter1 = Filter( name="Em_600", filter_type="Band pass", manufacturer=Organization.SEMROCK, @@ -179,8 +151,8 @@ thickness_unit=SizeUnit.MM, model="FF01-600/52-25", filter_wheel_index=1, - ), - Filter( + ) +filter2 = Filter( name="Em_690", filter_type="Band pass", manufacturer=Organization.CHROMA, @@ -189,29 +161,67 @@ thickness_unit=SizeUnit.MM, model="ET690/50m", filter_wheel_index=2, - ), - ], - additional_devices=[ - AdditionalImagingDevice( + ) +lens1 = AdditionalImagingDevice( name="Lens 1", imaging_device_type="Tunable lens", manufacturer=Organization.OPTOTUNE, model="EL-16-40-TC", - ), - AdditionalImagingDevice( + ) +lens2 = AdditionalImagingDevice( name="Lens 2", imaging_device_type="Tunable lens", manufacturer=Organization.OPTOTUNE, model="EL-16-40-TC", - ), - AdditionalImagingDevice( + ) +lens3 = AdditionalImagingDevice( name="Sample chamber", imaging_device_type="Sample Chamber", manufacturer=Organization.LIFECANVAS, model="Large-uncoated-glass", + ) + +inst = Rig( + rig_id="440_SmartSPIM2_20231004", + modification_date=datetime.date(2023, 10, 4), + instrument_type=ImagingInstrumentType.SMARTSPIM, + modalities=[Modality.SPIM], + manufacturer=Organization.LIFECANVAS, + temperature_control=False, + components=[ + objective, + camera1, + laser1, + laser2, + laser3, + stage0, + stage1, + stage2, + stage3, + stage4, + scan_stage1, + scan_stage2, + scan_stage3, + table, + filter0, + filter1, + filter2, + lens1, + lens2, + lens3, + ], + com_ports=[ + Com( + hardware_name="Laser Launch", + com_port="COM3", ), + Com( + hardware_name="ASI Tiger", + com_port="COM5", + ), + Com(hardware_name="MightyZap", com_port="COM4"), ], ) serialized = inst.model_dump_json() -deserialized = Instrument.model_validate_json(serialized) +deserialized = Rig.model_validate_json(serialized) deserialized.write_standard_file(prefix="aibs_smartspim") diff --git a/examples/test.py b/examples/test.py new file mode 100644 index 000000000..ea2c88a9d --- /dev/null +++ b/examples/test.py @@ -0,0 +1,30 @@ +from typing import Annotated, List, Union +from pydantic import BaseModel, Field + + +class TypeA(BaseModel): + item_type: str = "TypeA" + value: int + + +class TypeB(BaseModel): + item_type: str = "TypeB" + value: str + + +class TypeC(BaseModel): + item_type: str = "TypeC" + value: float + + +class MyModel(BaseModel): + items: List[Annotated[Union[TypeA, TypeB, TypeC], Field(discriminator="item_type")]] + + +# Example usage: +data = MyModel(items=[TypeA(value=42), TypeB(value="string"), TypeC(value=3.14)]) +print(data) + +# Example: Providing only a subset +data_subset = MyModel(items=[TypeA(value=42), TypeC(value=3.14)]) +print(data_subset) diff --git a/src/aind_data_schema/base.py b/src/aind_data_schema/base.py index cffbfb7a2..fe669da91 100644 --- a/src/aind_data_schema/base.py +++ b/src/aind_data_schema/base.py @@ -154,9 +154,7 @@ def default_filename(cls): """ Returns standard filename in snakecase """ - parent_classes = [ - base_class for base_class in cls.__bases__ if base_class.__name__ != DataCoreModel.__name__ - ] + parent_classes = [base_class for base_class in cls.__bases__ if base_class.__name__ != DataCoreModel.__name__] name = cls.__name__ diff --git a/src/aind_data_schema/components/devices.py b/src/aind_data_schema/components/devices.py index af739013d..cf30b9202 100644 --- a/src/aind_data_schema/components/devices.py +++ b/src/aind_data_schema/components/devices.py @@ -273,7 +273,7 @@ class Device(DataModel): default=None, title="Path to CAD diagram", description="For CUSTOM manufactured devices" ) port_index: Optional[str] = Field(default=None, title="Port index") - additional_settings: GenericModelType = Field(GenericModel(), title="Additional parameters") + additional_settings: Optional[GenericModelType] = Field(default=None, title="Additional parameters") notes: Optional[str] = Field(default=None, title="Notes") @@ -933,6 +933,3 @@ class MyomatrixArray(Device): device_type: Literal["Myomatrix Array"] = "Myomatrix Array" array_type: MyomatrixArrayType = Field(..., title="Array type") - - -LIGHT_SOURCES = Annotated[Union[Laser, LightEmittingDiode, Lamp], Field(discriminator="device_type")] diff --git a/src/aind_data_schema/core/instrument.py b/src/aind_data_schema/core/instrument.py deleted file mode 100644 index c62667805..000000000 --- a/src/aind_data_schema/core/instrument.py +++ /dev/null @@ -1,110 +0,0 @@ -""" schema describing imaging instrument """ - -from datetime import date -from typing import List, Literal, Optional - -from aind_data_schema_models.organizations import Organization -from pydantic import Field, SkipValidation, ValidationInfo, field_validator - -from aind_data_schema.base import DataCoreModel, DataModel -from aind_data_schema.components.devices import ( - LIGHT_SOURCES, - AdditionalImagingDevice, - DAQDevice, - Detector, - Enclosure, - Filter, - ImagingInstrumentType, - Lens, - MotorizedStage, - Objective, - OpticalTable, - ScanningStage, -) - - -class Com(DataModel): - """Description of a communication system""" - - hardware_name: str = Field(..., title="Controlled hardware device") - com_port: str = Field(..., title="COM port") - - -class Instrument(DataCoreModel): - """Description of an instrument, which is a collection of devices""" - - _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/instrument.py" - describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: SkipValidation[Literal["1.0.5"]] = Field(default="1.0.5") - - instrument_id: Optional[str] = Field( - default=None, - description="Unique instrument identifier, name convention: --", - title="Instrument ID", - ) - modification_date: date = Field(..., title="Date of modification") - instrument_type: ImagingInstrumentType = Field(..., title="Instrument type") - manufacturer: Organization.ONE_OF = Field(..., title="Instrument manufacturer") - temperature_control: Optional[bool] = Field(default=None, title="Temperature control") - optical_tables: List[OpticalTable] = Field(default=[], title="Optical table") - enclosure: Optional[Enclosure] = Field(default=None, title="Enclosure") - objectives: List[Objective] = Field(..., title="Objectives") - detectors: List[Detector] = Field(default=[], title="Detectors") - light_sources: List[LIGHT_SOURCES] = Field(default=[], title="Light sources") - lenses: List[Lens] = Field(default=[], title="Lenses") - fluorescence_filters: List[Filter] = Field(default=[], title="Fluorescence filters") - motorized_stages: List[MotorizedStage] = Field(default=[], title="Motorized stages") - scanning_stages: List[ScanningStage] = Field(default=[], title="Scanning motorized stages") - additional_devices: List[AdditionalImagingDevice] = Field(default=[], title="Additional devices") - calibration_date: Optional[date] = Field( - default=None, - description="Date of most recent calibration", - title="Calibration date", - ) - calibration_data: Optional[str] = Field( - default=None, - description="Path to calibration data from most recent calibration", - title="Calibration data", - ) - com_ports: List[Com] = Field(default=[], title="COM ports") - daqs: List[DAQDevice] = Field(default=[], title="DAQ") - notes: Optional[str] = Field(default=None, validate_default=True) - - @field_validator("daqs", mode="after") - def validate_device_names(cls, value: List[DAQDevice], info: ValidationInfo) -> List[DAQDevice]: - """validate that all DAQ channels are connected to devices that - actually exist - """ - daqs = value - all_devices = ( - info.data["motorized_stages"] - + info.data["scanning_stages"] - + info.data["light_sources"] - + info.data["detectors"] - + info.data["additional_devices"] - + daqs - ) - all_device_names = [device.name for device in all_devices] - for daq in daqs: - for channel in daq.channels: - if channel.device_name not in all_device_names: - raise ValueError( - f"Device name validation error: '{channel.device_name}' " - + f"is connected to '{channel.channel_name}' on '{daq.name}', but " - + "this device is not part of the rig." - ) - return daqs - - @field_validator("notes", mode="after") - def validate_other(cls, value: Optional[str], info: ValidationInfo) -> Optional[str]: - """Validator for other/notes""" - - if info.data.get("instrument_type") == ImagingInstrumentType.OTHER and not value: - raise ValueError( - "Notes cannot be empty if instrument_type is Other. Describe the instrument_type in the notes field." - ) - if info.data.get("manufacturer") == Organization.OTHER and not value: - raise ValueError( - "Notes cannot be empty if manufacturer is Other. Describe the manufacturer in the notes field." - ) - return value diff --git a/src/aind_data_schema/core/rig.py b/src/aind_data_schema/core/rig.py index 1e594aa6a..58d1485b1 100644 --- a/src/aind_data_schema/core/rig.py +++ b/src/aind_data_schema/core/rig.py @@ -7,10 +7,11 @@ from pydantic import Field, SkipValidation, ValidationInfo, field_serializer, field_validator, model_validator from typing_extensions import Annotated -from aind_data_schema.base import DataCoreModel +from aind_data_schema_models.organizations import Organization +from aind_data_schema.base import DataCoreModel, DataModel from aind_data_schema.components.coordinates import Axis, Origin from aind_data_schema.components.devices import ( - LIGHT_SOURCES, + AdditionalImagingDevice, Calibration, CameraAssembly, CameraTarget, @@ -23,62 +24,66 @@ FiberAssembly, Filter, HarpDevice, + ImagingInstrumentType, + Lamp, + Laser, LaserAssembly, Lens, + LightEmittingDiode, Monitor, + MotorizedStage, MousePlatform, NeuropixelsBasestation, Objective, Olfactometer, OpenEphysAcquisitionBoard, + OpticalTable, Patch, PockelsCell, PolygonalScanner, RewardDelivery, + ScanningStage, Speaker, ) MOUSE_PLATFORMS = Annotated[Union[tuple(MousePlatform.__subclasses__())], Field(discriminator="device_type")] -STIMULUS_DEVICES = Annotated[Union[Monitor, Olfactometer, RewardDelivery, Speaker], Field(discriminator="device_type")] -RIG_DAQ_DEVICES = Annotated[ - Union[HarpDevice, NeuropixelsBasestation, OpenEphysAcquisitionBoard, DAQDevice], Field(discriminator="device_type") -] RIG_ID_PATTERN = r"^[a-zA-Z0-9]+_[a-zA-Z0-9-]+_\d{8}$" +class Com(DataModel): + """Description of a communication system""" + + hardware_name: str = Field(..., title="Controlled hardware device") + com_port: str = Field(..., title="COM port") + + +class Connection(DataModel): + """Connection between two devices""" + + device_names: List[str] = Field(..., title="Names of connected devices") + inputs: Optional[List[bool]] = Field(default=None, title="Input status") + outputs: Optional[List[bool]] = Field(default=None, title="Output status") + channels: Optional[List[int]] = Field(default=None, title="Connection channels") + + class Rig(DataCoreModel): """Description of a rig""" + # metametadata _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/rig.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) schema_version: SkipValidation[Literal["1.0.5"]] = Field(default="1.0.5") + + # rig definition rig_id: str = Field( ..., description="Unique rig identifier, name convention: --", title="Rig ID", pattern=RIG_ID_PATTERN, ) + mouse_platform: Optional[MOUSE_PLATFORMS] = Field(default=None, title="Mouse platform") modification_date: date = Field(..., title="Date of modification") - mouse_platform: MOUSE_PLATFORMS - stimulus_devices: List[STIMULUS_DEVICES] = Field(default=[], title="Stimulus devices") - cameras: List[CameraAssembly] = Field(default=[], title="Camera assemblies") - enclosure: Optional[Enclosure] = Field(default=None, title="Enclosure") - ephys_assemblies: List[EphysAssembly] = Field(default=[], title="Ephys probes") - fiber_assemblies: List[FiberAssembly] = Field(default=[], title="Inserted fiber optics") - stick_microscopes: List[CameraAssembly] = Field(default=[], title="Stick microscopes") - laser_assemblies: List[LaserAssembly] = Field(default=[], title="Laser modules") - patch_cords: List[Patch] = Field(default=[], title="Patch cords") - light_sources: List[LIGHT_SOURCES] = Field(default=[], title="Light sources") - detectors: List[Detector] = Field(default=[], title="Detectors") - objectives: List[Objective] = Field(default=[], title="Objectives") - filters: List[Filter] = Field(default=[], title="Filters") - lenses: List[Lens] = Field(default=[], title="Lenses") - digital_micromirror_devices: List[DigitalMicromirrorDevice] = Field(default=[], title="DMDs") - polygonal_scanners: List[PolygonalScanner] = Field(default=[], title="Polygonal scanners") - pockels_cells: List[PockelsCell] = Field(default=[], title="Pockels cells") - additional_devices: List[Device] = Field(default=[], title="Additional devices") - daqs: List[RIG_DAQ_DEVICES] = Field(default=[], title="Data acquisition devices") - calibrations: List[Calibration] = Field(..., title="Full calibration of devices") + calibrations: Optional[List[Calibration]] = Field(default=None, title="Full calibration of devices") ccf_coordinate_transform: Optional[str] = Field( default=None, title="CCF coordinate transform", @@ -87,8 +92,59 @@ class Rig(DataCoreModel): origin: Optional[Origin] = Field(default=None, title="Origin point for rig position transforms") rig_axes: Optional[List[Axis]] = Field(default=None, title="Rig axes", min_length=3, max_length=3) modalities: Set[Modality.ONE_OF] = Field(..., title="Modalities") + com_ports: List[Com] = Field(default=[], title="COM ports") + instrument_type: Optional[ImagingInstrumentType] = Field(default=None, title="Instrument type") + manufacturer: Optional[Organization.ONE_OF] = Field(default=None, title="Instrument manufacturer") + temperature_control: Optional[bool] = Field(default=None, title="Temperature control") notes: Optional[str] = Field(default=None, title="Notes") + connections: List[Connection] = Field( + default=[], + title="Connections", + description="List of all connections between devices in the rig", + ) + + components: List[ + Annotated[ + Union[ + Monitor, + Olfactometer, + RewardDelivery, + Speaker, + CameraAssembly, + Enclosure, + EphysAssembly, + FiberAssembly, + LaserAssembly, + Patch, + Laser, + LightEmittingDiode, + Lamp, + Detector, + Objective, + Filter, + Lens, + DigitalMicromirrorDevice, + PolygonalScanner, + PockelsCell, + HarpDevice, + NeuropixelsBasestation, + OpenEphysAcquisitionBoard, + OpticalTable, + MotorizedStage, + ScanningStage, + AdditionalImagingDevice, + DAQDevice, + Device, # note that order matters in the Union, DAQDevice and Device should go last + ], + Field(discriminator="device_type"), + ] + ] = Field( + default=[], + title="Components", + description="List of all devices in the rig", + ) + @field_serializer("modalities", when_used="json") def serialize_modalities(self, modalities: Set[Modality.ONE_OF]): """Dynamically serialize modalities based on their type.""" @@ -99,154 +155,79 @@ def validate_cameras_other(self): """check if any cameras contain an 'other' field""" if self.notes is None: - for camera_assembly in self.cameras + self.stick_microscopes: - if camera_assembly.camera_target == CameraTarget.OTHER: + for component in self.components: + if isinstance(component, CameraAssembly) and component.camera_target == CameraTarget.OTHER: raise ValueError( f"Notes cannot be empty if a camera target contains an 'Other' field. " - f"Describe the camera target from ({camera_assembly.name}) in the notes field" + f"Describe the camera target from ({component.name}) in the notes field" ) return self - @field_validator("daqs", mode="after") - def validate_device_names(cls, value: List[DAQDevice], info: ValidationInfo) -> List[DAQDevice]: - """validate that all DAQ channels are connected to devices that - actually exist + @field_validator("connections", mode="after") + def validate_device_names(cls, value: List[Connection], info: ValidationInfo) -> List[Connection]: + """validate that all connections map between devices that actually exist """ - daqs = value - non_reward_delivery_stimulus_devices = [ - d for d in info.data.get("stimulus_devices", []) if not isinstance(d, RewardDelivery) - ] - standard_devices = ( - daqs - + info.data.get("light_sources", []) - + info.data.get("patch_cords", []) - + info.data.get("detectors", []) - + info.data.get("digital_micromirror_devices", []) - + info.data.get("polygonal_scanners", []) - + info.data.get("pockels_cells", []) - + info.data.get("additional_devices", []) - + non_reward_delivery_stimulus_devices - ) - camera_devices = info.data.get("cameras", []) + info.data.get("stick_microscopes", []) - standard_device_names = [device.name for device in standard_devices] - camera_names = [camera.camera.name for camera in camera_devices] - ephys_assembly_names = [ - probe.name for ephys_assembly in info.data.get("ephys_assemblies", []) for probe in ephys_assembly.probes - ] - laser_assembly_names = [ - laser.name for laser_assembly in info.data.get("laser_assemblies", []) for laser in laser_assembly.lasers - ] - mouse_platform_names = [] if info.data.get("mouse_platform") is None else [info.data["mouse_platform"].name] - reward_deliveries = [d for d in info.data.get("stimulus_devices", []) if isinstance(d, RewardDelivery)] - reward_delivery_device_names = [] - for rd in reward_deliveries: - for rs in rd.reward_spouts: - reward_delivery_device_names += [rs.name, rs.solenoid_valve.name, rs.lick_sensor.name] - - all_device_names = ( - standard_device_names - + camera_names - + ephys_assembly_names - + laser_assembly_names - + mouse_platform_names - + reward_delivery_device_names - ) - - for daq in daqs: - for channel in daq.channels: - if channel.device_name not in all_device_names: + device_names = [device.name for device in info.data.get("components", [])] + + for connection in value: + for device_name in connection.device_names: + if device_name not in device_names: raise ValueError( - f"Device name validation error: '{channel.device_name}' " - + f"is connected to '{channel.channel_name}' on '{daq.name}', but " - + "this device is not part of the rig." + f"Device name validation error: '{device_name}' is not part of the rig." ) - return daqs - @staticmethod - def _validate_ephys_modality(value: Set[Modality.ONE_OF], info: ValidationInfo) -> List[str]: - """Validate ecephys modality has ephys_assemblies and stick_microscopes""" - errors = [] - if Modality.ECEPHYS in value: - for k, v in { - "ephys_assemblies": len(info.data.get("ephys_assemblies", [])) > 0, - }.items(): - if v is False: - errors.append(f"{k} field must be utilized for Ecephys modality") - return errors - - @staticmethod - def _validate_fib_modality(value: Set[Modality.ONE_OF], info: ValidationInfo) -> List[str]: - """Validate FIB modality has light_sources, detectors, and patch_cords""" - errors = [] - if Modality.FIB in value: - for k, v in { - "light_sources": len(info.data.get("light_sources", [])) > 0, - "detectors": len(info.data.get("detectors", [])) > 0, - "patch_cords": len(info.data.get("patch_cords", [])) > 0, - }.items(): - if v is False: - errors.append(f"{k} field must be utilized for FIB modality") - return errors - - @staticmethod - def _validate_pophys_modality(value: Set[Modality.ONE_OF], info: ValidationInfo) -> List[str]: - """Validate POPHYS modality has light_sources, detectors, and objectives""" - errors = [] - if Modality.POPHYS in value: - for k, v in { - "light_sources": len(info.data.get("light_sources", [])) > 0, - "detectors": len(info.data.get("detectors", [])) > 0, - "objectives": len(info.data.get("objectives", [])) > 0, - }.items(): - if v is False: - errors.append(f"{k} field must be utilized for POPHYS modality") - return errors - - @staticmethod - def _validate_slap_modality(value: Set[Modality.ONE_OF], info: ValidationInfo) -> List[str]: - """Validate SLAP modality has light_sources, detectors, and objectives""" - errors = [] - if Modality.SLAP in value: - for k, v in { - "light_sources": len(info.data.get("light_sources", [])) > 0, - "detectors": len(info.data.get("detectors", [])) > 0, - "objectives": len(info.data.get("objectives", [])) > 0, - }.items(): - if v is False: - errors.append(f"{k} field must be utilized for SLAP modality") - return errors - - @staticmethod - def _validate_behavior_videos_modality(value: Set[Modality.ONE_OF], info: ValidationInfo) -> List[str]: - """Validate BEHAVIOR_VIDEOS modality has cameras""" - errors = [] - if Modality.BEHAVIOR_VIDEOS in value: - if len(info.data.get("cameras", [])) == 0: - errors.append("cameras field must be utilized for Behavior Videos modality") - return errors - - @staticmethod - def _validate_behavior_modality(value: Set[Modality.ONE_OF], info: ValidationInfo) -> List[str]: - """Validate that BEHAVIOR modality has stimulus_devices""" - errors = [] - if Modality.BEHAVIOR in value: - if len(info.data.get("stimulus_devices", [])) == 0: - errors.append("stimulus_devices field must be utilized for Behavior modality") + return value - return errors + @field_validator("notes", mode="after") + def validate_other(cls, value: Optional[str], info: ValidationInfo) -> Optional[str]: + """Validator for other/notes""" + + if info.data.get("instrument_type") == ImagingInstrumentType.OTHER and not value: + raise ValueError( + "Notes cannot be empty if instrument_type is Other. Describe the instrument_type in the notes field." + ) + if info.data.get("manufacturer") == Organization.OTHER and not value: + raise ValueError( + "Notes cannot be empty if manufacturer is Other. Describe the manufacturer in the notes field." + ) + return value @field_validator("modalities", mode="after") def validate_modalities(cls, value: Set[Modality.ONE_OF], info: ValidationInfo) -> Set[Modality.ONE_OF]: - """Validate each modality in modalities field has associated data""" - ephys_errors = cls._validate_ephys_modality(value, info) - fib_errors = cls._validate_fib_modality(value, info) - pophys_errors = cls._validate_pophys_modality(value, info) - slap_errors = cls._validate_slap_modality(value, info) - behavior_vids_errors = cls._validate_behavior_videos_modality(value, info) - behavior_errors = cls._validate_behavior_modality(value, info) - - errors = ephys_errors + fib_errors + pophys_errors + slap_errors + behavior_vids_errors + behavior_errors + """Validate that devices exist for the modalities specified""" + + type_mapping = { + Modality.ECEPHYS.abbreviation: [EphysAssembly], + Modality.FIB.abbreviation: [ + [Laser, LightEmittingDiode, Lamp], + [Detector], + [Patch] + ], + Modality.POPHYS.abbreviation: [ + [Laser, LightEmittingDiode, Lamp], + [Detector], + [Objective] + ], + Modality.SLAP.abbreviation: [ + [Laser, LightEmittingDiode, Lamp], + [Detector], + [Objective] + ], + Modality.BEHAVIOR_VIDEOS.abbreviation: [CameraAssembly], + Modality.BEHAVIOR.abbreviation: [Olfactometer, RewardDelivery, Speaker, Monitor], + } + + errors = [] + + for modality in value: + if modality.abbreviation in type_mapping: + for device_type in type_mapping[modality.abbreviation]: + if not any(isinstance(component, device) for device in device_type for component in info.data.get("components", [])): + errors.append( + f"Device type validation error: No device of type {device_type} is part of the rig." + ) + if len(errors) > 0: message = "\n ".join(errors) raise ValueError(message) diff --git a/tests/test_rig.py b/tests/test_rig.py index faa5cccb0..568b988ba 100644 --- a/tests/test_rig.py +++ b/tests/test_rig.py @@ -802,15 +802,7 @@ def test_rig_id_validator(self): rig_id="123", modification_date=date(2020, 10, 10), modalities=[Modality.ECEPHYS, Modality.FIB], - daqs=daqs, - cameras=[camera], - stick_microscopes=[stick_microscope], - light_sources=[light_source], - laser_assemblies=lms, - ephys_assemblies=ems, - detectors=[detector], - patch_cords=[patch_cord], - stimulus_devices=[stimulus_device], + components=[*daqs, camera, stick_microscope, light_source, *lms, *ems, detector, patch_cord, stimulus_device], mouse_platform=Disc(name="Disc A", radius=1), calibrations=[calibration], ) @@ -819,15 +811,7 @@ def test_rig_id_validator(self): rig_id="123_EPHYS-OPTO_2020-01-01", modification_date=date(2020, 10, 10), modalities=[Modality.ECEPHYS, Modality.FIB], - daqs=daqs, - cameras=[camera], - stick_microscopes=[stick_microscope], - light_sources=[light_source], - laser_assemblies=lms, - ephys_assemblies=ems, - detectors=[detector], - patch_cords=[patch_cord], - stimulus_devices=[stimulus_device], + components=[*daqs, camera, stick_microscope, light_source, *lms, *ems, detector, patch_cord, stimulus_device], mouse_platform=Disc(name="Disc A", radius=1), calibrations=[calibration], ) From 8d9bf81947654d3a71447fe8a78da7fd2fcefdc0 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Tue, 14 Jan 2025 15:19:46 -0800 Subject: [PATCH 02/34] examples: yikes --- examples/aibs_smartspim_rig.json | 503 +++++++++++++ examples/aibs_smartspim_rig.py | 268 +++---- examples/aind_smartspim_instrument.py | 264 ------- ...nstrument.json => aind_smartspim_rig.json} | 172 +++-- examples/aind_smartspim_rig.py | 294 ++++++++ examples/ephys_rig.json | 669 +++++++++-------- examples/ephys_rig.py | 20 +- examples/exaspim_instrument.py | 215 ------ ...aspim_instrument.json => exaspim_rig.json} | 293 ++++---- examples/exaspim_rig.py | 236 ++++++ examples/fip_behavior_rig.json | 621 ++++++++-------- examples/fip_behavior_rig.py | 692 +++++++++--------- examples/fip_ophys_rig.json | 339 ++++----- examples/fip_ophys_rig.py | 597 ++++++++------- examples/mri_session.json | 4 +- examples/ophys_procedures.json | 2 +- examples/test.py | 30 - src/aind_data_schema/components/devices.py | 7 +- src/aind_data_schema/core/metadata.py | 5 - src/aind_data_schema/core/rig.py | 84 ++- tests/test_imaging.py | 44 +- tests/test_rig.py | 24 +- 22 files changed, 2963 insertions(+), 2420 deletions(-) create mode 100644 examples/aibs_smartspim_rig.json delete mode 100644 examples/aind_smartspim_instrument.py rename examples/{aind_smartspim_instrument.json => aind_smartspim_rig.json} (92%) create mode 100644 examples/aind_smartspim_rig.py delete mode 100644 examples/exaspim_instrument.py rename examples/{exaspim_instrument.json => exaspim_rig.json} (91%) create mode 100644 examples/exaspim_rig.py delete mode 100644 examples/test.py diff --git a/examples/aibs_smartspim_rig.json b/examples/aibs_smartspim_rig.json new file mode 100644 index 000000000..c5f20c76b --- /dev/null +++ b/examples/aibs_smartspim_rig.json @@ -0,0 +1,503 @@ +{ + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", + "schema_version": "1.0.5", + "rig_id": "440_SmartSPIM2_20231004", + "mouse_platform": null, + "modification_date": "2023-10-04", + "calibrations": null, + "ccf_coordinate_transform": null, + "origin": null, + "rig_axes": null, + "modalities": [ + { + "name": "Selective plane illumination microscopy", + "abbreviation": "SPIM" + } + ], + "com_ports": [ + { + "hardware_name": "Laser Launch", + "com_port": "COM3" + }, + { + "hardware_name": "ASI Tiger", + "com_port": "COM5" + }, + { + "hardware_name": "MightyZap", + "com_port": "COM4" + } + ], + "instrument_type": "SmartSPIM", + "manufacturer": { + "name": "LifeCanvas", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "temperature_control": false, + "notes": null, + "connections": [], + "components": [ + { + "device_type": "Objective", + "name": "TLX Objective", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "TL4X-SAP", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", + "numerical_aperture": "0.2", + "magnification": "3.6", + "immersion": "multi", + "objective_type": null + }, + { + "device_type": "Detector", + "name": "Camera 1", + "serial_number": "001107", + "manufacturer": { + "name": "Hamamatsu", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "03natb733" + }, + "model": "C14440-20UP", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": null, + "frame_rate": null, + "frame_rate_unit": null, + "immersion": null, + "chroma": null, + "sensor_width": null, + "sensor_height": null, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + { + "device_type": "Laser", + "name": "Ex_488", + "serial_number": "VL01222A11", + "manufacturer": { + "name": "Vortran", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "Stradus", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "All lasers controlled via Vortran VersaLase System", + "wavelength": 488, + "wavelength_unit": "nanometer", + "maximum_power": "150", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Ex_561", + "serial_number": "417927", + "manufacturer": { + "name": "Coherent Scientific", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "031tysd23" + }, + "model": "Obis", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "All lasers controlled via Vortran VersaLase System", + "wavelength": 561, + "wavelength_unit": "nanometer", + "maximum_power": "150", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Ex_647", + "serial_number": "VL01222A10", + "manufacturer": { + "name": "Vortran", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "Stradus", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "All lasers controlled via Vortran VersaLase System", + "wavelength": 647, + "wavelength_unit": "nanometer", + "maximum_power": "160", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Motorized stage", + "name": "Focus stage", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-100", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "100", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #1", + "serial_number": null, + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #2", + "serial_number": null, + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #3", + "serial_number": null, + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #4", + "serial_number": null, + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Scanning stage", + "name": "Sample stage Z", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-50", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "50", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Detection axis", + "stage_axis_name": "Z" + }, + { + "device_type": "Scanning stage", + "name": "Sample stage X", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-50", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "50", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Illumination axis", + "stage_axis_name": "X" + }, + { + "device_type": "Scanning stage", + "name": "Sample stage Y", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-50", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "50", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Perpendicular axis", + "stage_axis_name": "Y" + }, + { + "device_type": "Optical table", + "name": "Table", + "serial_number": null, + "manufacturer": { + "name": "Technical Manufacturing Corporation", + "abbreviation": "TMC", + "registry": null, + "registry_identifier": null + }, + "model": "CleanTop", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "length": "35", + "width": "29", + "table_size_unit": "inch", + "vibration_control": true + }, + { + "device_type": "Filter", + "name": "Em_525", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF03-525/50-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 0, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Em_600", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF01-600/52-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 1, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Em_690", + "serial_number": null, + "manufacturer": { + "name": "Chroma", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "ET690/50m", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 2, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Additional imaging device", + "name": "Lens 1", + "serial_number": null, + "manufacturer": { + "name": "Optotune", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "EL-16-40-TC", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "imaging_device_type": "Tunable lens" + }, + { + "device_type": "Additional imaging device", + "name": "Lens 2", + "serial_number": null, + "manufacturer": { + "name": "Optotune", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "EL-16-40-TC", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "imaging_device_type": "Tunable lens" + }, + { + "device_type": "Additional imaging device", + "name": "Sample chamber", + "serial_number": null, + "manufacturer": { + "name": "LifeCanvas", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "Large-uncoated-glass", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "imaging_device_type": "Sample Chamber" + } + ] +} \ No newline at end of file diff --git a/examples/aibs_smartspim_rig.py b/examples/aibs_smartspim_rig.py index 3e745e59a..6ad750437 100644 --- a/examples/aibs_smartspim_rig.py +++ b/examples/aibs_smartspim_rig.py @@ -19,7 +19,7 @@ from aind_data_schema_models.modalities import Modality from aind_data_schema.core.rig import Com, Rig -objective = Objective( +objective = Objective( name="TLX Objective", numerical_aperture=0.2, magnification=3.6, @@ -29,157 +29,157 @@ notes="Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", ) camera1 = Detector( - name="Camera 1", - detector_type="Camera", - data_interface="USB", - cooling="Air", - manufacturer=Organization.HAMAMATSU, - model="C14440-20UP", - serial_number="001107", - ) + name="Camera 1", + detector_type="Camera", + data_interface="USB", + cooling="Air", + manufacturer=Organization.HAMAMATSU, + model="C14440-20UP", + serial_number="001107", +) laser1 = Laser( - name="Ex_488", - device_type="Laser", - coupling="Single-mode fiber", - wavelength=488, - maximum_power=150, - serial_number="VL01222A11", - manufacturer=Organization.VORTRAN, - model="Stradus", - notes="All lasers controlled via Vortran VersaLase System", - ) + name="Ex_488", + device_type="Laser", + coupling="Single-mode fiber", + wavelength=488, + maximum_power=150, + serial_number="VL01222A11", + manufacturer=Organization.VORTRAN, + model="Stradus", + notes="All lasers controlled via Vortran VersaLase System", +) laser2 = Laser( - name="Ex_561", - device_type="Laser", - coupling="Single-mode fiber", - wavelength=561, - maximum_power=150, - serial_number="417927", - manufacturer=Organization.COHERENT_SCIENTIFIC, - model="Obis", - notes="All lasers controlled via Vortran VersaLase System", - ) + name="Ex_561", + device_type="Laser", + coupling="Single-mode fiber", + wavelength=561, + maximum_power=150, + serial_number="417927", + manufacturer=Organization.COHERENT_SCIENTIFIC, + model="Obis", + notes="All lasers controlled via Vortran VersaLase System", +) laser3 = Laser( - name="Ex_647", - device_type="Laser", - coupling="Single-mode fiber", - wavelength=647, - maximum_power=160, - serial_number="VL01222A10", - manufacturer=Organization.VORTRAN, - model="Stradus", - notes="All lasers controlled via Vortran VersaLase System", - ) + name="Ex_647", + device_type="Laser", + coupling="Single-mode fiber", + wavelength=647, + maximum_power=160, + serial_number="VL01222A10", + manufacturer=Organization.VORTRAN, + model="Stradus", + notes="All lasers controlled via Vortran VersaLase System", +) stage0 = MotorizedStage( - name="Focus stage", - model="LS-100", - manufacturer=Organization.ASI, - travel=100, - ) + name="Focus stage", + model="LS-100", + manufacturer=Organization.ASI, + travel=100, +) stage1 = MotorizedStage( - name="Cylindrical lens #1", - model="L12-20F-4", - manufacturer=Organization.IR_ROBOT_CO, - travel=41, - ) + name="Cylindrical lens #1", + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + travel=41, +) stage2 = MotorizedStage( - name="Cylindrical lens #2", - model="L12-20F-4", - manufacturer=Organization.IR_ROBOT_CO, - travel=41, - ) + name="Cylindrical lens #2", + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + travel=41, +) stage3 = MotorizedStage( - name="Cylindrical lens #3", - model="L12-20F-4", - manufacturer=Organization.IR_ROBOT_CO, - travel=41, - ) + name="Cylindrical lens #3", + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + travel=41, +) stage4 = MotorizedStage( - name="Cylindrical lens #4", - model="L12-20F-4", - manufacturer=Organization.IR_ROBOT_CO, - travel=41, - ) + name="Cylindrical lens #4", + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + travel=41, +) scan_stage1 = ScanningStage( - name="Sample stage Z", - model="LS-50", - manufacturer=Organization.ASI, - stage_axis_direction="Detection axis", - stage_axis_name="Z", - travel=50, - ) + name="Sample stage Z", + model="LS-50", + manufacturer=Organization.ASI, + stage_axis_direction="Detection axis", + stage_axis_name="Z", + travel=50, +) scan_stage2 = ScanningStage( - name="Sample stage X", - model="LS-50", - manufacturer=Organization.ASI, - stage_axis_direction="Illumination axis", - stage_axis_name="X", - travel=50, - ) + name="Sample stage X", + model="LS-50", + manufacturer=Organization.ASI, + stage_axis_direction="Illumination axis", + stage_axis_name="X", + travel=50, +) scan_stage3 = ScanningStage( - name="Sample stage Y", - model="LS-50", - manufacturer=Organization.ASI, - stage_axis_direction="Perpendicular axis", - stage_axis_name="Y", - travel=50, - ) + name="Sample stage Y", + model="LS-50", + manufacturer=Organization.ASI, + stage_axis_direction="Perpendicular axis", + stage_axis_name="Y", + travel=50, +) table = OpticalTable( - name="Table", - model="CleanTop", # model="VIS2424-IG2-125A", # ~3 months - length=35, # length=24, - width=29, # width=24, - vibration_control=True, - manufacturer=Organization.TMC, - ) + name="Table", + model="CleanTop", # model="VIS2424-IG2-125A", # ~3 months + length=35, # length=24, + width=29, # width=24, + vibration_control=True, + manufacturer=Organization.TMC, +) filter0 = Filter( - name="Em_525", - filter_type="Band pass", - manufacturer=Organization.SEMROCK, - diameter=25, - thickness=2.0, - thickness_unit=SizeUnit.MM, - model="FF03-525/50-25", - filter_wheel_index=0, - ) + name="Em_525", + filter_type="Band pass", + manufacturer=Organization.SEMROCK, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FF03-525/50-25", + filter_wheel_index=0, +) filter1 = Filter( - name="Em_600", - filter_type="Band pass", - manufacturer=Organization.SEMROCK, - diameter=25, - thickness=2.0, - thickness_unit=SizeUnit.MM, - model="FF01-600/52-25", - filter_wheel_index=1, - ) + name="Em_600", + filter_type="Band pass", + manufacturer=Organization.SEMROCK, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FF01-600/52-25", + filter_wheel_index=1, +) filter2 = Filter( - name="Em_690", - filter_type="Band pass", - manufacturer=Organization.CHROMA, - diameter=25, - thickness=2.0, - thickness_unit=SizeUnit.MM, - model="ET690/50m", - filter_wheel_index=2, - ) + name="Em_690", + filter_type="Band pass", + manufacturer=Organization.CHROMA, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="ET690/50m", + filter_wheel_index=2, +) lens1 = AdditionalImagingDevice( - name="Lens 1", - imaging_device_type="Tunable lens", - manufacturer=Organization.OPTOTUNE, - model="EL-16-40-TC", - ) + name="Lens 1", + imaging_device_type="Tunable lens", + manufacturer=Organization.OPTOTUNE, + model="EL-16-40-TC", +) lens2 = AdditionalImagingDevice( - name="Lens 2", - imaging_device_type="Tunable lens", - manufacturer=Organization.OPTOTUNE, - model="EL-16-40-TC", - ) + name="Lens 2", + imaging_device_type="Tunable lens", + manufacturer=Organization.OPTOTUNE, + model="EL-16-40-TC", +) lens3 = AdditionalImagingDevice( - name="Sample chamber", - imaging_device_type="Sample Chamber", - manufacturer=Organization.LIFECANVAS, - model="Large-uncoated-glass", - ) + name="Sample chamber", + imaging_device_type="Sample Chamber", + manufacturer=Organization.LIFECANVAS, + model="Large-uncoated-glass", +) inst = Rig( rig_id="440_SmartSPIM2_20231004", diff --git a/examples/aind_smartspim_instrument.py b/examples/aind_smartspim_instrument.py deleted file mode 100644 index 104736892..000000000 --- a/examples/aind_smartspim_instrument.py +++ /dev/null @@ -1,264 +0,0 @@ -""" example SmartSPIM instrument """ - -import datetime - -from aind_data_schema_models.organizations import Organization -from aind_data_schema_models.units import SizeUnit - -from aind_data_schema.components.devices import Filter, Laser, MotorizedStage, OpticalTable, ScanningStage -from aind_data_schema.core.instrument import Com, Detector, Instrument, Objective - -inst = Instrument( - instrument_id="440_SmartSPIM1_20231004", - instrument_type="SmartSPIM", - manufacturer=Organization.LIFECANVAS, - modification_date=datetime.date(2023, 10, 4), - objectives=[ - Objective( - name="TLX Objective 1", - numerical_aperture=0.2, - magnification=3.6, - manufacturer=Organization.LIFECANVAS, - immersion="multi", - notes="Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", - serial_number="Unknown-1", - ), - Objective( - name="TLX Objective 2", - numerical_aperture=0.12, - magnification=1.625, - manufacturer=Organization.LIFECANVAS, - immersion="multi", - notes="Thorlabs TL2X-SAP with LifeCanvas dipping cap and correction optics.", - serial_number="Unknown-2", - ), - ], - detectors=[ - Detector( - detector_type="Camera", - data_interface="USB", - name="Camera 1", - cooling="Air", - manufacturer=Organization.HAMAMATSU, - model="C14440-20UP", - serial_number="001284", - ), - ], - light_sources=[ - Laser( - name="Ex_445", - device_type="Laser", - coupling="Single-mode fiber", - wavelength=445, - maximum_power=200, - serial_number="VL08223M03", - manufacturer=Organization.VORTRAN, - ), - Laser( - name="Ex_488", - device_type="Laser", - coupling="Single-mode fiber", - wavelength=488, - maximum_power=150, - serial_number="VL08223M03", - manufacturer=Organization.VORTRAN, - ), - Laser( - name="Ex_561", - device_type="Laser", - coupling="Single-mode fiber", - wavelength=561, - maximum_power=150, - serial_number="VL08223M03", - manufacturer=Organization.VORTRAN, - ), - Laser( - name="Ex_594", - device_type="Laser", - coupling="Single-mode fiber", - wavelength=594, - maximum_power=100, - serial_number="VL08223M03", - manufacturer=Organization.VORTRAN, - ), - Laser( - name="Ex_639", - device_type="Laser", - coupling="Single-mode fiber", - wavelength=639, - maximum_power=160, - serial_number="VL08223M03", - manufacturer=Organization.VORTRAN, - ), - Laser( - name="Ex_690", - device_type="Laser", - coupling="Single-mode fiber", - wavelength=690, - maximum_power=160, - serial_number="VL08223M03", - manufacturer=Organization.VORTRAN, - ), - ], - fluorescence_filters=[ - Filter( - name="Em_469", - filter_type="Band pass", - manufacturer=Organization.SEMROCK, - diameter=25, - thickness=2.0, - thickness_unit=SizeUnit.MM, - model="FF01-469/35-25", - filter_wheel_index=0, - serial_number="Unknown-0", - ), - Filter( - name="Em_525", - filter_type="Band pass", - manufacturer=Organization.SEMROCK, - diameter=25, - thickness=2.0, - thickness_unit=SizeUnit.MM, - model="FF01-525/45-25", - filter_wheel_index=1, - serial_number="Unknown-1", - ), - Filter( - name="Em_593", - filter_type="Band pass", - manufacturer=Organization.SEMROCK, - diameter=25, - thickness=2.0, - thickness_unit=SizeUnit.MM, - model="FF01-593/40-25", - filter_wheel_index=2, - serial_number="Unknown-2", - ), - Filter( - name="Em_624", - filter_type="Band pass", - manufacturer=Organization.SEMROCK, - diameter=25, - thickness=2.0, - thickness_unit=SizeUnit.MM, - model="FF01-624/40-25", - filter_wheel_index=3, - serial_number="Unknown-3", - ), - Filter( - name="Em_667", - filter_type="Band pass", - manufacturer=Organization.CHROMA, - diameter=25, - thickness=2.0, - thickness_unit=SizeUnit.MM, - model="ET667/30m", - filter_wheel_index=4, - serial_number="Unknown-4", - ), - Filter( - name="Em_700", - filter_type="Long pass", - manufacturer=Organization.THORLABS, - diameter=25, - thickness=2.0, - thickness_unit=SizeUnit.MM, - model="FELH0700", - filter_wheel_index=5, - serial_number="Unknown-5", - ), - ], - motorized_stages=[ - MotorizedStage( - model="LS-100", - manufacturer=Organization.ASI, - serial_number="Unknown-1", - travel=100, - name="Focus stage", - ), - MotorizedStage( - model="L12-20F-4", - manufacturer=Organization.IR_ROBOT_CO, - serial_number="Unknown-5", - travel=41, - name="Cylindrical lens #1", - ), - MotorizedStage( - model="L12-20F-4", - manufacturer=Organization.IR_ROBOT_CO, - serial_number="Unknown-6", - travel=41, - name="Cylindrical lens #2", - ), - MotorizedStage( - model="L12-20F-4", - manufacturer=Organization.IR_ROBOT_CO, - serial_number="Unknown-7", - travel=41, - name="Cylindrical lens #3", - ), - MotorizedStage( - model="L12-20F-4", - manufacturer=Organization.IR_ROBOT_CO, - serial_number="Unknown-8", - travel=41, - name="Cylindrical lens #4", - ), - ], - scanning_stages=[ - ScanningStage( - model="LS-50", - manufacturer=Organization.ASI, - serial_number="Unknown-2", - stage_axis_direction="Detection axis", - stage_axis_name="Z", - travel=50, - name="Sample stage Z", - ), - ScanningStage( - model="LS-50", - manufacturer=Organization.ASI, - serial_number="Unknown-3", - stage_axis_direction="Illumination axis", - stage_axis_name="X", - travel=50, - name="Sample stage X", - ), - ScanningStage( - model="LS-50", - manufacturer=Organization.ASI, - serial_number="Unknown-4", - stage_axis_direction="Perpendicular axis", - stage_axis_name="Y", - travel=50, - name="Sample stage Y", - ), - ], - optical_tables=[ - OpticalTable( - name="Main optical table", - length=36, - width=48, - vibration_control=False, - model="VIS2424-IG2-125A", - manufacturer=Organization.MKS_NEWPORT, - serial_number="Unknown", - ) - ], - com_ports=[ - Com(hardware_name="Laser Launch", com_port="COM4"), - Com( - hardware_name="ASI Tiger", - com_port="COM3", - ), - Com( - hardware_name="MightyZap", - com_port="COM9", - ), - ], - temperature_control=False, -) - -serialized = inst.model_dump_json() -deserialized = Instrument.model_validate_json(serialized) -deserialized.write_standard_file(prefix="aind_smartspim") diff --git a/examples/aind_smartspim_instrument.json b/examples/aind_smartspim_rig.json similarity index 92% rename from examples/aind_smartspim_instrument.json rename to examples/aind_smartspim_rig.json index 6eb6dd442..396f8d12f 100644 --- a/examples/aind_smartspim_instrument.json +++ b/examples/aind_smartspim_rig.json @@ -1,8 +1,33 @@ { - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/instrument.py", + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", "schema_version": "1.0.5", - "instrument_id": "440_SmartSPIM1_20231004", + "rig_id": "440_SmartSPIM1_20231004", + "mouse_platform": null, "modification_date": "2023-10-04", + "calibrations": null, + "ccf_coordinate_transform": null, + "origin": null, + "rig_axes": null, + "modalities": [ + { + "name": "Selective plane illumination microscopy", + "abbreviation": "SPIM" + } + ], + "com_ports": [ + { + "hardware_name": "Laser Launch", + "com_port": "COM4" + }, + { + "hardware_name": "ASI Tiger", + "com_port": "COM3" + }, + { + "hardware_name": "MightyZap", + "com_port": "COM9" + } + ], "instrument_type": "SmartSPIM", "manufacturer": { "name": "LifeCanvas", @@ -11,33 +36,9 @@ "registry_identifier": null }, "temperature_control": false, - "optical_tables": [ - { - "device_type": "Optical table", - "name": "Main optical table", - "serial_number": "Unknown", - "manufacturer": { - "name": "MKS Newport", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "00k17f049" - }, - "model": "VIS2424-IG2-125A", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "length": "36", - "width": "48", - "table_size_unit": "inch", - "vibration_control": false - } - ], - "enclosure": null, - "objectives": [ + "notes": null, + "connections": [], + "components": [ { "device_type": "Objective", "name": "TLX Objective 1", @@ -51,7 +52,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", "numerical_aperture": "0.2", "magnification": "3.6", @@ -71,15 +72,13 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Thorlabs TL2X-SAP with LifeCanvas dipping cap and correction optics.", "numerical_aperture": "0.12", "magnification": "1.625", "immersion": "multi", "objective_type": null - } - ], - "detectors": [ + }, { "device_type": "Detector", "name": "Camera 1", @@ -96,7 +95,7 @@ "model": "C14440-20UP", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -125,9 +124,7 @@ "recording_software": null, "driver": null, "driver_version": null - } - ], - "light_sources": [ + }, { "device_type": "Laser", "name": "Ex_445", @@ -141,7 +138,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 445, "wavelength_unit": "nanometer", @@ -165,7 +162,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 488, "wavelength_unit": "nanometer", @@ -189,7 +186,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 561, "wavelength_unit": "nanometer", @@ -213,7 +210,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 594, "wavelength_unit": "nanometer", @@ -237,7 +234,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 639, "wavelength_unit": "nanometer", @@ -261,7 +258,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 690, "wavelength_unit": "nanometer", @@ -271,10 +268,7 @@ "coupling_efficiency": null, "coupling_efficiency_unit": "percent", "item_number": null - } - ], - "lenses": [], - "fluorescence_filters": [ + }, { "device_type": "Filter", "name": "Em_469", @@ -288,7 +282,7 @@ "model": "FF01-469/35-25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -317,7 +311,7 @@ "model": "FF01-525/45-25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -346,7 +340,7 @@ "model": "FF01-593/40-25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -375,7 +369,7 @@ "model": "FF01-624/40-25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -404,7 +398,7 @@ "model": "ET667/30m", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -436,7 +430,7 @@ "model": "FELH0700", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Long pass", "diameter": "25", @@ -451,9 +445,7 @@ "center_wavelength": null, "wavelength_unit": "nanometer", "description": null - } - ], - "motorized_stages": [ + }, { "device_type": "Motorized stage", "name": "Focus stage", @@ -467,7 +459,7 @@ "model": "LS-100", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "100", "travel_unit": "millimeter", @@ -486,7 +478,7 @@ "model": "L12-20F-4", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "41", "travel_unit": "millimeter", @@ -505,7 +497,7 @@ "model": "L12-20F-4", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "41", "travel_unit": "millimeter", @@ -524,7 +516,7 @@ "model": "L12-20F-4", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "41", "travel_unit": "millimeter", @@ -543,16 +535,14 @@ "model": "L12-20F-4", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "41", "travel_unit": "millimeter", "firmware": null - } - ], - "scanning_stages": [ + }, { - "device_type": "Motorized stage", + "device_type": "Scanning stage", "name": "Sample stage Z", "serial_number": "Unknown-2", "manufacturer": { @@ -564,7 +554,7 @@ "model": "LS-50", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "50", "travel_unit": "millimeter", @@ -573,7 +563,7 @@ "stage_axis_name": "Z" }, { - "device_type": "Motorized stage", + "device_type": "Scanning stage", "name": "Sample stage X", "serial_number": "Unknown-3", "manufacturer": { @@ -585,7 +575,7 @@ "model": "LS-50", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "50", "travel_unit": "millimeter", @@ -594,7 +584,7 @@ "stage_axis_name": "X" }, { - "device_type": "Motorized stage", + "device_type": "Scanning stage", "name": "Sample stage Y", "serial_number": "Unknown-4", "manufacturer": { @@ -606,32 +596,36 @@ "model": "LS-50", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "50", "travel_unit": "millimeter", "firmware": null, "stage_axis_direction": "Perpendicular axis", "stage_axis_name": "Y" - } - ], - "additional_devices": [], - "calibration_date": null, - "calibration_data": null, - "com_ports": [ - { - "hardware_name": "Laser Launch", - "com_port": "COM4" }, { - "hardware_name": "ASI Tiger", - "com_port": "COM3" - }, - { - "hardware_name": "MightyZap", - "com_port": "COM9" + "device_type": "Optical table", + "name": "Main optical table", + "serial_number": "Unknown", + "manufacturer": { + "name": "MKS Newport", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "00k17f049" + }, + "model": "VIS2424-IG2-125A", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "length": "36", + "width": "48", + "table_size_unit": "inch", + "vibration_control": false } - ], - "daqs": [], - "notes": null + ] } \ No newline at end of file diff --git a/examples/aind_smartspim_rig.py b/examples/aind_smartspim_rig.py new file mode 100644 index 000000000..4dad6fd32 --- /dev/null +++ b/examples/aind_smartspim_rig.py @@ -0,0 +1,294 @@ +""" example SmartSPIM instrument """ + +import datetime + +from aind_data_schema_models.organizations import Organization +from aind_data_schema_models.units import SizeUnit + +from aind_data_schema.components.devices import ( + Filter, + ImagingInstrumentType, + Laser, + MotorizedStage, + OpticalTable, + ScanningStage, +) +from aind_data_schema.core.rig import Com, Detector, Rig, Objective +from aind_data_schema_models.modalities import Modality + +objective_1 = Objective( + name="TLX Objective 1", + numerical_aperture=0.2, + magnification=3.6, + manufacturer=Organization.LIFECANVAS, + immersion="multi", + notes="Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", + serial_number="Unknown-1", +) + +objective_2 = Objective( + name="TLX Objective 2", + numerical_aperture=0.12, + magnification=1.625, + manufacturer=Organization.LIFECANVAS, + immersion="multi", + notes="Thorlabs TL2X-SAP with LifeCanvas dipping cap and correction optics.", + serial_number="Unknown-2", +) + +detector_1 = Detector( + detector_type="Camera", + data_interface="USB", + name="Camera 1", + cooling="Air", + manufacturer=Organization.HAMAMATSU, + model="C14440-20UP", + serial_number="001284", +) + +laser_1 = Laser( + name="Ex_445", + coupling="Single-mode fiber", + wavelength=445, + maximum_power=200, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +laser_2 = Laser( + name="Ex_488", + coupling="Single-mode fiber", + wavelength=488, + maximum_power=150, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +laser_3 = Laser( + name="Ex_561", + coupling="Single-mode fiber", + wavelength=561, + maximum_power=150, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +laser_4 = Laser( + name="Ex_594", + coupling="Single-mode fiber", + wavelength=594, + maximum_power=100, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +laser_5 = Laser( + name="Ex_639", + coupling="Single-mode fiber", + wavelength=639, + maximum_power=160, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +laser_6 = Laser( + name="Ex_690", + coupling="Single-mode fiber", + wavelength=690, + maximum_power=160, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +filter_1 = Filter( + name="Em_469", + filter_type="Band pass", + manufacturer=Organization.SEMROCK, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FF01-469/35-25", + filter_wheel_index=0, + serial_number="Unknown-0", +) + +filter_2 = Filter( + name="Em_525", + filter_type="Band pass", + manufacturer=Organization.SEMROCK, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FF01-525/45-25", + filter_wheel_index=1, + serial_number="Unknown-1", +) + +filter_3 = Filter( + name="Em_593", + filter_type="Band pass", + manufacturer=Organization.SEMROCK, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FF01-593/40-25", + filter_wheel_index=2, + serial_number="Unknown-2", +) + +filter_4 = Filter( + name="Em_624", + filter_type="Band pass", + manufacturer=Organization.SEMROCK, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FF01-624/40-25", + filter_wheel_index=3, + serial_number="Unknown-3", +) + +filter_5 = Filter( + name="Em_667", + filter_type="Band pass", + manufacturer=Organization.CHROMA, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="ET667/30m", + filter_wheel_index=4, + serial_number="Unknown-4", +) + +filter_6 = Filter( + name="Em_700", + filter_type="Long pass", + manufacturer=Organization.THORLABS, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FELH0700", + filter_wheel_index=5, + serial_number="Unknown-5", +) + +motorized_stage_1 = MotorizedStage( + model="LS-100", + manufacturer=Organization.ASI, + serial_number="Unknown-1", + travel=100, + name="Focus stage", +) + +motorized_stage_2 = MotorizedStage( + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + serial_number="Unknown-5", + travel=41, + name="Cylindrical lens #1", +) + +motorized_stage_3 = MotorizedStage( + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + serial_number="Unknown-6", + travel=41, + name="Cylindrical lens #2", +) + +motorized_stage_4 = MotorizedStage( + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + serial_number="Unknown-7", + travel=41, + name="Cylindrical lens #3", +) + +motorized_stage_5 = MotorizedStage( + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + serial_number="Unknown-8", + travel=41, + name="Cylindrical lens #4", +) + +scanning_stage_1 = ScanningStage( + model="LS-50", + manufacturer=Organization.ASI, + serial_number="Unknown-2", + stage_axis_direction="Detection axis", + stage_axis_name="Z", + travel=50, + name="Sample stage Z", +) + +scanning_stage_2 = ScanningStage( + model="LS-50", + manufacturer=Organization.ASI, + serial_number="Unknown-3", + stage_axis_direction="Illumination axis", + stage_axis_name="X", + travel=50, + name="Sample stage X", +) + +scanning_stage_3 = ScanningStage( + model="LS-50", + manufacturer=Organization.ASI, + serial_number="Unknown-4", + stage_axis_direction="Perpendicular axis", + stage_axis_name="Y", + travel=50, + name="Sample stage Y", +) + +optical_table_1 = OpticalTable( + name="Main optical table", + length=36, + width=48, + vibration_control=False, + model="VIS2424-IG2-125A", + manufacturer=Organization.MKS_NEWPORT, + serial_number="Unknown", +) + +objectives = [objective_1, objective_2] +detectors = [detector_1] +lasers = [laser_1, laser_2, laser_3, laser_4, laser_5, laser_6] +fluorescence_filters = [filter_1, filter_2, filter_3, filter_4, filter_5, filter_6] +motorized_stages = [motorized_stage_1, motorized_stage_2, motorized_stage_3, motorized_stage_4, motorized_stage_5] +scanning_stages = [scanning_stage_1, scanning_stage_2, scanning_stage_3] +optical_tables = [optical_table_1] + +inst = Rig( + rig_id="440_SmartSPIM1_20231004", + instrument_type=ImagingInstrumentType.SMARTSPIM, + manufacturer=Organization.LIFECANVAS, + modification_date=datetime.date(2023, 10, 4), + modalities=[Modality.SPIM], + components=[ + *objectives, + *detectors, + *lasers, + *fluorescence_filters, + *motorized_stages, + *scanning_stages, + *optical_tables, + ], + com_ports=[ + Com(hardware_name="Laser Launch", com_port="COM4"), + Com( + hardware_name="ASI Tiger", + com_port="COM3", + ), + Com( + hardware_name="MightyZap", + com_port="COM9", + ), + ], + temperature_control=False, +) + +serialized = inst.model_dump_json() +deserialized = Rig.model_validate_json(serialized) +deserialized.write_standard_file(prefix="aind_smartspim") diff --git a/examples/ephys_rig.json b/examples/ephys_rig.json index dd29ab961..ed7d4b035 100644 --- a/examples/ephys_rig.json +++ b/examples/ephys_rig.json @@ -2,7 +2,6 @@ "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", "schema_version": "1.0.5", "rig_id": "323_EPHYS1_20231003", - "modification_date": "2023-10-03", "mouse_platform": { "device_type": "Disc", "name": "Running Wheel", @@ -11,7 +10,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "surface_material": null, "date_surface_replaced": null, @@ -22,10 +21,140 @@ "decoder": null, "encoder_firmware": null }, - "stimulus_devices": [], - "cameras": [ + "modification_date": "2023-10-03", + "calibrations": [ + { + "calibration_date": "2023-10-02T10:22:13Z", + "device_name": "Red Laser", + "description": "Laser power calibration", + "input": { + "power percent": [ + 10, + 20, + 40 + ] + }, + "output": { + "power mW": [ + 1, + 3, + 6 + ] + }, + "notes": null + }, + { + "calibration_date": "2023-10-02T10:22:13Z", + "device_name": "Blue Laser", + "description": "Laser power calibration", + "input": { + "power percent": [ + 10, + 20, + 40 + ] + }, + "output": { + "power mW": [ + 1, + 2, + 7 + ] + }, + "notes": null + } + ], + "ccf_coordinate_transform": null, + "origin": null, + "rig_axes": null, + "modalities": [ + { + "name": "Extracellular electrophysiology", + "abbreviation": "ecephys" + } + ], + "com_ports": [], + "instrument_type": null, + "manufacturer": null, + "temperature_control": null, + "notes": null, + "connections": [], + "components": [ + { + "name": "Ephys_assemblyA", + "device_type": "Ephys assembly", + "manipulator": { + "device_type": "Manipulator", + "name": "Manipulator 1", + "serial_number": "SN2938", + "manufacturer": { + "name": "New Scale Technologies", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "probes": [ + { + "device_type": "Ephys probe", + "name": "Probe A", + "serial_number": "9291019", + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "probe_model": "Neuropixels 1.0", + "lasers": [], + "headstage": null + } + ] + }, + { + "name": "Ephys_assemblyB", + "device_type": "Ephys assembly", + "manipulator": { + "device_type": "Manipulator", + "name": "Manipulator B", + "serial_number": "SN2939", + "manufacturer": { + "name": "New Scale Technologies", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "probes": [ + { + "device_type": "Ephys probe", + "name": "Probe B", + "serial_number": "9291020", + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "probe_model": "Neuropixels 1.0", + "lasers": [], + "headstage": null + } + ] + }, { "name": "Face Camera Assembly", + "device_type": "Camera assembly", "camera_target": "Face side left", "camera": { "device_type": "Detector", @@ -43,7 +172,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -89,7 +218,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "focal_length": "15", "focal_length_unit": "millimeter", @@ -115,7 +244,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Long pass", "diameter": null, @@ -135,6 +264,7 @@ }, { "name": "Body Camera Assembly", + "device_type": "Camera assembly", "camera_target": "Body", "camera": { "device_type": "Detector", @@ -152,7 +282,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -198,7 +328,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "focal_length": "15", "focal_length_unit": "millimeter", @@ -224,7 +354,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Long pass", "diameter": null, @@ -241,16 +371,14 @@ "description": "850 nm longpass filter" }, "position": null - } - ], - "enclosure": null, - "ephys_assemblies": [ + }, { - "name": "Ephys_assemblyA", + "name": "Laser_assemblyA", + "device_type": "Laser assembly", "manipulator": { "device_type": "Manipulator", - "name": "Manipulator 1", - "serial_number": "SN2938", + "name": "Manipulator A", + "serial_number": "SN2937", "manufacturer": { "name": "New Scale Technologies", "abbreviation": null, @@ -260,66 +388,199 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null }, - "probes": [ + "lasers": [ { - "device_type": "Ephys probe", - "name": "Probe A", - "serial_number": "9291019", - "manufacturer": null, + "device_type": "Laser", + "name": "Red Laser", + "serial_number": null, + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, - "probe_model": "Neuropixels 1.0", - "lasers": [], - "headstage": null + "wavelength": 473, + "wavelength_unit": "nanometer", + "maximum_power": null, + "power_unit": "milliwatt", + "coupling": null, + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Blue Laser", + "serial_number": null, + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 638, + "wavelength_unit": "nanometer", + "maximum_power": null, + "power_unit": "milliwatt", + "coupling": null, + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null } - ] - }, - { - "name": "Ephys_assemblyB", - "manipulator": { - "device_type": "Manipulator", - "name": "Manipulator B", - "serial_number": "SN2939", + ], + "collimator": { + "device_type": "device", + "name": "Collimator A", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "fiber": { + "device_type": "Patch", + "name": "Bundle Branching Fiber-optic Patch Cord", + "serial_number": null, "manufacturer": { - "name": "New Scale Technologies", + "name": "Doric", "abbreviation": null, - "registry": null, - "registry_identifier": null + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "059n53q30" }, - "model": null, + "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, - "notes": null + "additional_settings": null, + "notes": null, + "core_diameter": "200", + "numerical_aperture": "0.37", + "photobleaching_date": null + } + }, + { + "device_type": "Neuropixels basestation", + "name": "Basestation Slot 3", + "serial_number": null, + "manufacturer": { + "name": "Interuniversity Microelectronics Center", + "abbreviation": "IMEC", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "02kcbn207" }, - "probes": [ + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "PXI", + "computer_name": "W10DT72942", + "channels": [], + "firmware_version": null, + "hardware_version": null, + "basestation_firmware_version": "2.019", + "bsc_firmware_version": "2.199", + "slot": 3, + "ports": [ { - "device_type": "Ephys probe", - "name": "Probe B", - "serial_number": "9291020", - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "probe_model": "Neuropixels 1.0", - "lasers": [], - "headstage": null + "index": 1, + "probes": [ + "Probe A" + ] + }, + { + "index": 2, + "probes": [ + "Probe B" + ] } ] - } - ], - "fiber_assemblies": [], - "stick_microscopes": [ + }, + { + "device_type": "Harp device", + "name": "Harp Behavior", + "serial_number": null, + "manufacturer": { + "name": "Open Ephys Production Site", + "abbreviation": "OEPS", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "007rkz355" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "USB", + "computer_name": "W10DT72941", + "channels": [ + { + "channel_name": "DO0", + "device_name": "Face Camera", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DO1", + "device_name": "Body Camera", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "AI0", + "device_name": "Running Wheel", + "channel_type": "Analog Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + } + ], + "firmware_version": null, + "hardware_version": null, + "harp_device_type": { + "whoami": 1216, + "name": "Behavior" + }, + "core_version": "2.1", + "tag_version": null, + "is_clock_generator": false + }, { "name": "Stick_assembly_1", + "device_type": "Camera assembly", "camera_target": "Brain surface", "camera": { "device_type": "Detector", @@ -337,7 +598,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -383,7 +644,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "focal_length": null, "focal_length_unit": null, @@ -398,6 +659,7 @@ }, { "name": "Stick_assembly_2", + "device_type": "Camera assembly", "camera_target": "Brain surface", "camera": { "device_type": "Detector", @@ -415,7 +677,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -461,7 +723,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "focal_length": null, "focal_length_unit": null, @@ -476,6 +738,7 @@ }, { "name": "Stick_assembly_3", + "device_type": "Camera assembly", "camera_target": "Brain surface", "camera": { "device_type": "Detector", @@ -493,7 +756,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -539,7 +802,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "focal_length": null, "focal_length_unit": null, @@ -554,6 +817,7 @@ }, { "name": "Stick_assembly_4", + "device_type": "Camera assembly", "camera_target": "Brain surface", "camera": { "device_type": "Detector", @@ -571,7 +835,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -617,7 +881,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "focal_length": null, "focal_length_unit": null, @@ -630,276 +894,5 @@ "filter": null, "position": null } - ], - "laser_assemblies": [ - { - "name": "Laser_assemblyA", - "manipulator": { - "device_type": "Manipulator", - "name": "Manipulator A", - "serial_number": "SN2937", - "manufacturer": { - "name": "New Scale Technologies", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - }, - "lasers": [ - { - "device_type": "Laser", - "name": "Red Laser", - "serial_number": null, - "manufacturer": { - "name": "Oxxius", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "wavelength": 473, - "wavelength_unit": "nanometer", - "maximum_power": null, - "power_unit": "milliwatt", - "coupling": null, - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Blue Laser", - "serial_number": null, - "manufacturer": { - "name": "Oxxius", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "wavelength": 638, - "wavelength_unit": "nanometer", - "maximum_power": null, - "power_unit": "milliwatt", - "coupling": null, - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - } - ], - "collimator": { - "device_type": "Collimator", - "name": "Collimator A", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - }, - "fiber": { - "device_type": "Patch", - "name": "Bundle Branching Fiber-optic Patch Cord", - "serial_number": null, - "manufacturer": { - "name": "Doric", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "059n53q30" - }, - "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "core_diameter": "200", - "numerical_aperture": "0.37", - "photobleaching_date": null - } - } - ], - "patch_cords": [], - "light_sources": [], - "detectors": [], - "objectives": [], - "filters": [], - "lenses": [], - "digital_micromirror_devices": [], - "polygonal_scanners": [], - "pockels_cells": [], - "additional_devices": [], - "daqs": [ - { - "device_type": "Neuropixels basestation", - "name": "Basestation Slot 3", - "serial_number": null, - "manufacturer": { - "name": "Interuniversity Microelectronics Center", - "abbreviation": "IMEC", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "02kcbn207" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "data_interface": "PXI", - "computer_name": "W10DT72942", - "channels": [], - "firmware_version": null, - "hardware_version": null, - "basestation_firmware_version": "2.019", - "bsc_firmware_version": "2.199", - "slot": 3, - "ports": [ - { - "index": 1, - "probes": [ - "Probe A" - ] - }, - { - "index": 2, - "probes": [ - "Probe B" - ] - } - ] - }, - { - "device_type": "Harp device", - "name": "Harp Behavior", - "serial_number": null, - "manufacturer": { - "name": "Open Ephys Production Site", - "abbreviation": "OEPS", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "007rkz355" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "data_interface": "USB", - "computer_name": "W10DT72941", - "channels": [ - { - "channel_name": "DO0", - "device_name": "Face Camera", - "channel_type": "Digital Output", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DO1", - "device_name": "Body Camera", - "channel_type": "Digital Output", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "AI0", - "device_name": "Running Wheel", - "channel_type": "Analog Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - } - ], - "firmware_version": null, - "hardware_version": null, - "harp_device_type": { - "whoami": 1216, - "name": "Behavior" - }, - "core_version": "2.1", - "tag_version": null, - "is_clock_generator": false - } - ], - "calibrations": [ - { - "calibration_date": "2023-10-02T10:22:13Z", - "device_name": "Red Laser", - "description": "Laser power calibration", - "input": { - "power percent": [ - 10, - 20, - 40 - ] - }, - "output": { - "power mW": [ - 1, - 3, - 6 - ] - }, - "notes": null - }, - { - "calibration_date": "2023-10-02T10:22:13Z", - "device_name": "Blue Laser", - "description": "Laser power calibration", - "input": { - "power percent": [ - 10, - 20, - 40 - ] - }, - "output": { - "power mW": [ - 1, - 2, - 7 - ] - }, - "notes": null - } - ], - "ccf_coordinate_transform": null, - "origin": null, - "rig_axes": null, - "modalities": [ - { - "name": "Extracellular electrophysiology", - "abbreviation": "ecephys" - } - ], - "notes": null + ] } \ No newline at end of file diff --git a/examples/ephys_rig.py b/examples/ephys_rig.py index 4ea6f733d..e623b08ae 100644 --- a/examples/ephys_rig.py +++ b/examples/ephys_rig.py @@ -75,7 +75,7 @@ name="Manipulator A", serial_number="SN2937", manufacturer=Organization.NEW_SCALE_TECHNOLOGIES ), lasers=[red_laser, blue_laser], - collimator=Device(name="Collimator A", device_type="Collimator"), + collimator=Device(name="Collimator A"), fiber=Patch( name="Bundle Branching Fiber-optic Patch Cord", manufacturer=Organization.DORIC, @@ -279,11 +279,19 @@ rig_id="323_EPHYS1_20231003", modification_date=date(2023, 10, 3), modalities=[Modality.ECEPHYS], - ephys_assemblies=[ephys_assemblyA, ephys_assemblyB], - cameras=[camassm1, camassm2], - laser_assemblies=[laser_assembly], - daqs=[basestation, harp], - stick_microscopes=[microscope_1, microscope_2, microscope_3, microscope_4], + components=[ + ephys_assemblyA, + ephys_assemblyB, + camassm1, + camassm2, + laser_assembly, + basestation, + harp, + microscope_1, + microscope_2, + microscope_3, + microscope_4, + ], mouse_platform=running_wheel, calibrations=[red_laser_calibration, blue_laser_calibration], ) diff --git a/examples/exaspim_instrument.py b/examples/exaspim_instrument.py deleted file mode 100644 index 0ddacc86a..000000000 --- a/examples/exaspim_instrument.py +++ /dev/null @@ -1,215 +0,0 @@ -""" example ExaSPIM instrument """ - -import datetime - -from aind_data_schema_models.organizations import Organization -from aind_data_schema_models.units import SizeUnit, FrequencyUnit - -from aind_data_schema.components.devices import DAQChannel, DAQDevice, Detector, Filter, Laser -from aind_data_schema.core import instrument - -inst = instrument.Instrument( - instrument_id="440_exaSPIM1-20231004", - instrument_type="exaSPIM", - modification_date=datetime.date(2023, 10, 4), - manufacturer=Organization.CUSTOM, - objectives=[ - instrument.Objective( - name="Custom Objective", - numerical_aperture=0.305, - magnification=5, - immersion="air", - manufacturer=Organization.OTHER, - model="JM_DIAMOND 5.0X/1.3", - notes="manufacturer collaboration between Schneider-Kreuznach and Vieworks", - ), - ], - detectors=[ - Detector( - name="Camera 1", - detector_type="Camera", - data_interface="Coax", - cooling="Air", - manufacturer=Organization.VIEWORKS, - model="VNP-604MX", - serial_number="MB151BAY001", - ), - ], - light_sources=[ - Laser( - name="LAS-08307", - coupling="Single-mode fiber", - wavelength=405, - maximum_power=200, - serial_number="LAS-08307", - manufacturer=Organization.OXXIUS, - notes="Housed in commercial laser combiner", - ), - Laser( - name="LAS-08308", - coupling="Single-mode fiber", - wavelength=488, - maximum_power=200, - serial_number="LAS-08308", - manufacturer=Organization.OXXIUS, - notes="Housed in commercial laser combiner", - ), - Laser( - name="539251", - coupling="Single-mode fiber", - wavelength=561, - maximum_power=200, - serial_number="539251", - manufacturer=Organization.OXXIUS, - notes="Housed in commercial laser combiner", - ), - Laser( - name="LAS-08309", - coupling="Single-mode fiber", - wavelength=638, - maximum_power=200, - serial_number="LAS-08309", - manufacturer=Organization.OXXIUS, - notes="Housed in commercial laser combiner", - ), - ], - fluorescence_filters=[ - Filter( - name="Multiband filter", - filter_type="Multiband", - manufacturer=Organization.CHROMA, - diameter=44.05, - thickness=1, - thickness_unit=SizeUnit.MM, - model="ZET405/488/561/640mv2", - notes="Custom made filter", - filter_wheel_index=0, - ) - ], - daqs=[ - DAQDevice( - model="PCIe-6738", - data_interface="USB", - computer_name="Dev2", - manufacturer=Organization.NATIONAL_INSTRUMENTS, - name="Dev2", - channels=[ - DAQChannel( - channel_name="3", - channel_type="Analog Output", - device_name="LAS-08308", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="5", - channel_type="Analog Output", - device_name="539251", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="4", - channel_type="Analog Output", - device_name="LAS-08309", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="2", - channel_type="Analog Output", - device_name="stage-x", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="0", - channel_type="Analog Output", - device_name="TL-1", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="6", - channel_type="Analog Output", - device_name="LAS-08307", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - ], - ) - ], - scanning_stages=[ - instrument.ScanningStage( - name="stage-x", - stage_axis_direction="Detection axis", - stage_axis_name="X", - travel=1000, - model="MS-8000", - manufacturer=Organization.ASI, - ), - instrument.ScanningStage( - name="stage-y", - stage_axis_direction="Perpendicular axis", - stage_axis_name="Y", - travel=1000, - model="MS-8000", - manufacturer=Organization.ASI, - ), - instrument.ScanningStage( - name="stage-z", - stage_axis_direction="Illumination axis", - stage_axis_name="Z", - travel=100, - model="LS-100", - manufacturer=Organization.ASI, - ), - ], - additional_devices=[ - instrument.AdditionalImagingDevice( - imaging_device_type="Tunable lens", - name="TL-1", - manufacturer=Organization.OPTOTUNE, - model="EL-16-40-TC-VIS-20D-C", - serial_number="01", - ), - instrument.AdditionalImagingDevice( - name="RM-1", - imaging_device_type="Rotation mount", - manufacturer=Organization.THORLABS, - model="K10CR1", - serial_number="01", - ), - instrument.AdditionalImagingDevice( - name="LC-1", - imaging_device_type="Laser combiner", - manufacturer=Organization.OXXIUS, - model="L6Cc", - serial_number="L6CC-00513", - ), - ], - optical_tables=[ - instrument.OpticalTable( - name="Table", - length=36, - width=48, - vibration_control=True, - model="VIS3648-PG2-325A", - manufacturer=Organization.MKS_NEWPORT, - ) - ], - com_ports=[ - instrument.Com( - hardware_name="Laser Launch", - com_port="COM2", - ), - instrument.Com( - hardware_name="ASI Tiger", - com_port="COM5", - ), - ], - temperature_control=False, -) -serialized = inst.model_dump_json() -deserialized = instrument.Instrument.model_validate_json(serialized) -deserialized.write_standard_file(prefix="exaspim") diff --git a/examples/exaspim_instrument.json b/examples/exaspim_rig.json similarity index 91% rename from examples/exaspim_instrument.json rename to examples/exaspim_rig.json index bf2cd4cc9..dc5217382 100644 --- a/examples/exaspim_instrument.json +++ b/examples/exaspim_rig.json @@ -1,8 +1,29 @@ { - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/instrument.py", + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", "schema_version": "1.0.5", - "instrument_id": "440_exaSPIM1-20231004", + "rig_id": "440_exaSPIM1_20231004", + "mouse_platform": null, "modification_date": "2023-10-04", + "calibrations": null, + "ccf_coordinate_transform": null, + "origin": null, + "rig_axes": null, + "modalities": [ + { + "name": "Selective plane illumination microscopy", + "abbreviation": "SPIM" + } + ], + "com_ports": [ + { + "hardware_name": "Laser Launch", + "com_port": "COM2" + }, + { + "hardware_name": "ASI Tiger", + "com_port": "COM5" + } + ], "instrument_type": "exaSPIM", "manufacturer": { "name": "Custom", @@ -11,33 +32,9 @@ "registry_identifier": null }, "temperature_control": false, - "optical_tables": [ - { - "device_type": "Optical table", - "name": "Table", - "serial_number": null, - "manufacturer": { - "name": "MKS Newport", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "00k17f049" - }, - "model": "VIS3648-PG2-325A", - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "length": "36", - "width": "48", - "table_size_unit": "inch", - "vibration_control": true - } - ], - "enclosure": null, - "objectives": [ + "notes": null, + "connections": [], + "components": [ { "device_type": "Objective", "name": "Custom Objective", @@ -51,15 +48,13 @@ "model": "JM_DIAMOND 5.0X/1.3", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "manufacturer collaboration between Schneider-Kreuznach and Vieworks", "numerical_aperture": "0.305", "magnification": "5", "immersion": "air", "objective_type": null - } - ], - "detectors": [ + }, { "device_type": "Detector", "name": "Camera 1", @@ -73,7 +68,7 @@ "model": "VNP-604MX", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "Coax", @@ -102,9 +97,7 @@ "recording_software": null, "driver": null, "driver_version": null - } - ], - "light_sources": [ + }, { "device_type": "Laser", "name": "LAS-08307", @@ -118,7 +111,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Housed in commercial laser combiner", "wavelength": 405, "wavelength_unit": "nanometer", @@ -142,7 +135,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Housed in commercial laser combiner", "wavelength": 488, "wavelength_unit": "nanometer", @@ -166,7 +159,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Housed in commercial laser combiner", "wavelength": 561, "wavelength_unit": "nanometer", @@ -190,7 +183,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Housed in commercial laser combiner", "wavelength": 638, "wavelength_unit": "nanometer", @@ -200,10 +193,7 @@ "coupling_efficiency": null, "coupling_efficiency_unit": "percent", "item_number": null - } - ], - "lenses": [], - "fluorescence_filters": [ + }, { "device_type": "Filter", "name": "Multiband filter", @@ -217,7 +207,7 @@ "model": "ZET405/488/561/640mv2", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Custom made filter", "filter_type": "Multiband", "diameter": "44.05", @@ -232,12 +222,94 @@ "center_wavelength": null, "wavelength_unit": "nanometer", "description": null - } - ], - "motorized_stages": [], - "scanning_stages": [ + }, { - "device_type": "Motorized stage", + "device_type": "DAQ Device", + "name": "Dev2", + "serial_number": null, + "manufacturer": { + "name": "National Instruments", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "026exqw73" + }, + "model": "PCIe-6738", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "USB", + "computer_name": "Dev2", + "channels": [ + { + "channel_name": "3", + "device_name": "LAS-08308", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + }, + { + "channel_name": "5", + "device_name": "539251", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + }, + { + "channel_name": "4", + "device_name": "LAS-08309", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + }, + { + "channel_name": "2", + "device_name": "stage-x", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + }, + { + "channel_name": "0", + "device_name": "TL-1", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + }, + { + "channel_name": "6", + "device_name": "LAS-08307", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + } + ], + "firmware_version": null, + "hardware_version": null + }, + { + "device_type": "Scanning stage", "name": "stage-x", "serial_number": null, "manufacturer": { @@ -249,7 +321,7 @@ "model": "MS-8000", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "1000", "travel_unit": "millimeter", @@ -258,7 +330,7 @@ "stage_axis_name": "X" }, { - "device_type": "Motorized stage", + "device_type": "Scanning stage", "name": "stage-y", "serial_number": null, "manufacturer": { @@ -270,7 +342,7 @@ "model": "MS-8000", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "1000", "travel_unit": "millimeter", @@ -279,7 +351,7 @@ "stage_axis_name": "Y" }, { - "device_type": "Motorized stage", + "device_type": "Scanning stage", "name": "stage-z", "serial_number": null, "manufacturer": { @@ -291,16 +363,14 @@ "model": "LS-100", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "travel": "100", "travel_unit": "millimeter", "firmware": null, "stage_axis_direction": "Illumination axis", "stage_axis_name": "Z" - } - ], - "additional_devices": [ + }, { "device_type": "Additional imaging device", "name": "TL-1", @@ -314,7 +384,7 @@ "model": "EL-16-40-TC-VIS-20D-C", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "imaging_device_type": "Tunable lens" }, @@ -334,7 +404,7 @@ "model": "K10CR1", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "imaging_device_type": "Rotation mount" }, @@ -351,109 +421,32 @@ "model": "L6Cc", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "imaging_device_type": "Laser combiner" - } - ], - "calibration_date": null, - "calibration_data": null, - "com_ports": [ - { - "hardware_name": "Laser Launch", - "com_port": "COM2" }, { - "hardware_name": "ASI Tiger", - "com_port": "COM5" - } - ], - "daqs": [ - { - "device_type": "DAQ Device", - "name": "Dev2", + "device_type": "Optical table", + "name": "Table", "serial_number": null, "manufacturer": { - "name": "National Instruments", + "name": "MKS Newport", "abbreviation": null, "registry": { "name": "Research Organization Registry", "abbreviation": "ROR" }, - "registry_identifier": "026exqw73" + "registry_identifier": "00k17f049" }, - "model": "PCIe-6738", + "model": "VIS3648-PG2-325A", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, - "data_interface": "USB", - "computer_name": "Dev2", - "channels": [ - { - "channel_name": "3", - "device_name": "LAS-08308", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - }, - { - "channel_name": "5", - "device_name": "539251", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - }, - { - "channel_name": "4", - "device_name": "LAS-08309", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - }, - { - "channel_name": "2", - "device_name": "stage-x", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - }, - { - "channel_name": "0", - "device_name": "TL-1", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - }, - { - "channel_name": "6", - "device_name": "LAS-08307", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - } - ], - "firmware_version": null, - "hardware_version": null + "length": "36", + "width": "48", + "table_size_unit": "inch", + "vibration_control": true } - ], - "notes": null + ] } \ No newline at end of file diff --git a/examples/exaspim_rig.py b/examples/exaspim_rig.py new file mode 100644 index 000000000..32130c9a6 --- /dev/null +++ b/examples/exaspim_rig.py @@ -0,0 +1,236 @@ +""" example ExaSPIM instrument """ + +import datetime + +from aind_data_schema_models.organizations import Organization +from aind_data_schema_models.units import SizeUnit, FrequencyUnit + +from aind_data_schema.components.devices import AdditionalImagingDevice, DAQChannel, DAQDevice, Detector, Filter, Laser, Objective, OpticalTable, ScanningStage +from aind_data_schema.core.rig import Rig, Com +from aind_data_schema_models.modalities import Modality + +objectives = [ + Objective( + name="Custom Objective", + numerical_aperture=0.305, + magnification=5, + immersion="air", + manufacturer=Organization.OTHER, + model="JM_DIAMOND 5.0X/1.3", + notes="manufacturer collaboration between Schneider-Kreuznach and Vieworks", + ), +] + +detectors = [ + Detector( + name="Camera 1", + detector_type="Camera", + data_interface="Coax", + cooling="Air", + manufacturer=Organization.VIEWORKS, + model="VNP-604MX", + serial_number="MB151BAY001", + ), +] + +light_sources = [ + Laser( + name="LAS-08307", + coupling="Single-mode fiber", + wavelength=405, + maximum_power=200, + serial_number="LAS-08307", + manufacturer=Organization.OXXIUS, + notes="Housed in commercial laser combiner", + ), + Laser( + name="LAS-08308", + coupling="Single-mode fiber", + wavelength=488, + maximum_power=200, + serial_number="LAS-08308", + manufacturer=Organization.OXXIUS, + notes="Housed in commercial laser combiner", + ), + Laser( + name="539251", + coupling="Single-mode fiber", + wavelength=561, + maximum_power=200, + serial_number="539251", + manufacturer=Organization.OXXIUS, + notes="Housed in commercial laser combiner", + ), + Laser( + name="LAS-08309", + coupling="Single-mode fiber", + wavelength=638, + maximum_power=200, + serial_number="LAS-08309", + manufacturer=Organization.OXXIUS, + notes="Housed in commercial laser combiner", + ), +] + +fluorescence_filters = [ + Filter( + name="Multiband filter", + filter_type="Multiband", + manufacturer=Organization.CHROMA, + diameter=44.05, + thickness=1, + thickness_unit=SizeUnit.MM, + model="ZET405/488/561/640mv2", + notes="Custom made filter", + filter_wheel_index=0, + ) +] + +daqs = [ + DAQDevice( + model="PCIe-6738", + data_interface="USB", + computer_name="Dev2", + manufacturer=Organization.NATIONAL_INSTRUMENTS, + name="Dev2", + channels=[ + DAQChannel( + channel_name="3", + channel_type="Analog Output", + device_name="LAS-08308", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + DAQChannel( + channel_name="5", + channel_type="Analog Output", + device_name="539251", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + DAQChannel( + channel_name="4", + channel_type="Analog Output", + device_name="LAS-08309", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + DAQChannel( + channel_name="2", + channel_type="Analog Output", + device_name="stage-x", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + DAQChannel( + channel_name="0", + channel_type="Analog Output", + device_name="TL-1", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + DAQChannel( + channel_name="6", + channel_type="Analog Output", + device_name="LAS-08307", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + ], + ) +] + +scanning_stages = [ + ScanningStage( + name="stage-x", + stage_axis_direction="Detection axis", + stage_axis_name="X", + travel=1000, + model="MS-8000", + manufacturer=Organization.ASI, + ), + ScanningStage( + name="stage-y", + stage_axis_direction="Perpendicular axis", + stage_axis_name="Y", + travel=1000, + model="MS-8000", + manufacturer=Organization.ASI, + ), + ScanningStage( + name="stage-z", + stage_axis_direction="Illumination axis", + stage_axis_name="Z", + travel=100, + model="LS-100", + manufacturer=Organization.ASI, + ), +] + +additional_devices = [ + AdditionalImagingDevice( + imaging_device_type="Tunable lens", + name="TL-1", + manufacturer=Organization.OPTOTUNE, + model="EL-16-40-TC-VIS-20D-C", + serial_number="01", + ), + AdditionalImagingDevice( + name="RM-1", + imaging_device_type="Rotation mount", + manufacturer=Organization.THORLABS, + model="K10CR1", + serial_number="01", + ), + AdditionalImagingDevice( + name="LC-1", + imaging_device_type="Laser combiner", + manufacturer=Organization.OXXIUS, + model="L6Cc", + serial_number="L6CC-00513", + ), +] + +optical_tables = [ + OpticalTable( + name="Table", + length=36, + width=48, + vibration_control=True, + model="VIS3648-PG2-325A", + manufacturer=Organization.MKS_NEWPORT, + ) +] + +inst = Rig( + rig_id="440_exaSPIM1_20231004", + modalities=[Modality.SPIM], + instrument_type="exaSPIM", + modification_date=datetime.date(2023, 10, 4), + manufacturer=Organization.CUSTOM, + components=[ + *objectives, + *detectors, + *light_sources, + *fluorescence_filters, + *daqs, + *scanning_stages, + *additional_devices, + *optical_tables, + ], + com_ports=[ + Com( + hardware_name="Laser Launch", + com_port="COM2", + ), + Com( + hardware_name="ASI Tiger", + com_port="COM5", + ), + ], + temperature_control=False, +) + +serialized = inst.model_dump_json() +deserialized = Rig.model_validate_json(serialized) +deserialized.write_standard_file(prefix="exaspim") diff --git a/examples/fip_behavior_rig.json b/examples/fip_behavior_rig.json index f7f9baeb1..2013c29d9 100644 --- a/examples/fip_behavior_rig.json +++ b/examples/fip_behavior_rig.json @@ -2,7 +2,6 @@ "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", "schema_version": "1.0.5", "rig_id": "447_FIP-Behavior_20000101", - "modification_date": "2000-01-01", "mouse_platform": { "device_type": "Tube", "name": "mouse_tube_foraging", @@ -11,134 +10,87 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "surface_material": null, "date_surface_replaced": null, "diameter": "4.0", "diameter_unit": "centimeter" }, - "stimulus_devices": [ + "modification_date": "2000-01-01", + "calibrations": [ { - "device_type": "Reward delivery", - "stage_type": { - "device_type": "Motorized stage", - "name": "NewScaleMotor for LickSpouts", - "serial_number": "xxxx", - "manufacturer": { - "name": "New Scale Technologies", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "travel": "15.0", - "travel_unit": "millimeter", - "firmware": "https://github.com/AllenNeuralDynamics/python-newscale,branch: axes-on-target,commit #7c17497" + "calibration_date": "2023-10-02T03:15:22Z", + "device_name": "470nm LED", + "description": "LED calibration", + "input": { + "Power setting": [ + 0 + ] }, - "reward_spouts": [ - { - "device_type": "Reward spout", - "name": "Left spout", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "side": "Left", - "spout_diameter": "1.2", - "spout_diameter_unit": "millimeter", - "spout_position": null, - "solenoid_valve": { - "device_type": "Solenoid", - "name": "Solenoid Left", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - }, - "lick_sensor": { - "device_type": "Lick detector", - "name": "Janelia_Lick_Detector Left", - "serial_number": null, - "manufacturer": { - "name": "Janelia Research Campus", - "abbreviation": "Janelia", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "013sk6x84" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - }, - "lick_sensor_type": "Capacitive" - }, - { - "device_type": "Reward spout", - "name": "Right spout", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "side": "Right", - "spout_diameter": "1.2", - "spout_diameter_unit": "millimeter", - "spout_position": null, - "solenoid_valve": { - "device_type": "Solenoid", - "name": "Solenoid Right", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - }, - "lick_sensor": { - "device_type": "Lick detector", - "name": "Janelia_Lick_Detector Right", - "serial_number": null, - "manufacturer": { - "name": "Janelia Research Campus", - "abbreviation": "Janelia", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "013sk6x84" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - }, - "lick_sensor_type": "Capacitive" - } - ] + "output": { + "Power mW": [ + 0.02 + ] + }, + "notes": null + }, + { + "calibration_date": "2023-10-02T03:15:22Z", + "device_name": "415nm LED", + "description": "LED calibration", + "input": { + "Power setting": [ + 0 + ] + }, + "output": { + "Power mW": [ + 0.02 + ] + }, + "notes": null + }, + { + "calibration_date": "2023-10-02T03:15:22Z", + "device_name": "560nm LED", + "description": "LED calibration", + "input": { + "Power setting": [ + 0 + ] + }, + "output": { + "Power mW": [ + 0.02 + ] + }, + "notes": null + } + ], + "ccf_coordinate_transform": null, + "origin": null, + "rig_axes": null, + "modalities": [ + { + "name": "Behavior", + "abbreviation": "behavior" + }, + { + "name": "Fiber photometry", + "abbreviation": "fib" } ], - "cameras": [ + "com_ports": [], + "instrument_type": null, + "manufacturer": null, + "temperature_control": null, + "notes": null, + "connections": [], + "components": [ { "name": "BehaviorVideography_FaceSide", + "device_type": "Camera assembly", "camera_target": "Face side left", "camera": { "device_type": "Detector", @@ -153,7 +105,7 @@ "model": "ELP-USBFHD05MT-KL170IR", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "The light intensity sensor was removed; IR illumination is constantly on", "detector_type": "Camera", "data_interface": "USB", @@ -201,7 +153,7 @@ "model": "XC0922LENS", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Focal Length 9-22mm 1/3\" IR F1.4", "focal_length": null, "focal_length_unit": null, @@ -216,6 +168,7 @@ }, { "name": "BehaviorVideography_FaceBottom", + "device_type": "Camera assembly", "camera_target": "Face bottom", "camera": { "device_type": "Detector", @@ -230,7 +183,7 @@ "model": "ELP-USBFHD05MT-KL170IR", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "The light intensity sensor was removed; IR illumination is constantly on", "detector_type": "Camera", "data_interface": "USB", @@ -278,7 +231,7 @@ "model": "XC0922LENS", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Focal Length 9-22mm 1/3\" IR F1.4", "focal_length": null, "focal_length_unit": null, @@ -288,16 +241,207 @@ "wavelength_unit": "nanometer", "max_aperture": "f/1.4" }, - "filter": null, - "position": null - } - ], - "enclosure": null, - "ephys_assemblies": [], - "fiber_assemblies": [], - "stick_microscopes": [], - "laser_assemblies": [], - "patch_cords": [ + "filter": null, + "position": null + }, + { + "device_type": "Harp device", + "name": "Harp Behavior", + "serial_number": null, + "manufacturer": { + "name": "Open Ephys Production Site", + "abbreviation": "OEPS", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "007rkz355" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "USB", + "computer_name": "behavior_computer", + "channels": [ + { + "channel_name": "DO0", + "device_name": "Solenoid Left", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DO1", + "device_name": "Solenoid Right", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DI0", + "device_name": "Janelia_Lick_Detector Left", + "channel_type": "Digital Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DI1", + "device_name": "Janelia_Lick_Detector Right", + "channel_type": "Digital Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DI3", + "device_name": "Photometry Clock", + "channel_type": "Digital Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + } + ], + "firmware_version": "FTDI version:", + "hardware_version": null, + "harp_device_type": { + "whoami": 1216, + "name": "Behavior" + }, + "core_version": "2.1", + "tag_version": null, + "is_clock_generator": false + }, + { + "device_type": "Reward delivery", + "stage_type": { + "device_type": "Motorized stage", + "name": "NewScaleMotor for LickSpouts", + "serial_number": "xxxx", + "manufacturer": { + "name": "New Scale Technologies", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "15.0", + "travel_unit": "millimeter", + "firmware": "https://github.com/AllenNeuralDynamics/python-newscale,branch: axes-on-target,commit #7c17497" + }, + "reward_spouts": [ + { + "device_type": "Reward spout", + "name": "Left spout", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "side": "Left", + "spout_diameter": "1.2", + "spout_diameter_unit": "millimeter", + "spout_position": null, + "solenoid_valve": { + "device_type": "device", + "name": "Solenoid Left", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor": { + "device_type": "device", + "name": "Janelia_Lick_Detector Left", + "serial_number": null, + "manufacturer": { + "name": "Janelia Research Campus", + "abbreviation": "Janelia", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "013sk6x84" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor_type": "Capacitive" + }, + { + "device_type": "Reward spout", + "name": "Right spout", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "side": "Right", + "spout_diameter": "1.2", + "spout_diameter_unit": "millimeter", + "spout_position": null, + "solenoid_valve": { + "device_type": "device", + "name": "Solenoid Right", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor": { + "device_type": "device", + "name": "Janelia_Lick_Detector Right", + "serial_number": null, + "manufacturer": { + "name": "Janelia Research Campus", + "abbreviation": "Janelia", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "013sk6x84" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor_type": "Capacitive" + } + ] + }, { "device_type": "Patch", "name": "Bundle Branching Fiber-optic Patch Cord", @@ -314,14 +458,12 @@ "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "core_diameter": "200", "numerical_aperture": "0.37", "photobleaching_date": null - } - ], - "light_sources": [ + }, { "device_type": "Light emitting diode", "name": "470nm LED", @@ -338,7 +480,7 @@ "model": "M470F3", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 470, "wavelength_unit": "nanometer", @@ -361,7 +503,7 @@ "model": "M415F3", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 415, "wavelength_unit": "nanometer", @@ -384,15 +526,13 @@ "model": "M565F3", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 565, "wavelength_unit": "nanometer", "bandwidth": null, "bandwidth_unit": null - } - ], - "detectors": [ + }, { "device_type": "Detector", "name": "Green CMOS", @@ -409,7 +549,7 @@ "model": "BFS-U3-20S40M", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -455,7 +595,7 @@ "model": "BFS-U3-20S40M", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -484,9 +624,7 @@ "recording_software": null, "driver": null, "driver_version": null - } - ], - "objectives": [ + }, { "device_type": "Objective", "name": "Objective", @@ -503,15 +641,13 @@ "model": "CFI Plan Apochromat Lambda D 10x", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "numerical_aperture": "0.45", "magnification": "10", "immersion": "air", "objective_type": null - } - ], - "filters": [ + }, { "device_type": "Filter", "name": "Green emission filter", @@ -525,7 +661,7 @@ "model": "FF01-520/35-25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -554,7 +690,7 @@ "model": "FF01-600/37-25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -583,7 +719,7 @@ "model": "FF562-Di03-25x36", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Dichroic", "diameter": null, @@ -612,7 +748,7 @@ "model": "FF493/574-Di01-25x36", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", "filter_type": "Multiband", "diameter": null, @@ -644,7 +780,7 @@ "model": "FB410-10", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -676,7 +812,7 @@ "model": "FB470-10", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -708,7 +844,7 @@ "model": "FB560-10", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -740,7 +876,7 @@ "model": "#69-898", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Dichroic", "diameter": null, @@ -772,7 +908,7 @@ "model": "#69-899", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Dichroic", "diameter": null, @@ -787,9 +923,7 @@ "center_wavelength": null, "wavelength_unit": "nanometer", "description": null - } - ], - "lenses": [ + }, { "device_type": "Lens", "name": "Image focusing lens", @@ -806,7 +940,7 @@ "model": "AC254-080-A-ML", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "focal_length": "80", "focal_length_unit": "millimeter", @@ -815,170 +949,17 @@ "optimized_wavelength_range": null, "wavelength_unit": "nanometer", "max_aperture": null - } - ], - "digital_micromirror_devices": [], - "polygonal_scanners": [], - "pockels_cells": [], - "additional_devices": [ + }, { - "device_type": "Photometry Clock", + "device_type": "device", "name": "Photometry Clock", "serial_number": null, "manufacturer": null, "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, - "notes": null - } - ], - "daqs": [ - { - "device_type": "Harp device", - "name": "Harp Behavior", - "serial_number": null, - "manufacturer": { - "name": "Open Ephys Production Site", - "abbreviation": "OEPS", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "007rkz355" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "data_interface": "USB", - "computer_name": "behavior_computer", - "channels": [ - { - "channel_name": "DO0", - "device_name": "Solenoid Left", - "channel_type": "Digital Output", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DO1", - "device_name": "Solenoid Right", - "channel_type": "Digital Output", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DI0", - "device_name": "Janelia_Lick_Detector Left", - "channel_type": "Digital Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DI1", - "device_name": "Janelia_Lick_Detector Right", - "channel_type": "Digital Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DI3", - "device_name": "Photometry Clock", - "channel_type": "Digital Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - } - ], - "firmware_version": "FTDI version:", - "hardware_version": null, - "harp_device_type": { - "whoami": 1216, - "name": "Behavior" - }, - "core_version": "2.1", - "tag_version": null, - "is_clock_generator": false - } - ], - "calibrations": [ - { - "calibration_date": "2023-10-02T03:15:22Z", - "device_name": "470nm LED", - "description": "LED calibration", - "input": { - "Power setting": [ - 0 - ] - }, - "output": { - "Power mW": [ - 0.02 - ] - }, - "notes": null - }, - { - "calibration_date": "2023-10-02T03:15:22Z", - "device_name": "415nm LED", - "description": "LED calibration", - "input": { - "Power setting": [ - 0 - ] - }, - "output": { - "Power mW": [ - 0.02 - ] - }, - "notes": null - }, - { - "calibration_date": "2023-10-02T03:15:22Z", - "device_name": "560nm LED", - "description": "LED calibration", - "input": { - "Power setting": [ - 0 - ] - }, - "output": { - "Power mW": [ - 0.02 - ] - }, + "additional_settings": null, "notes": null } - ], - "ccf_coordinate_transform": null, - "origin": null, - "rig_axes": null, - "modalities": [ - { - "name": "Behavior", - "abbreviation": "behavior" - }, - { - "name": "Fiber photometry", - "abbreviation": "fib" - } - ], - "notes": null + ] } \ No newline at end of file diff --git a/examples/fip_behavior_rig.py b/examples/fip_behavior_rig.py index 6ce341a96..487ef074b 100644 --- a/examples/fip_behavior_rig.py +++ b/examples/fip_behavior_rig.py @@ -6,356 +6,360 @@ from aind_data_schema_models.modalities import Modality from aind_data_schema_models.units import FrequencyUnit, SizeUnit -import aind_data_schema.components.devices as d -import aind_data_schema.core.rig as r +from aind_data_schema.components.devices import ( + Calibration, + CameraAssembly, + CameraTarget, + Camera, + Organization, + Lens, + HarpDevice, + HarpDeviceType, + DAQChannel, + RewardDelivery, + RewardSpout, + Software, + SpoutSide, + Device, + LickSensorType, + MotorizedStage, + Patch, + LightEmittingDiode, + Detector, + Objective, + Filter, + Tube, +) +from aind_data_schema.core.rig import Rig -r = r.Rig( - rig_id="447_FIP-Behavior_20000101", - modification_date=date(2000, 1, 1), - modalities=[Modality.BEHAVIOR, Modality.FIB], - cameras=[ - d.CameraAssembly( - name="BehaviorVideography_FaceSide", - camera_target=d.CameraTarget.FACE_SIDE_LEFT, - camera=d.Camera( - name="Side face camera", - detector_type="Camera", - serial_number="TBD", - manufacturer=d.Organization.AILIPU, - model="ELP-USBFHD05MT-KL170IR", - notes="The light intensity sensor was removed; IR illumination is constantly on", - data_interface="USB", - computer_name="W10DTJK7N0M3", - frame_rate=120, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=640, - sensor_height=480, - chroma="Color", - cooling="Air", - bin_mode="Additive", - recording_software=d.Software(name="Bonsai", version="2.5"), - ), - lens=d.Lens( - name="Xenocam 1", - model="XC0922LENS", - serial_number="unknown", - manufacturer=d.Organization.OTHER, - max_aperture="f/1.4", - notes='Focal Length 9-22mm 1/3" IR F1.4', - ), - ), - d.CameraAssembly( - name="BehaviorVideography_FaceBottom", - camera_target=d.CameraTarget.FACE_BOTTOM, - camera=d.Camera( - name="Bottom face Camera", - detector_type="Camera", - serial_number="TBD", - manufacturer=d.Organization.AILIPU, - model="ELP-USBFHD05MT-KL170IR", - notes="The light intensity sensor was removed; IR illumination is constantly on", - data_interface="USB", - computer_name="W10DTJK7N0M3", - frame_rate=120, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=640, - sensor_height=480, - chroma="Color", - cooling="Air", - bin_mode="Additive", - recording_software=d.Software(name="Bonsai", version="2.5"), - ), - lens=d.Lens( - name="Xenocam 2", - model="XC0922LENS", - serial_number="unknown", - manufacturer=d.Organization.OTHER, - max_aperture="f/1.4", - notes='Focal Length 9-22mm 1/3" IR F1.4', - ), - ), - ], - daqs=[ - d.HarpDevice( - name="Harp Behavior", - harp_device_type=d.HarpDeviceType.BEHAVIOR, - core_version="2.1", - firmware_version="FTDI version:", - computer_name="behavior_computer", - is_clock_generator=False, - channels=[ - d.DAQChannel(channel_name="DO0", device_name="Solenoid Left", channel_type="Digital Output"), - d.DAQChannel(channel_name="DO1", device_name="Solenoid Right", channel_type="Digital Output"), - d.DAQChannel( - channel_name="DI0", device_name="Janelia_Lick_Detector Left", channel_type="Digital Input" - ), - d.DAQChannel( - channel_name="DI1", device_name="Janelia_Lick_Detector Right", channel_type="Digital Input" - ), - d.DAQChannel(channel_name="DI3", device_name="Photometry Clock", channel_type="Digital Input"), - ], - ) +camera1 = CameraAssembly( + name="BehaviorVideography_FaceSide", + camera_target=CameraTarget.FACE_SIDE_LEFT, + camera=Camera( + name="Side face camera", + detector_type="Camera", + serial_number="TBD", + manufacturer=Organization.AILIPU, + model="ELP-USBFHD05MT-KL170IR", + notes="The light intensity sensor was removed; IR illumination is constantly on", + data_interface="USB", + computer_name="W10DTJK7N0M3", + frame_rate=120, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=640, + sensor_height=480, + chroma="Color", + cooling="Air", + bin_mode="Additive", + recording_software=Software(name="Bonsai", version="2.5"), + ), + lens=Lens( + name="Xenocam 1", + model="XC0922LENS", + serial_number="unknown", + manufacturer=Organization.OTHER, + max_aperture="f/1.4", + notes='Focal Length 9-22mm 1/3" IR F1.4', + ), +) + +camera2 = CameraAssembly( + name="BehaviorVideography_FaceBottom", + camera_target=CameraTarget.FACE_BOTTOM, + camera=Camera( + name="Bottom face Camera", + detector_type="Camera", + serial_number="TBD", + manufacturer=Organization.AILIPU, + model="ELP-USBFHD05MT-KL170IR", + notes="The light intensity sensor was removed; IR illumination is constantly on", + data_interface="USB", + computer_name="W10DTJK7N0M3", + frame_rate=120, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=640, + sensor_height=480, + chroma="Color", + cooling="Air", + bin_mode="Additive", + recording_software=Software(name="Bonsai", version="2.5"), + ), + lens=Lens( + name="Xenocam 2", + model="XC0922LENS", + serial_number="unknown", + manufacturer=Organization.OTHER, + max_aperture="f/1.4", + notes='Focal Length 9-22mm 1/3" IR F1.4', + ), +) + +harp_behavior = HarpDevice( + name="Harp Behavior", + harp_device_type=HarpDeviceType.BEHAVIOR, + core_version="2.1", + firmware_version="FTDI version:", + computer_name="behavior_computer", + is_clock_generator=False, + channels=[ + DAQChannel(channel_name="DO0", device_name="Solenoid Left", channel_type="Digital Output"), + DAQChannel(channel_name="DO1", device_name="Solenoid Right", channel_type="Digital Output"), + DAQChannel(channel_name="DI0", device_name="Janelia_Lick_Detector Left", channel_type="Digital Input"), + DAQChannel(channel_name="DI1", device_name="Janelia_Lick_Detector Right", channel_type="Digital Input"), + DAQChannel(channel_name="DI3", device_name="Photometry Clock", channel_type="Digital Input"), ], - mouse_platform=d.Tube(name="mouse_tube_foraging", diameter=4.0), - stimulus_devices=[ - d.RewardDelivery( - reward_spouts=[ - d.RewardSpout( - name="Left spout", - side=d.SpoutSide.LEFT, - spout_diameter=1.2, - solenoid_valve=d.Device(device_type="Solenoid", name="Solenoid Left"), - lick_sensor=d.Device( - name="Janelia_Lick_Detector Left", - device_type="Lick detector", - manufacturer=d.Organization.JANELIA, - ), - lick_sensor_type=d.LickSensorType("Capacitive"), - ), - d.RewardSpout( - name="Right spout", - side=d.SpoutSide.RIGHT, - spout_diameter=1.2, - solenoid_valve=d.Device(device_type="Solenoid", name="Solenoid Right"), - lick_sensor=d.Device( - name="Janelia_Lick_Detector Right", - device_type="Lick detector", - manufacturer=d.Organization.JANELIA, - ), - lick_sensor_type=d.LickSensorType("Capacitive"), - ), - ], - stage_type=d.MotorizedStage( - name="NewScaleMotor for LickSpouts", - serial_number="xxxx", # grabbing from GUI/SettingFiles - manufacturer=d.Organization.NEW_SCALE_TECHNOLOGIES, - travel=15.0, # unit is mm - firmware=( - "https://github.com/AllenNeuralDynamics/python-newscale,branch: axes-on-target,commit #7c17497" - ), +) + +reward_delivery = RewardDelivery( + reward_spouts=[ + RewardSpout( + name="Left spout", + side=SpoutSide.LEFT, + spout_diameter=1.2, + solenoid_valve=Device(name="Solenoid Left"), + lick_sensor=Device( + name="Janelia_Lick_Detector Left", + manufacturer=Organization.JANELIA, ), + lick_sensor_type=LickSensorType("Capacitive"), ), - ], - # Common - # FIB Specific - patch_cords=[ - d.Patch( - name="Bundle Branching Fiber-optic Patch Cord", - manufacturer=d.Organization.DORIC, - model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - core_diameter=200, - numerical_aperture=0.37, - ) - ], - light_sources=[ - d.LightEmittingDiode( - name="470nm LED", - manufacturer=d.Organization.THORLABS, - model="M470F3", - wavelength=470, - ), - d.LightEmittingDiode( - name="415nm LED", - manufacturer=d.Organization.THORLABS, - model="M415F3", - wavelength=415, - ), - d.LightEmittingDiode( - name="565nm LED", - manufacturer=d.Organization.THORLABS, - model="M565F3", - wavelength=565, - ), - ], - detectors=[ - d.Detector( - name="Green CMOS", - serial_number="21396991", - manufacturer=d.Organization.FLIR, - model="BFS-U3-20S40M", - detector_type="Camera", - data_interface="USB", - cooling="Air", - immersion="air", - bin_width=4, - bin_height=4, - bin_mode="Additive", - crop_offset_x=0, - crop_offset_y=0, - crop_width=200, - crop_height=200, - gain=2, - chroma="Monochrome", - bit_depth=16, - ), - d.Detector( - name="Red CMOS", - serial_number="21396991", - manufacturer=d.Organization.FLIR, - model="BFS-U3-20S40M", - detector_type="Camera", - data_interface="USB", - cooling="Air", - immersion="air", - bin_width=4, - bin_height=4, - bin_mode="Additive", - crop_offset_x=0, - crop_offset_y=0, - crop_width=200, - crop_height=200, - gain=2, - chroma="Monochrome", - bit_depth=16, - ), - ], - objectives=[ - d.Objective( - name="Objective", - serial_number="128022336", - manufacturer=d.Organization.NIKON, - model="CFI Plan Apochromat Lambda D 10x", - numerical_aperture=0.45, - magnification=10, - immersion="air", - ) - ], - filters=[ - d.Filter( - name="Green emission filter", - manufacturer=d.Organization.SEMROCK, - model="FF01-520/35-25", - filter_type="Band pass", - center_wavelength=520, - diameter=25, - ), - d.Filter( - name="Red emission filter", - manufacturer=d.Organization.SEMROCK, - model="FF01-600/37-25", - filter_type="Band pass", - center_wavelength=600, - diameter=25, - ), - d.Filter( - name="Emission Dichroic", - model="FF562-Di03-25x36", - manufacturer=d.Organization.SEMROCK, - filter_type="Dichroic", - height=25, - width=36, - cut_off_wavelength=562, - ), - d.Filter( - name="dual-edge standard epi-fluorescence dichroic beamsplitter", - model="FF493/574-Di01-25x36", - manufacturer=d.Organization.SEMROCK, - notes="493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", - filter_type="Multiband", - width=36, - height=24, - ), - d.Filter( - name="Excitation filter 410nm", - manufacturer=d.Organization.THORLABS, - model="FB410-10", - filter_type="Band pass", - diameter=25, - center_wavelength=410, - ), - d.Filter( - name="Excitation filter 470nm", - manufacturer=d.Organization.THORLABS, - model="FB470-10", - filter_type="Band pass", - center_wavelength=470, - diameter=25, - ), - d.Filter( - name="Excitation filter 560nm", - manufacturer=d.Organization.THORLABS, - model="FB560-10", - filter_type="Band pass", - diameter=25, - center_wavelength=560, - ), - d.Filter( - name="450 Dichroic Longpass Filter", - manufacturer=d.Organization.EDMUND_OPTICS, - model="#69-898", - filter_type="Dichroic", - cut_off_wavelength=450, - width=35.6, - height=25.2, - ), - d.Filter( - name="500 Dichroic Longpass Filter", - manufacturer=d.Organization.EDMUND_OPTICS, - model="#69-899", - filter_type="Dichroic", - cut_off_wavelength=500, - width=35.6, - height=23.2, + RewardSpout( + name="Right spout", + side=SpoutSide.RIGHT, + spout_diameter=1.2, + solenoid_valve=Device(name="Solenoid Right"), + lick_sensor=Device( + name="Janelia_Lick_Detector Right", + manufacturer=Organization.JANELIA, + ), + lick_sensor_type=LickSensorType("Capacitive"), ), ], - lenses=[ - d.Lens( - manufacturer=d.Organization.THORLABS, - model="AC254-080-A-ML", - name="Image focusing lens", - focal_length=80, - focal_length_unit=SizeUnit.MM, - size=1, - ) - ], - additional_devices=[d.Device(device_type="Photometry Clock", name="Photometry Clock")], - # FIB Specific - # Optogenetics Specific # Xinxin to fill in - # light_sources=[ - # d.LightEmittingDiode( - # name="LED for photostimulation", - # manufacturer=d.Organization.PRIZMATIX, - # model="xxx", - # wavelength=470, - # ), - # ], - # daqs=[ - # d.DAQDevice( - # name="NIDAQ for opto", - # device_type="DAQ Device", - # data_interface="USB2.0", - # manufacturer=d.Organization.NATIONAL_INSTRUMENTS, - # computer_name="behavior_computer", - # channels=[ - # ], - # ) - # ], - # Optogenetics Specific - # Calibrations - calibrations=[ - d.Calibration( - calibration_date=datetime(2023, 10, 2, 3, 15, 22, tzinfo=timezone.utc), - device_name="470nm LED", - description="LED calibration", - input={"Power setting": [0]}, - output={"Power mW": [0.02]}, - ), - d.Calibration( - calibration_date=datetime(2023, 10, 2, 3, 15, 22, tzinfo=timezone.utc), - device_name="415nm LED", - description="LED calibration", - input={"Power setting": [0]}, - output={"Power mW": [0.02]}, - ), - d.Calibration( - calibration_date=datetime(2023, 10, 2, 3, 15, 22, tzinfo=timezone.utc), - device_name="560nm LED", - description="LED calibration", - input={"Power setting": [0]}, - output={"Power mW": [0.02]}, - ), - # Water calibration comes here# + stage_type=MotorizedStage( + name="NewScaleMotor for LickSpouts", + serial_number="xxxx", # grabbing from GUI/SettingFiles + manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, + travel=15.0, # unit is mm + firmware=("https://github.com/AllenNeuralDynamics/python-newscale,branch: axes-on-target,commit #7c17497"), + ), +) + +patch_cord = Patch( + name="Bundle Branching Fiber-optic Patch Cord", + manufacturer=Organization.DORIC, + model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", + core_diameter=200, + numerical_aperture=0.37, +) + +light_sources = [ + LightEmittingDiode( + name="470nm LED", + manufacturer=Organization.THORLABS, + model="M470F3", + wavelength=470, + ), + LightEmittingDiode( + name="415nm LED", + manufacturer=Organization.THORLABS, + model="M415F3", + wavelength=415, + ), + LightEmittingDiode( + name="565nm LED", + manufacturer=Organization.THORLABS, + model="M565F3", + wavelength=565, + ), +] + +detectors = [ + Detector( + name="Green CMOS", + serial_number="21396991", + manufacturer=Organization.FLIR, + model="BFS-U3-20S40M", + detector_type="Camera", + data_interface="USB", + cooling="Air", + immersion="air", + bin_width=4, + bin_height=4, + bin_mode="Additive", + crop_offset_x=0, + crop_offset_y=0, + crop_width=200, + crop_height=200, + gain=2, + chroma="Monochrome", + bit_depth=16, + ), + Detector( + name="Red CMOS", + serial_number="21396991", + manufacturer=Organization.FLIR, + model="BFS-U3-20S40M", + detector_type="Camera", + data_interface="USB", + cooling="Air", + immersion="air", + bin_width=4, + bin_height=4, + bin_mode="Additive", + crop_offset_x=0, + crop_offset_y=0, + crop_width=200, + crop_height=200, + gain=2, + chroma="Monochrome", + bit_depth=16, + ), +] + +objective = Objective( + name="Objective", + serial_number="128022336", + manufacturer=Organization.NIKON, + model="CFI Plan Apochromat Lambda D 10x", + numerical_aperture=0.45, + magnification=10, + immersion="air", +) + +filters = [ + Filter( + name="Green emission filter", + manufacturer=Organization.SEMROCK, + model="FF01-520/35-25", + filter_type="Band pass", + center_wavelength=520, + diameter=25, + ), + Filter( + name="Red emission filter", + manufacturer=Organization.SEMROCK, + model="FF01-600/37-25", + filter_type="Band pass", + center_wavelength=600, + diameter=25, + ), + Filter( + name="Emission Dichroic", + model="FF562-Di03-25x36", + manufacturer=Organization.SEMROCK, + filter_type="Dichroic", + height=25, + width=36, + cut_off_wavelength=562, + ), + Filter( + name="dual-edge standard epi-fluorescence dichroic beamsplitter", + model="FF493/574-Di01-25x36", + manufacturer=Organization.SEMROCK, + notes="493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", + filter_type="Multiband", + width=36, + height=24, + ), + Filter( + name="Excitation filter 410nm", + manufacturer=Organization.THORLABS, + model="FB410-10", + filter_type="Band pass", + diameter=25, + center_wavelength=410, + ), + Filter( + name="Excitation filter 470nm", + manufacturer=Organization.THORLABS, + model="FB470-10", + filter_type="Band pass", + center_wavelength=470, + diameter=25, + ), + Filter( + name="Excitation filter 560nm", + manufacturer=Organization.THORLABS, + model="FB560-10", + filter_type="Band pass", + diameter=25, + center_wavelength=560, + ), + Filter( + name="450 Dichroic Longpass Filter", + manufacturer=Organization.EDMUND_OPTICS, + model="#69-898", + filter_type="Dichroic", + cut_off_wavelength=450, + width=35.6, + height=25.2, + ), + Filter( + name="500 Dichroic Longpass Filter", + manufacturer=Organization.EDMUND_OPTICS, + model="#69-899", + filter_type="Dichroic", + cut_off_wavelength=500, + width=35.6, + height=23.2, + ), +] + +lens = Lens( + manufacturer=Organization.THORLABS, + model="AC254-080-A-ML", + name="Image focusing lens", + focal_length=80, + focal_length_unit=SizeUnit.MM, + size=1, +) + +additional_device = Device(name="Photometry Clock") + +calibrations = [ + Calibration( + calibration_date=datetime(2023, 10, 2, 3, 15, 22, tzinfo=timezone.utc), + device_name="470nm LED", + description="LED calibration", + input={"Power setting": [0]}, + output={"Power mW": [0.02]}, + ), + Calibration( + calibration_date=datetime(2023, 10, 2, 3, 15, 22, tzinfo=timezone.utc), + device_name="415nm LED", + description="LED calibration", + input={"Power setting": [0]}, + output={"Power mW": [0.02]}, + ), + Calibration( + calibration_date=datetime(2023, 10, 2, 3, 15, 22, tzinfo=timezone.utc), + device_name="560nm LED", + description="LED calibration", + input={"Power setting": [0]}, + output={"Power mW": [0.02]}, + ), + # Water calibration comes here# +] + +rig = Rig( + rig_id="447_FIP-Behavior_20000101", + modification_date=date(2000, 1, 1), + modalities=[Modality.BEHAVIOR, Modality.FIB], + mouse_platform=Tube(name="mouse_tube_foraging", diameter=4.0), + components=[ + camera1, + camera2, + harp_behavior, + reward_delivery, + patch_cord, + *light_sources, + *detectors, + objective, + *filters, + lens, + additional_device, ], + calibrations=calibrations, ) -r.write_standard_file(prefix="fip_behavior") +rig.write_standard_file(prefix="fip_behavior") diff --git a/examples/fip_ophys_rig.json b/examples/fip_ophys_rig.json index bf7bd982d..5409a1989 100644 --- a/examples/fip_ophys_rig.json +++ b/examples/fip_ophys_rig.json @@ -2,7 +2,6 @@ "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", "schema_version": "1.0.5", "rig_id": "428_FIP1_20231003", - "modification_date": "2023-10-03", "mouse_platform": { "device_type": "Disc", "name": "mouse_disc", @@ -11,7 +10,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "surface_material": null, "date_surface_replaced": null, @@ -22,93 +21,48 @@ "decoder": null, "encoder_firmware": null }, - "stimulus_devices": [ + "modification_date": "2023-10-03", + "calibrations": [ { - "device_type": "Reward delivery", - "stage_type": null, - "reward_spouts": [ - { - "device_type": "Reward spout", - "name": "Left spout", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "side": "Left", - "spout_diameter": "1.2", - "spout_diameter_unit": "millimeter", - "spout_position": null, - "solenoid_valve": { - "device_type": "Solenoid", - "name": "Solenoid Left", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - }, - "lick_sensor": { - "device_type": "Lick detector", - "name": "Lick-o-meter Left", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - }, - "lick_sensor_type": null - }, - { - "device_type": "Reward spout", - "name": "Right spout", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null, - "side": "Right", - "spout_diameter": "1.2", - "spout_diameter_unit": "millimeter", - "spout_position": null, - "solenoid_valve": { - "device_type": "Solenoid", - "name": "Solenoid Right", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - }, - "lick_sensor": { - "device_type": "Lick detector", - "name": "Lick-o-meter Right", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - }, - "lick_sensor_type": null - } - ] + "calibration_date": "2023-10-02T03:15:22Z", + "device_name": "470nm LED", + "description": "LED calibration", + "input": { + "Power setting": [ + 1, + 2, + 3 + ] + }, + "output": { + "Power mW": [ + 5, + 10, + 13 + ] + }, + "notes": null } ], - "cameras": [ + "ccf_coordinate_transform": null, + "origin": null, + "rig_axes": null, + "modalities": [ + { + "name": "Fiber photometry", + "abbreviation": "fib" + } + ], + "com_ports": [], + "instrument_type": null, + "manufacturer": null, + "temperature_control": null, + "notes": null, + "connections": [], + "components": [ { "name": "BehaviorVideography_FaceSide", + "device_type": "Camera assembly", "camera_target": "Face side left", "camera": { "device_type": "Detector", @@ -123,7 +77,7 @@ "model": "ELP-USBFHD05MT-KL170IR", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "The light intensity sensor was removed; IR illumination is constantly on", "detector_type": "Camera", "data_interface": "USB", @@ -171,7 +125,7 @@ "model": "XC0922LENS", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Focal Length 9-22mm 1/3\" IR F1.4", "focal_length": null, "focal_length_unit": null, @@ -186,6 +140,7 @@ }, { "name": "BehaviorVideography_FaceBottom", + "device_type": "Camera assembly", "camera_target": "Face bottom", "camera": { "device_type": "Detector", @@ -200,7 +155,7 @@ "model": "ELP-USBFHD05MT-KL170IR", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "The light intensity sensor was removed; IR illumination is constantly on", "detector_type": "Camera", "data_interface": "USB", @@ -248,7 +203,7 @@ "model": "XC0922LENS", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "Focal Length 9-22mm 1/3\" IR F1.4", "focal_length": null, "focal_length_unit": null, @@ -260,14 +215,7 @@ }, "filter": null, "position": null - } - ], - "enclosure": null, - "ephys_assemblies": [], - "fiber_assemblies": [], - "stick_microscopes": [], - "laser_assemblies": [], - "patch_cords": [ + }, { "device_type": "Patch", "name": "Bundle Branching Fiber-optic Patch Cord", @@ -284,14 +232,12 @@ "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "core_diameter": "200", "numerical_aperture": "0.37", "photobleaching_date": null - } - ], - "light_sources": [ + }, { "device_type": "Light emitting diode", "name": "470nm LED", @@ -308,7 +254,7 @@ "model": "M470F3", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 470, "wavelength_unit": "nanometer", @@ -331,7 +277,7 @@ "model": "M415F3", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 415, "wavelength_unit": "nanometer", @@ -354,15 +300,13 @@ "model": "M565F3", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "wavelength": 565, "wavelength_unit": "nanometer", "bandwidth": null, "bandwidth_unit": null - } - ], - "detectors": [ + }, { "device_type": "Detector", "name": "Green CMOS", @@ -379,7 +323,7 @@ "model": "BFS-U3-20S40M", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -425,7 +369,7 @@ "model": "BFS-U3-20S40M", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "detector_type": "Camera", "data_interface": "USB", @@ -454,9 +398,7 @@ "recording_software": null, "driver": null, "driver_version": null - } - ], - "objectives": [ + }, { "device_type": "Objective", "name": "Objective", @@ -473,15 +415,13 @@ "model": "CFI Plan Apochromat Lambda D 10x", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "numerical_aperture": "0.45", "magnification": "10", "immersion": "air", "objective_type": null - } - ], - "filters": [ + }, { "device_type": "Filter", "name": "Green emission filter", @@ -495,7 +435,7 @@ "model": "FF01-520/35-25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -524,7 +464,7 @@ "model": "FF01-600/37-25", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -553,7 +493,7 @@ "model": "FF562-Di03-25x36", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Dichroic", "diameter": null, @@ -582,7 +522,7 @@ "model": "FF493/574-Di01-25x36", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": "493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", "filter_type": "Multiband", "diameter": null, @@ -614,7 +554,7 @@ "model": "FB410-10", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -646,7 +586,7 @@ "model": "FB470-10", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -678,7 +618,7 @@ "model": "FB560-10", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Band pass", "diameter": "25", @@ -710,7 +650,7 @@ "model": "#69-898", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Dichroic", "diameter": null, @@ -742,7 +682,7 @@ "model": "#69-899", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "filter_type": "Dichroic", "diameter": null, @@ -757,9 +697,7 @@ "center_wavelength": null, "wavelength_unit": "nanometer", "description": null - } - ], - "lenses": [ + }, { "device_type": "Lens", "name": "Image focusing lens", @@ -776,7 +714,7 @@ "model": "AC254-080-A-ML", "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "focal_length": "80", "focal_length_unit": "millimeter", @@ -785,25 +723,7 @@ "optimized_wavelength_range": null, "wavelength_unit": "nanometer", "max_aperture": null - } - ], - "digital_micromirror_devices": [], - "polygonal_scanners": [], - "pockels_cells": [], - "additional_devices": [ - { - "device_type": "Photometry Clock", - "name": "Photometry Clock", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": {}, - "notes": null - } - ], - "daqs": [ + }, { "device_type": "Harp device", "name": "Harp Behavior", @@ -820,7 +740,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "data_interface": "USB", "computer_name": "behavior_computer", @@ -885,38 +805,99 @@ "core_version": "2.1", "tag_version": null, "is_clock_generator": false - } - ], - "calibrations": [ + }, { - "calibration_date": "2023-10-02T03:15:22Z", - "device_name": "470nm LED", - "description": "LED calibration", - "input": { - "Power setting": [ - 1, - 2, - 3 - ] - }, - "output": { - "Power mW": [ - 5, - 10, - 13 - ] - }, - "notes": null - } - ], - "ccf_coordinate_transform": null, - "origin": null, - "rig_axes": null, - "modalities": [ + "device_type": "Reward delivery", + "stage_type": null, + "reward_spouts": [ + { + "device_type": "Reward spout", + "name": "Left spout", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "side": "Left", + "spout_diameter": "1.2", + "spout_diameter_unit": "millimeter", + "spout_position": null, + "solenoid_valve": { + "device_type": "device", + "name": "Solenoid Left", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor": { + "device_type": "device", + "name": "Lick-o-meter Left", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor_type": null + }, + { + "device_type": "Reward spout", + "name": "Right spout", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "side": "Right", + "spout_diameter": "1.2", + "spout_diameter_unit": "millimeter", + "spout_position": null, + "solenoid_valve": { + "device_type": "device", + "name": "Solenoid Right", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor": { + "device_type": "device", + "name": "Lick-o-meter Right", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor_type": null + } + ] + }, { - "name": "Fiber photometry", - "abbreviation": "fib" + "device_type": "device", + "name": "Photometry Clock", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null } - ], - "notes": null + ] } \ No newline at end of file diff --git a/examples/fip_ophys_rig.py b/examples/fip_ophys_rig.py index 98213e3dc..51d5bd0ce 100644 --- a/examples/fip_ophys_rig.py +++ b/examples/fip_ophys_rig.py @@ -8,296 +8,325 @@ import aind_data_schema.components.devices as d import aind_data_schema.core.rig as r -rig = r.Rig( - rig_id="428_FIP1_20231003", - modification_date=date(2023, 10, 3), - modalities=[Modality.FIB], - cameras=[ - d.CameraAssembly( - name="BehaviorVideography_FaceSide", - camera_target=d.CameraTarget.FACE_SIDE_LEFT, - camera=d.Camera( - name="Side face camera", - detector_type="Camera", - serial_number="TBD", - manufacturer=d.Organization.AILIPU, - model="ELP-USBFHD05MT-KL170IR", - notes="The light intensity sensor was removed; IR illumination is constantly on", - data_interface="USB", - computer_name="W10DTJK7N0M3", - frame_rate=120, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=640, - sensor_height=480, - chroma="Color", - cooling="Air", - bin_mode="Additive", - recording_software=d.Software(name="Bonsai", version="2.5"), - ), - lens=d.Lens( - name="Xenocam 1", - model="XC0922LENS", - serial_number="unknown", - manufacturer=d.Organization.OTHER, - max_aperture="f/1.4", - notes='Focal Length 9-22mm 1/3" IR F1.4', +camera_assembly_1 = d.CameraAssembly( + name="BehaviorVideography_FaceSide", + camera_target=d.CameraTarget.FACE_SIDE_LEFT, + camera=d.Camera( + name="Side face camera", + detector_type="Camera", + serial_number="TBD", + manufacturer=d.Organization.AILIPU, + model="ELP-USBFHD05MT-KL170IR", + notes="The light intensity sensor was removed; IR illumination is constantly on", + data_interface="USB", + computer_name="W10DTJK7N0M3", + frame_rate=120, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=640, + sensor_height=480, + chroma="Color", + cooling="Air", + bin_mode="Additive", + recording_software=d.Software(name="Bonsai", version="2.5"), + ), + lens=d.Lens( + name="Xenocam 1", + model="XC0922LENS", + serial_number="unknown", + manufacturer=d.Organization.OTHER, + max_aperture="f/1.4", + notes='Focal Length 9-22mm 1/3" IR F1.4', + ), +) + +camera_assembly_2 = d.CameraAssembly( + name="BehaviorVideography_FaceBottom", + camera_target=d.CameraTarget.FACE_BOTTOM, + camera=d.Camera( + name="Bottom face Camera", + detector_type="Camera", + serial_number="TBD", + manufacturer=d.Organization.AILIPU, + model="ELP-USBFHD05MT-KL170IR", + notes="The light intensity sensor was removed; IR illumination is constantly on", + data_interface="USB", + computer_name="W10DTJK7N0M3", + frame_rate=120, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=640, + sensor_height=480, + chroma="Color", + cooling="Air", + bin_mode="Additive", + recording_software=d.Software(name="Bonsai", version="2.5"), + ), + lens=d.Lens( + name="Xenocam 2", + model="XC0922LENS", + serial_number="unknown", + manufacturer=d.Organization.OTHER, + max_aperture="f/1.4", + notes='Focal Length 9-22mm 1/3" IR F1.4', + ), +) + +patch_cord = d.Patch( + name="Bundle Branching Fiber-optic Patch Cord", + manufacturer=d.Organization.DORIC, + model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", + core_diameter=200, + numerical_aperture=0.37, +) + +light_source_1 = d.LightEmittingDiode( + name="470nm LED", + manufacturer=d.Organization.THORLABS, + model="M470F3", + wavelength=470, +) + +light_source_2 = d.LightEmittingDiode( + name="415nm LED", + manufacturer=d.Organization.THORLABS, + model="M415F3", + wavelength=415, +) + +light_source_3 = d.LightEmittingDiode( + name="565nm LED", + manufacturer=d.Organization.THORLABS, + model="M565F3", + wavelength=565, +) + +detector_1 = d.Detector( + name="Green CMOS", + serial_number="21396991", + manufacturer=d.Organization.FLIR, + model="BFS-U3-20S40M", + detector_type="Camera", + data_interface="USB", + cooling="Air", + immersion="air", + bin_width=4, + bin_height=4, + bin_mode="Additive", + crop_offset_x=0, + crop_offset_y=0, + crop_width=200, + crop_height=200, + gain=2, + chroma="Monochrome", + bit_depth=16, +) + +detector_2 = d.Detector( + name="Red CMOS", + serial_number="21396991", + manufacturer=d.Organization.FLIR, + model="BFS-U3-20S40M", + detector_type="Camera", + data_interface="USB", + cooling="Air", + immersion="air", + bin_width=4, + bin_height=4, + bin_mode="Additive", + crop_offset_x=0, + crop_offset_y=0, + crop_width=200, + crop_height=200, + gain=2, + chroma="Monochrome", + bit_depth=16, +) + +objective = d.Objective( + name="Objective", + serial_number="128022336", + manufacturer=d.Organization.NIKON, + model="CFI Plan Apochromat Lambda D 10x", + numerical_aperture=0.45, + magnification=10, + immersion="air", +) + +filter_1 = d.Filter( + name="Green emission filter", + manufacturer=d.Organization.SEMROCK, + model="FF01-520/35-25", + filter_type="Band pass", + center_wavelength=520, + diameter=25, +) + +filter_2 = d.Filter( + name="Red emission filter", + manufacturer=d.Organization.SEMROCK, + model="FF01-600/37-25", + filter_type="Band pass", + center_wavelength=600, + diameter=25, +) + +filter_3 = d.Filter( + name="Emission Dichroic", + model="FF562-Di03-25x36", + manufacturer=d.Organization.SEMROCK, + filter_type="Dichroic", + height=25, + width=36, + cut_off_wavelength=562, +) + +filter_4 = d.Filter( + name="dual-edge standard epi-fluorescence dichroic beamsplitter", + model="FF493/574-Di01-25x36", + manufacturer=d.Organization.SEMROCK, + notes="493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", + filter_type="Multiband", + width=36, + height=24, +) + +filter_5 = d.Filter( + name="Excitation filter 410nm", + manufacturer=d.Organization.THORLABS, + model="FB410-10", + filter_type="Band pass", + diameter=25, + center_wavelength=410, +) + +filter_6 = d.Filter( + name="Excitation filter 470nm", + manufacturer=d.Organization.THORLABS, + model="FB470-10", + filter_type="Band pass", + center_wavelength=470, + diameter=25, +) + +filter_7 = d.Filter( + name="Excitation filter 560nm", + manufacturer=d.Organization.THORLABS, + model="FB560-10", + filter_type="Band pass", + diameter=25, + center_wavelength=560, +) + +filter_8 = d.Filter( + name="450 Dichroic Longpass Filter", + manufacturer=d.Organization.EDMUND_OPTICS, + model="#69-898", + filter_type="Dichroic", + cut_off_wavelength=450, + width=35.6, + height=25.2, +) + +filter_9 = d.Filter( + name="500 Dichroic Longpass Filter", + manufacturer=d.Organization.EDMUND_OPTICS, + model="#69-899", + filter_type="Dichroic", + cut_off_wavelength=500, + width=35.6, + height=23.2, +) + +lens = d.Lens( + manufacturer=d.Organization.THORLABS, + model="AC254-080-A-ML", + name="Image focusing lens", + focal_length=80, + focal_length_unit=SizeUnit.MM, + size=1, +) + +daq = d.HarpDevice( + name="Harp Behavior", + harp_device_type=d.HarpDeviceType.BEHAVIOR, + core_version="2.1", + computer_name="behavior_computer", + is_clock_generator=False, + channels=[ + d.DAQChannel(channel_name="DO0", device_name="Solenoid Left", channel_type="Digital Output"), + d.DAQChannel(channel_name="DO1", device_name="Solenoid Right", channel_type="Digital Output"), + d.DAQChannel(channel_name="DI0", device_name="Lick-o-meter Left", channel_type="Digital Input"), + d.DAQChannel(channel_name="DI1", device_name="Lick-o-meter Right", channel_type="Digital Input"), + d.DAQChannel(channel_name="DI3", device_name="Photometry Clock", channel_type="Digital Input"), + ], +) + +mouse_platform = d.Disc(name="mouse_disc", radius=8.5) + +stimulus_device = d.RewardDelivery( + reward_spouts=[ + d.RewardSpout( + name="Left spout", + side=d.SpoutSide.LEFT, + spout_diameter=1.2, + solenoid_valve=d.Device(name="Solenoid Left"), + lick_sensor=d.Device( + name="Lick-o-meter Left", ), ), - d.CameraAssembly( - name="BehaviorVideography_FaceBottom", - camera_target=d.CameraTarget.FACE_BOTTOM, - camera=d.Camera( - name="Bottom face Camera", - detector_type="Camera", - serial_number="TBD", - manufacturer=d.Organization.AILIPU, - model="ELP-USBFHD05MT-KL170IR", - notes="The light intensity sensor was removed; IR illumination is constantly on", - data_interface="USB", - computer_name="W10DTJK7N0M3", - frame_rate=120, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=640, - sensor_height=480, - chroma="Color", - cooling="Air", - bin_mode="Additive", - recording_software=d.Software(name="Bonsai", version="2.5"), - ), - lens=d.Lens( - name="Xenocam 2", - model="XC0922LENS", - serial_number="unknown", - manufacturer=d.Organization.OTHER, - max_aperture="f/1.4", - notes='Focal Length 9-22mm 1/3" IR F1.4', + d.RewardSpout( + name="Right spout", + side=d.SpoutSide.RIGHT, + spout_diameter=1.2, + solenoid_valve=d.Device(name="Solenoid Right"), + lick_sensor=d.Device( + name="Lick-o-meter Right", ), ), + ] +) + +additional_device = d.Device(name="Photometry Clock") + +calibration = d.Calibration( + calibration_date=datetime(2023, 10, 2, 3, 15, 22, tzinfo=timezone.utc), + device_name="470nm LED", + description="LED calibration", + input={"Power setting": [1, 2, 3]}, + output={"Power mW": [5, 10, 13]}, +) + +rig = r.Rig( + rig_id="428_FIP1_20231003", + modification_date=date(2023, 10, 3), + modalities=[Modality.FIB], + components=[ + camera_assembly_1, + camera_assembly_2, + patch_cord, + light_source_1, + light_source_2, + light_source_3, + detector_1, + detector_2, + objective, + filter_1, + filter_2, + filter_3, + filter_4, + filter_5, + filter_6, + filter_7, + filter_8, + filter_9, + lens, + daq, + stimulus_device, + additional_device, ], - patch_cords=[ - d.Patch( - name="Bundle Branching Fiber-optic Patch Cord", - manufacturer=d.Organization.DORIC, - model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - core_diameter=200, - numerical_aperture=0.37, - ) - ], - light_sources=[ - d.LightEmittingDiode( - name="470nm LED", - manufacturer=d.Organization.THORLABS, - model="M470F3", - wavelength=470, - ), - d.LightEmittingDiode( - name="415nm LED", - manufacturer=d.Organization.THORLABS, - model="M415F3", - wavelength=415, - ), - d.LightEmittingDiode( - name="565nm LED", - manufacturer=d.Organization.THORLABS, - model="M565F3", - wavelength=565, - ), - ], - detectors=[ - d.Detector( - name="Green CMOS", - serial_number="21396991", - manufacturer=d.Organization.FLIR, - model="BFS-U3-20S40M", - detector_type="Camera", - data_interface="USB", - cooling="Air", - immersion="air", - bin_width=4, - bin_height=4, - bin_mode="Additive", - crop_offset_x=0, - crop_offset_y=0, - crop_width=200, - crop_height=200, - gain=2, - chroma="Monochrome", - bit_depth=16, - ), - d.Detector( - name="Red CMOS", - serial_number="21396991", - manufacturer=d.Organization.FLIR, - model="BFS-U3-20S40M", - detector_type="Camera", - data_interface="USB", - cooling="Air", - immersion="air", - bin_width=4, - bin_height=4, - bin_mode="Additive", - crop_offset_x=0, - crop_offset_y=0, - crop_width=200, - crop_height=200, - gain=2, - chroma="Monochrome", - bit_depth=16, - ), - ], - objectives=[ - d.Objective( - name="Objective", - serial_number="128022336", - manufacturer=d.Organization.NIKON, - model="CFI Plan Apochromat Lambda D 10x", - numerical_aperture=0.45, - magnification=10, - immersion="air", - ) - ], - filters=[ - d.Filter( - name="Green emission filter", - manufacturer=d.Organization.SEMROCK, - model="FF01-520/35-25", - filter_type="Band pass", - center_wavelength=520, - diameter=25, - ), - d.Filter( - name="Red emission filter", - manufacturer=d.Organization.SEMROCK, - model="FF01-600/37-25", - filter_type="Band pass", - center_wavelength=600, - diameter=25, - ), - d.Filter( - name="Emission Dichroic", - model="FF562-Di03-25x36", - manufacturer=d.Organization.SEMROCK, - filter_type="Dichroic", - height=25, - width=36, - cut_off_wavelength=562, - ), - d.Filter( - name="dual-edge standard epi-fluorescence dichroic beamsplitter", - model="FF493/574-Di01-25x36", - manufacturer=d.Organization.SEMROCK, - notes="493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", - filter_type="Multiband", - width=36, - height=24, - ), - d.Filter( - name="Excitation filter 410nm", - manufacturer=d.Organization.THORLABS, - model="FB410-10", - filter_type="Band pass", - diameter=25, - center_wavelength=410, - ), - d.Filter( - name="Excitation filter 470nm", - manufacturer=d.Organization.THORLABS, - model="FB470-10", - filter_type="Band pass", - center_wavelength=470, - diameter=25, - ), - d.Filter( - name="Excitation filter 560nm", - manufacturer=d.Organization.THORLABS, - model="FB560-10", - filter_type="Band pass", - diameter=25, - center_wavelength=560, - ), - d.Filter( - name="450 Dichroic Longpass Filter", - manufacturer=d.Organization.EDMUND_OPTICS, - model="#69-898", - filter_type="Dichroic", - cut_off_wavelength=450, - width=35.6, - height=25.2, - ), - d.Filter( - name="500 Dichroic Longpass Filter", - manufacturer=d.Organization.EDMUND_OPTICS, - model="#69-899", - filter_type="Dichroic", - cut_off_wavelength=500, - width=35.6, - height=23.2, - ), - ], - lenses=[ - d.Lens( - manufacturer=d.Organization.THORLABS, - model="AC254-080-A-ML", - name="Image focusing lens", - focal_length=80, - focal_length_unit=SizeUnit.MM, - size=1, - ) - ], - daqs=[ - d.HarpDevice( - name="Harp Behavior", - harp_device_type=d.HarpDeviceType.BEHAVIOR, - core_version="2.1", - computer_name="behavior_computer", - is_clock_generator=False, - channels=[ - d.DAQChannel(channel_name="DO0", device_name="Solenoid Left", channel_type="Digital Output"), - d.DAQChannel(channel_name="DO1", device_name="Solenoid Right", channel_type="Digital Output"), - d.DAQChannel(channel_name="DI0", device_name="Lick-o-meter Left", channel_type="Digital Input"), - d.DAQChannel(channel_name="DI1", device_name="Lick-o-meter Right", channel_type="Digital Input"), - d.DAQChannel(channel_name="DI3", device_name="Photometry Clock", channel_type="Digital Input"), - ], - ) - ], - mouse_platform=d.Disc(name="mouse_disc", radius=8.5), - stimulus_devices=[ - d.RewardDelivery( - reward_spouts=[ - d.RewardSpout( - name="Left spout", - side=d.SpoutSide.LEFT, - spout_diameter=1.2, - solenoid_valve=d.Device(device_type="Solenoid", name="Solenoid Left"), - lick_sensor=d.Device( - name="Lick-o-meter Left", - device_type="Lick detector", - ), - ), - d.RewardSpout( - name="Right spout", - side=d.SpoutSide.RIGHT, - spout_diameter=1.2, - solenoid_valve=d.Device(device_type="Solenoid", name="Solenoid Right"), - lick_sensor=d.Device( - name="Lick-o-meter Right", - device_type="Lick detector", - ), - ), - ] - ) - ], - additional_devices=[d.Device(device_type="Photometry Clock", name="Photometry Clock")], - calibrations=[ - d.Calibration( - calibration_date=datetime(2023, 10, 2, 3, 15, 22, tzinfo=timezone.utc), - device_name="470nm LED", - description="LED calibration", - input={"Power setting": [1, 2, 3]}, - output={"Power mW": [5, 10, 13]}, - ) - ], + mouse_platform=mouse_platform, + calibrations=[calibration], ) + serialized = rig.model_dump_json() deserialized = r.Rig.model_validate_json(serialized) deserialized.write_standard_file(prefix="fip_ophys") diff --git a/examples/mri_session.json b/examples/mri_session.json index 724c84dfd..8ced0dc2a 100644 --- a/examples/mri_session.json +++ b/examples/mri_session.json @@ -48,7 +48,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "scanner_location": "Fred Hutch", "magnetic_strength": 7, @@ -88,7 +88,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "scanner_location": "Fred Hutch", "magnetic_strength": 7, diff --git a/examples/ophys_procedures.json b/examples/ophys_procedures.json index 867308d06..b854cd1cd 100644 --- a/examples/ophys_procedures.json +++ b/examples/ophys_procedures.json @@ -91,7 +91,7 @@ "model": null, "path_to_cad": null, "port_index": null, - "additional_settings": {}, + "additional_settings": null, "notes": null, "core_diameter": "200", "core_diameter_unit": "micrometer", diff --git a/examples/test.py b/examples/test.py deleted file mode 100644 index ea2c88a9d..000000000 --- a/examples/test.py +++ /dev/null @@ -1,30 +0,0 @@ -from typing import Annotated, List, Union -from pydantic import BaseModel, Field - - -class TypeA(BaseModel): - item_type: str = "TypeA" - value: int - - -class TypeB(BaseModel): - item_type: str = "TypeB" - value: str - - -class TypeC(BaseModel): - item_type: str = "TypeC" - value: float - - -class MyModel(BaseModel): - items: List[Annotated[Union[TypeA, TypeB, TypeC], Field(discriminator="item_type")]] - - -# Example usage: -data = MyModel(items=[TypeA(value=42), TypeB(value="string"), TypeC(value=3.14)]) -print(data) - -# Example: Providing only a subset -data_subset = MyModel(items=[TypeA(value=42), TypeC(value=3.14)]) -print(data_subset) diff --git a/src/aind_data_schema/components/devices.py b/src/aind_data_schema/components/devices.py index cf30b9202..bd634ab0c 100644 --- a/src/aind_data_schema/components/devices.py +++ b/src/aind_data_schema/components/devices.py @@ -264,7 +264,7 @@ class MyomatrixArrayType(str, Enum): class Device(DataModel): """Generic device""" - device_type: str = Field(..., title="Device type") # Needs to be set by child classes that inherits + device_type: Literal["device"] = "device" name: str = Field(..., title="Device name") serial_number: Optional[str] = Field(default=None, title="Serial number") manufacturer: Optional[Organization.ONE_OF] = Field(default=None, title="Manufacturer") @@ -449,6 +449,7 @@ class CameraAssembly(DataModel): # required fields name: str = Field(..., title="Camera assembly name") + device_type: Literal["Camera assembly"] = "Camera assembly" camera_target: CameraTarget = Field(..., title="Camera target") camera: Camera = Field(..., title="Camera") lens: Lens = Field(..., title="Lens") @@ -614,6 +615,7 @@ class LaserAssembly(DataModel): """Assembly for optogenetic stimulation""" name: str = Field(..., title="Laser assembly name") + device_type: Literal["Laser assembly"] = "Laser assembly" manipulator: Manipulator = Field(..., title="Manipulator") lasers: List[Laser] = Field(..., title="Lasers connected to this module") collimator: Device = Field(..., title="Collimator") @@ -642,6 +644,7 @@ class EphysAssembly(DataModel): """Module for electrophysiological recording""" name: str = Field(..., title="Ephys assembly name") + device_type: Literal["Ephys assembly"] = "Ephys assembly" manipulator: Manipulator = Field(..., title="Manipulator") probes: List[EphysProbe] = Field(..., title="Probes that are held by this module") @@ -663,6 +666,7 @@ class FiberAssembly(DataModel): """Module for inserted fiber photometry recording""" name: str = Field(..., title="Fiber assembly name") + device_type: Literal["Fiber assembly"] = "Fiber assembly" manipulator: Manipulator = Field(..., title="Manipulator") fibers: List[FiberProbe] = Field(..., title="Probes that are held by this module") @@ -904,6 +908,7 @@ def validate_other(cls, value: Optional[str], info: ValidationInfo) -> Optional[ class ScanningStage(MotorizedStage): """Description of a scanning motorized stages""" + device_type: Literal["Scanning stage"] = "Scanning stage" stage_axis_direction: StageAxisDirection = Field(..., title="Direction of stage axis") stage_axis_name: StageAxisName = Field(..., title="Name of stage axis") diff --git a/src/aind_data_schema/core/metadata.py b/src/aind_data_schema/core/metadata.py index 16ef01f87..6dd4b6c0d 100644 --- a/src/aind_data_schema/core/metadata.py +++ b/src/aind_data_schema/core/metadata.py @@ -24,7 +24,6 @@ from aind_data_schema.base import DataCoreModel, is_dict_corrupt, AwareDatetimeWithDefault from aind_data_schema.core.acquisition import Acquisition from aind_data_schema.core.data_description import DataDescription -from aind_data_schema.core.instrument import Instrument from aind_data_schema.core.procedures import Injection, Procedures, Surgery from aind_data_schema.core.processing import Processing from aind_data_schema.core.quality_control import QualityControl @@ -41,7 +40,6 @@ "rig", "processing", "acquisition", - "instrument", "quality_control", ] @@ -126,9 +124,6 @@ class Metadata(DataCoreModel): acquisition: Optional[Acquisition] = Field( default=None, title="Acquisition", description="Imaging acquisition session" ) - instrument: Optional[Instrument] = Field( - default=None, title="Instrument", description="Instrument, which is a collection of devices" - ) quality_control: Optional[QualityControl] = Field( default=None, title="Quality Control", description="Description of quality metrics for a data asset" ) diff --git a/src/aind_data_schema/core/rig.py b/src/aind_data_schema/core/rig.py index 58d1485b1..f2e1b6b81 100644 --- a/src/aind_data_schema/core/rig.py +++ b/src/aind_data_schema/core/rig.py @@ -166,16 +166,13 @@ def validate_cameras_other(self): @field_validator("connections", mode="after") def validate_device_names(cls, value: List[Connection], info: ValidationInfo) -> List[Connection]: - """validate that all connections map between devices that actually exist - """ + """validate that all connections map between devices that actually exist""" device_names = [device.name for device in info.data.get("components", [])] for connection in value: for device_name in connection.device_names: if device_name not in device_names: - raise ValueError( - f"Device name validation error: '{device_name}' is not part of the rig." - ) + raise ValueError(f"Device name validation error: '{device_name}' is not part of the rig.") return value @@ -193,43 +190,60 @@ def validate_other(cls, value: Optional[str], info: ValidationInfo) -> Optional[ ) return value - @field_validator("modalities", mode="after") - def validate_modalities(cls, value: Set[Modality.ONE_OF], info: ValidationInfo) -> Set[Modality.ONE_OF]: - """Validate that devices exist for the modalities specified""" + @model_validator(mode="after") + def validate_modalities(cls, value): + """ + Validate that devices exist for the modalities specified. + + Args: + cls: The class being validated. + value: The set of modalities to validate. + info: Validation information, including other fields. + + Returns: + The validated set of modalities. + Raises: + ValueError: If a required device type is missing for any modality. + """ + # Define the mapping of modalities to their required device types type_mapping = { Modality.ECEPHYS.abbreviation: [EphysAssembly], - Modality.FIB.abbreviation: [ - [Laser, LightEmittingDiode, Lamp], - [Detector], - [Patch] - ], - Modality.POPHYS.abbreviation: [ - [Laser, LightEmittingDiode, Lamp], - [Detector], - [Objective] - ], - Modality.SLAP.abbreviation: [ - [Laser, LightEmittingDiode, Lamp], - [Detector], - [Objective] - ], + Modality.FIB.abbreviation: [[Laser, LightEmittingDiode, Lamp], [Detector], [Patch]], + Modality.POPHYS.abbreviation: [[Laser, LightEmittingDiode, Lamp], [Detector], [Objective]], + Modality.SLAP.abbreviation: [[Laser, LightEmittingDiode, Lamp], [Detector], [Objective]], Modality.BEHAVIOR_VIDEOS.abbreviation: [CameraAssembly], - Modality.BEHAVIOR.abbreviation: [Olfactometer, RewardDelivery, Speaker, Monitor], + Modality.BEHAVIOR.abbreviation: [[Olfactometer, RewardDelivery, Speaker, Monitor]], } + # Retrieve the components from the validation info + components = value.components errors = [] - for modality in value: - if modality.abbreviation in type_mapping: - for device_type in type_mapping[modality.abbreviation]: - if not any(isinstance(component, device) for device in device_type for component in info.data.get("components", [])): - errors.append( - f"Device type validation error: No device of type {device_type} is part of the rig." - ) - - if len(errors) > 0: - message = "\n ".join(errors) - raise ValueError(message) + # Validate each modality + for modality in value.modalities: + required_device_groups = type_mapping.get(modality.abbreviation) + if not required_device_groups: + # Skip modalities that don't require validation + continue + + # Check each group of required devices + for required_group in required_device_groups: + if not isinstance(required_group, list): + required_group = [required_group] + + # Check if at least one required device is present + if not any( + any(isinstance(component, device_type) for device_type in required_group) + for component in components + ): + errors.append( + f"Device type validation error: modality '{modality.abbreviation}' requires at least one device of type(s) " + f"{', '.join(device.__name__ for device in required_group)} in the rig components." + ) + + # Raise an error if there are validation issues + if errors: + raise ValueError("\n".join(errors)) return value diff --git a/tests/test_imaging.py b/tests/test_imaging.py index 59963d52f..cfb24236a 100644 --- a/tests/test_imaging.py +++ b/tests/test_imaging.py @@ -18,8 +18,9 @@ ) from aind_data_schema.components.devices import Calibration, DAQChannel, DAQDevice from aind_data_schema.core import acquisition as acq -from aind_data_schema.core import instrument as inst from aind_data_schema.core.processing import Registration +from aind_data_schema.core.rig import Rig +from aind_data_schema_models.modalities import Modality PYD_VERSION = re.match(r"(\d+.\d+).\d+", pyd_version).group(1) @@ -75,27 +76,25 @@ def test_constructors(self): self.assertIsNotNone(a) with self.assertRaises(ValidationError): - inst.Instrument() + Rig() - i = inst.Instrument( + i = Rig( + rig_id="exaSPIM1-1", + modalities=[Modality.SMARTSPIM], instrument_type="diSPIM", modification_date=datetime.now().date(), manufacturer=Organization.LIFECANVAS, - objectives=[], - detectors=[], - light_sources=[], ) self.assertIsNotNone(i) with self.assertRaises(ValidationError) as e1: - inst.Instrument( + Rig( + rig_id="exaSPIM1-1", + modalities=[Modality.SMARTSPIM], instrument_type="Other", modification_date=datetime(2020, 10, 10, 0, 0, 0).date(), manufacturer=Organization.OTHER, - objectives=[], - detectors=[], - light_sources=[], ) expected_exception1 = ( @@ -109,12 +108,12 @@ def test_constructors(self): self.assertEqual(expected_exception1, repr(e1.exception)) with self.assertRaises(ValidationError) as e2: - inst.Instrument( + Rig( + rig_id="exaSPIM1-1", + modalities=[Modality.SMARTSPIM], + modification_date=datetime(2020, 10, 10, 0, 0, 0).date(), instrument_type="diSPIM", manufacturer=Organization.OTHER, - objectives=[], - detectors=[], - light_sources=[], ) expected_exception2 = ( @@ -212,13 +211,7 @@ def test_validators(self): """test the validators""" with self.assertRaises(ValidationError) as e: - inst.Instrument( - instrument_id="exaSPIM1-1", - instrument_type="exaSPIM", - modification_date=date(2023, 10, 4), - manufacturer=Organization.CUSTOM, - daqs=[ - DAQDevice( + daq = DAQDevice( model="PCIe-6738", data_interface="USB", computer_name="Dev2", @@ -270,6 +263,15 @@ def test_validators(self): ), ], ) + + Rig( + rig_id="exaSPIM1-1", + modalities=[Modality.SMARTSPIM], + instrument_type="exaSPIM", + modification_date=date(2023, 10, 4), + manufacturer=Organization.CUSTOM, + components=[ + daq ], ) expected_exception = ( diff --git a/tests/test_rig.py b/tests/test_rig.py index 568b988ba..18f6a8e83 100644 --- a/tests/test_rig.py +++ b/tests/test_rig.py @@ -802,7 +802,17 @@ def test_rig_id_validator(self): rig_id="123", modification_date=date(2020, 10, 10), modalities=[Modality.ECEPHYS, Modality.FIB], - components=[*daqs, camera, stick_microscope, light_source, *lms, *ems, detector, patch_cord, stimulus_device], + components=[ + *daqs, + camera, + stick_microscope, + light_source, + *lms, + *ems, + detector, + patch_cord, + stimulus_device, + ], mouse_platform=Disc(name="Disc A", radius=1), calibrations=[calibration], ) @@ -811,7 +821,17 @@ def test_rig_id_validator(self): rig_id="123_EPHYS-OPTO_2020-01-01", modification_date=date(2020, 10, 10), modalities=[Modality.ECEPHYS, Modality.FIB], - components=[*daqs, camera, stick_microscope, light_source, *lms, *ems, detector, patch_cord, stimulus_device], + components=[ + *daqs, + camera, + stick_microscope, + light_source, + *lms, + *ems, + detector, + patch_cord, + stimulus_device, + ], mouse_platform=Disc(name="Disc A", radius=1), calibrations=[calibration], ) From 33d1f9deabd42655f9e1949dc3d30851ddee98f9 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Thu, 16 Jan 2025 10:41:15 -0800 Subject: [PATCH 03/34] refactor: rename Rig to Instrument, fixes to tests --- examples/aibs_smartspim_rig.py | 8 +- examples/aind_smartspim_rig.py | 8 +- examples/bergamo_ophys_session.py | 2 +- examples/ephys_rig.py | 10 +- examples/ephys_session.py | 2 +- examples/exaspim_rig.py | 20 +- examples/fip_behavior_rig.py | 8 +- examples/fip_ophys_rig.py | 10 +- examples/mri_session.py | 2 +- examples/multiplane_ophys_session.py | 2 +- examples/ophys_session.py | 2 +- src/aind_data_schema/core/data_description.py | 1 - .../core/{rig.py => instrument.py} | 41 +- src/aind_data_schema/core/metadata.py | 8 +- src/aind_data_schema/core/session.py | 2 +- .../utils/compatibility_check.py | 104 ++- tests/test_diagram_builder.py | 4 +- tests/test_imaging.py | 149 ++- ...lity.py => test_inst_acq_compatibility.py} | 34 +- tests/test_instrument.py | 498 ++++++++++ tests/test_metadata.py | 33 +- tests/test_rig.py | 865 ------------------ tests/test_session.py | 4 +- 23 files changed, 730 insertions(+), 1087 deletions(-) rename src/aind_data_schema/core/{rig.py => instrument.py} (86%) rename tests/{test_rig_session_compatibility.py => test_inst_acq_compatibility.py} (97%) create mode 100644 tests/test_instrument.py delete mode 100644 tests/test_rig.py diff --git a/examples/aibs_smartspim_rig.py b/examples/aibs_smartspim_rig.py index 6ad750437..277146289 100644 --- a/examples/aibs_smartspim_rig.py +++ b/examples/aibs_smartspim_rig.py @@ -17,7 +17,7 @@ ScanningStage, ) from aind_data_schema_models.modalities import Modality -from aind_data_schema.core.rig import Com, Rig +from aind_data_schema.core.instrument import Com, Rig objective = Objective( name="TLX Objective", @@ -181,8 +181,8 @@ model="Large-uncoated-glass", ) -inst = Rig( - rig_id="440_SmartSPIM2_20231004", +inst = Instrument( + instrument_id="440_SmartSPIM2_20231004", modification_date=datetime.date(2023, 10, 4), instrument_type=ImagingInstrumentType.SMARTSPIM, modalities=[Modality.SPIM], @@ -223,5 +223,5 @@ ], ) serialized = inst.model_dump_json() -deserialized = Rig.model_validate_json(serialized) +deserialized = Instrument.model_validate_json(serialized) deserialized.write_standard_file(prefix="aibs_smartspim") diff --git a/examples/aind_smartspim_rig.py b/examples/aind_smartspim_rig.py index 4dad6fd32..846e63284 100644 --- a/examples/aind_smartspim_rig.py +++ b/examples/aind_smartspim_rig.py @@ -13,7 +13,7 @@ OpticalTable, ScanningStage, ) -from aind_data_schema.core.rig import Com, Detector, Rig, Objective +from aind_data_schema.core.instrument import Com, Detector, Rig, Objective from aind_data_schema_models.modalities import Modality objective_1 = Objective( @@ -260,8 +260,8 @@ scanning_stages = [scanning_stage_1, scanning_stage_2, scanning_stage_3] optical_tables = [optical_table_1] -inst = Rig( - rig_id="440_SmartSPIM1_20231004", +inst = Instrument( + instrument_id="440_SmartSPIM1_20231004", instrument_type=ImagingInstrumentType.SMARTSPIM, manufacturer=Organization.LIFECANVAS, modification_date=datetime.date(2023, 10, 4), @@ -290,5 +290,5 @@ ) serialized = inst.model_dump_json() -deserialized = Rig.model_validate_json(serialized) +deserialized = Instrument.model_validate_json(serialized) deserialized.write_standard_file(prefix="aind_smartspim") diff --git a/examples/bergamo_ophys_session.py b/examples/bergamo_ophys_session.py index 64d7fe69a..c56e40b0f 100644 --- a/examples/bergamo_ophys_session.py +++ b/examples/bergamo_ophys_session.py @@ -28,7 +28,7 @@ subject_id="652567", session_type="BCI Photometry", iacuc_protocol="2115", - rig_id="ophys_rig", + instrument_id="ophys_inst", mouse_platform_name="Mouse tube", active_mouse_platform=False, data_streams=[ diff --git a/examples/ephys_rig.py b/examples/ephys_rig.py index e623b08ae..ab9f33879 100644 --- a/examples/ephys_rig.py +++ b/examples/ephys_rig.py @@ -27,7 +27,7 @@ Patch, ProbePort, ) -from aind_data_schema.core.rig import Rig +from aind_data_schema.core.instrument import Instrument # Describes a rig with running wheel, 2 behavior cameras, one Harp Behavior board, # one dual-color laser module, one stick microscope, and 2 Neuropixels probes @@ -275,8 +275,8 @@ output={"power mW": [1, 2, 7]}, ) -rig = Rig( - rig_id="323_EPHYS1_20231003", +inst = Instrument( + instrument_id="323_EPHYS1_20231003", modification_date=date(2023, 10, 3), modalities=[Modality.ECEPHYS], components=[ @@ -295,6 +295,6 @@ mouse_platform=running_wheel, calibrations=[red_laser_calibration, blue_laser_calibration], ) -serialized = rig.model_dump_json() -deserialized = Rig.model_validate_json(serialized) +serialized = inst.model_dump_json() +deserialized = Instrument.model_validate_json(serialized) deserialized.write_standard_file(prefix="ephys") diff --git a/examples/ephys_session.py b/examples/ephys_session.py index 226f424fc..535050099 100644 --- a/examples/ephys_session.py +++ b/examples/ephys_session.py @@ -25,7 +25,7 @@ session_end_time=datetime(year=2023, month=4, day=25, hour=3, minute=16, second=0, tzinfo=timezone.utc), session_type="Receptive field mapping", iacuc_protocol="2109", - rig_id="323_EPHYS1_20231003", + instrument_id="323_EPHYS1_20231003", active_mouse_platform=False, mouse_platform_name="Running Wheel", stimulus_epochs=[ diff --git a/examples/exaspim_rig.py b/examples/exaspim_rig.py index 32130c9a6..a3bd14ba5 100644 --- a/examples/exaspim_rig.py +++ b/examples/exaspim_rig.py @@ -5,8 +5,18 @@ from aind_data_schema_models.organizations import Organization from aind_data_schema_models.units import SizeUnit, FrequencyUnit -from aind_data_schema.components.devices import AdditionalImagingDevice, DAQChannel, DAQDevice, Detector, Filter, Laser, Objective, OpticalTable, ScanningStage -from aind_data_schema.core.rig import Rig, Com +from aind_data_schema.components.devices import ( + AdditionalImagingDevice, + DAQChannel, + DAQDevice, + Detector, + Filter, + Laser, + Objective, + OpticalTable, + ScanningStage, +) +from aind_data_schema.core.instrument import Instrument, Com from aind_data_schema_models.modalities import Modality objectives = [ @@ -202,8 +212,8 @@ ) ] -inst = Rig( - rig_id="440_exaSPIM1_20231004", +inst = Instrument( + instrument_id="440_exaSPIM1_20231004", modalities=[Modality.SPIM], instrument_type="exaSPIM", modification_date=datetime.date(2023, 10, 4), @@ -232,5 +242,5 @@ ) serialized = inst.model_dump_json() -deserialized = Rig.model_validate_json(serialized) +deserialized = Instrument.model_validate_json(serialized) deserialized.write_standard_file(prefix="exaspim") diff --git a/examples/fip_behavior_rig.py b/examples/fip_behavior_rig.py index 487ef074b..a061574d3 100644 --- a/examples/fip_behavior_rig.py +++ b/examples/fip_behavior_rig.py @@ -30,7 +30,7 @@ Filter, Tube, ) -from aind_data_schema.core.rig import Rig +from aind_data_schema.core.instrument import Instrument camera1 = CameraAssembly( name="BehaviorVideography_FaceSide", @@ -341,8 +341,8 @@ # Water calibration comes here# ] -rig = Rig( - rig_id="447_FIP-Behavior_20000101", +inst = Instrument( + instrument_id="447_FIP-Behavior_20000101", modification_date=date(2000, 1, 1), modalities=[Modality.BEHAVIOR, Modality.FIB], mouse_platform=Tube(name="mouse_tube_foraging", diameter=4.0), @@ -362,4 +362,4 @@ calibrations=calibrations, ) -rig.write_standard_file(prefix="fip_behavior") +inst.write_standard_file(prefix="fip_behavior") diff --git a/examples/fip_ophys_rig.py b/examples/fip_ophys_rig.py index 51d5bd0ce..f65cef664 100644 --- a/examples/fip_ophys_rig.py +++ b/examples/fip_ophys_rig.py @@ -6,7 +6,7 @@ from aind_data_schema_models.units import FrequencyUnit, SizeUnit import aind_data_schema.components.devices as d -import aind_data_schema.core.rig as r +import aind_data_schema.core.instrument as r camera_assembly_1 = d.CameraAssembly( name="BehaviorVideography_FaceSide", @@ -295,8 +295,8 @@ output={"Power mW": [5, 10, 13]}, ) -rig = r.Rig( - rig_id="428_FIP1_20231003", +instrument = r.Instrument( + instrument_id="428_FIP1_20231003", modification_date=date(2023, 10, 3), modalities=[Modality.FIB], components=[ @@ -327,6 +327,6 @@ calibrations=[calibration], ) -serialized = rig.model_dump_json() -deserialized = r.Rig.model_validate_json(serialized) +serialized = inst.model_dump_json() +deserialized = r.Instrument.model_validate_json(serialized) deserialized.write_standard_file(prefix="fip_ophys") diff --git a/examples/mri_session.py b/examples/mri_session.py index 100cad009..36acdbbfa 100644 --- a/examples/mri_session.py +++ b/examples/mri_session.py @@ -66,7 +66,7 @@ protocol_id=["dx.doi.org/10.57824/protocols.io.bh7kl4n6"], iacuc_protocol="1234", session_type="3D MRI Volume", - rig_id="NA", + instrument_id="NA", data_streams=[stream], mouse_platform_name="NA", active_mouse_platform=False, diff --git a/examples/multiplane_ophys_session.py b/examples/multiplane_ophys_session.py index 961cc844b..a36d4a9ef 100644 --- a/examples/multiplane_ophys_session.py +++ b/examples/multiplane_ophys_session.py @@ -19,7 +19,7 @@ subject_id="12345", session_type="Mesoscope", iacuc_protocol="12345", - rig_id="MESO.1", + instrument_id="MESO.1", mouse_platform_name="disc", active_mouse_platform=True, data_streams=[ diff --git a/examples/ophys_session.py b/examples/ophys_session.py index dcf948f2d..7988f49a5 100644 --- a/examples/ophys_session.py +++ b/examples/ophys_session.py @@ -15,7 +15,7 @@ subject_id="652567", session_type="Parameter Testing", iacuc_protocol="2115", - rig_id="ophys_rig", + instrument_id="ophys_inst", mouse_platform_name="Disc", active_mouse_platform=False, data_streams=[ diff --git a/src/aind_data_schema/core/data_description.py b/src/aind_data_schema/core/data_description.py index 1a4d5cfc9..77dbf76c3 100644 --- a/src/aind_data_schema/core/data_description.py +++ b/src/aind_data_schema/core/data_description.py @@ -142,7 +142,6 @@ def parse_name(cls, name): @model_validator(mode="after") def build_name(self): """sets the name of the file""" - print(self) if self.label is not None and self.name is None: self.name = build_data_name(self.label, creation_datetime=self.creation_time) elif self.name is None: diff --git a/src/aind_data_schema/core/rig.py b/src/aind_data_schema/core/instrument.py similarity index 86% rename from src/aind_data_schema/core/rig.py rename to src/aind_data_schema/core/instrument.py index f2e1b6b81..b25250923 100644 --- a/src/aind_data_schema/core/rig.py +++ b/src/aind_data_schema/core/instrument.py @@ -47,7 +47,7 @@ ) MOUSE_PLATFORMS = Annotated[Union[tuple(MousePlatform.__subclasses__())], Field(discriminator="device_type")] -RIG_ID_PATTERN = r"^[a-zA-Z0-9]+_[a-zA-Z0-9-]+_\d{8}$" +instrument_id_PATTERN = r"^[a-zA-Z0-9]+_[a-zA-Z0-9-]+_\d{8}$" class Com(DataModel): @@ -66,20 +66,20 @@ class Connection(DataModel): channels: Optional[List[int]] = Field(default=None, title="Connection channels") -class Rig(DataCoreModel): - """Description of a rig""" +class Instrument(DataCoreModel): + """Description of an instrument""" # metametadata - _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/rig.py" + _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/inst.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) schema_version: SkipValidation[Literal["1.0.5"]] = Field(default="1.0.5") - # rig definition - rig_id: str = Field( + # instrument definition + instrument_id: str = Field( ..., - description="Unique rig identifier, name convention: --", - title="Rig ID", - pattern=RIG_ID_PATTERN, + description="Unique instrument identifier, name convention: __", + title="Insturment ID", + pattern=instrument_id_PATTERN, ) mouse_platform: Optional[MOUSE_PLATFORMS] = Field(default=None, title="Mouse platform") modification_date: date = Field(..., title="Date of modification") @@ -89,8 +89,8 @@ class Rig(DataCoreModel): title="CCF coordinate transform", description="Path to file that details the CCF-to-lab coordinate transform", ) - origin: Optional[Origin] = Field(default=None, title="Origin point for rig position transforms") - rig_axes: Optional[List[Axis]] = Field(default=None, title="Rig axes", min_length=3, max_length=3) + origin: Optional[Origin] = Field(default=None, title="Origin point for instrument position transforms") + instrument_axes: Optional[List[Axis]] = Field(default=None, title="Instrument axes", min_length=3, max_length=3) modalities: Set[Modality.ONE_OF] = Field(..., title="Modalities") com_ports: List[Com] = Field(default=[], title="COM ports") instrument_type: Optional[ImagingInstrumentType] = Field(default=None, title="Instrument type") @@ -101,7 +101,7 @@ class Rig(DataCoreModel): connections: List[Connection] = Field( default=[], title="Connections", - description="List of all connections between devices in the rig", + description="List of all connections between devices in the instrument", ) components: List[ @@ -172,7 +172,7 @@ def validate_device_names(cls, value: List[Connection], info: ValidationInfo) -> for connection in value: for device_name in connection.device_names: if device_name not in device_names: - raise ValueError(f"Device name validation error: '{device_name}' is not part of the rig.") + raise ValueError(f"Device name validation error: '{device_name}' is not part of the inst.") return value @@ -207,13 +207,16 @@ def validate_modalities(cls, value): ValueError: If a required device type is missing for any modality. """ # Define the mapping of modalities to their required device types + # The list of list pattern is used to allow for multiple options within a group, so e.g. + # FIB requires a light (one of the options) plus a detector and a patch cord type_mapping = { - Modality.ECEPHYS.abbreviation: [EphysAssembly], - Modality.FIB.abbreviation: [[Laser, LightEmittingDiode, Lamp], [Detector], [Patch]], - Modality.POPHYS.abbreviation: [[Laser, LightEmittingDiode, Lamp], [Detector], [Objective]], - Modality.SLAP.abbreviation: [[Laser, LightEmittingDiode, Lamp], [Detector], [Objective]], - Modality.BEHAVIOR_VIDEOS.abbreviation: [CameraAssembly], - Modality.BEHAVIOR.abbreviation: [[Olfactometer, RewardDelivery, Speaker, Monitor]], + Modality.ECEPHYS: [EphysAssembly], + Modality.FIB: [[Laser, LightEmittingDiode, Lamp], [Detector], [Patch]], + Modality.POPHYS: [[Laser, LightEmittingDiode, Lamp], [Detector], [Objective]], + Modality.SLAP: [[Laser, LightEmittingDiode, Lamp], [Detector], [Objective]], + Modality.BEHAVIOR_VIDEOS: [CameraAssembly], + Modality.BEHAVIOR: [[Olfactometer, RewardDelivery, Speaker, Monitor]], + Modality.SPIM: [[Objective], [Detector], [ScanningStage], [MotorizedStage]], } # Retrieve the components from the validation info diff --git a/src/aind_data_schema/core/metadata.py b/src/aind_data_schema/core/metadata.py index 6dd4b6c0d..a234c8f71 100644 --- a/src/aind_data_schema/core/metadata.py +++ b/src/aind_data_schema/core/metadata.py @@ -27,10 +27,10 @@ from aind_data_schema.core.procedures import Injection, Procedures, Surgery from aind_data_schema.core.processing import Processing from aind_data_schema.core.quality_control import QualityControl -from aind_data_schema.core.rig import Rig +from aind_data_schema.core.instrument import Instrument from aind_data_schema.core.session import Session from aind_data_schema.core.subject import Subject -from aind_data_schema.utils.compatibility_check import RigSessionCompatibility +from aind_data_schema.utils.compatibility_check import InstrumentSessionCompatibility CORE_FILES = [ "subject", @@ -119,7 +119,7 @@ class Metadata(DataCoreModel): default=None, title="Procedures", description="All procedures performed on a subject." ) session: Optional[Session] = Field(default=None, title="Session", description="Description of a session.") - rig: Optional[Rig] = Field(default=None, title="Rig", description="Rig.") + rig: Optional[Rig] = Field(default=None, title="Rig", description="Instrument.") processing: Optional[Processing] = Field(default=None, title="Processing", description="All processes run on data.") acquisition: Optional[Acquisition] = Field( default=None, title="Acquisition", description="Imaging acquisition session" @@ -286,7 +286,7 @@ def validate_ecephys_metadata(self): return self @model_validator(mode="after") - def validate_rig_session_compatibility(self): + def validate_instrument_session_compatibility(self): """Validator for metadata""" if self.rig and self.session: check = RigSessionCompatibility(self.rig, self.session) diff --git a/src/aind_data_schema/core/session.py b/src/aind_data_schema/core/session.py index 9385c874f..200261fb6 100644 --- a/src/aind_data_schema/core/session.py +++ b/src/aind_data_schema/core/session.py @@ -556,7 +556,7 @@ class Session(DataCoreModel): session_end_time: Optional[AwareDatetimeWithDefault] = Field(default=None, title="Session end time") session_type: str = Field(..., title="Session type") iacuc_protocol: Optional[str] = Field(default=None, title="IACUC protocol") - rig_id: str = Field(..., title="Rig ID") + instrument_id: str = Field(..., title="Insturment ID") calibrations: List[Calibration] = Field( default=[], title="Calibrations", diff --git a/src/aind_data_schema/utils/compatibility_check.py b/src/aind_data_schema/utils/compatibility_check.py index 97f25a663..6ab1b1deb 100644 --- a/src/aind_data_schema/utils/compatibility_check.py +++ b/src/aind_data_schema/utils/compatibility_check.py @@ -2,7 +2,7 @@ from typing import Optional -from aind_data_schema.core.rig import RewardDelivery, Rig +from aind_data_schema.core.instrument import RewardDelivery, Rig from aind_data_schema.core.session import Session @@ -11,22 +11,24 @@ class RigSessionCompatibility: def __init__(self, rig: Rig, session: Session) -> None: """Initiate RigSessionCompatibility class""" - self.rig = rig + self.instrument = rig self.session = session - def _compare_rig_id(self) -> Optional[ValueError]: - """Compares rig_id""" - if self.session.rig_id != self.rig.rig_id: - return ValueError(f"Rig ID in session {self.session.rig_id} does not match the rig's {self.rig.rig_id}.") + def _compare_instrument_id(self) -> Optional[ValueError]: + """Compares instrument_id""" + if self.session.instrument_id != self.inst.instrument_id: + return ValueError( + f"Insturment ID in session {self.session.instrument_id} does not match the rig's {self.inst.instrument_id}." + ) else: return None def _compare_mouse_platform_name(self) -> Optional[ValueError]: """Compares mouse_platform_name""" - if self.session.mouse_platform_name != self.rig.mouse_platform.name: + if self.session.mouse_platform_name != self.inst.mouse_platform.name: return ValueError( f"Mouse platform name in session {self.session.mouse_platform_name} " - f"does not match the rig's {self.rig.mouse_platform.name}" + f"does not match the rig's {self.inst.mouse_platform.name}" ) def _compare_daq_names(self) -> Optional[ValueError]: @@ -34,11 +36,11 @@ def _compare_daq_names(self) -> Optional[ValueError]: session_daqs = [ daq for stream in getattr(self.session, "data_streams", []) for daq in getattr(stream, "daq_names", []) ] - rig_daqs = [getattr(daq, "name", None) for daq in getattr(self.rig, "daqs", [])] - if not set(session_daqs).issubset(set(rig_daqs)): + instrument_daqs = [getattr(daq, "name", None) for daq in getattr(self.rig, "daqs", [])] + if not set(session_daqs).issubset(set(instrument_daqs)): return ValueError( - f"daq names in session do not match daq names in rig. " - f"session_daqs: {set(session_daqs)} rig_daqs: {set(rig_daqs)}" + f"daq names in session do not match daq names in inst. " + f"session_daqs: {set(session_daqs)} instrument_daqs: {set(instrument_daqs)}" ) def _compare_camera_names(self) -> Optional[ValueError]: @@ -48,15 +50,15 @@ def _compare_camera_names(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for camera in getattr(stream, "camera_names", []) ] - rig_cameras = [ + instrument_cameras = [ name for camera_device in getattr(self.rig, "cameras", []) for name in (camera_device.camera.name, camera_device.name) ] - if not set(session_cameras).issubset(set(rig_cameras)): + if not set(session_cameras).issubset(set(instrument_cameras)): return ValueError( - f"camera names in session do not match camera names in rig. " - f"session_cameras: {set(session_cameras)} rig_cameras: {set(rig_cameras)}" + f"camera names in session do not match camera names in inst. " + f"session_cameras: {set(session_cameras)} instrument_cameras: {set(instrument_cameras)}" ) def _compare_light_sources(self) -> Optional[ValueError]: @@ -66,13 +68,13 @@ def _compare_light_sources(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for light_source in getattr(stream, "light_sources", []) ] - rig_light_sources = [ + instrument_light_sources = [ getattr(light_source, "name", None) for light_source in getattr(self.rig, "light_sources", []) ] - if not set(session_light_sources).issubset(set(rig_light_sources)): + if not set(session_light_sources).issubset(set(instrument_light_sources)): return ValueError( - f"light source names in session do not match light source names in rig. " - f"session_light_sources: {set(session_light_sources)} rig_light_sources: {set(rig_light_sources)}" + f"light source names in session do not match light source names in inst. " + f"session_light_sources: {set(session_light_sources)} instrument_light_sources: {set(instrument_light_sources)}" ) def _compare_ephys_assemblies(self) -> Optional[ValueError]: @@ -82,12 +84,14 @@ def _compare_ephys_assemblies(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for ephys_module in getattr(stream, "ephys_modules") ] - rig_ephys_assemblies = [ephys_assembly.name for ephys_assembly in getattr(self.rig, "ephys_assemblies", [])] - if not set(session_ephys_assemblies).issubset(set(rig_ephys_assemblies)): + instrument_ephys_assemblies = [ + ephys_assembly.name for ephys_assembly in getattr(self.rig, "ephys_assemblies", []) + ] + if not set(session_ephys_assemblies).issubset(set(instrument_ephys_assemblies)): return ValueError( - f"ephys assembly names in session do not match ephys assembly names in rig. " + f"ephys assembly names in session do not match ephys assembly names in inst. " f"session_ephys_assemblies: {set(session_ephys_assemblies)}" - f" rig_ephys_assemblies: {set(rig_ephys_assemblies)}" + f" instrument_ephys_assemblies: {set(instrument_ephys_assemblies)}" ) def _compare_stick_microscopes(self) -> Optional[ValueError]: @@ -97,16 +101,16 @@ def _compare_stick_microscopes(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for stick_microscope in getattr(stream, "stick_microscopes", []) ] - rig_stick_microscopes = [ + instrument_stick_microscopes = [ name for camera_device in getattr(self.rig, "stick_microscopes", []) for name in (camera_device.camera.name, camera_device.name) ] - if not set(session_stick_microscopes).issubset(set(rig_stick_microscopes)): + if not set(session_stick_microscopes).issubset(set(instrument_stick_microscopes)): return ValueError( - f"stick microscope names in session do not match stick microscope names in rig. " + f"stick microscope names in session do not match stick microscope names in inst. " f"session_stick_microscopes: {set(session_stick_microscopes)} " - f"rig_stick_microscopes: {set(rig_stick_microscopes)}" + f"instrument_stick_microscopes: {set(instrument_stick_microscopes)}" ) def _compare_manipulator_modules(self) -> Optional[ValueError]: @@ -116,12 +120,14 @@ def _compare_manipulator_modules(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for manipulator_module in getattr(stream, "manipulator_modules", []) ] - rig_manipulator_modules = [laser_assembly.name for laser_assembly in getattr(self.rig, "laser_assemblies", [])] - if not set(session_manipulator_modules).issubset(set(rig_manipulator_modules)): + instrument_manipulator_modules = [ + laser_assembly.name for laser_assembly in getattr(self.rig, "laser_assemblies", []) + ] + if not set(session_manipulator_modules).issubset(set(instrument_manipulator_modules)): return ValueError( - f"manipulator module names in session do not match manipulator names (laser assemblies) in rig. " + f"manipulator module names in session do not match manipulator names (laser assemblies) in inst. " f"session_manipulators: {set(session_manipulator_modules)}" - f" rig_manipulators: {set(rig_manipulator_modules)}" + f" instrument_manipulators: {set(instrument_manipulator_modules)}" ) def _compare_detectors(self) -> Optional[ValueError]: @@ -131,11 +137,11 @@ def _compare_detectors(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for detector in getattr(stream, "detectors", []) ] - rig_detectors = [detector.name for detector in getattr(self.rig, "detectors", [])] - if not set(session_detectors).issubset(set(rig_detectors)): + instrument_detectors = [detector.name for detector in getattr(self.rig, "detectors", [])] + if not set(session_detectors).issubset(set(instrument_detectors)): return ValueError( - f"detector names in session do not match detector names in rig. " - f"session_detectors: {set(session_detectors)} rig_detectors: {set(rig_detectors)}" + f"detector names in session do not match detector names in inst. " + f"session_detectors: {set(session_detectors)} instrument_detectors: {set(instrument_detectors)}" ) def _compare_patch_cords(self) -> Optional[ValueError]: @@ -145,11 +151,11 @@ def _compare_patch_cords(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for fiber_connection in getattr(stream, "fiber_connections", []) ] - rig_patch_cords = [patch_cord.name for patch_cord in getattr(self.rig, "patch_cords", [])] - if not set(session_patch_cords).issubset(set(rig_patch_cords)): + instrument_patch_cords = [patch_cord.name for patch_cord in getattr(self.rig, "patch_cords", [])] + if not set(session_patch_cords).issubset(set(instrument_patch_cords)): return ValueError( - f"patch cord names in session do not match patch cord names in rig. " - f"session_patch_cords: {set(session_patch_cords)} rig_patch_cords: {set(rig_patch_cords)}" + f"patch cord names in session do not match patch cord names in inst. " + f"session_patch_cords: {set(session_patch_cords)} instrument_patch_cords: {set(instrument_patch_cords)}" ) def _compare_fiber_modules(self) -> Optional[ValueError]: @@ -159,11 +165,11 @@ def _compare_fiber_modules(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for fiber_module in getattr(stream, "fiber_modules", []) ] - rig_fiber_modules = [fiber_assembly.name for fiber_assembly in getattr(self.rig, "fiber_assemblies", [])] - if not set(session_fiber_modules).issubset(set(rig_fiber_modules)): + instrument_fiber_modules = [fiber_assembly.name for fiber_assembly in getattr(self.rig, "fiber_assemblies", [])] + if not set(session_fiber_modules).issubset(set(instrument_fiber_modules)): return ValueError( - f"fiber module names in session do not match fiber assembly names in rig. " - f"session_fiber_modules: {set(session_fiber_modules)} rig_fiber_assemblies: {set(rig_fiber_modules)}" + f"fiber module names in session do not match fiber assembly names in inst. " + f"session_fiber_modules: {set(session_fiber_modules)} instrument_fiber_assemblies: {set(instrument_fiber_modules)}" ) def _compare_stimulus_devices(self) -> Optional[ValueError]: @@ -173,7 +179,7 @@ def _compare_stimulus_devices(self) -> Optional[ValueError]: for stimulus_epoch in getattr(self.session, "stimulus_epochs", []) for stimulus_device_name in getattr(stimulus_epoch, "stimulus_device_names") ] - rig_stimulus_devices = [ + instrument_stimulus_devices = [ stimulus_device.name for stimulus_device in getattr(self.rig, "stimulus_devices", []) if not isinstance(stimulus_device, RewardDelivery) @@ -183,17 +189,17 @@ def _compare_stimulus_devices(self) -> Optional[ValueError]: if isinstance(stimulus_device, RewardDelivery) for reward_spout in getattr(stimulus_device, "reward_spouts", []) ] - if not set(session_stimulus_devices).issubset(set(rig_stimulus_devices)): + if not set(session_stimulus_devices).issubset(set(instrument_stimulus_devices)): return ValueError( - f"stimulus device names in session do not match stimulus device names in rig. " + f"stimulus device names in session do not match stimulus device names in inst. " f"session_stimulus_devices: {set(session_stimulus_devices)} " - f"rig_stimulus_devices: {set(rig_stimulus_devices)}" + f"instrument_stimulus_devices: {set(instrument_stimulus_devices)}" ) def run_compatibility_check(self) -> None: """Runs compatibility check.Creates a dictionary of fields and whether it matches in rig and session.""" comparisons = [ - self._compare_rig_id(), + self._compare_instrument_id(), self._compare_mouse_platform_name(), self._compare_daq_names(), self._compare_camera_names(), diff --git a/tests/test_diagram_builder.py b/tests/test_diagram_builder.py index 17077896f..80289c0ea 100644 --- a/tests/test_diagram_builder.py +++ b/tests/test_diagram_builder.py @@ -45,7 +45,7 @@ def test_save_all_core_diagrams_default(self, mock_draw: MagicMock): call(Path("acquisition.svg")), call(Path("instrument.svg")), call(Path("procedures.svg")), - call(Path("rig.svg")), + call(Path("inst.svg")), call(Path("session.svg")), call(Path("metadata.nd.svg")), ], @@ -65,7 +65,7 @@ def test_save_all_core_diagrams_dir(self, mock_draw: MagicMock): call(Path("some_dir") / "acquisition.svg"), call(Path("some_dir") / "instrument.svg"), call(Path("some_dir") / "procedures.svg"), - call(Path("some_dir") / "rig.svg"), + call(Path("some_dir") / "inst.svg"), call(Path("some_dir") / "session.svg"), call(Path("some_dir") / "metadata.nd.svg"), ], diff --git a/tests/test_imaging.py b/tests/test_imaging.py index cfb24236a..d676f670d 100644 --- a/tests/test_imaging.py +++ b/tests/test_imaging.py @@ -19,7 +19,7 @@ from aind_data_schema.components.devices import Calibration, DAQChannel, DAQDevice from aind_data_schema.core import acquisition as acq from aind_data_schema.core.processing import Registration -from aind_data_schema.core.rig import Rig +from aind_data_schema.core.instrument import Instrument from aind_data_schema_models.modalities import Modality PYD_VERSION = re.match(r"(\d+.\d+).\d+", pyd_version).group(1) @@ -76,11 +76,11 @@ def test_constructors(self): self.assertIsNotNone(a) with self.assertRaises(ValidationError): - Rig() + Instrument() - i = Rig( - rig_id="exaSPIM1-1", - modalities=[Modality.SMARTSPIM], + i = Instrument( + instrument_id="room_exaSPIM1-1_20231004", + modalities=[Modality.SPIM], instrument_type="diSPIM", modification_date=datetime.now().date(), manufacturer=Organization.LIFECANVAS, @@ -88,29 +88,23 @@ def test_constructors(self): self.assertIsNotNone(i) + # Instrument type Other requires notes with self.assertRaises(ValidationError) as e1: - Rig( - rig_id="exaSPIM1-1", - modalities=[Modality.SMARTSPIM], + Instrument( + instrument_id="room_exaSPIM1-1_20231004", + modalities=[Modality.SPIM], instrument_type="Other", modification_date=datetime(2020, 10, 10, 0, 0, 0).date(), manufacturer=Organization.OTHER, ) - expected_exception1 = ( - "1 validation error for Instrument\n" - "notes\n" - " Value error, Notes cannot be empty if instrument_type is Other." - " Describe the instrument_type in the notes field." - " [type=value_error, input_value=None, input_type=NoneType]\n" - f" For further information visit https://errors.pydantic.dev/{PYD_VERSION}/v/value_error" - ) - self.assertEqual(expected_exception1, repr(e1.exception)) + self.assertIn("instrument_id", repr(e1.exception)) + # Modality SPIM requirements components with self.assertRaises(ValidationError) as e2: - Rig( - rig_id="exaSPIM1-1", - modalities=[Modality.SMARTSPIM], + Instrument( + instrument_id="room_exaSPIM1-1_20231004", + modalities=[Modality.SPIM], modification_date=datetime(2020, 10, 10, 0, 0, 0).date(), instrument_type="diSPIM", manufacturer=Organization.OTHER, @@ -212,67 +206,65 @@ def test_validators(self): with self.assertRaises(ValidationError) as e: daq = DAQDevice( - model="PCIe-6738", - data_interface="USB", - computer_name="Dev2", - manufacturer=Organization.NATIONAL_INSTRUMENTS, - name="Dev2", - serial_number="Unknown", - channels=[ - DAQChannel( - channel_name="3", - channel_type="Analog Output", - device_name="LAS-08308", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="5", - channel_type="Analog Output", - device_name="539251", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="4", - channel_type="Analog Output", - device_name="LAS-08309", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="2", - channel_type="Analog Output", - device_name="stage-x", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="0", - channel_type="Analog Output", - device_name="TL-1", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="6", - channel_type="Analog Output", - device_name="LAS-08307", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - ], - ) + model="PCIe-6738", + data_interface="USB", + computer_name="Dev2", + manufacturer=Organization.NATIONAL_INSTRUMENTS, + name="Dev2", + serial_number="Unknown", + channels=[ + DAQChannel( + channel_name="3", + channel_type="Analog Output", + device_name="LAS-08308", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + DAQChannel( + channel_name="5", + channel_type="Analog Output", + device_name="539251", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + DAQChannel( + channel_name="4", + channel_type="Analog Output", + device_name="LAS-08309", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + DAQChannel( + channel_name="2", + channel_type="Analog Output", + device_name="stage-x", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + DAQChannel( + channel_name="0", + channel_type="Analog Output", + device_name="TL-1", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + DAQChannel( + channel_name="6", + channel_type="Analog Output", + device_name="LAS-08307", + sample_rate=10000, + sample_rate_unit=FrequencyUnit.HZ, + ), + ], + ) - Rig( - rig_id="exaSPIM1-1", - modalities=[Modality.SMARTSPIM], + Instrument( + instrument_id="exaSPIM1-1", + modalities=[Modality.SPIM], instrument_type="exaSPIM", modification_date=date(2023, 10, 4), manufacturer=Organization.CUSTOM, - components=[ - daq - ], + components=[daq], ) expected_exception = ( "2 validation errors for Instrument\n" @@ -282,11 +274,10 @@ def test_validators(self): f" For further information visit https://errors.pydantic.dev/{PYD_VERSION}/v/missing\n" "daqs\n" " Value error, Device name validation error: 'LAS-08308' is connected to '3' on 'Dev2'," - " but this device is not part of the rig. [type=value_error," + " but this device is not part of the inst. [type=value_error," " input_value=[DAQDevice(device_type='D... hardware_version=None)], input_type=list]\n" f" For further information visit https://errors.pydantic.dev/{PYD_VERSION}/v/value_error" ) - print(repr(e.exception)) self.assertEqual(expected_exception, repr(e.exception)) diff --git a/tests/test_rig_session_compatibility.py b/tests/test_inst_acq_compatibility.py similarity index 97% rename from tests/test_rig_session_compatibility.py rename to tests/test_inst_acq_compatibility.py index e67fd129c..0e3415f78 100644 --- a/tests/test_rig_session_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -11,7 +11,7 @@ from aind_data_schema_models.units import FrequencyUnit, SizeUnit import aind_data_schema.components.devices as d -import aind_data_schema.core.rig as r +import aind_data_schema.core.instrument as r from aind_data_schema.components.devices import ( Calibration, Camera, @@ -33,7 +33,7 @@ ProbePort, Software, ) -from aind_data_schema.core.rig import Rig +from aind_data_schema.core.instrument import Instrument from aind_data_schema.core.session import ( CcfCoords, Coordinates3d, @@ -49,11 +49,11 @@ Stream, VisualStimulation, ) -from aind_data_schema.utils.compatibility_check import RigSessionCompatibility +from aind_data_schema.utils.compatibility_check import InstrumentSessionCompatibility from aind_data_schema_models.brain_atlas import CCFStructure EXAMPLES_DIR = Path(__file__).parents[1] / "examples" -EPHYS_RIG_JSON = EXAMPLES_DIR / "ephys_rig.json" +EPHYS_RIG_JSON = EXAMPLES_DIR / "ephys_inst.json" EPHYS_SESSION_JSON = EXAMPLES_DIR / "ephys_session.json" behavior_computer = "W10DT72941" @@ -99,7 +99,7 @@ name="Manipulator A", serial_number="SN2937", manufacturer=Organization.NEW_SCALE_TECHNOLOGIES ), lasers=[red_laser, blue_laser], - collimator=Device(name="Collimator A", device_type="Collimator"), + collimator=Device(name="Collimator A"), fiber=Patch( name="Bundle Branching Fiber-optic Patch Cord", manufacturer=Organization.DORIC, @@ -233,8 +233,8 @@ output={"power mW": [1, 2, 7]}, ) -ephys_rig = Rig( - rig_id="323_EPHYS1_20231003", +ephys_inst = Instrument( + instrument_id="323_EPHYS1_20231003", modification_date=date(2023, 10, 3), modalities=[Modality.ECEPHYS], ephys_assemblies=[ephys_assemblyA, ephys_assemblyB], @@ -253,7 +253,7 @@ session_end_time=datetime(year=2023, month=4, day=25, hour=3, minute=16, second=0, tzinfo=timezone.utc), session_type="Receptive field mapping", iacuc_protocol="2109", - rig_id="323_EPHYS2-RF_2023-04-24_01", + instrument_id="323_EPHYS2-RF_2023-04-24_01", active_mouse_platform=False, mouse_platform_name="mouse platform", stimulus_epochs=[ @@ -480,10 +480,10 @@ def read_json(filepath: Path) -> dict: contents = json.load(f) return contents - cls.example_ephys_rig = Rig.model_validate_json(json.dumps(read_json(EPHYS_RIG_JSON))) + cls.example_ephys_inst = Instrument.model_validate_json(json.dumps(read_json(EPHYS_RIG_JSON))) cls.example_ephys_session = Session.model_validate_json(json.dumps(read_json(EPHYS_SESSION_JSON))) - cls.ophys_rig = r.Rig( - rig_id="428_FIP1_20231003", + cls.ophys_instrument = r.Instrument( + instrument_id="428_FIP1_20231003", modification_date=date(2023, 10, 3), modalities=[Modality.FIB], cameras=[ @@ -783,7 +783,7 @@ def read_json(filepath: Path) -> dict: subject_id="652567", session_type="Parameter Testing", iacuc_protocol="2115", - rig_id="ophys_rig", + instrument_id="ophys_inst", mouse_platform_name="Disc", active_mouse_platform=False, data_streams=[ @@ -847,18 +847,20 @@ def read_json(filepath: Path) -> dict: def test_run_compatibility_check(self): """Tests compatibility check""" - expected_error = "Rig ID in session 323_EPHYS2-RF_2023-04-24_01 does not match the rig's 323_EPHYS1_20231003." + expected_error = ( + "Insturment ID in session 323_EPHYS2-RF_2023-04-24_01 does not match the rig's 323_EPHYS1_20231003." + ) with self.assertRaises(ValueError) as context: - RigSessionCompatibility(rig=ephys_rig, session=ephys_session).run_compatibility_check() + RigSessionCompatibility(rig=ephys_inst, session=ephys_session).run_compatibility_check() self.assertIn(expected_error, str(context.exception)) with self.assertRaises(ValueError): - RigSessionCompatibility(rig=self.ophys_rig, session=self.ophys_session).run_compatibility_check() + RigSessionCompatibility(rig=self.ophys_inst, session=self.ophys_session).run_compatibility_check() def test_check_examples_compatibility(self): """Tests that examples are compatible""" # check that ephys session and rig are synced - example_ephys_check = RigSessionCompatibility(rig=self.example_ephys_rig, session=self.example_ephys_session) + example_ephys_check = RigSessionCompatibility(rig=self.example_ephys_inst, session=self.example_ephys_session) self.assertIsNone(example_ephys_check.run_compatibility_check()) diff --git a/tests/test_instrument.py b/tests/test_instrument.py new file mode 100644 index 000000000..192fce0de --- /dev/null +++ b/tests/test_instrument.py @@ -0,0 +1,498 @@ +""" test Rig """ + +from decimal import Decimal +import json +import unittest +from datetime import date, datetime + +from aind_data_schema_models.modalities import Modality +from aind_data_schema_models.organizations import Organization +from aind_data_schema_models.units import FrequencyUnit +from pydantic import ValidationError +from pydantic_core import PydanticSerializationError + +from aind_data_schema.components.devices import ( + Calibration, + Camera, + CameraAssembly, + CameraTarget, + ChannelType, + DAQChannel, + Detector, + DetectorType, + Device, + Disc, + EphysAssembly, + EphysProbe, + Laser, + LaserAssembly, + Lens, + Manipulator, + NeuropixelsBasestation, + Olfactometer, + OlfactometerChannel, + Patch, +) +from aind_data_schema.core.instrument import Instrument + +daqs = [ + NeuropixelsBasestation( + name="Neuropixels basestation", + basestation_firmware_version="1", + bsc_firmware_version="2", + slot=0, + manufacturer=Organization.IMEC, + ports=[], + computer_name="foo", + channels=[ + DAQChannel( + channel_name="123", + device_name="Laser A", + channel_type="Analog Output", + ), + DAQChannel( + channel_name="321", + device_name="Probe A", + channel_type="Analog Output", + ), + DAQChannel( + channel_name="234", + device_name="Camera A", + channel_type="Digital Output", + ), + DAQChannel( + channel_name="2354", + device_name="Disc A", + channel_type="Digital Output", + ), + ], + ) +] + +ems = [ + EphysAssembly( + probes=[EphysProbe(probe_model="Neuropixels 1.0", name="Probe A")], + manipulator=Manipulator( + name="Probe manipulator", + manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, + serial_number="4321", + ), + name="Ephys_assemblyA", + ) +] + +lms = [ + LaserAssembly( + lasers=[ + Laser( + manufacturer=Organization.HAMAMATSU, + serial_number="1234", + name="Laser A", + wavelength=488, + ), + ], + manipulator=Manipulator( + name="Laser manipulator", + manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, + serial_number="1234", + ), + name="Laser_assembly", + collimator=Device(name="Collimator A"), + fiber=Patch( + name="Bundle Branching Fiber-optic Patch Cord", + manufacturer=Organization.DORIC, + model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", + core_diameter=200, + numerical_aperture=0.37, + ), + ) +] +cameras = [ + CameraAssembly( + name="cam", + camera_target="Face bottom", + lens=Lens(name="Camera lens", manufacturer=Organization.OTHER), + camera=Camera( + name="Camera A", + detector_type=DetectorType.CAMERA, + manufacturer=Organization.OTHER, + data_interface="USB", + computer_name="ASDF", + frame_rate=144, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=1, + sensor_height=1, + chroma="Color", + ), + ) +] +stick_microscopes = [ + CameraAssembly( + name="Assembly A", + camera=Camera( + name="Camera A", + detector_type=DetectorType.CAMERA, + manufacturer=Organization.OTHER, + data_interface="USB", + computer_name="ASDF", + frame_rate=144, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=1, + sensor_height=1, + chroma="Color", + ), + camera_target=CameraTarget.BRAIN_SURFACE, # NEEDS A VALUE + lens=Lens(name="Lens A", manufacturer=Organization.OTHER), + ) +] +light_sources = [ + Laser( + manufacturer=Organization.HAMAMATSU, + serial_number="1234", + name="Laser A", + wavelength=488, + ) +] +detectors = [ + Detector( + name="FLIR CMOS for Green Channel", + serial_number="21396991", + manufacturer=Organization.FLIR, + model="BFS-U3-20S40M", + detector_type=DetectorType.CAMERA, + data_interface="USB", + cooling="Air", + immersion="air", + bin_width=4, + bin_height=4, + bin_mode="Additive", + crop_width=200, + crop_height=200, + gain=2, + chroma="Monochrome", + bit_depth=16, + ) +] +patch_cords = [ + Patch( + name="Bundle Branching Fiber-optic Patch Cord", + manufacturer=Organization.DORIC, + model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", + core_diameter=200, + numerical_aperture=0.37, + ) +] +stimulus_devices = [ + Olfactometer( + name="Olfactometer", + manufacturer=Organization.CHAMPALIMAUD, + model="1234", + harp_device_type="1234", + serial_number="213456", + hardware_version="1", + is_clock_generator=False, + computer_name="W10XXX000", + channels=[ + OlfactometerChannel( + channel_index=0, + channel_type=ChannelType.CARRIER, + flow_capacity=100, + ), + OlfactometerChannel( + channel_index=1, + channel_type=ChannelType.ODOR, + flow_capacity=100, + ), + ], + ) +] +calibration = Calibration( + calibration_date=date(2020, 10, 10), + device_name="Laser A", + description="Laser power calibration", + input={"power percent": [10, 40, 80]}, + output={"power mW": [2, 6, 10]}, +) + + +class RigTests(unittest.TestCase): + """test rig schemas""" + + def test_constructors(self): + """always returns true""" + + with self.assertRaises(ValidationError): + Instrument() + + inst = Instrument( + instrument_id="123_EPHYS1-OPTO_20220101", + modification_date=date(2020, 10, 10), + modalities=[Modality.ECEPHYS, Modality.FIB], + components=[ + *daqs, + *cameras, + *stick_microscopes, + *light_sources, + *lms, + *ems, + *detectors, + *patch_cords, + *stimulus_devices, + ], + mouse_platform=Disc(name="Disc A", radius=1), + calibrations=[ + Calibration( + calibration_date=date(2020, 10, 10), + device_name="Laser A", + description="Laser power calibration", + input={"power percent": [10, 40, 80]}, + output={"power mW": [2, 6, 10]}, + ) + ], + ) + + assert rig is not None + + def test_validator(self): + """Test the rig file validators""" + neuropixels_base = NeuropixelsBasestation( + name="Basestation", + basestation_firmware_version="1", + bsc_firmware_version="2", + slot=0, + manufacturer=Organization.IMEC, + ports=[], + computer_name="foo", + channels=[ + DAQChannel( + channel_name="123", + device_name="Laser A", + channel_type="Analog Output", + ), + DAQChannel( + channel_name="321", + device_name="Probe A", + channel_type="Analog Output", + ), + DAQChannel( + channel_name="234", + device_name="Camera A", + channel_type="Digital Output", + ), + DAQChannel( + channel_name="2354", + device_name="Disc A", + channel_type="Digital Output", + ), + ], + ) + + # A Rig model with ECEPHYS in the modality list requires + # ephys_assemblies and stick microscopes + with self.assertRaises(ValidationError): + Instrument( + modalities=[ + Modality.ECEPHYS, + Modality.SLAP, + Modality.FIB, + Modality.BEHAVIOR_VIDEOS, + Modality.POPHYS, + Modality.BEHAVIOR, + ], + instrument_id="123_EPHYS1-OPTO_20220101", + modification_date=date(2020, 10, 10), + components=[ + neuropixels_base, + ], + calibrations=[ + Calibration( + calibration_date=date(2020, 10, 10), + device_name="Laser A", + description="Laser power calibration", + input={"power percent": [10, 40, 80]}, + output={"power mW": [2, 6, 10]}, + ) + ], + mouse_platform=Disc(name="Disc A", radius=1), + ) + + with self.assertRaises(ValidationError): + daqs = [ + NeuropixelsBasestation( + name="Basestation", + basestation_firmware_version="1", + bsc_firmware_version="2", + slot=0, + manufacturer=Organization.IMEC, + ports=[], + computer_name="foo", + channels=[ + DAQChannel( + channel_name="123", + device_name="Laser A", + channel_type="Analog Output", + ), + DAQChannel( + channel_name="321", + device_name="Probe A", + channel_type="Analog Output", + ), + DAQChannel( + channel_name="234", + device_name="Camera A", + channel_type="Digital Output", + ), + DAQChannel( + channel_name="2354", + device_name="Disc A", + channel_type="Digital Output", + ), + ], + ) + ] + + Instrument( + instrument_id="123_EPHYS1-OPTO_20220101", + modification_date=datetime.now(), + modalities=[Modality.ECEPHYS, Modality.FIB], + components=[*daqs], + ) + + with self.assertRaises(ValueError): + Instrument( + instrument_id="1234", + modification_date=date(2020, 10, 10), + modalities=[Modality.ECEPHYS, Modality.FIB], + components=[ + *daqs, + *cameras, + *stick_microscopes, + *light_sources, + *lms, + *ems, + *detectors, + *patch_cords, + *stimulus_devices, + ], + mouse_platform=Disc(name="Disc A", radius=1), + calibrations=[ + Calibration( + calibration_date=date(2020, 10, 10), + device_name="Laser A", + description="Laser power calibration", + input={"power percent": [10, 40, 80]}, + output={"power mW": [2, 6, 10]}, + ) + ], + ) + + # Tests that validators catch empty lists without KeyErrors + with self.assertRaises(ValidationError): + Instrument( + instrument_id="123_EPHYS1-OPTO_20220101", + modification_date=date(2020, 10, 10), + modalities=[Modality.ECEPHYS, Modality.FIB], + components=[], + mouse_platform=Disc(name="Disc A", radius=Decimal(1.0)), + calibrations=[], + ) + + # What is this testing? + with self.assertRaises(ValidationError): + Instrument( + instrument_id="123_EPHYS-OPTO_20200101", + modification_date=date(2020, 10, 10), + modalities=[Modality.ECEPHYS, Modality.FIB], + components=[ + *daqs, + *cameras, + *stick_microscopes, + *light_sources, + *lms, + *ems, + *detectors, + *patch_cords, + *stimulus_devices, + ], + mouse_platform=Disc(name="Disc A", radius=1), + calibrations=[ + Calibration( + calibration_date=date(2020, 10, 10), + device_name="Laser A", + description="Laser power calibration", + input={"power percent": [10, 40, 80]}, + output={"power mW": [2, 6, 10]}, + ) + ], + ) + + def test_instrument_id_validator(self): + """Tests that instrument_id validator works as expected""" + + with self.assertRaises(ValidationError): + Instrument( + instrument_id="123", + modification_date=date(2020, 10, 10), + modalities=[Modality.ECEPHYS, Modality.FIB], + components=[ + *daqs, + *cameras, + *stick_microscopes, + *light_sources, + *lms, + *ems, + *detectors, + *patch_cords, + *stimulus_devices, + ], + mouse_platform=Disc(name="Disc A", radius=1), + calibrations=[calibration], + ) + with self.assertRaises(ValidationError): + Instrument( + instrument_id="123_EPHYS-OPTO_2020-01-01", + modification_date=date(2020, 10, 10), + modalities=[Modality.ECEPHYS, Modality.FIB], + components=[ + *daqs, + *cameras, + *stick_microscopes, + *light_sources, + *lms, + *ems, + *detectors, + *patch_cords, + *stimulus_devices, + ], + mouse_platform=Disc(name="Disc A", radius=1), + calibrations=[calibration], + ) + + def test_serialize_modalities(self): + """Tests that modalities serializer can handle different types""" + expected_modalities = [{"name": "Extracellular electrophysiology", "abbreviation": "ecephys"}] + # Case 1: Modality is a class instance + instrument_instance_modality = Instrument.model_construct( + instrument_id="123_EPHYS1-OPTO_20220101", + modalities={Modality.ECEPHYS}, # Example with a valid Modality instance + ) + instrument_json = instrument_instance_modality.model_dump_json() + instrument_data = json.loads(instrument_json) + self.assertEqual(instrument_data["modalities"], expected_modalities) + + # Case 2: Modality is a dictionary when Rig is constructed from JSON + instrument_dict_modality = Instrument.model_construct(**instrument_data) + instrument_dict_json = instrument_dict_modality.model_dump_json() + instrument_dict_data = json.loads(instrument_dict_json) + self.assertEqual(instrument_dict_data["modalities"], expected_modalities) + + # Case 3: Modality is an unknown type + with self.assertRaises(PydanticSerializationError) as context: + instrument_unknown_modality = Instrument.model_construct(modalities={"UnknownModality"}) + + instrument_unknown_modality.model_dump_json() + self.assertIn("Error calling function `serialize_modalities`", str(context.exception)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 506ea3d96..7b593adbe 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -17,7 +17,6 @@ from aind_data_schema.components.devices import MousePlatform from aind_data_schema.core.acquisition import Acquisition from aind_data_schema.core.data_description import DataDescription, Funding -from aind_data_schema.core.instrument import Instrument from aind_data_schema.core.metadata import ExternalPlatforms, Metadata, MetadataStatus, create_metadata_json from aind_data_schema.core.procedures import ( IontophoresisInjection, @@ -27,7 +26,7 @@ ViralMaterial, ) from aind_data_schema.core.processing import PipelineProcess, Processing -from aind_data_schema.core.rig import Rig +from aind_data_schema.core.instrument import Instrument from aind_data_schema.core.session import Session from aind_data_schema.core.subject import BreedingInfo, Housing, Sex, Species, Subject @@ -244,7 +243,7 @@ def test_validate_smartspim_metadata(self): subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), acquisition=Acquisition.model_construct(), - instrument=Instrument.model_construct(), + rig=Instrument.model_construct(), processing=Processing.model_construct(), ) self.assertIn("Injection is missing injection_materials.", str(context.exception)) @@ -258,8 +257,12 @@ def test_multi_modal_metadata(self): surgery1 = Surgery.model_construct(procedures=[nano_inj, ionto_inj]) mouse_platform = MousePlatform.model_construct(name="platform1") - rig = Rig.model_construct(rig_id="123_EPHYS1_20220101", mouse_platform=mouse_platform) - session = Session.model_construct(rig_id="123_EPHYS1_20220101", mouse_platform_name="platform1") + inst = Instrument.model_construct( + instrument_id="123_EPHYS1_20220101", + mouse_platform=mouse_platform, + modalities=[Modality.BEHAVIOR, Modality.SPIM], + ) + session = Session.model_construct(instrument_id="123_EPHYS1_20220101", mouse_platform_name="platform1") m = Metadata( name="ecephys_655019_2023-04-03_18-17-09", @@ -276,7 +279,6 @@ def test_multi_modal_metadata(self): acquisition=Acquisition.model_construct(), rig=rig, processing=Processing.model_construct(), - instrument=Instrument.model_construct(), ) self.assertIsNotNone(m) @@ -299,7 +301,7 @@ def test_validate_ecephys_metadata(self): modality=[Modality.ECEPHYS], ), procedures=Procedures.model_construct(subject_procedures=[surgery1]), - rig=Rig.model_construct(), + rig=Instrument.model_construct(), ) self.assertIn( "ECEPHYS metadata missing required file: subject", @@ -320,7 +322,7 @@ def test_validate_ecephys_metadata(self): ), subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), - rig=Rig.model_construct(), + rig=Instrument.model_construct(), processing=Processing.model_construct(), session=Session.model_construct(), ) @@ -332,8 +334,8 @@ def test_validate_underscore_modality(self): nano_inj = NanojectInjection.model_construct(injection_materials=[viral_material]) ionto_inj = IontophoresisInjection.model_construct(injection_materials=[viral_material]) mouse_platform = MousePlatform.model_construct(name="platform1") - rig = Rig.model_construct(rig_id="123_EPHYS2_20230101", mouse_platform=mouse_platform) - session = Session.model_construct(rig_id="123_EPHYS2_20230101", mouse_platform_name="platform1") + inst = Instrument.model_construct(instrument_id="123_EPHYS2_20230101", mouse_platform=mouse_platform) + session = Session.model_construct(instrument_id="123_EPHYS2_20230101", mouse_platform_name="platform1") # Tests missing metadata surgery1 = Surgery.model_construct(procedures=[nano_inj, ionto_inj]) @@ -353,11 +355,11 @@ def test_validate_underscore_modality(self): ) self.assertIsNotNone(m) - def test_validate_rig_session_compatibility(self): + def test_validate_instrument_session_compatibility(self): """Tests that rig/session compatibility validator works as expected""" mouse_platform = MousePlatform.model_construct(name="platform1") - rig = Rig.model_construct(rig_id="123_EPHYS1_20220101", mouse_platform=mouse_platform) - session = Session.model_construct(rig_id="123_EPHYS2_20230101", mouse_platform_name="platform2") + inst = Instrument.model_construct(instrument_id="123_EPHYS1_20220101", mouse_platform=mouse_platform) + session = Session.model_construct(instrument_id="123_EPHYS2_20230101", mouse_platform_name="platform2") with self.assertRaises(ValidationError) as context: Metadata( name="ecephys_655019_2023-04-03_18-17-09", @@ -375,7 +377,7 @@ def test_validate_rig_session_compatibility(self): session=session, ) self.assertIn( - "Rig ID in session 123_EPHYS2_20230101 does not match the rig's 123_EPHYS1_20220101.", + "Insturment ID in session 123_EPHYS2_20230101 does not match the rig's 123_EPHYS1_20220101.", str(context.exception), ) @@ -406,7 +408,6 @@ def test_create_from_core_jsons(self): "rig": None, "processing": self.processing_json, "acquisition": None, - "instrument": None, "quality_control": None, } expected_md = Metadata( @@ -473,7 +474,6 @@ def test_create_from_core_jsons_invalid(self, mock_warning: MagicMock): "rig": None, "processing": self.processing_json, "acquisition": None, - "instrument": None, "quality_control": None, } # there are some userwarnings when creating Subject from json @@ -510,7 +510,6 @@ def test_create_from_core_jsons_corrupt(self, mock_is_dict_corrupt: MagicMock, m "rig": None, "processing": self.processing_json, "acquisition": None, - "instrument": None, "quality_control": None, } # there are some userwarnings when creating Subject from json diff --git a/tests/test_rig.py b/tests/test_rig.py deleted file mode 100644 index 18f6a8e83..000000000 --- a/tests/test_rig.py +++ /dev/null @@ -1,865 +0,0 @@ -""" test Rig """ - -import json -import unittest -from datetime import date, datetime - -from aind_data_schema_models.modalities import Modality -from aind_data_schema_models.organizations import Organization -from aind_data_schema_models.units import FrequencyUnit -from pydantic import ValidationError -from pydantic_core import PydanticSerializationError - -from aind_data_schema.components.devices import ( - Calibration, - Camera, - CameraAssembly, - CameraTarget, - ChannelType, - DAQChannel, - Detector, - DetectorType, - Device, - Disc, - EphysAssembly, - EphysProbe, - Laser, - LaserAssembly, - Lens, - Manipulator, - NeuropixelsBasestation, - Olfactometer, - OlfactometerChannel, - Patch, -) -from aind_data_schema.core.rig import Rig - - -class RigTests(unittest.TestCase): - """test rig schemas""" - - def test_constructors(self): - """always returns true""" - - with self.assertRaises(ValidationError): - Rig() - - daqs = [ - NeuropixelsBasestation( - name="Neuropixels basestation", - basestation_firmware_version="1", - bsc_firmware_version="2", - slot=0, - manufacturer=Organization.IMEC, - ports=[], - computer_name="foo", - channels=[ - DAQChannel( - channel_name="123", - device_name="Laser A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="321", - device_name="Probe A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="234", - device_name="Camera A", - channel_type="Digital Output", - ), - DAQChannel( - channel_name="2354", - device_name="Disc A", - channel_type="Digital Output", - ), - ], - ) - ] - - ems = [ - EphysAssembly( - probes=[EphysProbe(probe_model="Neuropixels 1.0", name="Probe A")], - manipulator=Manipulator( - name="Probe manipulator", - manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, - serial_number="4321", - ), - name="Ephys_assemblyA", - ) - ] - - lms = [ - LaserAssembly( - lasers=[ - Laser( - manufacturer=Organization.HAMAMATSU, - serial_number="1234", - name="Laser A", - wavelength=488, - ), - ], - manipulator=Manipulator( - name="Laser manipulator", - manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, - serial_number="1234", - ), - name="Laser_assembly", - collimator=Device(name="Collimator A", device_type="Collimator"), - fiber=Patch( - name="Bundle Branching Fiber-optic Patch Cord", - manufacturer=Organization.DORIC, - model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - core_diameter=200, - numerical_aperture=0.37, - ), - ) - ] - - rig = Rig( - rig_id="123_EPHYS1-OPTO_20220101", - modification_date=date(2020, 10, 10), - modalities=[Modality.ECEPHYS, Modality.FIB], - daqs=daqs, - cameras=[ - CameraAssembly( - name="cam", - camera_target="Face bottom", - lens=Lens(name="Camera lens", manufacturer=Organization.OTHER), - camera=Camera( - name="Camera A", - detector_type=DetectorType.CAMERA, - manufacturer=Organization.OTHER, - data_interface="USB", - computer_name="ASDF", - frame_rate=144, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=1, - sensor_height=1, - chroma="Color", - ), - ) - ], - stick_microscopes=[ - CameraAssembly( - name="Assembly A", - camera=Camera( - name="Camera A", - detector_type=DetectorType.CAMERA, - manufacturer=Organization.OTHER, - data_interface="USB", - computer_name="ASDF", - frame_rate=144, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=1, - sensor_height=1, - chroma="Color", - ), - camera_target=CameraTarget.BRAIN_SURFACE, # NEEDS A VALUE - lens=Lens(name="Lens A", manufacturer=Organization.OTHER), - ) - ], - light_sources=[ - Laser( - manufacturer=Organization.HAMAMATSU, - serial_number="1234", - name="Laser A", - wavelength=488, - ) - ], - laser_assemblies=lms, - ephys_assemblies=ems, - detectors=[ - Detector( - name="FLIR CMOS for Green Channel", - serial_number="21396991", - manufacturer=Organization.FLIR, - model="BFS-U3-20S40M", - detector_type=DetectorType.CAMERA, - data_interface="USB", - cooling="Air", - immersion="air", - bin_width=4, - bin_height=4, - bin_mode="Additive", - crop_width=200, - crop_height=200, - gain=2, - chroma="Monochrome", - bit_depth=16, - ) - ], - patch_cords=[ - Patch( - name="Bundle Branching Fiber-optic Patch Cord", - manufacturer=Organization.DORIC, - model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - core_diameter=200, - numerical_aperture=0.37, - ) - ], - stimulus_devices=[ - Olfactometer( - name="Olfactometer", - manufacturer=Organization.CHAMPALIMAUD, - model="1234", - serial_number="213456", - hardware_version="1", - is_clock_generator=False, - computer_name="W10XXX000", - channels=[ - OlfactometerChannel( - channel_index=0, - channel_type=ChannelType.CARRIER, - flow_capacity=100, - ), - OlfactometerChannel( - channel_index=1, - channel_type=ChannelType.ODOR, - flow_capacity=100, - ), - ], - ) - ], - mouse_platform=Disc(name="Disc A", radius=1), - calibrations=[ - Calibration( - calibration_date=date(2020, 10, 10), - device_name="Laser A", - description="Laser power calibration", - input={"power percent": [10, 40, 80]}, - output={"power mW": [2, 6, 10]}, - ) - ], - ) - - assert rig is not None - - def test_validator(self): - """Test the rig file validators""" - - # A Rig model with ECEPHYS in the modality list requires - # ephys_assemblies and stick microscopes - with self.assertRaises(ValidationError): - Rig( - modalities=[ - Modality.ECEPHYS, - Modality.SLAP, - Modality.FIB, - Modality.BEHAVIOR_VIDEOS, - Modality.POPHYS, - Modality.BEHAVIOR, - ], - rig_id="123_EPHYS1-OPTO_20220101", - modification_date=date(2020, 10, 10), - daqs=[ - NeuropixelsBasestation( - name="Basestation", - basestation_firmware_version="1", - bsc_firmware_version="2", - slot=0, - manufacturer=Organization.IMEC, - ports=[], - computer_name="foo", - channels=[ - DAQChannel( - channel_name="123", - device_name="Laser A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="321", - device_name="Probe A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="234", - device_name="Camera A", - channel_type="Digital Output", - ), - DAQChannel( - channel_name="2354", - device_name="Disc A", - channel_type="Digital Output", - ), - ], - ) - ], - calibrations=[ - Calibration( - calibration_date=date(2020, 10, 10), - device_name="Laser A", - description="Laser power calibration", - input={"power percent": [10, 40, 80]}, - output={"power mW": [2, 6, 10]}, - ) - ], - mouse_platform=Disc(name="Disc A", radius=1), - ) - - with self.assertRaises(ValidationError): - daqs = [ - NeuropixelsBasestation( - name="Basestation", - basestation_firmware_version="1", - bsc_firmware_version="2", - slot=0, - manufacturer=Organization.IMEC, - ports=[], - computer_name="foo", - channels=[ - DAQChannel( - channel_name="123", - device_name="Laser A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="321", - device_name="Probe A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="234", - device_name="Camera A", - channel_type="Digital Output", - ), - DAQChannel( - channel_name="2354", - device_name="Disc A", - channel_type="Digital Output", - ), - ], - ) - ] - - Rig( - rig_id="123_EPHYS1-OPTO_20220101", - modification_date=datetime.now(), - modalities=[Modality.ECEPHYS, Modality.FIB], - daqs=daqs, - ) - - with self.assertRaises(ValueError): - ems = [ - EphysAssembly( - probes=[EphysProbe(probe_model="Neuropixels 1.0", name="Probe A")], - manipulator=Manipulator( - name="Probe manipulator", - manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, - serial_number="4321", - ), - name="Ephys_assemblyA", - ) - ] - - lms = [ - LaserAssembly( - lasers=[ - Laser( - manufacturer=Organization.HAMAMATSU, - serial_number="1234", - name="Laser A", - wavelength=488, - ), - ], - manipulator=Manipulator( - name="Laser manipulator", - manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, - serial_number="1234", - ), - name="Laser_assembly", - collimator=Device(name="Collimator B", device_type="Collimator"), - fiber=Patch( - name="Bundle Branching Fiber-optic Patch Cord", - manufacturer=Organization.DORIC, - model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - core_diameter=200, - numerical_aperture=0.37, - ), - ) - ] - - Rig( - rig_id="1234", - modification_date=date(2020, 10, 10), - modalities=[Modality.ECEPHYS, Modality.FIB], - daqs=daqs, - cameras=[ - CameraAssembly( - name="cam", - camera_target=CameraTarget.OTHER, - lens=Lens(name="Camera lens", manufacturer=Organization.OTHER), - camera=Camera( - name="Camera A", - detector_type=DetectorType.CAMERA, - manufacturer=Organization.OTHER, - data_interface="USB", - computer_name="ASDF", - frame_rate=144, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=1, - sensor_height=1, - chroma="Color", - ), - ) - ], - stick_microscopes=[ - CameraAssembly( - name="Assembly A", - camera=Camera( - name="Camera A", - detector_type=DetectorType.CAMERA, - manufacturer=Organization.OTHER, - data_interface="USB", - computer_name="ASDF", - frame_rate=144, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=1, - sensor_height=1, - chroma="Color", - ), - camera_target=CameraTarget.OTHER, - lens=Lens(name="Lens A", manufacturer=Organization.OTHER), - ) - ], - light_sources=[ - Laser( - manufacturer=Organization.HAMAMATSU, - serial_number="1234", - name="Laser A", - wavelength=488, - ) - ], - laser_assemblies=lms, - ephys_assemblies=ems, - detectors=[ - Detector( - name="FLIR CMOS for Green Channel", - serial_number="21396991", - manufacturer=Organization.FLIR, - model="BFS-U3-20S40M", - detector_type=DetectorType.CAMERA, - data_interface="USB", - cooling="Air", - immersion="air", - bin_width=4, - bin_height=4, - bin_mode="Additive", - crop_width=200, - crop_height=200, - gain=2, - chroma="Monochrome", - bit_depth=16, - ) - ], - patch_cords=[ - Patch( - name="Bundle Branching Fiber-optic Patch Cord", - manufacturer=Organization.DORIC, - model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - core_diameter=200, - numerical_aperture=0.37, - ) - ], - stimulus_devices=[ - Olfactometer( - name="Olfactometer", - manufacturer=Organization.CHAMPALIMAUD, - model="1234", - serial_number="213456", - hardware_version="1", - is_clock_generator=False, - computer_name="W10XXX000", - channels=[ - OlfactometerChannel( - channel_index=0, - channel_type=ChannelType.CARRIER, - flow_capacity=100, - ), - OlfactometerChannel( - channel_index=1, - channel_type=ChannelType.ODOR, - flow_capacity=100, - ), - ], - ) - ], - mouse_platform=Disc(name="Disc A", radius=1), - calibrations=[ - Calibration( - calibration_date=date(2020, 10, 10), - device_name="Laser A", - description="Laser power calibration", - input={"power percent": [10, 40, 80]}, - output={"power mW": [2, 6, 10]}, - ) - ], - ) - - # Tests that validators catch empty lists without KeyErrors - with self.assertRaises(ValidationError): - Rig( - rig_id="123_EPHYS1-OPTO_20220101", - modification_date=date(2020, 10, 10), - modalities=[Modality.ECEPHYS, Modality.FIB], - daqs=daqs, - cameras=[], - stick_microscopes=[], - light_sources=[], - laser_assemblies=lms, - ephys_assemblies=ems, - detectors=[], - patch_cords=[], - stimulus_devices=[], - mouse_platform=Disc(name="Disc A", radius=1), - calibrations=[], - ) - - with self.assertRaises(ValidationError): - camera = CameraAssembly( - name="cam", - camera_target=CameraTarget.OTHER, - lens=Lens(name="Camera lens", manufacturer=Organization.OTHER), - camera=Camera( - name="Camera A", - detector_type=DetectorType.CAMERA, - manufacturer=Organization.OTHER, - data_interface="USB", - computer_name="ASDF", - frame_rate=144, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=1, - sensor_height=1, - chroma="Color", - ), - ) - Rig( - rig_id="123_EPHYS-OPTO_20200101", - modification_date=date(2020, 10, 10), - modalities=[Modality.ECEPHYS, Modality.FIB], - daqs=daqs, - cameras=[camera], - stick_microscopes=[ - CameraAssembly( - name="Assembly A", - camera=Camera( - name="Camera A", - detector_type=DetectorType.CAMERA, - manufacturer=Organization.OTHER, - data_interface="USB", - computer_name="ASDF", - frame_rate=144, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=1, - sensor_height=1, - chroma="Color", - ), - camera_target=CameraTarget.BRAIN_SURFACE, # NEEDS A VALUE - lens=Lens(name="Lens A", manufacturer=Organization.OTHER), - ) - ], - light_sources=[ - Laser( - manufacturer=Organization.HAMAMATSU, - serial_number="1234", - name="Laser A", - wavelength=488, - ) - ], - laser_assemblies=lms, - ephys_assemblies=ems, - detectors=[ - Detector( - name="FLIR CMOS for Green Channel", - serial_number="21396991", - manufacturer=Organization.FLIR, - model="BFS-U3-20S40M", - detector_type=DetectorType.CAMERA, - data_interface="USB", - cooling="Air", - immersion="air", - bin_width=4, - bin_height=4, - bin_mode="Additive", - crop_width=200, - crop_height=200, - gain=2, - chroma="Monochrome", - bit_depth=16, - ) - ], - patch_cords=[ - Patch( - name="Bundle Branching Fiber-optic Patch Cord", - manufacturer=Organization.DORIC, - model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - core_diameter=200, - numerical_aperture=0.37, - ) - ], - stimulus_devices=[ - Olfactometer( - name="Olfactometer", - manufacturer=Organization.CHAMPALIMAUD, - model="1234", - serial_number="213456", - hardware_version="1", - is_clock_generator=False, - computer_name="W10XXX000", - channels=[ - OlfactometerChannel( - channel_index=0, - channel_type=ChannelType.CARRIER, - flow_capacity=100, - ), - ], - ) - ], - mouse_platform=Disc(name="Disc A", radius=1), - calibrations=[ - Calibration( - calibration_date=date(2020, 10, 10), - device_name="Laser A", - description="Laser power calibration", - input={"power percent": [10, 40, 80]}, - output={"power mW": [2, 6, 10]}, - ) - ], - ) - - def test_rig_id_validator(self): - """Tests that rig_id validator works as expected""" - daqs = [ - NeuropixelsBasestation( - name="Neuropixels basestation", - basestation_firmware_version="1", - bsc_firmware_version="2", - slot=0, - manufacturer=Organization.IMEC, - ports=[], - computer_name="foo", - channels=[ - DAQChannel( - channel_name="123", - device_name="Laser A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="321", - device_name="Probe A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="234", - device_name="Camera A", - channel_type="Digital Output", - ), - DAQChannel( - channel_name="2354", - device_name="Disc A", - channel_type="Digital Output", - ), - ], - ) - ] - - ems = [ - EphysAssembly( - probes=[EphysProbe(probe_model="Neuropixels 1.0", name="Probe A")], - manipulator=Manipulator( - name="Probe manipulator", - manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, - serial_number="4321", - ), - name="Ephys_assemblyA", - ) - ] - - lms = [ - LaserAssembly( - lasers=[ - Laser( - manufacturer=Organization.HAMAMATSU, - serial_number="1234", - name="Laser A", - wavelength=488, - ), - ], - manipulator=Manipulator( - name="Laser manipulator", - manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, - serial_number="1234", - ), - name="Laser_assembly", - collimator=Device(name="Collimator A", device_type="Collimator"), - fiber=Patch( - name="Bundle Branching Fiber-optic Patch Cord", - manufacturer=Organization.DORIC, - model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - core_diameter=200, - numerical_aperture=0.37, - ), - ) - ] - camera = CameraAssembly( - name="cam", - camera_target="Face bottom", - lens=Lens(name="Camera lens", manufacturer=Organization.OTHER), - camera=Camera( - name="Camera A", - detector_type=DetectorType.CAMERA, - manufacturer=Organization.OTHER, - data_interface="USB", - computer_name="ASDF", - frame_rate=144, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=1, - sensor_height=1, - chroma="Color", - ), - ) - - stick_microscope = CameraAssembly( - name="Assembly A", - camera=Camera( - name="Camera A", - detector_type=DetectorType.CAMERA, - manufacturer=Organization.OTHER, - data_interface="USB", - computer_name="ASDF", - frame_rate=144, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=1, - sensor_height=1, - chroma="Color", - ), - camera_target=CameraTarget.BRAIN_SURFACE, # NEEDS A VALUE - lens=Lens(name="Lens A", manufacturer=Organization.OTHER), - ) - light_source = Laser( - manufacturer=Organization.HAMAMATSU, - serial_number="1234", - name="Laser A", - wavelength=488, - ) - detector = Detector( - name="FLIR CMOS for Green Channel", - serial_number="21396991", - manufacturer=Organization.FLIR, - model="BFS-U3-20S40M", - detector_type=DetectorType.CAMERA, - data_interface="USB", - cooling="Air", - immersion="air", - bin_width=4, - bin_height=4, - bin_mode="Additive", - crop_width=200, - crop_height=200, - gain=2, - chroma="Monochrome", - bit_depth=16, - ) - patch_cord = Patch( - name="Bundle Branching Fiber-optic Patch Cord", - manufacturer=Organization.DORIC, - model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - core_diameter=200, - numerical_aperture=0.37, - ) - stimulus_device = Olfactometer( - name="Olfactometer", - manufacturer=Organization.CHAMPALIMAUD, - model="1234", - serial_number="213456", - hardware_version="1", - is_clock_generator=False, - computer_name="W10XXX000", - channels=[ - OlfactometerChannel( - channel_index=0, - channel_type=ChannelType.CARRIER, - flow_capacity=100, - ), - OlfactometerChannel( - channel_index=1, - channel_type=ChannelType.ODOR, - flow_capacity=100, - ), - ], - ) - calibration = Calibration( - calibration_date=date(2020, 10, 10), - device_name="Laser A", - description="Laser power calibration", - input={"power percent": [10, 40, 80]}, - output={"power mW": [2, 6, 10]}, - ) - - with self.assertRaises(ValidationError): - Rig( - rig_id="123", - modification_date=date(2020, 10, 10), - modalities=[Modality.ECEPHYS, Modality.FIB], - components=[ - *daqs, - camera, - stick_microscope, - light_source, - *lms, - *ems, - detector, - patch_cord, - stimulus_device, - ], - mouse_platform=Disc(name="Disc A", radius=1), - calibrations=[calibration], - ) - with self.assertRaises(ValidationError): - Rig( - rig_id="123_EPHYS-OPTO_2020-01-01", - modification_date=date(2020, 10, 10), - modalities=[Modality.ECEPHYS, Modality.FIB], - components=[ - *daqs, - camera, - stick_microscope, - light_source, - *lms, - *ems, - detector, - patch_cord, - stimulus_device, - ], - mouse_platform=Disc(name="Disc A", radius=1), - calibrations=[calibration], - ) - - def test_serialize_modalities(self): - """Tests that modalities serializer can handle different types""" - expected_modalities = [{"name": "Extracellular electrophysiology", "abbreviation": "ecephys"}] - # Case 1: Modality is a class instance - rig_instance_modality = Rig.model_construct( - rig_id="123_EPHYS1-OPTO_20220101", modalities={Modality.ECEPHYS} # Example with a valid Modality instance - ) - rig_json = rig_instance_modality.model_dump_json() - rig_data = json.loads(rig_json) - self.assertEqual(rig_data["modalities"], expected_modalities) - - # Case 2: Modality is a dictionary when Rig is constructed from JSON - rig_dict_modality = Rig.model_construct(**rig_data) - rig_dict_json = rig_dict_modality.model_dump_json() - rig_dict_data = json.loads(rig_dict_json) - self.assertEqual(rig_dict_data["modalities"], expected_modalities) - - # Case 3: Modality is an unknown type - with self.assertRaises(PydanticSerializationError) as context: - rig_unknown_modality = Rig.model_construct(modalities={"UnknownModality"}) - - rig_unknown_modality.model_dump_json() - self.assertIn("Error calling function `serialize_modalities`", str(context.exception)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_session.py b/tests/test_session.py index 7a0547941..5870205a7 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -45,7 +45,7 @@ def test_constructors(self): session_end_time=datetime.now(), subject_id="1234", session_type="Test", - rig_id="1234", + instrument_id="1234", mouse_platform_name="Running wheel", active_mouse_platform=False, data_streams=[ @@ -126,7 +126,7 @@ def test_constructors(self): protocol_id=["doi_path"], iacuc_protocol="1234", session_type="3D MRI Volume", - rig_id="NA", + instrument_id="NA", animal_weight_prior=22.1, animal_weight_post=21.9, data_streams=[stream], From 9d812f221921aee79a7756fb7593f4eb7e285862 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Thu, 16 Jan 2025 10:52:43 -0800 Subject: [PATCH 04/34] chore: additional renaming --- docs/source/conf.py | 4 +-- examples/aibs_smartspim_rig.py | 2 +- examples/aind_smartspim_rig.py | 2 +- src/aind_data_schema/core/instrument.py | 2 +- src/aind_data_schema/core/metadata.py | 4 ++- .../utils/compatibility_check.py | 32 ++++++++++--------- tests/test_inst_acq_compatibility.py | 8 +++-- tests/test_metadata.py | 12 +++---- 8 files changed, 35 insertions(+), 31 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 42a7ee173..ca6254c48 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -18,7 +18,6 @@ metadata, procedures, processing, - rig, session, subject, quality_control, @@ -31,7 +30,7 @@ metadata, procedures, processing, - rig, + instrument, session, subject, quality_control, @@ -88,7 +87,6 @@ "metadata.nd", "procedures", "processing", - "rig", "session", "subject", "quality_control", diff --git a/examples/aibs_smartspim_rig.py b/examples/aibs_smartspim_rig.py index 277146289..47a6a3a9f 100644 --- a/examples/aibs_smartspim_rig.py +++ b/examples/aibs_smartspim_rig.py @@ -17,7 +17,7 @@ ScanningStage, ) from aind_data_schema_models.modalities import Modality -from aind_data_schema.core.instrument import Com, Rig +from aind_data_schema.core.instrument import Com, Instrument objective = Objective( name="TLX Objective", diff --git a/examples/aind_smartspim_rig.py b/examples/aind_smartspim_rig.py index 846e63284..7be59327e 100644 --- a/examples/aind_smartspim_rig.py +++ b/examples/aind_smartspim_rig.py @@ -13,7 +13,7 @@ OpticalTable, ScanningStage, ) -from aind_data_schema.core.instrument import Com, Detector, Rig, Objective +from aind_data_schema.core.instrument import Com, Detector, Instrument, Objective from aind_data_schema_models.modalities import Modality objective_1 = Objective( diff --git a/src/aind_data_schema/core/instrument.py b/src/aind_data_schema/core/instrument.py index b25250923..633ae54d6 100644 --- a/src/aind_data_schema/core/instrument.py +++ b/src/aind_data_schema/core/instrument.py @@ -1,4 +1,4 @@ -"""Core Rig model""" +"""Core Instrument model""" from datetime import date from typing import List, Literal, Optional, Set, Union diff --git a/src/aind_data_schema/core/metadata.py b/src/aind_data_schema/core/metadata.py index a234c8f71..033c1bc84 100644 --- a/src/aind_data_schema/core/metadata.py +++ b/src/aind_data_schema/core/metadata.py @@ -119,7 +119,9 @@ class Metadata(DataCoreModel): default=None, title="Procedures", description="All procedures performed on a subject." ) session: Optional[Session] = Field(default=None, title="Session", description="Description of a session.") - rig: Optional[Rig] = Field(default=None, title="Rig", description="Instrument.") + instrument: Optional[Instrument] = Field( + default=None, title="Instrument", description="Devices used to acquire data." + ) processing: Optional[Processing] = Field(default=None, title="Processing", description="All processes run on data.") acquisition: Optional[Acquisition] = Field( default=None, title="Acquisition", description="Imaging acquisition session" diff --git a/src/aind_data_schema/utils/compatibility_check.py b/src/aind_data_schema/utils/compatibility_check.py index 6ab1b1deb..476e2d125 100644 --- a/src/aind_data_schema/utils/compatibility_check.py +++ b/src/aind_data_schema/utils/compatibility_check.py @@ -2,16 +2,16 @@ from typing import Optional -from aind_data_schema.core.instrument import RewardDelivery, Rig +from aind_data_schema.core.instrument import RewardDelivery, Instrument from aind_data_schema.core.session import Session -class RigSessionCompatibility: +class InstrumentSessionCompatibility: """Class of methods to check compatibility between rig and session""" - def __init__(self, rig: Rig, session: Session) -> None: + def __init__(self, instrument: Instrument, session: Session) -> None: """Initiate RigSessionCompatibility class""" - self.instrument = rig + self.inst = instrument self.session = session def _compare_instrument_id(self) -> Optional[ValueError]: @@ -36,7 +36,7 @@ def _compare_daq_names(self) -> Optional[ValueError]: session_daqs = [ daq for stream in getattr(self.session, "data_streams", []) for daq in getattr(stream, "daq_names", []) ] - instrument_daqs = [getattr(daq, "name", None) for daq in getattr(self.rig, "daqs", [])] + instrument_daqs = [getattr(daq, "name", None) for daq in getattr(self.inst, "daqs", [])] if not set(session_daqs).issubset(set(instrument_daqs)): return ValueError( f"daq names in session do not match daq names in inst. " @@ -52,7 +52,7 @@ def _compare_camera_names(self) -> Optional[ValueError]: ] instrument_cameras = [ name - for camera_device in getattr(self.rig, "cameras", []) + for camera_device in getattr(self.inst, "cameras", []) for name in (camera_device.camera.name, camera_device.name) ] if not set(session_cameras).issubset(set(instrument_cameras)): @@ -69,7 +69,7 @@ def _compare_light_sources(self) -> Optional[ValueError]: for light_source in getattr(stream, "light_sources", []) ] instrument_light_sources = [ - getattr(light_source, "name", None) for light_source in getattr(self.rig, "light_sources", []) + getattr(light_source, "name", None) for light_source in getattr(self.inst, "light_sources", []) ] if not set(session_light_sources).issubset(set(instrument_light_sources)): return ValueError( @@ -85,7 +85,7 @@ def _compare_ephys_assemblies(self) -> Optional[ValueError]: for ephys_module in getattr(stream, "ephys_modules") ] instrument_ephys_assemblies = [ - ephys_assembly.name for ephys_assembly in getattr(self.rig, "ephys_assemblies", []) + ephys_assembly.name for ephys_assembly in getattr(self.inst, "ephys_assemblies", []) ] if not set(session_ephys_assemblies).issubset(set(instrument_ephys_assemblies)): return ValueError( @@ -103,7 +103,7 @@ def _compare_stick_microscopes(self) -> Optional[ValueError]: ] instrument_stick_microscopes = [ name - for camera_device in getattr(self.rig, "stick_microscopes", []) + for camera_device in getattr(self.inst, "stick_microscopes", []) for name in (camera_device.camera.name, camera_device.name) ] if not set(session_stick_microscopes).issubset(set(instrument_stick_microscopes)): @@ -121,7 +121,7 @@ def _compare_manipulator_modules(self) -> Optional[ValueError]: for manipulator_module in getattr(stream, "manipulator_modules", []) ] instrument_manipulator_modules = [ - laser_assembly.name for laser_assembly in getattr(self.rig, "laser_assemblies", []) + laser_assembly.name for laser_assembly in getattr(self.inst, "laser_assemblies", []) ] if not set(session_manipulator_modules).issubset(set(instrument_manipulator_modules)): return ValueError( @@ -137,7 +137,7 @@ def _compare_detectors(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for detector in getattr(stream, "detectors", []) ] - instrument_detectors = [detector.name for detector in getattr(self.rig, "detectors", [])] + instrument_detectors = [detector.name for detector in getattr(self.inst, "detectors", [])] if not set(session_detectors).issubset(set(instrument_detectors)): return ValueError( f"detector names in session do not match detector names in inst. " @@ -151,7 +151,7 @@ def _compare_patch_cords(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for fiber_connection in getattr(stream, "fiber_connections", []) ] - instrument_patch_cords = [patch_cord.name for patch_cord in getattr(self.rig, "patch_cords", [])] + instrument_patch_cords = [patch_cord.name for patch_cord in getattr(self.inst, "patch_cords", [])] if not set(session_patch_cords).issubset(set(instrument_patch_cords)): return ValueError( f"patch cord names in session do not match patch cord names in inst. " @@ -165,7 +165,9 @@ def _compare_fiber_modules(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for fiber_module in getattr(stream, "fiber_modules", []) ] - instrument_fiber_modules = [fiber_assembly.name for fiber_assembly in getattr(self.rig, "fiber_assemblies", [])] + instrument_fiber_modules = [ + fiber_assembly.name for fiber_assembly in getattr(self.inst, "fiber_assemblies", []) + ] if not set(session_fiber_modules).issubset(set(instrument_fiber_modules)): return ValueError( f"fiber module names in session do not match fiber assembly names in inst. " @@ -181,11 +183,11 @@ def _compare_stimulus_devices(self) -> Optional[ValueError]: ] instrument_stimulus_devices = [ stimulus_device.name - for stimulus_device in getattr(self.rig, "stimulus_devices", []) + for stimulus_device in getattr(self.inst, "stimulus_devices", []) if not isinstance(stimulus_device, RewardDelivery) ] + [ reward_spout.name - for stimulus_device in getattr(self.rig, "stimulus_devices", []) + for stimulus_device in getattr(self.inst, "stimulus_devices", []) if isinstance(stimulus_device, RewardDelivery) for reward_spout in getattr(stimulus_device, "reward_spouts", []) ] diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index 0e3415f78..8536efe93 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -851,16 +851,18 @@ def test_run_compatibility_check(self): "Insturment ID in session 323_EPHYS2-RF_2023-04-24_01 does not match the rig's 323_EPHYS1_20231003." ) with self.assertRaises(ValueError) as context: - RigSessionCompatibility(rig=ephys_inst, session=ephys_session).run_compatibility_check() + RigSessionCompatibility(instrument=ephys_inst, session=ephys_session).run_compatibility_check() self.assertIn(expected_error, str(context.exception)) with self.assertRaises(ValueError): - RigSessionCompatibility(rig=self.ophys_inst, session=self.ophys_session).run_compatibility_check() + RigSessionCompatibility(instrument=self.ophys_inst, session=self.ophys_session).run_compatibility_check() def test_check_examples_compatibility(self): """Tests that examples are compatible""" # check that ephys session and rig are synced - example_ephys_check = RigSessionCompatibility(rig=self.example_ephys_inst, session=self.example_ephys_session) + example_ephys_check = RigSessionCompatibility( + instrument=self.example_ephys_inst, session=self.example_ephys_session + ) self.assertIsNone(example_ephys_check.run_compatibility_check()) diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 7b593adbe..b9802041f 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -243,7 +243,7 @@ def test_validate_smartspim_metadata(self): subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), acquisition=Acquisition.model_construct(), - rig=Instrument.model_construct(), + instrument=Instrument.model_construct(), processing=Processing.model_construct(), ) self.assertIn("Injection is missing injection_materials.", str(context.exception)) @@ -277,7 +277,7 @@ def test_multi_modal_metadata(self): session=session, # SPIM excludes session, but BEHAVIOR requires it procedures=Procedures.model_construct(subject_procedures=[surgery1]), acquisition=Acquisition.model_construct(), - rig=rig, + instrument=rig, processing=Processing.model_construct(), ) self.assertIsNotNone(m) @@ -301,7 +301,7 @@ def test_validate_ecephys_metadata(self): modality=[Modality.ECEPHYS], ), procedures=Procedures.model_construct(subject_procedures=[surgery1]), - rig=Instrument.model_construct(), + instrument=Instrument.model_construct(), ) self.assertIn( "ECEPHYS metadata missing required file: subject", @@ -322,7 +322,7 @@ def test_validate_ecephys_metadata(self): ), subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), - rig=Instrument.model_construct(), + instrument=Instrument.model_construct(), processing=Processing.model_construct(), session=Session.model_construct(), ) @@ -350,7 +350,7 @@ def test_validate_underscore_modality(self): ), subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery1]), - rig=rig, + instrument=rig, session=session, ) self.assertIsNotNone(m) @@ -372,7 +372,7 @@ def test_validate_instrument_session_compatibility(self): ), subject=Subject.model_construct(), procedures=Procedures.model_construct(), - rig=rig, + instrument=rig, processing=Processing.model_construct(), session=session, ) From 9da2d095e49e1d393bab92a4b379aa752a550ec9 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Thu, 16 Jan 2025 12:55:59 -0800 Subject: [PATCH 05/34] chore: more renaming and fixes --- src/aind_data_schema/core/acquisition.py | 2 +- src/aind_data_schema/core/data_description.py | 2 +- src/aind_data_schema/core/instrument.py | 5 + src/aind_data_schema/core/metadata.py | 6 +- src/aind_data_schema/core/session.py | 14 +- .../utils/compatibility_check.py | 10 +- tests/test_inst_acq_compatibility.py | 569 +++++++++--------- tests/test_metadata.py | 31 +- 8 files changed, 316 insertions(+), 323 deletions(-) diff --git a/src/aind_data_schema/core/acquisition.py b/src/aind_data_schema/core/acquisition.py index 7f5e39b61..083ddab54 100644 --- a/src/aind_data_schema/core/acquisition.py +++ b/src/aind_data_schema/core/acquisition.py @@ -61,7 +61,7 @@ class Acquisition(DataCoreModel): description="List of calibration measurements taken prior to acquisition.", ) maintenance: List[Maintenance] = Field( - default=[], title="Maintenance", description="List of maintenance on rig prior to acquisition." + default=[], title="Maintenance", description="List of maintenance on instrument prior to acquisition." ) session_start_time: AwareDatetimeWithDefault = Field(..., title="Session start time") session_end_time: AwareDatetimeWithDefault = Field(..., title="Session end time") diff --git a/src/aind_data_schema/core/data_description.py b/src/aind_data_schema/core/data_description.py index 77dbf76c3..6b67b6962 100644 --- a/src/aind_data_schema/core/data_description.py +++ b/src/aind_data_schema/core/data_description.py @@ -254,7 +254,7 @@ def get_or_default(field_name: str) -> Any: class RawDataDescription(DataDescription): - """A logical collection of data files as acquired from a rig or instrument""" + """A logical collection of data files as acquired from an instrument""" data_level: Literal[DataLevel.RAW] = Field( default=DataLevel.RAW, description="level of processing that data has undergone", title="Data Level" diff --git a/src/aind_data_schema/core/instrument.py b/src/aind_data_schema/core/instrument.py index 633ae54d6..0ae89c4f9 100644 --- a/src/aind_data_schema/core/instrument.py +++ b/src/aind_data_schema/core/instrument.py @@ -206,6 +206,11 @@ def validate_modalities(cls, value): Raises: ValueError: If a required device type is missing for any modality. """ + + # Return if there are no modalities listed, this is largely for testing + if len(value.modalities) == 0: + return value + # Define the mapping of modalities to their required device types # The list of list pattern is used to allow for multiple options within a group, so e.g. # FIB requires a light (one of the options) plus a detector and a patch cord diff --git a/src/aind_data_schema/core/metadata.py b/src/aind_data_schema/core/metadata.py index 033c1bc84..1a4ff453a 100644 --- a/src/aind_data_schema/core/metadata.py +++ b/src/aind_data_schema/core/metadata.py @@ -37,7 +37,7 @@ "data_description", "procedures", "session", - "rig", + "instrument", "processing", "acquisition", "quality_control", @@ -290,8 +290,8 @@ def validate_ecephys_metadata(self): @model_validator(mode="after") def validate_instrument_session_compatibility(self): """Validator for metadata""" - if self.rig and self.session: - check = RigSessionCompatibility(self.rig, self.session) + if self.instrument and self.session: + check = InstrumentSessionCompatibility(self.instrument, self.session) check.run_compatibility_check() return self diff --git a/src/aind_data_schema/core/session.py b/src/aind_data_schema/core/session.py index 200261fb6..8b0acdbbf 100644 --- a/src/aind_data_schema/core/session.py +++ b/src/aind_data_schema/core/session.py @@ -203,7 +203,7 @@ class DomeModule(DataModel): rotation_angle: Optional[Decimal] = Field(default=None, title="Rotation Angle (deg)") coordinate_transform: Optional[str] = Field( default=None, - title="Transform from local manipulator axes to rig", + title="Transform from local manipulator axes to instrument", description="Path to coordinate transform", ) calibration_date: Optional[datetime] = Field( @@ -247,7 +247,7 @@ class LaserConfig(DataModel): """Description of laser settings in a session""" device_type: Literal["Laser"] = "Laser" - name: str = Field(..., title="Name", description="Must match rig json") + name: str = Field(..., title="Name", description="Must match instrument json") wavelength: int = Field(..., title="Wavelength (nm)") wavelength_unit: SizeUnit = Field(default=SizeUnit.NM, title="Wavelength unit") excitation_power: Optional[Decimal] = Field(default=None, title="Excitation power (mW)") @@ -271,7 +271,7 @@ class RewardSolution(str, Enum): class RewardSpoutConfig(DataModel): """Reward spout session information""" - side: SpoutSide = Field(..., title="Spout side", description="Must match rig") + side: SpoutSide = Field(..., title="Spout side", description="Must match instrument") starting_position: RelativePosition = Field(..., title="Starting position") variable_position: bool = Field( ..., @@ -301,7 +301,7 @@ def validate_other(cls, value: Optional[str], info: ValidationInfo) -> Optional[ class SpeakerConfig(DataModel): """Description of auditory speaker configuration""" - name: str = Field(..., title="Name", description="Must match rig json") + name: str = Field(..., title="Name", description="Must match instrument json") volume: Optional[Decimal] = Field(default=None, title="Volume (dB)") volume_unit: Optional[SoundIntensityUnit] = Field(default=None, title="Volume unit") @@ -394,7 +394,7 @@ class Stream(DataModel): stick_microscopes: List[DomeModule] = Field( default=[], title="Stick microscopes", - description="Must match stick microscope assemblies in rig file", + description="Must match stick microscope assemblies in instrument file", ) manipulator_modules: List[ManipulatorModule] = Field(default=[], title="Manipulator modules") detectors: List[DetectorConfig] = Field(default=[], title="Detectors") @@ -560,12 +560,12 @@ class Session(DataCoreModel): calibrations: List[Calibration] = Field( default=[], title="Calibrations", - description="Calibrations of rig devices prior to session", + description="Calibrations of instrument devices prior to session", ) maintenance: List[Maintenance] = Field( default=[], title="Maintenance", - description="Maintenance of rig devices prior to session", + description="Maintenance of instrument devices prior to session", ) subject_id: str = Field(..., title="Subject ID") animal_weight_prior: Optional[Decimal] = Field( diff --git a/src/aind_data_schema/utils/compatibility_check.py b/src/aind_data_schema/utils/compatibility_check.py index 476e2d125..96218abb1 100644 --- a/src/aind_data_schema/utils/compatibility_check.py +++ b/src/aind_data_schema/utils/compatibility_check.py @@ -7,10 +7,10 @@ class InstrumentSessionCompatibility: - """Class of methods to check compatibility between rig and session""" + """Class of methods to check compatibility between instrument and session""" def __init__(self, instrument: Instrument, session: Session) -> None: - """Initiate RigSessionCompatibility class""" + """Initiate InstrumentSessionCompatibility class""" self.inst = instrument self.session = session @@ -18,7 +18,7 @@ def _compare_instrument_id(self) -> Optional[ValueError]: """Compares instrument_id""" if self.session.instrument_id != self.inst.instrument_id: return ValueError( - f"Insturment ID in session {self.session.instrument_id} does not match the rig's {self.inst.instrument_id}." + f"Insturment ID in session {self.session.instrument_id} does not match the instrument's {self.inst.instrument_id}." ) else: return None @@ -28,7 +28,7 @@ def _compare_mouse_platform_name(self) -> Optional[ValueError]: if self.session.mouse_platform_name != self.inst.mouse_platform.name: return ValueError( f"Mouse platform name in session {self.session.mouse_platform_name} " - f"does not match the rig's {self.inst.mouse_platform.name}" + f"does not match the instrument's {self.inst.mouse_platform.name}" ) def _compare_daq_names(self) -> Optional[ValueError]: @@ -199,7 +199,7 @@ def _compare_stimulus_devices(self) -> Optional[ValueError]: ) def run_compatibility_check(self) -> None: - """Runs compatibility check.Creates a dictionary of fields and whether it matches in rig and session.""" + """Runs compatibility check.Creates a dictionary of fields and whether it matches in instrument and session.""" comparisons = [ self._compare_instrument_id(), self._compare_mouse_platform_name(), diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index 8536efe93..92d777786 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -53,7 +53,7 @@ from aind_data_schema_models.brain_atlas import CCFStructure EXAMPLES_DIR = Path(__file__).parents[1] / "examples" -EPHYS_RIG_JSON = EXAMPLES_DIR / "ephys_inst.json" +EPHYS_INST_JSON = EXAMPLES_DIR / "ephys_inst.json" EPHYS_SESSION_JSON = EXAMPLES_DIR / "ephys_session.json" behavior_computer = "W10DT72941" @@ -467,8 +467,8 @@ ) -class TestRigSessionCompatibility(unittest.TestCase): - """Tests RigSessionCompatibility class""" +class TestInstrumentSessionCompatibility(unittest.TestCase): + """Tests InstrumentSessionCompatibility class""" @classmethod def setUpClass(cls) -> None: @@ -480,292 +480,305 @@ def read_json(filepath: Path) -> dict: contents = json.load(f) return contents - cls.example_ephys_inst = Instrument.model_validate_json(json.dumps(read_json(EPHYS_RIG_JSON))) - cls.example_ephys_session = Session.model_validate_json(json.dumps(read_json(EPHYS_SESSION_JSON))) - cls.ophys_instrument = r.Instrument( - instrument_id="428_FIP1_20231003", - modification_date=date(2023, 10, 3), - modalities=[Modality.FIB], - cameras=[ - d.CameraAssembly( - name="BehaviorVideography_FaceSide", - camera_target=d.CameraTarget.FACE_SIDE_LEFT, - camera=d.Camera( - name="Side face camera", - detector_type="Camera", - serial_number="TBD", - manufacturer=d.Organization.AILIPU, - model="ELP-USBFHD05MT-KL170IR", - notes="The light intensity sensor was removed; IR illumination is constantly on", - data_interface="USB", - computer_name="W10DTJK7N0M3", - frame_rate=120, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=640, - sensor_height=480, - chroma="Color", - cooling="Air", - bin_mode="Additive", - recording_software=d.Software(name="Bonsai", version="2.5"), - ), - lens=d.Lens( - name="Xenocam 1", - model="XC0922LENS", - serial_number="unknown", - manufacturer=d.Organization.OTHER, - max_aperture="f/1.4", - notes='Focal Length 9-22mm 1/3" IR F1.4', - ), - ), - d.CameraAssembly( - name="BehaviorVideography_FaceBottom", - camera_target=d.CameraTarget.FACE_BOTTOM, - camera=d.Camera( - name="Bottom face Camera", - detector_type="Camera", - serial_number="TBD", - manufacturer=d.Organization.AILIPU, - model="ELP-USBFHD05MT-KL170IR", - notes="The light intensity sensor was removed; IR illumination is constantly on", - data_interface="USB", - computer_name="W10DTJK7N0M3", - frame_rate=120, - frame_rate_unit=FrequencyUnit.HZ, - sensor_width=640, - sensor_height=480, - chroma="Color", - cooling="Air", - bin_mode="Additive", - recording_software=d.Software(name="Bonsai", version="2.5"), - ), - lens=d.Lens( - name="Xenocam 2", - model="XC0922LENS", - serial_number="unknown", - manufacturer=d.Organization.OTHER, - max_aperture="f/1.4", - notes='Focal Length 9-22mm 1/3" IR F1.4', - ), - ), - ], - patch_cords=[ - d.Patch( - name="Bundle Branching Fiber-optic Patch Cord", - manufacturer=d.Organization.DORIC, - model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - core_diameter=200, - numerical_aperture=0.37, - ) - ], - light_sources=[ - d.LightEmittingDiode( - name="470nm LED", - manufacturer=d.Organization.THORLABS, - model="M470F3", - wavelength=470, - ), - d.LightEmittingDiode( - name="415nm LED", - manufacturer=d.Organization.THORLABS, - model="M415F3", - wavelength=415, - ), - d.LightEmittingDiode( - name="565nm LED", - manufacturer=d.Organization.THORLABS, - model="M565F3", - wavelength=565, - ), - ], - detectors=[ - d.Detector( - name="Green CMOS", - serial_number="21396991", - manufacturer=d.Organization.FLIR, - model="BFS-U3-20S40M", + cameras = [ + d.CameraAssembly( + name="BehaviorVideography_FaceSide", + camera_target=d.CameraTarget.FACE_SIDE_LEFT, + camera=d.Camera( + name="Side face camera", detector_type="Camera", + serial_number="TBD", + manufacturer=d.Organization.AILIPU, + model="ELP-USBFHD05MT-KL170IR", + notes="The light intensity sensor was removed; IR illumination is constantly on", data_interface="USB", + computer_name="W10DTJK7N0M3", + frame_rate=120, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=640, + sensor_height=480, + chroma="Color", cooling="Air", - immersion="air", - bin_width=4, - bin_height=4, bin_mode="Additive", - crop_width=200, - crop_height=200, - gain=2, - chroma="Monochrome", - bit_depth=16, + recording_software=d.Software(name="Bonsai", version="2.5"), + ), + lens=d.Lens( + name="Xenocam 1", + model="XC0922LENS", + serial_number="unknown", + manufacturer=d.Organization.OTHER, + max_aperture="f/1.4", + notes='Focal Length 9-22mm 1/3" IR F1.4', ), - d.Detector( - name="Red CMOS", - serial_number="21396991", - manufacturer=d.Organization.FLIR, - model="BFS-U3-20S40M", + ), + d.CameraAssembly( + name="BehaviorVideography_FaceBottom", + camera_target=d.CameraTarget.FACE_BOTTOM, + camera=d.Camera( + name="Bottom face Camera", detector_type="Camera", + serial_number="TBD", + manufacturer=d.Organization.AILIPU, + model="ELP-USBFHD05MT-KL170IR", + notes="The light intensity sensor was removed; IR illumination is constantly on", data_interface="USB", + computer_name="W10DTJK7N0M3", + frame_rate=120, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=640, + sensor_height=480, + chroma="Color", cooling="Air", - immersion="air", - bin_width=4, - bin_height=4, bin_mode="Additive", - crop_width=200, - crop_height=200, - gain=2, - chroma="Monochrome", - bit_depth=16, - ), - ], - objectives=[ - d.Objective( - name="Objective", - serial_number="128022336", - manufacturer=d.Organization.NIKON, - model="CFI Plan Apochromat Lambda D 10x", - numerical_aperture=0.45, - magnification=10, - immersion="air", - ) - ], - filters=[ - d.Filter( - name="Green emission filter", - manufacturer=d.Organization.SEMROCK, - model="FF01-520/35-25", - filter_type="Band pass", - center_wavelength=520, - diameter=25, - ), - d.Filter( - name="Red emission filter", - manufacturer=d.Organization.SEMROCK, - model="FF01-600/37-25", - filter_type="Band pass", - center_wavelength=600, - diameter=25, - ), - d.Filter( - name="Emission Dichroic", - model="FF562-Di03-25x36", - manufacturer=d.Organization.SEMROCK, - filter_type="Dichroic", - height=25, - width=36, - cut_off_wavelength=562, + recording_software=d.Software(name="Bonsai", version="2.5"), ), - d.Filter( - name="dual-edge standard epi-fluorescence dichroic beamsplitter", - model="FF493/574-Di01-25x36", - manufacturer=d.Organization.SEMROCK, - notes="493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", - filter_type="Multiband", - width=36, - height=24, + lens=d.Lens( + name="Xenocam 2", + model="XC0922LENS", + serial_number="unknown", + manufacturer=d.Organization.OTHER, + max_aperture="f/1.4", + notes='Focal Length 9-22mm 1/3" IR F1.4', ), - d.Filter( - name="Excitation filter 410nm", - manufacturer=d.Organization.THORLABS, - model="FB410-10", - filter_type="Band pass", - diameter=25, - center_wavelength=410, - ), - d.Filter( - name="Excitation filter 470nm", - manufacturer=d.Organization.THORLABS, - model="FB470-10", - filter_type="Band pass", - center_wavelength=470, - diameter=25, - ), - d.Filter( - name="Excitation filter 560nm", - manufacturer=d.Organization.THORLABS, - model="FB560-10", - filter_type="Band pass", - diameter=25, - center_wavelength=560, - ), - d.Filter( - name="450 Dichroic Longpass Filter", - manufacturer=d.Organization.EDMUND_OPTICS, - model="#69-898", - filter_type="Dichroic", - cut_off_wavelength=450, - width=35.6, - height=25.2, - ), - d.Filter( - name="500 Dichroic Longpass Filter", - manufacturer=d.Organization.EDMUND_OPTICS, - model="#69-899", - filter_type="Dichroic", - cut_off_wavelength=500, - width=35.6, - height=23.2, - ), - ], - lenses=[ - d.Lens( - manufacturer=d.Organization.THORLABS, - model="AC254-080-A-ML", - name="Image focusing lens", - focal_length=80, - focal_length_unit=SizeUnit.MM, - size=1, - ) - ], - daqs=[ - d.HarpDevice( - name="Harp Behavior", - harp_device_type=d.HarpDeviceType.BEHAVIOR, - core_version="2.1", - computer_name="behavior_computer", - is_clock_generator=False, - channels=[ - d.DAQChannel(channel_name="DO0", device_name="Solenoid Left", channel_type="Digital Output"), - d.DAQChannel(channel_name="DO1", device_name="Solenoid Right", channel_type="Digital Output"), - d.DAQChannel( - channel_name="DI0", device_name="Janelia_Lick_Detector Left", channel_type="Digital Input" + ), + ] + patch_cords = [ + d.Patch( + name="Bundle Branching Fiber-optic Patch Cord", + manufacturer=d.Organization.DORIC, + model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", + core_diameter=200, + numerical_aperture=0.37, + ) + ] + light_sources = [ + d.LightEmittingDiode( + name="470nm LED", + manufacturer=d.Organization.THORLABS, + model="M470F3", + wavelength=470, + ), + d.LightEmittingDiode( + name="415nm LED", + manufacturer=d.Organization.THORLABS, + model="M415F3", + wavelength=415, + ), + d.LightEmittingDiode( + name="565nm LED", + manufacturer=d.Organization.THORLABS, + model="M565F3", + wavelength=565, + ), + ] + detectors = [ + d.Detector( + name="Green CMOS", + serial_number="21396991", + manufacturer=d.Organization.FLIR, + model="BFS-U3-20S40M", + detector_type="Camera", + data_interface="USB", + cooling="Air", + immersion="air", + bin_width=4, + bin_height=4, + bin_mode="Additive", + crop_width=200, + crop_height=200, + gain=2, + chroma="Monochrome", + bit_depth=16, + ), + d.Detector( + name="Red CMOS", + serial_number="21396991", + manufacturer=d.Organization.FLIR, + model="BFS-U3-20S40M", + detector_type="Camera", + data_interface="USB", + cooling="Air", + immersion="air", + bin_width=4, + bin_height=4, + bin_mode="Additive", + crop_width=200, + crop_height=200, + gain=2, + chroma="Monochrome", + bit_depth=16, + ), + ] + objectives = [ + d.Objective( + name="Objective", + serial_number="128022336", + manufacturer=d.Organization.NIKON, + model="CFI Plan Apochromat Lambda D 10x", + numerical_aperture=0.45, + magnification=10, + immersion="air", + ) + ] + filters = [ + d.Filter( + name="Green emission filter", + manufacturer=d.Organization.SEMROCK, + model="FF01-520/35-25", + filter_type="Band pass", + center_wavelength=520, + diameter=25, + ), + d.Filter( + name="Red emission filter", + manufacturer=d.Organization.SEMROCK, + model="FF01-600/37-25", + filter_type="Band pass", + center_wavelength=600, + diameter=25, + ), + d.Filter( + name="Emission Dichroic", + model="FF562-Di03-25x36", + manufacturer=d.Organization.SEMROCK, + filter_type="Dichroic", + height=25, + width=36, + cut_off_wavelength=562, + ), + d.Filter( + name="dual-edge standard epi-fluorescence dichroic beamsplitter", + model="FF493/574-Di01-25x36", + manufacturer=d.Organization.SEMROCK, + notes="493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", + filter_type="Multiband", + width=36, + height=24, + ), + d.Filter( + name="Excitation filter 410nm", + manufacturer=d.Organization.THORLABS, + model="FB410-10", + filter_type="Band pass", + diameter=25, + center_wavelength=410, + ), + d.Filter( + name="Excitation filter 470nm", + manufacturer=d.Organization.THORLABS, + model="FB470-10", + filter_type="Band pass", + center_wavelength=470, + diameter=25, + ), + d.Filter( + name="Excitation filter 560nm", + manufacturer=d.Organization.THORLABS, + model="FB560-10", + filter_type="Band pass", + diameter=25, + center_wavelength=560, + ), + d.Filter( + name="450 Dichroic Longpass Filter", + manufacturer=d.Organization.EDMUND_OPTICS, + model="#69-898", + filter_type="Dichroic", + cut_off_wavelength=450, + width=35.6, + height=25.2, + ), + d.Filter( + name="500 Dichroic Longpass Filter", + manufacturer=d.Organization.EDMUND_OPTICS, + model="#69-899", + filter_type="Dichroic", + cut_off_wavelength=500, + width=35.6, + height=23.2, + ), + ] + lenses = [ + d.Lens( + manufacturer=d.Organization.THORLABS, + model="AC254-080-A-ML", + name="Image focusing lens", + focal_length=80, + focal_length_unit=SizeUnit.MM, + size=1, + ) + ] + daqs = [ + d.HarpDevice( + name="Harp Behavior", + harp_device_type=d.HarpDeviceType.BEHAVIOR, + core_version="2.1", + computer_name="behavior_computer", + is_clock_generator=False, + channels=[ + d.DAQChannel(channel_name="DO0", device_name="Solenoid Left", channel_type="Digital Output"), + d.DAQChannel(channel_name="DO1", device_name="Solenoid Right", channel_type="Digital Output"), + d.DAQChannel( + channel_name="DI0", device_name="Janelia_Lick_Detector Left", channel_type="Digital Input" + ), + d.DAQChannel( + channel_name="DI1", device_name="Janelia_Lick_Detector Right", channel_type="Digital Input" + ), + d.DAQChannel(channel_name="DI3", device_name="Photometry Clock", channel_type="Digital Input"), + ], + ) + ] + stimulus_devices = [ + d.RewardDelivery( + reward_spouts=[ + d.RewardSpout( + name="Left spout", + side=d.SpoutSide.LEFT, + spout_diameter=1.2, + solenoid_valve=d.Device(device_type="Solenoid", name="Solenoid Left"), + lick_sensor=d.Device( + name="Janelia_Lick_Detector Left", + device_type="Lick detector", + manufacturer=d.Organization.JANELIA, ), - d.DAQChannel( - channel_name="DI1", device_name="Janelia_Lick_Detector Right", channel_type="Digital Input" + lick_sensor_type=d.LickSensorType("Capacitive"), + ), + d.RewardSpout( + name="Right spout", + side=d.SpoutSide.RIGHT, + spout_diameter=1.2, + solenoid_valve=d.Device(device_type="Solenoid", name="Solenoid Right"), + lick_sensor=d.Device( + name="Janelia_Lick_Detector Right", + device_type="Lick detector", + manufacturer=d.Organization.JANELIA, ), - d.DAQChannel(channel_name="DI3", device_name="Photometry Clock", channel_type="Digital Input"), - ], - ) - ], + lick_sensor_type=d.LickSensorType("Capacitive"), + ), + ], + ), + ] + additional_devices = [d.Device(device_type="Photometry Clock", name="Photometry Clock")] + + cls.example_ephys_inst = Instrument.model_validate_json(json.dumps(read_json(EPHYS_INST_JSON))) + cls.example_ephys_session = Session.model_validate_json(json.dumps(read_json(EPHYS_SESSION_JSON))) + cls.ophys_instrument = r.Instrument( + instrument_id="428_FIP1_20231003", + modification_date=date(2023, 10, 3), + modalities=[Modality.FIB], mouse_platform=d.Disc(name="mouse_disc", radius=8.5), - stimulus_devices=[ - d.RewardDelivery( - reward_spouts=[ - d.RewardSpout( - name="Left spout", - side=d.SpoutSide.LEFT, - spout_diameter=1.2, - solenoid_valve=d.Device(device_type="Solenoid", name="Solenoid Left"), - lick_sensor=d.Device( - name="Janelia_Lick_Detector Left", - device_type="Lick detector", - manufacturer=d.Organization.JANELIA, - ), - lick_sensor_type=d.LickSensorType("Capacitive"), - ), - d.RewardSpout( - name="Right spout", - side=d.SpoutSide.RIGHT, - spout_diameter=1.2, - solenoid_valve=d.Device(device_type="Solenoid", name="Solenoid Right"), - lick_sensor=d.Device( - name="Janelia_Lick_Detector Right", - device_type="Lick detector", - manufacturer=d.Organization.JANELIA, - ), - lick_sensor_type=d.LickSensorType("Capacitive"), - ), - ], - ), + components=[ + *cameras, + *patch_cords, + *light_sources, + *detectors, + *objectives, + *filters, + *lenses, + *daqs, + *stimulus_devices, + *additional_devices, ], - additional_devices=[d.Device(device_type="Photometry Clock", name="Photometry Clock")], calibrations=[ d.Calibration( calibration_date=datetime(2023, 10, 2, 3, 15, 22, tzinfo=timezone.utc), @@ -851,16 +864,18 @@ def test_run_compatibility_check(self): "Insturment ID in session 323_EPHYS2-RF_2023-04-24_01 does not match the rig's 323_EPHYS1_20231003." ) with self.assertRaises(ValueError) as context: - RigSessionCompatibility(instrument=ephys_inst, session=ephys_session).run_compatibility_check() + InstrumentSessionCompatibility(instrument=ephys_inst, session=ephys_session).run_compatibility_check() self.assertIn(expected_error, str(context.exception)) with self.assertRaises(ValueError): - RigSessionCompatibility(instrument=self.ophys_inst, session=self.ophys_session).run_compatibility_check() + InstrumentSessionCompatibility( + instrument=self.ophys_inst, session=self.ophys_session + ).run_compatibility_check() def test_check_examples_compatibility(self): """Tests that examples are compatible""" # check that ephys session and rig are synced - example_ephys_check = RigSessionCompatibility( + example_ephys_check = InstrumentSessionCompatibility( instrument=self.example_ephys_inst, session=self.example_ephys_session ) self.assertIsNone(example_ephys_check.run_compatibility_check()) diff --git a/tests/test_metadata.py b/tests/test_metadata.py index b9802041f..a5a12622d 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -277,7 +277,7 @@ def test_multi_modal_metadata(self): session=session, # SPIM excludes session, but BEHAVIOR requires it procedures=Procedures.model_construct(subject_procedures=[surgery1]), acquisition=Acquisition.model_construct(), - instrument=rig, + instrument=inst, processing=Processing.model_construct(), ) self.assertIsNotNone(m) @@ -328,33 +328,6 @@ def test_validate_ecephys_metadata(self): ) self.assertIn("Injection is missing injection_materials.", str(context.exception)) - def test_validate_underscore_modality(self): - """Tests that ecephys validator works as expected""" - viral_material = ViralMaterial.model_construct() - nano_inj = NanojectInjection.model_construct(injection_materials=[viral_material]) - ionto_inj = IontophoresisInjection.model_construct(injection_materials=[viral_material]) - mouse_platform = MousePlatform.model_construct(name="platform1") - inst = Instrument.model_construct(instrument_id="123_EPHYS2_20230101", mouse_platform=mouse_platform) - session = Session.model_construct(instrument_id="123_EPHYS2_20230101", mouse_platform_name="platform1") - - # Tests missing metadata - surgery1 = Surgery.model_construct(procedures=[nano_inj, ionto_inj]) - m = Metadata( - name="ecephys_655019_2023-04-03_18-17-09", - location="bucket", - data_description=DataDescription.model_construct( - label="some label", - platform=Platform.ECEPHYS, - creation_time=time(12, 12, 12), - modality=[Modality.BEHAVIOR_VIDEOS], - ), - subject=Subject.model_construct(), - procedures=Procedures.model_construct(subject_procedures=[surgery1]), - instrument=rig, - session=session, - ) - self.assertIsNotNone(m) - def test_validate_instrument_session_compatibility(self): """Tests that rig/session compatibility validator works as expected""" mouse_platform = MousePlatform.model_construct(name="platform1") @@ -372,7 +345,7 @@ def test_validate_instrument_session_compatibility(self): ), subject=Subject.model_construct(), procedures=Procedures.model_construct(), - instrument=rig, + instrument=inst, processing=Processing.model_construct(), session=session, ) From f93672878b6d723a3833d4f7ecbd236e750d3208 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Fri, 17 Jan 2025 10:26:54 -0800 Subject: [PATCH 06/34] tests: fixing more tests --- src/aind_data_schema/core/instrument.py | 28 +-- tests/test_imaging.py | 85 +------- tests/test_instrument.py | 249 ++++++++++-------------- tests/test_metadata.py | 19 +- 4 files changed, 133 insertions(+), 248 deletions(-) diff --git a/src/aind_data_schema/core/instrument.py b/src/aind_data_schema/core/instrument.py index 0ae89c4f9..a662132e2 100644 --- a/src/aind_data_schema/core/instrument.py +++ b/src/aind_data_schema/core/instrument.py @@ -46,6 +46,19 @@ Speaker, ) +# Define the mapping of modalities to their required device types +# The list of list pattern is used to allow for multiple options within a group, so e.g. +# FIB requires a light (one of the options) plus a detector and a patch cord +DEVICES_REQUIRED = { + Modality.ECEPHYS.abbreviation: [EphysAssembly], + Modality.FIB.abbreviation: [[Laser, LightEmittingDiode, Lamp], [Detector], [Patch]], + Modality.POPHYS.abbreviation: [[Laser, LightEmittingDiode, Lamp], [Detector], [Objective]], + Modality.SLAP.abbreviation: [[Laser, LightEmittingDiode, Lamp], [Detector], [Objective]], + Modality.BEHAVIOR_VIDEOS.abbreviation: [CameraAssembly], + Modality.BEHAVIOR.abbreviation: [[Olfactometer, RewardDelivery, Speaker, Monitor]], + Modality.SPIM.abbreviation: [Objective], +} + MOUSE_PLATFORMS = Annotated[Union[tuple(MousePlatform.__subclasses__())], Field(discriminator="device_type")] instrument_id_PATTERN = r"^[a-zA-Z0-9]+_[a-zA-Z0-9-]+_\d{8}$" @@ -211,26 +224,13 @@ def validate_modalities(cls, value): if len(value.modalities) == 0: return value - # Define the mapping of modalities to their required device types - # The list of list pattern is used to allow for multiple options within a group, so e.g. - # FIB requires a light (one of the options) plus a detector and a patch cord - type_mapping = { - Modality.ECEPHYS: [EphysAssembly], - Modality.FIB: [[Laser, LightEmittingDiode, Lamp], [Detector], [Patch]], - Modality.POPHYS: [[Laser, LightEmittingDiode, Lamp], [Detector], [Objective]], - Modality.SLAP: [[Laser, LightEmittingDiode, Lamp], [Detector], [Objective]], - Modality.BEHAVIOR_VIDEOS: [CameraAssembly], - Modality.BEHAVIOR: [[Olfactometer, RewardDelivery, Speaker, Monitor]], - Modality.SPIM: [[Objective], [Detector], [ScanningStage], [MotorizedStage]], - } - # Retrieve the components from the validation info components = value.components errors = [] # Validate each modality for modality in value.modalities: - required_device_groups = type_mapping.get(modality.abbreviation) + required_device_groups = DEVICES_REQUIRED.get(modality.abbreviation) if not required_device_groups: # Skip modalities that don't require validation continue diff --git a/tests/test_imaging.py b/tests/test_imaging.py index d676f670d..dd6ad4e4e 100644 --- a/tests/test_imaging.py +++ b/tests/test_imaging.py @@ -2,10 +2,10 @@ import re import unittest -from datetime import date, datetime, timezone +from datetime import datetime, timezone from aind_data_schema_models.organizations import Organization -from aind_data_schema_models.units import PowerUnit, FrequencyUnit +from aind_data_schema_models.units import PowerUnit from pydantic import ValidationError from pydantic import __version__ as pyd_version @@ -16,7 +16,7 @@ Scale3dTransform, Translation3dTransform, ) -from aind_data_schema.components.devices import Calibration, DAQChannel, DAQDevice +from aind_data_schema.components.devices import Calibration from aind_data_schema.core import acquisition as acq from aind_data_schema.core.processing import Registration from aind_data_schema.core.instrument import Instrument @@ -201,85 +201,6 @@ def test_registration(self): self.assertIsNotNone(t) - def test_validators(self): - """test the validators""" - - with self.assertRaises(ValidationError) as e: - daq = DAQDevice( - model="PCIe-6738", - data_interface="USB", - computer_name="Dev2", - manufacturer=Organization.NATIONAL_INSTRUMENTS, - name="Dev2", - serial_number="Unknown", - channels=[ - DAQChannel( - channel_name="3", - channel_type="Analog Output", - device_name="LAS-08308", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="5", - channel_type="Analog Output", - device_name="539251", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="4", - channel_type="Analog Output", - device_name="LAS-08309", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="2", - channel_type="Analog Output", - device_name="stage-x", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="0", - channel_type="Analog Output", - device_name="TL-1", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - DAQChannel( - channel_name="6", - channel_type="Analog Output", - device_name="LAS-08307", - sample_rate=10000, - sample_rate_unit=FrequencyUnit.HZ, - ), - ], - ) - - Instrument( - instrument_id="exaSPIM1-1", - modalities=[Modality.SPIM], - instrument_type="exaSPIM", - modification_date=date(2023, 10, 4), - manufacturer=Organization.CUSTOM, - components=[daq], - ) - expected_exception = ( - "2 validation errors for Instrument\n" - "objectives\n" - " Field required [type=missing," - " input_value={'instrument_id': 'exaSPI...hardware_version=None)]}, input_type=dict]\n" - f" For further information visit https://errors.pydantic.dev/{PYD_VERSION}/v/missing\n" - "daqs\n" - " Value error, Device name validation error: 'LAS-08308' is connected to '3' on 'Dev2'," - " but this device is not part of the inst. [type=value_error," - " input_value=[DAQDevice(device_type='D... hardware_version=None)], input_type=list]\n" - f" For further information visit https://errors.pydantic.dev/{PYD_VERSION}/v/value_error" - ) - self.assertEqual(expected_exception, repr(e.exception)) - if __name__ == "__main__": unittest.main() diff --git a/tests/test_instrument.py b/tests/test_instrument.py index 192fce0de..d4dc180a4 100644 --- a/tests/test_instrument.py +++ b/tests/test_instrument.py @@ -24,16 +24,18 @@ Disc, EphysAssembly, EphysProbe, + ImagingInstrumentType, Laser, LaserAssembly, Lens, Manipulator, NeuropixelsBasestation, + Objective, Olfactometer, OlfactometerChannel, Patch, ) -from aind_data_schema.core.instrument import Instrument +from aind_data_schema.core.instrument import Instrument, DEVICES_REQUIRED daqs = [ NeuropixelsBasestation( @@ -182,12 +184,22 @@ numerical_aperture=0.37, ) ] +objectives = [ + Objective( + name="TLX Objective 1", + numerical_aperture=0.2, + magnification=3.6, + manufacturer=Organization.LIFECANVAS, + immersion="multi", + notes="Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", + serial_number="Unknown-1", + ) +] stimulus_devices = [ Olfactometer( name="Olfactometer", manufacturer=Organization.CHAMPALIMAUD, model="1234", - harp_device_type="1234", serial_number="213456", hardware_version="1", is_clock_generator=False, @@ -250,119 +262,32 @@ def test_constructors(self): ) ], ) + self.assertIsNotNone(inst) - assert rig is not None - - def test_validator(self): - """Test the rig file validators""" - neuropixels_base = NeuropixelsBasestation( - name="Basestation", - basestation_firmware_version="1", - bsc_firmware_version="2", - slot=0, - manufacturer=Organization.IMEC, - ports=[], - computer_name="foo", - channels=[ - DAQChannel( - channel_name="123", - device_name="Laser A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="321", - device_name="Probe A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="234", - device_name="Camera A", - channel_type="Digital Output", - ), - DAQChannel( - channel_name="2354", - device_name="Disc A", - channel_type="Digital Output", - ), - ], - ) - - # A Rig model with ECEPHYS in the modality list requires - # ephys_assemblies and stick microscopes - with self.assertRaises(ValidationError): - Instrument( - modalities=[ - Modality.ECEPHYS, - Modality.SLAP, - Modality.FIB, - Modality.BEHAVIOR_VIDEOS, - Modality.POPHYS, - Modality.BEHAVIOR, - ], - instrument_id="123_EPHYS1-OPTO_20220101", - modification_date=date(2020, 10, 10), - components=[ - neuropixels_base, - ], - calibrations=[ - Calibration( - calibration_date=date(2020, 10, 10), - device_name="Laser A", - description="Laser power calibration", - input={"power percent": [10, 40, 80]}, - output={"power mW": [2, 6, 10]}, - ) - ], - mouse_platform=Disc(name="Disc A", radius=1), - ) + def test_validator_modality_device_missing(self): + """Test that the modality -> device validator throws validation errors when devices are missing""" - with self.assertRaises(ValidationError): - daqs = [ - NeuropixelsBasestation( - name="Basestation", - basestation_firmware_version="1", - bsc_firmware_version="2", - slot=0, - manufacturer=Organization.IMEC, - ports=[], - computer_name="foo", - channels=[ - DAQChannel( - channel_name="123", - device_name="Laser A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="321", - device_name="Probe A", - channel_type="Analog Output", - ), - DAQChannel( - channel_name="234", - device_name="Camera A", - channel_type="Digital Output", - ), - DAQChannel( - channel_name="2354", - device_name="Disc A", - channel_type="Digital Output", - ), - ], + # Mapping is a dictionary of Modality -> List[Device groups] + for modality_abbreviation, _ in DEVICES_REQUIRED.items(): + with self.assertRaises(ValidationError): + Instrument( + modalities=[Modality.from_abbreviation(modality_abbreviation)], + instrument_id="123_EPHYS1-OPTO_20220101", + modification_date=date(2020, 10, 10), + components=[], + calibrations=[], ) - ] + + def test_validator_modality_device_present(self): + """Test that the modality -> device validator does not throw validation errors when devices are present""" - Instrument( - instrument_id="123_EPHYS1-OPTO_20220101", - modification_date=datetime.now(), - modalities=[Modality.ECEPHYS, Modality.FIB], - components=[*daqs], - ) + # Mapping is a dictionary of Modality -> List[Device groups] + for modality_abbreviation, device_groups in DEVICES_REQUIRED.items(): - with self.assertRaises(ValueError): - Instrument( - instrument_id="1234", + inst = Instrument( + modalities=[Modality.from_abbreviation(modality_abbreviation)], + instrument_id="123_EPHYS1-OPTO_20220101", modification_date=date(2020, 10, 10), - modalities=[Modality.ECEPHYS, Modality.FIB], components=[ *daqs, *cameras, @@ -373,59 +298,87 @@ def test_validator(self): *detectors, *patch_cords, *stimulus_devices, + *objectives, ], - mouse_platform=Disc(name="Disc A", radius=1), - calibrations=[ - Calibration( - calibration_date=date(2020, 10, 10), - device_name="Laser A", - description="Laser power calibration", - input={"power percent": [10, 40, 80]}, - output={"power mW": [2, 6, 10]}, - ) - ], + calibrations=[], ) + self.assertIsNotNone(inst) + + def test_validator_notes(self): + """Test the notes validator""" - # Tests that validators catch empty lists without KeyErrors + # Test when instrument_type is OTHER and notes are empty with self.assertRaises(ValidationError): Instrument( instrument_id="123_EPHYS1-OPTO_20220101", modification_date=date(2020, 10, 10), - modalities=[Modality.ECEPHYS, Modality.FIB], - components=[], - mouse_platform=Disc(name="Disc A", radius=Decimal(1.0)), - calibrations=[], + modalities=[Modality.ECEPHYS], + components=[*daqs, *ems], + instrument_type=ImagingInstrumentType.OTHER, + manufacturer=Organization.IMEC, + notes=None, ) - # What is this testing? + # Test when manufacturer is OTHER and notes are empty with self.assertRaises(ValidationError): Instrument( - instrument_id="123_EPHYS-OPTO_20200101", + instrument_id="123_EPHYS1-OPTO_20220101", modification_date=date(2020, 10, 10), - modalities=[Modality.ECEPHYS, Modality.FIB], - components=[ - *daqs, - *cameras, - *stick_microscopes, - *light_sources, - *lms, - *ems, - *detectors, - *patch_cords, - *stimulus_devices, - ], - mouse_platform=Disc(name="Disc A", radius=1), - calibrations=[ - Calibration( - calibration_date=date(2020, 10, 10), - device_name="Laser A", - description="Laser power calibration", - input={"power percent": [10, 40, 80]}, - output={"power mW": [2, 6, 10]}, - ) - ], + modalities=[Modality.ECEPHYS], + components=[*daqs, *ems], + instrument_type=ImagingInstrumentType.CONFOCAL, + manufacturer=Organization.OTHER, + notes=None, ) + # Test when both instrument_type and manufacturer are OTHER and notes are empty + with self.assertRaises(ValidationError): + Instrument( + instrument_id="123_EPHYS1-OPTO_20220101", + modification_date=date(2020, 10, 10), + modalities=[Modality.ECEPHYS], + components=[*daqs, *ems], + instrument_type=ImagingInstrumentType.OTHER, + manufacturer=Organization.OTHER, + notes=None, + ) + + # Test when notes are provided for instrument_type OTHER + inst = Instrument( + instrument_id="123_EPHYS1-OPTO_20220101", + modification_date=date(2020, 10, 10), + modalities=[Modality.ECEPHYS], + components=[*daqs, *ems], + instrument_type=ImagingInstrumentType.OTHER, + manufacturer=Organization.IMEC, + notes="This is a custom instrument type.", + ) + self.assertIsNotNone(inst) + + # Test when notes are provided for manufacturer OTHER + inst = Instrument( + instrument_id="123_EPHYS1-OPTO_20220101", + modification_date=date(2020, 10, 10), + modalities=[Modality.ECEPHYS], + components=[*daqs, *ems], + instrument_type=ImagingInstrumentType.CONFOCAL, + manufacturer=Organization.OTHER, + notes="This is a custom manufacturer.", + ) + self.assertIsNotNone(inst) + + # Test when notes are provided for both instrument_type and manufacturer OTHER + inst = Instrument( + instrument_id="123_EPHYS1-OPTO_20220101", + modification_date=date(2020, 10, 10), + modalities=[Modality.ECEPHYS], + components=[*daqs, *ems], + instrument_type=ImagingInstrumentType.OTHER, + manufacturer=Organization.OTHER, + notes="This is a custom instrument type and manufacturer.", + ) + self.assertIsNotNone(inst) + def test_instrument_id_validator(self): """Tests that instrument_id validator works as expected""" diff --git a/tests/test_metadata.py b/tests/test_metadata.py index a5a12622d..28f369659 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -14,7 +14,7 @@ from pydantic import ValidationError from pydantic import __version__ as pyd_version -from aind_data_schema.components.devices import MousePlatform +from aind_data_schema.components.devices import EphysAssembly, EphysProbe, Manipulator, MousePlatform from aind_data_schema.core.acquisition import Acquisition from aind_data_schema.core.data_description import DataDescription, Funding from aind_data_schema.core.metadata import ExternalPlatforms, Metadata, MetadataStatus, create_metadata_json @@ -243,7 +243,7 @@ def test_validate_smartspim_metadata(self): subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), acquisition=Acquisition.model_construct(), - instrument=Instrument.model_construct(), + instrument=Instrument.model_construct(modalities=[Modality.SPIM]), processing=Processing.model_construct(), ) self.assertIn("Injection is missing injection_materials.", str(context.exception)) @@ -330,8 +330,18 @@ def test_validate_ecephys_metadata(self): def test_validate_instrument_session_compatibility(self): """Tests that rig/session compatibility validator works as expected""" + ephys_assembly = EphysAssembly( + probes=[EphysProbe(probe_model="Neuropixels 1.0", name="Probe A")], + manipulator=Manipulator( + name="Probe manipulator", + manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, + serial_number="4321", + ), + name="Ephys_assemblyA", + ) + mouse_platform = MousePlatform.model_construct(name="platform1") - inst = Instrument.model_construct(instrument_id="123_EPHYS1_20220101", mouse_platform=mouse_platform) + inst = Instrument.model_construct(instrument_id="123_EPHYS1_20220101", mouse_platform=mouse_platform, modalities=[Modality.ECEPHYS], components=[ephys_assembly]) session = Session.model_construct(instrument_id="123_EPHYS2_20230101", mouse_platform_name="platform2") with self.assertRaises(ValidationError) as context: Metadata( @@ -349,8 +359,9 @@ def test_validate_instrument_session_compatibility(self): processing=Processing.model_construct(), session=session, ) + print(str(context.exception)) self.assertIn( - "Insturment ID in session 123_EPHYS2_20230101 does not match the rig's 123_EPHYS1_20220101.", + "Instrument ID in session 123_EPHYS2_20230101 does not match the rig's 123_EPHYS1_20220101.", str(context.exception), ) From 3d06811e52b58e477e88f067512694e052b0e66f Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Fri, 17 Jan 2025 10:34:47 -0800 Subject: [PATCH 07/34] tests: re-generate examples, fix missing Objective in test --- examples/aibs_smartspim_instrument.json | 503 ++++++++++++ examples/aind_smartspim_instrument.json | 631 ++++++++++++++++ examples/bergamo_ophys_session.json | 2 +- examples/ephys_instrument.json | 898 ++++++++++++++++++++++ examples/ephys_session.json | 2 +- examples/exaspim_instrument.json | 452 +++++++++++ examples/fip_behavior_instrument.json | 965 ++++++++++++++++++++++++ examples/fip_ophys_instrument.json | 903 ++++++++++++++++++++++ examples/fip_ophys_rig.py | 2 +- examples/mri_session.json | 2 +- examples/multiplane_ophys_session.json | 2 +- examples/ophys_session.json | 2 +- tests/test_examples.py | 5 +- tests/test_metadata.py | 4 +- 14 files changed, 4364 insertions(+), 9 deletions(-) create mode 100644 examples/aibs_smartspim_instrument.json create mode 100644 examples/aind_smartspim_instrument.json create mode 100644 examples/ephys_instrument.json create mode 100644 examples/exaspim_instrument.json create mode 100644 examples/fip_behavior_instrument.json create mode 100644 examples/fip_ophys_instrument.json diff --git a/examples/aibs_smartspim_instrument.json b/examples/aibs_smartspim_instrument.json new file mode 100644 index 000000000..ef23c6b98 --- /dev/null +++ b/examples/aibs_smartspim_instrument.json @@ -0,0 +1,503 @@ +{ + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "1.0.5", + "instrument_id": "440_SmartSPIM2_20231004", + "mouse_platform": null, + "modification_date": "2023-10-04", + "calibrations": null, + "ccf_coordinate_transform": null, + "origin": null, + "instrument_axes": null, + "modalities": [ + { + "name": "Selective plane illumination microscopy", + "abbreviation": "SPIM" + } + ], + "com_ports": [ + { + "hardware_name": "Laser Launch", + "com_port": "COM3" + }, + { + "hardware_name": "ASI Tiger", + "com_port": "COM5" + }, + { + "hardware_name": "MightyZap", + "com_port": "COM4" + } + ], + "instrument_type": "SmartSPIM", + "manufacturer": { + "name": "LifeCanvas", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "temperature_control": false, + "notes": null, + "connections": [], + "components": [ + { + "device_type": "Objective", + "name": "TLX Objective", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "TL4X-SAP", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", + "numerical_aperture": "0.2", + "magnification": "3.6", + "immersion": "multi", + "objective_type": null + }, + { + "device_type": "Detector", + "name": "Camera 1", + "serial_number": "001107", + "manufacturer": { + "name": "Hamamatsu", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "03natb733" + }, + "model": "C14440-20UP", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": null, + "frame_rate": null, + "frame_rate_unit": null, + "immersion": null, + "chroma": null, + "sensor_width": null, + "sensor_height": null, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + { + "device_type": "Laser", + "name": "Ex_488", + "serial_number": "VL01222A11", + "manufacturer": { + "name": "Vortran", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "Stradus", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "All lasers controlled via Vortran VersaLase System", + "wavelength": 488, + "wavelength_unit": "nanometer", + "maximum_power": "150", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Ex_561", + "serial_number": "417927", + "manufacturer": { + "name": "Coherent Scientific", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "031tysd23" + }, + "model": "Obis", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "All lasers controlled via Vortran VersaLase System", + "wavelength": 561, + "wavelength_unit": "nanometer", + "maximum_power": "150", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Ex_647", + "serial_number": "VL01222A10", + "manufacturer": { + "name": "Vortran", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "Stradus", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "All lasers controlled via Vortran VersaLase System", + "wavelength": 647, + "wavelength_unit": "nanometer", + "maximum_power": "160", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Motorized stage", + "name": "Focus stage", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-100", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "100", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #1", + "serial_number": null, + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #2", + "serial_number": null, + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #3", + "serial_number": null, + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #4", + "serial_number": null, + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Scanning stage", + "name": "Sample stage Z", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-50", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "50", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Detection axis", + "stage_axis_name": "Z" + }, + { + "device_type": "Scanning stage", + "name": "Sample stage X", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-50", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "50", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Illumination axis", + "stage_axis_name": "X" + }, + { + "device_type": "Scanning stage", + "name": "Sample stage Y", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-50", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "50", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Perpendicular axis", + "stage_axis_name": "Y" + }, + { + "device_type": "Optical table", + "name": "Table", + "serial_number": null, + "manufacturer": { + "name": "Technical Manufacturing Corporation", + "abbreviation": "TMC", + "registry": null, + "registry_identifier": null + }, + "model": "CleanTop", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "length": "35", + "width": "29", + "table_size_unit": "inch", + "vibration_control": true + }, + { + "device_type": "Filter", + "name": "Em_525", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF03-525/50-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 0, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Em_600", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF01-600/52-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 1, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Em_690", + "serial_number": null, + "manufacturer": { + "name": "Chroma", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "ET690/50m", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 2, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Additional imaging device", + "name": "Lens 1", + "serial_number": null, + "manufacturer": { + "name": "Optotune", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "EL-16-40-TC", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "imaging_device_type": "Tunable lens" + }, + { + "device_type": "Additional imaging device", + "name": "Lens 2", + "serial_number": null, + "manufacturer": { + "name": "Optotune", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "EL-16-40-TC", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "imaging_device_type": "Tunable lens" + }, + { + "device_type": "Additional imaging device", + "name": "Sample chamber", + "serial_number": null, + "manufacturer": { + "name": "LifeCanvas", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "Large-uncoated-glass", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "imaging_device_type": "Sample Chamber" + } + ] +} \ No newline at end of file diff --git a/examples/aind_smartspim_instrument.json b/examples/aind_smartspim_instrument.json new file mode 100644 index 000000000..d64b2b1bc --- /dev/null +++ b/examples/aind_smartspim_instrument.json @@ -0,0 +1,631 @@ +{ + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "1.0.5", + "instrument_id": "440_SmartSPIM1_20231004", + "mouse_platform": null, + "modification_date": "2023-10-04", + "calibrations": null, + "ccf_coordinate_transform": null, + "origin": null, + "instrument_axes": null, + "modalities": [ + { + "name": "Selective plane illumination microscopy", + "abbreviation": "SPIM" + } + ], + "com_ports": [ + { + "hardware_name": "Laser Launch", + "com_port": "COM4" + }, + { + "hardware_name": "ASI Tiger", + "com_port": "COM3" + }, + { + "hardware_name": "MightyZap", + "com_port": "COM9" + } + ], + "instrument_type": "SmartSPIM", + "manufacturer": { + "name": "LifeCanvas", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "temperature_control": false, + "notes": null, + "connections": [], + "components": [ + { + "device_type": "Objective", + "name": "TLX Objective 1", + "serial_number": "Unknown-1", + "manufacturer": { + "name": "LifeCanvas", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", + "numerical_aperture": "0.2", + "magnification": "3.6", + "immersion": "multi", + "objective_type": null + }, + { + "device_type": "Objective", + "name": "TLX Objective 2", + "serial_number": "Unknown-2", + "manufacturer": { + "name": "LifeCanvas", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Thorlabs TL2X-SAP with LifeCanvas dipping cap and correction optics.", + "numerical_aperture": "0.12", + "magnification": "1.625", + "immersion": "multi", + "objective_type": null + }, + { + "device_type": "Detector", + "name": "Camera 1", + "serial_number": "001284", + "manufacturer": { + "name": "Hamamatsu", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "03natb733" + }, + "model": "C14440-20UP", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": null, + "frame_rate": null, + "frame_rate_unit": null, + "immersion": null, + "chroma": null, + "sensor_width": null, + "sensor_height": null, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + { + "device_type": "Laser", + "name": "Ex_445", + "serial_number": "VL08223M03", + "manufacturer": { + "name": "Vortran", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 445, + "wavelength_unit": "nanometer", + "maximum_power": "200", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Ex_488", + "serial_number": "VL08223M03", + "manufacturer": { + "name": "Vortran", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 488, + "wavelength_unit": "nanometer", + "maximum_power": "150", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Ex_561", + "serial_number": "VL08223M03", + "manufacturer": { + "name": "Vortran", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 561, + "wavelength_unit": "nanometer", + "maximum_power": "150", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Ex_594", + "serial_number": "VL08223M03", + "manufacturer": { + "name": "Vortran", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 594, + "wavelength_unit": "nanometer", + "maximum_power": "100", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Ex_639", + "serial_number": "VL08223M03", + "manufacturer": { + "name": "Vortran", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 639, + "wavelength_unit": "nanometer", + "maximum_power": "160", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Ex_690", + "serial_number": "VL08223M03", + "manufacturer": { + "name": "Vortran", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 690, + "wavelength_unit": "nanometer", + "maximum_power": "160", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Filter", + "name": "Em_469", + "serial_number": "Unknown-0", + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF01-469/35-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 0, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Em_525", + "serial_number": "Unknown-1", + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF01-525/45-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 1, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Em_593", + "serial_number": "Unknown-2", + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF01-593/40-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 2, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Em_624", + "serial_number": "Unknown-3", + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF01-624/40-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 3, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Em_667", + "serial_number": "Unknown-4", + "manufacturer": { + "name": "Chroma", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "ET667/30m", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 4, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Em_700", + "serial_number": "Unknown-5", + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "FELH0700", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Long pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "2.0", + "thickness_unit": "millimeter", + "filter_wheel_index": 5, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Motorized stage", + "name": "Focus stage", + "serial_number": "Unknown-1", + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-100", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "100", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #1", + "serial_number": "Unknown-5", + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #2", + "serial_number": "Unknown-6", + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #3", + "serial_number": "Unknown-7", + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Motorized stage", + "name": "Cylindrical lens #4", + "serial_number": "Unknown-8", + "manufacturer": { + "name": "IR Robot Co", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L12-20F-4", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "41", + "travel_unit": "millimeter", + "firmware": null + }, + { + "device_type": "Scanning stage", + "name": "Sample stage Z", + "serial_number": "Unknown-2", + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-50", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "50", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Detection axis", + "stage_axis_name": "Z" + }, + { + "device_type": "Scanning stage", + "name": "Sample stage X", + "serial_number": "Unknown-3", + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-50", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "50", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Illumination axis", + "stage_axis_name": "X" + }, + { + "device_type": "Scanning stage", + "name": "Sample stage Y", + "serial_number": "Unknown-4", + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-50", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "50", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Perpendicular axis", + "stage_axis_name": "Y" + }, + { + "device_type": "Optical table", + "name": "Main optical table", + "serial_number": "Unknown", + "manufacturer": { + "name": "MKS Newport", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "00k17f049" + }, + "model": "VIS2424-IG2-125A", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "length": "36", + "width": "48", + "table_size_unit": "inch", + "vibration_control": false + } + ] +} \ No newline at end of file diff --git a/examples/bergamo_ophys_session.json b/examples/bergamo_ophys_session.json index 808e14394..61910c681 100644 --- a/examples/bergamo_ophys_session.json +++ b/examples/bergamo_ophys_session.json @@ -9,7 +9,7 @@ "session_end_time": "2022-07-12T07:00:00Z", "session_type": "BCI Photometry", "iacuc_protocol": "2115", - "rig_id": "ophys_rig", + "instrument_id": "ophys_inst", "calibrations": [], "maintenance": [], "subject_id": "652567", diff --git a/examples/ephys_instrument.json b/examples/ephys_instrument.json new file mode 100644 index 000000000..080fd1fe6 --- /dev/null +++ b/examples/ephys_instrument.json @@ -0,0 +1,898 @@ +{ + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "1.0.5", + "instrument_id": "323_EPHYS1_20231003", + "mouse_platform": { + "device_type": "Disc", + "name": "Running Wheel", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "surface_material": null, + "date_surface_replaced": null, + "radius": "15", + "radius_unit": "centimeter", + "output": null, + "encoder": null, + "decoder": null, + "encoder_firmware": null + }, + "modification_date": "2023-10-03", + "calibrations": [ + { + "calibration_date": "2023-10-02T10:22:13Z", + "device_name": "Red Laser", + "description": "Laser power calibration", + "input": { + "power percent": [ + 10, + 20, + 40 + ] + }, + "output": { + "power mW": [ + 1, + 3, + 6 + ] + }, + "notes": null + }, + { + "calibration_date": "2023-10-02T10:22:13Z", + "device_name": "Blue Laser", + "description": "Laser power calibration", + "input": { + "power percent": [ + 10, + 20, + 40 + ] + }, + "output": { + "power mW": [ + 1, + 2, + 7 + ] + }, + "notes": null + } + ], + "ccf_coordinate_transform": null, + "origin": null, + "instrument_axes": null, + "modalities": [ + { + "name": "Extracellular electrophysiology", + "abbreviation": "ecephys" + } + ], + "com_ports": [], + "instrument_type": null, + "manufacturer": null, + "temperature_control": null, + "notes": null, + "connections": [], + "components": [ + { + "name": "Ephys_assemblyA", + "device_type": "Ephys assembly", + "manipulator": { + "device_type": "Manipulator", + "name": "Manipulator 1", + "serial_number": "SN2938", + "manufacturer": { + "name": "New Scale Technologies", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "probes": [ + { + "device_type": "Ephys probe", + "name": "Probe A", + "serial_number": "9291019", + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "probe_model": "Neuropixels 1.0", + "lasers": [], + "headstage": null + } + ] + }, + { + "name": "Ephys_assemblyB", + "device_type": "Ephys assembly", + "manipulator": { + "device_type": "Manipulator", + "name": "Manipulator B", + "serial_number": "SN2939", + "manufacturer": { + "name": "New Scale Technologies", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "probes": [ + { + "device_type": "Ephys probe", + "name": "Probe B", + "serial_number": "9291020", + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "probe_model": "Neuropixels 1.0", + "lasers": [], + "headstage": null + } + ] + }, + { + "name": "Face Camera Assembly", + "device_type": "Camera assembly", + "camera_target": "Face side left", + "camera": { + "device_type": "Detector", + "name": "Face Camera", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72941", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Monochrome", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Camera lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": "15", + "focal_length_unit": "millimeter", + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": "f/2" + }, + "filter": { + "device_type": "Filter", + "name": "LP filter", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Long pass", + "diameter": null, + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": "850 nm longpass filter" + }, + "position": null + }, + { + "name": "Body Camera Assembly", + "device_type": "Camera assembly", + "camera_target": "Body", + "camera": { + "device_type": "Detector", + "name": "Body Camera", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72941", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Monochrome", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Camera lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": "15", + "focal_length_unit": "millimeter", + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": "f/2" + }, + "filter": { + "device_type": "Filter", + "name": "LP filter", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Long pass", + "diameter": null, + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": "850 nm longpass filter" + }, + "position": null + }, + { + "name": "Laser_assemblyA", + "device_type": "Laser assembly", + "manipulator": { + "device_type": "Manipulator", + "name": "Manipulator A", + "serial_number": "SN2937", + "manufacturer": { + "name": "New Scale Technologies", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lasers": [ + { + "device_type": "Laser", + "name": "Red Laser", + "serial_number": null, + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 473, + "wavelength_unit": "nanometer", + "maximum_power": null, + "power_unit": "milliwatt", + "coupling": null, + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Blue Laser", + "serial_number": null, + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 638, + "wavelength_unit": "nanometer", + "maximum_power": null, + "power_unit": "milliwatt", + "coupling": null, + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + } + ], + "collimator": { + "device_type": "device", + "name": "Collimator A", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "fiber": { + "device_type": "Patch", + "name": "Bundle Branching Fiber-optic Patch Cord", + "serial_number": null, + "manufacturer": { + "name": "Doric", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "059n53q30" + }, + "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "core_diameter": "200", + "numerical_aperture": "0.37", + "photobleaching_date": null + } + }, + { + "device_type": "Neuropixels basestation", + "name": "Basestation Slot 3", + "serial_number": null, + "manufacturer": { + "name": "Interuniversity Microelectronics Center", + "abbreviation": "IMEC", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "02kcbn207" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "PXI", + "computer_name": "W10DT72942", + "channels": [], + "firmware_version": null, + "hardware_version": null, + "basestation_firmware_version": "2.019", + "bsc_firmware_version": "2.199", + "slot": 3, + "ports": [ + { + "index": 1, + "probes": [ + "Probe A" + ] + }, + { + "index": 2, + "probes": [ + "Probe B" + ] + } + ] + }, + { + "device_type": "Harp device", + "name": "Harp Behavior", + "serial_number": null, + "manufacturer": { + "name": "Open Ephys Production Site", + "abbreviation": "OEPS", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "007rkz355" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "USB", + "computer_name": "W10DT72941", + "channels": [ + { + "channel_name": "DO0", + "device_name": "Face Camera", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DO1", + "device_name": "Body Camera", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "AI0", + "device_name": "Running Wheel", + "channel_type": "Analog Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + } + ], + "firmware_version": null, + "hardware_version": null, + "harp_device_type": { + "whoami": 1216, + "name": "Behavior" + }, + "core_version": "2.1", + "tag_version": null, + "is_clock_generator": false + }, + { + "name": "Stick_assembly_1", + "device_type": "Camera assembly", + "camera_target": "Brain surface", + "camera": { + "device_type": "Detector", + "name": "stick microscope 1", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72942", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Probe lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": null + }, + "filter": null, + "position": null + }, + { + "name": "Stick_assembly_2", + "device_type": "Camera assembly", + "camera_target": "Brain surface", + "camera": { + "device_type": "Detector", + "name": "stick microscope 2", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72942", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Probe lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": null + }, + "filter": null, + "position": null + }, + { + "name": "Stick_assembly_3", + "device_type": "Camera assembly", + "camera_target": "Brain surface", + "camera": { + "device_type": "Detector", + "name": "stick microscope 3", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72942", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Probe lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": null + }, + "filter": null, + "position": null + }, + { + "name": "Stick_assembly_4", + "device_type": "Camera assembly", + "camera_target": "Brain surface", + "camera": { + "device_type": "Detector", + "name": "stick microscope 4", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72942", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Probe lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": null + }, + "filter": null, + "position": null + } + ] +} \ No newline at end of file diff --git a/examples/ephys_session.json b/examples/ephys_session.json index b33ffece5..4da8e3a8f 100644 --- a/examples/ephys_session.json +++ b/examples/ephys_session.json @@ -10,7 +10,7 @@ "session_end_time": "2023-04-25T03:16:00Z", "session_type": "Receptive field mapping", "iacuc_protocol": "2109", - "rig_id": "323_EPHYS1_20231003", + "instrument_id": "323_EPHYS1_20231003", "calibrations": [], "maintenance": [], "subject_id": "664484", diff --git a/examples/exaspim_instrument.json b/examples/exaspim_instrument.json new file mode 100644 index 000000000..c05ec822c --- /dev/null +++ b/examples/exaspim_instrument.json @@ -0,0 +1,452 @@ +{ + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "1.0.5", + "instrument_id": "440_exaSPIM1_20231004", + "mouse_platform": null, + "modification_date": "2023-10-04", + "calibrations": null, + "ccf_coordinate_transform": null, + "origin": null, + "instrument_axes": null, + "modalities": [ + { + "name": "Selective plane illumination microscopy", + "abbreviation": "SPIM" + } + ], + "com_ports": [ + { + "hardware_name": "Laser Launch", + "com_port": "COM2" + }, + { + "hardware_name": "ASI Tiger", + "com_port": "COM5" + } + ], + "instrument_type": "exaSPIM", + "manufacturer": { + "name": "Custom", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "temperature_control": false, + "notes": null, + "connections": [], + "components": [ + { + "device_type": "Objective", + "name": "Custom Objective", + "serial_number": null, + "manufacturer": { + "name": "Other", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "JM_DIAMOND 5.0X/1.3", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "manufacturer collaboration between Schneider-Kreuznach and Vieworks", + "numerical_aperture": "0.305", + "magnification": "5", + "immersion": "air", + "objective_type": null + }, + { + "device_type": "Detector", + "name": "Camera 1", + "serial_number": "MB151BAY001", + "manufacturer": { + "name": "Vieworks", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "VNP-604MX", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "Coax", + "cooling": "Air", + "computer_name": null, + "frame_rate": null, + "frame_rate_unit": null, + "immersion": null, + "chroma": null, + "sensor_width": null, + "sensor_height": null, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + { + "device_type": "Laser", + "name": "LAS-08307", + "serial_number": "LAS-08307", + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Housed in commercial laser combiner", + "wavelength": 405, + "wavelength_unit": "nanometer", + "maximum_power": "200", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "LAS-08308", + "serial_number": "LAS-08308", + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Housed in commercial laser combiner", + "wavelength": 488, + "wavelength_unit": "nanometer", + "maximum_power": "200", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "539251", + "serial_number": "539251", + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Housed in commercial laser combiner", + "wavelength": 561, + "wavelength_unit": "nanometer", + "maximum_power": "200", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "LAS-08309", + "serial_number": "LAS-08309", + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Housed in commercial laser combiner", + "wavelength": 638, + "wavelength_unit": "nanometer", + "maximum_power": "200", + "power_unit": "milliwatt", + "coupling": "Single-mode fiber", + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Filter", + "name": "Multiband filter", + "serial_number": null, + "manufacturer": { + "name": "Chroma", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "ZET405/488/561/640mv2", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Custom made filter", + "filter_type": "Multiband", + "diameter": "44.05", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": "1", + "thickness_unit": "millimeter", + "filter_wheel_index": 0, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "DAQ Device", + "name": "Dev2", + "serial_number": null, + "manufacturer": { + "name": "National Instruments", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "026exqw73" + }, + "model": "PCIe-6738", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "USB", + "computer_name": "Dev2", + "channels": [ + { + "channel_name": "3", + "device_name": "LAS-08308", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + }, + { + "channel_name": "5", + "device_name": "539251", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + }, + { + "channel_name": "4", + "device_name": "LAS-08309", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + }, + { + "channel_name": "2", + "device_name": "stage-x", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + }, + { + "channel_name": "0", + "device_name": "TL-1", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + }, + { + "channel_name": "6", + "device_name": "LAS-08307", + "channel_type": "Analog Output", + "port": null, + "channel_index": null, + "sample_rate": "10000", + "sample_rate_unit": "hertz", + "event_based_sampling": null + } + ], + "firmware_version": null, + "hardware_version": null + }, + { + "device_type": "Scanning stage", + "name": "stage-x", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "MS-8000", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "1000", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Detection axis", + "stage_axis_name": "X" + }, + { + "device_type": "Scanning stage", + "name": "stage-y", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "MS-8000", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "1000", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Perpendicular axis", + "stage_axis_name": "Y" + }, + { + "device_type": "Scanning stage", + "name": "stage-z", + "serial_number": null, + "manufacturer": { + "name": "Applied Scientific Instrumentation", + "abbreviation": "ASI", + "registry": null, + "registry_identifier": null + }, + "model": "LS-100", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "100", + "travel_unit": "millimeter", + "firmware": null, + "stage_axis_direction": "Illumination axis", + "stage_axis_name": "Z" + }, + { + "device_type": "Additional imaging device", + "name": "TL-1", + "serial_number": "01", + "manufacturer": { + "name": "Optotune", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "EL-16-40-TC-VIS-20D-C", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "imaging_device_type": "Tunable lens" + }, + { + "device_type": "Additional imaging device", + "name": "RM-1", + "serial_number": "01", + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "K10CR1", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "imaging_device_type": "Rotation mount" + }, + { + "device_type": "Additional imaging device", + "name": "LC-1", + "serial_number": "L6CC-00513", + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "L6Cc", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "imaging_device_type": "Laser combiner" + }, + { + "device_type": "Optical table", + "name": "Table", + "serial_number": null, + "manufacturer": { + "name": "MKS Newport", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "00k17f049" + }, + "model": "VIS3648-PG2-325A", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "length": "36", + "width": "48", + "table_size_unit": "inch", + "vibration_control": true + } + ] +} \ No newline at end of file diff --git a/examples/fip_behavior_instrument.json b/examples/fip_behavior_instrument.json new file mode 100644 index 000000000..788678190 --- /dev/null +++ b/examples/fip_behavior_instrument.json @@ -0,0 +1,965 @@ +{ + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "1.0.5", + "instrument_id": "447_FIP-Behavior_20000101", + "mouse_platform": { + "device_type": "Tube", + "name": "mouse_tube_foraging", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "surface_material": null, + "date_surface_replaced": null, + "diameter": "4.0", + "diameter_unit": "centimeter" + }, + "modification_date": "2000-01-01", + "calibrations": [ + { + "calibration_date": "2023-10-02T03:15:22Z", + "device_name": "470nm LED", + "description": "LED calibration", + "input": { + "Power setting": [ + 0 + ] + }, + "output": { + "Power mW": [ + 0.02 + ] + }, + "notes": null + }, + { + "calibration_date": "2023-10-02T03:15:22Z", + "device_name": "415nm LED", + "description": "LED calibration", + "input": { + "Power setting": [ + 0 + ] + }, + "output": { + "Power mW": [ + 0.02 + ] + }, + "notes": null + }, + { + "calibration_date": "2023-10-02T03:15:22Z", + "device_name": "560nm LED", + "description": "LED calibration", + "input": { + "Power setting": [ + 0 + ] + }, + "output": { + "Power mW": [ + 0.02 + ] + }, + "notes": null + } + ], + "ccf_coordinate_transform": null, + "origin": null, + "instrument_axes": null, + "modalities": [ + { + "name": "Behavior", + "abbreviation": "behavior" + }, + { + "name": "Fiber photometry", + "abbreviation": "fib" + } + ], + "com_ports": [], + "instrument_type": null, + "manufacturer": null, + "temperature_control": null, + "notes": null, + "connections": [], + "components": [ + { + "name": "BehaviorVideography_FaceSide", + "device_type": "Camera assembly", + "camera_target": "Face side left", + "camera": { + "device_type": "Detector", + "name": "Side face camera", + "serial_number": "TBD", + "manufacturer": { + "name": "Ailipu Technology Co", + "abbreviation": "Ailipu", + "registry": null, + "registry_identifier": null + }, + "model": "ELP-USBFHD05MT-KL170IR", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "The light intensity sensor was removed; IR illumination is constantly on", + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": "W10DTJK7N0M3", + "frame_rate": "120", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 640, + "sensor_height": 480, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": null, + "bin_mode": "Additive", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": { + "name": "Bonsai", + "version": "2.5", + "url": null, + "parameters": {} + }, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Xenocam 1", + "serial_number": "unknown", + "manufacturer": { + "name": "Other", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "XC0922LENS", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Focal Length 9-22mm 1/3\" IR F1.4", + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": "f/1.4" + }, + "filter": null, + "position": null + }, + { + "name": "BehaviorVideography_FaceBottom", + "device_type": "Camera assembly", + "camera_target": "Face bottom", + "camera": { + "device_type": "Detector", + "name": "Bottom face Camera", + "serial_number": "TBD", + "manufacturer": { + "name": "Ailipu Technology Co", + "abbreviation": "Ailipu", + "registry": null, + "registry_identifier": null + }, + "model": "ELP-USBFHD05MT-KL170IR", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "The light intensity sensor was removed; IR illumination is constantly on", + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": "W10DTJK7N0M3", + "frame_rate": "120", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 640, + "sensor_height": 480, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": null, + "bin_mode": "Additive", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": { + "name": "Bonsai", + "version": "2.5", + "url": null, + "parameters": {} + }, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Xenocam 2", + "serial_number": "unknown", + "manufacturer": { + "name": "Other", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "XC0922LENS", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Focal Length 9-22mm 1/3\" IR F1.4", + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": "f/1.4" + }, + "filter": null, + "position": null + }, + { + "device_type": "Harp device", + "name": "Harp Behavior", + "serial_number": null, + "manufacturer": { + "name": "Open Ephys Production Site", + "abbreviation": "OEPS", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "007rkz355" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "USB", + "computer_name": "behavior_computer", + "channels": [ + { + "channel_name": "DO0", + "device_name": "Solenoid Left", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DO1", + "device_name": "Solenoid Right", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DI0", + "device_name": "Janelia_Lick_Detector Left", + "channel_type": "Digital Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DI1", + "device_name": "Janelia_Lick_Detector Right", + "channel_type": "Digital Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DI3", + "device_name": "Photometry Clock", + "channel_type": "Digital Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + } + ], + "firmware_version": "FTDI version:", + "hardware_version": null, + "harp_device_type": { + "whoami": 1216, + "name": "Behavior" + }, + "core_version": "2.1", + "tag_version": null, + "is_clock_generator": false + }, + { + "device_type": "Reward delivery", + "stage_type": { + "device_type": "Motorized stage", + "name": "NewScaleMotor for LickSpouts", + "serial_number": "xxxx", + "manufacturer": { + "name": "New Scale Technologies", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "travel": "15.0", + "travel_unit": "millimeter", + "firmware": "https://github.com/AllenNeuralDynamics/python-newscale,branch: axes-on-target,commit #7c17497" + }, + "reward_spouts": [ + { + "device_type": "Reward spout", + "name": "Left spout", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "side": "Left", + "spout_diameter": "1.2", + "spout_diameter_unit": "millimeter", + "spout_position": null, + "solenoid_valve": { + "device_type": "device", + "name": "Solenoid Left", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor": { + "device_type": "device", + "name": "Janelia_Lick_Detector Left", + "serial_number": null, + "manufacturer": { + "name": "Janelia Research Campus", + "abbreviation": "Janelia", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "013sk6x84" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor_type": "Capacitive" + }, + { + "device_type": "Reward spout", + "name": "Right spout", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "side": "Right", + "spout_diameter": "1.2", + "spout_diameter_unit": "millimeter", + "spout_position": null, + "solenoid_valve": { + "device_type": "device", + "name": "Solenoid Right", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor": { + "device_type": "device", + "name": "Janelia_Lick_Detector Right", + "serial_number": null, + "manufacturer": { + "name": "Janelia Research Campus", + "abbreviation": "Janelia", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "013sk6x84" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor_type": "Capacitive" + } + ] + }, + { + "device_type": "Patch", + "name": "Bundle Branching Fiber-optic Patch Cord", + "serial_number": null, + "manufacturer": { + "name": "Doric", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "059n53q30" + }, + "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "core_diameter": "200", + "numerical_aperture": "0.37", + "photobleaching_date": null + }, + { + "device_type": "Light emitting diode", + "name": "470nm LED", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "M470F3", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 470, + "wavelength_unit": "nanometer", + "bandwidth": null, + "bandwidth_unit": null + }, + { + "device_type": "Light emitting diode", + "name": "415nm LED", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "M415F3", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 415, + "wavelength_unit": "nanometer", + "bandwidth": null, + "bandwidth_unit": null + }, + { + "device_type": "Light emitting diode", + "name": "565nm LED", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "M565F3", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 565, + "wavelength_unit": "nanometer", + "bandwidth": null, + "bandwidth_unit": null + }, + { + "device_type": "Detector", + "name": "Green CMOS", + "serial_number": "21396991", + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": "BFS-U3-20S40M", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": null, + "frame_rate": null, + "frame_rate_unit": null, + "immersion": "air", + "chroma": "Monochrome", + "sensor_width": null, + "sensor_height": null, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": 16, + "bin_mode": "Additive", + "bin_width": 4, + "bin_height": 4, + "bin_unit": "pixel", + "gain": "2", + "crop_offset_x": 0, + "crop_offset_y": 0, + "crop_width": 200, + "crop_height": 200, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + { + "device_type": "Detector", + "name": "Red CMOS", + "serial_number": "21396991", + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": "BFS-U3-20S40M", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": null, + "frame_rate": null, + "frame_rate_unit": null, + "immersion": "air", + "chroma": "Monochrome", + "sensor_width": null, + "sensor_height": null, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": 16, + "bin_mode": "Additive", + "bin_width": 4, + "bin_height": 4, + "bin_unit": "pixel", + "gain": "2", + "crop_offset_x": 0, + "crop_offset_y": 0, + "crop_width": 200, + "crop_height": 200, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + { + "device_type": "Objective", + "name": "Objective", + "serial_number": "128022336", + "manufacturer": { + "name": "Nikon", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "0280y9h11" + }, + "model": "CFI Plan Apochromat Lambda D 10x", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "numerical_aperture": "0.45", + "magnification": "10", + "immersion": "air", + "objective_type": null + }, + { + "device_type": "Filter", + "name": "Green emission filter", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF01-520/35-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": 520, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Red emission filter", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF01-600/37-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": 600, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Emission Dichroic", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF562-Di03-25x36", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Dichroic", + "diameter": null, + "width": "36", + "height": "25", + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": 562, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "dual-edge standard epi-fluorescence dichroic beamsplitter", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF493/574-Di01-25x36", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", + "filter_type": "Multiband", + "diameter": null, + "width": "36", + "height": "24", + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Excitation filter 410nm", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "FB410-10", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": 410, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Excitation filter 470nm", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "FB470-10", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": 470, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Excitation filter 560nm", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "FB560-10", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": 560, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "450 Dichroic Longpass Filter", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": "#69-898", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Dichroic", + "diameter": null, + "width": "35.6", + "height": "25.2", + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": 450, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "500 Dichroic Longpass Filter", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": "#69-899", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Dichroic", + "diameter": null, + "width": "35.6", + "height": "23.2", + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": 500, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Lens", + "name": "Image focusing lens", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "AC254-080-A-ML", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": "80", + "focal_length_unit": "millimeter", + "size": 1, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": null + }, + { + "device_type": "device", + "name": "Photometry Clock", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + } + ] +} \ No newline at end of file diff --git a/examples/fip_ophys_instrument.json b/examples/fip_ophys_instrument.json new file mode 100644 index 000000000..1de8db8ed --- /dev/null +++ b/examples/fip_ophys_instrument.json @@ -0,0 +1,903 @@ +{ + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "1.0.5", + "instrument_id": "428_FIP1_20231003", + "mouse_platform": { + "device_type": "Disc", + "name": "mouse_disc", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "surface_material": null, + "date_surface_replaced": null, + "radius": "8.5", + "radius_unit": "centimeter", + "output": null, + "encoder": null, + "decoder": null, + "encoder_firmware": null + }, + "modification_date": "2023-10-03", + "calibrations": [ + { + "calibration_date": "2023-10-02T03:15:22Z", + "device_name": "470nm LED", + "description": "LED calibration", + "input": { + "Power setting": [ + 1, + 2, + 3 + ] + }, + "output": { + "Power mW": [ + 5, + 10, + 13 + ] + }, + "notes": null + } + ], + "ccf_coordinate_transform": null, + "origin": null, + "instrument_axes": null, + "modalities": [ + { + "name": "Fiber photometry", + "abbreviation": "fib" + } + ], + "com_ports": [], + "instrument_type": null, + "manufacturer": null, + "temperature_control": null, + "notes": null, + "connections": [], + "components": [ + { + "name": "BehaviorVideography_FaceSide", + "device_type": "Camera assembly", + "camera_target": "Face side left", + "camera": { + "device_type": "Detector", + "name": "Side face camera", + "serial_number": "TBD", + "manufacturer": { + "name": "Ailipu Technology Co", + "abbreviation": "Ailipu", + "registry": null, + "registry_identifier": null + }, + "model": "ELP-USBFHD05MT-KL170IR", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "The light intensity sensor was removed; IR illumination is constantly on", + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": "W10DTJK7N0M3", + "frame_rate": "120", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 640, + "sensor_height": 480, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": null, + "bin_mode": "Additive", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": { + "name": "Bonsai", + "version": "2.5", + "url": null, + "parameters": {} + }, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Xenocam 1", + "serial_number": "unknown", + "manufacturer": { + "name": "Other", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "XC0922LENS", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Focal Length 9-22mm 1/3\" IR F1.4", + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": "f/1.4" + }, + "filter": null, + "position": null + }, + { + "name": "BehaviorVideography_FaceBottom", + "device_type": "Camera assembly", + "camera_target": "Face bottom", + "camera": { + "device_type": "Detector", + "name": "Bottom face Camera", + "serial_number": "TBD", + "manufacturer": { + "name": "Ailipu Technology Co", + "abbreviation": "Ailipu", + "registry": null, + "registry_identifier": null + }, + "model": "ELP-USBFHD05MT-KL170IR", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "The light intensity sensor was removed; IR illumination is constantly on", + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": "W10DTJK7N0M3", + "frame_rate": "120", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 640, + "sensor_height": 480, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": null, + "bin_mode": "Additive", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": { + "name": "Bonsai", + "version": "2.5", + "url": null, + "parameters": {} + }, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Xenocam 2", + "serial_number": "unknown", + "manufacturer": { + "name": "Other", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "XC0922LENS", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "Focal Length 9-22mm 1/3\" IR F1.4", + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": "f/1.4" + }, + "filter": null, + "position": null + }, + { + "device_type": "Patch", + "name": "Bundle Branching Fiber-optic Patch Cord", + "serial_number": null, + "manufacturer": { + "name": "Doric", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "059n53q30" + }, + "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "core_diameter": "200", + "numerical_aperture": "0.37", + "photobleaching_date": null + }, + { + "device_type": "Light emitting diode", + "name": "470nm LED", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "M470F3", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 470, + "wavelength_unit": "nanometer", + "bandwidth": null, + "bandwidth_unit": null + }, + { + "device_type": "Light emitting diode", + "name": "415nm LED", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "M415F3", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 415, + "wavelength_unit": "nanometer", + "bandwidth": null, + "bandwidth_unit": null + }, + { + "device_type": "Light emitting diode", + "name": "565nm LED", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "M565F3", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 565, + "wavelength_unit": "nanometer", + "bandwidth": null, + "bandwidth_unit": null + }, + { + "device_type": "Detector", + "name": "Green CMOS", + "serial_number": "21396991", + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": "BFS-U3-20S40M", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": null, + "frame_rate": null, + "frame_rate_unit": null, + "immersion": "air", + "chroma": "Monochrome", + "sensor_width": null, + "sensor_height": null, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": 16, + "bin_mode": "Additive", + "bin_width": 4, + "bin_height": 4, + "bin_unit": "pixel", + "gain": "2", + "crop_offset_x": 0, + "crop_offset_y": 0, + "crop_width": 200, + "crop_height": 200, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + { + "device_type": "Detector", + "name": "Red CMOS", + "serial_number": "21396991", + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": "BFS-U3-20S40M", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "Air", + "computer_name": null, + "frame_rate": null, + "frame_rate_unit": null, + "immersion": "air", + "chroma": "Monochrome", + "sensor_width": null, + "sensor_height": null, + "size_unit": "pixel", + "sensor_format": null, + "sensor_format_unit": null, + "bit_depth": 16, + "bin_mode": "Additive", + "bin_width": 4, + "bin_height": 4, + "bin_unit": "pixel", + "gain": "2", + "crop_offset_x": 0, + "crop_offset_y": 0, + "crop_width": 200, + "crop_height": 200, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + { + "device_type": "Objective", + "name": "Objective", + "serial_number": "128022336", + "manufacturer": { + "name": "Nikon", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "0280y9h11" + }, + "model": "CFI Plan Apochromat Lambda D 10x", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "numerical_aperture": "0.45", + "magnification": "10", + "immersion": "air", + "objective_type": null + }, + { + "device_type": "Filter", + "name": "Green emission filter", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF01-520/35-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": 520, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Red emission filter", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF01-600/37-25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": 600, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Emission Dichroic", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF562-Di03-25x36", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Dichroic", + "diameter": null, + "width": "36", + "height": "25", + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": 562, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "dual-edge standard epi-fluorescence dichroic beamsplitter", + "serial_number": null, + "manufacturer": { + "name": "Semrock", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": "FF493/574-Di01-25x36", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": "493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", + "filter_type": "Multiband", + "diameter": null, + "width": "36", + "height": "24", + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Excitation filter 410nm", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "FB410-10", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": 410, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Excitation filter 470nm", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "FB470-10", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": 470, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "Excitation filter 560nm", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "FB560-10", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Band pass", + "diameter": "25", + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": 560, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "450 Dichroic Longpass Filter", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": "#69-898", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Dichroic", + "diameter": null, + "width": "35.6", + "height": "25.2", + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": 450, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Filter", + "name": "500 Dichroic Longpass Filter", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": "#69-899", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Dichroic", + "diameter": null, + "width": "35.6", + "height": "23.2", + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": 500, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": null + }, + { + "device_type": "Lens", + "name": "Image focusing lens", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": "AC254-080-A-ML", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": "80", + "focal_length_unit": "millimeter", + "size": 1, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": null + }, + { + "device_type": "Harp device", + "name": "Harp Behavior", + "serial_number": null, + "manufacturer": { + "name": "Open Ephys Production Site", + "abbreviation": "OEPS", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "007rkz355" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "USB", + "computer_name": "behavior_computer", + "channels": [ + { + "channel_name": "DO0", + "device_name": "Solenoid Left", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DO1", + "device_name": "Solenoid Right", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DI0", + "device_name": "Lick-o-meter Left", + "channel_type": "Digital Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DI1", + "device_name": "Lick-o-meter Right", + "channel_type": "Digital Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DI3", + "device_name": "Photometry Clock", + "channel_type": "Digital Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + } + ], + "firmware_version": null, + "hardware_version": null, + "harp_device_type": { + "whoami": 1216, + "name": "Behavior" + }, + "core_version": "2.1", + "tag_version": null, + "is_clock_generator": false + }, + { + "device_type": "Reward delivery", + "stage_type": null, + "reward_spouts": [ + { + "device_type": "Reward spout", + "name": "Left spout", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "side": "Left", + "spout_diameter": "1.2", + "spout_diameter_unit": "millimeter", + "spout_position": null, + "solenoid_valve": { + "device_type": "device", + "name": "Solenoid Left", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor": { + "device_type": "device", + "name": "Lick-o-meter Left", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor_type": null + }, + { + "device_type": "Reward spout", + "name": "Right spout", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "side": "Right", + "spout_diameter": "1.2", + "spout_diameter_unit": "millimeter", + "spout_position": null, + "solenoid_valve": { + "device_type": "device", + "name": "Solenoid Right", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor": { + "device_type": "device", + "name": "Lick-o-meter Right", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lick_sensor_type": null + } + ] + }, + { + "device_type": "device", + "name": "Photometry Clock", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + } + ] +} \ No newline at end of file diff --git a/examples/fip_ophys_rig.py b/examples/fip_ophys_rig.py index f65cef664..50ec31906 100644 --- a/examples/fip_ophys_rig.py +++ b/examples/fip_ophys_rig.py @@ -327,6 +327,6 @@ calibrations=[calibration], ) -serialized = inst.model_dump_json() +serialized = instrument.model_dump_json() deserialized = r.Instrument.model_validate_json(serialized) deserialized.write_standard_file(prefix="fip_ophys") diff --git a/examples/mri_session.json b/examples/mri_session.json index 8ced0dc2a..78bfe2fc2 100644 --- a/examples/mri_session.json +++ b/examples/mri_session.json @@ -11,7 +11,7 @@ "session_end_time": "2024-03-12T16:27:55.584892Z", "session_type": "3D MRI Volume", "iacuc_protocol": "1234", - "rig_id": "NA", + "instrument_id": "NA", "calibrations": [], "maintenance": [], "subject_id": "123456", diff --git a/examples/multiplane_ophys_session.json b/examples/multiplane_ophys_session.json index 44cd206fe..ed8a64d7e 100644 --- a/examples/multiplane_ophys_session.json +++ b/examples/multiplane_ophys_session.json @@ -9,7 +9,7 @@ "session_end_time": "2022-07-12T07:00:00Z", "session_type": "Mesoscope", "iacuc_protocol": "12345", - "rig_id": "MESO.1", + "instrument_id": "MESO.1", "calibrations": [], "maintenance": [], "subject_id": "12345", diff --git a/examples/ophys_session.json b/examples/ophys_session.json index 0e70a88e4..bd0e8d436 100644 --- a/examples/ophys_session.json +++ b/examples/ophys_session.json @@ -9,7 +9,7 @@ "session_end_time": "2022-07-12T07:00:00Z", "session_type": "Parameter Testing", "iacuc_protocol": "2115", - "rig_id": "ophys_rig", + "instrument_id": "ophys_inst", "calibrations": [], "maintenance": [], "subject_id": "652567", diff --git a/tests/test_examples.py b/tests/test_examples.py index 8e8e5d4f2..57701dc0e 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -18,7 +18,10 @@ class ExampleTests(unittest.TestCase): """tests for examples""" def test_examples(self): - """run through each example, compare to rendered json""" + """run through each example + + Check that the existing examples/*.json files match what gets generated from .write_standard_file() + """ for example_file in glob.glob(f"{EXAMPLES_DIR}/*.py"): logging.debug(f"testing {example_file}") diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 28f369659..3e0fd4150 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -14,7 +14,7 @@ from pydantic import ValidationError from pydantic import __version__ as pyd_version -from aind_data_schema.components.devices import EphysAssembly, EphysProbe, Manipulator, MousePlatform +from aind_data_schema.components.devices import EphysAssembly, EphysProbe, Manipulator, MousePlatform, Objective from aind_data_schema.core.acquisition import Acquisition from aind_data_schema.core.data_description import DataDescription, Funding from aind_data_schema.core.metadata import ExternalPlatforms, Metadata, MetadataStatus, create_metadata_json @@ -243,7 +243,7 @@ def test_validate_smartspim_metadata(self): subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), acquisition=Acquisition.model_construct(), - instrument=Instrument.model_construct(modalities=[Modality.SPIM]), + instrument=Instrument.model_construct(modalities=[Modality.SPIM], components=[Objective.model_construct()]), processing=Processing.model_construct(), ) self.assertIn("Injection is missing injection_materials.", str(context.exception)) From d51a3fb797130084517e718ff9ec37bbb2001a5b Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Fri, 17 Jan 2025 10:53:22 -0800 Subject: [PATCH 08/34] fix: update to support aind-data-schema-models v1 --- src/aind_data_schema/core/procedures.py | 10 +++++----- tests/test_device.py | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/aind_data_schema/core/procedures.py b/src/aind_data_schema/core/procedures.py index 9db2acb8b..8f3d906d5 100644 --- a/src/aind_data_schema/core/procedures.py +++ b/src/aind_data_schema/core/procedures.py @@ -5,7 +5,7 @@ from enum import Enum from typing import List, Literal, Optional, Set, Union -from aind_data_schema_models.mouse_anatomy import MouseAnatomicalStructure +from aind_data_schema_models.mouse_anatomy import MouseAnatomyModel from aind_data_schema_models.organizations import Organization from aind_data_schema_models.pid_names import PIDName from aind_data_schema_models.species import Species @@ -328,7 +328,7 @@ class CatheterImplant(DataModel): catheter_material: CatheterMaterial = Field(..., title="Catheter material") catheter_design: CatheterDesign = Field(..., title="Catheter design") catheter_port: CatheterPort = Field(..., title="Catheter port") - targeted_structure: MouseAnatomicalStructure.BLOOD_VESSELS = Field(..., title="Targeted blood vessel") + targeted_structure: MouseAnatomyModel = Field(..., title="Targeted blood vessel", description="Use options from MouseBloodVessels") class Craniotomy(DataModel): @@ -613,9 +613,9 @@ class WaterRestriction(DataModel): class MyomatrixContact(DataModel): """ "Description of a contact on a myomatrix thread""" - body_part: MouseAnatomicalStructure.BODY_PARTS = Field(..., title="Body part of contact insertion") + body_part: MouseAnatomyModel = Field(..., title="Body part of contact insertion", description="Use MouseBodyParts") side: Side = Field(..., title="Body side") - muscle: MouseAnatomicalStructure.EMG_MUSCLES = Field(..., title="Muscle of contact insertion") + muscle: MouseAnatomyModel = Field(..., title="Muscle of contact insertion", description="Use MouseEmgMuscles") in_muscle: bool = Field(..., title="In muscle") notes: Optional[str] = Field(default=None, title="Notes") @@ -623,7 +623,7 @@ class MyomatrixContact(DataModel): class MyomatrixThread(DataModel): """Description of a thread of a myomatrix array""" - ground_electrode_location: MouseAnatomicalStructure.BODY_PARTS = Field(..., title="Location of ground electrode") + ground_electrode_location: MouseAnatomyModel = Field(..., title="Location of ground electrode", description="Use MouseBodyParts") contacts: List[MyomatrixContact] = Field(..., title="Contacts") diff --git a/tests/test_device.py b/tests/test_device.py index 684cf7f6c..d444b84a0 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -35,11 +35,9 @@ def test_other_validators(self): name="test_reward_spout", spout_diameter=0.5, solenoid_valve=Device( - device_type="solenoid", name="test_solenoid", ), lick_sensor=Device( - device_type="Lick sensor", name="Sensor_test", ), side=SpoutSide.OTHER, @@ -52,7 +50,6 @@ def test_other_validators(self): " [type=value_error, input_value={'name': 'test_reward_spo...outSide.OTHER: 'Other'>}, input_type=dict]\n" f" For further information visit https://errors.pydantic.dev/{PYD_VERSION}/v/value_error" ) - self.assertEqual(repr(e1.exception), expected_e1) with self.assertRaises(ValueError) as e2: @@ -128,3 +125,7 @@ def test_other_validators(self): ) self.assertEqual(repr(e5.exception), expected_e5) + + +if __name__ == "__main__": + unittest.main() From b4b5a897fd959c829ce94493578a09d7510c5fd8 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Fri, 24 Jan 2025 09:47:07 -0800 Subject: [PATCH 09/34] feat: deprecating platform --- docs/source/data_description.rst | 30 ------------ docs/source/data_organization.rst | 20 +++----- .../example_workflow/example_workflow.py | 5 +- examples/data_description.json | 8 +-- examples/data_description.py | 4 +- pyproject.toml | 2 +- src/aind_data_schema/core/data_description.py | 18 ++----- src/aind_data_schema/core/metadata.py | 9 ++-- tests/test_data_description.py | 49 ++++++------------- 9 files changed, 36 insertions(+), 109 deletions(-) diff --git a/docs/source/data_description.rst b/docs/source/data_description.rst index 74aa343db..987e02724 100644 --- a/docs/source/data_description.rst +++ b/docs/source/data_description.rst @@ -11,26 +11,9 @@ data modalities, dates of collection, and more. The data description is created during data transfer based on information you provide to that service and pulling information from internal resources. -**Q: What is the difference between modality and platform?** - -Modalities are types of data being collected. A platform is a standardized way of collecting one or more modalities of -data that we give a name. Platform standardization -- of file formats, hardware setup, etc -- enables us automatically -and reliably process data with centrally managed data pipelines. - - Example 1: the behavior platform leverages Harp and Bonsai to run behavioral experiments and acquire multiple - modalities of data (behavior videos, electrophysiology, photometry, etc). - - Example 2: We use SmartSPIM lightsheet microscopes to collect whole-mesoscale whole brain neuroanatomy data. This - is a single-modality (SPIM) platform (mesoscale anatomy, SmartSPIM colloquially). - Questions for AIND users ------------------------ -**Q: What platform should I use?** - -There is a controlled vocabulary in (aind-data-schema-models)[https://github.com/AllenNeuralDynamics/aind-data-schema-models]. -Pick the one that most closely aligns with how you have collected data. If none exists, talk to Saskia de Vries or David Feng. - **Q: This data is for a AIND project and not part of a grant. Shouldn’t the funder be AIND?** No. The funding for internally funded AIND or AIBS work is listed as “Allen Institute”. @@ -44,16 +27,3 @@ to make sure your grant is on that sheet. In the future we may need to tag cloud resources based on the originating group, which may or may not be in AIND, in order to track usage and spending. - - -**Q: What happened to the “experiment type” asset label? Why are we using platform names instead?** - -Formerly we used a short label called “experiment type” in asset names instead of platform -names. This concept was confusing because it was difficult to distinguish from a “modality”. -Most of our data contains multiple modalities. A recording session may contain trained behavior -event data (e.g. lick times), behavior videos (e.g. face camera), neuropixels recordings, and -fiber photometry recordings. - -Anchoring browsing on data collection platforms is clearer. We will tag sessions in our metadata -database to indicate which modalities are present in which sessions. - \ No newline at end of file diff --git a/docs/source/data_organization.rst b/docs/source/data_organization.rst index 91fd3f1d4..5d7a43108 100644 --- a/docs/source/data_organization.rst +++ b/docs/source/data_organization.rst @@ -86,9 +86,7 @@ name be unique, but we should not use this name to encode essential metadata. All primary data assets have the following naming convention: - ___ - -A platform is a standardized system for collecting one or more modalities of data. + __ A few points: @@ -97,8 +95,7 @@ A few points: - Acquisition date and time are essential for uniqueness - Acquisition date and time are in local time zone - Time-zone is documented in metadata -- All tokens (e.g. ````, ````) must not contain underscores or illegal filename characters. -- ````: a less-than 10 character shorthand for a data acquisition platform +- All tokens (e.g. ````) must not contain underscores or illegal filename characters. Again, this name is strictly for uniqueness. We could use a GUID, but choose to have a relatively simple naming convention to facilitate casual browsing. @@ -123,7 +120,7 @@ Primary data assets are organized as follows: - logs (general log files generated by the instrument or rig that are not modality-specific) - -Platform abbreviation and modality terms come from controlled vocabularies in aind-data-schema-models. +Modality terms come from controlled vocabularies in aind-data-schema-models. Example for simultaneous electrophysiology with optotagging and fiber photometry: @@ -145,9 +142,9 @@ Example for simultaneous electrophysiology with optotagging and fiber photometry - face_camera.mp4 - body_camera.mp4 -Example for lightsheet microscopy data acquired on the ExaSPIM platform: +Example for lightsheet microscopy data: - - exaSPIM_655568_2022-04-26_11-48-09 + - 655568_2022-04-26_11-48-09 - - SPIM - SPIM.ome.zarr @@ -200,10 +197,9 @@ File name guidelines When naming files, we should: - use terms from vocabularies defined in aind-data-schema, e.g. - - platform names and modalities behavior video file names + - modalities, etc + - use isoformat datetimes, e.g. "YYYY-MM-DDThhmmss" - use “yyyy-mm-dd" and “hh-mm-ss" in local time zone for dates and times - separate tokens with underscores, and not include underscores in tokens, e.g. - - Do this: ``EFIP_655568_2022-04-26_11-48-09`` - - Not this: ``EFIP-655568-2022_04_26-11_48_09`` + - Do this: ``EFIP_655568_2022-04-26T114809`` - Do not include illegal filename characters in tokens - diff --git a/docs/source/example_workflow/example_workflow.py b/docs/source/example_workflow/example_workflow.py index 9c597dc5e..9a909c630 100644 --- a/docs/source/example_workflow/example_workflow.py +++ b/docs/source/example_workflow/example_workflow.py @@ -3,7 +3,6 @@ import pandas as pd from aind_data_schema_models.modalities import Modality from aind_data_schema_models.organizations import Organization -from aind_data_schema_models.platforms import Platform from aind_data_schema.core.data_description import Funding, RawDataDescription from aind_data_schema.core.procedures import NanojectInjection, Perfusion, Procedures, Surgery, ViralMaterial @@ -31,13 +30,13 @@ for session_idx, session in sessions_df.iterrows(): # our data always contains planar optical physiology and behavior videos d = RawDataDescription( - modality=[Modality.POPHYS, Modality.BEHAVIOR_VIDEOS], - platform=Platform.BEHAVIOR, + modalities=[Modality.POPHYS, Modality.BEHAVIOR_VIDEOS], subject_id=str(session["mouse_id"]), creation_time=session["end_time"].to_pydatetime(), institution=Organization.OTHER, experimenters=[experimenter], funding_source=[Funding(funder=Organization.NIMH)], + investigators=[experimenter)], ) # we will store our json files in a directory named after the session diff --git a/examples/data_description.json b/examples/data_description.json index 3816da4c9..a5f3ca3b8 100644 --- a/examples/data_description.json +++ b/examples/data_description.json @@ -2,14 +2,10 @@ "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/data_description.py", "schema_version": "1.0.5", "license": "CC-BY-4.0", - "platform": { - "name": "Electrophysiology platform", - "abbreviation": "ecephys" - }, "subject_id": "12345", "creation_time": "2022-02-21T16:30:01Z", "label": null, - "name": "ecephys_12345_2022-02-21_16-30-01", + "name": "12345_2022-02-21_16-30-01", "institution": { "name": "Allen Institute for Neural Dynamics", "abbreviation": "AIND", @@ -48,7 +44,7 @@ ], "project_name": null, "restrictions": null, - "modality": [ + "modalities": [ { "name": "Extracellular electrophysiology", "abbreviation": "ecephys" diff --git a/examples/data_description.py b/examples/data_description.py index d2ea029b3..c371581ab 100644 --- a/examples/data_description.py +++ b/examples/data_description.py @@ -4,14 +4,12 @@ from aind_data_schema_models.modalities import Modality from aind_data_schema_models.organizations import Organization -from aind_data_schema_models.platforms import Platform from aind_data_schema.components.identifiers import Person from aind_data_schema.core.data_description import Funding, RawDataDescription d = RawDataDescription( - modality=[Modality.ECEPHYS, Modality.BEHAVIOR_VIDEOS], - platform=Platform.ECEPHYS, + modalities=[Modality.ECEPHYS, Modality.BEHAVIOR_VIDEOS], subject_id="12345", creation_time=datetime(2022, 2, 21, 16, 30, 1, tzinfo=timezone.utc), institution=Organization.AIND, diff --git a/pyproject.toml b/pyproject.toml index 172b5eaf0..778b032a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ readme = "README.md" dynamic = ["version"] dependencies = [ - 'aind-data-schema-models>=0.5.4, <1.0.0', + 'aind-data-schema-models>=1.0.0', 'dictdiffer', 'pydantic>=2.7', 'inflection', diff --git a/src/aind_data_schema/core/data_description.py b/src/aind_data_schema/core/data_description.py index eba4099c4..265c67728 100644 --- a/src/aind_data_schema/core/data_description.py +++ b/src/aind_data_schema/core/data_description.py @@ -13,7 +13,6 @@ ) from aind_data_schema_models.modalities import Modality from aind_data_schema_models.organizations import Organization -from aind_data_schema_models.platforms import Platform from pydantic import Field, SkipValidation, model_validator from aind_data_schema.base import DataCoreModel, DataModel, AwareDatetimeWithDefault @@ -43,11 +42,6 @@ class DataDescription(DataCoreModel): schema_version: SkipValidation[Literal["1.0.5"]] = Field(default="1.0.5") license: Literal["CC-BY-4.0"] = Field("CC-BY-4.0", title="License") - platform: Platform.ONE_OF = Field( - ..., - description="Name for a standardized primary data collection system", - title="Platform", - ) subject_id: str = Field( ..., pattern=DataRegex.NO_UNDERSCORES.value, @@ -109,11 +103,11 @@ class DataDescription(DataCoreModel): description="Detail any restrictions on publishing or sharing these data", title="Restrictions", ) - modality: List[Modality.ONE_OF] = Field( + modalities: List[Modality.ONE_OF] = Field( ..., description="A short name for the specific manner, characteristic, pattern of application, or the employment" "of any technology or formal procedure to generate data for a study", - title="Modality", + title="Modalities", ) related_data: List[RelatedData] = Field( default=[], @@ -244,7 +238,6 @@ def get_or_default(field_name: str) -> Any: investigators=get_or_default("investigators"), restrictions=get_or_default("restrictions"), modality=get_or_default("modality"), - platform=get_or_default("platform"), project_name=get_or_default("project_name"), subject_id=get_or_default("subject_id"), related_data=get_or_default("related_data"), @@ -263,8 +256,7 @@ class RawDataDescription(DataDescription): @model_validator(mode="after") def build_name(self): """sets the name of the file""" - platform_abbreviation = self.platform.abbreviation - self.name = build_data_name(f"{platform_abbreviation}_{self.subject_id}", creation_datetime=self.creation_time) + self.name = build_data_name(f"{self.subject_id}", creation_datetime=self.creation_time) return self @classmethod @@ -278,11 +270,7 @@ def parse_name(cls, name): creation_time = datetime_from_name_string(m.group("c_date"), m.group("c_time")) - platform_abbreviation = m.group("platform_abbreviation") - platform = Platform.from_abbreviation(platform_abbreviation) - return dict( - platform=platform, subject_id=m.group("subject_id"), creation_time=creation_time, ) diff --git a/src/aind_data_schema/core/metadata.py b/src/aind_data_schema/core/metadata.py index 8613e1bd7..f0d9df1ed 100644 --- a/src/aind_data_schema/core/metadata.py +++ b/src/aind_data_schema/core/metadata.py @@ -8,8 +8,7 @@ from typing import Dict, List, Literal, Optional, get_args from uuid import UUID, uuid4 -from aind_data_schema_models.modalities import ExpectedFiles, FileRequirement -from aind_data_schema_models.platforms import Platform +from aind_data_schema_models.modalities import ExpectedFiles, FileRequirement, Modality from pydantic import ( Field, PrivateAttr, @@ -214,7 +213,7 @@ def validate_metadata(self): def validate_expected_files_by_modality(self): """Validator checks that all required/excluded files match the metadata model""" if self.data_description: - modalities = self.data_description.modality + modalities = self.data_description.modalities requirement_dict = {} @@ -257,7 +256,7 @@ def validate_smartspim_metadata(self): if ( self.data_description - and self.data_description.platform == Platform.SMARTSPIM + and any([modality == Modality.SPIM for modality in self.data_description.modalities]) and self.procedures and any( isinstance(surgery, Injection) and getattr(surgery, "injection_materials", None) is None @@ -275,7 +274,7 @@ def validate_ecephys_metadata(self): """Validator for metadata""" if ( self.data_description - and self.data_description.platform == Platform.ECEPHYS + and any([modality == Modality.ECEPHYS for modality in self.data_description.modalities]) and self.procedures and any( isinstance(surgery, Injection) and getattr(surgery, "injection_materials", None) is None diff --git a/tests/test_data_description.py b/tests/test_data_description.py index 23764ca38..552db8f61 100644 --- a/tests/test_data_description.py +++ b/tests/test_data_description.py @@ -10,7 +10,6 @@ from aind_data_schema_models.modalities import Modality from aind_data_schema_models.organizations import Organization -from aind_data_schema_models.platforms import Platform from pydantic import ValidationError from pydantic import __version__ as pyd_version @@ -58,8 +57,7 @@ def test_constructors(self): institution=Organization.AIND, data_level="raw", funding_source=[f], - modality=[Modality.ECEPHYS], - platform=Platform.ECEPHYS, + modalities=[Modality.ECEPHYS], subject_id="12345", investigators=[Person(name="Jane Smith")], ) @@ -70,8 +68,7 @@ def test_constructors(self): creation_time=dt, institution=Organization.AIND, funding_source=[f], - modality=da.modality, - platform=da.platform, + modalities=da.modalities, subject_id=da.subject_id, investigators=[Person(name="Jane Smith")], ) @@ -82,8 +79,7 @@ def test_constructors(self): creation_time=dt, institution=Organization.AIND, funding_source=[f], - modality=r1.modality, - platform=r1.platform, + modalities=r1.modalities, subject_id="12345", investigators=[Person(name="Jane Smith")], ) @@ -94,8 +90,7 @@ def test_constructors(self): creation_time=dt, institution=Organization.AIND, funding_source=[f], - modality=r2.modality, - platform=r2.platform, + modalities=r2.modalities, subject_id="12345", investigators=[Person(name="Jane Smith")], ) @@ -103,8 +98,7 @@ def test_constructors(self): dd = DataDescription( label="test_data", - modality=[Modality.SPIM], - platform=Platform.EXASPIM, + modalities=[Modality.SPIM], subject_id="1234", data_level="raw", creation_time=dt, @@ -119,8 +113,7 @@ def test_constructors(self): with self.assertRaises(ValidationError): DataDescription( label="test_data", - modality=[Modality.SPIM], - platform="fake platform", + modalities=[Modality.SPIM], subject_id="1234", data_level="raw", creation_time=dt, @@ -134,8 +127,7 @@ def test_constructors(self): project_name="project", creation_time=dt, subject_id="1234", - modality=[Modality.SPIM], - platform=Platform.EXASPIM, + modalities=[Modality.SPIM], institution=Organization.AIND, funding_source=[f], investigators=[Person(name="Jane Smith")], @@ -147,8 +139,7 @@ def test_constructors(self): analysis_name="ana lysis", project_name="pro_ject", subject_id="1234", - modality=[Modality.SPIM], - platform="exaspim", + modalities=[Modality.SPIM], creation_time=dt, institution=Organization.AIND, funding_source=[f], @@ -181,8 +172,7 @@ def test_constructors(self): analysis_name="", project_name="project", subject_id="1234", - modality=[Modality.SPIM], - platform="exaspim", + modalities=[Modality.SPIM], creation_time=dt, institution=Organization.AIND, funding_source=[f], @@ -194,8 +184,7 @@ def test_constructors(self): analysis_name="analysis", project_name="", subject_id="1234", - modality=[Modality.SPIM], - platform="exaspim", + modalities=[Modality.SPIM], creation_time=dt, institution=Organization.AIND, funding_source=[f], @@ -207,8 +196,7 @@ def test_pattern_errors(self): with self.assertRaises(ValidationError) as e: DataDescription( label="test_data", - modality=[Modality.SPIM], - platform=Platform.EXASPIM, + modalities=[Modality.SPIM], subject_id="1234", data_level="raw", project_name="a_32r&!#R$&#", @@ -239,8 +227,7 @@ def test_name_label_error(self): with self.assertRaises(ValidationError) as e: DataDescription( - modality=[Modality.SPIM], - platform=Platform.EXASPIM, + modalities=[Modality.SPIM], subject_id="1234", data_level="raw", creation_time=datetime.datetime(2020, 10, 10, 10, 10, 10), @@ -260,8 +247,7 @@ def test_round_trip(self): institution=Organization.AIND, data_level="raw", funding_source=[Funding(funder=Organization.NINDS, grant_number="grant001")], - modality=[Modality.SPIM], - platform=Platform.EXASPIM, + modalities=[Modality.SPIM], subject_id="12345", investigators=[Person(name="Jane Smith")], ) @@ -281,7 +267,6 @@ def test_parse_name(self): DataDescription.parse_name(self.BAD_NAME) toks = RawDataDescription.parse_name(self.BASIC_NAME) - assert toks["platform"] == Platform.ECEPHYS assert toks["subject_id"] == "1234" assert toks["creation_time"] == datetime.datetime(3033, 12, 21, 4, 22, 11) @@ -308,16 +293,13 @@ def test_unique_abbreviations(self): """Tests that abbreviations are unique""" modality_abbreviations = [m().abbreviation for m in Modality.ALL] self.assertEqual(len(set(modality_abbreviations)), len(modality_abbreviations)) - platform_abbreviations = [p().abbreviation for p in Platform.ALL] - self.assertEqual(len(set(platform_abbreviations)), len(platform_abbreviations)) def test_from_data_description(self): """Tests DerivedDataDescription.from_data_description method""" d1 = DataDescription( label="test_data", - modality=[Modality.SPIM], - platform=Platform.EXASPIM, + modalities=[Modality.SPIM], subject_id="1234", data_level="raw", creation_time=datetime.datetime(2020, 10, 10, 10, 10, 10), @@ -342,8 +324,7 @@ def test_derived_data_description_build_name(self): creation_time=datetime.datetime(2020, 10, 10, 10, 10, 10), institution=Organization.AIND, funding_source=[Funding(funder=Organization.NINDS, grant_number="grant001")], - modality=[Modality.ECEPHYS], - platform=Platform.ECEPHYS, + modalities=[Modality.ECEPHYS], subject_id="12345", investigators=[Person(name="Jane Smith")], ) From 85e2269860fd1e3abdd7f11b4ad600af8d9d5c57 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Fri, 24 Jan 2025 09:50:19 -0800 Subject: [PATCH 10/34] chore: lint --- src/aind_data_schema/core/procedures.py | 8 ++++++-- tests/test_examples.py | 2 +- tests/test_instrument.py | 2 +- tests/test_metadata.py | 11 +++++++++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/aind_data_schema/core/procedures.py b/src/aind_data_schema/core/procedures.py index 324e3b9c9..904880866 100644 --- a/src/aind_data_schema/core/procedures.py +++ b/src/aind_data_schema/core/procedures.py @@ -328,7 +328,9 @@ class CatheterImplant(DataModel): catheter_material: CatheterMaterial = Field(..., title="Catheter material") catheter_design: CatheterDesign = Field(..., title="Catheter design") catheter_port: CatheterPort = Field(..., title="Catheter port") - targeted_structure: MouseAnatomyModel = Field(..., title="Targeted blood vessel", description="Use options from MouseBloodVessels") + targeted_structure: MouseAnatomyModel = Field( + ..., title="Targeted blood vessel", description="Use options from MouseBloodVessels" + ) class Craniotomy(DataModel): @@ -623,7 +625,9 @@ class MyomatrixContact(DataModel): class MyomatrixThread(DataModel): """Description of a thread of a myomatrix array""" - ground_electrode_location: MouseAnatomyModel = Field(..., title="Location of ground electrode", description="Use MouseBodyParts") + ground_electrode_location: MouseAnatomyModel = Field( + ..., title="Location of ground electrode", description="Use MouseBodyParts" + ) contacts: List[MyomatrixContact] = Field(..., title="Contacts") diff --git a/tests/test_examples.py b/tests/test_examples.py index 57701dc0e..dad9c36ec 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -19,7 +19,7 @@ class ExampleTests(unittest.TestCase): def test_examples(self): """run through each example - + Check that the existing examples/*.json files match what gets generated from .write_standard_file() """ diff --git a/tests/test_instrument.py b/tests/test_instrument.py index d4dc180a4..d11b077c1 100644 --- a/tests/test_instrument.py +++ b/tests/test_instrument.py @@ -277,7 +277,7 @@ def test_validator_modality_device_missing(self): components=[], calibrations=[], ) - + def test_validator_modality_device_present(self): """Test that the modality -> device validator does not throw validation errors when devices are present""" diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 8ce4f9470..b75736777 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -238,7 +238,9 @@ def test_validate_smartspim_metadata(self): subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), acquisition=Acquisition.model_construct(), - instrument=Instrument.model_construct(modalities=[Modality.SPIM], components=[Objective.model_construct()]), + instrument=Instrument.model_construct( + modalities=[Modality.SPIM], components=[Objective.model_construct()] + ), processing=Processing.model_construct(), ) self.assertIn("Injection is missing injection_materials.", str(context.exception)) @@ -333,7 +335,12 @@ def test_validate_instrument_session_compatibility(self): ) mouse_platform = MousePlatform.model_construct(name="platform1") - inst = Instrument.model_construct(instrument_id="123_EPHYS1_20220101", mouse_platform=mouse_platform, modalities=[Modality.ECEPHYS], components=[ephys_assembly]) + inst = Instrument.model_construct( + instrument_id="123_EPHYS1_20220101", + mouse_platform=mouse_platform, + modalities=[Modality.ECEPHYS], + components=[ephys_assembly], + ) session = Session.model_construct(instrument_id="123_EPHYS2_20230101", mouse_platform_name="platform2") with self.assertRaises(ValidationError) as context: Metadata( From 5280c39ea97c2cb00ae7791f95591b665c634e02 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Fri, 24 Jan 2025 09:54:46 -0800 Subject: [PATCH 11/34] chore: lint --- README.md | 2 +- src/aind_data_schema/core/instrument.py | 5 +++-- src/aind_data_schema/utils/compatibility_check.py | 9 ++++++--- src/aind_data_schema/utils/diagrams.py | 2 +- src/aind_data_schema/utils/json_writer.py | 2 +- tests/test_inst_acq_compatibility.py | 2 +- tests/test_instrument.py | 3 +-- 7 files changed, 14 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c07055c79..11e6d57d1 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ If you'd like to propose a large change or addition, or generally have a questio ## Controlled Vocabularies -Controlled vocabularies and other enumerated lists are maintained in a separate repository: [aind-data-schema-models](https://github.com/AllenNeuralDynamics/aind-data-schema-models). This allows us to specify these lists without changing aind-data-schema. Controlled vocabularies include lists of organizations, manufacturers, species, modalities, platforms, units, harp devices, and registries. +Controlled vocabularies and other enumerated lists are maintained in a separate repository: [aind-data-schema-models](https://github.com/AllenNeuralDynamics/aind-data-schema-models). This allows us to specify these lists without changing aind-data-schema. Controlled vocabularies include lists of organizations, manufacturers, species, modalities, units, harp devices, and registries. To upgrade to the latest data models version: ``` diff --git a/src/aind_data_schema/core/instrument.py b/src/aind_data_schema/core/instrument.py index a662132e2..df8ac9047 100644 --- a/src/aind_data_schema/core/instrument.py +++ b/src/aind_data_schema/core/instrument.py @@ -246,9 +246,10 @@ def validate_modalities(cls, value): for component in components ): errors.append( - f"Device type validation error: modality '{modality.abbreviation}' requires at least one device of type(s) " - f"{', '.join(device.__name__ for device in required_group)} in the rig components." + f"Device type validation error: modality '{modality.abbreviation}' " + "requires at least one device of type(s) " ) + errors.append(f"{', '.join(device.__name__ for device in required_group)} in the rig components.") # Raise an error if there are validation issues if errors: diff --git a/src/aind_data_schema/utils/compatibility_check.py b/src/aind_data_schema/utils/compatibility_check.py index 96218abb1..a6b8f6e32 100644 --- a/src/aind_data_schema/utils/compatibility_check.py +++ b/src/aind_data_schema/utils/compatibility_check.py @@ -18,7 +18,8 @@ def _compare_instrument_id(self) -> Optional[ValueError]: """Compares instrument_id""" if self.session.instrument_id != self.inst.instrument_id: return ValueError( - f"Insturment ID in session {self.session.instrument_id} does not match the instrument's {self.inst.instrument_id}." + f"Insturment ID in session {self.session.instrument_id} does", + " not match the instrument's {self.inst.instrument_id}.", ) else: return None @@ -74,7 +75,8 @@ def _compare_light_sources(self) -> Optional[ValueError]: if not set(session_light_sources).issubset(set(instrument_light_sources)): return ValueError( f"light source names in session do not match light source names in inst. " - f"session_light_sources: {set(session_light_sources)} instrument_light_sources: {set(instrument_light_sources)}" + f"session_light_sources: {set(session_light_sources)} " + f"instrument_light_sources: {set(instrument_light_sources)}" ) def _compare_ephys_assemblies(self) -> Optional[ValueError]: @@ -171,7 +173,8 @@ def _compare_fiber_modules(self) -> Optional[ValueError]: if not set(session_fiber_modules).issubset(set(instrument_fiber_modules)): return ValueError( f"fiber module names in session do not match fiber assembly names in inst. " - f"session_fiber_modules: {set(session_fiber_modules)} instrument_fiber_assemblies: {set(instrument_fiber_modules)}" + f"session_fiber_modules: {set(session_fiber_modules)} " + f"instrument_fiber_assemblies: {set(instrument_fiber_modules)}" ) def _compare_stimulus_devices(self) -> Optional[ValueError]: diff --git a/src/aind_data_schema/utils/diagrams.py b/src/aind_data_schema/utils/diagrams.py index ee9f81526..cbe14122f 100644 --- a/src/aind_data_schema/utils/diagrams.py +++ b/src/aind_data_schema/utils/diagrams.py @@ -14,7 +14,7 @@ # Import all modules in core package for mod in core.__loader__.get_resource_reader().contents(): if "__" not in mod: - importlib.import_module(f"aind_data_schema.core.{mod.replace('.py','')}") + importlib.import_module(f"aind_data_schema.core.{mod.replace('.py', '')}") def save_diagram( diff --git a/src/aind_data_schema/utils/json_writer.py b/src/aind_data_schema/utils/json_writer.py index 5b9a3cbc7..a374c3682 100644 --- a/src/aind_data_schema/utils/json_writer.py +++ b/src/aind_data_schema/utils/json_writer.py @@ -14,7 +14,7 @@ # Import all modules in core package for mod in core.__loader__.get_resource_reader().contents(): if "__" not in mod: - importlib.import_module(f"aind_data_schema.core.{mod.replace('.py','')}") + importlib.import_module(f"aind_data_schema.core.{mod.replace('.py', '')}") class SchemaWriter: diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index f88128461..29e209768 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -762,7 +762,7 @@ def read_json(filepath: Path) -> dict: cls.example_ephys_inst = Instrument.model_validate_json(json.dumps(read_json(EPHYS_INST_JSON))) cls.example_ephys_session = Session.model_validate_json(json.dumps(read_json(EPHYS_SESSION_JSON))) - cls.ophys_instrument = r.Instrument( + cls.ophys_instrument = Instrument( instrument_id="428_FIP1_20231003", modification_date=date(2023, 10, 3), modalities=[Modality.FIB], diff --git a/tests/test_instrument.py b/tests/test_instrument.py index d11b077c1..c94d4d7fd 100644 --- a/tests/test_instrument.py +++ b/tests/test_instrument.py @@ -1,9 +1,8 @@ """ test Rig """ -from decimal import Decimal import json import unittest -from datetime import date, datetime +from datetime import date from aind_data_schema_models.modalities import Modality from aind_data_schema_models.organizations import Organization From 8e403f3b9ab041f745de3f9a2c36e8d6bf434746 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Fri, 24 Jan 2025 11:24:39 -0800 Subject: [PATCH 12/34] chore: fixing some typos, refactor modality->modalities, fixing a linter issue --- src/aind_data_schema/core/data_description.py | 4 ++-- src/aind_data_schema/core/model.py | 2 +- tests/test_bump_schema_versions.py | 8 ++++---- tests/test_data_description.py | 10 ++++++++-- tests/test_metadata.py | 14 +++++++------- tests/test_model.py | 2 +- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/aind_data_schema/core/data_description.py b/src/aind_data_schema/core/data_description.py index 265c67728..5b34975fa 100644 --- a/src/aind_data_schema/core/data_description.py +++ b/src/aind_data_schema/core/data_description.py @@ -40,7 +40,7 @@ class DataDescription(DataCoreModel): _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/data_description.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) schema_version: SkipValidation[Literal["1.0.5"]] = Field(default="1.0.5") - license: Literal["CC-BY-4.0"] = Field("CC-BY-4.0", title="License") + license: Literal["CC-BY-4.0"] = Field(default="CC-BY-4.0", title="License") subject_id: str = Field( ..., @@ -237,7 +237,7 @@ def get_or_default(field_name: str) -> Any: group=get_or_default("group"), investigators=get_or_default("investigators"), restrictions=get_or_default("restrictions"), - modality=get_or_default("modality"), + modalities=get_or_default("modalities"), project_name=get_or_default("project_name"), subject_id=get_or_default("subject_id"), related_data=get_or_default("related_data"), diff --git a/src/aind_data_schema/core/model.py b/src/aind_data_schema/core/model.py index a029d0c0c..1bd610e8e 100644 --- a/src/aind_data_schema/core/model.py +++ b/src/aind_data_schema/core/model.py @@ -63,7 +63,7 @@ class Model(DataCoreModel): license: str = Field(..., title="License") developers: Optional[List[Person]] = Field(default=None, title="Name of developer(s)") developer_institution: Optional[Organization.ONE_OF] = Field(default=None, title="Institute where developed") - modality: List[Modality.ONE_OF] = Field(..., title="Modality") + modalities: List[Modality.ONE_OF] = Field(..., title="Modalities") architecture: ModelArchitecture = Field(..., title="Model architecture") intended_use: str = Field(..., title="Intended model use", description="Semantic description of intended use") limitations: Optional[str] = Field(default=None, title="Model limitations") diff --git a/tests/test_bump_schema_versions.py b/tests/test_bump_schema_versions.py index 8f1c2e542..bbdf71ffb 100644 --- a/tests/test_bump_schema_versions.py +++ b/tests/test_bump_schema_versions.py @@ -8,7 +8,7 @@ from aind_data_schema.core.session import Session from aind_data_schema.core.subject import Subject -from aind_data_schema.core.rig import Rig +from aind_data_schema.core.instrument import Instrument from aind_data_schema.utils.json_writer import SchemaWriter from aind_data_schema.utils.schema_version_bump import SchemaVersionHandler @@ -134,11 +134,11 @@ def test_update_files(self, mock_write: MagicMock): new_subject_version = str(Version.parse(old_subject_version).bump_patch()) old_session_version = Session.model_fields["schema_version"].default new_session_version = str(Version.parse(old_session_version).bump_patch()) - old_rig_version = Rig.model_fields["schema_version"].default - new_rig_version = str(Version.parse(old_rig_version).bump_minor()) + old_inst_version = Instrument.model_fields["schema_version"].default + new_inst_version = str(Version.parse(old_inst_version).bump_minor()) # Pycharm raises a warning about types that we can ignore # noinspection PyTypeChecker - handler._update_files({Subject: new_subject_version, Session: new_session_version, Rig: new_rig_version}) + handler._update_files({Subject: new_subject_version, Session: new_session_version, Instrument: new_inst_version}) expected_line_change0 = ( f'schema_version: SkipValidation[Literal["{new_subject_version}"]] = Field(default="{new_subject_version}")' diff --git a/tests/test_data_description.py b/tests/test_data_description.py index 552db8f61..6e440d60b 100644 --- a/tests/test_data_description.py +++ b/tests/test_data_description.py @@ -294,6 +294,10 @@ def test_unique_abbreviations(self): modality_abbreviations = [m().abbreviation for m in Modality.ALL] self.assertEqual(len(set(modality_abbreviations)), len(modality_abbreviations)) + +class DerivedDataDescriptionTest(unittest.TestCase): + """test DerivedDataDescription""" + def test_from_data_description(self): """Tests DerivedDataDescription.from_data_description method""" @@ -311,10 +315,12 @@ def test_from_data_description(self): process_name = "spikesorter" dd1 = DerivedDataDescription.from_data_description(d1, process_name=process_name) - dd2 = DerivedDataDescription.from_data_description(d1, process_name=process_name, subject_id="12345") + # check that the original name is in the derived name self.assertTrue("test_data_2020-10-10_10-10-10_spikesorter_" in dd1.name) + # check that the subject ID is retained self.assertEqual("1234", dd1.subject_id) - self.assertEqual("12345", dd2.subject_id) + # check that the data level is upgraded + self.assertEqual("derived", dd1.data_level) def test_derived_data_description_build_name(self): """Tests build name method in derived data description class""" diff --git a/tests/test_metadata.py b/tests/test_metadata.py index b75736777..0185850ca 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -193,7 +193,7 @@ def test_validate_smartspim_metadata(self): data_description=DataDescription.model_construct( label="some label", creation_time=time(12, 12, 12), - modality=[Modality.SPIM], + modalities=[Modality.SPIM], ), procedures=Procedures.model_construct(subject_procedures=[surgery1]), acquisition=Acquisition.model_construct(), @@ -212,7 +212,7 @@ def test_validate_smartspim_metadata(self): data_description=DataDescription.model_construct( label="some label", creation_time=time(12, 12, 12), - modality=[Modality.SPIM], + modalities=[Modality.SPIM], ), subject=Subject.model_construct(), session=Session.model_construct(), @@ -233,7 +233,7 @@ def test_validate_smartspim_metadata(self): data_description=DataDescription.model_construct( label="some label", creation_time=time(12, 12, 12), - modality=[Modality.SPIM], + modalities=[Modality.SPIM], ), subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), @@ -267,7 +267,7 @@ def test_multi_modal_metadata(self): data_description=DataDescription.model_construct( label="some label", creation_time=time(12, 12, 12), - modality=[Modality.BEHAVIOR, Modality.SPIM], # technically this is impossible, but we need to test it + modalities=[Modality.BEHAVIOR, Modality.SPIM], # technically this is impossible, but we need to test it ), subject=Subject.model_construct(), session=session, # SPIM excludes session, but BEHAVIOR requires it @@ -293,7 +293,7 @@ def test_validate_ecephys_metadata(self): data_description=DataDescription.model_construct( label="some label", creation_time=time(12, 12, 12), - modality=[Modality.ECEPHYS], + modalities=[Modality.ECEPHYS], ), procedures=Procedures.model_construct(subject_procedures=[surgery1]), instrument=Instrument.model_construct(), @@ -312,7 +312,7 @@ def test_validate_ecephys_metadata(self): data_description=DataDescription.model_construct( label="some label", creation_time=time(12, 12, 12), - modality=[Modality.ECEPHYS], + modalities=[Modality.ECEPHYS], ), subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), @@ -349,7 +349,7 @@ def test_validate_instrument_session_compatibility(self): data_description=DataDescription.model_construct( label="some label", creation_time=time(12, 12, 12), - modality=[Modality.ECEPHYS], + modalities=[Modality.ECEPHYS], ), subject=Subject.model_construct(), procedures=Procedures.model_construct(), diff --git a/tests/test_model.py b/tests/test_model.py index 660840470..9a81adc5c 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -29,7 +29,7 @@ def test_constructors(self): license="CC-BY-4.0", developers=[Person(name="Dr. Dan")], developer_institution=Organization.AIND, - modality=[Modality.SPIM], + modalities=[Modality.SPIM], pretrained_source_url="url pretrained weights are from", architecture=ModelArchitecture( backbone=ModelBackbone.RESNET, From e08aceb33c24f0123207dcdf9154e43c5d7c4d21 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Fri, 24 Jan 2025 11:28:24 -0800 Subject: [PATCH 13/34] chore: various typos --- src/aind_data_schema/core/instrument.py | 2 +- src/aind_data_schema/utils/compatibility_check.py | 2 +- tests/test_inst_acq_compatibility.py | 2 +- tests/test_instrument.py | 6 +++--- tests/test_metadata.py | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/aind_data_schema/core/instrument.py b/src/aind_data_schema/core/instrument.py index df8ac9047..83a14a137 100644 --- a/src/aind_data_schema/core/instrument.py +++ b/src/aind_data_schema/core/instrument.py @@ -91,7 +91,7 @@ class Instrument(DataCoreModel): instrument_id: str = Field( ..., description="Unique instrument identifier, name convention: __", - title="Insturment ID", + title="Instrument ID", pattern=instrument_id_PATTERN, ) mouse_platform: Optional[MOUSE_PLATFORMS] = Field(default=None, title="Mouse platform") diff --git a/src/aind_data_schema/utils/compatibility_check.py b/src/aind_data_schema/utils/compatibility_check.py index a6b8f6e32..0f5b5f243 100644 --- a/src/aind_data_schema/utils/compatibility_check.py +++ b/src/aind_data_schema/utils/compatibility_check.py @@ -18,7 +18,7 @@ def _compare_instrument_id(self) -> Optional[ValueError]: """Compares instrument_id""" if self.session.instrument_id != self.inst.instrument_id: return ValueError( - f"Insturment ID in session {self.session.instrument_id} does", + f"Instrument ID in session {self.session.instrument_id} does", " not match the instrument's {self.inst.instrument_id}.", ) else: diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index 29e209768..de3376883 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -861,7 +861,7 @@ def read_json(filepath: Path) -> dict: def test_run_compatibility_check(self): """Tests compatibility check""" expected_error = ( - "Insturment ID in session 323_EPHYS2-RF_2023-04-24_01 does not match the rig's 323_EPHYS1_20231003." + "Instrument ID in session 323_EPHYS2-RF_2023-04-24_01 does not match the rig's 323_EPHYS1_20231003." ) with self.assertRaises(ValueError) as context: InstrumentSessionCompatibility(instrument=ephys_inst, session=ephys_session).run_compatibility_check() diff --git a/tests/test_instrument.py b/tests/test_instrument.py index c94d4d7fd..473f3aadf 100644 --- a/tests/test_instrument.py +++ b/tests/test_instrument.py @@ -1,4 +1,4 @@ -""" test Rig """ +""" test Instrument """ import json import unittest @@ -226,8 +226,8 @@ ) -class RigTests(unittest.TestCase): - """test rig schemas""" +class InstrumentTests(unittest.TestCase): + """test instrument schemas""" def test_constructors(self): """always returns true""" diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 0185850ca..3b85f5231 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -323,7 +323,7 @@ def test_validate_ecephys_metadata(self): self.assertIn("Injection is missing injection_materials.", str(context.exception)) def test_validate_instrument_session_compatibility(self): - """Tests that rig/session compatibility validator works as expected""" + """Tests that instrument/session compatibility validator works as expected""" ephys_assembly = EphysAssembly( probes=[EphysProbe(probe_model="Neuropixels 1.0", name="Probe A")], manipulator=Manipulator( @@ -387,7 +387,7 @@ def test_create_from_core_jsons(self): "data_description": None, "procedures": self.procedures_json, "session": None, - "rig": None, + "instrument": None, "processing": self.processing_json, "acquisition": None, "quality_control": None, @@ -453,7 +453,7 @@ def test_create_from_core_jsons_invalid(self, mock_warning: MagicMock): "data_description": self.dd_json, "procedures": self.procedures_json, "session": None, - "rig": None, + "instrument": None, "processing": self.processing_json, "acquisition": None, "quality_control": None, @@ -489,7 +489,7 @@ def test_create_from_core_jsons_corrupt(self, mock_is_dict_corrupt: MagicMock, m "data_description": None, "procedures": self.procedures_json, "session": None, - "rig": None, + "instrument": None, "processing": self.processing_json, "acquisition": None, "quality_control": None, From 9dddfa2def212c85e388d653c544f4628de8c940 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 08:56:31 -0800 Subject: [PATCH 14/34] tests: working on fixing tests for instrument changes --- examples/aind_smartspim_rig.py | 2 +- src/aind_data_schema/core/instrument.py | 2 +- tests/resources/spim_instrument.py | 287 ++++++++++++++++++++++++ tests/test_metadata.py | 8 +- 4 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 tests/resources/spim_instrument.py diff --git a/examples/aind_smartspim_rig.py b/examples/aind_smartspim_rig.py index 7be59327e..ff8bc504e 100644 --- a/examples/aind_smartspim_rig.py +++ b/examples/aind_smartspim_rig.py @@ -1,6 +1,6 @@ """ example SmartSPIM instrument """ -import datetime +from datetime import datetime from aind_data_schema_models.organizations import Organization from aind_data_schema_models.units import SizeUnit diff --git a/src/aind_data_schema/core/instrument.py b/src/aind_data_schema/core/instrument.py index 83a14a137..2ca3baad9 100644 --- a/src/aind_data_schema/core/instrument.py +++ b/src/aind_data_schema/core/instrument.py @@ -104,7 +104,7 @@ class Instrument(DataCoreModel): ) origin: Optional[Origin] = Field(default=None, title="Origin point for instrument position transforms") instrument_axes: Optional[List[Axis]] = Field(default=None, title="Instrument axes", min_length=3, max_length=3) - modalities: Set[Modality.ONE_OF] = Field(..., title="Modalities") + modalities: List[Modality.ONE_OF] = Field(..., title="Modalities") com_ports: List[Com] = Field(default=[], title="COM ports") instrument_type: Optional[ImagingInstrumentType] = Field(default=None, title="Instrument type") manufacturer: Optional[Organization.ONE_OF] = Field(default=None, title="Instrument manufacturer") diff --git a/tests/resources/spim_instrument.py b/tests/resources/spim_instrument.py new file mode 100644 index 000000000..58d6f976c --- /dev/null +++ b/tests/resources/spim_instrument.py @@ -0,0 +1,287 @@ +import datetime +from aind_data_schema_models.organizations import Organization +from aind_data_schema_models.units import SizeUnit + +from aind_data_schema.components.devices import ( + Filter, + ImagingInstrumentType, + Laser, + MotorizedStage, + OpticalTable, + ScanningStage, +) +from aind_data_schema.core.instrument import Com, Detector, Instrument, Objective +from aind_data_schema_models.modalities import Modality + +objective_1 = Objective( + name="TLX Objective 1", + numerical_aperture=0.2, + magnification=3.6, + manufacturer=Organization.LIFECANVAS, + immersion="multi", + notes="Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", + serial_number="Unknown-1", +) + +objective_2 = Objective( + name="TLX Objective 2", + numerical_aperture=0.12, + magnification=1.625, + manufacturer=Organization.LIFECANVAS, + immersion="multi", + notes="Thorlabs TL2X-SAP with LifeCanvas dipping cap and correction optics.", + serial_number="Unknown-2", +) + +detector_1 = Detector( + detector_type="Camera", + data_interface="USB", + name="Camera 1", + cooling="Air", + manufacturer=Organization.HAMAMATSU, + model="C14440-20UP", + serial_number="001284", +) + +laser_1 = Laser( + name="Ex_445", + coupling="Single-mode fiber", + wavelength=445, + maximum_power=200, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +laser_2 = Laser( + name="Ex_488", + coupling="Single-mode fiber", + wavelength=488, + maximum_power=150, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +laser_3 = Laser( + name="Ex_561", + coupling="Single-mode fiber", + wavelength=561, + maximum_power=150, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +laser_4 = Laser( + name="Ex_594", + coupling="Single-mode fiber", + wavelength=594, + maximum_power=100, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +laser_5 = Laser( + name="Ex_639", + coupling="Single-mode fiber", + wavelength=639, + maximum_power=160, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +laser_6 = Laser( + name="Ex_690", + coupling="Single-mode fiber", + wavelength=690, + maximum_power=160, + serial_number="VL08223M03", + manufacturer=Organization.VORTRAN, +) + +filter_1 = Filter( + name="Em_469", + filter_type="Band pass", + manufacturer=Organization.SEMROCK, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FF01-469/35-25", + filter_wheel_index=0, + serial_number="Unknown-0", +) + +filter_2 = Filter( + name="Em_525", + filter_type="Band pass", + manufacturer=Organization.SEMROCK, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FF01-525/45-25", + filter_wheel_index=1, + serial_number="Unknown-1", +) + +filter_3 = Filter( + name="Em_593", + filter_type="Band pass", + manufacturer=Organization.SEMROCK, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FF01-593/40-25", + filter_wheel_index=2, + serial_number="Unknown-2", +) + +filter_4 = Filter( + name="Em_624", + filter_type="Band pass", + manufacturer=Organization.SEMROCK, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FF01-624/40-25", + filter_wheel_index=3, + serial_number="Unknown-3", +) + +filter_5 = Filter( + name="Em_667", + filter_type="Band pass", + manufacturer=Organization.CHROMA, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="ET667/30m", + filter_wheel_index=4, + serial_number="Unknown-4", +) + +filter_6 = Filter( + name="Em_700", + filter_type="Long pass", + manufacturer=Organization.THORLABS, + diameter=25, + thickness=2.0, + thickness_unit=SizeUnit.MM, + model="FELH0700", + filter_wheel_index=5, + serial_number="Unknown-5", +) + +motorized_stage_1 = MotorizedStage( + model="LS-100", + manufacturer=Organization.ASI, + serial_number="Unknown-1", + travel=100, + name="Focus stage", +) + +motorized_stage_2 = MotorizedStage( + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + serial_number="Unknown-5", + travel=41, + name="Cylindrical lens #1", +) + +motorized_stage_3 = MotorizedStage( + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + serial_number="Unknown-6", + travel=41, + name="Cylindrical lens #2", +) + +motorized_stage_4 = MotorizedStage( + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + serial_number="Unknown-7", + travel=41, + name="Cylindrical lens #3", +) + +motorized_stage_5 = MotorizedStage( + model="L12-20F-4", + manufacturer=Organization.IR_ROBOT_CO, + serial_number="Unknown-8", + travel=41, + name="Cylindrical lens #4", +) + +scanning_stage_1 = ScanningStage( + model="LS-50", + manufacturer=Organization.ASI, + serial_number="Unknown-2", + stage_axis_direction="Detection axis", + stage_axis_name="Z", + travel=50, + name="Sample stage Z", +) + +scanning_stage_2 = ScanningStage( + model="LS-50", + manufacturer=Organization.ASI, + serial_number="Unknown-3", + stage_axis_direction="Illumination axis", + stage_axis_name="X", + travel=50, + name="Sample stage X", +) + +scanning_stage_3 = ScanningStage( + model="LS-50", + manufacturer=Organization.ASI, + serial_number="Unknown-4", + stage_axis_direction="Perpendicular axis", + stage_axis_name="Y", + travel=50, + name="Sample stage Y", +) + +optical_table_1 = OpticalTable( + name="Main optical table", + length=36, + width=48, + vibration_control=False, + model="VIS2424-IG2-125A", + manufacturer=Organization.MKS_NEWPORT, + serial_number="Unknown", +) + +objectives = [objective_1, objective_2] +detectors = [detector_1] +lasers = [laser_1, laser_2, laser_3, laser_4, laser_5, laser_6] +fluorescence_filters = [filter_1, filter_2, filter_3, filter_4, filter_5, filter_6] +motorized_stages = [motorized_stage_1, motorized_stage_2, motorized_stage_3, motorized_stage_4, motorized_stage_5] +scanning_stages = [scanning_stage_1, scanning_stage_2, scanning_stage_3] +optical_tables = [optical_table_1] + +inst = Instrument( + instrument_id="440_SmartSPIM1_20231004", + instrument_type=ImagingInstrumentType.SMARTSPIM, + manufacturer=Organization.LIFECANVAS, + modification_date=datetime.date(2023, 10, 4), + modalities=[Modality.SPIM], + components=[ + *objectives, + *detectors, + *lasers, + *fluorescence_filters, + *motorized_stages, + *scanning_stages, + *optical_tables, + ], + com_ports=[ + Com(hardware_name="Laser Launch", com_port="COM4"), + Com( + hardware_name="ASI Tiger", + com_port="COM3", + ), + Com( + hardware_name="MightyZap", + com_port="COM9", + ), + ], + temperature_control=False, +) \ No newline at end of file diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 3b85f5231..45ac5cde2 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -29,6 +29,8 @@ from aind_data_schema.core.session import Session from aind_data_schema.core.subject import BreedingInfo, Housing, Sex, Species, Subject +from resources.spim_instrument import inst + PYD_VERSION = re.match(r"(\d+.\d+).\d+", pyd_version).group(1) @@ -38,6 +40,8 @@ class TestMetadata(unittest.TestCase): @classmethod def setUpClass(cls) -> None: """Set up the test class.""" + spim_instrument = inst + subject = Subject( species=Species.MUS_MUSCULUS, subject_id="12345", @@ -238,9 +242,7 @@ def test_validate_smartspim_metadata(self): subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), acquisition=Acquisition.model_construct(), - instrument=Instrument.model_construct( - modalities=[Modality.SPIM], components=[Objective.model_construct()] - ), + instrument=inst, processing=Processing.model_construct(), ) self.assertIn("Injection is missing injection_materials.", str(context.exception)) From 28ab3b9fa74c129072b5a12dcfd7dafe8b64074d Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 10:57:32 -0800 Subject: [PATCH 15/34] refactor: fixing name generation to pass data description tests --- src/aind_data_schema/core/data_description.py | 9 +- tests/test_data_description.py | 93 ++++++++++++++++--- 2 files changed, 86 insertions(+), 16 deletions(-) diff --git a/src/aind_data_schema/core/data_description.py b/src/aind_data_schema/core/data_description.py index 5b34975fa..bc38b0967 100644 --- a/src/aind_data_schema/core/data_description.py +++ b/src/aind_data_schema/core/data_description.py @@ -136,10 +136,13 @@ def parse_name(cls, name): @model_validator(mode="after") def build_name(self): """sets the name of the file""" - if self.label is not None and self.name is None: - self.name = build_data_name(self.label, creation_datetime=self.creation_time) - elif self.name is None: + if self.name is None and self.label is None: raise ValueError("Either label or name must be set") + elif self.label is not None and self.name is None: + self.name = build_data_name(self.subject_id, creation_datetime=self.creation_time) + # check that the name matches the name regex + if not re.match(DataRegex.DATA.value, self.name): + raise ValueError(f"Name({self.name}) does not match allowed Regex pattern") return self diff --git a/tests/test_data_description.py b/tests/test_data_description.py index 6e440d60b..5decb5e8d 100644 --- a/tests/test_data_description.py +++ b/tests/test_data_description.py @@ -47,11 +47,15 @@ def setUpClass(cls): DERIVED_NAME = "ecephys_1234_3033-12-21_04-22-11_spikesorted-ks25_2022-10-12_23-23-11" ANALYSIS_NAME = "project_analysis_3033-12-21_04-22-11" - def test_constructors(self): - """test building from component parts""" + def test_funding_construction(self): + """Test Funding construction""" f = Funding(funder=Organization.NINDS, grant_number="grant001") + self.assertIsNotNone(f) + def test_raw_data_description_construction(self): + """Test RawDataDescription construction""" dt = datetime.datetime.now() + f = Funding(funder=Organization.NINDS, grant_number="grant001") da = RawDataDescription( creation_time=dt, institution=Organization.AIND, @@ -61,7 +65,21 @@ def test_constructors(self): subject_id="12345", investigators=[Person(name="Jane Smith")], ) + self.assertIsNotNone(da) + def test_derived_data_description_construction(self): + """Test DerivedDataDescription construction""" + dt = datetime.datetime.now() + f = Funding(funder=Organization.NINDS, grant_number="grant001") + da = RawDataDescription( + creation_time=dt, + institution=Organization.AIND, + data_level="raw", + funding_source=[f], + modalities=[Modality.ECEPHYS], + subject_id="12345", + investigators=[Person(name="Jane Smith")], + ) r1 = DerivedDataDescription( input_data_name=da.name, process_name="spikesort-ks25", @@ -72,7 +90,31 @@ def test_constructors(self): subject_id=da.subject_id, investigators=[Person(name="Jane Smith")], ) + self.assertIsNotNone(r1) + def test_nested_derived_data_description_construction(self): + """Test nested DerivedDataDescription construction""" + dt = datetime.datetime.now() + f = Funding(funder=Organization.NINDS, grant_number="grant001") + da = RawDataDescription( + creation_time=dt, + institution=Organization.AIND, + data_level="raw", + funding_source=[f], + modalities=[Modality.ECEPHYS], + subject_id="12345", + investigators=[Person(name="Jane Smith")], + ) + r1 = DerivedDataDescription( + input_data_name=da.name, + process_name="spikesort-ks25", + creation_time=dt, + institution=Organization.AIND, + funding_source=[f], + modalities=da.modalities, + subject_id=da.subject_id, + investigators=[Person(name="Jane Smith")], + ) r2 = DerivedDataDescription( input_data_name=r1.name, process_name="some-model", @@ -83,7 +125,6 @@ def test_constructors(self): subject_id="12345", investigators=[Person(name="Jane Smith")], ) - r3 = DerivedDataDescription( input_data_name=r2.name, process_name="a-paper", @@ -94,8 +135,12 @@ def test_constructors(self): subject_id="12345", investigators=[Person(name="Jane Smith")], ) - assert r3 is not None + self.assertIsNotNone(r3) + def test_data_description_construction(self): + """Test DataDescription construction""" + dt = datetime.datetime.now() + f = Funding(funder=Organization.NINDS, grant_number="grant001") dd = DataDescription( label="test_data", modalities=[Modality.SPIM], @@ -106,13 +151,15 @@ def test_constructors(self): funding_source=[f], investigators=[Person(name="Jane Smith")], ) + self.assertIsNotNone(dd) - assert dd is not None - - # test construction fails + def test_data_description_construction_failure(self): + """Test DataDescription construction failure""" + dt = datetime.datetime.now() + f = Funding(funder=Organization.NINDS, grant_number="grant001") with self.assertRaises(ValidationError): DataDescription( - label="test_data", + label="not-allowed-label-1224242", modalities=[Modality.SPIM], subject_id="1234", data_level="raw", @@ -122,6 +169,10 @@ def test_constructors(self): investigators=[Person(name="Jane Smith")], ) + def test_analysis_description_construction(self): + """Test AnalysisDescription construction""" + dt = datetime.datetime.now() + f = Funding(funder=Organization.NINDS, grant_number="grant001") ad = AnalysisDescription( analysis_name="analysis", project_name="project", @@ -134,6 +185,10 @@ def test_constructors(self): ) self.assertEqual(ad.name, build_data_name("project_analysis", dt)) + def test_analysis_description_invalid_name(self): + """Test AnalysisDescription invalid name""" + dt = datetime.datetime.now() + f = Funding(funder=Organization.NINDS, grant_number="grant001") with self.assertRaises(ValueError): AnalysisDescription( analysis_name="ana lysis", @@ -146,27 +201,39 @@ def test_constructors(self): investigators=[Person(name="Jane Smith")], ) + def test_data_description_missing_fields(self): + """Test DataDescription missing fields""" + dt = datetime.datetime.now() with self.assertRaises(ValueError): DataDescription() - with self.assertRaises(ValueError): DataDescription(creation_time=dt) + def test_derived_data_description_missing_fields(self): + """Test DerivedDataDescription missing fields""" + dt = datetime.datetime.now() with self.assertRaises(ValueError): DerivedDataDescription() - with self.assertRaises(ValueError): DerivedDataDescription(creation_time=dt) + def test_analysis_description_missing_fields(self): + """Test AnalysisDescription missing fields""" + dt = datetime.datetime.now() with self.assertRaises(ValueError): AnalysisDescription() - with self.assertRaises(ValueError): AnalysisDescription(creation_time=dt) + def test_raw_data_description_invalid_platform(self): + """Test RawDataDescription invalid platform""" with self.assertRaises(ValueError): RawDataDescription(platform="exaspim") + def test_analysis_description_empty_fields(self): + """Test AnalysisDescription empty fields""" + dt = datetime.datetime.now() + f = Funding(funder=Organization.NINDS, grant_number="grant001") with self.assertRaises(ValueError): AnalysisDescription( analysis_name="", @@ -178,7 +245,6 @@ def test_constructors(self): funding_source=[f], investigators=[Person(name="Jane Smith")], ) - with self.assertRaises(ValueError): AnalysisDescription( analysis_name="analysis", @@ -316,7 +382,8 @@ def test_from_data_description(self): dd1 = DerivedDataDescription.from_data_description(d1, process_name=process_name) # check that the original name is in the derived name - self.assertTrue("test_data_2020-10-10_10-10-10_spikesorter_" in dd1.name) + print(dd1.name) + self.assertTrue("1234_2020-10-10T101010_spikesorter_" in dd1.name) # check that the subject ID is retained self.assertEqual("1234", dd1.subject_id) # check that the data level is upgraded From d72618fd2f8d3e2e506fe1294996446501266b0b Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 11:06:56 -0800 Subject: [PATCH 16/34] refactor: use fromisoformat and fix tests from data_description --- src/aind_data_schema/core/data_description.py | 8 ++--- tests/test_data_description.py | 32 +++++++++++++------ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/aind_data_schema/core/data_description.py b/src/aind_data_schema/core/data_description.py index bc38b0967..a0c0c56f1 100644 --- a/src/aind_data_schema/core/data_description.py +++ b/src/aind_data_schema/core/data_description.py @@ -126,7 +126,7 @@ def parse_name(cls, name): if m is None: raise ValueError(f"name({name}) does not match pattern") - creation_time = datetime_from_name_string(m.group("c_date"), m.group("c_time")) + creation_time = datetime.fromisoformat(m.group("c_datetime")) return dict( label=m.group("label"), @@ -170,7 +170,7 @@ def parse_name(cls, name): if m is None: raise ValueError(f"name({name}) does not match pattern") - creation_time = datetime_from_name_string(m.group("c_date"), m.group("c_time")) + creation_time = datetime.fromisoformat(m.group("c_datetime")) return dict( process_name=m.group("process_name"), @@ -271,7 +271,7 @@ def parse_name(cls, name): if m is None: raise ValueError(f"name({name}) does not match pattern") - creation_time = datetime_from_name_string(m.group("c_date"), m.group("c_time")) + creation_time = datetime.fromisoformat(m.group("c_datetime")) return dict( subject_id=m.group("subject_id"), @@ -313,7 +313,7 @@ def parse_name(cls, name): if m is None: raise ValueError(f"name({name}) does not match pattern") - creation_time = datetime_from_name_string(m.group("c_date"), m.group("c_time")) + creation_time = datetime.fromisoformat(m.group("c_datetime")) return dict( project_abbreviation=m.group("project_abbreviation"), diff --git a/tests/test_data_description.py b/tests/test_data_description.py index 5decb5e8d..3cd50f47c 100644 --- a/tests/test_data_description.py +++ b/tests/test_data_description.py @@ -43,9 +43,9 @@ def setUpClass(cls): cls.data_descriptions = dict(data_descriptions) BAD_NAME = "fizzbuzz" - BASIC_NAME = "ecephys_1234_3033-12-21_04-22-11" - DERIVED_NAME = "ecephys_1234_3033-12-21_04-22-11_spikesorted-ks25_2022-10-12_23-23-11" - ANALYSIS_NAME = "project_analysis_3033-12-21_04-22-11" + BASIC_NAME = "1234_3033-12-21T042211" + DERIVED_NAME = "1234_3033-12-21T042211_spikesorted-ks25_2022-10-12T232311" + ANALYSIS_NAME = "project_analysis_3033-12-21T042211" def test_funding_construction(self): """Test Funding construction""" @@ -159,9 +159,9 @@ def test_data_description_construction_failure(self): f = Funding(funder=Organization.NINDS, grant_number="grant001") with self.assertRaises(ValidationError): DataDescription( - label="not-allowed-label-1224242", + label="", modalities=[Modality.SPIM], - subject_id="1234", + subject_id="", data_level="raw", creation_time=dt, institution=Organization.AIND, @@ -326,7 +326,7 @@ def test_parse_name(self): """tests for parsing names""" toks = DataDescription.parse_name(self.BASIC_NAME) - assert toks["label"] == "ecephys_1234" + assert toks["label"] == "1234" assert toks["creation_time"] == datetime.datetime(3033, 12, 21, 4, 22, 11) with self.assertRaises(ValueError): @@ -340,7 +340,7 @@ def test_parse_name(self): RawDataDescription.parse_name(self.BAD_NAME) toks = DerivedDataDescription.parse_name(self.DERIVED_NAME) - assert toks["input_data_name"] == "ecephys_1234_3033-12-21_04-22-11" + assert toks["input_data_name"] == "1234_3033-12-21T042211" assert toks["process_name"] == "spikesorted-ks25" assert toks["creation_time"] == datetime.datetime(2022, 10, 12, 23, 23, 11) @@ -393,15 +393,27 @@ def test_derived_data_description_build_name(self): """Tests build name method in derived data description class""" dd = DerivedDataDescription( - input_data_name="input", - creation_time=datetime.datetime(2020, 10, 10, 10, 10, 10), + input_data_name="12345_2020-10-10T101010", + creation_time=datetime.datetime(2021, 10, 10, 10, 10, 10), + institution=Organization.AIND, + funding_source=[Funding(funder=Organization.NINDS, grant_number="grant001")], + modalities=[Modality.ECEPHYS], + subject_id="12345", + investigators=[Person(name="Jane Smith")], + ) + self.assertEqual("12345_2020-10-10T101010_2021-10-10T101010", dd.name) + + dd2 = DerivedDataDescription( + input_data_name="12345_2020-10-10T101010", + creation_time=datetime.datetime(2021, 10, 10, 10, 10, 10), institution=Organization.AIND, funding_source=[Funding(funder=Organization.NINDS, grant_number="grant001")], modalities=[Modality.ECEPHYS], subject_id="12345", investigators=[Person(name="Jane Smith")], + process_name="spikesorter", ) - self.assertEqual("input_2020-10-10_10-10-10", dd.name) + self.assertIn("12345_2020-10-10T101010_spikesorter", dd2.name) if __name__ == "__main__": From 3a9ceab0a0974609d8cea09f3e2a24d8adc9f868 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 11:09:18 -0800 Subject: [PATCH 17/34] refactor: missing renames for AindCoreModel --- .../utils/schema_version_bump.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/aind_data_schema/utils/schema_version_bump.py b/src/aind_data_schema/utils/schema_version_bump.py index 201ff13e9..2360f7ec3 100644 --- a/src/aind_data_schema/utils/schema_version_bump.py +++ b/src/aind_data_schema/utils/schema_version_bump.py @@ -8,7 +8,7 @@ import dictdiffer import semver -from aind_data_schema.base import AindCoreModel +from aind_data_schema.base import DataCoreModel from aind_data_schema.utils.json_writer import SchemaWriter CURRENT_DIR = Path(os.path.dirname(os.path.realpath(__file__))) @@ -34,12 +34,12 @@ def __init__(self, commit_message: str = "", json_schemas_location: Path = Path( self.commit_message = commit_message self.json_schemas_location = json_schemas_location - def _get_schema_json(self, model: AindCoreModel) -> dict: + def _get_schema_json(self, model: DataCoreModel) -> dict: """ Get the json schema of a model Parameters ---------- - model : AindCoreModel + model : DataCoreModel The model to get the json schema of Returns @@ -58,14 +58,14 @@ def _get_schema_json(self, model: AindCoreModel) -> dict: raise FileNotFoundError(f"Schema file not found: {main_branch_schema_path}") return main_branch_schema_contents - def _get_list_of_models_that_changed(self) -> List[AindCoreModel]: + def _get_list_of_models_that_changed(self) -> List[DataCoreModel]: """ Get a list of core models that have been updated by comparing the json schema of the models to the json schema in the schemas folder. Returns ------- - List[AindCoreModel] - A list of AindCoreModels that changed. + List[DataCoreModel] + A list of DataCoreModels that changed. """ schemas_that_need_updating = [] for core_model in SchemaWriter.get_schemas(): @@ -81,18 +81,18 @@ def _get_list_of_models_that_changed(self) -> List[AindCoreModel]: print(f"Schemas that need updating: {[model.__name__ for model in schemas_that_need_updating]}") return schemas_that_need_updating - def _get_incremented_versions_map(self, models_that_changed: List[AindCoreModel]) -> Dict[AindCoreModel, str]: + def _get_incremented_versions_map(self, models_that_changed: List[DataCoreModel]) -> Dict[DataCoreModel, str]: """ Parameters ---------- - models_that_changed : List[AindCoreModel] + models_that_changed : List[DataCoreModel] A list of models that have been updated and need to have their version numbers incremented. Returns ------- - Dict[AindCoreModel, str] - A mapping of the AindCoreModel to its new version number. + Dict[DataCoreModel, str] + A mapping of the DataCoreModel to its new version number. """ version_bump_map = {} @@ -169,13 +169,13 @@ def _write_new_file(new_file_contents: list, python_file_path: str) -> None: for line in new_file_contents: f.write(line) - def _update_files(self, version_bump_map: Dict[AindCoreModel, str]) -> None: + def _update_files(self, version_bump_map: Dict[DataCoreModel, str]) -> None: """ Using the information in the version_bump_map, will update the python files in the core directory. Parameters ---------- - version_bump_map : Dict[AindCoreModel, str] + version_bump_map : Dict[DataCoreModel, str] The models that need updating are in the dictionary keys and the new version number is the dictionary value. From 08471b4344cf32099038f05f0403a2aeac3cf334 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 11:09:28 -0800 Subject: [PATCH 18/34] fix: typos in test_metadata --- tests/resources/spim_instrument.py | 2 +- tests/test_metadata.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/resources/spim_instrument.py b/tests/resources/spim_instrument.py index 58d6f976c..b8672db34 100644 --- a/tests/resources/spim_instrument.py +++ b/tests/resources/spim_instrument.py @@ -284,4 +284,4 @@ ), ], temperature_control=False, -) \ No newline at end of file +) diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 45ac5cde2..8dd6569ce 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -12,7 +12,7 @@ from pydantic import ValidationError from pydantic import __version__ as pyd_version -from aind_data_schema.components.devices import EphysAssembly, EphysProbe, Manipulator, MousePlatform, Objective +from aind_data_schema.components.devices import EphysAssembly, EphysProbe, Manipulator, MousePlatform from aind_data_schema.components.identifiers import Person from aind_data_schema.core.acquisition import Acquisition from aind_data_schema.core.data_description import DataDescription, Funding @@ -40,7 +40,7 @@ class TestMetadata(unittest.TestCase): @classmethod def setUpClass(cls) -> None: """Set up the test class.""" - spim_instrument = inst + cls.spim_instrument = inst subject = Subject( species=Species.MUS_MUSCULUS, From b79204638fd802aff380b3457e5e5ac99a25bec3 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:00:50 -0800 Subject: [PATCH 19/34] tests: fix import issue in examples --- examples/aind_smartspim_rig.py | 4 ++-- tests/test_metadata.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/aind_smartspim_rig.py b/examples/aind_smartspim_rig.py index ff8bc504e..b9f7667af 100644 --- a/examples/aind_smartspim_rig.py +++ b/examples/aind_smartspim_rig.py @@ -1,6 +1,6 @@ """ example SmartSPIM instrument """ -from datetime import datetime +from datetime import date from aind_data_schema_models.organizations import Organization from aind_data_schema_models.units import SizeUnit @@ -264,7 +264,7 @@ instrument_id="440_SmartSPIM1_20231004", instrument_type=ImagingInstrumentType.SMARTSPIM, manufacturer=Organization.LIFECANVAS, - modification_date=datetime.date(2023, 10, 4), + modification_date=date(2023, 10, 4), modalities=[Modality.SPIM], components=[ *objectives, diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 8dd6569ce..248674ae3 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -28,8 +28,7 @@ from aind_data_schema.core.instrument import Instrument from aind_data_schema.core.session import Session from aind_data_schema.core.subject import BreedingInfo, Housing, Sex, Species, Subject - -from resources.spim_instrument import inst +from tests.resources.spim_instrument import inst PYD_VERSION = re.match(r"(\d+.\d+).\d+", pyd_version).group(1) @@ -192,12 +191,13 @@ def test_validate_smartspim_metadata(self): surgery1 = Surgery.model_construct(procedures=[nano_inj, ionto_inj]) with self.assertRaises(ValidationError) as context: Metadata( - name="ecephys_655019_2023-04-03_18-17-09", + name="655019_2023-04-03T181709", location="bucket", data_description=DataDescription.model_construct( label="some label", creation_time=time(12, 12, 12), modalities=[Modality.SPIM], + subject_id="655019", ), procedures=Procedures.model_construct(subject_procedures=[surgery1]), acquisition=Acquisition.model_construct(), From 13888d6a9b7ed91d3c84d9e8335f1e8f010609cd Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:01:16 -0800 Subject: [PATCH 20/34] tests: re-generate examples --- examples/data_description.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/data_description.json b/examples/data_description.json index a5f3ca3b8..828e0a9f1 100644 --- a/examples/data_description.json +++ b/examples/data_description.json @@ -5,7 +5,7 @@ "subject_id": "12345", "creation_time": "2022-02-21T16:30:01Z", "label": null, - "name": "12345_2022-02-21_16-30-01", + "name": "12345_2022-02-21T163001", "institution": { "name": "Allen Institute for Neural Dynamics", "abbreviation": "AIND", From cf79a305c0cf47b041ff8057cd8b8b222796a4fd Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:05:45 -0800 Subject: [PATCH 21/34] tests: fixing label issues --- tests/test_data_description.py | 4 ---- tests/test_metadata.py | 15 +++++---------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/tests/test_data_description.py b/tests/test_data_description.py index 3cd50f47c..54b2ef58a 100644 --- a/tests/test_data_description.py +++ b/tests/test_data_description.py @@ -142,7 +142,6 @@ def test_data_description_construction(self): dt = datetime.datetime.now() f = Funding(funder=Organization.NINDS, grant_number="grant001") dd = DataDescription( - label="test_data", modalities=[Modality.SPIM], subject_id="1234", data_level="raw", @@ -159,7 +158,6 @@ def test_data_description_construction_failure(self): f = Funding(funder=Organization.NINDS, grant_number="grant001") with self.assertRaises(ValidationError): DataDescription( - label="", modalities=[Modality.SPIM], subject_id="", data_level="raw", @@ -261,7 +259,6 @@ def test_pattern_errors(self): """Tests that errors are raised if malformed strings are input""" with self.assertRaises(ValidationError) as e: DataDescription( - label="test_data", modalities=[Modality.SPIM], subject_id="1234", data_level="raw", @@ -368,7 +365,6 @@ def test_from_data_description(self): """Tests DerivedDataDescription.from_data_description method""" d1 = DataDescription( - label="test_data", modalities=[Modality.SPIM], subject_id="1234", data_level="raw", diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 248674ae3..dec9ea84c 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -59,7 +59,6 @@ def setUpClass(cls) -> None: background_strain="C57BL/6J", ) dd = DataDescription( - label="test_data", modalities=[Modality.ECEPHYS], subject_id="123456", data_level="raw", @@ -194,7 +193,6 @@ def test_validate_smartspim_metadata(self): name="655019_2023-04-03T181709", location="bucket", data_description=DataDescription.model_construct( - label="some label", creation_time=time(12, 12, 12), modalities=[Modality.SPIM], subject_id="655019", @@ -214,7 +212,6 @@ def test_validate_smartspim_metadata(self): name="ecephys_655019_2023-04-03_18-17-09", location="bucket", data_description=DataDescription.model_construct( - label="some label", creation_time=time(12, 12, 12), modalities=[Modality.SPIM], ), @@ -235,7 +232,6 @@ def test_validate_smartspim_metadata(self): name="ecephys_655019_2023-04-03_18-17-09", location="bucket", data_description=DataDescription.model_construct( - label="some label", creation_time=time(12, 12, 12), modalities=[Modality.SPIM], ), @@ -267,7 +263,7 @@ def test_multi_modal_metadata(self): name="ecephys_655019_2023-04-03_18-17-09", location="bucket", data_description=DataDescription.model_construct( - label="some label", + subject_id="655019", creation_time=time(12, 12, 12), modalities=[Modality.BEHAVIOR, Modality.SPIM], # technically this is impossible, but we need to test it ), @@ -290,12 +286,12 @@ def test_validate_ecephys_metadata(self): surgery1 = Surgery.model_construct(procedures=[nano_inj, ionto_inj]) with self.assertRaises(ValidationError) as context: Metadata( - name="ecephys_655019_2023-04-03_18-17-09", + name="655019_2023-04-03_18-17-09", location="bucket", data_description=DataDescription.model_construct( - label="some label", creation_time=time(12, 12, 12), modalities=[Modality.ECEPHYS], + subject_id="655019", ), procedures=Procedures.model_construct(subject_procedures=[surgery1]), instrument=Instrument.model_construct(), @@ -312,7 +308,6 @@ def test_validate_ecephys_metadata(self): name="ecephys_655019_2023-04-03_18-17-09", location="bucket", data_description=DataDescription.model_construct( - label="some label", creation_time=time(12, 12, 12), modalities=[Modality.ECEPHYS], ), @@ -346,12 +341,12 @@ def test_validate_instrument_session_compatibility(self): session = Session.model_construct(instrument_id="123_EPHYS2_20230101", mouse_platform_name="platform2") with self.assertRaises(ValidationError) as context: Metadata( - name="ecephys_655019_2023-04-03_18-17-09", + name="655019_2023-04-03_18-17-09", location="bucket", data_description=DataDescription.model_construct( - label="some label", creation_time=time(12, 12, 12), modalities=[Modality.ECEPHYS], + subject_id="655019", ), subject=Subject.model_construct(), procedures=Procedures.model_construct(), From ac35a65d62cef9f4e1b81853d7686bb57440f539 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:09:40 -0800 Subject: [PATCH 22/34] fix: fixing a bug in name generator --- src/aind_data_schema/core/data_description.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/aind_data_schema/core/data_description.py b/src/aind_data_schema/core/data_description.py index a0c0c56f1..5e5315cee 100644 --- a/src/aind_data_schema/core/data_description.py +++ b/src/aind_data_schema/core/data_description.py @@ -136,13 +136,13 @@ def parse_name(cls, name): @model_validator(mode="after") def build_name(self): """sets the name of the file""" - if self.name is None and self.label is None: - raise ValueError("Either label or name must be set") - elif self.label is not None and self.name is None: + if self.name is None: self.name = build_data_name(self.subject_id, creation_datetime=self.creation_time) - # check that the name matches the name regex - if not re.match(DataRegex.DATA.value, self.name): - raise ValueError(f"Name({self.name}) does not match allowed Regex pattern") + + # check that the name matches the name regex + if not re.match(DataRegex.DATA.value, self.name): + raise ValueError(f"Name({self.name}) does not match allowed Regex pattern") + return self From bc5c61fc79d55e4d907ca5bc47b494b29ae4aad9 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:09:48 -0800 Subject: [PATCH 23/34] tests: missing objective for valid instrument --- tests/test_imaging.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_imaging.py b/tests/test_imaging.py index a6c08c36d..bcee495f9 100644 --- a/tests/test_imaging.py +++ b/tests/test_imaging.py @@ -79,12 +79,25 @@ def test_constructors(self): with self.assertRaises(ValidationError): Instrument() + # Generate a valid instrument + + objective = Objective( + name="TLX Objective", + numerical_aperture=0.2, + magnification=3.6, + immersion="multi", + manufacturer=Organization.THORLABS, + model="TL4X-SAP", + notes="Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", + ) + i = Instrument( instrument_id="room_exaSPIM1-1_20231004", modalities=[Modality.SPIM], instrument_type="diSPIM", modification_date=datetime.now().date(), manufacturer=Organization.LIFECANVAS, + components=[objective], ) self.assertIsNotNone(i) From 7602294d72ff0e86356999204eccb2eef0ba39e2 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:31:33 -0800 Subject: [PATCH 24/34] tests: fixing various tests --- src/aind_data_schema/core/metadata.py | 3 +- tests/test_data_description.py | 16 ------ tests/test_imaging.py | 2 +- tests/test_inst_acq_compatibility.py | 17 +++--- tests/test_metadata.py | 78 ++++++++++++++++++--------- 5 files changed, 66 insertions(+), 50 deletions(-) diff --git a/src/aind_data_schema/core/metadata.py b/src/aind_data_schema/core/metadata.py index f0d9df1ed..482df21e7 100644 --- a/src/aind_data_schema/core/metadata.py +++ b/src/aind_data_schema/core/metadata.py @@ -35,7 +35,6 @@ "subject", "data_description", "procedures", - "session", "instrument", "processing", "acquisition", @@ -227,7 +226,7 @@ def validate_expected_files_by_modality(self): if file not in requirement_dict: requirement_dict[file] = (abbreviation, file_requirement) else: - (prev_modality, prev_requirement) = requirement_dict[file] + (_, prev_requirement) = requirement_dict[file] if (file_requirement == FileRequirement.REQUIRED) or ( file_requirement == FileRequirement.OPTIONAL diff --git a/tests/test_data_description.py b/tests/test_data_description.py index 54b2ef58a..ecade7767 100644 --- a/tests/test_data_description.py +++ b/tests/test_data_description.py @@ -285,21 +285,6 @@ def test_model_constructors(self): assert Modality.from_abbreviation("ecephys") == Modality.ECEPHYS assert Organization().name_map["Allen Institute for Neural Dynamics"] == Organization.AIND - def test_name_label_error(self): - """Tests an error is raised if label and name are None""" - - with self.assertRaises(ValidationError) as e: - DataDescription( - modalities=[Modality.SPIM], - subject_id="1234", - data_level="raw", - creation_time=datetime.datetime(2020, 10, 10, 10, 10, 10), - institution=Organization.AIND, - funding_source=[Funding(funder=Organization.NINDS, grant_number="grant001")], - investigators=[Person(name="Jane Smith")], - ) - self.assertTrue("Value error, Either label or name must be set" in repr(e.exception)) - def test_round_trip(self): """make sure we can round trip from json""" @@ -378,7 +363,6 @@ def test_from_data_description(self): dd1 = DerivedDataDescription.from_data_description(d1, process_name=process_name) # check that the original name is in the derived name - print(dd1.name) self.assertTrue("1234_2020-10-10T101010_spikesorter_" in dd1.name) # check that the subject ID is retained self.assertEqual("1234", dd1.subject_id) diff --git a/tests/test_imaging.py b/tests/test_imaging.py index bcee495f9..967375c86 100644 --- a/tests/test_imaging.py +++ b/tests/test_imaging.py @@ -16,7 +16,7 @@ Scale3dTransform, Translation3dTransform, ) -from aind_data_schema.components.devices import Calibration +from aind_data_schema.components.devices import Calibration, Objective from aind_data_schema.core import acquisition as acq from aind_data_schema.core.processing import Registration from aind_data_schema.core.instrument import Instrument diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index de3376883..65b560f3b 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -237,12 +237,17 @@ instrument_id="323_EPHYS1_20231003", modification_date=date(2023, 10, 3), modalities=[Modality.ECEPHYS], - ephys_assemblies=[ephys_assemblyA, ephys_assemblyB], - cameras=[camassm1, camassm2], - laser_assemblies=[laser_assembly], - daqs=[basestation, harp], - stick_microscopes=[microscope], - mouse_platform=running_wheel, + components=[ + ephys_assemblyA, + ephys_assemblyB, + camassm1, + camassm2, + laser_assembly, + basestation, + harp, + microscope, + running_wheel, + ], calibrations=[red_laser_calibration, blue_laser_calibration], ) diff --git a/tests/test_metadata.py b/tests/test_metadata.py index dec9ea84c..bbb0bc3be 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -12,7 +12,7 @@ from pydantic import ValidationError from pydantic import __version__ as pyd_version -from aind_data_schema.components.devices import EphysAssembly, EphysProbe, Manipulator, MousePlatform +from aind_data_schema.components.devices import Device, EphysAssembly, EphysProbe, LickSensorType, Manipulator, MotorizedStage, MousePlatform, Objective, RewardDelivery, RewardSpout, SpoutSide from aind_data_schema.components.identifiers import Person from aind_data_schema.core.acquisition import Acquisition from aind_data_schema.core.data_description import DataDescription, Funding @@ -205,26 +205,6 @@ def test_validate_smartspim_metadata(self): str(context.exception), ) - # Tests excluded metadata getting included - surgery1 = Surgery.model_construct(procedures=[nano_inj, ionto_inj]) - with self.assertRaises(ValidationError) as context: - Metadata( - name="ecephys_655019_2023-04-03_18-17-09", - location="bucket", - data_description=DataDescription.model_construct( - creation_time=time(12, 12, 12), - modalities=[Modality.SPIM], - ), - subject=Subject.model_construct(), - session=Session.model_construct(), - procedures=Procedures.model_construct(subject_procedures=[surgery1]), - acquisition=Acquisition.model_construct(), - ) - self.assertIn( - "SPIM metadata includes excluded file: session", - str(context.exception), - ) - # Tests missing injection materials surgery2 = Surgery.model_construct(procedures=[nano_inj]) with self.assertRaises(ValidationError) as context: @@ -252,10 +232,56 @@ def test_multi_modal_metadata(self): surgery1 = Surgery.model_construct(procedures=[nano_inj, ionto_inj]) mouse_platform = MousePlatform.model_construct(name="platform1") + + objective = Objective( + name="TLX Objective", + numerical_aperture=0.2, + magnification=3.6, + immersion="multi", + manufacturer=Organization.THORLABS, + model="TL4X-SAP", + notes="Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", + ) + + reward_delivery = RewardDelivery( + reward_spouts=[ + RewardSpout( + name="Left spout", + side=SpoutSide.LEFT, + spout_diameter=1.2, + solenoid_valve=Device(name="Solenoid Left"), + lick_sensor=Device( + name="Janelia_Lick_Detector Left", + manufacturer=Organization.JANELIA, + ), + lick_sensor_type=LickSensorType("Capacitive"), + ), + RewardSpout( + name="Right spout", + side=SpoutSide.RIGHT, + spout_diameter=1.2, + solenoid_valve=Device(name="Solenoid Right"), + lick_sensor=Device( + name="Janelia_Lick_Detector Right", + manufacturer=Organization.JANELIA, + ), + lick_sensor_type=LickSensorType("Capacitive"), + ), + ], + stage_type=MotorizedStage( + name="NewScaleMotor for LickSpouts", + serial_number="xxxx", # grabbing from GUI/SettingFiles + manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, + travel=15.0, # unit is mm + firmware=("https://github.com/AllenNeuralDynamics/python-newscale,branch: axes-on-target,commit #7c17497"), + ), + ) + inst = Instrument.model_construct( instrument_id="123_EPHYS1_20220101", mouse_platform=mouse_platform, modalities=[Modality.BEHAVIOR, Modality.SPIM], + components=[objective, reward_delivery], ) session = Session.model_construct(instrument_id="123_EPHYS1_20220101", mouse_platform_name="platform1") @@ -284,17 +310,18 @@ def test_validate_ecephys_metadata(self): # Tests missing metadata surgery1 = Surgery.model_construct(procedures=[nano_inj, ionto_inj]) + modalities = [Modality.ECEPHYS] with self.assertRaises(ValidationError) as context: Metadata( name="655019_2023-04-03_18-17-09", location="bucket", data_description=DataDescription.model_construct( creation_time=time(12, 12, 12), - modalities=[Modality.ECEPHYS], + modalities=modalities, subject_id="655019", ), procedures=Procedures.model_construct(subject_procedures=[surgery1]), - instrument=Instrument.model_construct(), + instrument=Instrument.model_construct(modalities=modalities), ) self.assertIn( "ECEPHYS metadata missing required file: subject", @@ -303,17 +330,18 @@ def test_validate_ecephys_metadata(self): # Tests missing injection materials surgery2 = Surgery.model_construct(procedures=[nano_inj]) + modalities = [Modality.ECEPHYS] with self.assertRaises(ValidationError) as context: Metadata( name="ecephys_655019_2023-04-03_18-17-09", location="bucket", data_description=DataDescription.model_construct( creation_time=time(12, 12, 12), - modalities=[Modality.ECEPHYS], + modalities=modalities, ), subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), - instrument=Instrument.model_construct(), + instrument=Instrument.model_construct(modalities=modalities), processing=Processing.model_construct(), session=Session.model_construct(), ) From f0bb3847c8e5e7f5ac93bb507340a915cae5e906 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:38:23 -0800 Subject: [PATCH 25/34] chore: bump version numbers and re-generate examples --- examples/aibs_smartspim_instrument.json | 2 +- examples/aibs_smartspim_procedures.json | 2 +- examples/aind_smartspim_instrument.json | 2 +- examples/bergamo_ophys_session.json | 2 +- examples/data_description.json | 2 +- examples/ephys_instrument.json | 2 +- examples/ephys_session.json | 2 +- examples/exaspim_acquisition.json | 2 +- examples/exaspim_instrument.json | 2 +- examples/fip_behavior_instrument.json | 2 +- examples/fip_ophys_instrument.json | 2 +- examples/mri_session.json | 2 +- examples/multiplane_ophys_session.json | 2 +- examples/ophys_procedures.json | 2 +- examples/ophys_session.json | 2 +- examples/procedures.json | 2 +- examples/processing.json | 2 +- examples/quality_control.json | 2 +- examples/subject.json | 2 +- src/aind_data_schema/core/acquisition.py | 2 +- src/aind_data_schema/core/data_description.py | 2 +- src/aind_data_schema/core/instrument.py | 2 +- src/aind_data_schema/core/metadata.py | 2 +- src/aind_data_schema/core/model.py | 2 +- src/aind_data_schema/core/procedures.py | 2 +- src/aind_data_schema/core/processing.py | 2 +- src/aind_data_schema/core/quality_control.py | 2 +- src/aind_data_schema/core/session.py | 2 +- src/aind_data_schema/core/subject.py | 2 +- tests/test_inst_acq_compatibility.py | 4 ++-- tests/test_metadata.py | 3 ++- 31 files changed, 33 insertions(+), 32 deletions(-) diff --git a/examples/aibs_smartspim_instrument.json b/examples/aibs_smartspim_instrument.json index ef23c6b98..f950cb0be 100644 --- a/examples/aibs_smartspim_instrument.json +++ b/examples/aibs_smartspim_instrument.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", - "schema_version": "1.0.5", + "schema_version": "2.0.0", "instrument_id": "440_SmartSPIM2_20231004", "mouse_platform": null, "modification_date": "2023-10-04", diff --git a/examples/aibs_smartspim_procedures.json b/examples/aibs_smartspim_procedures.json index 316ad84f3..879e2c304 100644 --- a/examples/aibs_smartspim_procedures.json +++ b/examples/aibs_smartspim_procedures.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/procedures.py", - "schema_version": "1.2.6", + "schema_version": "2.0.0", "subject_id": "651286", "subject_procedures": [ { diff --git a/examples/aind_smartspim_instrument.json b/examples/aind_smartspim_instrument.json index d64b2b1bc..51d739589 100644 --- a/examples/aind_smartspim_instrument.json +++ b/examples/aind_smartspim_instrument.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", - "schema_version": "1.0.5", + "schema_version": "2.0.0", "instrument_id": "440_SmartSPIM1_20231004", "mouse_platform": null, "modification_date": "2023-10-04", diff --git a/examples/bergamo_ophys_session.json b/examples/bergamo_ophys_session.json index 68567f2e6..bea3f5399 100644 --- a/examples/bergamo_ophys_session.json +++ b/examples/bergamo_ophys_session.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/session.py", - "schema_version": "1.1.5", + "schema_version": "2.0.0", "protocol_id": [], "experimenters": [ { diff --git a/examples/data_description.json b/examples/data_description.json index 828e0a9f1..3e4e980ca 100644 --- a/examples/data_description.json +++ b/examples/data_description.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/data_description.py", - "schema_version": "1.0.5", + "schema_version": "2.0.0", "license": "CC-BY-4.0", "subject_id": "12345", "creation_time": "2022-02-21T16:30:01Z", diff --git a/examples/ephys_instrument.json b/examples/ephys_instrument.json index 080fd1fe6..96157a28c 100644 --- a/examples/ephys_instrument.json +++ b/examples/ephys_instrument.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", - "schema_version": "1.0.5", + "schema_version": "2.0.0", "instrument_id": "323_EPHYS1_20231003", "mouse_platform": { "device_type": "Disc", diff --git a/examples/ephys_session.json b/examples/ephys_session.json index 5c345d4f6..9cdbd5499 100644 --- a/examples/ephys_session.json +++ b/examples/ephys_session.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/session.py", - "schema_version": "1.1.5", + "schema_version": "2.0.0", "protocol_id": [], "experimenters": [ { diff --git a/examples/exaspim_acquisition.json b/examples/exaspim_acquisition.json index 25d5a2a3f..14cf7a9d6 100644 --- a/examples/exaspim_acquisition.json +++ b/examples/exaspim_acquisition.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/acquisition.py", - "schema_version": "1.0.6", + "schema_version": "2.0.0", "protocol_id": [], "experimenters": [ { diff --git a/examples/exaspim_instrument.json b/examples/exaspim_instrument.json index c05ec822c..7cb56eff9 100644 --- a/examples/exaspim_instrument.json +++ b/examples/exaspim_instrument.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", - "schema_version": "1.0.5", + "schema_version": "2.0.0", "instrument_id": "440_exaSPIM1_20231004", "mouse_platform": null, "modification_date": "2023-10-04", diff --git a/examples/fip_behavior_instrument.json b/examples/fip_behavior_instrument.json index 788678190..c4f0194c7 100644 --- a/examples/fip_behavior_instrument.json +++ b/examples/fip_behavior_instrument.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", - "schema_version": "1.0.5", + "schema_version": "2.0.0", "instrument_id": "447_FIP-Behavior_20000101", "mouse_platform": { "device_type": "Tube", diff --git a/examples/fip_ophys_instrument.json b/examples/fip_ophys_instrument.json index 1de8db8ed..985d44f91 100644 --- a/examples/fip_ophys_instrument.json +++ b/examples/fip_ophys_instrument.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", - "schema_version": "1.0.5", + "schema_version": "2.0.0", "instrument_id": "428_FIP1_20231003", "mouse_platform": { "device_type": "Disc", diff --git a/examples/mri_session.json b/examples/mri_session.json index 0fccc20e2..9262dee85 100644 --- a/examples/mri_session.json +++ b/examples/mri_session.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/session.py", - "schema_version": "1.1.5", + "schema_version": "2.0.0", "protocol_id": [ "dx.doi.org/10.57824/protocols.io.bh7kl4n6" ], diff --git a/examples/multiplane_ophys_session.json b/examples/multiplane_ophys_session.json index 1fc051891..167d76173 100644 --- a/examples/multiplane_ophys_session.json +++ b/examples/multiplane_ophys_session.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/session.py", - "schema_version": "1.1.5", + "schema_version": "2.0.0", "protocol_id": [], "experimenters": [ { diff --git a/examples/ophys_procedures.json b/examples/ophys_procedures.json index 8ebc640c2..be9182ed7 100644 --- a/examples/ophys_procedures.json +++ b/examples/ophys_procedures.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/procedures.py", - "schema_version": "1.2.6", + "schema_version": "2.0.0", "subject_id": "625100", "subject_procedures": [ { diff --git a/examples/ophys_session.json b/examples/ophys_session.json index beb5724ba..dec0278f6 100644 --- a/examples/ophys_session.json +++ b/examples/ophys_session.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/session.py", - "schema_version": "1.1.5", + "schema_version": "2.0.0", "protocol_id": [], "experimenters": [ { diff --git a/examples/procedures.json b/examples/procedures.json index 86e51be85..04c416030 100644 --- a/examples/procedures.json +++ b/examples/procedures.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/procedures.py", - "schema_version": "1.2.6", + "schema_version": "2.0.0", "subject_id": "625100", "subject_procedures": [ { diff --git a/examples/processing.json b/examples/processing.json index 847d3ca4b..90906324b 100644 --- a/examples/processing.json +++ b/examples/processing.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/processing.py", - "schema_version": "1.1.6", + "schema_version": "2.0.0", "processing_pipeline": { "data_processes": [ { diff --git a/examples/quality_control.json b/examples/quality_control.json index ed0a9052b..8054975ad 100644 --- a/examples/quality_control.json +++ b/examples/quality_control.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/quality_control.py", - "schema_version": "1.2.2", + "schema_version": "2.0.0", "evaluations": [ { "modality": { diff --git a/examples/subject.json b/examples/subject.json index 132f5b337..e6ad27bee 100644 --- a/examples/subject.json +++ b/examples/subject.json @@ -1,6 +1,6 @@ { "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/subject.py", - "schema_version": "1.0.3", + "schema_version": "2.0.0", "subject_id": "12345", "sex": "Male", "date_of_birth": "2022-11-22", diff --git a/src/aind_data_schema/core/acquisition.py b/src/aind_data_schema/core/acquisition.py index 58088e7a3..7fcbd58a6 100644 --- a/src/aind_data_schema/core/acquisition.py +++ b/src/aind_data_schema/core/acquisition.py @@ -46,7 +46,7 @@ class Acquisition(DataCoreModel): _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/acquisition.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: SkipValidation[Literal["1.0.6"]] = Field(default="1.0.6") + schema_version: SkipValidation[Literal["2.0.0"]] = Field(default="2.0.0") protocol_id: List[str] = Field(default=[], title="Protocol ID", description="DOI for protocols.io") experimenters: List[Person] = Field( default=[], diff --git a/src/aind_data_schema/core/data_description.py b/src/aind_data_schema/core/data_description.py index 5e5315cee..6380d3ba6 100644 --- a/src/aind_data_schema/core/data_description.py +++ b/src/aind_data_schema/core/data_description.py @@ -39,7 +39,7 @@ class DataDescription(DataCoreModel): _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/data_description.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: SkipValidation[Literal["1.0.5"]] = Field(default="1.0.5") + schema_version: SkipValidation[Literal["2.0.0"]] = Field(default="2.0.0") license: Literal["CC-BY-4.0"] = Field(default="CC-BY-4.0", title="License") subject_id: str = Field( diff --git a/src/aind_data_schema/core/instrument.py b/src/aind_data_schema/core/instrument.py index 2ca3baad9..c820b704f 100644 --- a/src/aind_data_schema/core/instrument.py +++ b/src/aind_data_schema/core/instrument.py @@ -85,7 +85,7 @@ class Instrument(DataCoreModel): # metametadata _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/inst.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: SkipValidation[Literal["1.0.5"]] = Field(default="1.0.5") + schema_version: SkipValidation[Literal["2.0.0"]] = Field(default="2.0.0") # instrument definition instrument_id: str = Field( diff --git a/src/aind_data_schema/core/metadata.py b/src/aind_data_schema/core/metadata.py index 482df21e7..b268869c5 100644 --- a/src/aind_data_schema/core/metadata.py +++ b/src/aind_data_schema/core/metadata.py @@ -68,7 +68,7 @@ class Metadata(DataCoreModel): _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/metadata.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: SkipValidation[Literal["1.1.7"]] = Field(default="1.1.7") + schema_version: SkipValidation[Literal["2.0.0"]] = Field(default="2.0.0") id: UUID = Field( default_factory=uuid4, alias="_id", diff --git a/src/aind_data_schema/core/model.py b/src/aind_data_schema/core/model.py index 1bd610e8e..2bb563cbd 100644 --- a/src/aind_data_schema/core/model.py +++ b/src/aind_data_schema/core/model.py @@ -57,7 +57,7 @@ class Model(DataCoreModel): _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/model.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: Literal["0.0.1"] = Field(default="0.0.1") + schema_version: Literal["2.0.0"] = Field(default="2.0.0") name: str = Field(..., title="Name") license: str = Field(..., title="License") diff --git a/src/aind_data_schema/core/procedures.py b/src/aind_data_schema/core/procedures.py index 904880866..c5ec45bad 100644 --- a/src/aind_data_schema/core/procedures.py +++ b/src/aind_data_schema/core/procedures.py @@ -708,7 +708,7 @@ class Procedures(DataCoreModel): _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/procedures.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: SkipValidation[Literal["1.2.6"]] = Field(default="1.2.6") + schema_version: SkipValidation[Literal["2.0.0"]] = Field(default="2.0.0") subject_id: str = Field( ..., description="Unique identifier for the subject. If this is not a Allen LAS ID, indicate this in the Notes.", diff --git a/src/aind_data_schema/core/processing.py b/src/aind_data_schema/core/processing.py index 39f5d307d..d3f8a3c23 100644 --- a/src/aind_data_schema/core/processing.py +++ b/src/aind_data_schema/core/processing.py @@ -123,7 +123,7 @@ class Processing(DataCoreModel): _DESCRIBED_BY_URL: str = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/processing.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: SkipValidation[Literal["1.1.6"]] = Field(default="1.1.6") + schema_version: SkipValidation[Literal["2.0.0"]] = Field(default="2.0.0") processing_pipeline: PipelineProcess = Field( ..., description="Pipeline used to process data", title="Processing Pipeline" diff --git a/src/aind_data_schema/core/quality_control.py b/src/aind_data_schema/core/quality_control.py index 72d6e64ae..bd9435bf4 100644 --- a/src/aind_data_schema/core/quality_control.py +++ b/src/aind_data_schema/core/quality_control.py @@ -199,7 +199,7 @@ class QualityControl(DataCoreModel): _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/quality_control.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: SkipValidation[Literal["1.2.2"]] = Field(default="1.2.2") + schema_version: SkipValidation[Literal["2.0.0"]] = Field(default="2.0.0") evaluations: List[QCEvaluation] = Field(..., title="Evaluations") notes: Optional[str] = Field(default=None, title="Notes") diff --git a/src/aind_data_schema/core/session.py b/src/aind_data_schema/core/session.py index d182ae245..80df9aa4c 100644 --- a/src/aind_data_schema/core/session.py +++ b/src/aind_data_schema/core/session.py @@ -546,7 +546,7 @@ class Session(DataCoreModel): _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/session.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: SkipValidation[Literal["1.1.5"]] = Field(default="1.1.5") + schema_version: SkipValidation[Literal["2.0.0"]] = Field(default="2.0.0") protocol_id: List[str] = Field(default=[], title="Protocol ID", description="DOI for protocols.io") experimenters: List[Person] = Field( default=[], diff --git a/src/aind_data_schema/core/subject.py b/src/aind_data_schema/core/subject.py index ed15af1dc..dc52a3d79 100644 --- a/src/aind_data_schema/core/subject.py +++ b/src/aind_data_schema/core/subject.py @@ -89,7 +89,7 @@ class Subject(DataCoreModel): _DESCRIBED_BY_URL = DataCoreModel._DESCRIBED_BY_BASE_URL.default + "aind_data_schema/core/subject.py" describedBy: str = Field(default=_DESCRIBED_BY_URL, json_schema_extra={"const": _DESCRIBED_BY_URL}) - schema_version: SkipValidation[Literal["1.0.3"]] = Field(default="1.0.3") + schema_version: SkipValidation[Literal["2.0.0"]] = Field(default="2.0.0") subject_id: str = Field( ..., description="Unique identifier for the subject. If this is not a Allen LAS ID, indicate this in the Notes.", diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index 65b560f3b..e4d5623db 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -59,7 +59,7 @@ behavior_computer = "W10DT72941" ephys_computer = "W10DT72942" -running_wheel = Disc(name="Running Wheel", radius=15) +disc_mouse_platform = Disc(name="Running Wheel", radius=15) digital_out0 = DAQChannel(channel_name="DO0", device_name="Face Camera", channel_type="Digital Output") @@ -246,8 +246,8 @@ basestation, harp, microscope, - running_wheel, ], + mouse_platform=disc_mouse_platform, calibrations=[red_laser_calibration, blue_laser_calibration], ) diff --git a/tests/test_metadata.py b/tests/test_metadata.py index bbb0bc3be..8f6abb83e 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -209,11 +209,12 @@ def test_validate_smartspim_metadata(self): surgery2 = Surgery.model_construct(procedures=[nano_inj]) with self.assertRaises(ValidationError) as context: Metadata( - name="ecephys_655019_2023-04-03_18-17-09", + name="655019_2023-04-03T181709", location="bucket", data_description=DataDescription.model_construct( creation_time=time(12, 12, 12), modalities=[Modality.SPIM], + subject_id="655019", ), subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), From 1396653227edda35ff6ccfcd92948d8b5492f98e Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:41:38 -0800 Subject: [PATCH 26/34] chore: remove dead examples files --- examples/aibs_smartspim_instrument.json | 503 ------------ examples/aibs_smartspim_rig.py | 3 - examples/aind_smartspim_instrument.json | 631 ---------------- examples/fip_behavior_instrument.json | 965 ------------------------ examples/fip_ophys_instrument.json | 903 ---------------------- 5 files changed, 3005 deletions(-) delete mode 100644 examples/aibs_smartspim_instrument.json delete mode 100644 examples/aind_smartspim_instrument.json delete mode 100644 examples/fip_behavior_instrument.json delete mode 100644 examples/fip_ophys_instrument.json diff --git a/examples/aibs_smartspim_instrument.json b/examples/aibs_smartspim_instrument.json deleted file mode 100644 index f950cb0be..000000000 --- a/examples/aibs_smartspim_instrument.json +++ /dev/null @@ -1,503 +0,0 @@ -{ - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", - "schema_version": "2.0.0", - "instrument_id": "440_SmartSPIM2_20231004", - "mouse_platform": null, - "modification_date": "2023-10-04", - "calibrations": null, - "ccf_coordinate_transform": null, - "origin": null, - "instrument_axes": null, - "modalities": [ - { - "name": "Selective plane illumination microscopy", - "abbreviation": "SPIM" - } - ], - "com_ports": [ - { - "hardware_name": "Laser Launch", - "com_port": "COM3" - }, - { - "hardware_name": "ASI Tiger", - "com_port": "COM5" - }, - { - "hardware_name": "MightyZap", - "com_port": "COM4" - } - ], - "instrument_type": "SmartSPIM", - "manufacturer": { - "name": "LifeCanvas", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "temperature_control": false, - "notes": null, - "connections": [], - "components": [ - { - "device_type": "Objective", - "name": "TLX Objective", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "TL4X-SAP", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", - "numerical_aperture": "0.2", - "magnification": "3.6", - "immersion": "multi", - "objective_type": null - }, - { - "device_type": "Detector", - "name": "Camera 1", - "serial_number": "001107", - "manufacturer": { - "name": "Hamamatsu", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "03natb733" - }, - "model": "C14440-20UP", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": null, - "frame_rate": null, - "frame_rate_unit": null, - "immersion": null, - "chroma": null, - "sensor_width": null, - "sensor_height": null, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": null, - "bin_mode": "None", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - { - "device_type": "Laser", - "name": "Ex_488", - "serial_number": "VL01222A11", - "manufacturer": { - "name": "Vortran", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "Stradus", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "All lasers controlled via Vortran VersaLase System", - "wavelength": 488, - "wavelength_unit": "nanometer", - "maximum_power": "150", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Ex_561", - "serial_number": "417927", - "manufacturer": { - "name": "Coherent Scientific", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "031tysd23" - }, - "model": "Obis", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "All lasers controlled via Vortran VersaLase System", - "wavelength": 561, - "wavelength_unit": "nanometer", - "maximum_power": "150", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Ex_647", - "serial_number": "VL01222A10", - "manufacturer": { - "name": "Vortran", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "Stradus", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "All lasers controlled via Vortran VersaLase System", - "wavelength": 647, - "wavelength_unit": "nanometer", - "maximum_power": "160", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Motorized stage", - "name": "Focus stage", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-100", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "100", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #1", - "serial_number": null, - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #2", - "serial_number": null, - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #3", - "serial_number": null, - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #4", - "serial_number": null, - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Scanning stage", - "name": "Sample stage Z", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-50", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "50", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Detection axis", - "stage_axis_name": "Z" - }, - { - "device_type": "Scanning stage", - "name": "Sample stage X", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-50", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "50", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Illumination axis", - "stage_axis_name": "X" - }, - { - "device_type": "Scanning stage", - "name": "Sample stage Y", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-50", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "50", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Perpendicular axis", - "stage_axis_name": "Y" - }, - { - "device_type": "Optical table", - "name": "Table", - "serial_number": null, - "manufacturer": { - "name": "Technical Manufacturing Corporation", - "abbreviation": "TMC", - "registry": null, - "registry_identifier": null - }, - "model": "CleanTop", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "length": "35", - "width": "29", - "table_size_unit": "inch", - "vibration_control": true - }, - { - "device_type": "Filter", - "name": "Em_525", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF03-525/50-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 0, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Em_600", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF01-600/52-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 1, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Em_690", - "serial_number": null, - "manufacturer": { - "name": "Chroma", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "ET690/50m", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 2, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Additional imaging device", - "name": "Lens 1", - "serial_number": null, - "manufacturer": { - "name": "Optotune", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "EL-16-40-TC", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "imaging_device_type": "Tunable lens" - }, - { - "device_type": "Additional imaging device", - "name": "Lens 2", - "serial_number": null, - "manufacturer": { - "name": "Optotune", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "EL-16-40-TC", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "imaging_device_type": "Tunable lens" - }, - { - "device_type": "Additional imaging device", - "name": "Sample chamber", - "serial_number": null, - "manufacturer": { - "name": "LifeCanvas", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "Large-uncoated-glass", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "imaging_device_type": "Sample Chamber" - } - ] -} \ No newline at end of file diff --git a/examples/aibs_smartspim_rig.py b/examples/aibs_smartspim_rig.py index 47a6a3a9f..455ac18c8 100644 --- a/examples/aibs_smartspim_rig.py +++ b/examples/aibs_smartspim_rig.py @@ -39,7 +39,6 @@ ) laser1 = Laser( name="Ex_488", - device_type="Laser", coupling="Single-mode fiber", wavelength=488, maximum_power=150, @@ -50,7 +49,6 @@ ) laser2 = Laser( name="Ex_561", - device_type="Laser", coupling="Single-mode fiber", wavelength=561, maximum_power=150, @@ -61,7 +59,6 @@ ) laser3 = Laser( name="Ex_647", - device_type="Laser", coupling="Single-mode fiber", wavelength=647, maximum_power=160, diff --git a/examples/aind_smartspim_instrument.json b/examples/aind_smartspim_instrument.json deleted file mode 100644 index 51d739589..000000000 --- a/examples/aind_smartspim_instrument.json +++ /dev/null @@ -1,631 +0,0 @@ -{ - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", - "schema_version": "2.0.0", - "instrument_id": "440_SmartSPIM1_20231004", - "mouse_platform": null, - "modification_date": "2023-10-04", - "calibrations": null, - "ccf_coordinate_transform": null, - "origin": null, - "instrument_axes": null, - "modalities": [ - { - "name": "Selective plane illumination microscopy", - "abbreviation": "SPIM" - } - ], - "com_ports": [ - { - "hardware_name": "Laser Launch", - "com_port": "COM4" - }, - { - "hardware_name": "ASI Tiger", - "com_port": "COM3" - }, - { - "hardware_name": "MightyZap", - "com_port": "COM9" - } - ], - "instrument_type": "SmartSPIM", - "manufacturer": { - "name": "LifeCanvas", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "temperature_control": false, - "notes": null, - "connections": [], - "components": [ - { - "device_type": "Objective", - "name": "TLX Objective 1", - "serial_number": "Unknown-1", - "manufacturer": { - "name": "LifeCanvas", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Thorlabs TL4X-SAP with LifeCanvas dipping cap and correction optics.", - "numerical_aperture": "0.2", - "magnification": "3.6", - "immersion": "multi", - "objective_type": null - }, - { - "device_type": "Objective", - "name": "TLX Objective 2", - "serial_number": "Unknown-2", - "manufacturer": { - "name": "LifeCanvas", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Thorlabs TL2X-SAP with LifeCanvas dipping cap and correction optics.", - "numerical_aperture": "0.12", - "magnification": "1.625", - "immersion": "multi", - "objective_type": null - }, - { - "device_type": "Detector", - "name": "Camera 1", - "serial_number": "001284", - "manufacturer": { - "name": "Hamamatsu", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "03natb733" - }, - "model": "C14440-20UP", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": null, - "frame_rate": null, - "frame_rate_unit": null, - "immersion": null, - "chroma": null, - "sensor_width": null, - "sensor_height": null, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": null, - "bin_mode": "None", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - { - "device_type": "Laser", - "name": "Ex_445", - "serial_number": "VL08223M03", - "manufacturer": { - "name": "Vortran", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 445, - "wavelength_unit": "nanometer", - "maximum_power": "200", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Ex_488", - "serial_number": "VL08223M03", - "manufacturer": { - "name": "Vortran", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 488, - "wavelength_unit": "nanometer", - "maximum_power": "150", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Ex_561", - "serial_number": "VL08223M03", - "manufacturer": { - "name": "Vortran", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 561, - "wavelength_unit": "nanometer", - "maximum_power": "150", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Ex_594", - "serial_number": "VL08223M03", - "manufacturer": { - "name": "Vortran", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 594, - "wavelength_unit": "nanometer", - "maximum_power": "100", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Ex_639", - "serial_number": "VL08223M03", - "manufacturer": { - "name": "Vortran", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 639, - "wavelength_unit": "nanometer", - "maximum_power": "160", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Ex_690", - "serial_number": "VL08223M03", - "manufacturer": { - "name": "Vortran", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 690, - "wavelength_unit": "nanometer", - "maximum_power": "160", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Filter", - "name": "Em_469", - "serial_number": "Unknown-0", - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF01-469/35-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 0, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Em_525", - "serial_number": "Unknown-1", - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF01-525/45-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 1, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Em_593", - "serial_number": "Unknown-2", - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF01-593/40-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 2, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Em_624", - "serial_number": "Unknown-3", - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF01-624/40-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 3, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Em_667", - "serial_number": "Unknown-4", - "manufacturer": { - "name": "Chroma", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "ET667/30m", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 4, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Em_700", - "serial_number": "Unknown-5", - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "FELH0700", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Long pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "2.0", - "thickness_unit": "millimeter", - "filter_wheel_index": 5, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Motorized stage", - "name": "Focus stage", - "serial_number": "Unknown-1", - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-100", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "100", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #1", - "serial_number": "Unknown-5", - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #2", - "serial_number": "Unknown-6", - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #3", - "serial_number": "Unknown-7", - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Motorized stage", - "name": "Cylindrical lens #4", - "serial_number": "Unknown-8", - "manufacturer": { - "name": "IR Robot Co", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L12-20F-4", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "41", - "travel_unit": "millimeter", - "firmware": null - }, - { - "device_type": "Scanning stage", - "name": "Sample stage Z", - "serial_number": "Unknown-2", - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-50", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "50", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Detection axis", - "stage_axis_name": "Z" - }, - { - "device_type": "Scanning stage", - "name": "Sample stage X", - "serial_number": "Unknown-3", - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-50", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "50", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Illumination axis", - "stage_axis_name": "X" - }, - { - "device_type": "Scanning stage", - "name": "Sample stage Y", - "serial_number": "Unknown-4", - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-50", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "50", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Perpendicular axis", - "stage_axis_name": "Y" - }, - { - "device_type": "Optical table", - "name": "Main optical table", - "serial_number": "Unknown", - "manufacturer": { - "name": "MKS Newport", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "00k17f049" - }, - "model": "VIS2424-IG2-125A", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "length": "36", - "width": "48", - "table_size_unit": "inch", - "vibration_control": false - } - ] -} \ No newline at end of file diff --git a/examples/fip_behavior_instrument.json b/examples/fip_behavior_instrument.json deleted file mode 100644 index c4f0194c7..000000000 --- a/examples/fip_behavior_instrument.json +++ /dev/null @@ -1,965 +0,0 @@ -{ - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", - "schema_version": "2.0.0", - "instrument_id": "447_FIP-Behavior_20000101", - "mouse_platform": { - "device_type": "Tube", - "name": "mouse_tube_foraging", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "surface_material": null, - "date_surface_replaced": null, - "diameter": "4.0", - "diameter_unit": "centimeter" - }, - "modification_date": "2000-01-01", - "calibrations": [ - { - "calibration_date": "2023-10-02T03:15:22Z", - "device_name": "470nm LED", - "description": "LED calibration", - "input": { - "Power setting": [ - 0 - ] - }, - "output": { - "Power mW": [ - 0.02 - ] - }, - "notes": null - }, - { - "calibration_date": "2023-10-02T03:15:22Z", - "device_name": "415nm LED", - "description": "LED calibration", - "input": { - "Power setting": [ - 0 - ] - }, - "output": { - "Power mW": [ - 0.02 - ] - }, - "notes": null - }, - { - "calibration_date": "2023-10-02T03:15:22Z", - "device_name": "560nm LED", - "description": "LED calibration", - "input": { - "Power setting": [ - 0 - ] - }, - "output": { - "Power mW": [ - 0.02 - ] - }, - "notes": null - } - ], - "ccf_coordinate_transform": null, - "origin": null, - "instrument_axes": null, - "modalities": [ - { - "name": "Behavior", - "abbreviation": "behavior" - }, - { - "name": "Fiber photometry", - "abbreviation": "fib" - } - ], - "com_ports": [], - "instrument_type": null, - "manufacturer": null, - "temperature_control": null, - "notes": null, - "connections": [], - "components": [ - { - "name": "BehaviorVideography_FaceSide", - "device_type": "Camera assembly", - "camera_target": "Face side left", - "camera": { - "device_type": "Detector", - "name": "Side face camera", - "serial_number": "TBD", - "manufacturer": { - "name": "Ailipu Technology Co", - "abbreviation": "Ailipu", - "registry": null, - "registry_identifier": null - }, - "model": "ELP-USBFHD05MT-KL170IR", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "The light intensity sensor was removed; IR illumination is constantly on", - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": "W10DTJK7N0M3", - "frame_rate": "120", - "frame_rate_unit": "hertz", - "immersion": null, - "chroma": "Color", - "sensor_width": 640, - "sensor_height": 480, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": null, - "bin_mode": "Additive", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": { - "name": "Bonsai", - "version": "2.5", - "url": null, - "parameters": {} - }, - "driver": null, - "driver_version": null - }, - "lens": { - "device_type": "Lens", - "name": "Xenocam 1", - "serial_number": "unknown", - "manufacturer": { - "name": "Other", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "XC0922LENS", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Focal Length 9-22mm 1/3\" IR F1.4", - "focal_length": null, - "focal_length_unit": null, - "size": null, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": "f/1.4" - }, - "filter": null, - "position": null - }, - { - "name": "BehaviorVideography_FaceBottom", - "device_type": "Camera assembly", - "camera_target": "Face bottom", - "camera": { - "device_type": "Detector", - "name": "Bottom face Camera", - "serial_number": "TBD", - "manufacturer": { - "name": "Ailipu Technology Co", - "abbreviation": "Ailipu", - "registry": null, - "registry_identifier": null - }, - "model": "ELP-USBFHD05MT-KL170IR", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "The light intensity sensor was removed; IR illumination is constantly on", - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": "W10DTJK7N0M3", - "frame_rate": "120", - "frame_rate_unit": "hertz", - "immersion": null, - "chroma": "Color", - "sensor_width": 640, - "sensor_height": 480, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": null, - "bin_mode": "Additive", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": { - "name": "Bonsai", - "version": "2.5", - "url": null, - "parameters": {} - }, - "driver": null, - "driver_version": null - }, - "lens": { - "device_type": "Lens", - "name": "Xenocam 2", - "serial_number": "unknown", - "manufacturer": { - "name": "Other", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "XC0922LENS", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Focal Length 9-22mm 1/3\" IR F1.4", - "focal_length": null, - "focal_length_unit": null, - "size": null, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": "f/1.4" - }, - "filter": null, - "position": null - }, - { - "device_type": "Harp device", - "name": "Harp Behavior", - "serial_number": null, - "manufacturer": { - "name": "Open Ephys Production Site", - "abbreviation": "OEPS", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "007rkz355" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "data_interface": "USB", - "computer_name": "behavior_computer", - "channels": [ - { - "channel_name": "DO0", - "device_name": "Solenoid Left", - "channel_type": "Digital Output", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DO1", - "device_name": "Solenoid Right", - "channel_type": "Digital Output", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DI0", - "device_name": "Janelia_Lick_Detector Left", - "channel_type": "Digital Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DI1", - "device_name": "Janelia_Lick_Detector Right", - "channel_type": "Digital Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DI3", - "device_name": "Photometry Clock", - "channel_type": "Digital Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - } - ], - "firmware_version": "FTDI version:", - "hardware_version": null, - "harp_device_type": { - "whoami": 1216, - "name": "Behavior" - }, - "core_version": "2.1", - "tag_version": null, - "is_clock_generator": false - }, - { - "device_type": "Reward delivery", - "stage_type": { - "device_type": "Motorized stage", - "name": "NewScaleMotor for LickSpouts", - "serial_number": "xxxx", - "manufacturer": { - "name": "New Scale Technologies", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "15.0", - "travel_unit": "millimeter", - "firmware": "https://github.com/AllenNeuralDynamics/python-newscale,branch: axes-on-target,commit #7c17497" - }, - "reward_spouts": [ - { - "device_type": "Reward spout", - "name": "Left spout", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "side": "Left", - "spout_diameter": "1.2", - "spout_diameter_unit": "millimeter", - "spout_position": null, - "solenoid_valve": { - "device_type": "device", - "name": "Solenoid Left", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "lick_sensor": { - "device_type": "device", - "name": "Janelia_Lick_Detector Left", - "serial_number": null, - "manufacturer": { - "name": "Janelia Research Campus", - "abbreviation": "Janelia", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "013sk6x84" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "lick_sensor_type": "Capacitive" - }, - { - "device_type": "Reward spout", - "name": "Right spout", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "side": "Right", - "spout_diameter": "1.2", - "spout_diameter_unit": "millimeter", - "spout_position": null, - "solenoid_valve": { - "device_type": "device", - "name": "Solenoid Right", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "lick_sensor": { - "device_type": "device", - "name": "Janelia_Lick_Detector Right", - "serial_number": null, - "manufacturer": { - "name": "Janelia Research Campus", - "abbreviation": "Janelia", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "013sk6x84" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "lick_sensor_type": "Capacitive" - } - ] - }, - { - "device_type": "Patch", - "name": "Bundle Branching Fiber-optic Patch Cord", - "serial_number": null, - "manufacturer": { - "name": "Doric", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "059n53q30" - }, - "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "core_diameter": "200", - "numerical_aperture": "0.37", - "photobleaching_date": null - }, - { - "device_type": "Light emitting diode", - "name": "470nm LED", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "M470F3", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 470, - "wavelength_unit": "nanometer", - "bandwidth": null, - "bandwidth_unit": null - }, - { - "device_type": "Light emitting diode", - "name": "415nm LED", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "M415F3", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 415, - "wavelength_unit": "nanometer", - "bandwidth": null, - "bandwidth_unit": null - }, - { - "device_type": "Light emitting diode", - "name": "565nm LED", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "M565F3", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 565, - "wavelength_unit": "nanometer", - "bandwidth": null, - "bandwidth_unit": null - }, - { - "device_type": "Detector", - "name": "Green CMOS", - "serial_number": "21396991", - "manufacturer": { - "name": "Teledyne FLIR", - "abbreviation": "FLIR", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": "BFS-U3-20S40M", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": null, - "frame_rate": null, - "frame_rate_unit": null, - "immersion": "air", - "chroma": "Monochrome", - "sensor_width": null, - "sensor_height": null, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": 16, - "bin_mode": "Additive", - "bin_width": 4, - "bin_height": 4, - "bin_unit": "pixel", - "gain": "2", - "crop_offset_x": 0, - "crop_offset_y": 0, - "crop_width": 200, - "crop_height": 200, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - { - "device_type": "Detector", - "name": "Red CMOS", - "serial_number": "21396991", - "manufacturer": { - "name": "Teledyne FLIR", - "abbreviation": "FLIR", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": "BFS-U3-20S40M", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": null, - "frame_rate": null, - "frame_rate_unit": null, - "immersion": "air", - "chroma": "Monochrome", - "sensor_width": null, - "sensor_height": null, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": 16, - "bin_mode": "Additive", - "bin_width": 4, - "bin_height": 4, - "bin_unit": "pixel", - "gain": "2", - "crop_offset_x": 0, - "crop_offset_y": 0, - "crop_width": 200, - "crop_height": 200, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - { - "device_type": "Objective", - "name": "Objective", - "serial_number": "128022336", - "manufacturer": { - "name": "Nikon", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "0280y9h11" - }, - "model": "CFI Plan Apochromat Lambda D 10x", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "numerical_aperture": "0.45", - "magnification": "10", - "immersion": "air", - "objective_type": null - }, - { - "device_type": "Filter", - "name": "Green emission filter", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF01-520/35-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": 520, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Red emission filter", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF01-600/37-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": 600, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Emission Dichroic", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF562-Di03-25x36", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Dichroic", - "diameter": null, - "width": "36", - "height": "25", - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": 562, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "dual-edge standard epi-fluorescence dichroic beamsplitter", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF493/574-Di01-25x36", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", - "filter_type": "Multiband", - "diameter": null, - "width": "36", - "height": "24", - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Excitation filter 410nm", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "FB410-10", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": 410, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Excitation filter 470nm", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "FB470-10", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": 470, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Excitation filter 560nm", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "FB560-10", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": 560, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "450 Dichroic Longpass Filter", - "serial_number": null, - "manufacturer": { - "name": "Edmund Optics", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": "#69-898", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Dichroic", - "diameter": null, - "width": "35.6", - "height": "25.2", - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": 450, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "500 Dichroic Longpass Filter", - "serial_number": null, - "manufacturer": { - "name": "Edmund Optics", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": "#69-899", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Dichroic", - "diameter": null, - "width": "35.6", - "height": "23.2", - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": 500, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Lens", - "name": "Image focusing lens", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "AC254-080-A-ML", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "focal_length": "80", - "focal_length_unit": "millimeter", - "size": 1, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": null - }, - { - "device_type": "device", - "name": "Photometry Clock", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - } - ] -} \ No newline at end of file diff --git a/examples/fip_ophys_instrument.json b/examples/fip_ophys_instrument.json deleted file mode 100644 index 985d44f91..000000000 --- a/examples/fip_ophys_instrument.json +++ /dev/null @@ -1,903 +0,0 @@ -{ - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", - "schema_version": "2.0.0", - "instrument_id": "428_FIP1_20231003", - "mouse_platform": { - "device_type": "Disc", - "name": "mouse_disc", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "surface_material": null, - "date_surface_replaced": null, - "radius": "8.5", - "radius_unit": "centimeter", - "output": null, - "encoder": null, - "decoder": null, - "encoder_firmware": null - }, - "modification_date": "2023-10-03", - "calibrations": [ - { - "calibration_date": "2023-10-02T03:15:22Z", - "device_name": "470nm LED", - "description": "LED calibration", - "input": { - "Power setting": [ - 1, - 2, - 3 - ] - }, - "output": { - "Power mW": [ - 5, - 10, - 13 - ] - }, - "notes": null - } - ], - "ccf_coordinate_transform": null, - "origin": null, - "instrument_axes": null, - "modalities": [ - { - "name": "Fiber photometry", - "abbreviation": "fib" - } - ], - "com_ports": [], - "instrument_type": null, - "manufacturer": null, - "temperature_control": null, - "notes": null, - "connections": [], - "components": [ - { - "name": "BehaviorVideography_FaceSide", - "device_type": "Camera assembly", - "camera_target": "Face side left", - "camera": { - "device_type": "Detector", - "name": "Side face camera", - "serial_number": "TBD", - "manufacturer": { - "name": "Ailipu Technology Co", - "abbreviation": "Ailipu", - "registry": null, - "registry_identifier": null - }, - "model": "ELP-USBFHD05MT-KL170IR", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "The light intensity sensor was removed; IR illumination is constantly on", - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": "W10DTJK7N0M3", - "frame_rate": "120", - "frame_rate_unit": "hertz", - "immersion": null, - "chroma": "Color", - "sensor_width": 640, - "sensor_height": 480, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": null, - "bin_mode": "Additive", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": { - "name": "Bonsai", - "version": "2.5", - "url": null, - "parameters": {} - }, - "driver": null, - "driver_version": null - }, - "lens": { - "device_type": "Lens", - "name": "Xenocam 1", - "serial_number": "unknown", - "manufacturer": { - "name": "Other", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "XC0922LENS", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Focal Length 9-22mm 1/3\" IR F1.4", - "focal_length": null, - "focal_length_unit": null, - "size": null, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": "f/1.4" - }, - "filter": null, - "position": null - }, - { - "name": "BehaviorVideography_FaceBottom", - "device_type": "Camera assembly", - "camera_target": "Face bottom", - "camera": { - "device_type": "Detector", - "name": "Bottom face Camera", - "serial_number": "TBD", - "manufacturer": { - "name": "Ailipu Technology Co", - "abbreviation": "Ailipu", - "registry": null, - "registry_identifier": null - }, - "model": "ELP-USBFHD05MT-KL170IR", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "The light intensity sensor was removed; IR illumination is constantly on", - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": "W10DTJK7N0M3", - "frame_rate": "120", - "frame_rate_unit": "hertz", - "immersion": null, - "chroma": "Color", - "sensor_width": 640, - "sensor_height": 480, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": null, - "bin_mode": "Additive", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": { - "name": "Bonsai", - "version": "2.5", - "url": null, - "parameters": {} - }, - "driver": null, - "driver_version": null - }, - "lens": { - "device_type": "Lens", - "name": "Xenocam 2", - "serial_number": "unknown", - "manufacturer": { - "name": "Other", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "XC0922LENS", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Focal Length 9-22mm 1/3\" IR F1.4", - "focal_length": null, - "focal_length_unit": null, - "size": null, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": "f/1.4" - }, - "filter": null, - "position": null - }, - { - "device_type": "Patch", - "name": "Bundle Branching Fiber-optic Patch Cord", - "serial_number": null, - "manufacturer": { - "name": "Doric", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "059n53q30" - }, - "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "core_diameter": "200", - "numerical_aperture": "0.37", - "photobleaching_date": null - }, - { - "device_type": "Light emitting diode", - "name": "470nm LED", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "M470F3", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 470, - "wavelength_unit": "nanometer", - "bandwidth": null, - "bandwidth_unit": null - }, - { - "device_type": "Light emitting diode", - "name": "415nm LED", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "M415F3", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 415, - "wavelength_unit": "nanometer", - "bandwidth": null, - "bandwidth_unit": null - }, - { - "device_type": "Light emitting diode", - "name": "565nm LED", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "M565F3", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 565, - "wavelength_unit": "nanometer", - "bandwidth": null, - "bandwidth_unit": null - }, - { - "device_type": "Detector", - "name": "Green CMOS", - "serial_number": "21396991", - "manufacturer": { - "name": "Teledyne FLIR", - "abbreviation": "FLIR", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": "BFS-U3-20S40M", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": null, - "frame_rate": null, - "frame_rate_unit": null, - "immersion": "air", - "chroma": "Monochrome", - "sensor_width": null, - "sensor_height": null, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": 16, - "bin_mode": "Additive", - "bin_width": 4, - "bin_height": 4, - "bin_unit": "pixel", - "gain": "2", - "crop_offset_x": 0, - "crop_offset_y": 0, - "crop_width": 200, - "crop_height": 200, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - { - "device_type": "Detector", - "name": "Red CMOS", - "serial_number": "21396991", - "manufacturer": { - "name": "Teledyne FLIR", - "abbreviation": "FLIR", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": "BFS-U3-20S40M", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "Air", - "computer_name": null, - "frame_rate": null, - "frame_rate_unit": null, - "immersion": "air", - "chroma": "Monochrome", - "sensor_width": null, - "sensor_height": null, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": 16, - "bin_mode": "Additive", - "bin_width": 4, - "bin_height": 4, - "bin_unit": "pixel", - "gain": "2", - "crop_offset_x": 0, - "crop_offset_y": 0, - "crop_width": 200, - "crop_height": 200, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - { - "device_type": "Objective", - "name": "Objective", - "serial_number": "128022336", - "manufacturer": { - "name": "Nikon", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "0280y9h11" - }, - "model": "CFI Plan Apochromat Lambda D 10x", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "numerical_aperture": "0.45", - "magnification": "10", - "immersion": "air", - "objective_type": null - }, - { - "device_type": "Filter", - "name": "Green emission filter", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF01-520/35-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": 520, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Red emission filter", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF01-600/37-25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": 600, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Emission Dichroic", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF562-Di03-25x36", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Dichroic", - "diameter": null, - "width": "36", - "height": "25", - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": 562, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "dual-edge standard epi-fluorescence dichroic beamsplitter", - "serial_number": null, - "manufacturer": { - "name": "Semrock", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "FF493/574-Di01-25x36", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "493/574 nm BrightLine dual-edge standard epi-fluorescence dichroic beamsplitter", - "filter_type": "Multiband", - "diameter": null, - "width": "36", - "height": "24", - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Excitation filter 410nm", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "FB410-10", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": 410, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Excitation filter 470nm", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "FB470-10", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": 470, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "Excitation filter 560nm", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "FB560-10", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Band pass", - "diameter": "25", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": 560, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "450 Dichroic Longpass Filter", - "serial_number": null, - "manufacturer": { - "name": "Edmund Optics", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": "#69-898", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Dichroic", - "diameter": null, - "width": "35.6", - "height": "25.2", - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": 450, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Filter", - "name": "500 Dichroic Longpass Filter", - "serial_number": null, - "manufacturer": { - "name": "Edmund Optics", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": "#69-899", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Dichroic", - "diameter": null, - "width": "35.6", - "height": "23.2", - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": 500, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "Lens", - "name": "Image focusing lens", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "AC254-080-A-ML", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "focal_length": "80", - "focal_length_unit": "millimeter", - "size": 1, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": null - }, - { - "device_type": "Harp device", - "name": "Harp Behavior", - "serial_number": null, - "manufacturer": { - "name": "Open Ephys Production Site", - "abbreviation": "OEPS", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "007rkz355" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "data_interface": "USB", - "computer_name": "behavior_computer", - "channels": [ - { - "channel_name": "DO0", - "device_name": "Solenoid Left", - "channel_type": "Digital Output", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DO1", - "device_name": "Solenoid Right", - "channel_type": "Digital Output", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DI0", - "device_name": "Lick-o-meter Left", - "channel_type": "Digital Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DI1", - "device_name": "Lick-o-meter Right", - "channel_type": "Digital Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DI3", - "device_name": "Photometry Clock", - "channel_type": "Digital Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - } - ], - "firmware_version": null, - "hardware_version": null, - "harp_device_type": { - "whoami": 1216, - "name": "Behavior" - }, - "core_version": "2.1", - "tag_version": null, - "is_clock_generator": false - }, - { - "device_type": "Reward delivery", - "stage_type": null, - "reward_spouts": [ - { - "device_type": "Reward spout", - "name": "Left spout", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "side": "Left", - "spout_diameter": "1.2", - "spout_diameter_unit": "millimeter", - "spout_position": null, - "solenoid_valve": { - "device_type": "device", - "name": "Solenoid Left", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "lick_sensor": { - "device_type": "device", - "name": "Lick-o-meter Left", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "lick_sensor_type": null - }, - { - "device_type": "Reward spout", - "name": "Right spout", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "side": "Right", - "spout_diameter": "1.2", - "spout_diameter_unit": "millimeter", - "spout_position": null, - "solenoid_valve": { - "device_type": "device", - "name": "Solenoid Right", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "lick_sensor": { - "device_type": "device", - "name": "Lick-o-meter Right", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "lick_sensor_type": null - } - ] - }, - { - "device_type": "device", - "name": "Photometry Clock", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - } - ] -} \ No newline at end of file From 695085d99f4bbd9d7ef872c84d05b3f823057d6d Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:41:51 -0800 Subject: [PATCH 27/34] fix: remove device_type fields (came from a bad merge) --- tests/test_inst_acq_compatibility.py | 6 ++---- tests/test_procedures.py | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index e4d5623db..39735d3a1 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -740,10 +740,9 @@ def read_json(filepath: Path) -> dict: name="Left spout", side=d.SpoutSide.LEFT, spout_diameter=1.2, - solenoid_valve=d.Device(device_type="Solenoid", name="Solenoid Left"), + solenoid_valve=d.Device(name="Solenoid Left"), lick_sensor=d.Device( name="Janelia_Lick_Detector Left", - device_type="Lick detector", manufacturer=d.Organization.JANELIA, ), lick_sensor_type=d.LickSensorType("Capacitive"), @@ -752,10 +751,9 @@ def read_json(filepath: Path) -> dict: name="Right spout", side=d.SpoutSide.RIGHT, spout_diameter=1.2, - solenoid_valve=d.Device(device_type="Solenoid", name="Solenoid Right"), + solenoid_valve=d.Device(name="Solenoid Right"), lick_sensor=d.Device( name="Janelia_Lick_Detector Right", - device_type="Lick detector", manufacturer=d.Organization.JANELIA, ), lick_sensor_type=d.LickSensorType("Capacitive"), diff --git a/tests/test_procedures.py b/tests/test_procedures.py index 5db9b2c19..3e633bd58 100644 --- a/tests/test_procedures.py +++ b/tests/test_procedures.py @@ -215,7 +215,6 @@ def test_injection_material_check(self): probes=[ OphysProbe( ophys_probe=FiberProbe( - device_type="Fiber optic probe", name="Probe A", manufacturer=Organization.DORIC, model="8", From c45b5e616425ac7a3bf0caef4bd53aab2d74f72e Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:44:37 -0800 Subject: [PATCH 28/34] fix: bad merge renamed rig files, renaming to instrument and re-generate --- ...ig.json => aibs_smartspim_instrument.json} | 8 +- ...im_rig.py => aibs_smartspim_instrument.py} | 0 ...ig.json => aind_smartspim_instrument.json} | 8 +- ...im_rig.py => aind_smartspim_instrument.py} | 0 .../{ephys_rig.py => ephys_insturment.py} | 0 examples/ephys_rig.json | 898 ------------------ .../{exaspim_rig.py => exaspim_instrument.py} | 0 examples/exaspim_rig.json | 452 --------- ..._rig.json => fip_behavior_instrument.json} | 8 +- ...vior_rig.py => fip_behavior_instrument.py} | 0 ...hys_rig.json => fip_ophys_instrument.json} | 8 +- ...p_ophys_rig.py => fip_ophys_instrument.py} | 0 tests/test_inst_acq_compatibility.py | 2 +- 13 files changed, 17 insertions(+), 1367 deletions(-) rename examples/{aibs_smartspim_rig.json => aibs_smartspim_instrument.json} (98%) rename examples/{aibs_smartspim_rig.py => aibs_smartspim_instrument.py} (100%) rename examples/{aind_smartspim_rig.json => aind_smartspim_instrument.json} (99%) rename examples/{aind_smartspim_rig.py => aind_smartspim_instrument.py} (100%) rename examples/{ephys_rig.py => ephys_insturment.py} (100%) delete mode 100644 examples/ephys_rig.json rename examples/{exaspim_rig.py => exaspim_instrument.py} (100%) delete mode 100644 examples/exaspim_rig.json rename examples/{fip_behavior_rig.json => fip_behavior_instrument.json} (99%) rename examples/{fip_behavior_rig.py => fip_behavior_instrument.py} (100%) rename examples/{fip_ophys_rig.json => fip_ophys_instrument.json} (99%) rename examples/{fip_ophys_rig.py => fip_ophys_instrument.py} (100%) diff --git a/examples/aibs_smartspim_rig.json b/examples/aibs_smartspim_instrument.json similarity index 98% rename from examples/aibs_smartspim_rig.json rename to examples/aibs_smartspim_instrument.json index c5f20c76b..f950cb0be 100644 --- a/examples/aibs_smartspim_rig.json +++ b/examples/aibs_smartspim_instrument.json @@ -1,13 +1,13 @@ { - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", - "schema_version": "1.0.5", - "rig_id": "440_SmartSPIM2_20231004", + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "2.0.0", + "instrument_id": "440_SmartSPIM2_20231004", "mouse_platform": null, "modification_date": "2023-10-04", "calibrations": null, "ccf_coordinate_transform": null, "origin": null, - "rig_axes": null, + "instrument_axes": null, "modalities": [ { "name": "Selective plane illumination microscopy", diff --git a/examples/aibs_smartspim_rig.py b/examples/aibs_smartspim_instrument.py similarity index 100% rename from examples/aibs_smartspim_rig.py rename to examples/aibs_smartspim_instrument.py diff --git a/examples/aind_smartspim_rig.json b/examples/aind_smartspim_instrument.json similarity index 99% rename from examples/aind_smartspim_rig.json rename to examples/aind_smartspim_instrument.json index 396f8d12f..51d739589 100644 --- a/examples/aind_smartspim_rig.json +++ b/examples/aind_smartspim_instrument.json @@ -1,13 +1,13 @@ { - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", - "schema_version": "1.0.5", - "rig_id": "440_SmartSPIM1_20231004", + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "2.0.0", + "instrument_id": "440_SmartSPIM1_20231004", "mouse_platform": null, "modification_date": "2023-10-04", "calibrations": null, "ccf_coordinate_transform": null, "origin": null, - "rig_axes": null, + "instrument_axes": null, "modalities": [ { "name": "Selective plane illumination microscopy", diff --git a/examples/aind_smartspim_rig.py b/examples/aind_smartspim_instrument.py similarity index 100% rename from examples/aind_smartspim_rig.py rename to examples/aind_smartspim_instrument.py diff --git a/examples/ephys_rig.py b/examples/ephys_insturment.py similarity index 100% rename from examples/ephys_rig.py rename to examples/ephys_insturment.py diff --git a/examples/ephys_rig.json b/examples/ephys_rig.json deleted file mode 100644 index ed7d4b035..000000000 --- a/examples/ephys_rig.json +++ /dev/null @@ -1,898 +0,0 @@ -{ - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", - "schema_version": "1.0.5", - "rig_id": "323_EPHYS1_20231003", - "mouse_platform": { - "device_type": "Disc", - "name": "Running Wheel", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "surface_material": null, - "date_surface_replaced": null, - "radius": "15", - "radius_unit": "centimeter", - "output": null, - "encoder": null, - "decoder": null, - "encoder_firmware": null - }, - "modification_date": "2023-10-03", - "calibrations": [ - { - "calibration_date": "2023-10-02T10:22:13Z", - "device_name": "Red Laser", - "description": "Laser power calibration", - "input": { - "power percent": [ - 10, - 20, - 40 - ] - }, - "output": { - "power mW": [ - 1, - 3, - 6 - ] - }, - "notes": null - }, - { - "calibration_date": "2023-10-02T10:22:13Z", - "device_name": "Blue Laser", - "description": "Laser power calibration", - "input": { - "power percent": [ - 10, - 20, - 40 - ] - }, - "output": { - "power mW": [ - 1, - 2, - 7 - ] - }, - "notes": null - } - ], - "ccf_coordinate_transform": null, - "origin": null, - "rig_axes": null, - "modalities": [ - { - "name": "Extracellular electrophysiology", - "abbreviation": "ecephys" - } - ], - "com_ports": [], - "instrument_type": null, - "manufacturer": null, - "temperature_control": null, - "notes": null, - "connections": [], - "components": [ - { - "name": "Ephys_assemblyA", - "device_type": "Ephys assembly", - "manipulator": { - "device_type": "Manipulator", - "name": "Manipulator 1", - "serial_number": "SN2938", - "manufacturer": { - "name": "New Scale Technologies", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "probes": [ - { - "device_type": "Ephys probe", - "name": "Probe A", - "serial_number": "9291019", - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "probe_model": "Neuropixels 1.0", - "lasers": [], - "headstage": null - } - ] - }, - { - "name": "Ephys_assemblyB", - "device_type": "Ephys assembly", - "manipulator": { - "device_type": "Manipulator", - "name": "Manipulator B", - "serial_number": "SN2939", - "manufacturer": { - "name": "New Scale Technologies", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "probes": [ - { - "device_type": "Ephys probe", - "name": "Probe B", - "serial_number": "9291020", - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "probe_model": "Neuropixels 1.0", - "lasers": [], - "headstage": null - } - ] - }, - { - "name": "Face Camera Assembly", - "device_type": "Camera assembly", - "camera_target": "Face side left", - "camera": { - "device_type": "Detector", - "name": "Face Camera", - "serial_number": null, - "manufacturer": { - "name": "Teledyne FLIR", - "abbreviation": "FLIR", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "None", - "computer_name": "W10DT72941", - "frame_rate": "50", - "frame_rate_unit": "hertz", - "immersion": null, - "chroma": "Monochrome", - "sensor_width": 1080, - "sensor_height": 570, - "size_unit": "pixel", - "sensor_format": "1/2.9", - "sensor_format_unit": "inches", - "bit_depth": null, - "bin_mode": "None", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - "lens": { - "device_type": "Lens", - "name": "Camera lens", - "serial_number": null, - "manufacturer": { - "name": "Edmund Optics", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "focal_length": "15", - "focal_length_unit": "millimeter", - "size": null, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": "f/2" - }, - "filter": { - "device_type": "Filter", - "name": "LP filter", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Long pass", - "diameter": null, - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": "850 nm longpass filter" - }, - "position": null - }, - { - "name": "Body Camera Assembly", - "device_type": "Camera assembly", - "camera_target": "Body", - "camera": { - "device_type": "Detector", - "name": "Body Camera", - "serial_number": null, - "manufacturer": { - "name": "Teledyne FLIR", - "abbreviation": "FLIR", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "None", - "computer_name": "W10DT72941", - "frame_rate": "50", - "frame_rate_unit": "hertz", - "immersion": null, - "chroma": "Monochrome", - "sensor_width": 1080, - "sensor_height": 570, - "size_unit": "pixel", - "sensor_format": "1/2.9", - "sensor_format_unit": "inches", - "bit_depth": null, - "bin_mode": "None", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - "lens": { - "device_type": "Lens", - "name": "Camera lens", - "serial_number": null, - "manufacturer": { - "name": "Edmund Optics", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "focal_length": "15", - "focal_length_unit": "millimeter", - "size": null, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": "f/2" - }, - "filter": { - "device_type": "Filter", - "name": "LP filter", - "serial_number": null, - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "filter_type": "Long pass", - "diameter": null, - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": null, - "thickness_unit": null, - "filter_wheel_index": null, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": "850 nm longpass filter" - }, - "position": null - }, - { - "name": "Laser_assemblyA", - "device_type": "Laser assembly", - "manipulator": { - "device_type": "Manipulator", - "name": "Manipulator A", - "serial_number": "SN2937", - "manufacturer": { - "name": "New Scale Technologies", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "lasers": [ - { - "device_type": "Laser", - "name": "Red Laser", - "serial_number": null, - "manufacturer": { - "name": "Oxxius", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 473, - "wavelength_unit": "nanometer", - "maximum_power": null, - "power_unit": "milliwatt", - "coupling": null, - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "Blue Laser", - "serial_number": null, - "manufacturer": { - "name": "Oxxius", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "wavelength": 638, - "wavelength_unit": "nanometer", - "maximum_power": null, - "power_unit": "milliwatt", - "coupling": null, - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - } - ], - "collimator": { - "device_type": "device", - "name": "Collimator A", - "serial_number": null, - "manufacturer": null, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null - }, - "fiber": { - "device_type": "Patch", - "name": "Bundle Branching Fiber-optic Patch Cord", - "serial_number": null, - "manufacturer": { - "name": "Doric", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "059n53q30" - }, - "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "core_diameter": "200", - "numerical_aperture": "0.37", - "photobleaching_date": null - } - }, - { - "device_type": "Neuropixels basestation", - "name": "Basestation Slot 3", - "serial_number": null, - "manufacturer": { - "name": "Interuniversity Microelectronics Center", - "abbreviation": "IMEC", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "02kcbn207" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "data_interface": "PXI", - "computer_name": "W10DT72942", - "channels": [], - "firmware_version": null, - "hardware_version": null, - "basestation_firmware_version": "2.019", - "bsc_firmware_version": "2.199", - "slot": 3, - "ports": [ - { - "index": 1, - "probes": [ - "Probe A" - ] - }, - { - "index": 2, - "probes": [ - "Probe B" - ] - } - ] - }, - { - "device_type": "Harp device", - "name": "Harp Behavior", - "serial_number": null, - "manufacturer": { - "name": "Open Ephys Production Site", - "abbreviation": "OEPS", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "007rkz355" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "data_interface": "USB", - "computer_name": "W10DT72941", - "channels": [ - { - "channel_name": "DO0", - "device_name": "Face Camera", - "channel_type": "Digital Output", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "DO1", - "device_name": "Body Camera", - "channel_type": "Digital Output", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - }, - { - "channel_name": "AI0", - "device_name": "Running Wheel", - "channel_type": "Analog Input", - "port": null, - "channel_index": null, - "sample_rate": null, - "sample_rate_unit": null, - "event_based_sampling": null - } - ], - "firmware_version": null, - "hardware_version": null, - "harp_device_type": { - "whoami": 1216, - "name": "Behavior" - }, - "core_version": "2.1", - "tag_version": null, - "is_clock_generator": false - }, - { - "name": "Stick_assembly_1", - "device_type": "Camera assembly", - "camera_target": "Brain surface", - "camera": { - "device_type": "Detector", - "name": "stick microscope 1", - "serial_number": null, - "manufacturer": { - "name": "Teledyne FLIR", - "abbreviation": "FLIR", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "None", - "computer_name": "W10DT72942", - "frame_rate": "50", - "frame_rate_unit": "hertz", - "immersion": null, - "chroma": "Color", - "sensor_width": 1080, - "sensor_height": 570, - "size_unit": "pixel", - "sensor_format": "1/2.9", - "sensor_format_unit": "inches", - "bit_depth": null, - "bin_mode": "None", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - "lens": { - "device_type": "Lens", - "name": "Probe lens", - "serial_number": null, - "manufacturer": { - "name": "Edmund Optics", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "focal_length": null, - "focal_length_unit": null, - "size": null, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": null - }, - "filter": null, - "position": null - }, - { - "name": "Stick_assembly_2", - "device_type": "Camera assembly", - "camera_target": "Brain surface", - "camera": { - "device_type": "Detector", - "name": "stick microscope 2", - "serial_number": null, - "manufacturer": { - "name": "Teledyne FLIR", - "abbreviation": "FLIR", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "None", - "computer_name": "W10DT72942", - "frame_rate": "50", - "frame_rate_unit": "hertz", - "immersion": null, - "chroma": "Color", - "sensor_width": 1080, - "sensor_height": 570, - "size_unit": "pixel", - "sensor_format": "1/2.9", - "sensor_format_unit": "inches", - "bit_depth": null, - "bin_mode": "None", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - "lens": { - "device_type": "Lens", - "name": "Probe lens", - "serial_number": null, - "manufacturer": { - "name": "Edmund Optics", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "focal_length": null, - "focal_length_unit": null, - "size": null, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": null - }, - "filter": null, - "position": null - }, - { - "name": "Stick_assembly_3", - "device_type": "Camera assembly", - "camera_target": "Brain surface", - "camera": { - "device_type": "Detector", - "name": "stick microscope 3", - "serial_number": null, - "manufacturer": { - "name": "Teledyne FLIR", - "abbreviation": "FLIR", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "None", - "computer_name": "W10DT72942", - "frame_rate": "50", - "frame_rate_unit": "hertz", - "immersion": null, - "chroma": "Color", - "sensor_width": 1080, - "sensor_height": 570, - "size_unit": "pixel", - "sensor_format": "1/2.9", - "sensor_format_unit": "inches", - "bit_depth": null, - "bin_mode": "None", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - "lens": { - "device_type": "Lens", - "name": "Probe lens", - "serial_number": null, - "manufacturer": { - "name": "Edmund Optics", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "focal_length": null, - "focal_length_unit": null, - "size": null, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": null - }, - "filter": null, - "position": null - }, - { - "name": "Stick_assembly_4", - "device_type": "Camera assembly", - "camera_target": "Brain surface", - "camera": { - "device_type": "Detector", - "name": "stick microscope 4", - "serial_number": null, - "manufacturer": { - "name": "Teledyne FLIR", - "abbreviation": "FLIR", - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "USB", - "cooling": "None", - "computer_name": "W10DT72942", - "frame_rate": "50", - "frame_rate_unit": "hertz", - "immersion": null, - "chroma": "Color", - "sensor_width": 1080, - "sensor_height": 570, - "size_unit": "pixel", - "sensor_format": "1/2.9", - "sensor_format_unit": "inches", - "bit_depth": null, - "bin_mode": "None", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - "lens": { - "device_type": "Lens", - "name": "Probe lens", - "serial_number": null, - "manufacturer": { - "name": "Edmund Optics", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "01j1gwp17" - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "focal_length": null, - "focal_length_unit": null, - "size": null, - "lens_size_unit": "inch", - "optimized_wavelength_range": null, - "wavelength_unit": "nanometer", - "max_aperture": null - }, - "filter": null, - "position": null - } - ] -} \ No newline at end of file diff --git a/examples/exaspim_rig.py b/examples/exaspim_instrument.py similarity index 100% rename from examples/exaspim_rig.py rename to examples/exaspim_instrument.py diff --git a/examples/exaspim_rig.json b/examples/exaspim_rig.json deleted file mode 100644 index dc5217382..000000000 --- a/examples/exaspim_rig.json +++ /dev/null @@ -1,452 +0,0 @@ -{ - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", - "schema_version": "1.0.5", - "rig_id": "440_exaSPIM1_20231004", - "mouse_platform": null, - "modification_date": "2023-10-04", - "calibrations": null, - "ccf_coordinate_transform": null, - "origin": null, - "rig_axes": null, - "modalities": [ - { - "name": "Selective plane illumination microscopy", - "abbreviation": "SPIM" - } - ], - "com_ports": [ - { - "hardware_name": "Laser Launch", - "com_port": "COM2" - }, - { - "hardware_name": "ASI Tiger", - "com_port": "COM5" - } - ], - "instrument_type": "exaSPIM", - "manufacturer": { - "name": "Custom", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "temperature_control": false, - "notes": null, - "connections": [], - "components": [ - { - "device_type": "Objective", - "name": "Custom Objective", - "serial_number": null, - "manufacturer": { - "name": "Other", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "JM_DIAMOND 5.0X/1.3", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "manufacturer collaboration between Schneider-Kreuznach and Vieworks", - "numerical_aperture": "0.305", - "magnification": "5", - "immersion": "air", - "objective_type": null - }, - { - "device_type": "Detector", - "name": "Camera 1", - "serial_number": "MB151BAY001", - "manufacturer": { - "name": "Vieworks", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "VNP-604MX", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "detector_type": "Camera", - "data_interface": "Coax", - "cooling": "Air", - "computer_name": null, - "frame_rate": null, - "frame_rate_unit": null, - "immersion": null, - "chroma": null, - "sensor_width": null, - "sensor_height": null, - "size_unit": "pixel", - "sensor_format": null, - "sensor_format_unit": null, - "bit_depth": null, - "bin_mode": "None", - "bin_width": null, - "bin_height": null, - "bin_unit": "pixel", - "gain": null, - "crop_offset_x": null, - "crop_offset_y": null, - "crop_width": null, - "crop_height": null, - "crop_unit": "pixel", - "recording_software": null, - "driver": null, - "driver_version": null - }, - { - "device_type": "Laser", - "name": "LAS-08307", - "serial_number": "LAS-08307", - "manufacturer": { - "name": "Oxxius", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Housed in commercial laser combiner", - "wavelength": 405, - "wavelength_unit": "nanometer", - "maximum_power": "200", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "LAS-08308", - "serial_number": "LAS-08308", - "manufacturer": { - "name": "Oxxius", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Housed in commercial laser combiner", - "wavelength": 488, - "wavelength_unit": "nanometer", - "maximum_power": "200", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "539251", - "serial_number": "539251", - "manufacturer": { - "name": "Oxxius", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Housed in commercial laser combiner", - "wavelength": 561, - "wavelength_unit": "nanometer", - "maximum_power": "200", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Laser", - "name": "LAS-08309", - "serial_number": "LAS-08309", - "manufacturer": { - "name": "Oxxius", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": null, - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Housed in commercial laser combiner", - "wavelength": 638, - "wavelength_unit": "nanometer", - "maximum_power": "200", - "power_unit": "milliwatt", - "coupling": "Single-mode fiber", - "coupling_efficiency": null, - "coupling_efficiency_unit": "percent", - "item_number": null - }, - { - "device_type": "Filter", - "name": "Multiband filter", - "serial_number": null, - "manufacturer": { - "name": "Chroma", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "ZET405/488/561/640mv2", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": "Custom made filter", - "filter_type": "Multiband", - "diameter": "44.05", - "width": null, - "height": null, - "size_unit": "millimeter", - "thickness": "1", - "thickness_unit": "millimeter", - "filter_wheel_index": 0, - "cut_off_wavelength": null, - "cut_on_wavelength": null, - "center_wavelength": null, - "wavelength_unit": "nanometer", - "description": null - }, - { - "device_type": "DAQ Device", - "name": "Dev2", - "serial_number": null, - "manufacturer": { - "name": "National Instruments", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "026exqw73" - }, - "model": "PCIe-6738", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "data_interface": "USB", - "computer_name": "Dev2", - "channels": [ - { - "channel_name": "3", - "device_name": "LAS-08308", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - }, - { - "channel_name": "5", - "device_name": "539251", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - }, - { - "channel_name": "4", - "device_name": "LAS-08309", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - }, - { - "channel_name": "2", - "device_name": "stage-x", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - }, - { - "channel_name": "0", - "device_name": "TL-1", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - }, - { - "channel_name": "6", - "device_name": "LAS-08307", - "channel_type": "Analog Output", - "port": null, - "channel_index": null, - "sample_rate": "10000", - "sample_rate_unit": "hertz", - "event_based_sampling": null - } - ], - "firmware_version": null, - "hardware_version": null - }, - { - "device_type": "Scanning stage", - "name": "stage-x", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "MS-8000", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "1000", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Detection axis", - "stage_axis_name": "X" - }, - { - "device_type": "Scanning stage", - "name": "stage-y", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "MS-8000", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "1000", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Perpendicular axis", - "stage_axis_name": "Y" - }, - { - "device_type": "Scanning stage", - "name": "stage-z", - "serial_number": null, - "manufacturer": { - "name": "Applied Scientific Instrumentation", - "abbreviation": "ASI", - "registry": null, - "registry_identifier": null - }, - "model": "LS-100", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "travel": "100", - "travel_unit": "millimeter", - "firmware": null, - "stage_axis_direction": "Illumination axis", - "stage_axis_name": "Z" - }, - { - "device_type": "Additional imaging device", - "name": "TL-1", - "serial_number": "01", - "manufacturer": { - "name": "Optotune", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "EL-16-40-TC-VIS-20D-C", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "imaging_device_type": "Tunable lens" - }, - { - "device_type": "Additional imaging device", - "name": "RM-1", - "serial_number": "01", - "manufacturer": { - "name": "Thorlabs", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "04gsnvb07" - }, - "model": "K10CR1", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "imaging_device_type": "Rotation mount" - }, - { - "device_type": "Additional imaging device", - "name": "LC-1", - "serial_number": "L6CC-00513", - "manufacturer": { - "name": "Oxxius", - "abbreviation": null, - "registry": null, - "registry_identifier": null - }, - "model": "L6Cc", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "imaging_device_type": "Laser combiner" - }, - { - "device_type": "Optical table", - "name": "Table", - "serial_number": null, - "manufacturer": { - "name": "MKS Newport", - "abbreviation": null, - "registry": { - "name": "Research Organization Registry", - "abbreviation": "ROR" - }, - "registry_identifier": "00k17f049" - }, - "model": "VIS3648-PG2-325A", - "path_to_cad": null, - "port_index": null, - "additional_settings": null, - "notes": null, - "length": "36", - "width": "48", - "table_size_unit": "inch", - "vibration_control": true - } - ] -} \ No newline at end of file diff --git a/examples/fip_behavior_rig.json b/examples/fip_behavior_instrument.json similarity index 99% rename from examples/fip_behavior_rig.json rename to examples/fip_behavior_instrument.json index 2013c29d9..c4f0194c7 100644 --- a/examples/fip_behavior_rig.json +++ b/examples/fip_behavior_instrument.json @@ -1,7 +1,7 @@ { - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", - "schema_version": "1.0.5", - "rig_id": "447_FIP-Behavior_20000101", + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "2.0.0", + "instrument_id": "447_FIP-Behavior_20000101", "mouse_platform": { "device_type": "Tube", "name": "mouse_tube_foraging", @@ -70,7 +70,7 @@ ], "ccf_coordinate_transform": null, "origin": null, - "rig_axes": null, + "instrument_axes": null, "modalities": [ { "name": "Behavior", diff --git a/examples/fip_behavior_rig.py b/examples/fip_behavior_instrument.py similarity index 100% rename from examples/fip_behavior_rig.py rename to examples/fip_behavior_instrument.py diff --git a/examples/fip_ophys_rig.json b/examples/fip_ophys_instrument.json similarity index 99% rename from examples/fip_ophys_rig.json rename to examples/fip_ophys_instrument.json index 5409a1989..985d44f91 100644 --- a/examples/fip_ophys_rig.json +++ b/examples/fip_ophys_instrument.json @@ -1,7 +1,7 @@ { - "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/rig.py", - "schema_version": "1.0.5", - "rig_id": "428_FIP1_20231003", + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "2.0.0", + "instrument_id": "428_FIP1_20231003", "mouse_platform": { "device_type": "Disc", "name": "mouse_disc", @@ -46,7 +46,7 @@ ], "ccf_coordinate_transform": null, "origin": null, - "rig_axes": null, + "instrument_axes": null, "modalities": [ { "name": "Fiber photometry", diff --git a/examples/fip_ophys_rig.py b/examples/fip_ophys_instrument.py similarity index 100% rename from examples/fip_ophys_rig.py rename to examples/fip_ophys_instrument.py diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index 39735d3a1..5e27aa1cc 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -1,4 +1,4 @@ -"""Tests rig session compatibility check""" +"""Tests instrument session compatibility check""" import json import unittest From 1ff075ea02ea49ac915646389869a8602d0955fa Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 13:55:16 -0800 Subject: [PATCH 29/34] tests: more test fixing --- ...phys_insturment.py => ephys_instrument.py} | 0 tests/test_imaging.py | 28 +++++----------- tests/test_inst_acq_compatibility.py | 4 +-- tests/test_metadata.py | 33 +++++++++++-------- 4 files changed, 31 insertions(+), 34 deletions(-) rename examples/{ephys_insturment.py => ephys_instrument.py} (100%) diff --git a/examples/ephys_insturment.py b/examples/ephys_instrument.py similarity index 100% rename from examples/ephys_insturment.py rename to examples/ephys_instrument.py diff --git a/tests/test_imaging.py b/tests/test_imaging.py index 967375c86..7ead1de6e 100644 --- a/tests/test_imaging.py +++ b/tests/test_imaging.py @@ -29,8 +29,8 @@ class ImagingTests(unittest.TestCase): """test imaging schemas""" - def test_constructors(self): - """testing constructors""" + def test_acquisition_constructor(self): + """testing Acquisition constructor""" with self.assertRaises(ValidationError): acq.Acquisition() @@ -76,11 +76,11 @@ def test_constructors(self): self.assertIsNotNone(a) + def test_instrument_constructor(self): + """testing Instrument constructor""" with self.assertRaises(ValidationError): Instrument() - # Generate a valid instrument - objective = Objective( name="TLX Objective", numerical_aperture=0.2, @@ -102,7 +102,8 @@ def test_constructors(self): self.assertIsNotNone(i) - # Instrument type Other requires notes + def test_instrument_type_other_requires_notes(self): + """testing Instrument type Other requires notes""" with self.assertRaises(ValidationError) as e1: Instrument( instrument_id="room_exaSPIM1-1_20231004", @@ -114,7 +115,8 @@ def test_constructors(self): self.assertIn("instrument_id", repr(e1.exception)) - # Modality SPIM requirements components + def test_modality_spim_requires_components(self): + """testing Modality SPIM requires components""" with self.assertRaises(ValidationError) as e2: Instrument( instrument_id="room_exaSPIM1-1_20231004", @@ -124,19 +126,7 @@ def test_constructors(self): manufacturer=Organization.OTHER, ) - expected_exception2 = ( - "2 validation errors for Instrument\n" - "modification_date\n" - " Field required [type=missing, input_value={'instrument_type': 'diSP...[]," - " 'light_sources': []}, input_type=dict]\n" - f" For further information visit https://errors.pydantic.dev/{PYD_VERSION}/v/missing\n" - "notes\n" - " Value error, Notes cannot be empty if manufacturer is Other." - " Describe the manufacturer in the notes field." - " [type=value_error, input_value=None, input_type=NoneType]\n" - f" For further information visit https://errors.pydantic.dev/{PYD_VERSION}/v/value_error" - ) - self.assertEqual(expected_exception2, repr(e2.exception)) + self.assertIn("modality 'SPIM' requires at least one device", repr(e2.exception)) def test_axis(self): """test the axis class""" diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index 5e27aa1cc..3ae5f2f42 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -53,7 +53,7 @@ from aind_data_schema_models.brain_atlas import CCFStructure EXAMPLES_DIR = Path(__file__).parents[1] / "examples" -EPHYS_INST_JSON = EXAMPLES_DIR / "ephys_inst.json" +EPHYS_INST_JSON = EXAMPLES_DIR / "ephys_instrument.json" EPHYS_SESSION_JSON = EXAMPLES_DIR / "ephys_session.json" behavior_computer = "W10DT72941" @@ -761,7 +761,7 @@ def read_json(filepath: Path) -> dict: ], ), ] - additional_devices = [d.Device(device_type="Photometry Clock", name="Photometry Clock")] + additional_devices = [d.Device(name="Photometry Clock")] cls.example_ephys_inst = Instrument.model_validate_json(json.dumps(read_json(EPHYS_INST_JSON))) cls.example_ephys_session = Session.model_validate_json(json.dumps(read_json(EPHYS_SESSION_JSON))) diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 8f6abb83e..b90a6f8c8 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -32,6 +32,16 @@ PYD_VERSION = re.match(r"(\d+.\d+).\d+", pyd_version).group(1) +ephys_assembly = EphysAssembly( + probes=[EphysProbe(probe_model="Neuropixels 1.0", name="Probe A")], + manipulator=Manipulator( + name="Probe manipulator", + manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, + serial_number="4321", + ), + name="Ephys_assemblyA", +) + class TestMetadata(unittest.TestCase): """Class to test Metadata model""" @@ -322,7 +332,12 @@ def test_validate_ecephys_metadata(self): subject_id="655019", ), procedures=Procedures.model_construct(subject_procedures=[surgery1]), - instrument=Instrument.model_construct(modalities=modalities), + instrument=Instrument.model_construct( + modalities=modalities, + components=[ + ephys_assembly, + ], + ), ) self.assertIn( "ECEPHYS metadata missing required file: subject", @@ -350,15 +365,6 @@ def test_validate_ecephys_metadata(self): def test_validate_instrument_session_compatibility(self): """Tests that instrument/session compatibility validator works as expected""" - ephys_assembly = EphysAssembly( - probes=[EphysProbe(probe_model="Neuropixels 1.0", name="Probe A")], - manipulator=Manipulator( - name="Probe manipulator", - manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, - serial_number="4321", - ), - name="Ephys_assemblyA", - ) mouse_platform = MousePlatform.model_construct(name="platform1") inst = Instrument.model_construct( @@ -367,7 +373,6 @@ def test_validate_instrument_session_compatibility(self): modalities=[Modality.ECEPHYS], components=[ephys_assembly], ) - session = Session.model_construct(instrument_id="123_EPHYS2_20230101", mouse_platform_name="platform2") with self.assertRaises(ValidationError) as context: Metadata( name="655019_2023-04-03_18-17-09", @@ -381,9 +386,11 @@ def test_validate_instrument_session_compatibility(self): procedures=Procedures.model_construct(), instrument=inst, processing=Processing.model_construct(), - session=session, + acquisition=Acquisition.model_construct( + instrument_id="123_EPHYS2_20230101", + ), + session=Session.model_construct(instrument_id="123_EPHYS2_20230101"), ) - print(str(context.exception)) self.assertIn( "Instrument ID in session 123_EPHYS2_20230101 does not match the rig's 123_EPHYS1_20220101.", str(context.exception), From de6ddf181f6f2f4f025bfdf127a446ac3223dc09 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 14:07:51 -0800 Subject: [PATCH 30/34] tests: additional fixes --- ephys_instrument.json | 898 ++++++++++++++++++ .../utils/compatibility_check.py | 3 +- tests/resources/ephys_instrument.py | 300 ++++++ tests/test_inst_acq_compatibility.py | 10 +- tests/test_metadata.py | 40 +- 5 files changed, 1224 insertions(+), 27 deletions(-) create mode 100644 ephys_instrument.json create mode 100644 tests/resources/ephys_instrument.py diff --git a/ephys_instrument.json b/ephys_instrument.json new file mode 100644 index 000000000..96157a28c --- /dev/null +++ b/ephys_instrument.json @@ -0,0 +1,898 @@ +{ + "describedBy": "https://raw.githubusercontent.com/AllenNeuralDynamics/aind-data-schema/main/src/aind_data_schema/core/inst.py", + "schema_version": "2.0.0", + "instrument_id": "323_EPHYS1_20231003", + "mouse_platform": { + "device_type": "Disc", + "name": "Running Wheel", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "surface_material": null, + "date_surface_replaced": null, + "radius": "15", + "radius_unit": "centimeter", + "output": null, + "encoder": null, + "decoder": null, + "encoder_firmware": null + }, + "modification_date": "2023-10-03", + "calibrations": [ + { + "calibration_date": "2023-10-02T10:22:13Z", + "device_name": "Red Laser", + "description": "Laser power calibration", + "input": { + "power percent": [ + 10, + 20, + 40 + ] + }, + "output": { + "power mW": [ + 1, + 3, + 6 + ] + }, + "notes": null + }, + { + "calibration_date": "2023-10-02T10:22:13Z", + "device_name": "Blue Laser", + "description": "Laser power calibration", + "input": { + "power percent": [ + 10, + 20, + 40 + ] + }, + "output": { + "power mW": [ + 1, + 2, + 7 + ] + }, + "notes": null + } + ], + "ccf_coordinate_transform": null, + "origin": null, + "instrument_axes": null, + "modalities": [ + { + "name": "Extracellular electrophysiology", + "abbreviation": "ecephys" + } + ], + "com_ports": [], + "instrument_type": null, + "manufacturer": null, + "temperature_control": null, + "notes": null, + "connections": [], + "components": [ + { + "name": "Ephys_assemblyA", + "device_type": "Ephys assembly", + "manipulator": { + "device_type": "Manipulator", + "name": "Manipulator 1", + "serial_number": "SN2938", + "manufacturer": { + "name": "New Scale Technologies", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "probes": [ + { + "device_type": "Ephys probe", + "name": "Probe A", + "serial_number": "9291019", + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "probe_model": "Neuropixels 1.0", + "lasers": [], + "headstage": null + } + ] + }, + { + "name": "Ephys_assemblyB", + "device_type": "Ephys assembly", + "manipulator": { + "device_type": "Manipulator", + "name": "Manipulator B", + "serial_number": "SN2939", + "manufacturer": { + "name": "New Scale Technologies", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "probes": [ + { + "device_type": "Ephys probe", + "name": "Probe B", + "serial_number": "9291020", + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "probe_model": "Neuropixels 1.0", + "lasers": [], + "headstage": null + } + ] + }, + { + "name": "Face Camera Assembly", + "device_type": "Camera assembly", + "camera_target": "Face side left", + "camera": { + "device_type": "Detector", + "name": "Face Camera", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72941", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Monochrome", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Camera lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": "15", + "focal_length_unit": "millimeter", + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": "f/2" + }, + "filter": { + "device_type": "Filter", + "name": "LP filter", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Long pass", + "diameter": null, + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": "850 nm longpass filter" + }, + "position": null + }, + { + "name": "Body Camera Assembly", + "device_type": "Camera assembly", + "camera_target": "Body", + "camera": { + "device_type": "Detector", + "name": "Body Camera", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72941", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Monochrome", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Camera lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": "15", + "focal_length_unit": "millimeter", + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": "f/2" + }, + "filter": { + "device_type": "Filter", + "name": "LP filter", + "serial_number": null, + "manufacturer": { + "name": "Thorlabs", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "04gsnvb07" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "filter_type": "Long pass", + "diameter": null, + "width": null, + "height": null, + "size_unit": "millimeter", + "thickness": null, + "thickness_unit": null, + "filter_wheel_index": null, + "cut_off_wavelength": null, + "cut_on_wavelength": null, + "center_wavelength": null, + "wavelength_unit": "nanometer", + "description": "850 nm longpass filter" + }, + "position": null + }, + { + "name": "Laser_assemblyA", + "device_type": "Laser assembly", + "manipulator": { + "device_type": "Manipulator", + "name": "Manipulator A", + "serial_number": "SN2937", + "manufacturer": { + "name": "New Scale Technologies", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "lasers": [ + { + "device_type": "Laser", + "name": "Red Laser", + "serial_number": null, + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 473, + "wavelength_unit": "nanometer", + "maximum_power": null, + "power_unit": "milliwatt", + "coupling": null, + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + }, + { + "device_type": "Laser", + "name": "Blue Laser", + "serial_number": null, + "manufacturer": { + "name": "Oxxius", + "abbreviation": null, + "registry": null, + "registry_identifier": null + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "wavelength": 638, + "wavelength_unit": "nanometer", + "maximum_power": null, + "power_unit": "milliwatt", + "coupling": null, + "coupling_efficiency": null, + "coupling_efficiency_unit": "percent", + "item_number": null + } + ], + "collimator": { + "device_type": "device", + "name": "Collimator A", + "serial_number": null, + "manufacturer": null, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null + }, + "fiber": { + "device_type": "Patch", + "name": "Bundle Branching Fiber-optic Patch Cord", + "serial_number": null, + "manufacturer": { + "name": "Doric", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "059n53q30" + }, + "model": "BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "core_diameter": "200", + "numerical_aperture": "0.37", + "photobleaching_date": null + } + }, + { + "device_type": "Neuropixels basestation", + "name": "Basestation Slot 3", + "serial_number": null, + "manufacturer": { + "name": "Interuniversity Microelectronics Center", + "abbreviation": "IMEC", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "02kcbn207" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "PXI", + "computer_name": "W10DT72942", + "channels": [], + "firmware_version": null, + "hardware_version": null, + "basestation_firmware_version": "2.019", + "bsc_firmware_version": "2.199", + "slot": 3, + "ports": [ + { + "index": 1, + "probes": [ + "Probe A" + ] + }, + { + "index": 2, + "probes": [ + "Probe B" + ] + } + ] + }, + { + "device_type": "Harp device", + "name": "Harp Behavior", + "serial_number": null, + "manufacturer": { + "name": "Open Ephys Production Site", + "abbreviation": "OEPS", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "007rkz355" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "data_interface": "USB", + "computer_name": "W10DT72941", + "channels": [ + { + "channel_name": "DO0", + "device_name": "Face Camera", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "DO1", + "device_name": "Body Camera", + "channel_type": "Digital Output", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + }, + { + "channel_name": "AI0", + "device_name": "Running Wheel", + "channel_type": "Analog Input", + "port": null, + "channel_index": null, + "sample_rate": null, + "sample_rate_unit": null, + "event_based_sampling": null + } + ], + "firmware_version": null, + "hardware_version": null, + "harp_device_type": { + "whoami": 1216, + "name": "Behavior" + }, + "core_version": "2.1", + "tag_version": null, + "is_clock_generator": false + }, + { + "name": "Stick_assembly_1", + "device_type": "Camera assembly", + "camera_target": "Brain surface", + "camera": { + "device_type": "Detector", + "name": "stick microscope 1", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72942", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Probe lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": null + }, + "filter": null, + "position": null + }, + { + "name": "Stick_assembly_2", + "device_type": "Camera assembly", + "camera_target": "Brain surface", + "camera": { + "device_type": "Detector", + "name": "stick microscope 2", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72942", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Probe lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": null + }, + "filter": null, + "position": null + }, + { + "name": "Stick_assembly_3", + "device_type": "Camera assembly", + "camera_target": "Brain surface", + "camera": { + "device_type": "Detector", + "name": "stick microscope 3", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72942", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Probe lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": null + }, + "filter": null, + "position": null + }, + { + "name": "Stick_assembly_4", + "device_type": "Camera assembly", + "camera_target": "Brain surface", + "camera": { + "device_type": "Detector", + "name": "stick microscope 4", + "serial_number": null, + "manufacturer": { + "name": "Teledyne FLIR", + "abbreviation": "FLIR", + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "detector_type": "Camera", + "data_interface": "USB", + "cooling": "None", + "computer_name": "W10DT72942", + "frame_rate": "50", + "frame_rate_unit": "hertz", + "immersion": null, + "chroma": "Color", + "sensor_width": 1080, + "sensor_height": 570, + "size_unit": "pixel", + "sensor_format": "1/2.9", + "sensor_format_unit": "inches", + "bit_depth": null, + "bin_mode": "None", + "bin_width": null, + "bin_height": null, + "bin_unit": "pixel", + "gain": null, + "crop_offset_x": null, + "crop_offset_y": null, + "crop_width": null, + "crop_height": null, + "crop_unit": "pixel", + "recording_software": null, + "driver": null, + "driver_version": null + }, + "lens": { + "device_type": "Lens", + "name": "Probe lens", + "serial_number": null, + "manufacturer": { + "name": "Edmund Optics", + "abbreviation": null, + "registry": { + "name": "Research Organization Registry", + "abbreviation": "ROR" + }, + "registry_identifier": "01j1gwp17" + }, + "model": null, + "path_to_cad": null, + "port_index": null, + "additional_settings": null, + "notes": null, + "focal_length": null, + "focal_length_unit": null, + "size": null, + "lens_size_unit": "inch", + "optimized_wavelength_range": null, + "wavelength_unit": "nanometer", + "max_aperture": null + }, + "filter": null, + "position": null + } + ] +} \ No newline at end of file diff --git a/src/aind_data_schema/utils/compatibility_check.py b/src/aind_data_schema/utils/compatibility_check.py index 0f5b5f243..a4ea38255 100644 --- a/src/aind_data_schema/utils/compatibility_check.py +++ b/src/aind_data_schema/utils/compatibility_check.py @@ -18,8 +18,7 @@ def _compare_instrument_id(self) -> Optional[ValueError]: """Compares instrument_id""" if self.session.instrument_id != self.inst.instrument_id: return ValueError( - f"Instrument ID in session {self.session.instrument_id} does", - " not match the instrument's {self.inst.instrument_id}.", + f"Instrument ID in session {self.session.instrument_id} does not match the instrument's {self.inst.instrument_id}." ) else: return None diff --git a/tests/resources/ephys_instrument.py b/tests/resources/ephys_instrument.py new file mode 100644 index 000000000..ab9f33879 --- /dev/null +++ b/tests/resources/ephys_instrument.py @@ -0,0 +1,300 @@ +"""Generates an example JSON file for an ephys rig""" + +from datetime import date, datetime, timezone + +from aind_data_schema_models.harp_types import HarpDeviceType +from aind_data_schema_models.modalities import Modality +from aind_data_schema_models.organizations import Organization +from aind_data_schema_models.units import FrequencyUnit, SizeUnit + +from aind_data_schema.components.devices import ( + Calibration, + Camera, + CameraAssembly, + CameraTarget, + DAQChannel, + Device, + Disc, + EphysAssembly, + EphysProbe, + Filter, + HarpDevice, + Laser, + LaserAssembly, + Lens, + Manipulator, + NeuropixelsBasestation, + Patch, + ProbePort, +) +from aind_data_schema.core.instrument import Instrument + +# Describes a rig with running wheel, 2 behavior cameras, one Harp Behavior board, +# one dual-color laser module, one stick microscope, and 2 Neuropixels probes + +behavior_computer = "W10DT72941" +ephys_computer = "W10DT72942" + +running_wheel = Disc(name="Running Wheel", radius=15) + +digital_out0 = DAQChannel(channel_name="DO0", device_name="Face Camera", channel_type="Digital Output") + +digital_out1 = DAQChannel(channel_name="DO1", device_name="Body Camera", channel_type="Digital Output") + +analog_input = DAQChannel(channel_name="AI0", device_name="Running Wheel", channel_type="Analog Input") + +harp = HarpDevice( + name="Harp Behavior", + harp_device_type=HarpDeviceType.BEHAVIOR, + core_version="2.1", + computer_name=behavior_computer, + channels=[digital_out0, digital_out1, analog_input], + is_clock_generator=False, +) + +port1 = ProbePort(index=1, probes=["Probe A"]) + +port2 = ProbePort(index=2, probes=["Probe B"]) + +basestation = NeuropixelsBasestation( + name="Basestation Slot 3", + basestation_firmware_version="2.019", + bsc_firmware_version="2.199", + slot=3, + ports=[port1, port2], + computer_name=ephys_computer, +) + +red_laser = Laser(name="Red Laser", wavelength=473, manufacturer=Organization.OXXIUS) + +blue_laser = Laser(name="Blue Laser", wavelength=638, manufacturer=Organization.OXXIUS) + +laser_assembly = LaserAssembly( + name="Laser_assemblyA", + manipulator=Manipulator( + name="Manipulator A", serial_number="SN2937", manufacturer=Organization.NEW_SCALE_TECHNOLOGIES + ), + lasers=[red_laser, blue_laser], + collimator=Device(name="Collimator A"), + fiber=Patch( + name="Bundle Branching Fiber-optic Patch Cord", + manufacturer=Organization.DORIC, + model="BBP(4)_200/220/900-0.37_Custom_FCM-4xMF1.25", + core_diameter=200, + numerical_aperture=0.37, + ), +) + +probe_camera_1 = Camera( + name="stick microscope 1", + detector_type="Camera", + data_interface="USB", + manufacturer=Organization.FLIR, + computer_name=ephys_computer, + frame_rate=50, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=1080, + sensor_height=570, + sensor_format="1/2.9", + sensor_format_unit="inches", + chroma="Color", +) + +probe_camera_2 = Camera( + name="stick microscope 2", + detector_type="Camera", + data_interface="USB", + manufacturer=Organization.FLIR, + computer_name=ephys_computer, + frame_rate=50, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=1080, + sensor_height=570, + sensor_format="1/2.9", + sensor_format_unit="inches", + chroma="Color", +) + +probe_camera_3 = Camera( + name="stick microscope 3", + detector_type="Camera", + data_interface="USB", + manufacturer=Organization.FLIR, + computer_name=ephys_computer, + frame_rate=50, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=1080, + sensor_height=570, + sensor_format="1/2.9", + sensor_format_unit="inches", + chroma="Color", +) + +probe_camera_4 = Camera( + name="stick microscope 4", + detector_type="Camera", + data_interface="USB", + manufacturer=Organization.FLIR, + computer_name=ephys_computer, + frame_rate=50, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=1080, + sensor_height=570, + sensor_format="1/2.9", + sensor_format_unit="inches", + chroma="Color", +) + +stick_lens = Lens(name="Probe lens", manufacturer=Organization.EDMUND_OPTICS) + +microscope_1 = CameraAssembly( + name="Stick_assembly_1", + camera_target=CameraTarget.BRAIN_SURFACE, # NEEDS TO BE FILLED OUT + camera=probe_camera_1, + lens=stick_lens, +) + +microscope_2 = CameraAssembly( + name="Stick_assembly_2", + camera_target=CameraTarget.BRAIN_SURFACE, # NEEDS TO BE FILLED OUT + camera=probe_camera_2, + lens=stick_lens, +) + +microscope_3 = CameraAssembly( + name="Stick_assembly_3", + camera_target=CameraTarget.BRAIN_SURFACE, # NEEDS TO BE FILLED OUT + camera=probe_camera_3, + lens=stick_lens, +) + +microscope_4 = CameraAssembly( + name="Stick_assembly_4", + camera_target=CameraTarget.BRAIN_SURFACE, # NEEDS TO BE FILLED OUT + camera=probe_camera_4, + lens=stick_lens, +) + +probeA = EphysProbe(name="Probe A", serial_number="9291019", probe_model="Neuropixels 1.0") + +probeB = EphysProbe(name="Probe B", serial_number="9291020", probe_model="Neuropixels 1.0") + +ephys_assemblyA = EphysAssembly( + name="Ephys_assemblyA", + manipulator=Manipulator( + name="Manipulator 1", serial_number="SN2938", manufacturer=Organization.NEW_SCALE_TECHNOLOGIES + ), + probes=[probeA], +) + +ephys_assemblyB = EphysAssembly( + name="Ephys_assemblyB", + manipulator=Manipulator( + name="Manipulator B", serial_number="SN2939", manufacturer=Organization.NEW_SCALE_TECHNOLOGIES + ), + probes=[probeB], +) + +filt = Filter( + name="LP filter", + filter_type="Long pass", + manufacturer=Organization.THORLABS, + description="850 nm longpass filter", +) + +lens = Lens( + name="Camera lens", + focal_length=15, + focal_length_unit=SizeUnit.MM, + manufacturer=Organization.EDMUND_OPTICS, + max_aperture="f/2", +) + +face_camera = Camera( + name="Face Camera", + detector_type="Camera", + data_interface="USB", + manufacturer=Organization.FLIR, + computer_name=behavior_computer, + frame_rate=50, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=1080, + sensor_height=570, + sensor_format="1/2.9", + sensor_format_unit="inches", + chroma="Monochrome", +) + +camassm1 = CameraAssembly( + name="Face Camera Assembly", + camera=face_camera, + camera_target="Face side left", + filter=filt, + lens=lens, +) + +body_camera = Camera( + name="Body Camera", + detector_type="Camera", + data_interface="USB", + manufacturer=Organization.FLIR, + computer_name=behavior_computer, + frame_rate=50, + frame_rate_unit=FrequencyUnit.HZ, + sensor_width=1080, + sensor_height=570, + sensor_format="1/2.9", + sensor_format_unit="inches", + chroma="Monochrome", +) + +camassm2 = CameraAssembly( + name="Body Camera Assembly", + camera=body_camera, + camera_target="Body", + filter=filt, + lens=lens, +) + +# If a timezone isn't specified, the timezone of the computer running this +# script will be used as default + +red_laser_calibration = Calibration( + calibration_date=datetime(2023, 10, 2, 10, 22, 13, tzinfo=timezone.utc), + device_name="Red Laser", + description="Laser power calibration", + input={"power percent": [10, 20, 40]}, + output={"power mW": [1, 3, 6]}, +) + +blue_laser_calibration = Calibration( + calibration_date=datetime(2023, 10, 2, 10, 22, 13, tzinfo=timezone.utc), + device_name="Blue Laser", + description="Laser power calibration", + input={"power percent": [10, 20, 40]}, + output={"power mW": [1, 2, 7]}, +) + +inst = Instrument( + instrument_id="323_EPHYS1_20231003", + modification_date=date(2023, 10, 3), + modalities=[Modality.ECEPHYS], + components=[ + ephys_assemblyA, + ephys_assemblyB, + camassm1, + camassm2, + laser_assembly, + basestation, + harp, + microscope_1, + microscope_2, + microscope_3, + microscope_4, + ], + mouse_platform=running_wheel, + calibrations=[red_laser_calibration, blue_laser_calibration], +) +serialized = inst.model_dump_json() +deserialized = Instrument.model_validate_json(serialized) +deserialized.write_standard_file(prefix="ephys") diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index 3ae5f2f42..b184ad92e 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -863,16 +863,12 @@ def read_json(filepath: Path) -> dict: def test_run_compatibility_check(self): """Tests compatibility check""" - expected_error = ( - "Instrument ID in session 323_EPHYS2-RF_2023-04-24_01 does not match the rig's 323_EPHYS1_20231003." - ) - with self.assertRaises(ValueError) as context: - InstrumentSessionCompatibility(instrument=ephys_inst, session=ephys_session).run_compatibility_check() - self.assertIn(expected_error, str(context.exception)) + + self.assertRaises(ValueError, InstrumentSessionCompatibility(instrument=self.ophys_instrument, session=self.ophys_session).run_compatibility_check) with self.assertRaises(ValueError): InstrumentSessionCompatibility( - instrument=self.ophys_inst, session=self.ophys_session + instrument=self.ophys_instrument, session=self.ophys_session ).run_compatibility_check() def test_check_examples_compatibility(self): diff --git a/tests/test_metadata.py b/tests/test_metadata.py index b90a6f8c8..67873c9ea 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -29,6 +29,7 @@ from aind_data_schema.core.session import Session from aind_data_schema.core.subject import BreedingInfo, Housing, Sex, Species, Subject from tests.resources.spim_instrument import inst +from tests.resources.ephys_instrument import inst as ephys_inst PYD_VERSION = re.match(r"(\d+.\d+).\d+", pyd_version).group(1) @@ -84,8 +85,8 @@ def setUpClass(cls) -> None: processing_pipeline=PipelineProcess(experimenters=[Person(name="Dan Processor")], data_processes=[]), ) - cls.sample_name = "ecephys_655019_2023-04-03_18-17-09" - cls.sample_location = "s3://bucket/ecephys_655019_2023-04-03_18-17-09" + cls.sample_name = "655019_2023-04-03T181709" + cls.sample_location = "s3://bucket/655019_2023-04-03T181709" cls.subject = subject cls.dd = dd cls.procedures = procedures @@ -114,8 +115,8 @@ def test_valid_subject_info(self): ), genotype="Emx1-IRES-Cre;Camk2a-tTA;Ai93(TITL-GCaMP6f)/wt", ) - d1 = Metadata(name="ecephys_655019_2023-04-03_18-17-09", location="bucket", subject=s1) - self.assertEqual("ecephys_655019_2023-04-03_18-17-09", d1.name) + d1 = Metadata(name="655019_2023-04-03T181709", location="bucket", subject=s1) + self.assertEqual("655019_2023-04-03T181709", d1.name) self.assertEqual("bucket", d1.location) self.assertEqual(MetadataStatus.VALID, d1.metadata_status) self.assertEqual(s1, d1.subject) @@ -125,11 +126,11 @@ def test_missing_subject_info(self): present""" d1 = Metadata( - name="ecephys_655019_2023-04-03_18-17-09", + name="655019_2023-04-03T181709", location="bucket", ) self.assertEqual(MetadataStatus.MISSING, d1.metadata_status) - self.assertEqual("ecephys_655019_2023-04-03_18-17-09", d1.name) + self.assertEqual("655019_2023-04-03T181709", d1.name) self.assertEqual("bucket", d1.location) # Assert at least a name and location are required @@ -151,7 +152,7 @@ def test_invalid_core_models(self): metadata_status as INVALID""" # Invalid subject model - d1 = Metadata(name="ecephys_655019_2023-04-03_18-17-09", location="bucket", subject=Subject.model_construct()) + d1 = Metadata(name="655019_2023-04-03T181709", location="bucket", subject=Subject.model_construct()) self.assertEqual(MetadataStatus.INVALID, d1.metadata_status) # Valid subject model, but invalid procedures model @@ -171,7 +172,7 @@ def test_invalid_core_models(self): genotype="Emx1-IRES-Cre;Camk2a-tTA;Ai93(TITL-GCaMP6f)/wt", ) d2 = Metadata( - name="ecephys_655019_2023-04-03_18-17-09", + name="655019_2023-04-03T181709", location="bucket", subject=s2, procedures=Procedures.model_construct(injection_materials=["some materials"]), @@ -180,7 +181,7 @@ def test_invalid_core_models(self): # Tests constructed via dictionary d3 = Metadata( - name="ecephys_655019_2023-04-03_18-17-09", + name="655019_2023-04-03T181709", location="bucket", subject=json.loads(Subject.model_construct().model_dump_json()), ) @@ -297,7 +298,7 @@ def test_multi_modal_metadata(self): session = Session.model_construct(instrument_id="123_EPHYS1_20220101", mouse_platform_name="platform1") m = Metadata( - name="ecephys_655019_2023-04-03_18-17-09", + name="655019_2023-04-03T181709", location="bucket", data_description=DataDescription.model_construct( subject_id="655019", @@ -324,7 +325,7 @@ def test_validate_ecephys_metadata(self): modalities = [Modality.ECEPHYS] with self.assertRaises(ValidationError) as context: Metadata( - name="655019_2023-04-03_18-17-09", + name="655019_2023-04-03T181709", location="bucket", data_description=DataDescription.model_construct( creation_time=time(12, 12, 12), @@ -349,37 +350,40 @@ def test_validate_ecephys_metadata(self): modalities = [Modality.ECEPHYS] with self.assertRaises(ValidationError) as context: Metadata( - name="ecephys_655019_2023-04-03_18-17-09", + name="655019_2023-04-03T181709", location="bucket", data_description=DataDescription.model_construct( creation_time=time(12, 12, 12), modalities=modalities, + subject_id="655019", ), subject=Subject.model_construct(), procedures=Procedures.model_construct(subject_procedures=[surgery2]), - instrument=Instrument.model_construct(modalities=modalities), + instrument=ephys_inst, processing=Processing.model_construct(), session=Session.model_construct(), + acquisition=Acquisition.model_construct(instrument_id="323_EPHYS1_20231003"), ) self.assertIn("Injection is missing injection_materials.", str(context.exception)) def test_validate_instrument_session_compatibility(self): """Tests that instrument/session compatibility validator works as expected""" + modalities = [Modality.ECEPHYS] mouse_platform = MousePlatform.model_construct(name="platform1") inst = Instrument.model_construct( instrument_id="123_EPHYS1_20220101", mouse_platform=mouse_platform, - modalities=[Modality.ECEPHYS], + modalities=modalities, components=[ephys_assembly], ) with self.assertRaises(ValidationError) as context: Metadata( - name="655019_2023-04-03_18-17-09", + name="655019_2023-04-03T181709", location="bucket", data_description=DataDescription.model_construct( creation_time=time(12, 12, 12), - modalities=[Modality.ECEPHYS], + modalities=modalities, subject_id="655019", ), subject=Subject.model_construct(), @@ -389,10 +393,10 @@ def test_validate_instrument_session_compatibility(self): acquisition=Acquisition.model_construct( instrument_id="123_EPHYS2_20230101", ), - session=Session.model_construct(instrument_id="123_EPHYS2_20230101"), + session=Session.model_construct(instrument_id="123_EPHYS2_20230101", mouse_platform_name="platform1"), ) self.assertIn( - "Instrument ID in session 123_EPHYS2_20230101 does not match the rig's 123_EPHYS1_20220101.", + "Instrument ID in session 123_EPHYS2_20230101 does not match the instrument's 123_EPHYS1_20220101.", str(context.exception), ) From 6503010c646f4eb4653e5200ffc2ea4a3a95d16f Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 14:33:24 -0800 Subject: [PATCH 31/34] tests: final fixes to compatibility check code --- .../utils/compatibility_check.py | 94 ++++++++----------- 1 file changed, 39 insertions(+), 55 deletions(-) diff --git a/src/aind_data_schema/utils/compatibility_check.py b/src/aind_data_schema/utils/compatibility_check.py index a4ea38255..dc37dd8f7 100644 --- a/src/aind_data_schema/utils/compatibility_check.py +++ b/src/aind_data_schema/utils/compatibility_check.py @@ -2,6 +2,7 @@ from typing import Optional +from aind_data_schema.components.devices import CameraAssembly, EphysAssembly from aind_data_schema.core.instrument import RewardDelivery, Instrument from aind_data_schema.core.session import Session @@ -36,11 +37,11 @@ def _compare_daq_names(self) -> Optional[ValueError]: session_daqs = [ daq for stream in getattr(self.session, "data_streams", []) for daq in getattr(stream, "daq_names", []) ] - instrument_daqs = [getattr(daq, "name", None) for daq in getattr(self.inst, "daqs", [])] - if not set(session_daqs).issubset(set(instrument_daqs)): + instrument_component_names = [getattr(comp, "name", None) for comp in getattr(self.inst, "components", [])] + if not set(session_daqs).issubset(set(instrument_component_names)): return ValueError( f"daq names in session do not match daq names in inst. " - f"session_daqs: {set(session_daqs)} instrument_daqs: {set(instrument_daqs)}" + f"session_daqs: {set(session_daqs)} instrument components: {set(instrument_component_names)}" ) def _compare_camera_names(self) -> Optional[ValueError]: @@ -50,15 +51,12 @@ def _compare_camera_names(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for camera in getattr(stream, "camera_names", []) ] - instrument_cameras = [ - name - for camera_device in getattr(self.inst, "cameras", []) - for name in (camera_device.camera.name, camera_device.name) - ] - if not set(session_cameras).issubset(set(instrument_cameras)): + instrument_camera_names = [comp.camera.name if isinstance(comp, CameraAssembly) else None for comp in getattr(self.inst, "components", [])] + + if not set(session_cameras).issubset(set(instrument_camera_names)): return ValueError( f"camera names in session do not match camera names in inst. " - f"session_cameras: {set(session_cameras)} instrument_cameras: {set(instrument_cameras)}" + f"session_cameras: {set(session_cameras)} instrument_cameras: {set(instrument_camera_names)}" ) def _compare_light_sources(self) -> Optional[ValueError]: @@ -68,14 +66,12 @@ def _compare_light_sources(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for light_source in getattr(stream, "light_sources", []) ] - instrument_light_sources = [ - getattr(light_source, "name", None) for light_source in getattr(self.inst, "light_sources", []) - ] - if not set(session_light_sources).issubset(set(instrument_light_sources)): + instrument_component_names = [getattr(comp, "name", None) for comp in getattr(self.inst, "components", [])] + if not set(session_light_sources).issubset(set(instrument_component_names)): return ValueError( f"light source names in session do not match light source names in inst. " f"session_light_sources: {set(session_light_sources)} " - f"instrument_light_sources: {set(instrument_light_sources)}" + f"instrument_light_sources: {set(instrument_component_names)}" ) def _compare_ephys_assemblies(self) -> Optional[ValueError]: @@ -85,14 +81,13 @@ def _compare_ephys_assemblies(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for ephys_module in getattr(stream, "ephys_modules") ] - instrument_ephys_assemblies = [ - ephys_assembly.name for ephys_assembly in getattr(self.inst, "ephys_assemblies", []) - ] - if not set(session_ephys_assemblies).issubset(set(instrument_ephys_assemblies)): + instrument_component_names = [comp.name if isinstance(comp, EphysAssembly) else None for comp in getattr(self.inst, "components", [])] + + if not set(session_ephys_assemblies).issubset(set(instrument_component_names)): return ValueError( f"ephys assembly names in session do not match ephys assembly names in inst. " f"session_ephys_assemblies: {set(session_ephys_assemblies)}" - f" instrument_ephys_assemblies: {set(instrument_ephys_assemblies)}" + f" instrument_ephys_assemblies: {set(instrument_component_names)}" ) def _compare_stick_microscopes(self) -> Optional[ValueError]: @@ -102,16 +97,13 @@ def _compare_stick_microscopes(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for stick_microscope in getattr(stream, "stick_microscopes", []) ] - instrument_stick_microscopes = [ - name - for camera_device in getattr(self.inst, "stick_microscopes", []) - for name in (camera_device.camera.name, camera_device.name) - ] - if not set(session_stick_microscopes).issubset(set(instrument_stick_microscopes)): + instrument_camera_names = [comp.camera.name if isinstance(comp, CameraAssembly) else None for comp in getattr(self.inst, "components", [])] + + if not set(session_stick_microscopes).issubset(set(instrument_camera_names)): return ValueError( f"stick microscope names in session do not match stick microscope names in inst. " f"session_stick_microscopes: {set(session_stick_microscopes)} " - f"instrument_stick_microscopes: {set(instrument_stick_microscopes)}" + f"instrument_stick_microscopes: {set(instrument_camera_names)}" ) def _compare_manipulator_modules(self) -> Optional[ValueError]: @@ -121,14 +113,13 @@ def _compare_manipulator_modules(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for manipulator_module in getattr(stream, "manipulator_modules", []) ] - instrument_manipulator_modules = [ - laser_assembly.name for laser_assembly in getattr(self.inst, "laser_assemblies", []) - ] - if not set(session_manipulator_modules).issubset(set(instrument_manipulator_modules)): + instrument_component_names = [getattr(comp, "name", None) for comp in getattr(self.inst, "components", [])] + + if not set(session_manipulator_modules).issubset(set(instrument_component_names)): return ValueError( f"manipulator module names in session do not match manipulator names (laser assemblies) in inst. " f"session_manipulators: {set(session_manipulator_modules)}" - f" instrument_manipulators: {set(instrument_manipulator_modules)}" + f" instrument_manipulators: {set(instrument_component_names)}" ) def _compare_detectors(self) -> Optional[ValueError]: @@ -138,11 +129,12 @@ def _compare_detectors(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for detector in getattr(stream, "detectors", []) ] - instrument_detectors = [detector.name for detector in getattr(self.inst, "detectors", [])] - if not set(session_detectors).issubset(set(instrument_detectors)): + instrument_component_names = [getattr(comp, "name", None) for comp in getattr(self.inst, "components", [])] + + if not set(session_detectors).issubset(set(instrument_component_names)): return ValueError( f"detector names in session do not match detector names in inst. " - f"session_detectors: {set(session_detectors)} instrument_detectors: {set(instrument_detectors)}" + f"session_detectors: {set(session_detectors)} instrument_detectors: {set(instrument_component_names)}" ) def _compare_patch_cords(self) -> Optional[ValueError]: @@ -152,11 +144,12 @@ def _compare_patch_cords(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for fiber_connection in getattr(stream, "fiber_connections", []) ] - instrument_patch_cords = [patch_cord.name for patch_cord in getattr(self.inst, "patch_cords", [])] - if not set(session_patch_cords).issubset(set(instrument_patch_cords)): + instrument_component_names = [getattr(comp, "name", None) for comp in getattr(self.inst, "components", [])] + + if not set(session_patch_cords).issubset(set(instrument_component_names)): return ValueError( f"patch cord names in session do not match patch cord names in inst. " - f"session_patch_cords: {set(session_patch_cords)} instrument_patch_cords: {set(instrument_patch_cords)}" + f"session_patch_cords: {set(session_patch_cords)} instrument_patch_cords: {set(instrument_component_names)}" ) def _compare_fiber_modules(self) -> Optional[ValueError]: @@ -166,14 +159,13 @@ def _compare_fiber_modules(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for fiber_module in getattr(stream, "fiber_modules", []) ] - instrument_fiber_modules = [ - fiber_assembly.name for fiber_assembly in getattr(self.inst, "fiber_assemblies", []) - ] - if not set(session_fiber_modules).issubset(set(instrument_fiber_modules)): + instrument_component_names = [getattr(comp, "name", None) for comp in getattr(self.inst, "components", [])] + + if not set(session_fiber_modules).issubset(set(instrument_component_names)): return ValueError( f"fiber module names in session do not match fiber assembly names in inst. " f"session_fiber_modules: {set(session_fiber_modules)} " - f"instrument_fiber_assemblies: {set(instrument_fiber_modules)}" + f"instrument_fiber_assemblies: {set(instrument_component_names)}" ) def _compare_stimulus_devices(self) -> Optional[ValueError]: @@ -183,21 +175,13 @@ def _compare_stimulus_devices(self) -> Optional[ValueError]: for stimulus_epoch in getattr(self.session, "stimulus_epochs", []) for stimulus_device_name in getattr(stimulus_epoch, "stimulus_device_names") ] - instrument_stimulus_devices = [ - stimulus_device.name - for stimulus_device in getattr(self.inst, "stimulus_devices", []) - if not isinstance(stimulus_device, RewardDelivery) - ] + [ - reward_spout.name - for stimulus_device in getattr(self.inst, "stimulus_devices", []) - if isinstance(stimulus_device, RewardDelivery) - for reward_spout in getattr(stimulus_device, "reward_spouts", []) - ] - if not set(session_stimulus_devices).issubset(set(instrument_stimulus_devices)): + instrument_component_names = [getattr(comp, "name", None) for comp in getattr(self.inst, "components", [])] + + if not set(session_stimulus_devices).issubset(set(instrument_component_names)): return ValueError( f"stimulus device names in session do not match stimulus device names in inst. " f"session_stimulus_devices: {set(session_stimulus_devices)} " - f"instrument_stimulus_devices: {set(instrument_stimulus_devices)}" + f"instrument_stimulus_devices: {set(instrument_component_names)}" ) def run_compatibility_check(self) -> None: From dae201ca23395f46ee7ab4e9263a2db7c7b5777d Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 14:33:55 -0800 Subject: [PATCH 32/34] chore: lint --- .../utils/compatibility_check.py | 14 +++++++++++--- tests/test_bump_schema_versions.py | 4 +++- tests/test_inst_acq_compatibility.py | 7 ++++++- tests/test_metadata.py | 18 ++++++++++++++++-- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/aind_data_schema/utils/compatibility_check.py b/src/aind_data_schema/utils/compatibility_check.py index dc37dd8f7..c49583479 100644 --- a/src/aind_data_schema/utils/compatibility_check.py +++ b/src/aind_data_schema/utils/compatibility_check.py @@ -51,7 +51,10 @@ def _compare_camera_names(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for camera in getattr(stream, "camera_names", []) ] - instrument_camera_names = [comp.camera.name if isinstance(comp, CameraAssembly) else None for comp in getattr(self.inst, "components", [])] + instrument_camera_names = [ + comp.camera.name if isinstance(comp, CameraAssembly) else None + for comp in getattr(self.inst, "components", []) + ] if not set(session_cameras).issubset(set(instrument_camera_names)): return ValueError( @@ -81,7 +84,9 @@ def _compare_ephys_assemblies(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for ephys_module in getattr(stream, "ephys_modules") ] - instrument_component_names = [comp.name if isinstance(comp, EphysAssembly) else None for comp in getattr(self.inst, "components", [])] + instrument_component_names = [ + comp.name if isinstance(comp, EphysAssembly) else None for comp in getattr(self.inst, "components", []) + ] if not set(session_ephys_assemblies).issubset(set(instrument_component_names)): return ValueError( @@ -97,7 +102,10 @@ def _compare_stick_microscopes(self) -> Optional[ValueError]: for stream in getattr(self.session, "data_streams", []) for stick_microscope in getattr(stream, "stick_microscopes", []) ] - instrument_camera_names = [comp.camera.name if isinstance(comp, CameraAssembly) else None for comp in getattr(self.inst, "components", [])] + instrument_camera_names = [ + comp.camera.name if isinstance(comp, CameraAssembly) else None + for comp in getattr(self.inst, "components", []) + ] if not set(session_stick_microscopes).issubset(set(instrument_camera_names)): return ValueError( diff --git a/tests/test_bump_schema_versions.py b/tests/test_bump_schema_versions.py index bbdf71ffb..848219efe 100644 --- a/tests/test_bump_schema_versions.py +++ b/tests/test_bump_schema_versions.py @@ -138,7 +138,9 @@ def test_update_files(self, mock_write: MagicMock): new_inst_version = str(Version.parse(old_inst_version).bump_minor()) # Pycharm raises a warning about types that we can ignore # noinspection PyTypeChecker - handler._update_files({Subject: new_subject_version, Session: new_session_version, Instrument: new_inst_version}) + handler._update_files( + {Subject: new_subject_version, Session: new_session_version, Instrument: new_inst_version} + ) expected_line_change0 = ( f'schema_version: SkipValidation[Literal["{new_subject_version}"]] = Field(default="{new_subject_version}")' diff --git a/tests/test_inst_acq_compatibility.py b/tests/test_inst_acq_compatibility.py index b184ad92e..f675baa88 100644 --- a/tests/test_inst_acq_compatibility.py +++ b/tests/test_inst_acq_compatibility.py @@ -864,7 +864,12 @@ def read_json(filepath: Path) -> dict: def test_run_compatibility_check(self): """Tests compatibility check""" - self.assertRaises(ValueError, InstrumentSessionCompatibility(instrument=self.ophys_instrument, session=self.ophys_session).run_compatibility_check) + self.assertRaises( + ValueError, + InstrumentSessionCompatibility( + instrument=self.ophys_instrument, session=self.ophys_session + ).run_compatibility_check, + ) with self.assertRaises(ValueError): InstrumentSessionCompatibility( diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 67873c9ea..6945a12bb 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -12,7 +12,19 @@ from pydantic import ValidationError from pydantic import __version__ as pyd_version -from aind_data_schema.components.devices import Device, EphysAssembly, EphysProbe, LickSensorType, Manipulator, MotorizedStage, MousePlatform, Objective, RewardDelivery, RewardSpout, SpoutSide +from aind_data_schema.components.devices import ( + Device, + EphysAssembly, + EphysProbe, + LickSensorType, + Manipulator, + MotorizedStage, + MousePlatform, + Objective, + RewardDelivery, + RewardSpout, + SpoutSide, +) from aind_data_schema.components.identifiers import Person from aind_data_schema.core.acquisition import Acquisition from aind_data_schema.core.data_description import DataDescription, Funding @@ -285,7 +297,9 @@ def test_multi_modal_metadata(self): serial_number="xxxx", # grabbing from GUI/SettingFiles manufacturer=Organization.NEW_SCALE_TECHNOLOGIES, travel=15.0, # unit is mm - firmware=("https://github.com/AllenNeuralDynamics/python-newscale,branch: axes-on-target,commit #7c17497"), + firmware=( + "https://github.com/AllenNeuralDynamics/python-newscale,branch: axes-on-target,commit #7c17497" + ), ), ) From ab8560485970078c245b685503b0a08b729a83a1 Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 14:35:47 -0800 Subject: [PATCH 33/34] chore: lint --- src/aind_data_schema/core/data_description.py | 1 - src/aind_data_schema/utils/compatibility_check.py | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/aind_data_schema/core/data_description.py b/src/aind_data_schema/core/data_description.py index 6380d3ba6..17c743b19 100644 --- a/src/aind_data_schema/core/data_description.py +++ b/src/aind_data_schema/core/data_description.py @@ -9,7 +9,6 @@ DataRegex, Group, build_data_name, - datetime_from_name_string, ) from aind_data_schema_models.modalities import Modality from aind_data_schema_models.organizations import Organization diff --git a/src/aind_data_schema/utils/compatibility_check.py b/src/aind_data_schema/utils/compatibility_check.py index c49583479..9a38d0dca 100644 --- a/src/aind_data_schema/utils/compatibility_check.py +++ b/src/aind_data_schema/utils/compatibility_check.py @@ -3,7 +3,7 @@ from typing import Optional from aind_data_schema.components.devices import CameraAssembly, EphysAssembly -from aind_data_schema.core.instrument import RewardDelivery, Instrument +from aind_data_schema.core.instrument import Instrument from aind_data_schema.core.session import Session @@ -18,9 +18,7 @@ def __init__(self, instrument: Instrument, session: Session) -> None: def _compare_instrument_id(self) -> Optional[ValueError]: """Compares instrument_id""" if self.session.instrument_id != self.inst.instrument_id: - return ValueError( - f"Instrument ID in session {self.session.instrument_id} does not match the instrument's {self.inst.instrument_id}." - ) + return ValueError(f"Instrument ID in session {self.session.instrument_id} does not match the instrument's {self.inst.instrument_id}.") # noqa: E501 else: return None @@ -157,7 +155,7 @@ def _compare_patch_cords(self) -> Optional[ValueError]: if not set(session_patch_cords).issubset(set(instrument_component_names)): return ValueError( f"patch cord names in session do not match patch cord names in inst. " - f"session_patch_cords: {set(session_patch_cords)} instrument_patch_cords: {set(instrument_component_names)}" + f"session_patch_cords: {set(session_patch_cords)} instrument_patch_cords: {set(instrument_component_names)}" # noqa: E501 ) def _compare_fiber_modules(self) -> Optional[ValueError]: From 8dd0432ea8e9ad55f83f538f968d0cff10691f8d Mon Sep 17 00:00:00 2001 From: Dan Birman Date: Mon, 27 Jan 2025 14:39:06 -0800 Subject: [PATCH 34/34] chore: bump adsm to 2.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 778b032a5..9ef618ce8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ readme = "README.md" dynamic = ["version"] dependencies = [ - 'aind-data-schema-models>=1.0.0', + 'aind-data-schema-models>=2.0.0', 'dictdiffer', 'pydantic>=2.7', 'inflection',