opentrons 8.3.2a0__py2.py3-none-any.whl → 8.4.0__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.

Potentially problematic release.


This version of opentrons might be problematic. Click here for more details.

Files changed (196) hide show
  1. opentrons/calibration_storage/ot2/mark_bad_calibration.py +2 -0
  2. opentrons/calibration_storage/ot2/tip_length.py +6 -6
  3. opentrons/config/advanced_settings.py +9 -11
  4. opentrons/config/feature_flags.py +0 -4
  5. opentrons/config/reset.py +7 -2
  6. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  7. opentrons/drivers/asyncio/communication/async_serial.py +4 -0
  8. opentrons/drivers/asyncio/communication/errors.py +41 -8
  9. opentrons/drivers/asyncio/communication/serial_connection.py +36 -10
  10. opentrons/drivers/flex_stacker/__init__.py +9 -3
  11. opentrons/drivers/flex_stacker/abstract.py +140 -15
  12. opentrons/drivers/flex_stacker/driver.py +593 -47
  13. opentrons/drivers/flex_stacker/errors.py +64 -0
  14. opentrons/drivers/flex_stacker/simulator.py +222 -24
  15. opentrons/drivers/flex_stacker/types.py +211 -15
  16. opentrons/drivers/flex_stacker/utils.py +19 -0
  17. opentrons/execute.py +4 -2
  18. opentrons/hardware_control/api.py +5 -0
  19. opentrons/hardware_control/backends/flex_protocol.py +4 -0
  20. opentrons/hardware_control/backends/ot3controller.py +12 -1
  21. opentrons/hardware_control/backends/ot3simulator.py +3 -0
  22. opentrons/hardware_control/backends/subsystem_manager.py +8 -4
  23. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +10 -6
  24. opentrons/hardware_control/instruments/ot3/pipette_handler.py +59 -6
  25. opentrons/hardware_control/modules/__init__.py +12 -1
  26. opentrons/hardware_control/modules/absorbance_reader.py +11 -9
  27. opentrons/hardware_control/modules/flex_stacker.py +498 -0
  28. opentrons/hardware_control/modules/heater_shaker.py +12 -10
  29. opentrons/hardware_control/modules/magdeck.py +5 -1
  30. opentrons/hardware_control/modules/tempdeck.py +5 -1
  31. opentrons/hardware_control/modules/thermocycler.py +15 -14
  32. opentrons/hardware_control/modules/types.py +191 -1
  33. opentrons/hardware_control/modules/utils.py +3 -0
  34. opentrons/hardware_control/motion_utilities.py +20 -0
  35. opentrons/hardware_control/ot3api.py +145 -15
  36. opentrons/hardware_control/protocols/liquid_handler.py +47 -1
  37. opentrons/hardware_control/types.py +6 -0
  38. opentrons/legacy_commands/commands.py +102 -5
  39. opentrons/legacy_commands/helpers.py +74 -1
  40. opentrons/legacy_commands/types.py +33 -2
  41. opentrons/protocol_api/__init__.py +2 -0
  42. opentrons/protocol_api/_liquid.py +39 -8
  43. opentrons/protocol_api/_liquid_properties.py +20 -19
  44. opentrons/protocol_api/_transfer_liquid_validation.py +91 -0
  45. opentrons/protocol_api/core/common.py +3 -1
  46. opentrons/protocol_api/core/engine/deck_conflict.py +11 -1
  47. opentrons/protocol_api/core/engine/instrument.py +1356 -107
  48. opentrons/protocol_api/core/engine/labware.py +8 -4
  49. opentrons/protocol_api/core/engine/load_labware_params.py +68 -10
  50. opentrons/protocol_api/core/engine/module_core.py +118 -2
  51. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +6 -14
  52. opentrons/protocol_api/core/engine/protocol.py +253 -11
  53. opentrons/protocol_api/core/engine/stringify.py +19 -8
  54. opentrons/protocol_api/core/engine/transfer_components_executor.py +858 -0
  55. opentrons/protocol_api/core/engine/well.py +73 -5
  56. opentrons/protocol_api/core/instrument.py +71 -21
  57. opentrons/protocol_api/core/labware.py +6 -2
  58. opentrons/protocol_api/core/legacy/labware_offset_provider.py +7 -3
  59. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +76 -49
  60. opentrons/protocol_api/core/legacy/legacy_labware_core.py +8 -4
  61. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +36 -0
  62. opentrons/protocol_api/core/legacy/legacy_well_core.py +27 -2
  63. opentrons/protocol_api/core/legacy/load_info.py +4 -12
  64. opentrons/protocol_api/core/legacy/module_geometry.py +6 -1
  65. opentrons/protocol_api/core/legacy/well_geometry.py +3 -3
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +73 -23
  67. opentrons/protocol_api/core/module.py +43 -0
  68. opentrons/protocol_api/core/protocol.py +33 -0
  69. opentrons/protocol_api/core/well.py +23 -2
  70. opentrons/protocol_api/instrument_context.py +454 -150
  71. opentrons/protocol_api/labware.py +98 -50
  72. opentrons/protocol_api/module_contexts.py +140 -0
  73. opentrons/protocol_api/protocol_context.py +163 -19
  74. opentrons/protocol_api/validation.py +51 -41
  75. opentrons/protocol_engine/__init__.py +21 -2
  76. opentrons/protocol_engine/actions/actions.py +5 -5
  77. opentrons/protocol_engine/clients/sync_client.py +6 -0
  78. opentrons/protocol_engine/commands/__init__.py +66 -36
  79. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +0 -1
  80. opentrons/protocol_engine/commands/air_gap_in_place.py +3 -2
  81. opentrons/protocol_engine/commands/aspirate.py +6 -2
  82. opentrons/protocol_engine/commands/aspirate_in_place.py +3 -1
  83. opentrons/protocol_engine/commands/aspirate_while_tracking.py +210 -0
  84. opentrons/protocol_engine/commands/blow_out.py +2 -0
  85. opentrons/protocol_engine/commands/blow_out_in_place.py +4 -1
  86. opentrons/protocol_engine/commands/command_unions.py +102 -33
  87. opentrons/protocol_engine/commands/configure_for_volume.py +3 -0
  88. opentrons/protocol_engine/commands/dispense.py +3 -1
  89. opentrons/protocol_engine/commands/dispense_in_place.py +3 -0
  90. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  91. opentrons/protocol_engine/commands/drop_tip.py +23 -1
  92. opentrons/protocol_engine/commands/flex_stacker/__init__.py +106 -0
  93. opentrons/protocol_engine/commands/flex_stacker/close_latch.py +72 -0
  94. opentrons/protocol_engine/commands/flex_stacker/common.py +15 -0
  95. opentrons/protocol_engine/commands/flex_stacker/empty.py +161 -0
  96. opentrons/protocol_engine/commands/flex_stacker/fill.py +164 -0
  97. opentrons/protocol_engine/commands/flex_stacker/open_latch.py +70 -0
  98. opentrons/protocol_engine/commands/flex_stacker/prepare_shuttle.py +112 -0
  99. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +394 -0
  100. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +190 -0
  101. opentrons/protocol_engine/commands/flex_stacker/store.py +291 -0
  102. opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
  103. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  104. opentrons/protocol_engine/commands/liquid_probe.py +27 -13
  105. opentrons/protocol_engine/commands/load_labware.py +42 -39
  106. opentrons/protocol_engine/commands/load_lid.py +21 -13
  107. opentrons/protocol_engine/commands/load_lid_stack.py +130 -47
  108. opentrons/protocol_engine/commands/load_module.py +18 -17
  109. opentrons/protocol_engine/commands/load_pipette.py +3 -0
  110. opentrons/protocol_engine/commands/move_labware.py +139 -20
  111. opentrons/protocol_engine/commands/move_to_well.py +5 -11
  112. opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
  113. opentrons/protocol_engine/commands/pipetting_common.py +159 -8
  114. opentrons/protocol_engine/commands/prepare_to_aspirate.py +15 -5
  115. opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +33 -34
  116. opentrons/protocol_engine/commands/reload_labware.py +6 -19
  117. opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +97 -76
  118. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +3 -1
  119. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +6 -1
  120. opentrons/protocol_engine/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +31 -40
  121. opentrons/protocol_engine/errors/__init__.py +10 -0
  122. opentrons/protocol_engine/errors/exceptions.py +62 -0
  123. opentrons/protocol_engine/execution/equipment.py +123 -106
  124. opentrons/protocol_engine/execution/labware_movement.py +8 -6
  125. opentrons/protocol_engine/execution/pipetting.py +235 -25
  126. opentrons/protocol_engine/execution/tip_handler.py +82 -32
  127. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  128. opentrons/protocol_engine/protocol_engine.py +22 -13
  129. opentrons/protocol_engine/resources/deck_configuration_provider.py +98 -2
  130. opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
  131. opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
  132. opentrons/protocol_engine/resources/labware_validation.py +7 -5
  133. opentrons/protocol_engine/slot_standardization.py +11 -23
  134. opentrons/protocol_engine/state/addressable_areas.py +84 -46
  135. opentrons/protocol_engine/state/frustum_helpers.py +36 -14
  136. opentrons/protocol_engine/state/geometry.py +892 -227
  137. opentrons/protocol_engine/state/labware.py +252 -55
  138. opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
  139. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
  140. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
  141. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
  142. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
  143. opentrons/protocol_engine/state/modules.py +210 -67
  144. opentrons/protocol_engine/state/pipettes.py +54 -0
  145. opentrons/protocol_engine/state/state.py +1 -1
  146. opentrons/protocol_engine/state/tips.py +14 -0
  147. opentrons/protocol_engine/state/update_types.py +180 -25
  148. opentrons/protocol_engine/state/wells.py +55 -9
  149. opentrons/protocol_engine/types/__init__.py +300 -0
  150. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  151. opentrons/protocol_engine/types/command_annotations.py +53 -0
  152. opentrons/protocol_engine/types/deck_configuration.py +72 -0
  153. opentrons/protocol_engine/types/execution.py +96 -0
  154. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  155. opentrons/protocol_engine/types/instrument.py +47 -0
  156. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  157. opentrons/protocol_engine/types/labware.py +111 -0
  158. opentrons/protocol_engine/types/labware_movement.py +22 -0
  159. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  160. opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
  161. opentrons/protocol_engine/types/liquid.py +40 -0
  162. opentrons/protocol_engine/types/liquid_class.py +59 -0
  163. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  164. opentrons/protocol_engine/types/liquid_level_detection.py +131 -0
  165. opentrons/protocol_engine/types/location.py +194 -0
  166. opentrons/protocol_engine/types/module.py +301 -0
  167. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  168. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  169. opentrons/protocol_engine/types/tip.py +18 -0
  170. opentrons/protocol_engine/types/util.py +21 -0
  171. opentrons/protocol_engine/types/well_position.py +124 -0
  172. opentrons/protocol_reader/extract_labware_definitions.py +7 -3
  173. opentrons/protocol_reader/file_format_validator.py +5 -3
  174. opentrons/protocol_runner/json_translator.py +4 -2
  175. opentrons/protocol_runner/legacy_command_mapper.py +6 -2
  176. opentrons/protocol_runner/run_orchestrator.py +4 -1
  177. opentrons/protocols/advanced_control/transfers/common.py +48 -1
  178. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
  179. opentrons/protocols/api_support/definitions.py +1 -1
  180. opentrons/protocols/api_support/instrument.py +16 -3
  181. opentrons/protocols/labware.py +27 -23
  182. opentrons/protocols/models/__init__.py +0 -21
  183. opentrons/simulate.py +4 -2
  184. opentrons/types.py +20 -7
  185. opentrons/util/logging_config.py +94 -25
  186. opentrons/util/logging_queue_handler.py +61 -0
  187. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/METADATA +4 -4
  188. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/RECORD +192 -151
  189. opentrons/calibration_storage/ot2/models/defaults.py +0 -0
  190. opentrons/calibration_storage/ot3/models/defaults.py +0 -0
  191. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  192. opentrons/protocol_engine/types.py +0 -1311
  193. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/LICENSE +0 -0
  194. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/WHEEL +0 -0
  195. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/entry_points.txt +0 -0
  196. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,6 @@ from opentrons_shared_data.errors.exceptions import (
8
8
  UnexpectedTipRemovalError,
9
9
  UnsupportedHardwareCommand,
10
10
  )
11
- from opentrons_shared_data.robot.types import RobotTypeEnum
12
11
 
13
12
  from opentrons.legacy_broker import LegacyBroker
14
13
  from opentrons.hardware_control.dev_types import PipetteDict
@@ -37,12 +36,13 @@ from .config import Clearances
37
36
  from .disposal_locations import TrashBin, WasteChute
38
37
  from ._nozzle_layout import NozzleLayout
39
38
  from ._liquid import LiquidClass
39
+ from ._transfer_liquid_validation import verify_and_normalize_transfer_args
40
40
  from . import labware, validation
41
- from ..config import feature_flags
42
41
  from ..protocols.advanced_control.transfers.common import (
43
42
  TransferTipPolicyV2,
44
43
  TransferTipPolicyV2Type,
45
44
  )
45
+ from ..protocol_engine.types import LiquidTrackingType
46
46
 
47
47
  _DEFAULT_ASPIRATE_CLEARANCE = 1.0
48
48
  _DEFAULT_DISPENSE_CLEARANCE = 1.0
@@ -66,6 +66,7 @@ _PARTIAL_NOZZLE_CONFIGURATION_SINGLE_ROW_PARTIAL_COLUMN_ADDED_IN = APIVersion(2,
66
66
  _AIR_GAP_TRACKING_ADDED_IN = APIVersion(2, 22)
67
67
  """The version after which air gaps should be implemented with a separate call instead of an aspirate for better liquid volume tracking."""
68
68
 
69
+
69
70
  AdvancedLiquidHandling = v1_transfer.AdvancedLiquidHandling
70
71
 
71
72
 
@@ -166,6 +167,11 @@ class InstrumentContext(publisher.CommandPublisher):
166
167
  def default_speed(self, speed: float) -> None:
167
168
  self._core.set_default_speed(speed)
168
169
 
170
+ @requires_version(2, 21)
171
+ def get_minimum_liquid_sense_height(self) -> float:
172
+ """Get the minimum allowed height for liquid-level detection."""
173
+ return self._core.get_minimum_liquid_sense_height()
174
+
169
175
  @requires_version(2, 0)
170
176
  def aspirate(
171
177
  self,
@@ -226,7 +232,6 @@ class InstrumentContext(publisher.CommandPublisher):
226
232
 
227
233
  move_to_location: types.Location
228
234
  well: Optional[labware.Well] = None
229
- is_meniscus: Optional[bool] = None
230
235
  last_location = self._get_last_location_by_api_version()
231
236
  try:
232
237
  target = validation.validate_location(
@@ -244,7 +249,7 @@ class InstrumentContext(publisher.CommandPublisher):
244
249
  raise ValueError(
245
250
  "Trash Bin and Waste Chute are not acceptable location parameters for Aspirate commands."
246
251
  )
247
- move_to_location, well, is_meniscus = self._handle_aspirate_target(
252
+ move_to_location, well, meniscus_tracking = self._handle_aspirate_target(
248
253
  target=target
249
254
  )
250
255
  if self.api_version >= APIVersion(2, 11):
@@ -266,6 +271,7 @@ class InstrumentContext(publisher.CommandPublisher):
266
271
  and self.liquid_presence_detection
267
272
  and self._core.nozzle_configuration_valid_for_lld()
268
273
  and self._core.get_current_volume() == 0
274
+ and self._core.get_has_clean_tip()
269
275
  ):
270
276
  self._raise_if_pressure_not_supported_by_pipette()
271
277
  self.require_liquid_presence(well=well)
@@ -287,13 +293,13 @@ class InstrumentContext(publisher.CommandPublisher):
287
293
  rate=rate,
288
294
  flow_rate=flow_rate,
289
295
  in_place=target.in_place,
290
- is_meniscus=is_meniscus,
296
+ meniscus_tracking=meniscus_tracking,
291
297
  )
292
298
 
293
299
  return self
294
300
 
295
301
  @requires_version(2, 0)
296
- def dispense( # noqa: C901
302
+ def dispense(
297
303
  self,
298
304
  volume: Optional[float] = None,
299
305
  location: Optional[
@@ -360,7 +366,10 @@ class InstrumentContext(publisher.CommandPublisher):
360
366
  :param push_out: Continue past the plunger bottom to help ensure all liquid
361
367
  leaves the tip. Measured in µL. The default value is ``None``.
362
368
 
363
- See :ref:`push-out-dispense` for details.
369
+ When not specified or set to ``None``, the plunger moves by a non-zero default amount.
370
+
371
+
372
+ For a table of default values, see :ref:`push-out-dispense`.
364
373
  :type push_out: float
365
374
 
366
375
  :returns: This instance.
@@ -389,8 +398,6 @@ class InstrumentContext(publisher.CommandPublisher):
389
398
  volume, location if location else "current position", rate
390
399
  )
391
400
  )
392
- well: Optional[labware.Well] = None
393
- is_meniscus: Optional[bool] = None
394
401
  last_location = self._get_last_location_by_api_version()
395
402
 
396
403
  try:
@@ -405,29 +412,6 @@ class InstrumentContext(publisher.CommandPublisher):
405
412
  "knows where it is."
406
413
  ) from e
407
414
 
408
- if isinstance(target, validation.WellTarget):
409
- well = target.well
410
- if target.location:
411
- move_to_location = target.location
412
- is_meniscus = target.location.is_meniscus
413
- elif well.parent._core.is_fixed_trash():
414
- move_to_location = target.well.top()
415
- else:
416
- move_to_location = target.well.bottom(
417
- z=self._well_bottom_clearances.dispense
418
- )
419
- if isinstance(target, validation.PointTarget):
420
- move_to_location = target.location
421
-
422
- if self.api_version >= APIVersion(2, 11) and not isinstance(
423
- target, (TrashBin, WasteChute)
424
- ):
425
- instrument.validate_takes_liquid(
426
- location=move_to_location,
427
- reject_module=self.api_version >= APIVersion(2, 13),
428
- reject_adapter=self.api_version >= APIVersion(2, 15),
429
- )
430
-
431
415
  if self.api_version >= APIVersion(2, 16):
432
416
  c_vol = self._core.get_current_volume() if volume is None else volume
433
417
  else:
@@ -454,9 +438,21 @@ class InstrumentContext(publisher.CommandPublisher):
454
438
  flow_rate=flow_rate,
455
439
  in_place=False,
456
440
  push_out=push_out,
441
+ meniscus_tracking=None,
457
442
  )
458
443
  return self
459
444
 
445
+ move_to_location, well, meniscus_tracking = self._handle_dispense_target(
446
+ target=target
447
+ )
448
+
449
+ if self.api_version >= APIVersion(2, 11):
450
+ instrument.validate_takes_liquid(
451
+ location=move_to_location,
452
+ reject_module=self.api_version >= APIVersion(2, 13),
453
+ reject_adapter=self.api_version >= APIVersion(2, 15),
454
+ )
455
+
460
456
  with publisher.publish_context(
461
457
  broker=self.broker,
462
458
  command=cmds.dispense(
@@ -475,7 +471,7 @@ class InstrumentContext(publisher.CommandPublisher):
475
471
  flow_rate=flow_rate,
476
472
  in_place=target.in_place,
477
473
  push_out=push_out,
478
- is_meniscus=is_meniscus,
474
+ meniscus_tracking=meniscus_tracking,
479
475
  )
480
476
 
481
477
  return self
@@ -610,7 +606,20 @@ class InstrumentContext(publisher.CommandPublisher):
610
606
  "Blow_out being performed on a tiprack. "
611
607
  "Please re-check your code"
612
608
  )
613
- move_to_location = target.location or target.well.top()
609
+ if target.location:
610
+ # because the lower levels of blowout don't handle LiquidHandlingWellLocation and
611
+ # there is no "operation_volume" for blowout we need to convert the relative location
612
+ # given with a .meniscus to an absolute point. To maintain the meniscus behavior
613
+ # we can just add the offset to the current liquid height.
614
+ if target.location.meniscus_tracking:
615
+ move_to_location = target.well.bottom(
616
+ target.well.current_liquid_height() # type: ignore [arg-type]
617
+ + target.location.point.z
618
+ )
619
+ else:
620
+ move_to_location = target.location
621
+ else:
622
+ move_to_location = target.well.top()
614
623
  well = target.well
615
624
  elif isinstance(target, validation.PointTarget):
616
625
  move_to_location = target.location
@@ -756,18 +765,20 @@ class InstrumentContext(publisher.CommandPublisher):
756
765
 
757
766
  :returns: This instance.
758
767
 
759
- .. note::
768
+ Both ``volume`` and ``height`` are optional, but if you want to specify only
769
+ ``height`` you must do it as a keyword argument:
770
+ ``pipette.air_gap(height=2)``. If you call ``air_gap`` with a single,
771
+ unnamed argument, it will always be interpreted as a volume.
760
772
 
761
- Both ``volume`` and ``height`` are optional, but if you want to specify only
762
- ``height`` you must do it as a keyword argument:
763
- ``pipette.air_gap(height=2)``. If you call ``air_gap`` with a single,
764
- unnamed argument, it will always be interpreted as a volume.
773
+ .. note::
765
774
 
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
775
+ In API version 2.21 and earlier, this function was implemented as an aspirate, and
768
776
  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
777
+ the well. In API version 2.22 and later, air gap volume is not tracked as liquid
770
778
  when dispensing into a well.
779
+
780
+ .. versionchanged:: 2.22
781
+ No longer implemented as an aspirate.
771
782
  """
772
783
  if not self._core.has_tip():
773
784
  raise UnexpectedTipRemovalError("air_gap", self.name, self.mount)
@@ -780,6 +791,7 @@ class InstrumentContext(publisher.CommandPublisher):
780
791
  target = loc.labware.as_well().top(height)
781
792
  self.move_to(target, publish=False)
782
793
  if self.api_version >= _AIR_GAP_TRACKING_ADDED_IN:
794
+ self._core.prepare_to_aspirate()
783
795
  c_vol = self._core.get_available_volume() if volume is None else volume
784
796
  flow_rate = self._core.get_aspirate_flow_rate()
785
797
  self._core.air_gap_in_place(c_vol, flow_rate)
@@ -1509,7 +1521,8 @@ class InstrumentContext(publisher.CommandPublisher):
1509
1521
  for cmd in plan:
1510
1522
  getattr(self, cmd["method"])(*cmd["args"], **cmd["kwargs"])
1511
1523
 
1512
- def transfer_liquid(
1524
+ @requires_version(2, 23)
1525
+ def transfer_with_liquid_class(
1513
1526
  self,
1514
1527
  liquid_class: LiquidClass,
1515
1528
  volume: float,
@@ -1520,96 +1533,335 @@ class InstrumentContext(publisher.CommandPublisher):
1520
1533
  labware.Well, Sequence[labware.Well], Sequence[Sequence[labware.Well]]
1521
1534
  ],
1522
1535
  new_tip: TransferTipPolicyV2Type = "once",
1523
- tip_drop_location: Optional[
1536
+ trash_location: Optional[
1524
1537
  Union[types.Location, labware.Well, TrashBin, WasteChute]
1525
- ] = None, # Maybe call this 'tip_drop_location' which is similar to PD
1538
+ ] = None,
1539
+ return_tip: bool = False,
1540
+ visit_every_well: bool = False,
1526
1541
  ) -> InstrumentContext:
1527
- """Transfer liquid from source to dest using the specified liquid class properties.
1542
+ """Move a particular type of liquid from one well or group of wells to another.
1543
+
1544
+ ..
1545
+ This is intended for Opentrons internal use only and is not a guaranteed API.
1546
+
1547
+ :param liquid_class: The type of liquid to move. You must specify the liquid class,
1548
+ even if you have used :py:meth:`.Labware.load_liquid` to indicate what liquid the
1549
+ source contains.
1550
+ :type liquid_class: :py:class:`.LiquidClass`
1528
1551
 
1529
- TODO: Add args description.
1552
+ :param volume: The amount, in µL, to aspirate from each source and dispense to
1553
+ each destination.
1554
+ :param source: A single well or a list of wells to aspirate liquid from.
1555
+ :param dest: A single well or a list of wells to dispense liquid into.
1556
+ :param new_tip: When to pick up and drop tips during the command.
1557
+ Defaults to ``"once"``.
1558
+
1559
+ - ``"once"``: Use one tip for the entire command.
1560
+ - ``"always"``: Use a new tip for each set of aspirate and dispense steps.
1561
+ - ``"per source"``: Use one tip for each source well, even if
1562
+ :ref:`tip refilling <complex-tip-refilling>` is required.
1563
+ - ``"never"``: Do not pick up or drop tips at all.
1564
+
1565
+ See :ref:`param-tip-handling` for details.
1566
+
1567
+ :param trash_location: A trash container, well, or other location to dispose of
1568
+ tips. Depending on the liquid class, the pipette may also blow out liquid here.
1569
+ :param return_tip: Whether to drop used tips in their original locations
1570
+ in the tip rack, instead of the trash.
1571
+
1572
+ :meta private:
1530
1573
  """
1531
- if not feature_flags.allow_liquid_classes(
1532
- robot_type=RobotTypeEnum.robot_literal_to_enum(
1533
- self._protocol_core.robot_type
1574
+ if volume == 0.0:
1575
+ _log.info(
1576
+ f"Transfer of {liquid_class.name} specified with a volume of 0uL."
1577
+ f" Skipping."
1534
1578
  )
1535
- ):
1536
- raise NotImplementedError("This method is not implemented.")
1579
+ return self
1537
1580
 
1538
- flat_sources_list = validation.ensure_valid_flat_wells_list_for_transfer_v2(
1539
- source
1581
+ transfer_args = verify_and_normalize_transfer_args(
1582
+ source=source,
1583
+ dest=dest,
1584
+ tip_policy=new_tip,
1585
+ last_tip_picked_up_from=self._last_tip_picked_up_from,
1586
+ tip_racks=self._tip_racks,
1587
+ nozzle_map=self._core.get_nozzle_map(),
1588
+ target_all_wells=visit_every_well,
1589
+ current_volume=self.current_volume,
1590
+ trash_location=(
1591
+ trash_location if trash_location is not None else self.trash_container
1592
+ ),
1540
1593
  )
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):
1594
+ if len(transfer_args.sources_list) != len(transfer_args.destinations_list):
1549
1595
  raise ValueError(
1550
1596
  "Sources and destinations should be of the same length in order to perform a transfer."
1551
1597
  " To transfer liquid from one source to many destinations, use 'distribute_liquid',"
1552
1598
  " to transfer liquid onto one destinations from many sources, use 'consolidate_liquid'."
1553
1599
  )
1554
1600
 
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."
1601
+ with publisher.publish_context(
1602
+ broker=self.broker,
1603
+ command=cmds.transfer_with_liquid_class(
1604
+ instrument=self,
1605
+ liquid_class=liquid_class,
1606
+ volume=volume,
1607
+ source=source,
1608
+ destination=dest,
1609
+ ),
1610
+ ):
1611
+ self._core.transfer_with_liquid_class(
1612
+ liquid_class=liquid_class,
1613
+ volume=volume,
1614
+ source=[
1615
+ (types.Location(types.Point(), labware=well), well._core)
1616
+ for well in transfer_args.sources_list
1617
+ ],
1618
+ dest=[
1619
+ (types.Location(types.Point(), labware=well), well._core)
1620
+ for well in transfer_args.destinations_list
1621
+ ],
1622
+ new_tip=transfer_args.tip_policy,
1623
+ tip_racks=[
1624
+ (types.Location(types.Point(), labware=rack), rack._core)
1625
+ for rack in transfer_args.tip_racks
1626
+ ],
1627
+ starting_tip=(
1628
+ self.starting_tip._core if self.starting_tip is not None else None
1629
+ ),
1630
+ trash_location=transfer_args.trash_location,
1631
+ return_tip=return_tip,
1577
1632
  )
1633
+ return self
1578
1634
 
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
1635
+ @requires_version(2, 23)
1636
+ def distribute_with_liquid_class(
1637
+ self,
1638
+ liquid_class: LiquidClass,
1639
+ volume: float,
1640
+ source: Union[labware.Well, Sequence[labware.Well]],
1641
+ dest: Union[
1642
+ labware.Well, Sequence[labware.Well], Sequence[Sequence[labware.Well]]
1643
+ ],
1644
+ new_tip: TransferTipPolicyV2Type = "once",
1645
+ trash_location: Optional[
1646
+ Union[types.Location, labware.Well, TrashBin, WasteChute]
1647
+ ] = None,
1648
+ return_tip: bool = False,
1649
+ visit_every_well: bool = False,
1650
+ ) -> InstrumentContext:
1651
+ """
1652
+ Distribute a particular type of liquid from one well to a group of wells.
1653
+
1654
+ ..
1655
+ This is intended for Opentrons internal use only and is not a guaranteed API.
1656
+
1657
+ :param liquid_class: The type of liquid to move. You must specify the liquid class,
1658
+ even if you have used :py:meth:`.Labware.load_liquid` to indicate what liquid the
1659
+ source contains.
1660
+ :type liquid_class: :py:class:`.LiquidClass`
1661
+
1662
+ :param volume: The amount, in µL, to aspirate from the source and dispense to
1663
+ each destination.
1664
+ :param source: A single well to aspirate liquid from.
1665
+ :param dest: A list of wells to dispense liquid into.
1666
+ :param new_tip: When to pick up and drop tips during the command.
1667
+ Defaults to ``"once"``.
1668
+
1669
+ - ``"once"``: Use one tip for the entire command.
1670
+ - ``"never"``: Do not pick up or drop tips at all.
1588
1671
 
1589
- checked_trash_location = (
1590
- validation.ensure_valid_tip_drop_location_for_transfer_v2(
1591
- tip_drop_location=_trash_location
1672
+ See :ref:`param-tip-handling` for details.
1673
+
1674
+ :param trash_location: A trash container, well, or other location to dispose of
1675
+ tips. Depending on the liquid class, the pipette may also blow out liquid here.
1676
+ :param return_tip: Whether to drop used tips in their original locations
1677
+ in the tip rack, instead of the trash.
1678
+
1679
+ :meta private:
1680
+ """
1681
+ if volume == 0.0:
1682
+ _log.info(
1683
+ f"Distribution of {liquid_class.name} specified with a volume of 0uL."
1684
+ f" Skipping."
1592
1685
  )
1686
+ return self
1687
+
1688
+ transfer_args = verify_and_normalize_transfer_args(
1689
+ source=source,
1690
+ dest=dest,
1691
+ tip_policy=new_tip,
1692
+ last_tip_picked_up_from=self._last_tip_picked_up_from,
1693
+ tip_racks=self._tip_racks,
1694
+ nozzle_map=self._core.get_nozzle_map(),
1695
+ target_all_wells=visit_every_well,
1696
+ current_volume=self.current_volume,
1697
+ trash_location=(
1698
+ trash_location if trash_location is not None else self.trash_container
1699
+ ),
1593
1700
  )
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
- )
1701
+ if len(transfer_args.sources_list) != 1:
1702
+ raise ValueError(
1703
+ f"Source should be a single well (or resolve to a single transfer for multi-channel) "
1704
+ f"but received {transfer_args.sources_list}."
1705
+ )
1706
+ if transfer_args.tip_policy not in [
1707
+ TransferTipPolicyV2.ONCE,
1708
+ TransferTipPolicyV2.NEVER,
1709
+ ]:
1710
+ raise ValueError(
1711
+ f"Incompatible `new_tip` value of {new_tip}."
1712
+ f" `distribute_with_liquid_class()` only supports `new_tip` values of"
1713
+ f" 'once' and 'never'."
1714
+ )
1715
+
1716
+ verified_source = transfer_args.sources_list[0]
1717
+ with publisher.publish_context(
1718
+ broker=self.broker,
1719
+ command=cmds.distribute_with_liquid_class(
1720
+ instrument=self,
1721
+ liquid_class=liquid_class,
1722
+ volume=volume,
1723
+ source=source,
1724
+ destination=dest,
1725
+ ),
1726
+ ):
1727
+ self._core.distribute_with_liquid_class(
1728
+ liquid_class=liquid_class,
1729
+ volume=volume,
1730
+ source=(
1731
+ types.Location(types.Point(), labware=verified_source),
1732
+ verified_source._core,
1733
+ ),
1734
+ dest=[
1735
+ (types.Location(types.Point(), labware=well), well._core)
1736
+ for well in transfer_args.destinations_list
1737
+ ],
1738
+ new_tip=transfer_args.tip_policy, # type: ignore[arg-type]
1739
+ tip_racks=[
1740
+ (types.Location(types.Point(), labware=rack), rack._core)
1741
+ for rack in transfer_args.tip_racks
1742
+ ],
1743
+ starting_tip=(
1744
+ self.starting_tip._core if self.starting_tip is not None else None
1745
+ ),
1746
+ trash_location=transfer_args.trash_location,
1747
+ return_tip=return_tip,
1748
+ )
1749
+ return self
1750
+
1751
+ @requires_version(2, 23)
1752
+ def consolidate_with_liquid_class(
1753
+ self,
1754
+ liquid_class: LiquidClass,
1755
+ volume: float,
1756
+ source: Union[
1757
+ labware.Well, Sequence[labware.Well], Sequence[Sequence[labware.Well]]
1758
+ ],
1759
+ dest: Union[labware.Well, Sequence[labware.Well]],
1760
+ new_tip: TransferTipPolicyV2Type = "once",
1761
+ trash_location: Optional[
1762
+ Union[types.Location, labware.Well, TrashBin, WasteChute]
1763
+ ] = None,
1764
+ return_tip: bool = False,
1765
+ visit_every_well: bool = False,
1766
+ ) -> InstrumentContext:
1767
+ """
1768
+ Consolidate a particular type of liquid from a group of wells to one well.
1769
+
1770
+ ..
1771
+ This is intended for Opentrons internal use only and is not a guaranteed API.
1772
+
1773
+ :param liquid_class: The type of liquid to move. You must specify the liquid class,
1774
+ even if you have used :py:meth:`.Labware.load_liquid` to indicate what liquid the
1775
+ source contains.
1776
+ :type liquid_class: :py:class:`.LiquidClass`
1777
+
1778
+ :param volume: The amount, in µL, to aspirate from the source and dispense to
1779
+ each destination.
1780
+ :param source: A list of wells to aspirate liquid from.
1781
+ :param dest: A single well to dispense liquid into.
1782
+ :param new_tip: When to pick up and drop tips during the command.
1783
+ Defaults to ``"once"``.
1784
+
1785
+ - ``"once"``: Use one tip for the entire command.
1786
+ - ``"never"``: Do not pick up or drop tips at all.
1787
+
1788
+ See :ref:`param-tip-handling` for details.
1789
+
1790
+ :param trash_location: A trash container, well, or other location to dispose of
1791
+ tips. Depending on the liquid class, the pipette may also blow out liquid here.
1792
+ :param return_tip: Whether to drop used tips in their original locations
1793
+ in the tip rack, instead of the trash.
1794
+
1795
+ :meta private:
1796
+ """
1797
+ if volume == 0.0:
1798
+ _log.info(
1799
+ f"Consolidation of {liquid_class.name} specified with a volume of 0uL."
1800
+ f" Skipping."
1801
+ )
1802
+ return self
1599
1803
 
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,
1804
+ transfer_args = verify_and_normalize_transfer_args(
1805
+ source=source,
1806
+ dest=dest,
1807
+ tip_policy=new_tip,
1808
+ last_tip_picked_up_from=self._last_tip_picked_up_from,
1809
+ tip_racks=self._tip_racks,
1810
+ nozzle_map=self._core.get_nozzle_map(),
1811
+ target_all_wells=visit_every_well,
1812
+ current_volume=self.current_volume,
1606
1813
  trash_location=(
1607
- checked_trash_location._core
1608
- if isinstance(checked_trash_location, labware.Well)
1609
- else checked_trash_location
1814
+ trash_location if trash_location is not None else self.trash_container
1610
1815
  ),
1611
1816
  )
1817
+ if len(transfer_args.destinations_list) != 1:
1818
+ raise ValueError(
1819
+ f"Destination should be a single well (or resolve to a single transfer for multi-channel) "
1820
+ f"but received {transfer_args.destinations_list}."
1821
+ )
1822
+ if transfer_args.tip_policy not in [
1823
+ TransferTipPolicyV2.ONCE,
1824
+ TransferTipPolicyV2.NEVER,
1825
+ ]:
1826
+ raise ValueError(
1827
+ f"Incompatible `new_tip` value of {new_tip}."
1828
+ f" `consolidate_with_liquid_class()` only supports `new_tip` values of"
1829
+ f" 'once' and 'never'."
1830
+ )
1612
1831
 
1832
+ verified_dest = transfer_args.destinations_list[0]
1833
+ with publisher.publish_context(
1834
+ broker=self.broker,
1835
+ command=cmds.consolidate_with_liquid_class(
1836
+ instrument=self,
1837
+ liquid_class=liquid_class,
1838
+ volume=volume,
1839
+ source=source,
1840
+ destination=dest,
1841
+ ),
1842
+ ):
1843
+ self._core.consolidate_with_liquid_class(
1844
+ liquid_class=liquid_class,
1845
+ volume=volume,
1846
+ source=[
1847
+ (types.Location(types.Point(), labware=well), well._core)
1848
+ for well in transfer_args.sources_list
1849
+ ],
1850
+ dest=(
1851
+ types.Location(types.Point(), labware=verified_dest),
1852
+ verified_dest._core,
1853
+ ),
1854
+ new_tip=transfer_args.tip_policy, # type: ignore[arg-type]
1855
+ tip_racks=[
1856
+ (types.Location(types.Point(), labware=rack), rack._core)
1857
+ for rack in transfer_args.tip_racks
1858
+ ],
1859
+ starting_tip=(
1860
+ self.starting_tip._core if self.starting_tip is not None else None
1861
+ ),
1862
+ trash_location=transfer_args.trash_location,
1863
+ return_tip=return_tip,
1864
+ )
1613
1865
  return self
1614
1866
 
1615
1867
  @requires_version(2, 0)
@@ -1690,6 +1942,7 @@ class InstrumentContext(publisher.CommandPublisher):
1690
1942
  force_direct=force_direct,
1691
1943
  minimum_z_height=minimum_z_height,
1692
1944
  speed=speed,
1945
+ check_for_movement_conflicts=False,
1693
1946
  )
