opentrons 8.5.0a4__py2.py3-none-any.whl → 8.5.0a7__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.

@@ -38,6 +38,9 @@ if TYPE_CHECKING:
38
38
  log = logging.getLogger(__name__)
39
39
 
40
40
 
41
+ AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP = 2
42
+
43
+
41
44
  @dataclass
42
45
  class LiquidAndAirGapPair:
43
46
  """Pairing of a liquid and air gap in a tip, with air gap below the liquid in a tip."""
@@ -188,7 +191,6 @@ class TransferComponentsExecutor:
188
191
  )
189
192
  tx_utils.raise_if_location_inside_liquid(
190
193
  location=submerge_start_location,
191
- well_location=self._target_location,
192
194
  well_core=self._target_well,
193
195
  location_check_descriptors=LocationCheckDescriptors(
194
196
  location_type="submerge start",
@@ -335,18 +337,23 @@ class TransferComponentsExecutor:
335
337
  - Touch tip to the sides at the specified speed (tip moves back to the center as part of touch tip)
336
338
  - Return back to the retract position
337
339
  4. Air gap
338
- - Air gap volume depends on the amount of liquid in the pipette
339
- So if total aspirated volume is 20, use the value for airGapByVolume[20]
340
- Flow rate = min(aspirateFlowRate, (airGapByVolume)/sec)
340
+ - If the retract location is at or above the safe location of
341
+ AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP, then add the air gap at the
342
+ retract location (where the pipette is already assumed to be).
343
+ - If the retract location is below the safe location, then move to
344
+ the safe location and then add the air gap.
345
+ - Air gap volume depends on the amount of liquid in the pipette.
346
+ So, if the total aspirated volume is 20, use the value for airGapByVolume[20]
347
+ Flow rate = max(aspirateFlowRate, (airGapByVolume)/sec)
341
348
  - Use post-aspirate delay
342
349
 
343
350
  Args:
344
351
  volume: dispense volume
345
352
  add_air_gap: whether to add an air gap before moving away from the current well.
346
353
  This value is True for all retractions, except when retracting
347
- during a multi-dispense.
354
+ during a multi-dispense. Value of add_air_gap during multi-dispense
355
+ will depend on whether a conditioning volume is used.
348
356
  """
349
- # TODO: Raise error if retract is below the meniscus
350
357
  assert (
351
358
  isinstance(self._target_location, Location)
352
359
  and self._target_well is not None
@@ -364,7 +371,6 @@ class TransferComponentsExecutor:
364
371
  )
365
372
  tx_utils.raise_if_location_inside_liquid(
366
373
  location=retract_location,
367
- well_location=self._target_location,
368
374
  well_core=self._target_well,
369
375
  location_check_descriptors=LocationCheckDescriptors(
370
376
  location_type="retract end",
@@ -412,6 +418,29 @@ class TransferComponentsExecutor:
412
418
  else:
413
419
  volume_for_air_gap = volume
414
420
  if add_air_gap:
421
+ # If we need to add air gap, move to a safe location above the well if
422
+ # the retract location is not already at or above this safe location
423
+ if (
424
+ retract_location.point.z
425
+ < self._target_well.get_top(AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP).z
426
+ ):
427
+ self._instrument.move_to(
428
+ location=Location(
429
+ point=Point(
430
+ retract_location.point.x,
431
+ retract_location.point.y,
432
+ self._target_well.get_top(
433
+ AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP
434
+ ).z,
435
+ ),
436
+ labware=retract_location.labware,
437
+ ),
438
+ well_core=self._target_well,
439
+ force_direct=True,
440
+ minimum_z_height=None,
441
+ # Full speed because the tip will already be out of the liquid
442
+ speed=None,
443
+ )
415
444
  self._add_air_gap(
416
445
  air_gap_volume=self._transfer_properties.aspirate.retract.air_gap_by_volume.get_for_volume(
417
446
  volume_for_air_gap
@@ -451,7 +480,6 @@ class TransferComponentsExecutor:
451
480
  If target location is a trash bin or waste chute, the retract movement step is skipped along with touch tip,
452
481
  even if it is enabled.
453
482
  """
454
- # TODO: Raise error if retract is below the meniscus
455
483
  retract_props = self._transfer_properties.dispense.retract
456
484
 
457
485
  retract_location: Union[Location, TrashBin, WasteChute]
@@ -469,7 +497,6 @@ class TransferComponentsExecutor:
469
497
  )
470
498
  tx_utils.raise_if_location_inside_liquid(
471
499
  location=retract_location,
472
- well_location=self._target_location,
473
500
  well_core=self._target_well,
474
501
  location_check_descriptors=LocationCheckDescriptors(
475
502
  location_type="retract end",
@@ -513,11 +540,9 @@ class TransferComponentsExecutor:
513
540
  # when leaving the dispense well. If this will be the final air gap, i.e,
514
541
  # we won't be moving to a Trash or a Source for Blowout after this air gap,
515
542
  # then skip the final air gap if we have been told to do so.
516
- self._do_touch_tip_and_air_gap(
543
+ self._do_touch_tip_and_air_gap_after_dispense(
517
544
  touch_tip_properties=retract_props.touch_tip,
518
- location=retract_location
519
- if isinstance(retract_location, Location)
520
- else None,
545
+ location=retract_location,
521
546
  well=self._target_well,
522
547
  add_air_gap=False if is_final_air_gap and not add_final_air_gap else True,
523
548
  )
@@ -529,7 +554,7 @@ class TransferComponentsExecutor:
529
554
  # TODO: no-op touch tip if touch tip is enabled and blowout is in trash/ reservoir/ any labware with touch-tip disabled
530
555
  assert blowout_props.flow_rate is not None
531
556
  self._instrument.set_flow_rate(blow_out=blowout_props.flow_rate)
532
- touch_tip_and_air_gap_location: Optional[Location]
557
+ touch_tip_and_air_gap_location: Union[Location, TrashBin, WasteChute]
533
558
  if blowout_props.location == BlowoutLocation.SOURCE:
534
559
  if source_location is None or source_well is None:
535
560
  raise RuntimeError(
@@ -553,9 +578,7 @@ class TransferComponentsExecutor:
553
578
  well_core=None,
554
579
  in_place=False,
555
580
  )
556
- touch_tip_and_air_gap_location = (
557
- trash_location if isinstance(trash_location, Location) else None
558
- )
581
+ touch_tip_and_air_gap_location = trash_location
559
582
  touch_tip_and_air_gap_well = (
560
583
  # We have already established that trash location of `Location` type
561
584
  # has its `labware` as `Well` type.
@@ -570,7 +593,7 @@ class TransferComponentsExecutor:
570
593
  self._tip_state.delete_air_gap(last_air_gap)
571
594
  self._tip_state.ready_to_aspirate = False
572
595
  # Do touch tip and air gap again after blowing out into source well or trash
573
- self._do_touch_tip_and_air_gap(
596
+ self._do_touch_tip_and_air_gap_after_dispense(
574
597
  touch_tip_properties=retract_props.touch_tip,
575
598
  location=touch_tip_and_air_gap_location,
576
599
  well=touch_tip_and_air_gap_well,
@@ -602,7 +625,6 @@ class TransferComponentsExecutor:
602
625
  that it handles air gaps differently based on the disposal volume, conditioning volume
603
626
  and whether we are moving to another dispense or going back to the source.
604
627
  """
605
- # TODO: Raise error if retract is below the meniscus
606
628
  assert (
607
629
  isinstance(self._target_location, Location)
608
630
  and self._target_well is not None
@@ -622,7 +644,6 @@ class TransferComponentsExecutor:
622
644
  )
623
645
  tx_utils.raise_if_location_inside_liquid(
624
646
  location=retract_location,
625
- well_location=self._target_location,
626
647
  well_core=self._target_well,
627
648
  location_check_descriptors=LocationCheckDescriptors(
628
649
  location_type="retract end",
@@ -697,7 +718,7 @@ class TransferComponentsExecutor:
697
718
  # Add an air gap depending on conditioning volume + whether this is
698
719
  # the last step of a multi-dispense sequence + whether this is the last step
699
720
  # of the entire liquid distribution.
700
- self._do_touch_tip_and_air_gap(
721
+ self._do_touch_tip_and_air_gap_after_dispense(
701
722
  touch_tip_properties=retract_props.touch_tip,
702
723
  location=retract_location,
703
724
  well=self._target_well,
@@ -711,7 +732,7 @@ class TransferComponentsExecutor:
711
732
  ):
712
733
  assert blowout_props.flow_rate is not None
713
734
  self._instrument.set_flow_rate(blow_out=blowout_props.flow_rate)
714
- touch_tip_and_air_gap_location: Optional[Location]
735
+ touch_tip_and_air_gap_location: Union[Location, TrashBin, WasteChute]
715
736
  if blowout_props.location == BlowoutLocation.SOURCE:
716
737
  if source_location is None or source_well is None:
717
738
  raise RuntimeError(
@@ -735,9 +756,7 @@ class TransferComponentsExecutor:
735
756
  well_core=None,
736
757
  in_place=False,
737
758
  )
738
- touch_tip_and_air_gap_location = (
739
- trash_location if isinstance(trash_location, Location) else None
740
- )
759
+ touch_tip_and_air_gap_location = trash_location
741
760
  touch_tip_and_air_gap_well = (
742
761
  # We have already established that trash location of `Location` type
743
762
  # has its `labware` as `Well` type.
@@ -751,7 +770,7 @@ class TransferComponentsExecutor:
751
770
  self._tip_state.ready_to_aspirate = False
752
771
 
753
772
  # Do touch tip and air gap again after blowing out into source well or trash
754
- self._do_touch_tip_and_air_gap(
773
+ self._do_touch_tip_and_air_gap_after_dispense(
755
774
  touch_tip_properties=retract_props.touch_tip,
756
775
  location=touch_tip_and_air_gap_location,
757
776
  well=touch_tip_and_air_gap_well,
@@ -763,14 +782,25 @@ class TransferComponentsExecutor:
763
782
  ),
764
783
  )
765
784
 
766
- def _do_touch_tip_and_air_gap(
785
+ def _do_touch_tip_and_air_gap_after_dispense( # noqa: C901
767
786
  self,
768
787
  touch_tip_properties: TouchTipProperties,
769
- location: Optional[Location],
788
+ location: Union[Location, TrashBin, WasteChute],
770
789
  well: Optional[WellCore],
771
790
  add_air_gap: bool,
772
791
  ) -> None:
773
- """Perform touch tip and air gap as part of post-dispense retract."""
792
+ """Perform touch tip and air gap as part of post-dispense retract.
793
+
794
+ If the retract location is at or above the safe location of
795
+ AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP, then add the air gap at the retract location
796
+ (where the pipette is already assumed to be at).
797
+
798
+ If the retract location is below the safe location, then move to the safe location
799
+ and then add the air gap.
800
+
801
+ Note: if the plunger needs to be adjusted to prepare for aspirate, it will be done
802
+ at the same location where the air gap will be added.
803
+ """
774
804
  if touch_tip_properties.enabled:
775
805
  assert (
776
806
  touch_tip_properties.speed is not None
@@ -780,7 +810,7 @@ class TransferComponentsExecutor:
780
810
  # TODO:, check that when blow out is a non-dest-well,
781
811
  # whether the touch tip params from transfer props should be used for
782
812
  # both dest-well touch tip and non-dest-well touch tip.
783
- if well is not None and location is not None:
813
+ if isinstance(location, Location) and well is not None:
784
814
  try:
785
815
  self._instrument.touch_tip(
786
816
  location=location,
@@ -803,19 +833,63 @@ class TransferComponentsExecutor:
803
833
  # Full speed because the tip will already be out of the liquid
804
834
  speed=None,
805
835
  )
836
+ if add_air_gap or not self._tip_state.ready_to_aspirate:
837
+ # If we need to move the plunger up either to prepare for aspirate or to add air gap,
838
+ # move to a safe location above the well if the retract location is not already
839
+ # at or above this safe location
840
+ if isinstance(location, Location):
841
+ assert well is not None # For mypy purposes only
842
+ if (
843
+ location.point.z
844
+ < well.get_top(AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP).z
845
+ ):
846
+ self._instrument.move_to(
847
+ location=Location(
848
+ point=Point(
849
+ location.point.x,
850
+ location.point.y,
851
+ well.get_top(AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP).z,
852
+ ),
853
+ labware=location.labware,
854
+ ),
855
+ well_core=well,
856
+ force_direct=True,
857
+ minimum_z_height=None,
858
+ speed=None,
859
+ )
860
+ else:
861
+ if (
862
+ location.offset.z
863
+ < location.top(
864
+ x=0, y=0, z=AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP
865
+ ).offset.z
866
+ ):
867
+ self._instrument.move_to(
868
+ location=location.top(
869
+ x=location.offset.x,
870
+ y=location.offset.y,
871
+ z=AIR_GAP_LOC_Z_OFFSET_FROM_WELL_TOP,
872
+ ),
873
+ well_core=None,
874
+ force_direct=True,
875
+ minimum_z_height=None,
876
+ speed=None,
877
+ )
806
878
 
807
- # TODO: check if it is okay to just do `prepare_to_aspirate` unconditionally
808
- if not self._tip_state.ready_to_aspirate:
809
- self._instrument.prepare_to_aspirate()
810
- self._tip_state.ready_to_aspirate = True
811
- if add_air_gap:
812
- self._add_air_gap(
813
- air_gap_volume=self._transfer_properties.aspirate.retract.air_gap_by_volume.get_for_volume(
814
- 0
879
+ if not self._tip_state.ready_to_aspirate:
880
+ self._instrument.prepare_to_aspirate()
881
+ self._tip_state.ready_to_aspirate = True
882
+ if add_air_gap:
883
+ self._add_air_gap(
884
+ air_gap_volume=self._transfer_properties.aspirate.retract.air_gap_by_volume.get_for_volume(
885
+ 0
886
+ )
815
887
  )
816
- )
817
888
 
818
- def _add_air_gap(self, air_gap_volume: float) -> None:
889
+ def _add_air_gap(
890
+ self,
891
+ air_gap_volume: float,
892
+ ) -> None:
819
893
  """Add an air gap."""
820
894
  if air_gap_volume == 0:
821
895
  return
@@ -371,7 +371,9 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
371
371
  starting_tip: Optional[WellCoreType],
372
372
  trash_location: Union[types.Location, TrashBin, WasteChute],
373
373
  return_tip: bool,
374
- ) -> None:
374
+ keep_last_tip: bool,
375
+ last_tip_location: Optional[Tuple[types.Location, WellCoreType]],
376
+ ) -> Optional[Tuple[types.Location, WellCoreType]]:
375
377
  """Transfer a liquid from source to dest according to liquid class properties."""
376
378
  ...
377
379
 
@@ -387,7 +389,9 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
387
389
  starting_tip: Optional[WellCoreType],
388
390
  trash_location: Union[types.Location, TrashBin, WasteChute],
389
391
  return_tip: bool,
390
- ) -> None:
392
+ keep_last_tip: bool,
393
+ last_tip_location: Optional[Tuple[types.Location, WellCoreType]],
394
+ ) -> Optional[Tuple[types.Location, WellCoreType]]:
391
395
  """
392
396
  Distribute a liquid from single source to multiple destinations
393
397
  according to liquid class properties.
@@ -406,7 +410,9 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
406
410
  starting_tip: Optional[WellCoreType],
407
411
  trash_location: Union[types.Location, TrashBin, WasteChute],
408
412
  return_tip: bool,
409
- ) -> None:
413
+ keep_last_tip: bool,
414
+ last_tip_location: Optional[Tuple[types.Location, WellCoreType]],
415
+ ) -> Optional[Tuple[types.Location, WellCoreType]]:
410
416
  """
411
417
  Consolidate liquid from multiple sources to a single destination
412
418
  using the specified liquid class properties.
@@ -611,7 +611,9 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
611
611
  starting_tip: Optional[LegacyWellCore],
612
612
  trash_location: Union[types.Location, TrashBin, WasteChute],
613
613
  return_tip: bool,
614
- ) -> None:
614
+ keep_last_tip: bool,
615
+ last_tip_location: Optional[Tuple[types.Location, LegacyWellCore]],
616
+ ) -> Optional[Tuple[types.Location, LegacyWellCore]]:
615
617
  """This will never be called because it was added in API 2.23"""
616
618
  assert False, "transfer_liquid is not supported in legacy context"
617
619
 
@@ -626,7 +628,9 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
626
628
  starting_tip: Optional[LegacyWellCore],
627
629
  trash_location: Union[types.Location, TrashBin, WasteChute],
628
630
  return_tip: bool,
629
- ) -> None:
631
+ keep_last_tip: bool,
632
+ last_tip_location: Optional[Tuple[types.Location, LegacyWellCore]],
633
+ ) -> Optional[Tuple[types.Location, LegacyWellCore]]:
630
634
  """This will never be called because it was added in API 2.23"""
631
635
  assert False, "distribute_liquid is not supported in legacy context"
632
636
 
@@ -641,7 +645,9 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
641
645
  starting_tip: Optional[LegacyWellCore],
642
646
  trash_location: Union[types.Location, TrashBin, WasteChute],
643
647
  return_tip: bool,
644
- ) -> None:
648
+ keep_last_tip: bool,
649
+ last_tip_location: Optional[Tuple[types.Location, LegacyWellCore]],
650
+ ) -> Optional[Tuple[types.Location, LegacyWellCore]]:
645
651
  """This will never be called because it was added in API 2.23."""
646
652
  assert False, "consolidate_liquid is not supported in legacy context"
647
653
 
@@ -525,7 +525,9 @@ class LegacyInstrumentCoreSimulator(
525
525
  starting_tip: Optional[LegacyWellCore],
526
526
  trash_location: Union[types.Location, TrashBin, WasteChute],
527
527
  return_tip: bool,
528
- ) -> None:
528
+ keep_last_tip: bool,
529
+ last_tip_location: Optional[Tuple[types.Location, LegacyWellCore]],
530
+ ) -> Optional[Tuple[types.Location, LegacyWellCore]]:
529
531
  """This will never be called because it was added in API 2.23."""
530
532
  assert False, "transfer_liquid is not supported in legacy context"
531
533
 
@@ -540,7 +542,9 @@ class LegacyInstrumentCoreSimulator(
540
542
  starting_tip: Optional[LegacyWellCore],
541
543
  trash_location: Union[types.Location, TrashBin, WasteChute],
542
544
  return_tip: bool,
543
- ) -> None:
545
+ keep_last_tip: bool,
546
+ last_tip_location: Optional[Tuple[types.Location, LegacyWellCore]],
547
+ ) -> Optional[Tuple[types.Location, LegacyWellCore]]:
544
548
  """This will never be called because it was added in API 2.23."""
545
549
  assert False, "distribute_liquid is not supported in legacy context"
546
550
 
@@ -555,7 +559,9 @@ class LegacyInstrumentCoreSimulator(
555
559
  starting_tip: Optional[LegacyWellCore],
556
560
  trash_location: Union[types.Location, TrashBin, WasteChute],
557
561
  return_tip: bool,
558
- ) -> None:
562
+ keep_last_tip: bool,
563
+ last_tip_location: Optional[Tuple[types.Location, LegacyWellCore]],
564
+ ) -> Optional[Tuple[types.Location, LegacyWellCore]]:
559
565
  """This will never be called because it was added in API 2.23."""
560
566
  assert False, "consolidate_liquid is not supported in legacy context"
561
567