opentrons 8.2.0a4__py2.py3-none-any.whl → 8.3.0a1__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.
Files changed (229) hide show
  1. opentrons/calibration_storage/deck_configuration.py +3 -3
  2. opentrons/calibration_storage/file_operators.py +3 -3
  3. opentrons/calibration_storage/helpers.py +3 -1
  4. opentrons/calibration_storage/ot2/models/v1.py +16 -29
  5. opentrons/calibration_storage/ot2/tip_length.py +7 -4
  6. opentrons/calibration_storage/ot3/models/v1.py +14 -23
  7. opentrons/cli/analyze.py +18 -6
  8. opentrons/config/defaults_ot3.py +1 -0
  9. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  10. opentrons/drivers/asyncio/communication/errors.py +16 -3
  11. opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
  12. opentrons/drivers/command_builder.py +2 -2
  13. opentrons/drivers/flex_stacker/__init__.py +9 -0
  14. opentrons/drivers/flex_stacker/abstract.py +89 -0
  15. opentrons/drivers/flex_stacker/driver.py +260 -0
  16. opentrons/drivers/flex_stacker/simulator.py +109 -0
  17. opentrons/drivers/flex_stacker/types.py +138 -0
  18. opentrons/drivers/heater_shaker/driver.py +18 -3
  19. opentrons/drivers/temp_deck/driver.py +13 -3
  20. opentrons/drivers/thermocycler/driver.py +17 -3
  21. opentrons/execute.py +3 -1
  22. opentrons/hardware_control/__init__.py +1 -2
  23. opentrons/hardware_control/api.py +28 -20
  24. opentrons/hardware_control/backends/flex_protocol.py +17 -7
  25. opentrons/hardware_control/backends/ot3controller.py +213 -63
  26. opentrons/hardware_control/backends/ot3simulator.py +18 -9
  27. opentrons/hardware_control/backends/ot3utils.py +43 -15
  28. opentrons/hardware_control/dev_types.py +4 -0
  29. opentrons/hardware_control/emulation/heater_shaker.py +4 -0
  30. opentrons/hardware_control/emulation/module_server/client.py +1 -1
  31. opentrons/hardware_control/emulation/module_server/server.py +5 -3
  32. opentrons/hardware_control/emulation/settings.py +3 -4
  33. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
  34. opentrons/hardware_control/instruments/ot2/pipette.py +15 -22
  35. opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
  36. opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
  37. opentrons/hardware_control/instruments/ot3/pipette.py +23 -22
  38. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
  39. opentrons/hardware_control/modules/mod_abc.py +2 -2
  40. opentrons/hardware_control/motion_utilities.py +68 -0
  41. opentrons/hardware_control/nozzle_manager.py +39 -41
  42. opentrons/hardware_control/ot3_calibration.py +1 -1
  43. opentrons/hardware_control/ot3api.py +60 -23
  44. opentrons/hardware_control/protocols/gripper_controller.py +3 -0
  45. opentrons/hardware_control/protocols/hardware_manager.py +5 -1
  46. opentrons/hardware_control/protocols/liquid_handler.py +18 -0
  47. opentrons/hardware_control/protocols/motion_controller.py +6 -0
  48. opentrons/hardware_control/robot_calibration.py +1 -1
  49. opentrons/hardware_control/types.py +61 -0
  50. opentrons/protocol_api/__init__.py +20 -1
  51. opentrons/protocol_api/_liquid.py +24 -49
  52. opentrons/protocol_api/_liquid_properties.py +754 -0
  53. opentrons/protocol_api/_types.py +24 -0
  54. opentrons/protocol_api/core/common.py +2 -0
  55. opentrons/protocol_api/core/engine/instrument.py +82 -10
  56. opentrons/protocol_api/core/engine/labware.py +29 -7
  57. opentrons/protocol_api/core/engine/protocol.py +130 -5
  58. opentrons/protocol_api/core/engine/robot.py +139 -0
  59. opentrons/protocol_api/core/engine/well.py +4 -1
  60. opentrons/protocol_api/core/instrument.py +46 -4
  61. opentrons/protocol_api/core/labware.py +13 -4
  62. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +37 -3
  63. opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
  64. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
  65. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +37 -3
  67. opentrons/protocol_api/core/protocol.py +34 -1
  68. opentrons/protocol_api/core/robot.py +51 -0
  69. opentrons/protocol_api/instrument_context.py +158 -44
  70. opentrons/protocol_api/labware.py +231 -7
  71. opentrons/protocol_api/module_contexts.py +21 -17
  72. opentrons/protocol_api/protocol_context.py +125 -4
  73. opentrons/protocol_api/robot_context.py +204 -32
  74. opentrons/protocol_api/validation.py +262 -3
  75. opentrons/protocol_engine/__init__.py +4 -0
  76. opentrons/protocol_engine/actions/actions.py +2 -3
  77. opentrons/protocol_engine/clients/sync_client.py +18 -0
  78. opentrons/protocol_engine/commands/__init__.py +81 -0
  79. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
  80. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
  81. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
  82. opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
  83. opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
  84. opentrons/protocol_engine/commands/aspirate.py +103 -53
  85. opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
  86. opentrons/protocol_engine/commands/blow_out.py +44 -39
  87. opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
  88. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
  89. opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
  90. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
  91. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
  92. opentrons/protocol_engine/commands/command.py +73 -66
  93. opentrons/protocol_engine/commands/command_unions.py +101 -1
  94. opentrons/protocol_engine/commands/comment.py +1 -1
  95. opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
  96. opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
  97. opentrons/protocol_engine/commands/custom.py +6 -12
  98. opentrons/protocol_engine/commands/dispense.py +82 -48
  99. opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
  100. opentrons/protocol_engine/commands/drop_tip.py +52 -31
  101. opentrons/protocol_engine/commands/drop_tip_in_place.py +13 -3
  102. opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
  103. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  104. opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
  105. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
  106. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
  107. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
  108. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
  109. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
  110. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  111. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
  112. opentrons/protocol_engine/commands/home.py +13 -4
  113. opentrons/protocol_engine/commands/liquid_probe.py +67 -24
  114. opentrons/protocol_engine/commands/load_labware.py +29 -7
  115. opentrons/protocol_engine/commands/load_lid.py +146 -0
  116. opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
  117. opentrons/protocol_engine/commands/load_liquid.py +12 -4
  118. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  119. opentrons/protocol_engine/commands/load_module.py +31 -10
  120. opentrons/protocol_engine/commands/load_pipette.py +19 -8
  121. opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
  122. opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
  123. opentrons/protocol_engine/commands/move_labware.py +19 -6
  124. opentrons/protocol_engine/commands/move_relative.py +35 -25
  125. opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
  126. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
  127. opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
  128. opentrons/protocol_engine/commands/move_to_well.py +40 -24
  129. opentrons/protocol_engine/commands/movement_common.py +338 -0
  130. opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
  131. opentrons/protocol_engine/commands/pipetting_common.py +169 -87
  132. opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
  133. opentrons/protocol_engine/commands/reload_labware.py +1 -1
  134. opentrons/protocol_engine/commands/retract_axis.py +1 -1
  135. opentrons/protocol_engine/commands/robot/__init__.py +69 -0
  136. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
  137. opentrons/protocol_engine/commands/robot/common.py +18 -0
  138. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  139. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  140. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  141. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
  142. opentrons/protocol_engine/commands/save_position.py +14 -5
  143. opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
  144. opentrons/protocol_engine/commands/set_status_bar.py +1 -1
  145. opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
  146. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
  147. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
  148. opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
  149. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
  150. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
  151. opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
  152. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +8 -2
  153. opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
  154. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
  155. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
  156. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
  157. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
  158. opentrons/protocol_engine/commands/touch_tip.py +65 -16
  159. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +4 -1
  160. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
  161. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
  162. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
  163. opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
  164. opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
  165. opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
  166. opentrons/protocol_engine/errors/__init__.py +8 -0
  167. opentrons/protocol_engine/errors/error_occurrence.py +19 -20
  168. opentrons/protocol_engine/errors/exceptions.py +50 -0
  169. opentrons/protocol_engine/execution/command_executor.py +1 -1
  170. opentrons/protocol_engine/execution/equipment.py +73 -5
  171. opentrons/protocol_engine/execution/gantry_mover.py +364 -8
  172. opentrons/protocol_engine/execution/movement.py +27 -0
  173. opentrons/protocol_engine/execution/pipetting.py +5 -1
  174. opentrons/protocol_engine/execution/tip_handler.py +4 -6
  175. opentrons/protocol_engine/notes/notes.py +1 -1
  176. opentrons/protocol_engine/protocol_engine.py +7 -6
  177. opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
  178. opentrons/protocol_engine/resources/labware_validation.py +5 -0
  179. opentrons/protocol_engine/resources/module_data_provider.py +1 -1
  180. opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
  181. opentrons/protocol_engine/slot_standardization.py +9 -9
  182. opentrons/protocol_engine/state/_move_types.py +9 -5
  183. opentrons/protocol_engine/state/_well_math.py +193 -0
  184. opentrons/protocol_engine/state/addressable_areas.py +25 -61
  185. opentrons/protocol_engine/state/command_history.py +12 -0
  186. opentrons/protocol_engine/state/commands.py +17 -13
  187. opentrons/protocol_engine/state/files.py +10 -12
  188. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  189. opentrons/protocol_engine/state/frustum_helpers.py +57 -32
  190. opentrons/protocol_engine/state/geometry.py +47 -1
  191. opentrons/protocol_engine/state/labware.py +79 -25
  192. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  193. opentrons/protocol_engine/state/liquids.py +16 -4
  194. opentrons/protocol_engine/state/modules.py +52 -70
  195. opentrons/protocol_engine/state/motion.py +6 -1
  196. opentrons/protocol_engine/state/pipettes.py +144 -58
  197. opentrons/protocol_engine/state/state.py +21 -2
  198. opentrons/protocol_engine/state/state_summary.py +4 -2
  199. opentrons/protocol_engine/state/tips.py +11 -44
  200. opentrons/protocol_engine/state/update_types.py +343 -48
  201. opentrons/protocol_engine/state/wells.py +19 -11
  202. opentrons/protocol_engine/types.py +176 -28
  203. opentrons/protocol_reader/extract_labware_definitions.py +5 -2
  204. opentrons/protocol_reader/file_format_validator.py +5 -5
  205. opentrons/protocol_runner/json_file_reader.py +9 -3
  206. opentrons/protocol_runner/json_translator.py +51 -25
  207. opentrons/protocol_runner/legacy_command_mapper.py +66 -64
  208. opentrons/protocol_runner/protocol_runner.py +35 -4
  209. opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
  210. opentrons/protocol_runner/run_orchestrator.py +13 -3
  211. opentrons/protocols/advanced_control/common.py +38 -0
  212. opentrons/protocols/advanced_control/mix.py +1 -1
  213. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  214. opentrons/protocols/advanced_control/transfers/common.py +56 -0
  215. opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
  216. opentrons/protocols/api_support/definitions.py +1 -1
  217. opentrons/protocols/api_support/instrument.py +1 -1
  218. opentrons/protocols/api_support/util.py +10 -0
  219. opentrons/protocols/labware.py +39 -6
  220. opentrons/protocols/models/json_protocol.py +5 -9
  221. opentrons/simulate.py +3 -1
  222. opentrons/types.py +162 -2
  223. opentrons/util/logging_config.py +1 -1
  224. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/METADATA +16 -15
  225. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/RECORD +229 -202
  226. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/WHEEL +1 -1
  227. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/LICENSE +0 -0
  228. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/entry_points.txt +0 -0
  229. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/top_level.txt +0 -0
