opentrons 8.3.0a0__py2.py3-none-any.whl → 8.3.0a2__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opentrons/calibration_storage/deck_configuration.py +3 -3
- opentrons/calibration_storage/file_operators.py +3 -3
- opentrons/calibration_storage/helpers.py +3 -1
- opentrons/calibration_storage/ot2/models/v1.py +16 -29
- opentrons/calibration_storage/ot2/tip_length.py +7 -4
- opentrons/calibration_storage/ot3/models/v1.py +14 -23
- opentrons/cli/analyze.py +18 -6
- opentrons/drivers/asyncio/communication/__init__.py +2 -0
- opentrons/drivers/asyncio/communication/errors.py +16 -3
- opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
- opentrons/drivers/command_builder.py +2 -2
- opentrons/drivers/flex_stacker/__init__.py +9 -0
- opentrons/drivers/flex_stacker/abstract.py +89 -0
- opentrons/drivers/flex_stacker/driver.py +260 -0
- opentrons/drivers/flex_stacker/simulator.py +109 -0
- opentrons/drivers/flex_stacker/types.py +138 -0
- opentrons/drivers/heater_shaker/driver.py +18 -3
- opentrons/drivers/temp_deck/driver.py +13 -3
- opentrons/drivers/thermocycler/driver.py +17 -3
- opentrons/execute.py +3 -1
- opentrons/hardware_control/__init__.py +1 -2
- opentrons/hardware_control/api.py +28 -20
- opentrons/hardware_control/backends/flex_protocol.py +4 -6
- opentrons/hardware_control/backends/ot3controller.py +177 -59
- opentrons/hardware_control/backends/ot3simulator.py +10 -8
- opentrons/hardware_control/backends/ot3utils.py +3 -13
- opentrons/hardware_control/dev_types.py +2 -0
- opentrons/hardware_control/emulation/heater_shaker.py +4 -0
- opentrons/hardware_control/emulation/module_server/client.py +1 -1
- opentrons/hardware_control/emulation/module_server/server.py +5 -3
- opentrons/hardware_control/emulation/settings.py +3 -4
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
- opentrons/hardware_control/instruments/ot2/pipette.py +9 -21
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
- opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
- opentrons/hardware_control/instruments/ot3/pipette.py +13 -22
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
- opentrons/hardware_control/modules/mod_abc.py +2 -2
- opentrons/hardware_control/motion_utilities.py +68 -0
- opentrons/hardware_control/nozzle_manager.py +39 -41
- opentrons/hardware_control/ot3_calibration.py +1 -1
- opentrons/hardware_control/ot3api.py +34 -22
- opentrons/hardware_control/protocols/gripper_controller.py +3 -0
- opentrons/hardware_control/protocols/hardware_manager.py +5 -1
- opentrons/hardware_control/protocols/liquid_handler.py +18 -0
- opentrons/hardware_control/protocols/motion_controller.py +6 -0
- opentrons/hardware_control/robot_calibration.py +1 -1
- opentrons/hardware_control/types.py +61 -0
- opentrons/protocol_api/__init__.py +20 -1
- opentrons/protocol_api/_liquid.py +24 -49
- opentrons/protocol_api/_liquid_properties.py +754 -0
- opentrons/protocol_api/_types.py +24 -0
- opentrons/protocol_api/core/common.py +2 -0
- opentrons/protocol_api/core/engine/instrument.py +67 -10
- opentrons/protocol_api/core/engine/labware.py +29 -7
- opentrons/protocol_api/core/engine/protocol.py +130 -5
- opentrons/protocol_api/core/engine/robot.py +139 -0
- opentrons/protocol_api/core/engine/well.py +4 -1
- opentrons/protocol_api/core/instrument.py +42 -4
- opentrons/protocol_api/core/labware.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +34 -3
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
- opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +34 -3
- opentrons/protocol_api/core/protocol.py +34 -1
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/instrument_context.py +145 -43
- opentrons/protocol_api/labware.py +231 -7
- opentrons/protocol_api/module_contexts.py +21 -17
- opentrons/protocol_api/protocol_context.py +125 -4
- opentrons/protocol_api/robot_context.py +204 -32
- opentrons/protocol_api/validation.py +261 -3
- opentrons/protocol_engine/__init__.py +4 -0
- opentrons/protocol_engine/actions/actions.py +2 -3
- opentrons/protocol_engine/clients/sync_client.py +18 -0
- opentrons/protocol_engine/commands/__init__.py +81 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
- opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
- opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
- opentrons/protocol_engine/commands/aspirate.py +103 -53
- opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
- opentrons/protocol_engine/commands/blow_out.py +44 -39
- opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
- opentrons/protocol_engine/commands/command.py +73 -66
- opentrons/protocol_engine/commands/command_unions.py +101 -1
- opentrons/protocol_engine/commands/comment.py +1 -1
- opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
- opentrons/protocol_engine/commands/custom.py +6 -12
- opentrons/protocol_engine/commands/dispense.py +82 -48
- opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
- opentrons/protocol_engine/commands/drop_tip.py +52 -31
- opentrons/protocol_engine/commands/drop_tip_in_place.py +13 -3
- opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
- opentrons/protocol_engine/commands/get_next_tip.py +134 -0
- opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/home.py +13 -4
- opentrons/protocol_engine/commands/liquid_probe.py +60 -25
- opentrons/protocol_engine/commands/load_labware.py +29 -7
- opentrons/protocol_engine/commands/load_lid.py +146 -0
- opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
- opentrons/protocol_engine/commands/load_liquid.py +12 -4
- opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
- opentrons/protocol_engine/commands/load_module.py +31 -10
- opentrons/protocol_engine/commands/load_pipette.py +19 -8
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
- opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
- opentrons/protocol_engine/commands/move_labware.py +19 -6
- opentrons/protocol_engine/commands/move_relative.py +35 -25
- opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
- opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
- opentrons/protocol_engine/commands/move_to_well.py +40 -24
- opentrons/protocol_engine/commands/movement_common.py +338 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
- opentrons/protocol_engine/commands/pipetting_common.py +169 -87
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
- opentrons/protocol_engine/commands/reload_labware.py +1 -1
- opentrons/protocol_engine/commands/retract_axis.py +1 -1
- opentrons/protocol_engine/commands/robot/__init__.py +69 -0
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
- opentrons/protocol_engine/commands/robot/common.py +18 -0
- opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
- opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
- opentrons/protocol_engine/commands/robot/move_to.py +94 -0
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
- opentrons/protocol_engine/commands/save_position.py +14 -5
- opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
- opentrons/protocol_engine/commands/set_status_bar.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +8 -2
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/touch_tip.py +65 -16
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +4 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
- opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
- opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
- opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
- opentrons/protocol_engine/errors/__init__.py +8 -0
- opentrons/protocol_engine/errors/error_occurrence.py +19 -20
- opentrons/protocol_engine/errors/exceptions.py +50 -0
- opentrons/protocol_engine/execution/command_executor.py +1 -1
- opentrons/protocol_engine/execution/equipment.py +73 -5
- opentrons/protocol_engine/execution/gantry_mover.py +364 -8
- opentrons/protocol_engine/execution/movement.py +27 -0
- opentrons/protocol_engine/execution/pipetting.py +5 -1
- opentrons/protocol_engine/execution/tip_handler.py +4 -6
- opentrons/protocol_engine/notes/notes.py +1 -1
- opentrons/protocol_engine/protocol_engine.py +7 -6
- opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
- opentrons/protocol_engine/resources/labware_validation.py +5 -0
- opentrons/protocol_engine/resources/module_data_provider.py +1 -1
- opentrons/protocol_engine/resources/pipette_data_provider.py +12 -0
- opentrons/protocol_engine/slot_standardization.py +9 -9
- opentrons/protocol_engine/state/_move_types.py +9 -5
- opentrons/protocol_engine/state/_well_math.py +193 -0
- opentrons/protocol_engine/state/addressable_areas.py +25 -61
- opentrons/protocol_engine/state/command_history.py +12 -0
- opentrons/protocol_engine/state/commands.py +17 -13
- opentrons/protocol_engine/state/files.py +10 -12
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/frustum_helpers.py +57 -32
- opentrons/protocol_engine/state/geometry.py +47 -1
- opentrons/protocol_engine/state/labware.py +79 -25
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +16 -4
- opentrons/protocol_engine/state/modules.py +52 -70
- opentrons/protocol_engine/state/motion.py +6 -1
- opentrons/protocol_engine/state/pipettes.py +135 -58
- opentrons/protocol_engine/state/state.py +21 -2
- opentrons/protocol_engine/state/state_summary.py +4 -2
- opentrons/protocol_engine/state/tips.py +11 -44
- opentrons/protocol_engine/state/update_types.py +343 -48
- opentrons/protocol_engine/state/wells.py +19 -11
- opentrons/protocol_engine/types.py +176 -28
- opentrons/protocol_reader/extract_labware_definitions.py +5 -2
- opentrons/protocol_reader/file_format_validator.py +5 -5
- opentrons/protocol_runner/json_file_reader.py +9 -3
- opentrons/protocol_runner/json_translator.py +51 -25
- opentrons/protocol_runner/legacy_command_mapper.py +66 -64
- opentrons/protocol_runner/protocol_runner.py +35 -4
- opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
- opentrons/protocol_runner/run_orchestrator.py +13 -3
- opentrons/protocols/advanced_control/common.py +38 -0
- opentrons/protocols/advanced_control/mix.py +1 -1
- opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
- opentrons/protocols/advanced_control/transfers/common.py +56 -0
- opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/instrument.py +1 -1
- opentrons/protocols/api_support/util.py +10 -0
- opentrons/protocols/labware.py +70 -8
- opentrons/protocols/models/json_protocol.py +5 -9
- opentrons/simulate.py +3 -1
- opentrons/types.py +162 -2
- opentrons/util/entrypoint_util.py +2 -5
- opentrons/util/logging_config.py +1 -1
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/METADATA +16 -15
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/RECORD +229 -202
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/WHEEL +1 -1
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/LICENSE +0 -0
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/entry_points.txt +0 -0
- {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/top_level.txt +0 -0
|
@@ -2,13 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
import logging
|
|
3
3
|
from contextlib import ExitStack
|
|
4
4
|
from typing import Any, List, Optional, Sequence, Union, cast, Dict
|
|
5
|
-
from opentrons.protocol_engine.errors.exceptions import TipNotAttachedError
|
|
6
5
|
from opentrons_shared_data.errors.exceptions import (
|
|
7
6
|
CommandPreconditionViolated,
|
|
8
7
|
CommandParameterLimitViolated,
|
|
9
8
|
UnexpectedTipRemovalError,
|
|
10
9
|
UnsupportedHardwareCommand,
|
|
11
10
|
)
|
|
11
|
+
from opentrons_shared_data.robot.types import RobotTypeEnum
|
|
12
|
+
|
|
12
13
|
from opentrons.legacy_broker import LegacyBroker
|
|
13
14
|
from opentrons.hardware_control.dev_types import PipetteDict
|
|
14
15
|
from opentrons import types
|
|
@@ -16,8 +17,7 @@ from opentrons.legacy_commands import commands as cmds
|
|
|
16
17
|
|
|
17
18
|
from opentrons.legacy_commands import publisher
|
|
18
19
|
from opentrons.protocols.advanced_control.mix import mix_from_kwargs
|
|
19
|
-
from opentrons.protocols.advanced_control import
|
|
20
|
-
|
|
20
|
+
from opentrons.protocols.advanced_control.transfers import transfer as v1_transfer
|
|
21
21
|
from opentrons.protocols.api_support.deck_type import NoTrashDefinedError
|
|
22
22
|
from opentrons.protocols.api_support.types import APIVersion
|
|
23
23
|
from opentrons.protocols.api_support import instrument
|
|
@@ -29,7 +29,6 @@ from opentrons.protocols.api_support.util import (
|
|
|
29
29
|
APIVersionError,
|
|
30
30
|
UnsupportedAPIError,
|
|
31
31
|
)
|
|
32
|
-
from opentrons.hardware_control.nozzle_manager import NozzleConfigurationType
|
|
33
32
|
|
|
34
33
|
from .core.common import InstrumentCore, ProtocolCore
|
|
35
34
|
from .core.engine import ENGINE_CORE_API_VERSION
|
|
@@ -37,10 +36,13 @@ from .core.legacy.legacy_instrument_core import LegacyInstrumentCore
|
|
|
37
36
|
from .config import Clearances
|
|
38
37
|
from .disposal_locations import TrashBin, WasteChute
|
|
39
38
|
from ._nozzle_layout import NozzleLayout
|
|
39
|
+
from ._liquid import LiquidClass
|
|
40
40
|
from . import labware, validation
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
from ..config import feature_flags
|
|
42
|
+
from ..protocols.advanced_control.transfers.common import (
|
|
43
|
+
TransferTipPolicyV2,
|
|
44
|
+
TransferTipPolicyV2Type,
|
|
45
|
+
)
|
|
44
46
|
|
|
45
47
|
_DEFAULT_ASPIRATE_CLEARANCE = 1.0
|
|
46
48
|
_DEFAULT_DISPENSE_CLEARANCE = 1.0
|
|
@@ -61,6 +63,10 @@ _DISPOSAL_LOCATION_OFFSET_ADDED_IN = APIVersion(2, 18)
|
|
|
61
63
|
"""The version after which offsets for deck configured trash containers and changes to alternating tip drop behavior were introduced."""
|
|
62
64
|
_PARTIAL_NOZZLE_CONFIGURATION_SINGLE_ROW_PARTIAL_COLUMN_ADDED_IN = APIVersion(2, 20)
|
|
63
65
|
"""The version after which partial nozzle configurations of single, row, and partial column layouts became available."""
|
|
66
|
+
_AIR_GAP_TRACKING_ADDED_IN = APIVersion(2, 22)
|
|
67
|
+
"""The version after which air gaps should be implemented with a separate call instead of an aspirate for better liquid volume tracking."""
|
|
68
|
+
|
|
69
|
+
AdvancedLiquidHandling = v1_transfer.AdvancedLiquidHandling
|
|
64
70
|
|
|
65
71
|
|
|
66
72
|
class InstrumentContext(publisher.CommandPublisher):
|
|
@@ -258,7 +264,7 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
258
264
|
self.api_version >= APIVersion(2, 20)
|
|
259
265
|
and well is not None
|
|
260
266
|
and self.liquid_presence_detection
|
|
261
|
-
and self.
|
|
267
|
+
and self._core.nozzle_configuration_valid_for_lld()
|
|
262
268
|
and self._core.get_current_volume() == 0
|
|
263
269
|
):
|
|
264
270
|
self._raise_if_pressure_not_supported_by_pipette()
|
|
@@ -515,6 +521,8 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
515
521
|
``pipette.mix(1, location=wellplate['A1'])`` is a valid call, but
|
|
516
522
|
``pipette.mix(1, wellplate['A1'])`` is not.
|
|
517
523
|
|
|
524
|
+
.. versionchanged:: 2.21
|
|
525
|
+
Does not repeatedly check for liquid presence.
|
|
518
526
|
"""
|
|
519
527
|
_log.debug(
|
|
520
528
|
"mixing {}uL with {} repetitions in {} at rate={}".format(
|
|
@@ -755,7 +763,11 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
755
763
|
``pipette.air_gap(height=2)``. If you call ``air_gap`` with a single,
|
|
756
764
|
unnamed argument, it will always be interpreted as a volume.
|
|
757
765
|
|
|
758
|
-
|
|
766
|
+
.. TODO: restore this as a note block for 2.22 docs
|
|
767
|
+
Before API version 2.22, this function was implemented as an aspirate, and
|
|
768
|
+
dispensing into a well would add the air gap volume to the liquid tracked in
|
|
769
|
+
the well. At or above API version 2.22, air gap volume is not counted as liquid
|
|
770
|
+
when dispensing into a well.
|
|
759
771
|
"""
|
|
760
772
|
if not self._core.has_tip():
|
|
761
773
|
raise UnexpectedTipRemovalError("air_gap", self.name, self.mount)
|
|
@@ -767,7 +779,12 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
767
779
|
raise RuntimeError("No previous Well cached to perform air gap")
|
|
768
780
|
target = loc.labware.as_well().top(height)
|
|
769
781
|
self.move_to(target, publish=False)
|
|
770
|
-
self.
|
|
782
|
+
if self.api_version >= _AIR_GAP_TRACKING_ADDED_IN:
|
|
783
|
+
c_vol = self._core.get_available_volume() if volume is None else volume
|
|
784
|
+
flow_rate = self._core.get_aspirate_flow_rate()
|
|
785
|
+
self._core.air_gap_in_place(c_vol, flow_rate)
|
|
786
|
+
else:
|
|
787
|
+
self.aspirate(volume)
|
|
771
788
|
return self
|
|
772
789
|
|
|
773
790
|
@publisher.publish(command=cmds.return_tip)
|
|
@@ -936,7 +953,7 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
936
953
|
if location is None:
|
|
937
954
|
if (
|
|
938
955
|
nozzle_map is not None
|
|
939
|
-
and nozzle_map.configuration != NozzleConfigurationType.FULL
|
|
956
|
+
and nozzle_map.configuration != types.NozzleConfigurationType.FULL
|
|
940
957
|
and self.starting_tip is not None
|
|
941
958
|
):
|
|
942
959
|
# Disallowing this avoids concerning the system with the direction
|
|
@@ -1208,7 +1225,6 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1208
1225
|
self._core.home_plunger()
|
|
1209
1226
|
return self
|
|
1210
1227
|
|
|
1211
|
-
# TODO (spp, 2024-03-08): verify if ok to & change source & dest types to AdvancedLiquidHandling
|
|
1212
1228
|
@publisher.publish(command=cmds.distribute)
|
|
1213
1229
|
@requires_version(2, 0)
|
|
1214
1230
|
def distribute(
|
|
@@ -1248,7 +1264,6 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1248
1264
|
|
|
1249
1265
|
return self.transfer(volume, source, dest, **kwargs)
|
|
1250
1266
|
|
|
1251
|
-
# TODO (spp, 2024-03-08): verify if ok to & change source & dest types to AdvancedLiquidHandling
|
|
1252
1267
|
@publisher.publish(command=cmds.consolidate)
|
|
1253
1268
|
@requires_version(2, 0)
|
|
1254
1269
|
def consolidate(
|
|
@@ -1398,9 +1413,9 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1398
1413
|
mix_strategy, mix_opts = mix_from_kwargs(kwargs)
|
|
1399
1414
|
|
|
1400
1415
|
if trash:
|
|
1401
|
-
drop_tip =
|
|
1416
|
+
drop_tip = v1_transfer.DropTipStrategy.TRASH
|
|
1402
1417
|
else:
|
|
1403
|
-
drop_tip =
|
|
1418
|
+
drop_tip = v1_transfer.DropTipStrategy.RETURN
|
|
1404
1419
|
|
|
1405
1420
|
new_tip = kwargs.get("new_tip")
|
|
1406
1421
|
if isinstance(new_tip, str):
|
|
@@ -1422,19 +1437,19 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1422
1437
|
|
|
1423
1438
|
if blow_out and not blowout_location:
|
|
1424
1439
|
if self.current_volume:
|
|
1425
|
-
blow_out_strategy =
|
|
1440
|
+
blow_out_strategy = v1_transfer.BlowOutStrategy.SOURCE
|
|
1426
1441
|
else:
|
|
1427
|
-
blow_out_strategy =
|
|
1442
|
+
blow_out_strategy = v1_transfer.BlowOutStrategy.TRASH
|
|
1428
1443
|
elif blow_out and blowout_location:
|
|
1429
1444
|
if blowout_location == "source well":
|
|
1430
|
-
blow_out_strategy =
|
|
1445
|
+
blow_out_strategy = v1_transfer.BlowOutStrategy.SOURCE
|
|
1431
1446
|
elif blowout_location == "destination well":
|
|
1432
|
-
blow_out_strategy =
|
|
1447
|
+
blow_out_strategy = v1_transfer.BlowOutStrategy.DEST
|
|
1433
1448
|
elif blowout_location == "trash":
|
|
1434
|
-
blow_out_strategy =
|
|
1449
|
+
blow_out_strategy = v1_transfer.BlowOutStrategy.TRASH
|
|
1435
1450
|
|
|
1436
1451
|
if new_tip != types.TransferTipPolicy.NEVER:
|
|
1437
|
-
|
|
1452
|
+
_, next_tip = labware.next_available_tip(
|
|
1438
1453
|
self.starting_tip,
|
|
1439
1454
|
self.tip_racks,
|
|
1440
1455
|
active_channels,
|
|
@@ -1446,9 +1461,9 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1446
1461
|
|
|
1447
1462
|
touch_tip = None
|
|
1448
1463
|
if kwargs.get("touch_tip"):
|
|
1449
|
-
touch_tip =
|
|
1464
|
+
touch_tip = v1_transfer.TouchTipStrategy.ALWAYS
|
|
1450
1465
|
|
|
1451
|
-
default_args =
|
|
1466
|
+
default_args = v1_transfer.Transfer()
|
|
1452
1467
|
|
|
1453
1468
|
disposal = kwargs.get("disposal_volume")
|
|
1454
1469
|
if disposal is None:
|
|
@@ -1461,7 +1476,7 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1461
1476
|
f"working volume, {max_volume}uL"
|
|
1462
1477
|
)
|
|
1463
1478
|
|
|
1464
|
-
transfer_args =
|
|
1479
|
+
transfer_args = v1_transfer.Transfer(
|
|
1465
1480
|
new_tip=new_tip or default_args.new_tip,
|
|
1466
1481
|
air_gap=air_gap,
|
|
1467
1482
|
carryover=kwargs.get("carryover") or default_args.carryover,
|
|
@@ -1474,10 +1489,10 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1474
1489
|
blow_out_strategy=blow_out_strategy or default_args.blow_out_strategy,
|
|
1475
1490
|
touch_tip_strategy=(touch_tip or default_args.touch_tip_strategy),
|
|
1476
1491
|
)
|
|
1477
|
-
transfer_options =
|
|
1492
|
+
transfer_options = v1_transfer.TransferOptions(
|
|
1478
1493
|
transfer=transfer_args, mix=mix_opts
|
|
1479
1494
|
)
|
|
1480
|
-
plan =
|
|
1495
|
+
plan = v1_transfer.TransferPlan(
|
|
1481
1496
|
volume,
|
|
1482
1497
|
source,
|
|
1483
1498
|
dest,
|
|
@@ -1490,10 +1505,113 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1490
1505
|
self._execute_transfer(plan)
|
|
1491
1506
|
return self
|
|
1492
1507
|
|
|
1493
|
-
def _execute_transfer(self, plan:
|
|
1508
|
+
def _execute_transfer(self, plan: v1_transfer.TransferPlan) -> None:
|
|
1494
1509
|
for cmd in plan:
|
|
1495
1510
|
getattr(self, cmd["method"])(*cmd["args"], **cmd["kwargs"])
|
|
1496
1511
|
|
|
1512
|
+
def transfer_liquid(
|
|
1513
|
+
self,
|
|
1514
|
+
liquid_class: LiquidClass,
|
|
1515
|
+
volume: float,
|
|
1516
|
+
source: Union[
|
|
1517
|
+
labware.Well, Sequence[labware.Well], Sequence[Sequence[labware.Well]]
|
|
1518
|
+
],
|
|
1519
|
+
dest: Union[
|
|
1520
|
+
labware.Well, Sequence[labware.Well], Sequence[Sequence[labware.Well]]
|
|
1521
|
+
],
|
|
1522
|
+
new_tip: TransferTipPolicyV2Type = "once",
|
|
1523
|
+
tip_drop_location: Optional[
|
|
1524
|
+
Union[types.Location, labware.Well, TrashBin, WasteChute]
|
|
1525
|
+
] = None, # Maybe call this 'tip_drop_location' which is similar to PD
|
|
1526
|
+
) -> InstrumentContext:
|
|
1527
|
+
"""Transfer liquid from source to dest using the specified liquid class properties.
|
|
1528
|
+
|
|
1529
|
+
TODO: Add args description.
|
|
1530
|
+
"""
|
|
1531
|
+
if not feature_flags.allow_liquid_classes(
|
|
1532
|
+
robot_type=RobotTypeEnum.robot_literal_to_enum(
|
|
1533
|
+
self._protocol_core.robot_type
|
|
1534
|
+
)
|
|
1535
|
+
):
|
|
1536
|
+
raise NotImplementedError("This method is not implemented.")
|
|
1537
|
+
|
|
1538
|
+
flat_sources_list = validation.ensure_valid_flat_wells_list_for_transfer_v2(
|
|
1539
|
+
source
|
|
1540
|
+
)
|
|
1541
|
+
flat_dests_list = validation.ensure_valid_flat_wells_list_for_transfer_v2(dest)
|
|
1542
|
+
for well in flat_sources_list + flat_dests_list:
|
|
1543
|
+
instrument.validate_takes_liquid(
|
|
1544
|
+
location=well.top(),
|
|
1545
|
+
reject_module=True,
|
|
1546
|
+
reject_adapter=True,
|
|
1547
|
+
)
|
|
1548
|
+
if len(flat_sources_list) != len(flat_dests_list):
|
|
1549
|
+
raise ValueError(
|
|
1550
|
+
"Sources and destinations should be of the same length in order to perform a transfer."
|
|
1551
|
+
" To transfer liquid from one source to many destinations, use 'distribute_liquid',"
|
|
1552
|
+
" to transfer liquid onto one destinations from many sources, use 'consolidate_liquid'."
|
|
1553
|
+
)
|
|
1554
|
+
|
|
1555
|
+
valid_new_tip = validation.ensure_new_tip_policy(new_tip)
|
|
1556
|
+
if valid_new_tip == TransferTipPolicyV2.NEVER:
|
|
1557
|
+
if self._last_tip_picked_up_from is None:
|
|
1558
|
+
raise RuntimeError(
|
|
1559
|
+
"Pipette has no tip attached to perform transfer."
|
|
1560
|
+
" Either do a pick_up_tip beforehand or specify a new_tip parameter"
|
|
1561
|
+
" of 'once' or 'always'."
|
|
1562
|
+
)
|
|
1563
|
+
else:
|
|
1564
|
+
tiprack = self._last_tip_picked_up_from.parent
|
|
1565
|
+
else:
|
|
1566
|
+
tiprack, well = labware.next_available_tip(
|
|
1567
|
+
starting_tip=self.starting_tip,
|
|
1568
|
+
tip_racks=self.tip_racks,
|
|
1569
|
+
channels=self.active_channels,
|
|
1570
|
+
nozzle_map=self._core.get_nozzle_map(),
|
|
1571
|
+
)
|
|
1572
|
+
if self.current_volume != 0:
|
|
1573
|
+
raise RuntimeError(
|
|
1574
|
+
"A transfer on a liquid class cannot start with liquid already in the tip."
|
|
1575
|
+
" Ensure that all previously aspirated liquid is dispensed before starting"
|
|
1576
|
+
" a new transfer."
|
|
1577
|
+
)
|
|
1578
|
+
|
|
1579
|
+
_trash_location: Union[types.Location, labware.Well, TrashBin, WasteChute]
|
|
1580
|
+
if tip_drop_location is None:
|
|
1581
|
+
saved_trash = self.trash_container
|
|
1582
|
+
if isinstance(saved_trash, labware.Labware):
|
|
1583
|
+
_trash_location = saved_trash.wells()[0]
|
|
1584
|
+
else:
|
|
1585
|
+
_trash_location = saved_trash
|
|
1586
|
+
else:
|
|
1587
|
+
_trash_location = tip_drop_location
|
|
1588
|
+
|
|
1589
|
+
checked_trash_location = (
|
|
1590
|
+
validation.ensure_valid_tip_drop_location_for_transfer_v2(
|
|
1591
|
+
tip_drop_location=_trash_location
|
|
1592
|
+
)
|
|
1593
|
+
)
|
|
1594
|
+
liquid_class_id = self._core.load_liquid_class(
|
|
1595
|
+
liquid_class=liquid_class,
|
|
1596
|
+
pipette_load_name=self.name,
|
|
1597
|
+
tiprack_uri=tiprack.uri,
|
|
1598
|
+
)
|
|
1599
|
+
|
|
1600
|
+
self._core.transfer_liquid(
|
|
1601
|
+
liquid_class_id=liquid_class_id,
|
|
1602
|
+
volume=volume,
|
|
1603
|
+
source=[well._core for well in flat_sources_list],
|
|
1604
|
+
dest=[well._core for well in flat_dests_list],
|
|
1605
|
+
new_tip=valid_new_tip,
|
|
1606
|
+
trash_location=(
|
|
1607
|
+
checked_trash_location._core
|
|
1608
|
+
if isinstance(checked_trash_location, labware.Well)
|
|
1609
|
+
else checked_trash_location
|
|
1610
|
+
),
|
|
1611
|
+
)
|
|
1612
|
+
|
|
1613
|
+
return self
|
|
1614
|
+
|
|
1497
1615
|
@requires_version(2, 0)
|
|
1498
1616
|
def delay(self, *args: Any, **kwargs: Any) -> None:
|
|
1499
1617
|
"""
|
|
@@ -1874,19 +1992,6 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1874
1992
|
else:
|
|
1875
1993
|
return self._protocol_core.get_last_location()
|
|
1876
1994
|
|
|
1877
|
-
def _96_tip_config_valid(self) -> bool:
|
|
1878
|
-
n_map = self._core.get_nozzle_map()
|
|
1879
|
-
channels = self._core.get_active_channels()
|
|
1880
|
-
if channels == 96:
|
|
1881
|
-
if (
|
|
1882
|
-
n_map.back_left != n_map.full_instrument_back_left
|
|
1883
|
-
and n_map.front_right != n_map.full_instrument_front_right
|
|
1884
|
-
):
|
|
1885
|
-
raise TipNotAttachedError(
|
|
1886
|
-
"Either the front right or the back left nozzle must have a tip attached to do LLD."
|
|
1887
|
-
)
|
|
1888
|
-
return True
|
|
1889
|
-
|
|
1890
1995
|
def __repr__(self) -> str:
|
|
1891
1996
|
return "<{}: {} in {}>".format(
|
|
1892
1997
|
self.__class__.__name__,
|
|
@@ -2149,7 +2254,6 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
2149
2254
|
"""
|
|
2150
2255
|
self._raise_if_pressure_not_supported_by_pipette()
|
|
2151
2256
|
loc = well.top()
|
|
2152
|
-
self._96_tip_config_valid()
|
|
2153
2257
|
return self._core.detect_liquid_presence(well._core, loc)
|
|
2154
2258
|
|
|
2155
2259
|
@requires_version(2, 20)
|
|
@@ -2163,7 +2267,6 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
2163
2267
|
"""
|
|
2164
2268
|
self._raise_if_pressure_not_supported_by_pipette()
|
|
2165
2269
|
loc = well.top()
|
|
2166
|
-
self._96_tip_config_valid()
|
|
2167
2270
|
self._core.liquid_probe_with_recovery(well._core, loc)
|
|
2168
2271
|
|
|
2169
2272
|
@requires_version(2, 20)
|
|
@@ -2178,7 +2281,6 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
2178
2281
|
"""
|
|
2179
2282
|
self._raise_if_pressure_not_supported_by_pipette()
|
|
2180
2283
|
loc = well.top()
|
|
2181
|
-
self._96_tip_config_valid()
|
|
2182
2284
|
height = self._core.liquid_probe_without_recovery(well._core, loc)
|
|
2183
2285
|
return height
|
|
2184
2286
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""opentrons.protocol_api.labware: classes and functions for labware handling
|
|
2
2
|
|
|
3
3
|
This module provides things like :py:class:`Labware`, and :py:class:`Well`
|
|
4
4
|
to encapsulate labware instances used in protocols
|
|
@@ -13,18 +13,29 @@ from __future__ import annotations
|
|
|
13
13
|
import logging
|
|
14
14
|
|
|
15
15
|
from itertools import dropwhile
|
|
16
|
-
from typing import
|
|
16
|
+
from typing import (
|
|
17
|
+
TYPE_CHECKING,
|
|
18
|
+
Any,
|
|
19
|
+
List,
|
|
20
|
+
Dict,
|
|
21
|
+
Optional,
|
|
22
|
+
Union,
|
|
23
|
+
Tuple,
|
|
24
|
+
cast,
|
|
25
|
+
Sequence,
|
|
26
|
+
Mapping,
|
|
27
|
+
)
|
|
17
28
|
|
|
18
29
|
from opentrons_shared_data.labware.types import LabwareDefinition, LabwareParameters
|
|
19
30
|
|
|
20
|
-
from opentrons.types import Location, Point
|
|
31
|
+
from opentrons.types import Location, Point, NozzleMapInterface
|
|
21
32
|
from opentrons.protocols.api_support.types import APIVersion
|
|
22
33
|
from opentrons.protocols.api_support.util import (
|
|
23
34
|
requires_version,
|
|
24
35
|
APIVersionError,
|
|
25
36
|
UnsupportedAPIError,
|
|
26
37
|
)
|
|
27
|
-
|
|
38
|
+
|
|
28
39
|
|
|
29
40
|
# TODO(mc, 2022-09-02): re-exports provided for backwards compatibility
|
|
30
41
|
# remove when their usage is no longer needed
|
|
@@ -280,6 +291,10 @@ class Well:
|
|
|
280
291
|
|
|
281
292
|
:param Liquid liquid: The liquid to load into the well.
|
|
282
293
|
:param float volume: The volume of liquid to load, in µL.
|
|
294
|
+
|
|
295
|
+
.. TODO: flag as deprecated in 2.22 docs
|
|
296
|
+
In API version 2.22 and later, use :py:meth:`~Labware.load_liquid`, :py:meth:`~Labware.load_liquid_by_well`,
|
|
297
|
+
or :py:meth:`~Labware.load_empty` to load liquid into a well.
|
|
283
298
|
"""
|
|
284
299
|
self._core.load_liquid(
|
|
285
300
|
liquid=liquid,
|
|
@@ -529,6 +544,7 @@ class Labware:
|
|
|
529
544
|
self,
|
|
530
545
|
name: str,
|
|
531
546
|
label: Optional[str] = None,
|
|
547
|
+
lid: Optional[str] = None,
|
|
532
548
|
namespace: Optional[str] = None,
|
|
533
549
|
version: Optional[int] = None,
|
|
534
550
|
) -> Labware:
|
|
@@ -558,6 +574,20 @@ class Labware:
|
|
|
558
574
|
|
|
559
575
|
self._core_map.add(labware_core, labware)
|
|
560
576
|
|
|
577
|
+
if lid is not None:
|
|
578
|
+
if self._api_version < validation.LID_STACK_VERSION_GATE:
|
|
579
|
+
raise APIVersionError(
|
|
580
|
+
api_element="Loading a Lid on a Labware",
|
|
581
|
+
until_version="2.23",
|
|
582
|
+
current_version=f"{self._api_version}",
|
|
583
|
+
)
|
|
584
|
+
self._protocol_core.load_lid(
|
|
585
|
+
load_name=lid,
|
|
586
|
+
location=labware_core,
|
|
587
|
+
namespace=namespace,
|
|
588
|
+
version=version,
|
|
589
|
+
)
|
|
590
|
+
|
|
561
591
|
return labware
|
|
562
592
|
|
|
563
593
|
@requires_version(2, 15)
|
|
@@ -582,6 +612,65 @@ class Labware:
|
|
|
582
612
|
label=label,
|
|
583
613
|
)
|
|
584
614
|
|
|
615
|
+
@requires_version(2, 23)
|
|
616
|
+
def load_lid_stack(
|
|
617
|
+
self,
|
|
618
|
+
load_name: str,
|
|
619
|
+
quantity: int,
|
|
620
|
+
namespace: Optional[str] = None,
|
|
621
|
+
version: Optional[int] = None,
|
|
622
|
+
) -> Labware:
|
|
623
|
+
"""
|
|
624
|
+
Load a stack of Lids onto a valid Deck Location or Adapter.
|
|
625
|
+
|
|
626
|
+
:param str load_name: A string to use for looking up a lid definition.
|
|
627
|
+
You can find the ``load_name`` for any standard lid on the Opentrons
|
|
628
|
+
`Labware Library <https://labware.opentrons.com>`_.
|
|
629
|
+
:param int quantity: The quantity of lids to be loaded in the stack.
|
|
630
|
+
:param str namespace: The namespace that the lid labware definition belongs to.
|
|
631
|
+
If unspecified, the API will automatically search two namespaces:
|
|
632
|
+
|
|
633
|
+
- ``"opentrons"``, to load standard Opentrons labware definitions.
|
|
634
|
+
- ``"custom_beta"``, to load custom labware definitions created with the
|
|
635
|
+
`Custom Labware Creator <https://labware.opentrons.com/create>`__.
|
|
636
|
+
|
|
637
|
+
You might need to specify an explicit ``namespace`` if you have a custom
|
|
638
|
+
definition whose ``load_name`` is the same as an Opentrons-verified
|
|
639
|
+
definition, and you want to explicitly choose one or the other.
|
|
640
|
+
|
|
641
|
+
:param version: The version of the labware definition. You should normally
|
|
642
|
+
leave this unspecified to let ``load_lid_stack()`` choose a version
|
|
643
|
+
automatically.
|
|
644
|
+
|
|
645
|
+
:return: The initialized and loaded labware object representing the Lid Stack.
|
|
646
|
+
"""
|
|
647
|
+
if self._api_version < validation.LID_STACK_VERSION_GATE:
|
|
648
|
+
raise APIVersionError(
|
|
649
|
+
api_element="Loading a Lid Stack",
|
|
650
|
+
until_version="2.23",
|
|
651
|
+
current_version=f"{self._api_version}",
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
load_location = self._core
|
|
655
|
+
|
|
656
|
+
load_name = validation.ensure_lowercase_name(load_name)
|
|
657
|
+
|
|
658
|
+
result = self._protocol_core.load_lid_stack(
|
|
659
|
+
load_name=load_name,
|
|
660
|
+
location=load_location,
|
|
661
|
+
quantity=quantity,
|
|
662
|
+
namespace=namespace,
|
|
663
|
+
version=version,
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
labware = Labware(
|
|
667
|
+
core=result,
|
|
668
|
+
api_version=self._api_version,
|
|
669
|
+
protocol_core=self._protocol_core,
|
|
670
|
+
core_map=self._core_map,
|
|
671
|
+
)
|
|
672
|
+
return labware
|
|
673
|
+
|
|
585
674
|
def set_calibration(self, delta: Point) -> None:
|
|
586
675
|
"""
|
|
587
676
|
An internal, deprecated method used for updating the labware offset.
|
|
@@ -932,7 +1021,7 @@ class Labware:
|
|
|
932
1021
|
num_tips: int = 1,
|
|
933
1022
|
starting_tip: Optional[Well] = None,
|
|
934
1023
|
*,
|
|
935
|
-
nozzle_map: Optional[
|
|
1024
|
+
nozzle_map: Optional[NozzleMapInterface] = None,
|
|
936
1025
|
) -> Optional[Well]:
|
|
937
1026
|
"""
|
|
938
1027
|
Find the next valid well for pick-up.
|
|
@@ -1105,6 +1194,141 @@ class Labware:
|
|
|
1105
1194
|
"""
|
|
1106
1195
|
self._core.reset_tips()
|
|
1107
1196
|
|
|
1197
|
+
@requires_version(2, 22)
|
|
1198
|
+
def load_liquid(
|
|
1199
|
+
self, wells: Sequence[Union[str, Well]], volume: float, liquid: Liquid
|
|
1200
|
+
) -> None:
|
|
1201
|
+
"""Mark several wells as containing the same amount of liquid.
|
|
1202
|
+
|
|
1203
|
+
This method should be called at the beginning of a protocol, soon after loading the labware and before
|
|
1204
|
+
liquid handling operations begin. It is a base of information for liquid tracking functionality. If a well in a labware
|
|
1205
|
+
has not been named in a call to :py:meth:`~Labware.load_empty`, :py:meth:`~Labware.load_liquid`, or
|
|
1206
|
+
:py:meth:`~Labware.load_liquid_by_well`, the volume it contains is unknown and the well's liquid will not be tracked.
|
|
1207
|
+
|
|
1208
|
+
For example, to load 10µL of a liquid named ``water`` (defined with :py:meth:`~ProtocolContext.define_liquid`)
|
|
1209
|
+
into all the wells of a labware, you could call ``labware.load_liquid(labware.wells(), 10, water)``.
|
|
1210
|
+
|
|
1211
|
+
If you want to load different volumes of liquid into different wells, use :py:meth:`~Labware.load_liquid_by_well`.
|
|
1212
|
+
|
|
1213
|
+
If you want to mark the well as containing no liquid, use :py:meth:`~Labware.load_empty`.
|
|
1214
|
+
|
|
1215
|
+
:param wells: The wells to load the liquid into.
|
|
1216
|
+
:type wells: List of well names or list of Well objects, for instance from :py:meth:`~Labware.wells`.
|
|
1217
|
+
|
|
1218
|
+
:param volume: The volume of liquid to load into each well, in 10µL.
|
|
1219
|
+
:type volume: float
|
|
1220
|
+
|
|
1221
|
+
:param liquid: The liquid to load into each well, previously defined by :py:meth:`~ProtocolContext.define_liquid`
|
|
1222
|
+
:type liquid: Liquid
|
|
1223
|
+
"""
|
|
1224
|
+
well_names: List[str] = []
|
|
1225
|
+
for well in wells:
|
|
1226
|
+
if isinstance(well, str):
|
|
1227
|
+
if well not in self.wells_by_name():
|
|
1228
|
+
raise KeyError(
|
|
1229
|
+
f"{well} is not a well in labware {self.name}. The elements of wells should name wells in this labware."
|
|
1230
|
+
)
|
|
1231
|
+
well_names.append(well)
|
|
1232
|
+
elif isinstance(well, Well):
|
|
1233
|
+
if well.parent is not self:
|
|
1234
|
+
raise KeyError(
|
|
1235
|
+
f"{well.well_name} is not a well in labware {self.name}. The elements of wells should be wells of this labware."
|
|
1236
|
+
)
|
|
1237
|
+
well_names.append(well.well_name)
|
|
1238
|
+
else:
|
|
1239
|
+
raise TypeError(
|
|
1240
|
+
f"Unexpected type for element {repr(well)}. The elements of wells should be Well instances or well names."
|
|
1241
|
+
)
|
|
1242
|
+
if not isinstance(volume, (float, int)):
|
|
1243
|
+
raise TypeError(
|
|
1244
|
+
f"Unexpected type for volume {repr(volume)}. Volume should be a number in microliters."
|
|
1245
|
+
)
|
|
1246
|
+
self._core.load_liquid({well_name: volume for well_name in well_names}, liquid)
|
|
1247
|
+
|
|
1248
|
+
@requires_version(2, 22)
|
|
1249
|
+
def load_liquid_by_well(
|
|
1250
|
+
self, volumes: Mapping[Union[str, Well], float], liquid: Liquid
|
|
1251
|
+
) -> None:
|
|
1252
|
+
"""Mark several wells as containing unique volumes of liquid.
|
|
1253
|
+
|
|
1254
|
+
This method should be called at the beginning of a protocol, soon after loading the labware and before
|
|
1255
|
+
liquid handling operations begin. It is a base of information for liquid tracking functionality. If a well in a labware
|
|
1256
|
+
has not been named in a call to :py:meth:`~Labware.load_empty`, :py:meth:`~Labware.load_liquid`, or
|
|
1257
|
+
:py:meth:`~Labware.load_liquid_by_well`, the volume it contains is unknown and the well's liquid will not be tracked.
|
|
1258
|
+
|
|
1259
|
+
For example, to load a decreasing amount of of a liquid named ``water`` (defined with :py:meth:`~ProtocolContext.define_liquid`)
|
|
1260
|
+
into each successive well of a row, you could call
|
|
1261
|
+
``labware.load_liquid_by_well({'A1': 1000, 'A2': 950, 'A3': 900, ..., 'A12': 600}, water)``
|
|
1262
|
+
|
|
1263
|
+
If you want to load the same volume of a liquid into multiple wells, it is often easier to use :py:meth:`~Labware.load_liquid`.
|
|
1264
|
+
|
|
1265
|
+
If you want to mark the well as containing no liquid, use :py:meth:`~Labware.load_empty`.
|
|
1266
|
+
|
|
1267
|
+
:param volumes: A dictionary of well names (or :py:class:`Well` objects, for instance from ``labware['A1']``)
|
|
1268
|
+
:type wells: Dict[Union[str, Well], float]
|
|
1269
|
+
|
|
1270
|
+
:param liquid: The liquid to load into each well, previously defined by :py:meth:`~ProtocolContext.define_liquid`
|
|
1271
|
+
:type liquid: Liquid
|
|
1272
|
+
"""
|
|
1273
|
+
verified_volumes: Dict[str, float] = {}
|
|
1274
|
+
for well, volume in volumes.items():
|
|
1275
|
+
if isinstance(well, str):
|
|
1276
|
+
if well not in self.wells_by_name():
|
|
1277
|
+
raise KeyError(
|
|
1278
|
+
f"{well} is not a well in {self.name}. The keys of volumes should name wells in this labware"
|
|
1279
|
+
)
|
|
1280
|
+
verified_volumes[well] = volume
|
|
1281
|
+
elif isinstance(well, Well):
|
|
1282
|
+
if well.parent is not self:
|
|
1283
|
+
raise KeyError(
|
|
1284
|
+
f"{well.well_name} is not a well in {self.name}. The keys of volumes should be wells of this labware"
|
|
1285
|
+
)
|
|
1286
|
+
verified_volumes[well.well_name] = volume
|
|
1287
|
+
else:
|
|
1288
|
+
raise TypeError(
|
|
1289
|
+
f"Unexpected type for well name {repr(well)}. The keys of volumes should be Well instances or well names."
|
|
1290
|
+
)
|
|
1291
|
+
if not isinstance(volume, (float, int)):
|
|
1292
|
+
raise TypeError(
|
|
1293
|
+
f"Unexpected type for volume {repr(volume)}. The values of volumes should be numbers in microliters."
|
|
1294
|
+
)
|
|
1295
|
+
self._core.load_liquid(verified_volumes, liquid)
|
|
1296
|
+
|
|
1297
|
+
@requires_version(2, 22)
|
|
1298
|
+
def load_empty(self, wells: Sequence[Union[Well, str]]) -> None:
|
|
1299
|
+
"""Mark several wells as empty.
|
|
1300
|
+
|
|
1301
|
+
This method should be called at the beginning of a protocol, soon after loading the labware and before liquid handling
|
|
1302
|
+
operations begin. It is a base of information for liquid tracking functionality. If a well in a labware has not been named
|
|
1303
|
+
in a call to :py:meth:`Labware.load_empty`, :py:meth:`Labware.load_liquid`, or :py:meth:`Labware.load_liquid_by_well`, the
|
|
1304
|
+
volume it contains is unknown and the well's liquid will not be tracked.
|
|
1305
|
+
|
|
1306
|
+
For instance, to mark all wells in the labware as empty, you can call ``labware.load_empty(labware.wells())``.
|
|
1307
|
+
|
|
1308
|
+
:param wells: The list of wells to mark empty. To mark all wells as empty, pass ``labware.wells()``. You can also specify
|
|
1309
|
+
wells by their names (for instance, ``labware.load_empty(['A1', 'A2'])``).
|
|
1310
|
+
:type wells: Union[List[Well], List[str]]
|
|
1311
|
+
"""
|
|
1312
|
+
well_names: List[str] = []
|
|
1313
|
+
for well in wells:
|
|
1314
|
+
if isinstance(well, str):
|
|
1315
|
+
if well not in self.wells_by_name():
|
|
1316
|
+
raise KeyError(
|
|
1317
|
+
f"{well} is not a well in {self.name}. The elements of wells should name wells in this labware."
|
|
1318
|
+
)
|
|
1319
|
+
well_names.append(well)
|
|
1320
|
+
elif isinstance(well, Well):
|
|
1321
|
+
if well.parent is not self:
|
|
1322
|
+
raise KeyError(
|
|
1323
|
+
f"{well.well_name} is not a well in {self.name}. The elements of wells should be wells of this labware."
|
|
1324
|
+
)
|
|
1325
|
+
well_names.append(well.well_name)
|
|
1326
|
+
else:
|
|
1327
|
+
raise TypeError(
|
|
1328
|
+
f"Unexpected type for well name {repr(well)}. The elements of wells should be Well instances or well names."
|
|
1329
|
+
)
|
|
1330
|
+
self._core.load_empty(well_names)
|
|
1331
|
+
|
|
1108
1332
|
|
|
1109
1333
|
# TODO(mc, 2022-11-09): implementation detail, move to core
|
|
1110
1334
|
def split_tipracks(tip_racks: List[Labware]) -> Tuple[Labware, List[Labware]]:
|
|
@@ -1121,7 +1345,7 @@ def select_tiprack_from_list(
|
|
|
1121
1345
|
num_channels: int,
|
|
1122
1346
|
starting_point: Optional[Well] = None,
|
|
1123
1347
|
*,
|
|
1124
|
-
nozzle_map: Optional[
|
|
1348
|
+
nozzle_map: Optional[NozzleMapInterface] = None,
|
|
1125
1349
|
) -> Tuple[Labware, Well]:
|
|
1126
1350
|
try:
|
|
1127
1351
|
first, rest = split_tipracks(tip_racks)
|
|
@@ -1159,7 +1383,7 @@ def next_available_tip(
|
|
|
1159
1383
|
tip_racks: List[Labware],
|
|
1160
1384
|
channels: int,
|
|
1161
1385
|
*,
|
|
1162
|
-
nozzle_map: Optional[
|
|
1386
|
+
nozzle_map: Optional[NozzleMapInterface] = None,
|
|
1163
1387
|
) -> Tuple[Labware, Well]:
|
|
1164
1388
|
start = starting_tip
|
|
1165
1389
|
if start is None:
|