diff --git a/api/src/opentrons/config/defaults_ot3.py b/api/src/opentrons/config/defaults_ot3.py index 53fab18392c0..cf7807ed385c 100644 --- a/api/src/opentrons/config/defaults_ot3.py +++ b/api/src/opentrons/config/defaults_ot3.py @@ -158,7 +158,7 @@ OT3AxisKind.X: 0.5, OT3AxisKind.Y: 0.5, OT3AxisKind.Z: 0.5, - OT3AxisKind.P: 0.3, + OT3AxisKind.P: 0.8, OT3AxisKind.Z_G: 0.2, OT3AxisKind.Q: 0.3, }, diff --git a/api/tests/opentrons/protocol_engine/resources/test_pipette_data_provider.py b/api/tests/opentrons/protocol_engine/resources/test_pipette_data_provider.py index b65d3eb0d8fb..c909953ddf14 100644 --- a/api/tests/opentrons/protocol_engine/resources/test_pipette_data_provider.py +++ b/api/tests/opentrons/protocol_engine/resources/test_pipette_data_provider.py @@ -110,7 +110,10 @@ def test_configure_virtual_pipette_for_volume( nozzle_map=result1.nozzle_map, back_left_corner_offset=Point(-8.0, -22.0, -259.15), front_right_corner_offset=Point(-8.0, -22.0, -259.15), - pipette_lld_settings={"t50": {"minHeight": 1.0, "minVolume": 0.0}}, + pipette_lld_settings={ + "t20": {"minHeight": 1.5, "minVolume": 0.0}, + "t50": {"minHeight": 1.0, "minVolume": 0.0}, + }, plunger_positions={ "top": 0.0, "bottom": 71.5, @@ -144,7 +147,10 @@ def test_configure_virtual_pipette_for_volume( nozzle_map=result2.nozzle_map, back_left_corner_offset=Point(-8.0, -22.0, -259.15), front_right_corner_offset=Point(-8.0, -22.0, -259.15), - pipette_lld_settings={"t50": {"minHeight": 1.0, "minVolume": 0.0}}, + pipette_lld_settings={ + "t20": {"minHeight": 1.5, "minVolume": 0.0}, + "t50": {"minHeight": 1.0, "minVolume": 0.0}, + }, plunger_positions={ "top": 0.0, "bottom": 61.5, diff --git a/hardware-testing/Makefile b/hardware-testing/Makefile index b8db8ee31f00..ba90a0895803 100755 --- a/hardware-testing/Makefile +++ b/hardware-testing/Makefile @@ -229,7 +229,12 @@ push-no-restart-ot3: sdist Pipfile.lock $(call push-python-sdist,$(host),$(ssh_key),$(ssh_opts),$(sdist_file),/opt/opentrons-robot-server,"hardware_testing",,,$(version_file)) .PHONY: push-ot3 -push-ot3: push-no-restart-ot3 push-plot-webpage-ot3 push-description-ot3 push-labware-ot3 +push-ot3: + $(MAKE) push-no-restart-ot3 + $(MAKE) push-plot-webpage-ot3 + $(MAKE) push-description-ot3 + $(MAKE) push-labware-ot3 + cd ../ && $(MAKE) -C shared-data push-ot3 .PHONE: open-dev-app open-dev-app: @@ -280,27 +285,21 @@ echo "Restarting robot server" &&\ systemctl restart opentrons-robot-server" endef -.PHONY: sync-sw-ot3 -sync-sw-ot3: push-ot3 - cd .. && $(MAKE) push-ot3 host=$(host) +.PHONY: push-ot3-factory +push-ot3-factory: push-ot3 + cd ../ && $(MAKE) push-ot3 host=$(host) + $(MAKE) push-ot3 + +.PHONY: push-ot3-scripts +push-ot3-scripts: push-ot3 + $(MAKE) -C hardware-testing push-ot3 .PHONY: push-ot3-fixture push-ot3-fixture: $(MAKE) apply-patches-fixture - -$(MAKE) sync-sw-ot3 + -$(MAKE) push-ot3-factory $(MAKE) remove-patches-fixture - -.PHONY: push-ot3-lld -push-ot3-lld: - $(MAKE) apply-patches-fixture - cd ../ && $(MAKE) -C shared-data push-ot3 - cd ../ && $(MAKE) -C hardware push-ot3 - cd ../ && $(MAKE) -C hardware_testing push-ot3 - cd ../ && $(MAKE) -C api push-ot3 - $(MAKE) remove-patches-fixture - - .PHONY: apply-patches-fixture apply-patches-fixture: cd ../ && git apply ./hardware-testing/fixture_overrides/*.patch --allow-empty @@ -315,14 +314,7 @@ sync-fw-ot3: $(call push-and-update-fw,$(host),$(ssh_key),$(ssh_opts),$(zip),$(notdir $(zip))) .PHONY: sync-ot3 -sync-ot3: sync-sw-ot3 sync-fw-ot3 - -.PHONY: push-ot3-gravimetric -push-ot3-gravimetric: - $(MAKE) push-ot3 - $(MAKE) apply-patches-gravimetric - cd ../ && $(MAKE) -C shared-data push-ot3 - $(MAKE) remove-patches-gravimetric +sync-ot3: push-ot3-factory sync-fw-ot3 .PHONY: apply-patches-gravimetric apply-patches-gravimetric: diff --git a/hardware-testing/hardware_testing/gravimetric/helpers.py b/hardware-testing/hardware_testing/gravimetric/helpers.py index a5af737b87eb..8a828b5399b5 100644 --- a/hardware-testing/hardware_testing/gravimetric/helpers.py +++ b/hardware-testing/hardware_testing/gravimetric/helpers.py @@ -18,6 +18,7 @@ from opentrons.protocol_api._types import OffDeckType from opentrons.protocol_api._nozzle_layout import NozzleLayout from opentrons.protocols.types import APIVersion +from opentrons.protocols.api_support.deck_type import NoTrashDefinedError from opentrons.hardware_control.thread_manager import ThreadManager from opentrons.hardware_control.types import OT3Mount, Axis, HardwareFeatureFlags from opentrons.hardware_control.ot3api import OT3API @@ -365,19 +366,7 @@ def _drop_tip( if return_tip: pipette.return_tip(home_after=False) else: - if offset is not None: - # we don't actually need the offset, if this is an 8 channel we always center channel - # a1 over the back of the trash - trash_well = pipette.trash_container.well(0) # type: ignore[union-attr] - trash_container = trash_well.center().move( - Point(0, trash_well.width / 2, 0) # type: ignore[union-attr, operator] - ) - pipette.drop_tip( - trash_container, - home_after=False, - ) - else: - pipette.drop_tip(home_after=False) + pipette.drop_tip(home_after=False) if minimum_z_height > 0: cur_location = pipette._get_last_location_by_api_version() if cur_location is not None: @@ -439,7 +428,6 @@ def _load_pipette( if pipette_mount in loaded_pipettes.keys(): return loaded_pipettes[pipette_mount] - trash = ctx.load_labware("opentrons_1_trash_3200ml_fixed", "A3") pipette = ctx.load_instrument(pip_name, pipette_mount) loaded_pipettes = ctx.loaded_instruments assert pipette.max_volume == pipette_volume, ( @@ -462,7 +450,12 @@ def _load_pipette( pipette_movement_conflict.check_safe_for_pipette_movement = ( _override_check_safe_for_pipette_movement ) - pipette.trash_container = trash + try: + trash = pipette.trash_container + except NoTrashDefinedError: + trash = ctx.load_trash_bin("A3") + pipette.trash_container = trash + pass return pipette diff --git a/hardware-testing/hardware_testing/gravimetric/liquid_class/defaults.py b/hardware-testing/hardware_testing/gravimetric/liquid_class/defaults.py index e9fc477446fe..c7d4065daf94 100644 --- a/hardware-testing/hardware_testing/gravimetric/liquid_class/defaults.py +++ b/hardware-testing/hardware_testing/gravimetric/liquid_class/defaults.py @@ -11,8 +11,6 @@ _default_submerge_aspirate_mm = 1.5 _p50_multi_submerge_aspirate_mm = 1.5 _default_submerge_dispense_mm = 1.5 -_p200_default_submerge_aspirate_mm = 2.5 -_p200_default_submerge_dispense_mm = 3.0 _default_retract_mm = 5.0 _default_retract_discontinuity = 20 @@ -31,32 +29,29 @@ 1: DispenseSettings( # 1uL z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_p50_ul_sec_sec, - plunger_flow_rate=57, # ul/sec + plunger_flow_rate=22, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, - blow_out_submerged=7, - blow_out_flow_rate=57, + blow_out_submerged=3.5, ), 10: DispenseSettings( # 10uL z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_p50_ul_sec_sec, - plunger_flow_rate=57, # ul/sec + plunger_flow_rate=22, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, - blow_out_submerged=2, - blow_out_flow_rate=57, + blow_out_submerged=3.5, ), 20: DispenseSettings( # 20uL z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_p50_ul_sec_sec, - plunger_flow_rate=57, # ul/sec + plunger_flow_rate=22, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, - blow_out_submerged=2, - blow_out_flow_rate=57, + blow_out_submerged=3.5, ), }, 50: { # T50 @@ -68,7 +63,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=7, - blow_out_flow_rate=57, ), 10: DispenseSettings( # 10uL z_submerge_depth=_default_submerge_dispense_mm, @@ -78,7 +72,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=2, - blow_out_flow_rate=57, ), 50: DispenseSettings( # 50uL z_submerge_depth=_default_submerge_dispense_mm, @@ -88,7 +81,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=2, - blow_out_flow_rate=57, ), }, }, @@ -102,7 +94,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=318, ), 10: DispenseSettings( # 10uL z_submerge_depth=_default_submerge_dispense_mm, @@ -112,7 +103,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=418, ), 50: DispenseSettings( # 10uL z_submerge_depth=_default_submerge_dispense_mm, @@ -122,7 +112,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=418, ), }, 200: { # T200 @@ -134,7 +123,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=716, ), 50: DispenseSettings( # 50uL z_submerge_depth=_default_submerge_dispense_mm, @@ -144,7 +132,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=716, ), 200: DispenseSettings( # 200uL z_submerge_depth=_default_submerge_dispense_mm, @@ -154,7 +141,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=716, ), }, 1000: { # T1000 @@ -166,7 +152,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=20, - blow_out_flow_rate=160, ), 100: DispenseSettings( # 100uL z_submerge_depth=_default_submerge_dispense_mm, @@ -176,7 +161,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=20, - blow_out_flow_rate=716, ), 1000: DispenseSettings( # 1000uL z_submerge_depth=_default_submerge_dispense_mm, @@ -186,7 +170,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=20, - blow_out_flow_rate=716, ), }, }, @@ -202,7 +185,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=6, - blow_out_flow_rate=57, ), 10: DispenseSettings( # 5uL z_submerge_depth=_default_submerge_dispense_mm, @@ -212,7 +194,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=2, - blow_out_flow_rate=57, ), 50: DispenseSettings( # 50uL z_submerge_depth=_default_submerge_dispense_mm, @@ -222,7 +203,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=2, - blow_out_flow_rate=57, ), }, }, @@ -236,7 +216,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=318, ), 10: DispenseSettings( # 10uL z_submerge_depth=_default_submerge_dispense_mm, @@ -246,7 +225,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=478, ), 50: DispenseSettings( # 50uL z_submerge_depth=_default_submerge_dispense_mm, @@ -256,7 +234,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=478, ), }, 200: { # T200 @@ -268,7 +245,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=716, ), 50: DispenseSettings( # 50uL z_submerge_depth=_default_submerge_dispense_mm, @@ -278,7 +254,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=716, ), 200: DispenseSettings( # 200uL z_submerge_depth=_default_submerge_dispense_mm, @@ -288,7 +263,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=716, ), }, 1000: { # T1000 @@ -300,7 +274,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=20, - blow_out_flow_rate=160, ), 100: DispenseSettings( # 100uL z_submerge_depth=_default_submerge_dispense_mm, @@ -310,7 +283,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=20, - blow_out_flow_rate=716, ), 1000: DispenseSettings( # 1000uL z_submerge_depth=_default_submerge_dispense_mm, @@ -320,7 +292,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=20, - blow_out_flow_rate=716, ), }, }, @@ -336,7 +307,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=80, ), 5: DispenseSettings( # 10uL z_submerge_depth=_default_submerge_dispense_mm, @@ -346,7 +316,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=80, ), 20: DispenseSettings( # 50uL z_submerge_depth=_default_submerge_dispense_mm, @@ -356,7 +325,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=80, ), }, 50: { # T50 @@ -368,7 +336,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=80, ), 10: DispenseSettings( # 10uL z_submerge_depth=_default_submerge_dispense_mm, @@ -378,7 +345,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=80, ), 50: DispenseSettings( # 50uL z_submerge_depth=_default_submerge_dispense_mm, @@ -388,7 +354,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=80, ), }, 200: { # T200 @@ -400,7 +365,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=80, ), 50: DispenseSettings( # 50uL z_submerge_depth=_default_submerge_dispense_mm, @@ -410,7 +374,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=80, ), 200: DispenseSettings( # 200uL z_submerge_depth=_default_submerge_dispense_mm, @@ -420,7 +383,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=80, ), }, 1000: { # T1000 @@ -432,7 +394,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=20, - blow_out_flow_rate=80, ), 100: DispenseSettings( # 100uL z_submerge_depth=_default_submerge_dispense_mm, @@ -442,7 +403,6 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=20, - blow_out_flow_rate=80, ), 1000: DispenseSettings( # 1000uL z_submerge_depth=_default_submerge_dispense_mm, @@ -452,105 +412,95 @@ z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=20, - blow_out_flow_rate=80, ), }, }, 200: { # P200 20: { # T20 1: DispenseSettings( # 5uL - z_submerge_depth=_p200_default_submerge_dispense_mm, + z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, plunger_flow_rate=6.5, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=3.5, - blow_out_flow_rate=22, ), 5: DispenseSettings( # 10uL - z_submerge_depth=_p200_default_submerge_dispense_mm, + z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, plunger_flow_rate=6.5, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=7, - blow_out_flow_rate=5, ), 20: DispenseSettings( # 20uL - z_submerge_depth=_p200_default_submerge_dispense_mm, + z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, plunger_flow_rate=6.5, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=7, - blow_out_flow_rate=5, ), }, 50: { # T50 5: DispenseSettings( # 5uL - z_submerge_depth=_p200_default_submerge_dispense_mm, + z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, - plunger_flow_rate=6.5, # ul/sec + plunger_flow_rate=80, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=10, ), 10: DispenseSettings( # 10uL - z_submerge_depth=_p200_default_submerge_dispense_mm, + z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, - plunger_flow_rate=6.5, # ul/sec + plunger_flow_rate=80, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=10, ), 50: DispenseSettings( # 50uL - z_submerge_depth=_p200_default_submerge_dispense_mm, + z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, - plunger_flow_rate=6.5, # ul/sec + plunger_flow_rate=80, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, blow_out_submerged=5, - blow_out_flow_rate=10, ), }, 200: { # T200 5: DispenseSettings( # 5uL - z_submerge_depth=_p200_default_submerge_dispense_mm, + z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, - plunger_flow_rate=15, # ul/sec + plunger_flow_rate=80, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, - blow_out_submerged=15, - blow_out_flow_rate=10, + blow_out_submerged=5, ), 50: DispenseSettings( # 50uL - z_submerge_depth=_p200_default_submerge_dispense_mm, + z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, - plunger_flow_rate=15, # ul/sec + plunger_flow_rate=80, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, - blow_out_submerged=15, - blow_out_flow_rate=10, + blow_out_submerged=5, ), 200: DispenseSettings( # 200uL - z_submerge_depth=_p200_default_submerge_dispense_mm, + z_submerge_depth=_default_submerge_dispense_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, - plunger_flow_rate=15, # ul/sec + plunger_flow_rate=80, # ul/sec delay=_default_dispense_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, - blow_out_submerged=15, - blow_out_flow_rate=10, + blow_out_submerged=5, ), }, }, @@ -564,7 +514,7 @@ 1: AspirateSettings( # 1uL z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_p50_ul_sec_sec, - plunger_flow_rate=35, # ul/sec + plunger_flow_rate=22, # ul/sec delay=_default_aspirate_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, @@ -574,7 +524,7 @@ 10: AspirateSettings( # 10uL z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_p50_ul_sec_sec, - plunger_flow_rate=23.5, # ul/sec + plunger_flow_rate=22, # ul/sec delay=_default_aspirate_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, @@ -584,7 +534,7 @@ 20: AspirateSettings( # 20uL z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_p50_ul_sec_sec, - plunger_flow_rate=35, # ul/sec + plunger_flow_rate=22, # ul/sec delay=_default_aspirate_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, @@ -992,7 +942,7 @@ 200: { # P200 20: { # T20 1: AspirateSettings( # 5uL - z_submerge_depth=_p200_default_submerge_aspirate_mm, + z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, plunger_flow_rate=6.5, # ul/sec delay=_default_aspirate_delay_seconds, @@ -1002,7 +952,7 @@ trailing_air_gap=0.1, ), 5: AspirateSettings( # 10uL - z_submerge_depth=_p200_default_submerge_aspirate_mm, + z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, plunger_flow_rate=6.5, # ul/sec delay=_default_aspirate_delay_seconds, @@ -1012,7 +962,7 @@ trailing_air_gap=0.1, ), 20: AspirateSettings( # 20uL - z_submerge_depth=_p200_default_submerge_aspirate_mm, + z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, plunger_flow_rate=6.5, # ul/sec delay=_default_aspirate_delay_seconds, @@ -1024,7 +974,7 @@ }, 50: { # T50 5: AspirateSettings( # 5uL - z_submerge_depth=_p200_default_submerge_aspirate_mm, + z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, plunger_flow_rate=6.5, # ul/sec delay=_default_aspirate_delay_seconds, @@ -1034,7 +984,7 @@ trailing_air_gap=0.1, ), 10: AspirateSettings( # 10uL - z_submerge_depth=_p200_default_submerge_aspirate_mm, + z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, plunger_flow_rate=6.5, # ul/sec delay=_default_aspirate_delay_seconds, @@ -1044,7 +994,7 @@ trailing_air_gap=0.1, ), 50: AspirateSettings( # 50uL - z_submerge_depth=_p200_default_submerge_aspirate_mm, + z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, plunger_flow_rate=6.5, # ul/sec delay=_default_aspirate_delay_seconds, @@ -1056,9 +1006,9 @@ }, 200: { # T200 5: AspirateSettings( # 5uL - z_submerge_depth=_p200_default_submerge_aspirate_mm, + z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, - plunger_flow_rate=15, # ul/sec + plunger_flow_rate=80, # ul/sec delay=_default_aspirate_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, @@ -1066,9 +1016,9 @@ trailing_air_gap=2, ), 50: AspirateSettings( # 50uL - z_submerge_depth=_p200_default_submerge_aspirate_mm, + z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, - plunger_flow_rate=15, # ul/sec + plunger_flow_rate=80, # ul/sec delay=_default_aspirate_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, @@ -1076,9 +1026,9 @@ trailing_air_gap=3.5, ), 200: AspirateSettings( # 200uL - z_submerge_depth=_p200_default_submerge_aspirate_mm, + z_submerge_depth=_default_submerge_aspirate_mm, plunger_acceleration=_default_accel_96ch_ul_sec_sec, - plunger_flow_rate=15, # ul/sec + plunger_flow_rate=80, # ul/sec delay=_default_aspirate_delay_seconds, z_retract_discontinuity=_default_retract_discontinuity, z_retract_height=_default_retract_mm, diff --git a/hardware-testing/hardware_testing/gravimetric/liquid_class/definition.py b/hardware-testing/hardware_testing/gravimetric/liquid_class/definition.py index 9f03efe759a5..229d16c4b1ac 100644 --- a/hardware-testing/hardware_testing/gravimetric/liquid_class/definition.py +++ b/hardware-testing/hardware_testing/gravimetric/liquid_class/definition.py @@ -27,7 +27,6 @@ class DispenseSettings(LiquidSettings): """Dispense Settings.""" blow_out_submerged: float # microliters - blow_out_flow_rate: float # ul/s @dataclass @@ -91,8 +90,5 @@ def _interp(lower: float, upper: float) -> float: blow_out_submerged=_interp( a.dispense.blow_out_submerged, b.dispense.blow_out_submerged ), - blow_out_flow_rate=_interp( - a.dispense.blow_out_flow_rate, b.dispense.blow_out_flow_rate - ), ), ) diff --git a/hardware-testing/hardware_testing/gravimetric/liquid_class/pipetting.py b/hardware-testing/hardware_testing/gravimetric/liquid_class/pipetting.py index 81fb1180580a..36c802a55e41 100644 --- a/hardware-testing/hardware_testing/gravimetric/liquid_class/pipetting.py +++ b/hardware-testing/hardware_testing/gravimetric/liquid_class/pipetting.py @@ -313,7 +313,7 @@ def _dispense_on_retract() -> None: # PHASE 1: APPROACH pipette.flow_rate.aspirate = liquid_class.aspirate.plunger_flow_rate pipette.flow_rate.dispense = liquid_class.dispense.plunger_flow_rate - pipette.flow_rate.blow_out = liquid_class.dispense.blow_out_flow_rate + pipette.flow_rate.blow_out = liquid_class.dispense.plunger_flow_rate pipette.move_to(well.bottom(approach_mm).move(channel_offset)) _aspirate_on_approach() if aspirate or mix else _dispense_on_approach() diff --git a/hardware-testing/hardware_testing/gravimetric/overrides/api.patch b/hardware-testing/hardware_testing/gravimetric/overrides/api.patch deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/hardware-testing/hardware_testing/gravimetric/overrides/hardware.patch b/hardware-testing/hardware_testing/gravimetric/overrides/hardware.patch deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/hardware-testing/hardware_testing/gravimetric/overrides/robot-server.patch b/hardware-testing/hardware_testing/gravimetric/overrides/robot-server.patch deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/hardware-testing/hardware_testing/gravimetric/workarounds.py b/hardware-testing/hardware_testing/gravimetric/workarounds.py index 116defac0fe1..cbe41fb278b4 100644 --- a/hardware-testing/hardware_testing/gravimetric/workarounds.py +++ b/hardware-testing/hardware_testing/gravimetric/workarounds.py @@ -83,17 +83,25 @@ def get_latest_offset_for_labware( labware_offsets: List[LabwareOffset], labware: Labware ) -> Point: """Get latest offset for labware.""" + lw_uri = str(labware.uri) def _is_offset_present(_o: LabwareOffset) -> bool: _v = _o.vector return _v.x != 0 or _v.y != 0 or _v.z != 0 def _offset_applies_to_labware(_o: LabwareOffset) -> bool: - if ( - _o.location.slotName.value != labware.parent - or _o.definitionUri != labware.uri - ): + if _o.location.slotName.value != labware.parent: return False + offset_uri = _o.definitionUri + if offset_uri[0:-1] != lw_uri[0:-1]: # drop schema version number + # ui.print_info(f"{_o} does not apply {offset_uri} != {lw_uri}") + # NOTE: we're allowing tip-rack adapters to share offsets + # because it doesn't make a difference which volume + # of tip it holds + o_is_adp = "custom_beta" in offset_uri and "_adp" in offset_uri + l_is_adp = "custom_beta" in lw_uri and "_adp" in lw_uri + if not o_is_adp or not l_is_adp: + return False return _is_offset_present(_o) lw_offsets = [ diff --git a/hardware-testing/hardware_testing/modules/flex_stacker_dvt_qc/utils.py b/hardware-testing/hardware_testing/modules/flex_stacker_dvt_qc/utils.py index 23387278f6a0..ab296bd0db43 100644 --- a/hardware-testing/hardware_testing/modules/flex_stacker_dvt_qc/utils.py +++ b/hardware-testing/hardware_testing/modules/flex_stacker_dvt_qc/utils.py @@ -115,7 +115,7 @@ def generate_tof_baseline( def labware_detected( - histogram: Dict[int, List[int]], + histogram: Dict[int, List[float]], sensor: TOFSensor, bins: List[int], zones: List[int], diff --git a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py index 981c070db6d2..f13e0273aa2a 100644 --- a/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py +++ b/hardware-testing/hardware_testing/opentrons_api/helpers_ot3.py @@ -640,8 +640,8 @@ async def move_tip_motor_relative_ot3( current_gear_pos = api._backend.gear_motor_position or 0.0 target_pos = current_gear_pos + distance - if speed is not None and distance < 0: - speed *= -1 + # if speed is not None and distance < 0: + # speed *= -1 _move_coro = api._backend.tip_action(current_gear_pos, [(target_pos, speed or 400)]) if motor_current is None: @@ -1114,8 +1114,14 @@ def get_pipette_serial_ot3(pipette: Union[PipetteOT2, PipetteOT3]) -> str: """Get pipette serial number.""" model = pipette.model volume = model.split("_")[0].replace("p", "") - volume = "1K" if volume == "1000" else volume + # volume = "1K" if volume == "1000" else volume + if volume == "1000": + volume = "1K" + elif volume == "200": + volume = "2H" channels = "S" if "single" in model else "M" + if "96" in model: + channels = "H" version = model.split("v")[-1].strip().replace(".", "") assert pipette.pipette_id, f"no pipette_id found for pipette: {pipette}" if "P" in pipette.pipette_id: diff --git a/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_capacitance.py b/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_capacitance.py index 2a431ecaf9c5..f4189bfb4abb 100644 --- a/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_capacitance.py +++ b/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_capacitance.py @@ -45,6 +45,8 @@ ), } +PROBE_POSITIONS = [InstrumentProbeType.PRIMARY, InstrumentProbeType.SECONDARY] + def _get_test_tag(probe: InstrumentProbeType, reading: str) -> str: return f"{probe.name.lower()}-{reading}" @@ -53,7 +55,7 @@ def _get_test_tag(probe: InstrumentProbeType, reading: str) -> str: def build_csv_lines() -> List[Union[CSVLine, CSVLineRepeating]]: """Build CSV Lines.""" lines: List[Union[CSVLine, CSVLineRepeating]] = list() - for p in InstrumentProbeType: + for p in PROBE_POSITIONS: for r in PROBE_READINGS: lines.append(CSVLine(_get_test_tag(p, r), [float, CSVResult])) if "mm" in r: @@ -118,7 +120,7 @@ async def run( if not api.is_simulator: ui.get_user_ready("REMOVE everything from the deck") - for probe in InstrumentProbeType: + for probe in PROBE_POSITIONS: # store the thresolds (for reference) for k in THRESHOLDS.keys(): report(section, _get_test_tag(probe, f"{k}-min"), [THRESHOLDS[k][0]]) diff --git a/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_droplets.py b/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_droplets.py index 578e0d6ae42d..93f97bab96f2 100644 --- a/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_droplets.py +++ b/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_droplets.py @@ -100,14 +100,14 @@ def get_tiprack_partial_nominal(pipette: Literal[200, 1000]) -> Point: async def aspirate_and_wait( - api: OT3API, reservoir: Point, pipette: Literal[200, 1000], seconds: int = 30 + api: OT3API, reservoir: Point, volume: int, seconds: int = 30 ) -> Tuple[bool, float]: """Aspirate and wait.""" await helpers_ot3.move_to_arched_ot3(api, OT3Mount.LEFT, reservoir) await api.move_to( OT3Mount.LEFT, reservoir + Point(z=DEPTH_INTO_RESERVOIR_FOR_ASPIRATE) ) - await api.aspirate(OT3Mount.LEFT, pipette) + await api.aspirate(OT3Mount.LEFT, volume) await api.move_to(OT3Mount.LEFT, reservoir + Point(z=HOVER_HEIGHT_MM)) start_time = time() @@ -201,31 +201,46 @@ async def _find_reservoir_pos() -> None: reservoir_a1_actual = await api.gantry_position(OT3Mount.LEFT) # PICK-UP 96 TIPS - ui.print_header("JOG to 96-Tip RACK") - if not api.is_simulator: - ui.get_user_ready(f"ADD 96 tip-rack to slot #{TIP_RACK_96_SLOT}") - await helpers_ot3.move_to_arched_ot3( - api, OT3Mount.LEFT, tip_rack_96_a1_nominal + Point(z=30) - ) - await helpers_ot3.jog_mount_ot3(api, OT3Mount.LEFT) - print("picking up tips") - await api.pick_up_tip(OT3Mount.LEFT, helpers_ot3.get_default_tip_length(pipette)) - await api.home_z(OT3Mount.LEFT) - if not api.is_simulator: - ui.get_user_ready("about to move to RESERVOIR") - - # TEST DROPLETS for 96 TIPS - ui.print_header("96 Tips: ASPIRATE and WAIT") - await _find_reservoir_pos() - assert reservoir_a1_actual - result, duration = await aspirate_and_wait( - api, - reservoir_a1_actual, - pipette=pipette, - seconds=NUM_SECONDS_TO_WAIT, + droplets_result = True + for trial in range(2): + ui.print_header("JOG to 96-Tip RACK") + if trial == 0: + tip_rack = str(pipette) + "ul" + test_volume: int = pipette + else: + tip_rack = "50ul" + test_volume = 1 if pipette == 200 else 5 + if not api.is_simulator: + ui.get_user_ready(f"ADD 96 tip-rack-{tip_rack} to slot #{TIP_RACK_96_SLOT}") + await helpers_ot3.move_to_arched_ot3( + api, OT3Mount.LEFT, tip_rack_96_a1_nominal + Point(z=30) + ) + await helpers_ot3.jog_mount_ot3(api, OT3Mount.LEFT) + print("picking up tips") + await api.pick_up_tip( + OT3Mount.LEFT, helpers_ot3.get_default_tip_length(pipette) + ) + await api.home_z(OT3Mount.LEFT) + if reservoir_a1_actual is None: + if not api.is_simulator: + ui.get_user_ready("about to move to RESERVOIR") + + # TEST DROPLETS for 96 TIPS + ui.print_header("96 Tips: ASPIRATE and WAIT") + await _find_reservoir_pos() + assert reservoir_a1_actual + result, duration = await aspirate_and_wait( + api, + reservoir_a1_actual, + test_volume, + seconds=NUM_SECONDS_TO_WAIT, + ) + droplets_result = droplets_result & result + await _drop_tip(api, trash_nominal, pipette) + await api.home_z(OT3Mount.LEFT) + report( + section, "droplets-96-tips", [duration, CSVResult.from_bool(droplets_result)] ) - report(section, "droplets-96-tips", [duration, CSVResult.from_bool(result)]) - await _drop_tip(api, trash_nominal, pipette) # if not api.is_simulator: # ui.get_user_ready(f"REMOVE 96 tip-rack from slot #{TIP_RACK_96_SLOT}") diff --git a/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_pressure.py b/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_pressure.py index b3cf2a24252c..096f6d26fe84 100644 --- a/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_pressure.py +++ b/hardware-testing/hardware_testing/production_qc/ninety_six_assembly_qc_ot3/test_pressure.py @@ -24,18 +24,18 @@ ASPIRATE_VOLUME = 2 PRESSURE_READINGS = ["open-pa", "sealed-pa", "aspirate-pa", "dispense-pa"] -THRESHOLDS = { +THRESHOLDS_1000 = { "open-pa": ( - -10, - 10, + 0, + 20, ), "sealed-pa": ( - -30, - 30, + 5, + 50, ), "aspirate-pa": ( - -600, - -400, + -900, + -500, ), "dispense-pa": ( 2500, @@ -43,6 +43,27 @@ ), } +THRESHOLDS_200 = { + "open-pa": ( + -50, + 50, + ), + "sealed-pa": ( + -100, + 100, + ), + "aspirate-pa": ( + -2000, + -500, + ), + "dispense-pa": ( + 1000, + 2500, + ), +} + +PROBE_POSITIONS = [InstrumentProbeType.PRIMARY, InstrumentProbeType.SECONDARY] + def _get_test_tag(probe: InstrumentProbeType, reading: str) -> str: assert reading in PRESSURE_READINGS, f"{reading} not in PRESSURE_READINGS" @@ -52,7 +73,7 @@ def _get_test_tag(probe: InstrumentProbeType, reading: str) -> str: def build_csv_lines() -> List[Union[CSVLine, CSVLineRepeating]]: """Build CSV Lines.""" lines: List[Union[CSVLine, CSVLineRepeating]] = list() - for p in InstrumentProbeType: + for p in PROBE_POSITIONS: for r in PRESSURE_READINGS: tag = _get_test_tag(p, r) lines.append(CSVLine(tag, [float, CSVResult])) @@ -83,8 +104,14 @@ async def _read_from_sensor( return sum(readings) / num_readings -def check_value(test_value: float, test_name: str) -> CSVResult: +def check_value( + test_value: float, test_name: str, pipette: Literal[1000, 200] +) -> CSVResult: """Determine if value is within pass limits.""" + if pipette == 1000: + THRESHOLDS = THRESHOLDS_1000 + if pipette == 200: + THRESHOLDS = THRESHOLDS_200 low_limit = THRESHOLDS[test_name][0] high_limit = THRESHOLDS[test_name][1] @@ -103,7 +130,7 @@ async def run( home_pos = await api.gantry_position(OT3Mount.LEFT) await api.move_to(OT3Mount.LEFT, slot_5._replace(z=home_pos.z)) - for probe in InstrumentProbeType: + for probe in PROBE_POSITIONS: sensor_id = sensor_id_for_instrument(probe) ui.print_header(f"Sensor: {probe}") @@ -116,7 +143,7 @@ async def run( ui.print_error(f"{probe} pressure sensor not working, skipping") continue print(f"open-pa: {open_pa}") - open_result = check_value(open_pa, "open-pa") + open_result = check_value(open_pa, "open-pa", pipette) report(section, _get_test_tag(probe, "open-pa"), [open_pa, open_result]) # SEALED-Pa @@ -134,7 +161,7 @@ async def run( ui.print_error(f"{probe} pressure sensor not working, skipping") break print(f"sealed-pa: {sealed_pa}") - sealed_result = check_value(sealed_pa, "sealed-pa") + sealed_result = check_value(sealed_pa, "sealed-pa", pipette) report(section, _get_test_tag(probe, "sealed-pa"), [sealed_pa, sealed_result]) # ASPIRATE-Pa @@ -149,7 +176,7 @@ async def run( ui.print_error(f"{probe} pressure sensor not working, skipping") break print(f"aspirate-pa: {aspirate_pa}") - aspirate_result = check_value(aspirate_pa, "aspirate-pa") + aspirate_result = check_value(aspirate_pa, "aspirate-pa", pipette) report( section, _get_test_tag(probe, "aspirate-pa"), [aspirate_pa, aspirate_result] ) @@ -166,7 +193,7 @@ async def run( ui.print_error(f"{probe} pressure sensor not working, skipping") break print(f"dispense-pa: {dispense_pa}") - dispense_result = check_value(dispense_pa, "dispense-pa") + dispense_result = check_value(dispense_pa, "dispense-pa", pipette) report( section, _get_test_tag(probe, "dispense-pa"), [dispense_pa, dispense_result] ) diff --git a/hardware-testing/hardware_testing/protocols/96CH_LV_FILL_LIQUID_RevA1.3.py b/hardware-testing/hardware_testing/protocols/96CH_LV_FILL_LIQUID_RevA1.3.py new file mode 100644 index 000000000000..e570f3d215f6 --- /dev/null +++ b/hardware-testing/hardware_testing/protocols/96CH_LV_FILL_LIQUID_RevA1.3.py @@ -0,0 +1,70 @@ +# flake8: noqa + +from opentrons import protocol_api +from opentrons import types +import random + +metadata = { + "protocolName": "96CH_LV_FILL_LIQUID_RevA1.3", + "author": "Andy Hu ", +} +requirements = {"robotType": "Flex", "apiLevel": "2.20"} + + +def add_parameters(parameters: protocol_api.ParameterContext) -> None: + """Add test parameters.""" + parameters.add_int( + display_name="volume of dipensation", + variable_name="dispension_volume", + default=149, + minimum=1, + maximum=200, + description="Set the liquid volume", + ) + + +def run(protocol: protocol_api.ProtocolContext): + + volume = protocol.params.dispension_volume # type: ignore [attr-defined] + + # DECK SETUP AND LABWARE + pcr_plate1 = protocol.load_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt", "D1" + ) + pcr_plate2 = protocol.load_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt", "D2" + ) + pcr_plate3 = protocol.load_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt", "D3" + ) + pcr_plate4 = protocol.load_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt", "C2" + ) + pcr_plate5 = protocol.load_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt", "C3" + ) + pcrs = [pcr_plate1, pcr_plate2, pcr_plate3, pcr_plate4, pcr_plate5] + + reservoir = protocol.load_labware("nest_1_reservoir_195ml", "C1") + + tiprack_1000 = protocol.load_labware( + load_name="opentrons_flex_96_tiprack_1000ul", + location="B1", + adapter="opentrons_flex_96_tiprack_adapter", + ) + + trash_labware = protocol.load_trash_bin("A3") + + # LOAD PIPETTES + p1000 = protocol.load_instrument("flex_96channel_1000", "left") + p1000.trash_container = trash_labware + # COMMANDS + + p1000.pick_up_tip(tiprack_1000.wells_by_name()["A1"]) + + for pcr in pcrs: + p1000.aspirate(volume, reservoir.wells_by_name()["A1"]) + p1000.dispense(volume, pcr.wells_by_name()["A1"]) + p1000.blow_out() + + p1000.return_tip() diff --git a/hardware-testing/hardware_testing/protocols/96CH_LV_QC_PROTOCOL_RevA1.1.py b/hardware-testing/hardware_testing/protocols/96CH_LV_QC_PROTOCOL_RevA1.1.py new file mode 100644 index 000000000000..06c48f772201 --- /dev/null +++ b/hardware-testing/hardware_testing/protocols/96CH_LV_QC_PROTOCOL_RevA1.1.py @@ -0,0 +1,58 @@ +# flake8: noqa + +from opentrons import protocol_api +from opentrons import types +import random + +metadata = { + "protocolName": "96CH_LV_QC_Protocol_RevA1.1", + "author": "Jon Klar (updated by RSS)", +} +requirements = {"robotType": "Flex", "apiLevel": "2.20"} + + +def run(protocol: protocol_api.ProtocolContext): + + # DECK SETUP AND LABWARE + pcr_plate = protocol.load_labware( + "armadillo_96_wellplate_200ul_pcr_full_skirt", "D1" + ) + plate_396_1 = protocol.load_labware("biorad_384_wellplate_50ul", "A1") + plate_396_2 = protocol.load_labware("biorad_384_wellplate_50ul", "D3") + reservoir = protocol.load_labware("nest_1_reservoir_195ml", "C1") + tiprack_50 = protocol.load_labware( + load_name="opentrons_flex_96_tiprack_50ul", + location="C3", + adapter="opentrons_flex_96_tiprack_adapter", + ) + tiprack_50_2 = protocol.load_labware( + load_name="opentrons_flex_96_tiprack_50ul", + location="B3", + adapter="opentrons_flex_96_tiprack_adapter", + ) + tiprack_200 = protocol.load_labware( + load_name="opentrons_flex_96_tiprack_200ul", + location="A2", + adapter="opentrons_flex_96_tiprack_adapter", + ) + trash_labware = protocol.load_trash_bin("A3") + + # LOAD PIPETTES + p1000 = protocol.load_instrument("flex_96channel_200", "left") + p1000.trash_container = trash_labware + # COMMANDS + + p1000.pick_up_tip(tiprack_200.wells_by_name()["A1"]) + p1000.aspirate(200, reservoir.wells_by_name()["A1"]) + p1000.dispense(200, pcr_plate.wells_by_name()["A1"]) + p1000.drop_tip() + + p1000.pick_up_tip(tiprack_50.wells_by_name()["A1"]) + p1000.aspirate(10, reservoir.wells_by_name()["A1"]) + p1000.dispense(10, plate_396_1.wells_by_name()["A1"]) + p1000.drop_tip() + + p1000.pick_up_tip(tiprack_50_2.wells_by_name()["A1"]) + p1000.aspirate(1, reservoir.wells_by_name()["A1"]) + p1000.dispense(1, plate_396_2.wells_by_name()["A2"]) + p1000.drop_tip() diff --git a/hardware-testing/hardware_testing/protocols/96ch_preheat.py b/hardware-testing/hardware_testing/protocols/96ch_preheat.py new file mode 100644 index 000000000000..698d04767351 --- /dev/null +++ b/hardware-testing/hardware_testing/protocols/96ch_preheat.py @@ -0,0 +1,84 @@ +"""Heat up and wait for a 96ch to reach the desired temperature.""" + +from opentrons.protocol_api import ProtocolContext, ParameterContext +from opentrons.hardware_control.adapters import SynchronousAdapter +from opentrons.hardware_control.ot3api import OT3API +from opentrons.hardware_control.types import Axis +from opentrons_hardware.sensors.sensor_driver import SensorDriver +from opentrons_hardware.sensors.sensor_types import EnvironmentSensor +from opentrons_hardware.firmware_bindings.constants import NodeId, SensorId + + +def add_parameters(parameters: ParameterContext) -> None: + """Build the runtime parameters.""" + parameters.add_int( + display_name="model type", + variable_name="model_type", + default=200, + choices=[ + {"display_name": "200", "value": 200}, + {"display_name": "1000", "value": 1000}, + ], + description="Select model type.", + ) + + parameters.add_int( + display_name="Target Temperature", + variable_name="temp", + default=27, + minimum=20, + maximum=35, + description="Set the target temperature for the pre-heat", + ) + + +metadata = {"protocolName": "96ch Pre-heating protocol"} + +requirements = {"robotType": "Flex", "apiLevel": "2.21"} + + +async def read_sensor(self, sensor: EnvironmentSensor) -> float: # noqa: ANN001 + """Read and return the current sensor information.""" + s_driver = SensorDriver() + sensor_data = await s_driver.read( + can_messenger=self._backend._messenger, + sensor=sensor, + offset=False, + ) + assert sensor_data.temperature is not None # type: ignore [union-attr] + return sensor_data.temperature.to_float() # type: ignore [union-attr] + + +def get_motors_hot(ot3api: SynchronousAdapter) -> None: + """Adjust the motor hold currents to heat quicker.""" + axis_settings = [Axis.P_L, Axis.Q] + ot3api.engage_axes(axis_settings) + + +def run(ctx: ProtocolContext) -> None: + """Run.""" + ot3api = ctx._core.get_hardware() + if not ctx.is_simulating(): + OT3API.read_sensor = read_sensor # type: ignore [attr-defined] + primary = EnvironmentSensor.build( + sensor_id=SensorId.S0, + node_id=NodeId.pipette_left, + ) + secondary = EnvironmentSensor.build( + sensor_id=SensorId.S1, + node_id=NodeId.pipette_left, + ) + _ = ctx.load_instrument( + f"flex_96channel_{ctx.params.model_type}", "left" # type: ignore [attr-defined] + ) + if not ctx.is_simulating(): + current_temp_1 = ot3api.read_sensor(primary) + current_temp_2 = ot3api.read_sensor(secondary) + get_motors_hot(ot3api) # type: ignore [arg-type] + avg_temp = (current_temp_1 + current_temp_2) / 2 + target = ctx.params.temp # type: ignore [attr-defined] + while avg_temp < target: + current_temp_1 = ot3api.read_sensor(primary) + current_temp_2 = ot3api.read_sensor(secondary) + avg_temp = (current_temp_1 + current_temp_2) / 2 + ctx.delay(seconds=15, msg=f"Current temperature {avg_temp} target={target}") diff --git a/hardware-testing/hardware_testing/protocols/flex_diluent_for_96ch.py b/hardware-testing/hardware_testing/protocols/flex_diluent_for_96ch.py index 4f1a55f4940a..202aeb39473e 100644 --- a/hardware-testing/hardware_testing/protocols/flex_diluent_for_96ch.py +++ b/hardware-testing/hardware_testing/protocols/flex_diluent_for_96ch.py @@ -2,7 +2,12 @@ from math import pi from typing import List, Optional, Dict, Tuple -from opentrons.protocol_api import ProtocolContext, InstrumentContext, Labware +from opentrons.protocol_api import ( + ProtocolContext, + InstrumentContext, + Labware, + ParameterContext, +) ############################################## # EDIT - START # @@ -11,7 +16,7 @@ # FIXME: make these variables configurable through RUNTIME-VARIABLES metadata = {"protocolName": "Flex: Diluent for 96ch"} -requirements = {"robotType": "Flex", "apiLevel": "2.15"} +requirements = {"robotType": "Flex", "apiLevel": "2.21"} RETURN_TIP = False FILL_MULTIPLE_PLATES = True @@ -20,7 +25,6 @@ LIQUID_DESCRIPTION = "Artel MVS Diluent" LIQUID_COLOR = "#0000FF" -TARGET_VOLUME = 195 TARGET_PUSH_OUT = 15 TARGET_SOURCES = [ { @@ -57,6 +61,18 @@ } +def add_parameters(parameters: ParameterContext) -> None: + """Build the runtime parameters.""" + parameters.add_float( + display_name="Target volume of diluent", + variable_name="target_volume", + default=195, + minimum=1, + maximum=200, + description="How much diluent to put in each well", + ) + + class _LiquidHeightInFlatBottomWell: def __init__( self, @@ -160,7 +176,7 @@ def _assign_starting_volumes( ) for test in TARGET_SOURCES: src_ul_per_trial = _start_volumes_per_trial( - TARGET_VOLUME, + ctx.params.target_volume, # type: ignore[attr-defined] reservoir.load_name, pipette.channels, len(test["destinations"]), @@ -221,13 +237,15 @@ def run(ctx: ProtocolContext) -> None: plate = ctx.load_labware("corning_96_wellplate_360ul_flat", "D2") pipette = ctx.load_instrument("flex_8channel_1000", "left", tip_racks=[tips]) _assign_starting_volumes(ctx, pipette, reservoir) + + ctx.load_trash_bin("A3") for i in range(12): - pipette.configure_for_volume(TARGET_VOLUME) + pipette.configure_for_volume(ctx.params.target_volume) # type: ignore[attr-defined] pipette.pick_up_tip() for test in TARGET_SOURCES: _transfer( ctx, - TARGET_VOLUME, + ctx.params.target_volume, # type: ignore[attr-defined] pipette, reservoir, plate, diff --git a/hardware-testing/hardware_testing/protocols/universal_photometric.py b/hardware-testing/hardware_testing/protocols/universal_photometric.py new file mode 100644 index 000000000000..52a82a32d34e --- /dev/null +++ b/hardware-testing/hardware_testing/protocols/universal_photometric.py @@ -0,0 +1,552 @@ +"""Universal photometric test.""" +import os +from typing import Tuple + +from opentrons import protocol_api +from opentrons.protocol_engine.errors.exceptions import InvalidLiquidHeightFound +from opentrons_shared_data.load import get_shared_data_root + +metadata = {"protocolName": "96ch Universal Photometric Protocol"} +requirements = {"robotType": "Flex", "apiLevel": "2.20"} + +DYE_RESERVOIR_DEAD_VOLUME = 20000 # 20k uL + +TIPRACK_LOCATIONS = ["D1", "C1", "C2", "C3", "B1"] + + +def add_parameters(parameters: protocol_api.ParameterContext) -> None: + """Add test parameters.""" + parameters.add_int( + display_name="tip type", + variable_name="tip_type", + default=50, + choices=[ + {"display_name": "20", "value": 20}, + {"display_name": "50", "value": 50}, + {"display_name": "200", "value": 200}, + {"display_name": "1000", "value": 1000}, + ], + description="Select tip type", + ) + + parameters.add_int( + display_name="model type", + variable_name="model_type", + default=200, + choices=[ + {"display_name": "200", "value": 200}, + {"display_name": "1000", "value": 1000}, + ], + description="Select model type.", + ) + + parameters.add_int( + display_name="number of cycles", + variable_name="cycles", + default=5, + minimum=1, + maximum=100, + description="Set number of cycles", + ) + + parameters.add_float( + display_name="target volume", + variable_name="target_volume", + default=5, + minimum=0, + maximum=1000, + description="Set target aspirate volume.", + ) + parameters.add_int( + variable_name="number_of_tipracks", + display_name="Number of tipracks", + description="Choose 1 or 5 tipracks to load at the start.", + default=5, + choices=[ + {"display_name": "1", "value": 1}, + {"display_name": "5", "value": 5}, + ], + ) + + parameters.add_bool( + variable_name="use_pip_motion_defaults", + display_name="Use pipette motion defaults", + description="Use default values for pipette motion.", + default=True, + ) + + parameters.add_float( + display_name="conditioning volume", + variable_name="conditioning_volume", + default=0, + minimum=0, + maximum=1000, + description="Set conditioning aspirate volume.", + ) + + parameters.add_int( + display_name="aspirate flow rate min/max", + variable_name="asp_flow_rate", + default=22, + minimum=1, + maximum=200, + description="Set aspirate flow rate.", + ) + + parameters.add_int( + display_name="dispense flow rate min/max", + variable_name="disp_flow_rate", + default=22, + minimum=1, + maximum=200, + description="Set dispense flow rate", + ) + + parameters.add_int( + display_name="blowout flow rate min/max", + variable_name="blowout_flow_rate", + default=22, + minimum=1, + maximum=200, + description="Set blowout flow rate.", + ) + + parameters.add_float( + display_name="push out min/max", + variable_name="push_out", + default=3.5, + minimum=1, + maximum=10, + description="Set push out volume.", + ) + + parameters.add_int( + display_name="aspirate submerge speed", + variable_name="asp_submerge_speed", + default=50, + minimum=1, + maximum=100, + description="Set aspirate submerge speed.", + ) + + parameters.add_int( + display_name="dispense submerge speed", + variable_name="disp_submerge_speed", + default=50, + minimum=1, + maximum=100, + description="Set dispense submerge speed.", + ) + + parameters.add_int( + display_name="aspirate exit speed", + variable_name="asp_exit_speed", + default=50, + minimum=1, + maximum=100, + description="Set aspirate exit speed.", + ) + + parameters.add_int( + display_name="dispense exit speed", + variable_name="disp_exit_speed", + default=50, + minimum=1, + maximum=100, + description="Set dispense exit speed.", + ) + + parameters.add_float( + display_name="aspirate_submerge_depth", + variable_name="asp_sub_depth", + default=1.5, + minimum=0, + maximum=5, + description="Set aspirate submerge depth.", + ) + + parameters.add_float( + display_name="dispense_submerge_depth", + variable_name="disp_sub_depth", + default=1.5, + minimum=0, + maximum=5, + description="Set dispense submerge depth.", + ) + parameters.add_float( + display_name="Dye volume", + variable_name="dye_volume", + default=40000, + minimum=20000, + maximum=290000, + description="Set uL of Dye in the Reservoir.", + ) + + parameters.add_float( + display_name="submerged delay time", + variable_name="submerged_delay_time", + default=0, + minimum=0, + maximum=60, + description="Set submerged delay time.", + ) + + parameters.add_bool( + variable_name="pause_after_asp", + display_name="pause after aspirate", + description=("Pause protocol after aspiration."), + default=True, + ) + + parameters.add_str( + variable_name="reservoir_labware_loadname", + display_name="Source labware load name.", + description=("Load name of the source labware."), + choices=[ + {"display_name": "NEST 195mL", "value": "nest_1_reservoir_195ml"}, + {"display_name": "NEST 290mL", "value": "nest_1_reservoir_290ml"}, + {"display_name": "None", "value": "none"}, + ], + default="nest_1_reservoir_195ml", + ) + parameters.add_str( + variable_name="destination_labware_loadname", + display_name="Destination labware load name.", + description=("Load name of the destination labware."), + choices=[ + { + "display_name": "Corning 96 360uL flat", + "value": "corning_96_wellplate_360ul_flat", + }, + { + "display_name": "Nest 96 100uL pcr", + "value": "nest_96_wellplate_100ul_pcr_full_skirt", + }, + {"display_name": "None", "value": "none"}, + ], + default="corning_96_wellplate_360ul_flat", + ) + parameters.add_bool( + variable_name="lld", + display_name="enable lld", + description=("Use LLD to detect liquid height."), + default=True, + ) + + +def _find_latest_labware_version(loadname: str) -> int: + latest = sorted( + os.listdir(f"{get_shared_data_root()}/labware/definitions/3/{loadname}/") + )[-1] + return int(latest[0]) + + +def _get_height_after_liquid_handling( + labware: protocol_api.Labware, + height_before: float, + volume: float, +) -> float: + """Get height after liquid handling fr 96 channel pipetting.""" + well_core = labware._core.get_well_core("A1") + geometry = well_core._engine_client.state.geometry # type: ignore [attr-defined] + labware_id = well_core.labware_id # type: ignore [attr-defined] + well_name = well_core._name # type: ignore [attr-defined] + + try: + return geometry.get_well_height_after_volume( + labware_id=labware_id, + well_name=well_name, + initial_height=height_before, + volume=volume, + ) + except InvalidLiquidHeightFound: + raise ValueError(f"called with height before = {height_before} vol = {volume}") + + +def _get_well_volume_at_height( + labware: protocol_api.Labware, + height: float, +) -> float: + well_core = labware._core.get_well_core("A1") + geometry = well_core._engine_client.state.geometry # type: ignore [attr-defined] + labware_id = well_core.labware_id # type: ignore [attr-defined] + well_name = well_core._name # type: ignore [attr-defined] + + return geometry.get_well_volume_at_height( + labware_id=labware_id, + well_name=well_name, + height=height, + ) + + +def _get_well_height_at_volume( + labware: protocol_api.Labware, + volume: float, +) -> float: + well_core = labware._core.get_well_core("A1") + geometry = well_core._engine_client.state.geometry # type: ignore [attr-defined] + labware_id = well_core.labware_id # type: ignore [attr-defined] + well_name = well_core._name # type: ignore [attr-defined] + + return geometry.get_well_height_at_volume( + labware_id=labware_id, + well_name=well_name, + volume=volume, + ) + + +def _get_current_liquid_height(labware: protocol_api.Labware) -> float: + source_core = labware._core.get_well_core("A1") + source_core_geometry = source_core._engine_client.state.geometry # type: ignore [attr-defined] + source_labware_id = source_core._labware_id # type: ignore [attr-defined] + source_well_name = source_core._name # type: ignore [attr-defined] + return source_core_geometry.get_meniscus_height(source_labware_id, source_well_name) + + +def run(ctx: protocol_api.ProtocolContext) -> None: # noqa: C901 + """Run.""" + ctx.load_trash_bin("A3") + # tips + tipracks = [ + ctx.load_labware( + f"opentrons_flex_96_tiprack_{ctx.params.tip_type}uL", # type: ignore [attr-defined] + location=deck_slot, + adapter="opentrons_flex_96_tiprack_adapter", + ) + for deck_slot in TIPRACK_LOCATIONS + ] + + def _get_tiprack(trial_number: int) -> protocol_api.Labware: + if ctx.params.number_of_tipracks == 1: # type: ignore [attr-defined] + return tipracks[0] + return tipracks[trial_number] + + # pipette + pip = ctx.load_instrument( + f"flex_96channel_{ctx.params.model_type}", # type: ignore [attr-defined] + "left", + tip_racks=tipracks, + ) + + # dye source + src_labware_version = _find_latest_labware_version( + loadname=ctx.params.reservoir_labware_loadname # type: ignore [attr-defined] + ) + dye_source = ctx.load_labware( + ctx.params.reservoir_labware_loadname, # type: ignore [attr-defined] + "D2", + version=src_labware_version, # type: ignore [attr-defined] + ) + dye = ctx.define_liquid( + name="Dye", + description="Food Coloring", + display_color="#FF0000", + ) + if not ctx.params.lld: # type: ignore [attr-defined] + dye_source["A1"].load_liquid(dye, ctx.params.dye_volume) # type: ignore [attr-defined] + + # destination plate + plate_labware_version = _find_latest_labware_version( + loadname=ctx.params.destination_labware_loadname # type: ignore [attr-defined] + ) + plate = ctx.load_labware( + ctx.params.destination_labware_loadname, # type: ignore [attr-defined] + location="D3", + version=plate_labware_version, + ) + diluent = ctx.define_liquid( + name="Diluent", + description="Food Coloring", + display_color="#FE0000", + ) + diluent_volume = 200 - ctx.params.target_volume # type: ignore [attr-defined] + dye_source["A1"].load_liquid(diluent, diluent_volume) # type: ignore [attr-defined] + + def _validate_dye_liquid_height() -> float: + + liquid_height_valid = False + retrying = False + nonlocal dye_source + while not liquid_height_valid: + # liquid probe and make sure there is enough volume for all trials + if ctx.params.lld or retrying: # type: ignore [attr-defined] + # if this detects no liquid, the protocol will exit + # if it detects liquid that is lower than expected, it will let you + # try again. + pip.detect_liquid_presence(dye_source["A1"]) + + src_liquid_height = _get_current_liquid_height(dye_source) + + actual_starting_dye_volume = _get_well_volume_at_height( + labware=dye_source, height=src_liquid_height + ) + needed_starting_dye_volume = ( + 96 + * ctx.params.cycles # type: ignore [attr-defined] + * ctx.params.target_volume # type: ignore [attr-defined] + ) + DYE_RESERVOIR_DEAD_VOLUME + # note: want to acct for needed dead volume here + if actual_starting_dye_volume > needed_starting_dye_volume: + liquid_height_valid = True + else: + pip._retract() + ctx.pause( + f"Need {round(needed_starting_dye_volume, 2)} uL dye to start. \ + Only {round(actual_starting_dye_volume, 2)} uL detected. Refill and try again." + ) + retrying = True + pip._retract() + if ctx.params.lld: # type: ignore [attr-defined] + pip.return_tip() + pip._retract() + ctx.pause("Replace tip rack.") + pip.pick_up_tip(tips["A1"]) + return src_liquid_height + + def _set_pipettte_motion_settings() -> Tuple[float, float, float, float, float]: + if ctx.params.use_pip_motion_defaults: # type: ignore [attr-defined] + aspirate_submerge_speed = 50 + dispense_submerge_speed = 50 + aspirate_exit_speed = 50 + dispense_exit_speed = 50 + if not ctx.is_simulating: # type: ignore [truthy-function] + from hardware_testing.gravimetric.liquid_class.defaults import ( + get_liquid_class, + ) + + liquid_class = get_liquid_class( + pipette=ctx.params.model_type, # type: ignore [attr-defined] + channels=96, + tip=ctx.params.tip_type, # type: ignore [attr-defined] + volume=ctx.params.target_volume, # type: ignore [attr-defined] + ) + pip.flow_rate.aspirate = liquid_class.aspirate.plunger_flow_rate + pip.flow_rate.dispense = liquid_class.dispense.plunger_flow_rate + set_push_out = liquid_class.dispense.blow_out_submerged + else: # if simulating + pip.flow_rate.aspirate = ctx.params.asp_flow_rate # type: ignore [attr-defined] + pip.flow_rate.dispense = ctx.params.disp_flow_rate # type: ignore [attr-defined] + set_push_out = ctx.params.push_out # type: ignore [attr-defined] + else: + set_push_out = ctx.params.push_out # type: ignore [attr-defined] + pip.flow_rate.aspirate = ctx.params.asp_flow_rate # type: ignore [attr-defined] + pip.flow_rate.dispense = ctx.params.disp_flow_rate # type: ignore [attr-defined] + pip.flow_rate.blow_out = ctx.params.blowout_flow_rate # type: ignore [attr-defined] + aspirate_submerge_speed = ctx.params.asp_submerge_speed # type: ignore [attr-defined] + dispense_submerge_speed = ctx.params.disp_submerge_speed # type: ignore [attr-defined] + return ( + aspirate_submerge_speed, + aspirate_exit_speed, + dispense_submerge_speed, + dispense_exit_speed, + set_push_out, + ) + + ( + aspirate_submerge_speed, + aspirate_exit_speed, + dispense_submerge_speed, + dispense_exit_speed, + set_push_out, + ) = _set_pipettte_motion_settings() + current_src_volume = ctx.params.dye_volume # type: ignore [attr-defined] + current_plate_volume = 0 + for i in range(ctx.params.cycles): # type: ignore [attr-defined] + tips = _get_tiprack(i) + pip.pick_up_tip(tips["A1"]) + + if i == 0: + source_liquid_height = _validate_dye_liquid_height() + else: + source_liquid_height = _get_well_height_at_volume( + labware=dye_source, volume=current_src_volume + ) + current_src_volume = _get_well_volume_at_height( + labware=dye_source, height=source_liquid_height + ) + src_volume_after_aspirate = ( + current_src_volume + - 96 * ctx.params.target_volume # type: ignore [attr-defined] + ) + aspirate_volume = ( + ctx.params.target_volume # type: ignore [attr-defined] + + ctx.params.conditioning_volume # type: ignore [attr-defined] + ) + aspirate_pos = ( + _get_well_height_at_volume( + labware=dye_source, volume=src_volume_after_aspirate + ) + - ctx.params.asp_sub_depth # type: ignore [attr-defined] + ) + # Move above reservoir + pip.move_to(location=dye_source["A1"].top()) + # Move to aspirate position at aspirate submerge speed + pip.move_to( + location=dye_source["A1"].bottom(aspirate_pos), + speed=aspirate_submerge_speed, + ) + # Submerged delay time + ctx.delay(seconds=ctx.params.submerged_delay_time) # type: ignore [attr-defined] + # Aspirate in place + pip.aspirate( + volume=aspirate_volume, + location=None, + ) + current_src_volume = src_volume_after_aspirate + # Dispense conditioning volume, if any, while submerged + if ctx.params.conditioning_volume: # type: ignore [attr-defined] + pip.dispense( + volume=ctx.params.conditioning_volume, # type: ignore [attr-defined] + location=None, + ) + # Exit liquid from aspirate position at aspirate exit speed + pip.move_to( + location=dye_source["A1"].top(), + speed=aspirate_exit_speed, + ) + # Retract pipette + pip._retract() + # Pause after aspiration + if ctx.params.pause_after_asp: # type: ignore [attr-defined] + ctx.pause("Inspect for dropouts.") + # we'll always end up with 200 uL after dispensing + dispense_pos = _get_well_height_at_volume(labware=plate, volume=200) + + # note: would probably be good to add a needed dead volume in this comparison + dispense_submerge_depth = ctx.params.disp_sub_depth # type: ignore [attr-defined] + if dispense_submerge_depth >= dispense_pos: # type: ignore [attr-defined] + raise ValueError( + f"submerge depth {dispense_submerge_depth} \ + too deep for dispense position {dispense_pos}" + ) + dispense_pos -= ctx.params.disp_sub_depth # type: ignore [attr-defined] + # Move to plate + pip.move_to(location=plate["A1"].top()) + # Move to dispense position at dispense submerge speed + pip.move_to( + location=plate["A1"].bottom(dispense_pos), + speed=dispense_submerge_speed, + ) + # Dispense + pip.dispense( + volume=ctx.params.target_volume, # type: ignore [attr-defined] + location=None, + push_out=set_push_out, # type: ignore [attr-defined] + ) + current_plate_volume += ctx.params.target_volume # type: ignore [attr-defined] + # Exit liquid from dispense position at dispense exit speed + blow_out_pos = plate["A1"].bottom( + dispense_pos + ctx.params.disp_sub_depth + 5 # type: ignore [attr-defined] + ) + pip.move_to( + location=blow_out_pos, + speed=dispense_exit_speed, + ) + # Perform blow out + pip.blow_out() + # Return tip to tip rack + pip.return_tip() + # Retract pipette + pip._retract() + # Pause protocol + ctx.pause("Replace tips and dispense plate.") diff --git a/hardware-testing/hardware_testing/scripts/faster_plunger_lifetime_test.py b/hardware-testing/hardware_testing/scripts/faster_plunger_lifetime_test.py new file mode 100644 index 000000000000..19be8a5d99fc --- /dev/null +++ b/hardware-testing/hardware_testing/scripts/faster_plunger_lifetime_test.py @@ -0,0 +1,239 @@ +"""Test Plunger.""" +from typing import Tuple, Dict, Literal, Optional +from typing_extensions import TypedDict + +from opentrons.hardware_control.ot3api import OT3API +from dataclasses import dataclass + +import time +from hardware_testing.opentrons_api import helpers_ot3 +from hardware_testing.opentrons_api.types import Axis, OT3Mount +import enum +import argparse +import csv +import asyncio +from datetime import datetime + + +class TestSection(enum.Enum): + """Test Section.""" + + PLUNGER = "PLUNGER" + + +@dataclass +class TestConfig: + """Test Config.""" + + simulate: bool + pipette: Literal[200, 1000] + + +class TestData(TypedDict): + """Test Data entry.""" + + time_sec: Optional[float] + cycle: Optional[int] + stall: Optional[str] + position: Optional[str] + position_check: Optional[bool] + error: Optional[str] + + +PLUNGER_MAX_SKIP_MM = 0.1 +SPEEDS_TO_TEST: float = 25 +CURRENTS_SPEEDS: Dict[float, float] = { + 0.7: SPEEDS_TO_TEST, +} + + +async def _is_plunger_still_aligned_with_encoder( + api: OT3API, +) -> Tuple[float, float, bool]: + enc_pos = await api.encoder_current_position_ot3(OT3Mount.LEFT) + motor_pos = await api.current_position_ot3(OT3Mount.LEFT) + p_enc = enc_pos[Axis.P_L] + p_est = motor_pos[Axis.P_L] + is_aligned = abs(p_est - p_enc) < PLUNGER_MAX_SKIP_MM + return p_enc, p_est, is_aligned + + +async def main(args: argparse.Namespace, cfg: TestConfig) -> None: + """Run.""" + pipette_string = "p1000_96_v3.4" if cfg.pipette == 1000 else "p200_96_v3.1" + + api = await helpers_ot3.build_async_ot3_hardware_api( + is_simulating=cfg.simulate, + pipette_left=pipette_string, + stall_detection_enable=False, + ) + ax = Axis.P_L + mount = OT3Mount.LEFT + settings = helpers_ot3.get_gantry_load_per_axis_motion_settings_ot3(api, ax) + settings.max_speed = 25 + settings.acceleration = 100 + settings.run_current = 0.7 + default_current = settings.run_current + default_speed = settings.max_speed + default_acceleration = 100 + top, bottom, _, _ = helpers_ot3.get_plunger_positions_ot3(api, mount) + print(f"Settings: {settings}") + + async def position_check() -> bool: + est, enc, aligned = await _is_plunger_still_aligned_with_encoder(api) + print(f"Estimate: {est}, Encoder: {enc}, Aligned: {aligned}") + return aligned + + await api.home_z(OT3Mount.LEFT) + # LOOP THROUGH CURRENTS + SPEEDS + await api.home() + today = datetime.now().strftime("%m-%d-%y_%H-%M") + with open( + f"/data/testing_data/P200H_test_plunger_speed_test_{today}.csv", "w", newline="" + ) as csvfile: + test_data: TestData = { + "time_sec": None, + "cycle": None, + "stall": None, + "position": None, + "position_check": None, + "error": None, + } + writer = csv.DictWriter(csvfile, test_data) + writer.writeheader() + start_time = time.time() + try: + currents = list(CURRENTS_SPEEDS.keys()) + for cycle in range(1, args.cycles + 1): + print(f"Cycle: {cycle}") + for current in sorted(currents, reverse=True): + speed = CURRENTS_SPEEDS[current] + # HOME + print("homing...") + await api.home([ax]) + + print(f"run-current set to {current} amps") + await helpers_ot3.set_gantry_load_per_axis_current_settings_ot3( + api, + ax, + run_current=current, + ) + await helpers_ot3.set_gantry_load_per_axis_motion_settings_ot3( + api, + ax, + default_max_speed=speed, + acceleration=default_acceleration, + ) + # MOVE DOWN + print(f"moving down {bottom} mm at {speed} mm/sec") + position_checked = await position_check() + print(f"position checked: {position_checked}") + try: + await helpers_ot3.move_plunger_absolute_ot3( + api, mount, bottom, speed=speed, motor_current=current + ) + down_passed = await position_check() + test_data["time_sec"] = time.time() - start_time + test_data["cycle"] = cycle + test_data["position"] = "bottom" + test_data["position_check"] = down_passed + test_data["stall"] = "NONE" + print(test_data) + writer.writerow(test_data) + csvfile.flush() + except Exception as e: + print("STALL DETECTION") + down_passed = await position_check() + test_data["position_check"] = down_passed + test_data["stall"] = str("Failed to move plunger down") + test_data["error"] = str(e) + print(test_data) + writer.writerow(test_data) + csvfile.flush() + print("homing...") + await helpers_ot3.set_gantry_load_per_axis_current_settings_ot3( + api, ax, run_current=default_current + ) + await helpers_ot3.set_gantry_load_per_axis_motion_settings_ot3( + api, + ax, + default_max_speed=default_speed, + acceleration=default_acceleration, + ) + await api._backend.set_active_current( + {Axis.P_L: default_current} + ) + await api.home([ax]) + await helpers_ot3.move_plunger_absolute_ot3( + api, + mount, + bottom, + speed=default_speed, + motor_current=default_current, + ) + # MOVE UP + print(f"moving up {top} mm at {speed} mm/sec") + position_checked = await position_check() + print(f"position checked: {position_checked}") + await helpers_ot3.set_gantry_load_per_axis_current_settings_ot3( + api, + ax, + run_current=current, + ) + await helpers_ot3.set_gantry_load_per_axis_motion_settings_ot3( + api, + ax, + default_max_speed=speed, + acceleration=default_acceleration, + ) + try: + await helpers_ot3.move_plunger_absolute_ot3( + api, mount, 0, speed=speed, motor_current=current + ) + up_passed = await position_check() + test_data["time_sec"] = time.time() - start_time + test_data["cycle"] = cycle + test_data["position"] = "top" + test_data["position_check"] = up_passed + print(test_data) + writer.writerow(test_data) + test_data["stall"] = "NONE" + csvfile.flush() + except Exception as e: + print("STALL DETECTION") + up_passed = await position_check() + test_data["stall"] = str("Failed to move plunger down") + test_data["position_check"] = up_passed + test_data["error"] = str(e) + print(test_data) + writer.writerow(test_data) + csvfile.flush() + # RESET CURRENTS AND HOME + print("homing...") + await helpers_ot3.set_gantry_load_per_axis_current_settings_ot3( + api, ax, run_current=default_current + ) + await helpers_ot3.set_gantry_load_per_axis_motion_settings_ot3( + api, + ax, + default_max_speed=default_speed, + acceleration=default_acceleration, + ) + await api._backend.set_active_current({Axis.P_L: default_current}) + + except Exception as e: + test_data["error"] = str(e) + writer.writerow(test_data) + csvfile.flush() + raise Exception(f"Error StallDetection: {e}") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--cycles", type=int, default=100000) + parser.add_argument("--simulate", action="store_true") + parser.add_argument("--pipette", type=int, choices=[200, 1000], default=200) + args = parser.parse_args() + _config = TestConfig(simulate=args.simulate, pipette=args.pipette) + + asyncio.run(main(args, _config)) diff --git a/hardware/opentrons_hardware/hardware_control/motion_planning/move_utils.py b/hardware/opentrons_hardware/hardware_control/motion_planning/move_utils.py index fee27ee82cb0..1ef6e48de3f4 100644 --- a/hardware/opentrons_hardware/hardware_control/motion_planning/move_utils.py +++ b/hardware/opentrons_hardware/hardware_control/motion_planning/move_utils.py @@ -425,10 +425,10 @@ def build_blocks( - have at most one 0 acceleration coast phase at our max speed """ log = logging.getLogger("build_blocks") - assert abs(initial_speed) <= max_speed or np.isclose( + assert abs(initial_speed) <= abs(max_speed) or np.isclose( abs(initial_speed), max_speed ), f"initial speed {initial_speed} exceeds max speed {max_speed}" - assert abs(final_speed) <= max_speed or np.isclose( + assert abs(final_speed) <= abs(max_speed) or np.isclose( abs(final_speed), max_speed ), f"final speed {final_speed} exceeds max speed {max_speed}" diff --git a/shared-data/pipette/definitions/2/general/ninety_six_channel/p200/3_1.json b/shared-data/pipette/definitions/2/general/ninety_six_channel/p200/3_1.json new file mode 100644 index 000000000000..4ce934023cef --- /dev/null +++ b/shared-data/pipette/definitions/2/general/ninety_six_channel/p200/3_1.json @@ -0,0 +1,660 @@ +{ + "$otSharedSchema": "#/pipette/schemas/2/pipettePropertiesSchema.json", + "displayName": "Flex 96-Channel 200 µL", + "model": "p200", + "displayCategory": "FLEX", + "validNozzleMaps": { + "maps": { + "SingleA1": ["A1"], + "SingleH1": ["H1"], + "SingleA12": ["A12"], + "SingleH12": ["H12"], + "Column1": ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"], + "Column12": ["A12", "B12", "C12", "D12", "E12", "F12", "G12", "H12"], + "RowA": [ + "A1", + "A2", + "A3", + "A4", + "A5", + "A6", + "A7", + "A8", + "A9", + "A10", + "A11", + "A12" + ], + "RowH": [ + "H1", + "H2", + "H3", + "H4", + "H5", + "H6", + "H7", + "H8", + "H9", + "H10", + "H11", + "H12" + ], + "Full": [ + "A1", + "A2", + "A3", + "A4", + "A5", + "A6", + "A7", + "A8", + "A9", + "A10", + "A11", + "A12", + "B1", + "B2", + "B3", + "B4", + "B5", + "B6", + "B7", + "B8", + "B9", + "B10", + "B11", + "B12", + "C1", + "C2", + "C3", + "C4", + "C5", + "C6", + "C7", + "C8", + "C9", + "C10", + "C11", + "C12", + "D1", + "D2", + "D3", + "D4", + "D5", + "D6", + "D7", + "D8", + "D9", + "D10", + "D11", + "D12", + "E1", + "E2", + "E3", + "E4", + "E5", + "E6", + "E7", + "E8", + "E9", + "E10", + "E11", + "E12", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "G1", + "G2", + "G3", + "G4", + "G5", + "G6", + "G7", + "G8", + "G9", + "G10", + "G11", + "G12", + "H1", + "H2", + "H3", + "H4", + "H5", + "H6", + "H7", + "H8", + "H9", + "H10", + "H11", + "H12" + ] + } + }, + "pickUpTipConfigurations": { + "pressFit": { + "presses": 1, + "increment": 0.0, + "configurationsByNozzleMap": { + "SingleA1": { + "default": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 9.884, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.884, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.884 + } + } + }, + "t200": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5 + }, + "v1": { + "default": 9.981, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.981 + } + } + }, + "t50": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 9.884, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.884, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.884 + } + } + } + }, + "SingleH1": { + "default": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 9.884, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.884, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.884 + } + } + }, + "t200": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5 + }, + "v1": { + "default": 9.981, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.981 + } + } + }, + "t50": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 9.884, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.884, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.884 + } + } + } + }, + "SingleA12": { + "default": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 9.884, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.884, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.884 + } + } + }, + "t200": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5 + }, + "v1": { + "default": 9.981, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.981 + } + } + }, + "t50": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 9.884, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.884, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.884 + } + } + } + }, + "SingleH12": { + "default": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 9.884, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.884, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.884 + } + } + }, + "t200": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5 + }, + "v1": { + "default": 9.981, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.981, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.981 + } + } + }, + "t50": { + "speed": 10.0, + "distance": 10.5, + "current": 0.4, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 9.884, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.884, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.884 + } + } + } + }, + "Column1": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.55, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.16, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.74 + }, + "v3": { + "default": 9.49, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.52, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.49, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.49, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.52 + } + } + }, + "t200": { + "speed": 10.0, + "distance": 13.0, + "current": 0.55, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5 + }, + "v1": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.74 + }, + "v3": { + "default": 9.49, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.49, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.49 + } + } + }, + "t50": { + "speed": 10.0, + "distance": 13.0, + "current": 0.55, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.16 + }, + "v3": { + "default": 9.52, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.52, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.52 + } + } + } + }, + "Column12": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.55, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.16, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.74 + }, + "v3": { + "default": 9.49, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.52, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.49, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.49, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.52 + } + } + }, + "t200": { + "speed": 10.0, + "distance": 13.0, + "current": 0.55, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 10.5 + }, + "v1": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.74 + }, + "v3": { + "default": 9.49, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.49, + "opentrons/opentrons_flex_96_filtertiprack_200ul/1": 9.49 + } + } + }, + "t50": { + "speed": 10.0, + "distance": 13.0, + "current": 0.55, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 10.5 + }, + "v1": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.16 + }, + "v3": { + "default": 9.52, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.52, + "opentrons/opentrons_flex_96_filtertiprack_50ul/1": 9.52 + } + } + } + }, + "RowA": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.55, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5 + }, + "v1": { + "default": 9.379, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.379, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.6 + } + } + } + }, + "RowH": { + "default": { + "speed": 10.0, + "distance": 13.0, + "current": 0.55, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5 + }, + "v1": { + "default": 9.401, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 9.401, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.415 + } + } + } + } + } + }, + "camAction": { + "prep_move_distance": 8.25, + "prep_move_speed": 10.0, + "connectTiprackDistanceMM": 7.0, + "configurationsByNozzleMap": { + "Full": { + "default": { + "speed": 5.5, + "distance": 10.0, + "current": 1.5, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5 + }, + "v1": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.16, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.74 + } + } + }, + "t200": { + "speed": 5.5, + "distance": 10.0, + "current": 1.5, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 10.5 + }, + "v1": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_200ul/1": 9.74 + } + } + }, + "t50": { + "speed": 5.5, + "distance": 10.0, + "current": 1.5, + "tipOverlaps": { + "v0": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.5 + }, + "v1": { + "default": 10.5, + "opentrons/opentrons_flex_96_tiprack_50ul/1": 10.16 + } + } + } + } + } + } + }, + "dropTipConfigurations": { + "camAction": { + "current": 1.5, + "speed": 5.5, + "distance": 10.8, + "prep_move_distance": 19.0, + "prep_move_speed": 10.0 + } + }, + "plungerMotorConfigurations": { + "idle": 0.3, + "run": 0.8 + }, + "plungerPositionsConfigurations": { + "default": { + "top": 0.5, + "bottom": 68.5, + "blowout": 73.5, + "drop": 80 + } + }, + "availableSensors": { + "sensors": ["pressure", "capacitive", "environment"], + "pressure": { + "count": 2 + }, + "capacitive": { + "count": 2 + }, + "environment": { + "count": 1 + } + }, + "partialTipConfigurations": { + "partialTipSupported": true, + "availableConfigurations": [1, 8, 12, 16, 24, 48, 96] + }, + "backCompatNames": [], + "channels": 96, + "shaftDiameter": 2, + "shaftULperMM": 3.14159, + "backlashDistance": 3.0, + "quirks": [], + "plungerHomingConfigurations": { + "current": 0.8, + "speed": 5 + }, + "tipPresenceCheckDistanceMM": 8.0, + "endTipActionRetractDistanceMM": 2.0 +} diff --git a/shared-data/pipette/definitions/2/geometry/eight_channel/p50/3_5.json b/shared-data/pipette/definitions/2/geometry/eight_channel/p50/3_5.json index 4bd9c9048f9d..3005948ff04a 100644 --- a/shared-data/pipette/definitions/2/geometry/eight_channel/p50/3_5.json +++ b/shared-data/pipette/definitions/2/geometry/eight_channel/p50/3_5.json @@ -39,6 +39,10 @@ "H1": [-8.0, -79.0, -259.15] }, "lldSettings": { + "t20": { + "minHeight": 1.5, + "minVolume": 0 + }, "t50": { "minHeight": 1.0, "minVolume": 0 diff --git a/shared-data/pipette/definitions/2/geometry/ninety_six_channel/p200/3_0.json b/shared-data/pipette/definitions/2/geometry/ninety_six_channel/p200/3_0.json index 437830a58c57..a37cf94915a7 100644 --- a/shared-data/pipette/definitions/2/geometry/ninety_six_channel/p200/3_0.json +++ b/shared-data/pipette/definitions/2/geometry/ninety_six_channel/p200/3_0.json @@ -293,6 +293,10 @@ "H12": [63.0, -88.5, -259.15] }, "lldSettings": { + "t20": { + "minHeight": 1.5, + "minVolume": 0 + }, "t50": { "minHeight": 1.5, "minVolume": 0 diff --git a/shared-data/pipette/definitions/2/geometry/ninety_six_channel/p200/3_1.json b/shared-data/pipette/definitions/2/geometry/ninety_six_channel/p200/3_1.json new file mode 100644 index 000000000000..a37cf94915a7 --- /dev/null +++ b/shared-data/pipette/definitions/2/geometry/ninety_six_channel/p200/3_1.json @@ -0,0 +1,309 @@ +{ + "$otSharedSchema": "#/pipette/schemas/2/pipetteGeometrySchema.json", + "pathTo3D": "pipette/definitions/2/geometry/ninety_six_channel/p200/placeholder.gltf", + "nozzleOffset": [-36.0, -25.5, -259.15], + "pipetteBoundingBoxOffsets": { + "backLeftCorner": [-67.0, -3.5, -259.15], + "frontRightCorner": [94.0, -113.0, -259.15] + }, + "orderedRows": [ + { + "key": "A", + "orderedNozzles": [ + "A1", + "A2", + "A3", + "A4", + "A5", + "A6", + "A7", + "A8", + "A9", + "A10", + "A11", + "A12" + ] + }, + { + "key": "B", + "orderedNozzles": [ + "B1", + "B2", + "B3", + "B4", + "B5", + "B6", + "B7", + "B8", + "B9", + "B10", + "B11", + "B12" + ] + }, + { + "key": "C", + "orderedNozzles": [ + "C1", + "C2", + "C3", + "C4", + "C5", + "C6", + "C7", + "C8", + "C9", + "C10", + "C11", + "C12" + ] + }, + { + "key": "D", + "orderedNozzles": [ + "D1", + "D2", + "D3", + "D4", + "D5", + "D6", + "D7", + "D8", + "D9", + "D10", + "D11", + "D12" + ] + }, + { + "key": "E", + "orderedNozzles": [ + "E1", + "E2", + "E3", + "E4", + "E5", + "E6", + "E7", + "E8", + "E9", + "E10", + "E11", + "E12" + ] + }, + { + "key": "F", + "orderedNozzles": [ + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12" + ] + }, + { + "key": "G", + "orderedNozzles": [ + "G1", + "G2", + "G3", + "G4", + "G5", + "G6", + "G7", + "G8", + "G9", + "G10", + "G11", + "G12" + ] + }, + { + "key": "H", + "orderedNozzles": [ + "H1", + "H2", + "H3", + "H4", + "H5", + "H6", + "H7", + "H8", + "H9", + "H10", + "H11", + "H12" + ] + } + ], + "orderedColumns": [ + { + "key": "1", + "orderedNozzles": ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1"] + }, + { + "key": "2", + "orderedNozzles": ["A2", "B2", "C2", "D2", "E2", "F2", "G2", "H2"] + }, + { + "key": "3", + "orderedNozzles": ["A3", "B3", "C3", "D3", "E3", "F3", "G3", "H3"] + }, + { + "key": "4", + "orderedNozzles": ["A4", "B4", "C4", "D4", "E4", "F4", "G4", "H4"] + }, + { + "key": "5", + "orderedNozzles": ["A5", "B5", "C5", "D5", "E5", "F5", "G5", "H5"] + }, + { + "key": "6", + "orderedNozzles": ["A6", "B6", "C6", "D6", "E6", "F6", "G6", "H6"] + }, + { + "key": "7", + "orderedNozzles": ["A7", "B7", "C7", "D7", "E7", "F7", "G7", "H7"] + }, + { + "key": "8", + "orderedNozzles": ["A8", "B8", "C8", "D8", "E8", "F8", "G8", "H8"] + }, + { + "key": "9", + "orderedNozzles": ["A9", "B9", "C9", "D9", "E9", "F9", "G9", "H9"] + }, + { + "key": "10", + "orderedNozzles": ["A10", "B10", "C10", "D10", "E10", "F10", "G10", "H10"] + }, + { + "key": "11", + "orderedNozzles": ["A11", "B11", "C11", "D11", "E11", "F11", "G11", "H11"] + }, + { + "key": "12", + "orderedNozzles": ["A12", "B12", "C12", "D12", "E12", "F12", "G12", "H12"] + } + ], + "nozzleMap": { + "A1": [-36.0, -25.5, -259.15], + "A2": [-27.0, -25.5, -259.15], + "A3": [-18.0, -25.5, -259.15], + "A4": [-9.0, -25.5, -259.15], + "A5": [0.0, -25.5, -259.15], + "A6": [9.0, -25.5, -259.15], + "A7": [18.0, -25.5, -259.15], + "A8": [27.0, -25.5, -259.15], + "A9": [36.0, -25.5, -259.15], + "A10": [45.0, -25.5, -259.15], + "A11": [54.0, -25.5, -259.15], + "A12": [63.0, -25.5, -259.15], + "B1": [-36.0, -34.5, -259.15], + "B2": [-27.0, -34.5, -259.15], + "B3": [-18.0, -34.5, -259.15], + "B4": [-9.0, -34.5, -259.15], + "B5": [0.0, -34.5, -259.15], + "B6": [9.0, -34.5, -259.15], + "B7": [18.0, -34.5, -259.15], + "B8": [27.0, -34.5, -259.15], + "B9": [36.0, -34.5, -259.15], + "B10": [45.0, -34.5, -259.15], + "B11": [54.0, -34.5, -259.15], + "B12": [63.0, -34.5, -259.15], + "C1": [-36.0, -43.5, -259.15], + "C2": [-27.0, -43.5, -259.15], + "C3": [-18.0, -43.5, -259.15], + "C4": [-9.0, -43.5, -259.15], + "C5": [0.0, -43.5, -259.15], + "C6": [9.0, -43.5, -259.15], + "C7": [18.0, -43.5, -259.15], + "C8": [27.0, -43.5, -259.15], + "C9": [36.0, -43.5, -259.15], + "C10": [45.0, -43.5, -259.15], + "C11": [54.0, -43.5, -259.15], + "C12": [63.0, -43.5, -259.15], + "D1": [-36.0, -52.5, -259.15], + "D2": [-27.0, -52.5, -259.15], + "D3": [-18.0, -52.5, -259.15], + "D4": [-9.0, -52.5, -259.15], + "D5": [0.0, -52.5, -259.15], + "D6": [9.0, -52.5, -259.15], + "D7": [18.0, -52.5, -259.15], + "D8": [27.0, -52.5, -259.15], + "D9": [36.0, -52.5, -259.15], + "D10": [45.0, -52.5, -259.15], + "D11": [54.0, -52.5, -259.15], + "D12": [63.0, -52.5, -259.15], + "E1": [-36.0, -61.5, -259.15], + "E2": [-27.0, -61.5, -259.15], + "E3": [-18.0, -61.5, -259.15], + "E4": [-9.0, -61.5, -259.15], + "E5": [0.0, -61.5, -259.15], + "E6": [9.0, -61.5, -259.15], + "E7": [18.0, -61.5, -259.15], + "E8": [27.0, -61.5, -259.15], + "E9": [36.0, -61.5, -259.15], + "E10": [45.0, -61.5, -259.15], + "E11": [54.0, -61.5, -259.15], + "E12": [63.0, -61.5, -259.15], + "F1": [-36.0, -70.5, -259.15], + "F2": [-27.0, -70.5, -259.15], + "F3": [-18.0, -70.5, -259.15], + "F4": [-9.0, -70.5, -259.15], + "F5": [0.0, -70.5, -259.15], + "F6": [9.0, -70.5, -259.15], + "F7": [18.0, -70.5, -259.15], + "F8": [27.0, -70.5, -259.15], + "F9": [36.0, -70.5, -259.15], + "F10": [45.0, -70.5, -259.15], + "F11": [54.0, -70.5, -259.15], + "F12": [63.0, -70.5, -259.15], + "G1": [-36.0, -79.5, -259.15], + "G2": [-27.0, -79.5, -259.15], + "G3": [-18.0, -79.5, -259.15], + "G4": [-9.0, -79.5, -259.15], + "G5": [0.0, -79.5, -259.15], + "G6": [9.0, -79.5, -259.15], + "G7": [18.0, -79.5, -259.15], + "G8": [27.0, -79.5, -259.15], + "G9": [36.0, -79.5, -259.15], + "G10": [45.0, -79.5, -259.15], + "G11": [54.0, -79.5, -259.15], + "G12": [63.0, -79.5, -259.15], + "H1": [-36.0, -88.5, -259.15], + "H2": [-27.0, -88.5, -259.15], + "H3": [-18.0, -88.5, -259.15], + "H4": [-9.0, -88.5, -259.15], + "H5": [0.0, -88.5, -259.15], + "H6": [9.0, -88.5, -259.15], + "H7": [18.0, -88.5, -259.15], + "H8": [27.0, -88.5, -259.15], + "H9": [36.0, -88.5, -259.15], + "H10": [45.0, -88.5, -259.15], + "H11": [54.0, -88.5, -259.15], + "H12": [63.0, -88.5, -259.15] + }, + "lldSettings": { + "t20": { + "minHeight": 1.5, + "minVolume": 0 + }, + "t50": { + "minHeight": 1.5, + "minVolume": 0 + }, + "t200": { + "minHeight": 1.5, + "minVolume": 0 + } + } +} diff --git a/shared-data/pipette/definitions/2/geometry/single_channel/p50/3_6.json b/shared-data/pipette/definitions/2/geometry/single_channel/p50/3_6.json index 3b5953516896..18499eb3b47b 100644 --- a/shared-data/pipette/definitions/2/geometry/single_channel/p50/3_6.json +++ b/shared-data/pipette/definitions/2/geometry/single_channel/p50/3_6.json @@ -12,6 +12,10 @@ "A1": [-8.0, -22.0, -259.15] }, "lldSettings": { + "t20": { + "minHeight": 1.5, + "minVolume": 0 + }, "t50": { "minHeight": 1.0, "minVolume": 0 diff --git a/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/1_0.json b/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/1_0.json index 845d7bc7e3ba..7d73d09c5621 100644 --- a/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/1_0.json +++ b/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/1_0.json @@ -2,7 +2,7 @@ "$otSharedSchema": "#/pipette/schemas/2/pipetteLiquidPropertiesSchema.json", "supportedTips": { "t20": { - "uiMaxFlowRate": 189.1, + "uiMaxFlowRate": 23, "defaultAspirateFlowRate": { "default": 6, "valuesByApiLevel": { "2.14": 6 } @@ -12,8 +12,8 @@ "valuesByApiLevel": { "2.14": 6 } }, "defaultBlowOutFlowRate": { - "default": 80, - "valuesByApiLevel": { "2.14": 80 } + "default": 23, + "valuesByApiLevel": { "2.14": 23 } }, "defaultFlowAcceleration": 16000.0, "defaultTipLength": 57.9, @@ -62,8 +62,8 @@ "valuesByApiLevel": { "2.14": 6 } }, "defaultBlowOutFlowRate": { - "default": 80, - "valuesByApiLevel": { "2.14": 80 } + "default": 23, + "valuesByApiLevel": { "2.14": 23 } }, "defaultFlowAcceleration": 16000.0, "defaultTipLength": 57.9, @@ -168,16 +168,16 @@ }, "t200": { "defaultAspirateFlowRate": { - "default": 80, - "valuesByApiLevel": { "2.14": 80 } + "default": 23, + "valuesByApiLevel": { "2.14": 23 } }, "defaultDispenseFlowRate": { - "default": 80, - "valuesByApiLevel": { "2.14": 80 } + "default": 23, + "valuesByApiLevel": { "2.14": 23 } }, "defaultBlowOutFlowRate": { - "default": 80, - "valuesByApiLevel": { "2.14": 80 } + "default": 23, + "valuesByApiLevel": { "2.14": 23 } }, "defaultFlowAcceleration": 16000.0, "defaultTipLength": 58.35, diff --git a/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_0.json b/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_0.json index 4faa944089de..e5aa5ef509b5 100644 --- a/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_0.json +++ b/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_0.json @@ -2,7 +2,7 @@ "$otSharedSchema": "#/pipette/schemas/2/pipetteLiquidPropertiesSchema.json", "supportedTips": { "t20": { - "uiMaxFlowRate": 45, + "uiMaxFlowRate": 23, "defaultAspirateFlowRate": { "default": 6.5, "valuesByApiLevel": { "2.14": 6.5 } @@ -51,14 +51,14 @@ "defaultPushOutVolume": 2 }, "t50": { - "uiMaxFlowRate": 45, + "uiMaxFlowRate": 23, "defaultAspirateFlowRate": { - "default": 6.5, - "valuesByApiLevel": { "2.14": 6.5 } + "default": 6, + "valuesByApiLevel": { "2.14": 6 } }, "defaultDispenseFlowRate": { - "default": 6.5, - "valuesByApiLevel": { "2.14": 6.5 } + "default": 6, + "valuesByApiLevel": { "2.14": 6 } }, "defaultBlowOutFlowRate": { "default": 10, @@ -114,14 +114,14 @@ "defaultPushOutVolume": 7 }, "t200": { - "uiMaxFlowRate": 45, + "uiMaxFlowRate": 23, "defaultAspirateFlowRate": { - "default": 15, - "valuesByApiLevel": { "2.14": 15 } + "default": 10, + "valuesByApiLevel": { "2.14": 10 } }, "defaultDispenseFlowRate": { - "default": 15, - "valuesByApiLevel": { "2.14": 15 } + "default": 10, + "valuesByApiLevel": { "2.14": 10 } }, "defaultBlowOutFlowRate": { "default": 10, @@ -166,7 +166,7 @@ ] } }, - "defaultPushOutVolume": 5 + "defaultPushOutVolume": 10 } }, "maxVolume": 200, diff --git a/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_1.json b/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_1.json new file mode 100644 index 000000000000..15c21cc300b9 --- /dev/null +++ b/shared-data/pipette/definitions/2/liquid/ninety_six_channel/p200/default/3_1.json @@ -0,0 +1,182 @@ +{ + "$otSharedSchema": "#/pipette/schemas/2/pipetteLiquidPropertiesSchema.json", + "supportedTips": { + "t20": { + "uiMaxFlowRate": 23, + "defaultAspirateFlowRate": { + "default": 6.5, + "valuesByApiLevel": { "2.14": 6.5 } + }, + "defaultDispenseFlowRate": { + "default": 6.5, + "valuesByApiLevel": { "2.14": 6.5 } + }, + "defaultBlowOutFlowRate": { + "default": 10, + "valuesByApiLevel": { "2.14": 10 } + }, + "defaultFlowAcceleration": 3160.0, + "defaultTipLength": 52.0, + "defaultReturnTipHeight": 0.6, + "aspirate": { + "default": { + "1": [ + [0.534, 1.2036, 1.0349], + [0.747, 0.599, 1.3577], + [1.238, 0.9829, 1.071], + [2.545, 0.2887, 1.9304], + [4.41, 0.0567, 2.5208], + [9.075, 0.0172, 2.6952], + [13.744, 0.0059, 2.7975], + [18.45, 0.0042, 2.8213], + [22.198, 0.002, 2.8607] + ] + } + }, + "dispense": { + "default": { + "1": [ + [0.534, 1.2036, 1.0349], + [0.747, 0.599, 1.3577], + [1.238, 0.9829, 1.071], + [2.545, 0.2887, 1.9304], + [4.41, 0.0567, 2.5208], + [9.075, 0.0172, 2.6952], + [13.744, 0.0059, 2.7975], + [18.45, 0.0042, 2.8213], + [22.198, 0.002, 2.8607] + ] + } + }, + "defaultPushOutVolume": 2 + }, + "t50": { + "uiMaxFlowRate": 23, + "defaultAspirateFlowRate": { + "default": 22, + "valuesByApiLevel": { "2.14": 22 } + }, + "defaultDispenseFlowRate": { + "default": 22, + "valuesByApiLevel": { "2.14": 22 } + }, + "defaultBlowOutFlowRate": { + "default": 22, + "valuesByApiLevel": { "2.14": 22 } + }, + "defaultFlowAcceleration": 3160.0, + "defaultTipLength": 57.9, + "defaultReturnTipHeight": 0.2, + "aspirate": { + "default": { + "1": [ + [0.482, 2.3672, 0.3733], + [0.995, 1.1105, 0.979], + [1.498, 0.535, 1.5515], + [1.99, 0.3001, 1.9035], + [2.482, 0.2001, 2.1026], + [3.289, -0.0198, 2.6483], + [4.374, 0.1522, 2.0827], + [5.353, 0.0557, 2.5045], + [6.313, 0.0317, 2.6331], + [7.247, 0.0135, 2.7479], + [8.213, 0.0217, 2.6884], + [9.155, 0.0098, 2.7862], + [13.93, 0.0087, 2.7968], + [23.46, 0.0032, 2.8728], + [37.72, 0.001, 2.9243], + [56.537, -0.0001, 2.967] + ] + } + }, + "dispense": { + "default": { + "1": [ + [0.482, 2.3672, 0.3733], + [0.995, 1.1105, 0.979], + [1.498, 0.535, 1.5515], + [1.99, 0.3001, 1.9035], + [2.482, 0.2001, 2.1026], + [3.289, -0.0198, 2.6483], + [4.374, 0.1522, 2.0827], + [5.353, 0.0557, 2.5045], + [6.313, 0.0317, 2.6331], + [7.247, 0.0135, 2.7479], + [8.213, 0.0217, 2.6884], + [9.155, 0.0098, 2.7862], + [13.93, 0.0087, 2.7968], + [23.46, 0.0032, 2.8728], + [37.72, 0.001, 2.9243], + [56.537, -0.0001, 2.967] + ] + } + }, + "defaultPushOutVolume": 5 + }, + "t200": { + "uiMaxFlowRate": 23, + "defaultAspirateFlowRate": { + "default": 15, + "valuesByApiLevel": { "2.14": 15 } + }, + "defaultDispenseFlowRate": { + "default": 15, + "valuesByApiLevel": { "2.14": 15 } + }, + "defaultBlowOutFlowRate": { + "default": 10, + "valuesByApiLevel": { "2.14": 10 } + }, + "defaultFlowAcceleration": 3160.0, + "defaultTipLength": 58.35, + "defaultReturnTipHeight": 0.6, + "aspirate": { + "default": { + "1": [ + [1.26, 1.4427, 0.1614], + [3.225, 0.2818, 1.6242], + [5.118, 0.0776, 2.2827], + [7.018, 0.0401, 2.4746], + [8.935, 0.0266, 2.5691], + [18.358, 0.0081, 2.7343], + [47.024, 0.0025, 2.8382], + [95.235, 0.0008, 2.9182], + [143.39, 0.0002, 2.9696], + [191.56, 0.0001, 2.9857], + [201.194, 0.0001, 2.9924], + [205.899, -0.0148, 5.9871] + ] + } + }, + "dispense": { + "default": { + "1": [ + [1.26, 1.4427, 0.1614], + [3.225, 0.2818, 1.6242], + [5.118, 0.0776, 2.2827], + [7.018, 0.0401, 2.4746], + [8.935, 0.0266, 2.5691], + [18.358, 0.0081, 2.7343], + [47.024, 0.0025, 2.8382], + [95.235, 0.0008, 2.9182], + [143.39, 0.0002, 2.9696], + [191.56, 0.0001, 2.9857], + [201.194, 0.0001, 2.9924], + [205.899, -0.0148, 5.9871] + ] + } + }, + "defaultPushOutVolume": 5 + } + }, + "maxVolume": 200, + "minVolume": 0.5, + "defaultTipracks": [ + "opentrons/opentrons_flex_96_tiprack_200ul/1", + "opentrons/opentrons_flex_96_tiprack_50ul/1", + "opentrons/opentrons_flex_96_tiprack_20ul/1", + "opentrons/opentrons_flex_96_filtertiprack_200ul/1", + "opentrons/opentrons_flex_96_filtertiprack_50ul/1", + "opentrons/opentrons_flex_96_filtertiprack_20ul/1" + ] +} diff --git a/shared-data/pipette/definitions/2/liquid/single_channel/p50/default/3_6.json b/shared-data/pipette/definitions/2/liquid/single_channel/p50/default/3_6.json index e6506c77bb80..b04ec62bad26 100644 --- a/shared-data/pipette/definitions/2/liquid/single_channel/p50/default/3_6.json +++ b/shared-data/pipette/definitions/2/liquid/single_channel/p50/default/3_6.json @@ -4,12 +4,12 @@ "t20": { "uiMaxFlowRate": 57, "defaultAspirateFlowRate": { - "default": 35, - "valuesByApiLevel": { "2.14": 35 } + "default": 22, + "valuesByApiLevel": { "2.14": 22 } }, "defaultDispenseFlowRate": { - "default": 57, - "valuesByApiLevel": { "2.14": 57 } + "default": 22, + "valuesByApiLevel": { "2.14": 22 } }, "defaultBlowOutFlowRate": { "default": 57, @@ -64,7 +64,7 @@ ] } }, - "defaultPushOutVolume": 2 + "defaultPushOutVolume": 3.5 }, "t50": { "uiMaxFlowRate": 57, diff --git a/shared-data/pipette/definitions/2/liquid/single_channel/p50/lowVolumeDefault/3_6.json b/shared-data/pipette/definitions/2/liquid/single_channel/p50/lowVolumeDefault/3_6.json index 320494037fc6..a31e5e983029 100644 --- a/shared-data/pipette/definitions/2/liquid/single_channel/p50/lowVolumeDefault/3_6.json +++ b/shared-data/pipette/definitions/2/liquid/single_channel/p50/lowVolumeDefault/3_6.json @@ -4,12 +4,12 @@ "t20": { "uiMaxFlowRate": 26.7, "defaultAspirateFlowRate": { - "default": 26.7, - "valuesByApiLevel": { "2.14": 26.7 } + "default": 22, + "valuesByApiLevel": { "2.14": 22 } }, "defaultDispenseFlowRate": { - "default": 26.7, - "valuesByApiLevel": { "2.14": 26.7 } + "default": 22, + "valuesByApiLevel": { "2.14": 22 } }, "defaultBlowOutFlowRate": { "default": 26.7, @@ -62,7 +62,7 @@ ] } }, - "defaultPushOutVolume": 7 + "defaultPushOutVolume": 3.5 }, "t50": { "uiMaxFlowRate": 26.7, diff --git a/shared-data/pipette/schemas/2/pipetteGeometrySchema.json b/shared-data/pipette/schemas/2/pipetteGeometrySchema.json index a9a263b45f55..db786f84dc08 100644 --- a/shared-data/pipette/schemas/2/pipetteGeometrySchema.json +++ b/shared-data/pipette/schemas/2/pipetteGeometrySchema.json @@ -90,6 +90,13 @@ "description": "Minimum space requirements for Liquid Level Detection to work properly", "additionalProperties": false, "properties": { + "t20": { + "type": "object", + "properties": { + "minHeight": { "type": "number" }, + "minVolume": { "type": "number" } + } + }, "t50": { "type": "object", "properties": { diff --git a/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py b/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py index e0797ddec08f..2024cb0d94be 100644 --- a/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py +++ b/shared-data/python/tests/pipette/test_max_flow_rates_per_volume.py @@ -87,19 +87,25 @@ def test_max_flow_rates_per_volume(pipette: PipetteModel, action: str) -> None: to match the default blowout and dispense flowRates. uiMaxFlowRate will be reevaluated in the future.""" if not ( - pipette_model_version_str - in { - "p50_single_v3.4", - "p50_single_v3.5", - "p50_single_v3.6", - "p50_multi_v3.5", - "p50_multi_v3.4", - } - and liquid_properties.min_volume == 5.0 + ( + pipette_model_version_str + in { + "p50_single_v3.4", + "p50_single_v3.5", + "p50_single_v3.6", + "p50_multi_v3.5", + "p50_multi_v3.4", + } + and liquid_properties.min_volume == 5.0 + ) + or ( + pipette_model_version_str in {"p200_96_v3.0", "p200_96_v3.1"} + and liquid_properties.min_volume == 0.5 + ) ): - assert supported_tip.ui_max_flow_rate < _get_max_flow_rate_at_volume( + assert supported_tip.ui_max_flow_rate <= _get_max_flow_rate_at_volume( supported_tip.aspirate, pipette, liquid_properties.min_volume ) - assert supported_tip.ui_max_flow_rate < _get_max_flow_rate_at_volume( + assert supported_tip.ui_max_flow_rate <= _get_max_flow_rate_at_volume( supported_tip.dispense, pipette, liquid_properties.min_volume )