@@ -2,12 +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,
9
+ UnsupportedHardwareCommand,
10
10
  )
11
+ from opentrons_shared_data.robot.types import RobotTypeEnum
12
+
11
13
  from opentrons.legacy_broker import LegacyBroker
12
14
  from opentrons.hardware_control.dev_types import PipetteDict
13
15
  from opentrons import types
@@ -15,8 +17,7 @@ from opentrons.legacy_commands import commands as cmds
15
17
 
16
18
  from opentrons.legacy_commands import publisher
17
19
  from opentrons.protocols.advanced_control.mix import mix_from_kwargs
18
- from opentrons.protocols.advanced_control import transfers
19
-
20
+ from opentrons.protocols.advanced_control.transfers import transfer as v1_transfer
20
21
  from opentrons.protocols.api_support.deck_type import NoTrashDefinedError
21
22
  from opentrons.protocols.api_support.types import APIVersion
22
23
  from opentrons.protocols.api_support import instrument
@@ -28,7 +29,6 @@ from opentrons.protocols.api_support.util import (
28
29
  APIVersionError,
29
30
  UnsupportedAPIError,
30
31
  )
31
- from opentrons.hardware_control.nozzle_manager import NozzleConfigurationType
32
32
 
33
33
  from .core.common import InstrumentCore, ProtocolCore
34
34
  from .core.engine import ENGINE_CORE_API_VERSION
@@ -36,10 +36,13 @@ from .core.legacy.legacy_instrument_core import LegacyInstrumentCore
36
36
  from .config import Clearances
37
37
  from .disposal_locations import TrashBin, WasteChute
38
38
  from ._nozzle_layout import NozzleLayout
39
+ from ._liquid import LiquidClass
39
40
  from . import labware, validation
40
-
41
-
42
- AdvancedLiquidHandling = transfers.AdvancedLiquidHandling
41
+ from ..config import feature_flags
42
+ from ..protocols.advanced_control.transfers.common import (
43
+ TransferTipPolicyV2,
44
+ TransferTipPolicyV2Type,
45
+ )
43
46
 
44
47
  _DEFAULT_ASPIRATE_CLEARANCE = 1.0
45
48
  _DEFAULT_DISPENSE_CLEARANCE = 1.0
@@ -60,6 +63,10 @@ _DISPOSAL_LOCATION_OFFSET_ADDED_IN = APIVersion(2, 18)
60
63
  """The version after which offsets for deck configured trash containers and changes to alternating tip drop behavior were introduced."""
61
64
  _PARTIAL_NOZZLE_CONFIGURATION_SINGLE_ROW_PARTIAL_COLUMN_ADDED_IN = APIVersion(2, 20)
62
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
63
70
 
64
71
 
65
72
  class InstrumentContext(publisher.CommandPublisher):
@@ -257,9 +264,10 @@ class InstrumentContext(publisher.CommandPublisher):
257
264
  self.api_version >= APIVersion(2, 20)
258
265
  and well is not None
259
266
  and self.liquid_presence_detection
260
- and self._96_tip_config_valid()
267
+ and self._core.nozzle_configuration_valid_for_lld()
261
268
  and self._core.get_current_volume() == 0
262
269
  ):
270
+ self._raise_if_pressure_not_supported_by_pipette()
263
271
  self.require_liquid_presence(well=well)
264
272
 
265
273
  with publisher.publish_context(
@@ -513,6 +521,8 @@ class InstrumentContext(publisher.CommandPublisher):
513
521
  ``pipette.mix(1, location=wellplate['A1'])`` is a valid call, but
514
522
  ``pipette.mix(1, wellplate['A1'])`` is not.
515
523
 
524
+ .. versionchanged:: 2.21
525
+ Does not repeatedly check for liquid presence.
516
526
  """
517
527
  _log.debug(
518
528
  "mixing {}uL with {} repetitions in {} at rate={}".format(
@@ -753,7 +763,11 @@ class InstrumentContext(publisher.CommandPublisher):
753
763
  ``pipette.air_gap(height=2)``. If you call ``air_gap`` with a single,
754
764
  unnamed argument, it will always be interpreted as a volume.
755
765
 
756
-
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.
757
771
  """
758
772
  if not self._core.has_tip():
759
773
  raise UnexpectedTipRemovalError("air_gap", self.name, self.mount)
@@ -765,7 +779,12 @@ class InstrumentContext(publisher.CommandPublisher):
765
779
  raise RuntimeError("No previous Well cached to perform air gap")
766
780
  target = loc.labware.as_well().top(height)
767
781
  self.move_to(target, publish=False)
768
- self.aspirate(volume)
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)
769
788
  return self
770
789
 
771
790
  @publisher.publish(command=cmds.return_tip)
@@ -934,7 +953,7 @@ class InstrumentContext(publisher.CommandPublisher):
934
953
  if location is None:
935
954
  if (
936
955
  nozzle_map is not None
937
- and nozzle_map.configuration != NozzleConfigurationType.FULL
956
+ and nozzle_map.configuration != types.NozzleConfigurationType.FULL
938
957
  and self.starting_tip is not None
939
958
  ):
940
959
  # Disallowing this avoids concerning the system with the direction
@@ -1206,7 +1225,6 @@ class InstrumentContext(publisher.CommandPublisher):
1206
1225
  self._core.home_plunger()
1207
1226
  return self
1208
1227
 
1209
- # TODO (spp, 2024-03-08): verify if ok to & change source & dest types to AdvancedLiquidHandling
1210
1228
  @publisher.publish(command=cmds.distribute)
1211
1229
  @requires_version(2, 0)
1212
1230
  def distribute(
@@ -1246,7 +1264,6 @@ class InstrumentContext(publisher.CommandPublisher):
1246
1264
 
1247
1265
  return self.transfer(volume, source, dest, **kwargs)
1248
1266
 
1249
- # TODO (spp, 2024-03-08): verify if ok to & change source & dest types to AdvancedLiquidHandling
1250
1267
  @publisher.publish(command=cmds.consolidate)
1251
1268
  @requires_version(2, 0)
1252
1269
  def consolidate(
@@ -1396,9 +1413,9 @@ class InstrumentContext(publisher.CommandPublisher):
1396
1413
  mix_strategy, mix_opts = mix_from_kwargs(kwargs)
1397
1414
 
1398
1415
  if trash:
1399
- drop_tip = transfers.DropTipStrategy.TRASH
1416
+ drop_tip = v1_transfer.DropTipStrategy.TRASH
1400
1417
  else:
1401
- drop_tip = transfers.DropTipStrategy.RETURN
1418
+ drop_tip = v1_transfer.DropTipStrategy.RETURN
1402
1419
 
1403
1420
  new_tip = kwargs.get("new_tip")
1404
1421
  if isinstance(new_tip, str):
@@ -1420,19 +1437,19 @@ class InstrumentContext(publisher.CommandPublisher):
1420
1437
 
1421
1438
  if blow_out and not blowout_location:
1422
1439
  if self.current_volume:
1423
- blow_out_strategy = transfers.BlowOutStrategy.SOURCE
1440
+ blow_out_strategy = v1_transfer.BlowOutStrategy.SOURCE
1424
1441
  else:
1425
- blow_out_strategy = transfers.BlowOutStrategy.TRASH
1442
+ blow_out_strategy = v1_transfer.BlowOutStrategy.TRASH
1426
1443
  elif blow_out and blowout_location:
1427
1444
  if blowout_location == "source well":
1428
- blow_out_strategy = transfers.BlowOutStrategy.SOURCE
1445
+ blow_out_strategy = v1_transfer.BlowOutStrategy.SOURCE
1429
1446
  elif blowout_location == "destination well":
1430
- blow_out_strategy = transfers.BlowOutStrategy.DEST
1447
+ blow_out_strategy = v1_transfer.BlowOutStrategy.DEST
1431
1448
  elif blowout_location == "trash":
1432
- blow_out_strategy = transfers.BlowOutStrategy.TRASH
1449
+ blow_out_strategy = v1_transfer.BlowOutStrategy.TRASH
1433
1450
 
1434
1451
  if new_tip != types.TransferTipPolicy.NEVER:
1435
- tr, next_tip = labware.next_available_tip(
1452
+ _, next_tip = labware.next_available_tip(
1436
1453
  self.starting_tip,
1437
1454
  self.tip_racks,
1438
1455
  active_channels,
@@ -1444,9 +1461,9 @@ class InstrumentContext(publisher.CommandPublisher):
1444
1461
 
1445
1462
  touch_tip = None
1446
1463
  if kwargs.get("touch_tip"):
1447
- touch_tip = transfers.TouchTipStrategy.ALWAYS
1464
+ touch_tip = v1_transfer.TouchTipStrategy.ALWAYS
1448
1465
 
1449
- default_args = transfers.Transfer()
1466
+ default_args = v1_transfer.Transfer()
1450
1467
 
1451
1468
  disposal = kwargs.get("disposal_volume")
1452
1469
  if disposal is None:
@@ -1459,7 +1476,7 @@ class InstrumentContext(publisher.CommandPublisher):
1459
1476
  f"working volume, {max_volume}uL"
1460
1477
  )
1461
1478
 
1462
- transfer_args = transfers.Transfer(
1479
+ transfer_args = v1_transfer.Transfer(
1463
1480
  new_tip=new_tip or default_args.new_tip,
1464
1481
  air_gap=air_gap,
1465
1482
  carryover=kwargs.get("carryover") or default_args.carryover,
@@ -1472,10 +1489,10 @@ class InstrumentContext(publisher.CommandPublisher):
1472
1489
  blow_out_strategy=blow_out_strategy or default_args.blow_out_strategy,
1473
1490
  touch_tip_strategy=(touch_tip or default_args.touch_tip_strategy),
1474
1491
  )
1475
- transfer_options = transfers.TransferOptions(
1492
+ transfer_options = v1_transfer.TransferOptions(
1476
1493
  transfer=transfer_args, mix=mix_opts
1477
1494
  )
1478
- plan = transfers.TransferPlan(
1495
+ plan = v1_transfer.TransferPlan(
1479
1496
  volume,
1480
1497
  source,
1481
1498
  dest,
@@ -1488,10 +1505,113 @@ class InstrumentContext(publisher.CommandPublisher):
1488
1505
  self._execute_transfer(plan)
1489
1506
  return self
1490
1507
 
1491
- def _execute_transfer(self, plan: transfers.TransferPlan) -> None:
1508
+ def _execute_transfer(self, plan: v1_transfer.TransferPlan) -> None:
1492
1509
  for cmd in plan:
1493
1510
  getattr(self, cmd["method"])(*cmd["args"], **cmd["kwargs"])
1494
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
+
1495
1615
  @requires_version(2, 0)
1496
1616
  def delay(self, *args: Any, **kwargs: Any) -> None:
1497
1617
  """
@@ -1694,6 +1814,8 @@ class InstrumentContext(publisher.CommandPublisher):
1694
1814
  @liquid_presence_detection.setter
1695
1815
  @requires_version(2, 20)
1696
1816
  def liquid_presence_detection(self, enable: bool) -> None:
1817
+ if enable:
1818
+ self._raise_if_pressure_not_supported_by_pipette()
1697
1819
  self._core.set_liquid_presence_detection(enable)
1698
1820
 
1699
1821
  @property
@@ -1870,19 +1992,6 @@ class InstrumentContext(publisher.CommandPublisher):
1870
1992
  else:
1871
1993
  return self._protocol_core.get_last_location()
1872
1994
 
1873
- def _96_tip_config_valid(self) -> bool:
1874
- n_map = self._core.get_nozzle_map()
1875
- channels = self._core.get_active_channels()
1876
- if channels == 96:
1877
- if (
1878
- n_map.back_left != n_map.full_instrument_back_left
1879
- and n_map.front_right != n_map.full_instrument_front_right
1880
- ):
1881
- raise TipNotAttachedError(
1882
- "Either the front right or the back left nozzle must have a tip attached to do LLD."
1883
- )
1884
- return True
1885
-
1886
1995
  def __repr__(self) -> str:
1887
1996
  return "<{}: {} in {}>".format(
1888
1997
  self.__class__.__name__,
@@ -2143,8 +2252,8 @@ class InstrumentContext(publisher.CommandPublisher):
2143
2252
  .. note::
2144
2253
  The pressure sensors for the Flex 8-channel pipette are on channels 1 and 8 (positions A1 and H1). For the Flex 96-channel pipette, the pressure sensors are on channels 1 and 96 (positions A1 and H12). Other channels on multi-channel pipettes do not have sensors and cannot detect liquid.
2145
2254
  """
2255
+ self._raise_if_pressure_not_supported_by_pipette()
2146
2256
  loc = well.top()
2147
- self._96_tip_config_valid()
2148
2257
  return self._core.detect_liquid_presence(well._core, loc)
2149
2258
 
2150
2259
  @requires_version(2, 20)
@@ -2156,8 +2265,8 @@ class InstrumentContext(publisher.CommandPublisher):
2156
2265
  .. note::
2157
2266
  The pressure sensors for the Flex 8-channel pipette are on channels 1 and 8 (positions A1 and H1). For the Flex 96-channel pipette, the pressure sensors are on channels 1 and 96 (positions A1 and H12). Other channels on multi-channel pipettes do not have sensors and cannot detect liquid.
2158
2267
  """
2268
+ self._raise_if_pressure_not_supported_by_pipette()
2159
2269
  loc = well.top()
2160
- self._96_tip_config_valid()
2161
2270
  self._core.liquid_probe_with_recovery(well._core, loc)
2162
2271
 
2163
2272
  @requires_version(2, 20)
@@ -2170,9 +2279,8 @@ class InstrumentContext(publisher.CommandPublisher):
2170
2279
 
2171
2280
  This is intended for Opentrons internal use only and is not a guaranteed API.
2172
2281
  """
2173
-
2282
+ self._raise_if_pressure_not_supported_by_pipette()
2174
2283
  loc = well.top()
2175
- self._96_tip_config_valid()
2176
2284
  height = self._core.liquid_probe_without_recovery(well._core, loc)
2177
2285
  return height
2178
2286
 
@@ -2192,6 +2300,12 @@ class InstrumentContext(publisher.CommandPublisher):
2192
2300
  )
2193
2301
  # SINGLE, QUADRANT and ALL are supported by all pipettes
2194
2302
 
2303
+ def _raise_if_pressure_not_supported_by_pipette(self) -> None:
2304
+ if not self._core._pressure_supported_by_pipette():
2305
+ raise UnsupportedHardwareCommand(
2306
+ "Pressure sensor not available for this pipette"
2307
+ )
2308
+
2195
2309
  def _handle_aspirate_target(
2196
2310
  self, target: validation.ValidTarget
2197
2311
  ) -> tuple[types.Location, Optional[labware.Well], Optional[bool]]: