opentrons 8.4.0a13__py2.py3-none-any.whl → 8.5.0a0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opentrons/config/defaults_ot3.py +1 -1
- opentrons/legacy_commands/commands.py +16 -4
- opentrons/legacy_commands/robot_commands.py +51 -0
- opentrons/legacy_commands/types.py +91 -2
- opentrons/protocol_api/_liquid.py +60 -15
- opentrons/protocol_api/_liquid_properties.py +137 -90
- opentrons/protocol_api/_transfer_liquid_validation.py +10 -6
- opentrons/protocol_api/core/engine/instrument.py +172 -75
- opentrons/protocol_api/core/engine/protocol.py +13 -14
- opentrons/protocol_api/core/engine/robot.py +2 -2
- opentrons/protocol_api/core/engine/transfer_components_executor.py +157 -126
- opentrons/protocol_api/core/engine/well.py +16 -0
- opentrons/protocol_api/core/instrument.py +2 -2
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -2
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -1
- opentrons/protocol_api/core/legacy/legacy_well_core.py +8 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -2
- opentrons/protocol_api/core/protocol.py +2 -2
- opentrons/protocol_api/core/well.py +8 -0
- opentrons/protocol_api/instrument_context.py +377 -86
- opentrons/protocol_api/labware.py +10 -0
- opentrons/protocol_api/protocol_context.py +79 -4
- opentrons/protocol_api/robot_context.py +48 -6
- opentrons/protocol_api/validation.py +15 -8
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +0 -1
- opentrons/protocol_engine/commands/command_unions.py +10 -10
- opentrons/protocol_engine/commands/generate_command_schema.py +1 -1
- opentrons/protocol_engine/commands/get_next_tip.py +2 -2
- opentrons/protocol_engine/commands/pick_up_tip.py +9 -3
- opentrons/protocol_engine/commands/robot/__init__.py +20 -20
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +34 -24
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +29 -20
- opentrons/protocol_engine/commands/seal_pipette_to_tip.py +1 -1
- opentrons/protocol_engine/commands/unsafe/__init__.py +17 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +1 -2
- opentrons/protocol_engine/execution/labware_movement.py +9 -2
- opentrons/protocol_engine/execution/movement.py +12 -9
- opentrons/protocol_engine/execution/queue_worker.py +8 -1
- opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +52 -19
- opentrons/protocol_engine/state/_well_math.py +2 -2
- opentrons/protocol_engine/state/commands.py +14 -28
- opentrons/protocol_engine/state/frustum_helpers.py +11 -7
- opentrons/protocol_engine/state/modules.py +1 -1
- opentrons/protocol_engine/state/pipettes.py +8 -0
- opentrons/protocol_engine/state/tips.py +46 -83
- opentrons/protocol_engine/state/update_types.py +8 -23
- opentrons/protocol_runner/legacy_command_mapper.py +11 -4
- opentrons/protocol_runner/run_orchestrator.py +1 -1
- opentrons/protocols/advanced_control/transfers/common.py +54 -11
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +1 -1
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/types.py +6 -6
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a0.dist-info}/METADATA +4 -4
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a0.dist-info}/RECORD +58 -57
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a0.dist-info}/LICENSE +0 -0
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a0.dist-info}/WHEEL +0 -0
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a0.dist-info}/top_level.txt +0 -0
|
@@ -12,7 +12,6 @@ from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
|
12
12
|
Coordinate,
|
|
13
13
|
BlowoutLocation,
|
|
14
14
|
)
|
|
15
|
-
from opentrons_shared_data.pipette.types import LIQUID_PROBE_START_OFFSET_FROM_WELL_TOP
|
|
16
15
|
|
|
17
16
|
from opentrons.protocol_api._liquid_properties import (
|
|
18
17
|
Submerge,
|
|
@@ -23,7 +22,7 @@ from opentrons.protocol_api._liquid_properties import (
|
|
|
23
22
|
TouchTipProperties,
|
|
24
23
|
)
|
|
25
24
|
from opentrons.protocol_engine.errors import TouchTipDisabledError
|
|
26
|
-
from opentrons.types import Location, Point
|
|
25
|
+
from opentrons.types import Location, Point, Mount
|
|
27
26
|
from opentrons.protocols.advanced_control.transfers.transfer_liquid_utils import (
|
|
28
27
|
LocationCheckDescriptors,
|
|
29
28
|
)
|
|
@@ -119,11 +118,32 @@ class TransferComponentsExecutor:
|
|
|
119
118
|
self,
|
|
120
119
|
instrument_core: InstrumentCore,
|
|
121
120
|
transfer_properties: TransferProperties,
|
|
122
|
-
target_location: Location,
|
|
123
|
-
target_well: WellCore,
|
|
121
|
+
target_location: Union[Location, TrashBin, WasteChute],
|
|
122
|
+
target_well: Optional[WellCore],
|
|
124
123
|
tip_state: TipState,
|
|
125
124
|
transfer_type: TransferType,
|
|
126
125
|
) -> None:
|
|
126
|
+
"""Create a TransferComponentsExecutor instance.
|
|
127
|
+
|
|
128
|
+
One instance should be created to execute all the steps inside each of the
|
|
129
|
+
liquid class' transfer components- aspirate, dispense and multi-dispense.
|
|
130
|
+
The state of the TransferComponentsExecutor instance is expected to be valid
|
|
131
|
+
only for the component it was created.
|
|
132
|
+
|
|
133
|
+
For example, if we want to execute all the steps (submerge, dispense, retract, etc)
|
|
134
|
+
related to the 'dispense' component of a liquid-class based transfer, the class
|
|
135
|
+
will be used to initialize info about the dispense by assigning values
|
|
136
|
+
to class attributes as follows-
|
|
137
|
+
- target_location: the dispense location
|
|
138
|
+
- target_well: the well associated with dispense location, will be None when the
|
|
139
|
+
target_location argument is a TrashBin or WasteChute
|
|
140
|
+
- tip_state: the state of the tip before dispense component steps are executed
|
|
141
|
+
- transfer_type: whether the dispense component is being called as a part of a
|
|
142
|
+
1-to-1 transfer or a consolidation or a distribution
|
|
143
|
+
|
|
144
|
+
These attributes will remain the same throughout the component's execution,
|
|
145
|
+
except `tip_state`, which will keep updating as fluids are handled.
|
|
146
|
+
"""
|
|
127
147
|
self._instrument = instrument_core
|
|
128
148
|
self._transfer_properties = transfer_properties
|
|
129
149
|
self._target_location = target_location
|
|
@@ -140,7 +160,6 @@ class TransferComponentsExecutor:
|
|
|
140
160
|
self,
|
|
141
161
|
submerge_properties: Submerge,
|
|
142
162
|
post_submerge_action: Literal["aspirate", "dispense"],
|
|
143
|
-
volume_for_pipette_mode_configuration: Optional[float],
|
|
144
163
|
) -> None:
|
|
145
164
|
"""Execute submerge steps.
|
|
146
165
|
|
|
@@ -148,56 +167,38 @@ class TransferComponentsExecutor:
|
|
|
148
167
|
Should raise an error if this point is inside the liquid?
|
|
149
168
|
For liquid meniscus this is easy to tell. Can’t be below meniscus
|
|
150
169
|
For reference pos of anything else, do not allow submerge position to be below aspirate position
|
|
151
|
-
2. move to aspirate position at desired speed
|
|
170
|
+
2. move to aspirate/dispense position at desired speed
|
|
152
171
|
3. delay
|
|
172
|
+
|
|
173
|
+
If target location is a trash bin or waste chute, the pipette will move to the disposal location given,
|
|
174
|
+
remove air gap and delay
|
|
153
175
|
"""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
point=self._target_well.get_top(
|
|
171
|
-
LIQUID_PROBE_START_OFFSET_FROM_WELL_TOP.z
|
|
172
|
-
),
|
|
173
|
-
labware=self._target_location.labware,
|
|
174
|
-
),
|
|
176
|
+
submerge_start_location: Union[Location, TrashBin, WasteChute]
|
|
177
|
+
if isinstance(self._target_location, Location):
|
|
178
|
+
assert self._target_well is not None
|
|
179
|
+
submerge_start_point = absolute_point_from_position_reference_and_offset(
|
|
180
|
+
well=self._target_well,
|
|
181
|
+
well_volume_difference=0,
|
|
182
|
+
position_reference=submerge_properties.start_position.position_reference,
|
|
183
|
+
offset=submerge_properties.start_position.offset,
|
|
184
|
+
mount=self._instrument.get_mount(),
|
|
185
|
+
)
|
|
186
|
+
submerge_start_location = Location(
|
|
187
|
+
point=submerge_start_point, labware=self._target_location.labware
|
|
188
|
+
)
|
|
189
|
+
tx_utils.raise_if_location_inside_liquid(
|
|
190
|
+
location=submerge_start_location,
|
|
191
|
+
well_location=self._target_location,
|
|
175
192
|
well_core=self._target_well,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
193
|
+
location_check_descriptors=LocationCheckDescriptors(
|
|
194
|
+
location_type="submerge start",
|
|
195
|
+
pipetting_action=post_submerge_action,
|
|
196
|
+
),
|
|
197
|
+
logger=log,
|
|
179
198
|
)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
and self._instrument.get_liquid_presence_detection()
|
|
184
|
-
):
|
|
185
|
-
self._instrument.liquid_probe_with_recovery(
|
|
186
|
-
well_core=self._target_well, loc=submerge_start_location
|
|
187
|
-
)
|
|
188
|
-
# TODO: do volume configuration + prepare for aspirate only if the mode needs to be changed
|
|
189
|
-
self._instrument.configure_for_volume(volume_for_pipette_mode_configuration) # type: ignore[arg-type]
|
|
190
|
-
self._instrument.prepare_to_aspirate()
|
|
191
|
-
tx_utils.raise_if_location_inside_liquid(
|
|
192
|
-
location=submerge_start_location,
|
|
193
|
-
well_location=self._target_location,
|
|
194
|
-
well_core=self._target_well,
|
|
195
|
-
location_check_descriptors=LocationCheckDescriptors(
|
|
196
|
-
location_type="submerge start",
|
|
197
|
-
pipetting_action=post_submerge_action,
|
|
198
|
-
),
|
|
199
|
-
logger=log,
|
|
200
|
-
)
|
|
199
|
+
else:
|
|
200
|
+
submerge_start_location = self._target_location
|
|
201
|
+
|
|
201
202
|
self._instrument.move_to(
|
|
202
203
|
location=submerge_start_location,
|
|
203
204
|
well_core=self._target_well,
|
|
@@ -205,21 +206,26 @@ class TransferComponentsExecutor:
|
|
|
205
206
|
minimum_z_height=None,
|
|
206
207
|
speed=None,
|
|
207
208
|
)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
209
|
+
self._remove_air_gap(location=submerge_start_location)
|
|
210
|
+
if isinstance(self._target_location, Location):
|
|
211
|
+
self._instrument.move_to(
|
|
212
|
+
location=self._target_location,
|
|
213
|
+
well_core=self._target_well,
|
|
214
|
+
force_direct=True,
|
|
215
|
+
minimum_z_height=None,
|
|
216
|
+
speed=submerge_properties.speed,
|
|
217
|
+
)
|
|
218
|
+
|
|
217
219
|
if submerge_properties.delay.enabled and submerge_properties.delay.duration:
|
|
218
220
|
self._instrument.delay(submerge_properties.delay.duration)
|
|
219
221
|
|
|
220
222
|
def aspirate_and_wait(self, volume: float) -> None:
|
|
221
223
|
"""Aspirate according to aspirate properties and wait if enabled."""
|
|
222
224
|
# TODO: handle volume correction
|
|
225
|
+
assert (
|
|
226
|
+
isinstance(self._target_location, Location)
|
|
227
|
+
and self._target_well is not None
|
|
228
|
+
)
|
|
223
229
|
aspirate_props = self._transfer_properties.aspirate
|
|
224
230
|
correction_volume = aspirate_props.correction_by_volume.get_for_volume(volume)
|
|
225
231
|
self._instrument.aspirate(
|
|
@@ -275,11 +281,15 @@ class TransferComponentsExecutor:
|
|
|
275
281
|
NOTE: For most of our built-in definitions, we will keep _mix_ off because it is a very application specific thing.
|
|
276
282
|
We should mention in our docs that users should adjust this property according to their application.
|
|
277
283
|
"""
|
|
278
|
-
if not mix_properties.enabled
|
|
284
|
+
if not mix_properties.enabled or not isinstance(
|
|
285
|
+
self._target_location, Location
|
|
286
|
+
):
|
|
279
287
|
return
|
|
280
288
|
# Assertion only for mypy purposes
|
|
281
289
|
assert (
|
|
282
|
-
mix_properties.repetitions is not None
|
|
290
|
+
mix_properties.repetitions is not None
|
|
291
|
+
and mix_properties.volume is not None
|
|
292
|
+
and self._target_well is not None
|
|
283
293
|
)
|
|
284
294
|
push_out_vol = (
|
|
285
295
|
self._transfer_properties.dispense.push_out_by_volume.get_for_volume(
|
|
@@ -337,11 +347,17 @@ class TransferComponentsExecutor:
|
|
|
337
347
|
during a multi-dispense.
|
|
338
348
|
"""
|
|
339
349
|
# TODO: Raise error if retract is below the meniscus
|
|
350
|
+
assert (
|
|
351
|
+
isinstance(self._target_location, Location)
|
|
352
|
+
and self._target_well is not None
|
|
353
|
+
)
|
|
340
354
|
retract_props = self._transfer_properties.aspirate.retract
|
|
341
355
|
retract_point = absolute_point_from_position_reference_and_offset(
|
|
342
356
|
well=self._target_well,
|
|
343
|
-
|
|
344
|
-
|
|
357
|
+
well_volume_difference=0,
|
|
358
|
+
position_reference=retract_props.end_position.position_reference,
|
|
359
|
+
offset=retract_props.end_position.offset,
|
|
360
|
+
mount=self._instrument.get_mount(),
|
|
345
361
|
)
|
|
346
362
|
retract_location = Location(
|
|
347
363
|
retract_point, labware=self._target_location.labware
|
|
@@ -371,7 +387,7 @@ class TransferComponentsExecutor:
|
|
|
371
387
|
assert (
|
|
372
388
|
touch_tip_props.speed is not None
|
|
373
389
|
and touch_tip_props.z_offset is not None
|
|
374
|
-
and touch_tip_props.
|
|
390
|
+
and touch_tip_props.mm_from_edge is not None
|
|
375
391
|
)
|
|
376
392
|
self._instrument.touch_tip(
|
|
377
393
|
location=retract_location,
|
|
@@ -379,7 +395,7 @@ class TransferComponentsExecutor:
|
|
|
379
395
|
radius=1,
|
|
380
396
|
z_offset=touch_tip_props.z_offset,
|
|
381
397
|
speed=touch_tip_props.speed,
|
|
382
|
-
mm_from_edge=touch_tip_props.
|
|
398
|
+
mm_from_edge=touch_tip_props.mm_from_edge,
|
|
383
399
|
)
|
|
384
400
|
self._instrument.move_to(
|
|
385
401
|
location=retract_location,
|
|
@@ -431,35 +447,47 @@ class TransferComponentsExecutor:
|
|
|
431
447
|
- Prepare-to-aspirate (top of well)
|
|
432
448
|
- Do air-gap (top of well)
|
|
433
449
|
7. If drop tip, move to drop tip location, drop tip
|
|
450
|
+
|
|
451
|
+
If target location is a trash bin or waste chute, the retract movement step is skipped along with touch tip,
|
|
452
|
+
even if it is enabled.
|
|
434
453
|
"""
|
|
435
454
|
# TODO: Raise error if retract is below the meniscus
|
|
436
|
-
|
|
437
455
|
retract_props = self._transfer_properties.dispense.retract
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
456
|
+
|
|
457
|
+
retract_location: Union[Location, TrashBin, WasteChute]
|
|
458
|
+
if isinstance(self._target_location, Location):
|
|
459
|
+
assert self._target_well is not None
|
|
460
|
+
retract_point = absolute_point_from_position_reference_and_offset(
|
|
461
|
+
well=self._target_well,
|
|
462
|
+
well_volume_difference=0,
|
|
463
|
+
position_reference=retract_props.end_position.position_reference,
|
|
464
|
+
offset=retract_props.end_position.offset,
|
|
465
|
+
mount=self._instrument.get_mount(),
|
|
466
|
+
)
|
|
467
|
+
retract_location = Location(
|
|
468
|
+
retract_point, labware=self._target_location.labware
|
|
469
|
+
)
|
|
470
|
+
tx_utils.raise_if_location_inside_liquid(
|
|
471
|
+
location=retract_location,
|
|
472
|
+
well_location=self._target_location,
|
|
473
|
+
well_core=self._target_well,
|
|
474
|
+
location_check_descriptors=LocationCheckDescriptors(
|
|
475
|
+
location_type="retract end",
|
|
476
|
+
pipetting_action="dispense",
|
|
477
|
+
),
|
|
478
|
+
logger=log,
|
|
479
|
+
)
|
|
480
|
+
self._instrument.move_to(
|
|
481
|
+
location=retract_location,
|
|
482
|
+
well_core=self._target_well,
|
|
483
|
+
force_direct=True,
|
|
484
|
+
minimum_z_height=None,
|
|
485
|
+
speed=retract_props.speed,
|
|
486
|
+
)
|
|
487
|
+
else:
|
|
488
|
+
retract_location = self._target_location
|
|
489
|
+
|
|
490
|
+
# TODO should we delay here for a trash despite not having a "retract"?
|
|
463
491
|
retract_delay = retract_props.delay
|
|
464
492
|
if retract_delay.enabled and retract_delay.duration:
|
|
465
493
|
self._instrument.delay(retract_delay.duration)
|
|
@@ -487,7 +515,9 @@ class TransferComponentsExecutor:
|
|
|
487
515
|
# then skip the final air gap if we have been told to do so.
|
|
488
516
|
self._do_touch_tip_and_air_gap(
|
|
489
517
|
touch_tip_properties=retract_props.touch_tip,
|
|
490
|
-
location=retract_location
|
|
518
|
+
location=retract_location
|
|
519
|
+
if isinstance(retract_location, Location)
|
|
520
|
+
else None,
|
|
491
521
|
well=self._target_well,
|
|
492
522
|
add_air_gap=False if is_final_air_gap and not add_final_air_gap else True,
|
|
493
523
|
)
|
|
@@ -573,14 +603,19 @@ class TransferComponentsExecutor:
|
|
|
573
603
|
and whether we are moving to another dispense or going back to the source.
|
|
574
604
|
"""
|
|
575
605
|
# TODO: Raise error if retract is below the meniscus
|
|
576
|
-
|
|
606
|
+
assert (
|
|
607
|
+
isinstance(self._target_location, Location)
|
|
608
|
+
and self._target_well is not None
|
|
609
|
+
)
|
|
577
610
|
assert self._transfer_properties.multi_dispense is not None
|
|
578
611
|
|
|
579
612
|
retract_props = self._transfer_properties.multi_dispense.retract
|
|
580
613
|
retract_point = absolute_point_from_position_reference_and_offset(
|
|
581
614
|
well=self._target_well,
|
|
582
|
-
|
|
583
|
-
|
|
615
|
+
well_volume_difference=0,
|
|
616
|
+
position_reference=retract_props.end_position.position_reference,
|
|
617
|
+
offset=retract_props.end_position.offset,
|
|
618
|
+
mount=self._instrument.get_mount(),
|
|
584
619
|
)
|
|
585
620
|
retract_location = Location(
|
|
586
621
|
retract_point, labware=self._target_location.labware
|
|
@@ -740,7 +775,7 @@ class TransferComponentsExecutor:
|
|
|
740
775
|
assert (
|
|
741
776
|
touch_tip_properties.speed is not None
|
|
742
777
|
and touch_tip_properties.z_offset is not None
|
|
743
|
-
and touch_tip_properties.
|
|
778
|
+
and touch_tip_properties.mm_from_edge is not None
|
|
744
779
|
)
|
|
745
780
|
# TODO:, check that when blow out is a non-dest-well,
|
|
746
781
|
# whether the touch tip params from transfer props should be used for
|
|
@@ -753,7 +788,7 @@ class TransferComponentsExecutor:
|
|
|
753
788
|
radius=1,
|
|
754
789
|
z_offset=touch_tip_properties.z_offset,
|
|
755
790
|
speed=touch_tip_properties.speed,
|
|
756
|
-
mm_from_edge=touch_tip_properties.
|
|
791
|
+
mm_from_edge=touch_tip_properties.mm_from_edge,
|
|
757
792
|
)
|
|
758
793
|
except TouchTipDisabledError:
|
|
759
794
|
# TODO: log a warning
|
|
@@ -803,43 +838,33 @@ class TransferComponentsExecutor:
|
|
|
803
838
|
self._instrument.delay(delay_props.duration)
|
|
804
839
|
self._tip_state.append_air_gap(air_gap_volume)
|
|
805
840
|
|
|
806
|
-
def _remove_air_gap(self, location: Location) -> None:
|
|
841
|
+
def _remove_air_gap(self, location: Union[Location, TrashBin, WasteChute]) -> None:
|
|
807
842
|
"""Remove a previously added air gap."""
|
|
808
843
|
last_air_gap = self._tip_state.last_liquid_and_air_gap_in_tip.air_gap
|
|
809
|
-
if last_air_gap == 0:
|
|
810
|
-
return
|
|
811
|
-
|
|
812
844
|
dispense_props = self._transfer_properties.dispense
|
|
813
|
-
|
|
814
|
-
last_air_gap
|
|
815
|
-
|
|
816
|
-
# The minimum flow rate should be air_gap_volume per second
|
|
817
|
-
flow_rate = max(
|
|
818
|
-
dispense_props.flow_rate_by_volume.get_for_volume(last_air_gap),
|
|
819
|
-
last_air_gap,
|
|
820
|
-
)
|
|
821
|
-
self._instrument.dispense(
|
|
845
|
+
self._instrument.remove_air_gap_during_transfer_with_liquid_class(
|
|
846
|
+
last_air_gap=last_air_gap,
|
|
847
|
+
dispense_props=dispense_props,
|
|
822
848
|
location=location,
|
|
823
|
-
well_core=None,
|
|
824
|
-
volume=last_air_gap,
|
|
825
|
-
rate=1,
|
|
826
|
-
flow_rate=flow_rate,
|
|
827
|
-
in_place=True,
|
|
828
|
-
push_out=0,
|
|
829
|
-
correction_volume=correction_volume,
|
|
830
849
|
)
|
|
831
850
|
self._tip_state.delete_air_gap(last_air_gap)
|
|
832
|
-
dispense_delay = dispense_props.delay
|
|
833
|
-
if dispense_delay.enabled and dispense_delay.duration:
|
|
834
|
-
self._instrument.delay(dispense_delay.duration)
|
|
835
851
|
|
|
836
852
|
|
|
837
853
|
def absolute_point_from_position_reference_and_offset(
|
|
838
854
|
well: WellCore,
|
|
855
|
+
well_volume_difference: float,
|
|
839
856
|
position_reference: PositionReference,
|
|
840
857
|
offset: Coordinate,
|
|
858
|
+
mount: Mount,
|
|
841
859
|
) -> Point:
|
|
842
|
-
"""Return the absolute point, given the well, the position reference and offset.
|
|
860
|
+
"""Return the absolute point, given the well, the position reference and offset.
|
|
861
|
+
|
|
862
|
+
If using meniscus as the position reference, well_volume_difference should be specified.
|
|
863
|
+
`well_volume_difference` is the expected *difference* in well volume we want to consider
|
|
864
|
+
when estimating the height of the liquid meniscus after an aspirate/ dispense.
|
|
865
|
+
So, for liquid height estimation after an aspirate, well_volume_difference is
|
|
866
|
+
expected to be a -ve value while for a dispense, it will be a +ve value.
|
|
867
|
+
"""
|
|
843
868
|
match position_reference:
|
|
844
869
|
case PositionReference.WELL_TOP:
|
|
845
870
|
reference_point = well.get_top(0)
|
|
@@ -848,11 +873,17 @@ def absolute_point_from_position_reference_and_offset(
|
|
|
848
873
|
case PositionReference.WELL_CENTER:
|
|
849
874
|
reference_point = well.get_center()
|
|
850
875
|
case PositionReference.LIQUID_MENISCUS:
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
876
|
+
estimated_liquid_height = well.estimate_liquid_height_after_pipetting(
|
|
877
|
+
mount=mount,
|
|
878
|
+
operation_volume=well_volume_difference,
|
|
879
|
+
)
|
|
880
|
+
if isinstance(estimated_liquid_height, (float, int)):
|
|
881
|
+
reference_point = well.get_bottom(z_offset=estimated_liquid_height)
|
|
854
882
|
else:
|
|
855
|
-
|
|
883
|
+
# If estimated liquid height gives a SimulatedProbeResult then
|
|
884
|
+
# assume meniscus is at well center.
|
|
885
|
+
# Will this cause more harm than good? Is there a better alternative to this?
|
|
886
|
+
reference_point = well.get_center()
|
|
856
887
|
case _:
|
|
857
888
|
raise ValueError(f"Unknown position reference {position_reference}")
|
|
858
889
|
return reference_point + Point(offset.x, offset.y, offset.z)
|
|
@@ -223,3 +223,19 @@ class WellCore(AbstractWellCore):
|
|
|
223
223
|
return self._engine_client.state.geometry.get_current_well_volume(
|
|
224
224
|
labware_id=labware_id, well_name=well_name
|
|
225
225
|
)
|
|
226
|
+
|
|
227
|
+
def height_from_volume(self, volume: LiquidTrackingType) -> LiquidTrackingType:
|
|
228
|
+
"""Return the height in a well corresponding to a given volume."""
|
|
229
|
+
labware_id = self.labware_id
|
|
230
|
+
well_name = self._name
|
|
231
|
+
return self._engine_client.state.geometry.get_well_height_at_volume(
|
|
232
|
+
labware_id=labware_id, well_name=well_name, volume=volume
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def volume_from_height(self, height: LiquidTrackingType) -> LiquidTrackingType:
|
|
236
|
+
"""Return the volume contained in a well at any height."""
|
|
237
|
+
labware_id = self.labware_id
|
|
238
|
+
well_name = self._name
|
|
239
|
+
return self._engine_client.state.geometry.get_well_volume_at_height(
|
|
240
|
+
labware_id=labware_id, well_name=well_name, height=height
|
|
241
|
+
)
|
|
@@ -365,7 +365,7 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
|
|
|
365
365
|
liquid_class: LiquidClass,
|
|
366
366
|
volume: float,
|
|
367
367
|
source: List[Tuple[types.Location, WellCoreType]],
|
|
368
|
-
dest: List[Tuple[types.Location, WellCoreType]],
|
|
368
|
+
dest: Union[List[Tuple[types.Location, WellCoreType]], TrashBin, WasteChute],
|
|
369
369
|
new_tip: TransferTipPolicyV2,
|
|
370
370
|
tip_racks: List[Tuple[types.Location, LabwareCoreType]],
|
|
371
371
|
starting_tip: Optional[WellCoreType],
|
|
@@ -400,7 +400,7 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
|
|
|
400
400
|
liquid_class: LiquidClass,
|
|
401
401
|
volume: float,
|
|
402
402
|
source: List[Tuple[types.Location, WellCoreType]],
|
|
403
|
-
dest: Tuple[types.Location, WellCoreType],
|
|
403
|
+
dest: Union[Tuple[types.Location, WellCoreType], TrashBin, WasteChute],
|
|
404
404
|
new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
|
|
405
405
|
tip_racks: List[Tuple[types.Location, LabwareCoreType]],
|
|
406
406
|
starting_tip: Optional[WellCoreType],
|
|
@@ -605,7 +605,7 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
|
|
|
605
605
|
liquid_class: LiquidClass,
|
|
606
606
|
volume: float,
|
|
607
607
|
source: List[Tuple[types.Location, LegacyWellCore]],
|
|
608
|
-
dest: List[Tuple[types.Location, LegacyWellCore]],
|
|
608
|
+
dest: Union[List[Tuple[types.Location, LegacyWellCore]], TrashBin, WasteChute],
|
|
609
609
|
new_tip: TransferTipPolicyV2,
|
|
610
610
|
tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
|
|
611
611
|
starting_tip: Optional[LegacyWellCore],
|
|
@@ -635,7 +635,7 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
|
|
|
635
635
|
liquid_class: LiquidClass,
|
|
636
636
|
volume: float,
|
|
637
637
|
source: List[Tuple[types.Location, LegacyWellCore]],
|
|
638
|
-
dest: Tuple[types.Location, LegacyWellCore],
|
|
638
|
+
dest: Union[Tuple[types.Location, LegacyWellCore], TrashBin, WasteChute],
|
|
639
639
|
new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
|
|
640
640
|
tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
|
|
641
641
|
starting_tip: Optional[LegacyWellCore],
|
|
@@ -599,7 +599,7 @@ class LegacyProtocolCore(
|
|
|
599
599
|
"""Define a liquid to load into a well."""
|
|
600
600
|
assert False, "define_liquid only supported on engine core"
|
|
601
601
|
|
|
602
|
-
def define_liquid_class(self, name: str) -> LiquidClass:
|
|
602
|
+
def define_liquid_class(self, name: str, version: int) -> LiquidClass:
|
|
603
603
|
"""Define a liquid class."""
|
|
604
604
|
assert False, "define_liquid_class is only supported on engine core"
|
|
605
605
|
|
|
@@ -143,6 +143,14 @@ class LegacyWellCore(AbstractWellCore):
|
|
|
143
143
|
"""Get the current well volume."""
|
|
144
144
|
return 0.0
|
|
145
145
|
|
|
146
|
+
def height_from_volume(self, volume: LiquidTrackingType) -> LiquidTrackingType:
|
|
147
|
+
"""Return the height in a well corresponding to a given volume."""
|
|
148
|
+
return 0.0
|
|
149
|
+
|
|
150
|
+
def volume_from_height(self, height: LiquidTrackingType) -> LiquidTrackingType:
|
|
151
|
+
"""Return the volume contained in a well at any height."""
|
|
152
|
+
return 0.0
|
|
153
|
+
|
|
146
154
|
# TODO(mc, 2022-10-28): is this used and/or necessary?
|
|
147
155
|
def __repr__(self) -> str:
|
|
148
156
|
"""Use the well's display name as its repr."""
|
|
@@ -519,7 +519,7 @@ class LegacyInstrumentCoreSimulator(
|
|
|
519
519
|
liquid_class: LiquidClass,
|
|
520
520
|
volume: float,
|
|
521
521
|
source: List[Tuple[types.Location, LegacyWellCore]],
|
|
522
|
-
dest: List[Tuple[types.Location, LegacyWellCore]],
|
|
522
|
+
dest: Union[List[Tuple[types.Location, LegacyWellCore]], TrashBin, WasteChute],
|
|
523
523
|
new_tip: TransferTipPolicyV2,
|
|
524
524
|
tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
|
|
525
525
|
starting_tip: Optional[LegacyWellCore],
|
|
@@ -549,7 +549,7 @@ class LegacyInstrumentCoreSimulator(
|
|
|
549
549
|
liquid_class: LiquidClass,
|
|
550
550
|
volume: float,
|
|
551
551
|
source: List[Tuple[types.Location, LegacyWellCore]],
|
|
552
|
-
dest: Tuple[types.Location, LegacyWellCore],
|
|
552
|
+
dest: Union[Tuple[types.Location, LegacyWellCore], TrashBin, WasteChute],
|
|
553
553
|
new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
|
|
554
554
|
tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
|
|
555
555
|
starting_tip: Optional[LegacyWellCore],
|
|
@@ -230,7 +230,7 @@ class AbstractProtocol(
|
|
|
230
230
|
def get_last_location(
|
|
231
231
|
self,
|
|
232
232
|
mount: Optional[Mount] = None,
|
|
233
|
-
) -> Optional[Location]:
|
|
233
|
+
) -> Optional[Union[Location, TrashBin, WasteChute]]:
|
|
234
234
|
...
|
|
235
235
|
|
|
236
236
|
@abstractmethod
|
|
@@ -311,7 +311,7 @@ class AbstractProtocol(
|
|
|
311
311
|
"""Define a liquid to load into a well."""
|
|
312
312
|
|
|
313
313
|
@abstractmethod
|
|
314
|
-
def define_liquid_class(self, name: str) -> LiquidClass:
|
|
314
|
+
def define_liquid_class(self, name: str, version: int) -> LiquidClass:
|
|
315
315
|
"""Define a liquid class for use in transfer functions."""
|
|
316
316
|
|
|
317
317
|
@abstractmethod
|
|
@@ -104,5 +104,13 @@ class AbstractWellCore(ABC):
|
|
|
104
104
|
def get_liquid_volume(self) -> LiquidTrackingType:
|
|
105
105
|
"""Get the current volume within a well."""
|
|
106
106
|
|
|
107
|
+
@abstractmethod
|
|
108
|
+
def height_from_volume(self, volume: LiquidTrackingType) -> LiquidTrackingType:
|
|
109
|
+
"""Return the height in a well corresponding to a given volume."""
|
|
110
|
+
|
|
111
|
+
@abstractmethod
|
|
112
|
+
def volume_from_height(self, height: LiquidTrackingType) -> LiquidTrackingType:
|
|
113
|
+
"""Return the volume contained in a well at any height."""
|
|
114
|
+
|
|
107
115
|
|
|
108
116
|
WellCoreType = TypeVar("WellCoreType", bound=AbstractWellCore)
|