1694
1947
  else:
1695
1948
  if publish:
@@ -1708,23 +1961,24 @@ class InstrumentContext(publisher.CommandPublisher):
1708
1961
  force_direct=force_direct,
1709
1962
  minimum_z_height=minimum_z_height,
1710
1963
  speed=speed,
1964
+ check_for_movement_conflicts=False,
1711
1965
  )
1712
1966
 
1713
1967
  return self
1714
1968
 
1715
- @requires_version(2, 22)
1969
+ @requires_version(2, 23)
1716
1970
  def resin_tip_seal(
1717
1971
  self,
1718
1972
  location: Union[labware.Well, labware.Labware],
1719
1973
  ) -> InstrumentContext:
1720
1974
  """Seal resin tips onto the pipette.
1721
1975
 
1722
- The location provided should contain resin tips. Sealing the
1723
- tip will perform a `pick up` action but there will be no tip tracking
1724
- associated with the pipette.
1976
+ The location provided should contain resin tips. The pipette will attach itself
1977
+ to the resin tips but does not check any tip presence sensors. Before the pipette
1978
+ seals to the tips, the plunger will rise to the top of its working range so that
1979
+ it can perform a :py:func:`resin_tip_dispense` immediately.
1725
1980
 
1726
1981
  :param location: A location containing resin tips, must be a Labware or a Well.
1727
-
1728
1982
  :type location: :py:class:`~.types.Location`
1729
1983
  """
1730
1984
  if isinstance(location, labware.Labware):
@@ -1744,7 +1998,7 @@ class InstrumentContext(publisher.CommandPublisher):
1744
1998
  )
1745
1999
  return self
1746
2000
 
1747
- @requires_version(2, 22)
2001
+ @requires_version(2, 23)
1748
2002
  def resin_tip_unseal(
1749
2003
  self,
1750
2004
  location: Union[labware.Well, labware.Labware],
@@ -1778,35 +2032,64 @@ class InstrumentContext(publisher.CommandPublisher):
1778
2032
  location=well,
1779
2033
  ),
1780
2034
  ):
1781
- self._core.resin_tip_unseal(location=well.top(), well_core=well._core)
2035
+ self._core.resin_tip_unseal(location=None, well_core=well._core)
1782
2036
 
1783
2037
  return self
1784
2038
 
1785
- @requires_version(2, 22)
2039
+ @requires_version(2, 23)
1786
2040
  def resin_tip_dispense(
1787
2041
  self,
1788
2042
  location: types.Location,
1789
2043
  volume: Optional[float] = None,
1790
2044
  rate: Optional[float] = None,
1791
2045
  ) -> InstrumentContext:
1792
- """Dispense a volume from resin tips into a labware.
2046
+ """Push liquid out of resin tips that are currently sealed to a pipette.
2047
+
2048
+ The volume and rate parameters for this function control the motion of the plunger
2049
+ to create a desired pressure profile inside the pipette chamber. Unlike a regular
2050
+ dispense action, the volume and rate do not correspond to liquid volume or flow rate
2051
+ dispensed from the resin tips. Select your values for volume and flow rate based on
2052
+ experimentation with the resin tips to create a pressure profile.
2053
+
2054
+ The common way to use this function is as follows:
2055
+
2056
+ #. Seal resin tips to the pipette using :py:meth:`InstrumentContext.resin_tip_seal`.
2057
+
2058
+ #. Use :py:meth:`InstrumentContext.resin_tip_dispense` to displace an experimentally
2059
+ derived volume at an experimentally derived rate to create an experimentally derived
2060
+ target pressure inside the pipette.
2061
+
2062
+ #. Use :py:meth:`ProtocolContext.delay` to wait an experimentally derived amount of
2063
+ time for the pressure inside the pipette to push liquid into and through the resin tip
2064
+ and out the other side.
2065
+
2066
+ #. As liquid passes through the resin tip, the pressure inside the pipette will
2067
+ fall. If not all liquid has been dispensed from the resin tip, repeat steps 2
2068
+ and 3.
1793
2069
 
1794
- The location provided should contain resin tips labware as well as a
1795
- receptical for dispensed liquid. Dispensing from tip will perform a
1796
- `dispense` action of the specified volume at a desired flow rate.
2070
+ #. Unseal resin tips from the pipette using :py:meth:`InstrumentContext.resin_tip_unseal`.
1797
2071
 
1798
- :param location: A location containing resin tips.
2072
+ Flex pipette pressure sensors will raise an overpressure when a differential pressure
2073
+ inside the pipette chamber above sensor limits is detected. You may need to disable the
2074
+ pressure sensor to create the required pressure profile.
2075
+
2076
+ .. warning::
2077
+ Building excessive pressure inside the pipette chamber (significantly above the sensor
2078
+ limit) with the pressure sensors disabled can damage the pipette.
2079
+
2080
+
2081
+ :param location: Tells the robot where to dispense.
1799
2082
  :type location: :py:class:`~.types.Location`
1800
2083
 
1801
- :param volume: Will default to maximum, recommended to use the default.
1802
- The volume, in µL, that the pipette will prepare to handle.
2084
+ :param volume: The volume that the plunger should displace, in µL. Does not directly relate
2085
+ to the volume of liquid that will be dispensed.
1803
2086
  :type volume: float
1804
2087
 
1805
- :param rate: Will default to 10.0, recommended to use the default. How quickly
1806
- a pipette dispenses liquid. The speed in µL/s is calculated as
1807
- ``rate`` multiplied by :py:attr:`flow_rate.dispense<flow_rate>`.
1808
- :type rate: float
2088
+ :param rate: How quickly the plunger moves to displace the commanded volume, in µL/s. This rate does not directly relate to
2089
+ the flow rate of liquid out of the resin tip.
1809
2090
 
2091
+ Defaults to ``10.0`` µL/s.
2092
+ :type rate: float
1810
2093
  """
1811
2094
  well: Optional[labware.Well] = None
1812
2095
  last_location = self._get_last_location_by_api_version()
@@ -1998,7 +2281,15 @@ class InstrumentContext(publisher.CommandPublisher):
1998
2281
  @requires_version(2, 0)
1999
2282
  def name(self) -> str:
2000
2283
  """
2001
- The name string for the pipette (e.g., ``"p300_single"``).
2284
+ The name string for the pipette.
2285
+
2286
+ From API version 2.15 to 2.22, this property returned an internal name for Flex
2287
+ pipettes. (e.g., ``"p1000_single_flex"``).
2288
+
2289
+ .. TODO uncomment when 2.23 is ready
2290
+ In API version 2.23 and later, this property returns the Python Protocol API
2291
+ :ref:`load name <new-pipette-models>` of Flex pipettes (e.g.,
2292
+ ``"flex_1channel_1000"``).
2002
2293
  """
2003
2294
  return self._core.get_pipette_name()
2004
2295
 
@@ -2411,19 +2702,15 @@ class InstrumentContext(publisher.CommandPublisher):
2411
2702
  self._core.liquid_probe_with_recovery(well._core, loc)
2412
2703
 
2413
2704
  @requires_version(2, 20)
2414
- def measure_liquid_height(self, well: labware.Well) -> float:
2705
+ def measure_liquid_height(self, well: labware.Well) -> LiquidTrackingType:
2415
2706
  """Check the height of the liquid within a well.
2416
2707
 
2417
- :returns: The height, in mm, of the liquid from the deck.
2418
-
2419
- :meta private:
2420
-
2421
- This is intended for Opentrons internal use only and is not a guaranteed API.
2708
+ :returns: The height, in mm, of the liquid from the bottom of the well.
2422
2709
  """
2423
2710
  self._raise_if_pressure_not_supported_by_pipette()
2424
2711
  loc = well.top()
2425
- height = self._core.liquid_probe_without_recovery(well._core, loc)
2426
- return height
2712
+ self._core.liquid_probe_with_recovery(well._core, loc)
2713
+ return well.current_liquid_height()
2427
2714
 
2428
2715
  def _raise_if_configuration_not_supported_by_pipette(
2429
2716
  self, style: NozzleLayout
@@ -2448,24 +2735,41 @@ class InstrumentContext(publisher.CommandPublisher):
2448
2735
  )
2449
2736
 
2450
2737
  def _handle_aspirate_target(
2451
- self, target: validation.ValidTarget
2452
- ) -> tuple[types.Location, Optional[labware.Well], Optional[bool]]:
2453
- move_to_location: types.Location
2454
- well: Optional[labware.Well] = None
2455
- is_meniscus: Optional[bool] = None
2738
+ self, target: Union[validation.WellTarget, validation.PointTarget]
2739
+ ) -> tuple[
2740
+ types.Location, Optional[labware.Well], Optional[types.MeniscusTrackingTarget]
2741
+ ]:
2456
2742
  if isinstance(target, validation.WellTarget):
2457
- well = target.well
2458
2743
  if target.location:
2459
- move_to_location = target.location
2460
- is_meniscus = target.location.is_meniscus
2744
+ return target.location, target.well, target.location.meniscus_tracking
2461
2745
 
2462
2746
  else:
2463
- move_to_location = target.well.bottom(
2464
- z=self._well_bottom_clearances.aspirate
2747
+ return (
2748
+ target.well.bottom(z=self._well_bottom_clearances.aspirate),
2749
+ target.well,
2750
+ None,
2465
2751
  )
2466
2752
  if isinstance(target, validation.PointTarget):
2467
- move_to_location = target.location
2468
- return (move_to_location, well, is_meniscus)
2753
+ return target.location, None, None
2754
+
2755
+ def _handle_dispense_target(
2756
+ self, target: Union[validation.WellTarget, validation.PointTarget]
2757
+ ) -> tuple[
2758
+ types.Location, Optional[labware.Well], Optional[types.MeniscusTrackingTarget]
2759
+ ]:
2760
+ if isinstance(target, validation.WellTarget):
2761
+ if target.location:
2762
+ return target.location, target.well, target.location.meniscus_tracking
2763
+ elif target.well.parent._core.is_fixed_trash():
2764
+ return target.well.top(), target.well, None
2765
+ else:
2766
+ return (
2767
+ target.well.bottom(z=self._well_bottom_clearances.dispense),
2768
+ target.well,
2769
+ None,
2770
+ )
2771
+ if isinstance(target, validation.PointTarget):
2772
+ return target.location, None, None
2469
2773
 
2470
2774
 
2471
2775
  class AutoProbeDisable: